首页 > 解决方案 > 需要防止 GAS Web 应用实例发生冲突

问题描述

我正在使用 Google Apps 脚本为我的 Telegram 机器人创建一个网络应用程序。机器人通过网络钩子调用网络应用程序。

我的机器人有一个内联键盘,你可以在下面的 GIF 1 中看到它。点击键盘按钮,您可以输入出现在屏幕上的代码。

我的问题是当我更快地点击按钮时,对 Web 应用程序的回调查询会发生冲突,从而导致输入混乱。见 GIF 2。

我一直在绞尽脑汁试图找出一种方法来防止网络应用程序实例发生冲突,但到目前为止我还没有运气。

我在下面发布我的代码。请帮忙。

我看到它的方式是,脚本的每个实例都需要更多时间才能完成,然后才能开始下一个。我已经尽我所能尝试使用 async/await 和 lockService 。有人建议我尝试对查询进行排队,但遗憾的是无法使其工作。

动图 1

动图 2

var lock = LockService.getScriptLock();
    
function doPost(e){

      var contents = JSON.parse(e.postData.contents);
   
      var query_id = contents.callback_query.id;
        
      var mes_id = contents.callback_query.message.message_id;
        
      var userinput = contents.callback_query.data;
        
      var message_now = contents.callback_query.message.text;
      
      var inline_keyboard = contents.callback_query.message.reply_markup;  
      
  
      var message_upd = message_now + " " + userinput;
           
        var keydata = {
            method: "post",
            payload: {
              method: "editMessageText",
              chat_id: String(chat_id),
              message_id: mes_id,
              text: message_upd ,
              parse_mode: "HTML",
              reply_markup: JSON.stringify(inline_keyboard)
            }
          }
   
    lock.waitLock(10000);

    UrlFetchApp.fetch('https://api.telegram.org/bot' + token + '/', keydata); 
        
    UrlFetchApp.fetch(url + "/answerCallbackQuery?callback_query_id=" + query_id);
      
    lock.releaseLock();

    } 

标签: google-apps-scriptweb-applicationslockingtelegram-bottelegram-webhook

解决方案


问题:

我相信lock确实有效。问题可能是电报机器人发送的回调查询。在你的第二个 gif 中,

在按 2、3 和 4 时,它所附加的消息是空的。因此,所有 4 个回调message.text都将为空

var message_now = contents.callback_query.message.text; 

message_now对于所有 4 条消息都是空的,并且所有 4 条消息mesage_upd都会不同:

var message_upd = message_now + " " + userinput;

即使您使用 对所有服务器端进行排队LockService,如果message_now电报提供的所有 4 条消息都为空,则排队对于创建这样的连接字符串是无用的。

可能的解决方案):

  • 排队回调应该在客户端完成。只有在收到第一个按钮按下的响应后,才应该激活第二个回调。我不确定电报是否提供如此精细的控制。但如果是这样,这是首选的解决方案。

  • 使用缓存服务服务器端将message_now特定message.id范围内的最后一个缓存到特定用户。将其保存到缓存服务 30 秒左右。如果另一个具有相同 message_id 的回调在 30 秒后出现并且 message.text 为空,请改用缓存的消息。

    • key: 某种类型message_iduser_id组合
    • value: 当前串联message_now

片段:

  let message_now = contents.callback_query.message.text;
  if (message_now === '') message_now = cache.get(String(mes_id)) || '';
  /*....*/
  cache.put(String(mes_id), String(message_upd), 30);

function doPost(e) {
  const lock = LockService.getScriptLock();
  lock.waitLock(10000);
  const cache = CacheService.getScriptCache();
  const contents = JSON.parse(e.postData.contents);
  const query_id = contents.callback_query.id;
  const mes_id = contents.callback_query.message.message_id;
  const userinput = contents.callback_query.data;
  let message_now = contents.callback_query.message.text;
  if (message_now === '') message_now = cache.get(String(mes_id)) || '';
  const inline_keyboard = contents.callback_query.message.reply_markup;
  const message_upd = message_now + ' ' + userinput;
  const keydata = {
    method: 'post',
    payload: {
      method: 'editMessageText',
      chat_id: String(chat_id),
      message_id: mes_id,
      text: message_upd,
      parse_mode: 'HTML',
      reply_markup: JSON.stringify(inline_keyboard),
    },
  };
  UrlFetchApp.fetch('https://api.telegram.org/bot' + token + '/', keydata);
  UrlFetchApp.fetch(url + '/answerCallbackQuery?callback_query_id=' + query_id);
  cache.put(String(mes_id), String(message_upd), 30);
  lock.releaseLock();
}

参考:


推荐阅读