【问题标题】:Prevent getting old updates from Telegram Bot API using a web hook防止使用网络挂钩从 Telegram Bot API 获取旧更新
【发布时间】:2015-10-20 20:03:33
【问题描述】:

我正在编写一个 Telegram 机器人,我正在使用 official bot API。我有一个 webhook 服务器来处理请求并为每个请求发送一个 200 OK 响应。

在服务器停止之前,webhook 已分离,因此 Telegram 不再发送更新。但是,每当我打开机器人并再次设置 webhook URL 时,Telegram 就会开始用旧更新淹没 webhook 服务器。

有什么办法可以防止这种情况发生,而无需反复请求/getUpdates,直到我到达最后一次更新?

这是我的代码的一个高度简化的版本:

var http = require('http'),
    unirest = require('unirest'),
    token = '***';

// Attach the webhook
unirest.post('https://api.telegram.org/bot' + token + '/setWebhook')
    .field('url', 'https://example.com/api/update')
    .end();

process.on('exit', function() {
    // Detach the webhook
    unirest.post('https://api.telegram.org/bot' + token + '/setWebhook')
        .field('url', '')
        .end();
});

// Handle requests
var server = http.createServer(function(req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' })
    res.end('Thanks!');
});

server.listen(80);

提前致谢。

【问题讨论】:

    标签: telegram-bot


    【解决方案1】:

    当您的服务器启动时,您可以记录时间戳,然后使用它与传入消息date 值进行比较。如果日期 >= 您开始时的时间戳...则可以处理该消息。

    我不确定是否有办法告诉 Telegram 你只对新更新感兴趣,他们的重试机制是一项功能,因此不会错过消息......即使你的机器人离线。

    【讨论】:

    【解决方案2】:

    最好的方法是使用update_id,这是一个特定的数字,会随着每个新请求(即更新)而增加。如何实现?

    首先,让我们从以下anonymous class(使用PHP7)开始:

    $lastUpdateId = new class()
    {
        const FILE_PATH = "last-update-id.txt";
        private $value = 1;
    
        public function __construct()
        {
            $this->ensureFileExists();
            $this->value = filesize(self::FILE_PATH) == 0
                ? 0 : (int)(file_get_contents(self::FILE_PATH));
        }
    
        public function set(int $lastUpdateId)
        {
            $this->ensureFileExists();
            file_put_contents(self::FILE_PATH, $lastUpdateId);
            $this->value = $lastUpdateId;
        }
    
        public function get(): int
        {
            return $this->value;
        }
    
        public function isNewRequest(int $updateId): bool
        {
            return $updateId > $this->value;
        }
    
        private function ensureFileExists()
        {
            if (!file_exists(self::FILE_PATH)) {
                touch(self::FILE_PATH);
            }
        }
    };
    

    类的作用很明确:通过纯文件处理最后一个update_id

    注意:课程尽量简短。它不提供错误检查。改用您的自定义实现(例如,使用 SplFileObject 代替 file_{get|put}_contents() 函数)。

    现在,有两种获取更新的方法:Long Polling xor WebHooks(有关每种方法和所有 JSON 属性的更多详细信息,请查看 Telegram bot API)。上述代码(或类似代码)应在这两种情况下使用。

    注意:目前无法同时使用这两种方法。

    长轮询方法(默认)

    通过这种方式,您可以向 Telegram 机器人 API 发送 HTTPS 请求,并且您会在 JSON 格式的对象中获得更新作为响应。所以,可以做以下工作来获得新的更新(APIwhy using offset):

    $botToken = "<token>";
    
    $updates = json_decode(file_get_contents("https://api.telegram.org/bot{$botToken}/getUpdates?offset={$lastUpdateId->get()}"), true);
    
    // Split updates from each other in $updates
    // It is considered that one sample update is stored in $update
    
    // See the section below
    parseUpdate($update);
    

    WebHook 方法(首选)

    要求您的服务器支持 HTTPS POST 方法,这是即时获取更新的最佳方式。

    最初,您必须使用以下请求 (more details) 为您的机器人启用 WebHook:

    https://api.telegram.org/bot<token>/setWebhook?url=<file>
    

    &lt;token&gt; 替换为您的机器人令牌,将&lt;file&gt; 替换为您将接受新请求的文件的地址。同样,它必须是 HTTPS。

    好的,最后一步是在指定的 URL 创建文件:

    // The update is sent
    $update = $_POST;
    
    // See the section below
    parseUpdate($update);
    

    从现在开始,您的机器人的所有请求和更新都将直接发送到文件中。

    parseUpdate()的实现

    它的实现完全取决于您。然而,为了展示如何在实现中使用上面的类,这是一个示例和简短的实现:

    function parseUpdate($update)
    {
        // Validate $update, first
        // Actually, you should have a validation class for it
    
        // Here, we suppose that: $update["update_id"] !== null
        if ($lastUpdateId->isNewRequest($update["update_id"])) {
            $lastUpdateId->set($update["update_id"]);
            // New request, go on
        } else {
            // Old request (or possible file error)
            // You may throw exceptions here
        }
    }
    

    享受吧!

    编辑:感谢@Amir 提出的版本建议使这个答案更加完整和有用。

    【讨论】:

    • 对于 webhook,您不能使用 offsetgetUpdates 。我使用了没有 $updates 的代码,但同样的问题仍然存在......
    • @Amir 是的,对于 WebHooks,请求将直接发送到您的文件;因此,您应该改用$_REQUEST superglobal。
    • 感谢您的关注,您能否更新您的答案并为其添加 webhook 案例支持? @MAChitgarha
    • @Amir 我刚刚编辑了答案;换句话说,我做了一个完整的重写。希望对您有所帮助。
    • $_post 中查找update_id 时出现问题,并且未在此处定义。顺便说一句,如果您查看我的最后一个问题,如果您对此有任何建议,我将不胜感激,我会很高兴听到它。绝对是对这个答案的赞许以及我会在自己的问题中得到的答案:))
    【解决方案3】:

    在 webhook 模式下,Telegram 服务器每分钟发送一次更新,直到收到来自 webhook 程序的 OK 响应。 所以我推荐这些步骤:

    1. 检查您的 webhook 程序,您将其地址指定为 setWebhook 方法的 url 参数。在浏览器中调用它的地址。它不会产生要查看的输出,但会清除您的程序中可能没有错误。
    2. 在您的程序中包含一个生成“200 OK Status”标头输出的命令,以确保程序将此标头发送到 Telegram 服务器。

    【讨论】:

    • 你能提供几行代码吗?我试过你的意思,但我一直在获取回复../
    • 在php中你可以使用header("HTTP/1.1 200 OK");
    【解决方案4】:

    我有同样的问题,然后我尝试使用

    重置默认 webhook

    https://api.telegram.org/bot[mybotuniqueID]/setWebhook?url=

    之后,我验证了当前的 getUpdates 查询是相同的旧更新,但我通过电报的机器人聊天发送了新请求

    https://api.telegram.org/bot[mybotuniqueID]/getUpdates

    当我再次设置我的 webhook 时,webhook 会读取相同的旧更新。可能 getUpdates 方法没有刷新 JSON 内容。

    注意: 就我而言,它工作正常,直到我决定从 botfather 更改 /set 隐私机器人设置

    【讨论】:

      猜你喜欢
      • 2018-12-09
      • 1970-01-01
      • 2018-12-14
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 2016-02-23
      • 2020-11-23
      • 2020-06-20
      相关资源
      最近更新 更多