You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Google Apps Script的Telegram机器人Inline按钮重复响应问题

问题描述

我用Google Apps Script搭建了一个Telegram机器人,通过Webhook处理更新。点击Inline按钮时,机器人能返回正确回复,但每隔1-2分钟会重复发送相同响应,用户并未再次点击。

日志分析显示,Telegram重复发送同一callback_query的原因是脚本未及时返回HTTP 200 OK,或未快速调用answerCallbackQuery。我尝试返回ContentService.createTextOutput()并发送200 OK响应,但问题仍存在。如何确保每次按钮点击仅触发一次响应,避免无限重复?

代码示例

function installBot() {
  const res = UrlFetchApp.fetch(`${API_URL}/setWebhook?url=${WEB_APP_URL}`);
  Logger.log(res.getContentText());
}

function doPost(e) {
  const update = JSON.parse(e.postData.contents);
  Logger.log(update);

  if (update.message) handleMessage(update.message);
  else if (update.callback_query) handleCallback(update.callback_query);

  return ContentService.createTextOutput("OK");
}

function handleMessage(msg) {
  const chatId = msg.chat.id;
  const text = msg.text ? msg.text.trim().toLowerCase() : "";

  switch (text) {
    case "/start":
      sendMainMenu(chatId);
      break;
    ...
    default:
      sendText(chatId, "❌ Commande inconnue.\nEssayez /start ou /help.");
  }
}

function handleCallback(query) {
  const chatId = query.message.chat.id;
  const data = query.data;
  const queryId = query.id;

  let replyText = "";

  UrlFetchApp.fetch(`${API_URL}/answerCallbackQuery`, {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({
      callback_query_id: queryId,
      text: "",
      show_alert: false
    }),
  });

  switch (data) {
    case "trial":
      replyText = "⚠️ Option inconnue.";
      break;
        ...
  }

  if (replyText) {
    sendMessage(chatId, replyText, { parse_mode: "HTML" });
  }
}

function sendText(chatId, text, mode = "HTML") {
  const url = `${API_URL}/sendMessage`;
  const payload = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({
      chat_id: chatId,
      text: text,
      parse_mode: mode,
    }),
  };
  UrlFetchApp.fetch(url, payload);
}

function sendMessage(chatId, text, options) {
  const url = `${API_URL}/sendMessage`;
  const payload = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({
      chat_id: chatId,
      text: text,
      ...options,
    }),
  };
  UrlFetchApp.fetch(url, payload);
}

解决方案

1. 异步处理耗时操作,优先返回200 OK

Google Apps Script的doPost函数如果同步执行UrlFetchApp等耗时操作,会延迟返回响应,导致Telegram判定请求超时并重复发送callback_query。需先快速返回200,再通过触发器异步处理业务逻辑:

修改doPost函数并新增异步处理函数:

function doPost(e) {
  const update = JSON.parse(e.postData.contents);
  Logger.log(update);

  // 创建异步触发器,延迟1秒执行处理逻辑
  const trigger = ScriptApp.newTrigger("processUpdate")
    .timeBased()
    .after(1000)
    .create();
  
  // 将update数据存入缓存,供异步函数读取
  CacheService.getScriptCache().put(trigger.getUniqueId(), JSON.stringify(update), 60);

  // 立即返回200
  return ContentService.createTextOutput("OK").setMimeType(ContentService.MimeType.TEXT);
}

// 异步处理更新逻辑
function processUpdate(e) {
  const triggerId = e.triggerUid;
  const update = JSON.parse(CacheService.getScriptCache().get(triggerId));
  
  // 删除触发器,释放资源
  ScriptApp.deleteTrigger(ScriptApp.getTriggerById(triggerId));

  if (update.message) handleMessage(update.message);
  else if (update.callback_query) handleCallback(update.callback_query);
}

2. 添加callback_query去重机制

用CacheService记录已处理的callback_query_id,避免重复响应:

修改handleCallback函数:

function handleCallback(query) {
  const queryId = query.id;
  const cache = CacheService.getScriptCache();
  
  // 检查该query是否已处理,已处理则直接返回
  if (cache.get(queryId)) return;
  
  // 标记为已处理,缓存10分钟(覆盖Telegram重试周期)
  cache.put(queryId, "processed", 600);

  const chatId = query.message.chat.id;
  const data = query.data;
  let replyText = "";

  // 优先调用answerCallbackQuery,确保10秒内完成
  UrlFetchApp.fetch(`${API_URL}/answerCallbackQuery`, {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({
      callback_query_id: queryId,
      text: "",
      show_alert: false
    }),
    muteHttpExceptions: true // 避免请求失败中断执行
  });

  switch (data) {
    case "trial":
      replyText = "⚠️ Option inconnue.";
      break;
        ...
  }

  if (replyText) {
    sendMessage(chatId, replyText, { parse_mode: "HTML" });
  }
}

3. 优化UrlFetchApp调用

给UrlFetchApp.fetch添加超时设置,并开启静默异常,避免因网络问题导致整个函数中断:

修改sendText和sendMessage函数:

function sendText(chatId, text, mode = "HTML") {
  const url = `${API_URL}/sendMessage`;
  const payload = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({
      chat_id: chatId,
      text: text,
      parse_mode: mode,
    }),
    muteHttpExceptions: true,
    timeout: 5000 // 设置5秒超时
  };
  UrlFetchApp.fetch(url, payload);
}

function sendMessage(chatId, text, options) {
  const url = `${API_URL}/sendMessage`;
  const payload = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({
      chat_id: chatId,
      text: text,
      ...options,
    }),
    muteHttpExceptions: true,
    timeout: 5000 // 设置5秒超时
  };
  UrlFetchApp.fetch(url, payload);
}

内容的提问来源于stack exchange,提问作者Joey Perkins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 04:04:50