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

如何判断Telegram Webhook接收的媒体组事件是否为最后一个?

Telegram机器人转发相册媒体组的实现困境

背景

用Google Apps Script开发Telegram聊天机器人,已配置Webhook接收事件,无法使用logger.log输出日志,因此通过Telegram API的sendMessage方法记录事件,用doPost处理请求。需求是实现机器人转发群组中上传的相册形式照片组,但在使用Telegram API的sendMediaGroup方法时遇到问题。

核心问题

sendMediaGroup需要传入chat_id和media参数,其中media是InputMediaPhoto(对应照片场景)组成的数组。但Telegram API在接收相册时,会返回与照片数量相等的独立事件——每个事件包含相同的media_group_id,但仅携带单张照片的属性,而非包含完整媒体组的单个事件。因此需要逐个从事件中提取InputMediaPhoto存入数组,但无法判断当前事件是否为该媒体组的最后一个,导致无法确定何时调用sendMediaGroup执行转发。

现有代码框架

// 声明bot token、设置webhook、声明myID为机器人ID 
// 声明sendMessage、sendMediaGroup等函数
// 主逻辑通过doPost响应事件

var cache = CacheService.getScriptCache();

var msgIDstring = cache.get("msgID");

var mediastring = cache.get("mymedia");
if (mediastring == null){
  var media = []
}
else{
  var media = JSON.parse(mediastring);
}

function doPost(e) {
 
  let update = JSON.parse(e.postData.contents);
  
  let chat_id = update.message.chat.id; // 群组chat id

  sendMessage(myID, JSON.stringify(update), 'HTML');

if (update.message.media_group_id == null) {

// 处理其他逻辑

}

  else {
    
    if (msgIDstring == null){
      cache.put("msgID",String(update.message.message_id));
      let photo_id = String(update.message.photo[0].file_id);
      let InputMediaPhoto = {
      'type': "photo",
      'media': photo_id
    }
    
    media.push(InputMediaPhoto);
    cache.put("mymedia",JSON.stringify(media));
    }   
    
    else {
      let i = update.message.message_id - Number(msgIDstring);
       
      while (i==1) {
        
        let photo_id = String(update.message.photo[0].file_id);
        let InputMediaPhoto = {
        'type': "photo",
        'media': photo_id
        };    
        media.push(InputMediaPhoto); 
        sendMessage(myID, JSON.stringify(media), 'HTML');       
        cache.put("mymedia",JSON.stringify(media));        
        cache.put("msgID",String(update.message.message_id));

        if (i==0){
          break;
        }
      }
             
      sendMessage(myID, String(i), 'HTML'); // 此处会发送"0",逻辑卡在这
      sendMessage(myID, cache.get("mymedia"), 'HTML');
      sendMediaGroup(chat_id,media);   
                      
    }
    
  }  
  //sendMessage(myID, JSON.stringify(media), 'HTML');
}

现有代码的问题

当前用message_id差值判断是否为同组后续事件的逻辑不成立——Telegram的message_id不一定是连续递增1的,且无法通过这个方式判断是否是最后一个事件。实际测试中i的值为0,导致循环不执行,直接触发sendMediaGroup,此时media数组并未收集全所有照片。

解决方案

1. 核心思路

  • 以media_group_id作为缓存键,同一个媒体组的所有事件共享唯一ID,以此归集同组照片。
  • 引入超时机制:Telegram发送同媒体组事件的间隔通常很短,设置5秒超时,超时后认定该组所有事件已接收,执行转发并清理缓存。

2. 修改后的代码示例

// 声明bot token、设置webhook、声明myID为机器人ID 
// 声明sendMessage、sendMediaGroup等函数

const CACHE_TTL = 10; // 缓存有效期10秒,留足够时间接收同组事件
const TIMEOUT_DELAY = 5000; // 超时延迟5秒

function doPost(e) {
  let update = JSON.parse(e.postData.contents);
  let message = update.message;
  
  // 记录事件日志
  sendMessage(myID, JSON.stringify(update), 'HTML');

  if (!message.media_group_id) {
    // 处理非媒体组消息逻辑
    return ContentService.createTextOutput(JSON.stringify({status: "ok"})).setMimeType(ContentService.MimeType.JSON);
  }

  const cache = CacheService.getScriptCache();
  const mediaGroupId = message.media_group_id;
  const chatId = message.chat.id;

  // 从缓存获取当前媒体组的照片数组
  let media = [];
  const cachedMedia = cache.get(`media_${mediaGroupId}`);
  if (cachedMedia) {
    media = JSON.parse(cachedMedia);
  }

  // 添加当前照片到数组(取最高分辨率的照片)
  const photoId = message.photo[message.photo.length - 1].file_id;
  media.push({
    type: "photo",
    media: photoId
  });

  // 更新缓存
  cache.put(`media_${mediaGroupId}`, JSON.stringify(media), CACHE_TTL);
  cache.put(`chat_${mediaGroupId}`, chatId, CACHE_TTL);

  // 检查是否已设置该媒体组的超时触发器
  const triggerKey = `trigger_${mediaGroupId}`;
  if (!cache.get(triggerKey)) {
    // 设置触发器,5秒后执行转发
    ScriptApp.newTrigger('processMediaGroup')
      .timeBased()
      .after(TIMEOUT_DELAY)
      .create();
    cache.put(triggerKey, "set", CACHE_TTL);
  }

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

function processMediaGroup(e) {
  const triggerId = e.triggerUid;
  // 删除当前触发器
  ScriptApp.deleteTrigger(ScriptApp.getTriggerById(triggerId));

  const cache = CacheService.getScriptCache();
  // 遍历缓存中的所有媒体组键
  const allKeys = cache.getAllKeys();
  const mediaGroupKeys = allKeys.filter(key => key.startsWith('media_'));

  for (const key of mediaGroupKeys) {
    const mediaGroupId = key.replace('media_', '');
    const media = JSON.parse(cache.get(key));
    const chatId = cache.get(`chat_${mediaGroupId}`);

    if (media && chatId) {
      // 执行转发
      sendMediaGroup(chatId, media);
      // 清理缓存
      cache.remove(`media_${mediaGroupId}`);
      cache.remove(`chat_${mediaGroupId}`);
      cache.remove(`trigger_${mediaGroupId}`);
    }
  }
}

关键说明

  • 用media_group_id作为缓存键,确保同组照片被正确归集。
  • 设置超时触发器,等待足够时间接收同组所有事件后再执行转发。
  • 触发器执行时清理对应缓存和自身,避免重复执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 21:57:55