5

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

在服务器停止之前,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);

提前致谢。

4

4 回答 4

3

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

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

于 2015-07-29T19:56:42.370 回答
3

最好的方法是使用update_id在每个新请求(即更新)时增加的特定数字。如何实施?

首先,让我们从以下匿名类开始(使用 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)。在这两种情况下都应该使用上面的代码(或类似的)。

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

长轮询方法(默认)

通过这种方式,您将 HTTPS 请求发送到 Telegram bot API,并且您将在 JSON 格式的对象中获得更新作为响应。因此,可以完成以下工作来获得新的更新(API为什么使用 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 方法,这是目前获取更新的最佳方式。

最初,您必须使用以下请求(更多详细信息)为您的机器人启用 WebHooks:

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

替换<token>为您的机器人令牌,以及<file>将接受新请求的文件的地址。同样,它必须是 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 建议的版本使这个答案更加完整和有用。

于 2017-06-16T21:22:42.857 回答
2

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

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

我有同样的问题,然后我尝试使用重置默认 webhook

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

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

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

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

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

于 2015-11-11T14:58:28.763 回答