You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于GAS开发的Telegram机器人:如何捕获私聊回复实现对话式参数交互?

核心结论

完全不用切换到Polling模式,Webhook下就能实现你要的对话式交互!关键在于解决会话状态持久化非命令消息的处理这两个问题,下面给你一步步拆解方案:

1. 解决GAS易失性:用持久化存储会话状态

GAS的内存变量确实会在脚本执行结束后清空,但我们可以用Google提供的持久化存储方案来保存用户的会话状态,推荐两种方式:

  • Properties Service:轻量且易用,适合存储简单的会话状态(比如当前需要收集的参数类型)。可以用PropertiesService.getUserProperties()为每个用户单独存储状态,键可以设为chatId_state(比如123456_state),值设为类似waiting_for_read_rangewaiting_for_write_data这样的标记。
  • Google Sheet专用表:如果需要存储更复杂的状态(比如临时保存已输入的部分参数),可以在你的交互Sheet里新增一个“会话状态”工作表,每行记录chatIdcurrentStatetimestamptempData等字段。

2. 捕获用户回复:在doPost里处理所有消息

Telegram的Webhook会把用户发送的所有消息(包括纯文本回复、命令)都推送到你的doPost函数,所以你不需要依赖斜杠命令来触发脚本——只要在doPost里解析收到的update对象,结合用户的会话状态,就能判断该做什么:

具体逻辑流程:

  1. 解析update,提取chatId、用户发送的文本textuserId等信息。
  2. 从持久化存储中读取该用户的当前会话状态。
  3. 如果状态为空:判断用户发的是/read还是/write,设置对应的会话状态,然后发送第一个参数的询问。
  4. 如果状态不为空:根据状态标记,把用户的纯文本回复当作对应参数处理,完成后更新状态(比如切换到下一个参数的收集状态,或者完成操作后清空状态)。

3. 关于隐私模式的疑问

在私聊场景下,不管机器人的隐私模式设置如何,机器人都能收到用户发送的所有消息——这是Telegram的固有特性,所以你完全不需要修改隐私模式,私聊回复会正常触发doPost

4. 为什么不用Polling模式?

Polling模式需要定时触发GAS脚本去拉取Telegram的getUpdates接口,但GAS的脚本执行时间限制是6分钟,超时会被强制终止,反而容易出现交互中断的问题。而Webhook是事件驱动,只有用户发消息时才会触发脚本,效率更高,也更稳定。

简单代码示例

下面是一个简化版的实现,用Properties Service存储状态:

function doPost(e) {
  const update = JSON.parse(e.postData.contents);
  const chatId = update.message.chat.id;
  const text = update.message.text.trim();
  
  // 获取用户专属的属性存储
  const userProps = PropertiesService.getUserProperties();
  const currentState = userProps.getProperty(`state_${chatId}`);

  if (!currentState) {
    // 初始状态:处理命令
    if (text === '/read') {
      userProps.setProperty(`state_${chatId}`, 'waiting_for_read_range');
      sendTgMessage(chatId, '请输入要读取的表格范围(比如A1:B5):');
    } else if (text === '/write') {
      userProps.setProperty(`state_${chatId}`, 'waiting_for_write_range');
      sendTgMessage(chatId, '请输入要写入的表格范围(比如A1:C1):');
    } else {
      sendTgMessage(chatId, '请发送 /read 或 /write 开始操作哦~');
    }
  } else {
    // 会话中:根据状态处理用户回复
    switch(currentState) {
      case 'waiting_for_read_range':
        try {
          const sheet = SpreadsheetApp.openById('你的SheetID').getActiveSheet();
          const data = sheet.getRange(text).getValues();
          sendTgMessage(chatId, `读取到的数据:\n${JSON.stringify(data).replace(/,/g, '\n')}`);
        } catch(err) {
          sendTgMessage(chatId, '范围格式不对,请重新输入(比如A1:B5):');
          // 保持当前状态,让用户重新输入
          return;
        }
        // 清空状态
        userProps.deleteProperty(`state_${chatId}`);
        break;

      case 'waiting_for_write_range':
        // 临时保存范围,切换到收集数据的状态
        userProps.setProperty(`temp_write_range_${chatId}`, text);
        userProps.setProperty(`state_${chatId}`, 'waiting_for_write_data');
        sendTgMessage(chatId, '请输入要写入的数据(用逗号分隔,比如"张三,25,北京"):');
        break;

      case 'waiting_for_write_data':
        try {
          const writeRange = userProps.getProperty(`temp_write_range_${chatId}`);
          const dataArray = text.split(',').map(item => item.trim());
          const sheet = SpreadsheetApp.openById('你的SheetID').getActiveSheet();
          sheet.getRange(writeRange).setValues([dataArray]); // 单行数据需转为二维数组
          sendTgMessage(chatId, '数据已成功写入!😎');
        } catch(err) {
          sendTgMessage(chatId, '数据格式不对,请重新输入(比如"张三,25,北京"):');
          return;
        }
        // 清空状态和临时数据
        userProps.deleteProperty(`state_${chatId}`);
        userProps.deleteProperty(`temp_write_range_${chatId}`);
        break;
    }
  }

  return ContentService.createTextOutput(JSON.stringify({ok: true})).setMimeType(ContentService.MimeType.JSON);
}

// 发送Telegram消息的辅助函数
function sendTgMessage(chatId, text) {
  const botToken = '你的机器人Token';
  const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
  UrlFetchApp.fetch(url, {
    method: 'POST',
    contentType: 'application/json',
    payload: JSON.stringify({ chat_id: chatId, text: text })
  });
}

额外注意事项

  • 会话超时:可以在存储状态时同时记录时间戳,每次处理消息时检查是否超过设定的超时时间(比如10分钟),如果超时就自动清空状态,避免用户长时间不操作导致状态残留。
  • 错误处理:要捕获Sheet操作的异常(比如无效范围、数据格式错误),提示用户重新输入并保持当前状态,提升用户体验。
  • 属性大小限制:Properties Service的单个属性值不能超过9KB,如果需要存储大量临时数据,建议用Google Sheet的会话状态表来替代。

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

火山引擎 最新活动