连接到 Telegram Bot API 的 webhook 出现问题

Posted

技术标签:

【中文标题】连接到 Telegram Bot API 的 webhook 出现问题【英文标题】:Got problems with webhook to Telegram Bot API 【发布时间】:2016-01-23 00:51:09 【问题描述】:

为什么我的 webhook 不工作?我没有从电报机器人 API 获得任何数据。这是我的问题的详细解释:

我从 StartSSL 获得 SSL 证书,它在我的网站上运行良好(根据 GeoCerts SSL checker),但我的 Telegram Bot API 的 webhook 似乎仍然不起作用(尽管它说设置了 webhook 我没有得到任何数据)。

我正在以这种形式在我的网站上为我的脚本制作一个 webhook:

https://api.telegram.org/bot<token>/setWebhook?url=https://mywebsite.com/path/to/giveawaysbot.php

我收到以下回复:

"ok":true,"result":true,"description":"Webhook was set"

所以它一定可以工作,但实际上没有。

这是我的脚本代码:

<?php 

ini_set('error_reporting', E_ALL);

$botToken = "<token>";
$website = "https://api.telegram.org/bot".$botToken;

$update = file_get_contents('php://input');
$update = json_decode($update);

print_r($update); // this is made to check if i get any data or not

$chatId = $update["message"]["chat"]["id"];
$message = $update["message"]["text"];


switch ($message) 
    case "/test":
        sendMessage($chatId,"test complete");
        break;
    case "/hi":
        sendMessage($chatId,"hey there");
        break;
    default:
        sendMessage($chatId,"nono i dont understand you");



function sendMessage ($chatId, $message) 
    $url = $GLOBALS[website]."/sendMessage?chat_id=".$chatId."&text=".urlencode($message);
    file_get_contents($url);


?>

我实际上没有收到任何要 $update 的数据。所以 webhook 不工作。为什么?

【问题讨论】:

可能与未获取任何数据有关,您应该使用json_decode($update, true) 将数据作为数组获取,而不是stdClass ixchi, json_decode($update, true) 没有任何改变,仍然无法正常工作:\ 你确定你真的收到了 webhook 吗?它对我来说工作正常。 ixchi,在我的帖子开头我写道:“为什么我的 webhook 不起作用?我没有从电报机器人 API 获得任何数据。所以是的,我不确定我是否收到了 webhook,我也不知道为什么,因为 SSL 和其他东西似乎没问题... 有什么消息吗?我有同样的问题,尝试了不同的证书提供者,将完整的证书链插入我的 nginx。但是,它仍然无法正常工作,并且我没有收到任何来自 api 的更新。 【参考方案1】:

可能是 SSL 证书。我遇到了同样的问题:Webhook 已确认,但实际上 SSL 证书失效了。

这个 reddit 帖子很有帮助:https://www.reddit.com/r/Telegram/comments/3b4z1k/bot_api_recieving_nothing_on_a_correctly/

【讨论】:

【参考方案2】:

我有类似的问题。现在解决了。 问题可能出在错误的公共证书中。请遵循我在项目中提出的注意说明:

https://github.com/solyaris/BOTServer/blob/master/wiki/usage.md#step-4-create-self-signed-certificate

openssl req -newkey rsa:2048 -sha256 -nodes -keyout /your_home/BOTServer/ssl/PRIVATE.key -x509 -days 365 -out /your_home/BOTServer/ssl/PUBLIC.pem -subj "/C=IT/ST=state/L=location/O=description/CN=your_domain.com"

Telegram setWebhooks API 不会检查您的自签名数字证书中的数据,即使您没有指定有效的 /CN,也会返回“ok”!所以要小心生成包含 /CN=your_domain 对应于您的 REAL HOST 域名的公共 .pem 证书!

【讨论】:

如何获得真正的HOT域名?可以只是服务器的ip吗? @RodrigoFormighieri REAL HOST == 公共 IP【参考方案3】:

我遇到了这个问题。我试图到处寻找并找不到解决问题的方法,因为人们一直在说问题出在 SSL 证书上。但是我发现了问题,代码中缺少很多东西来与涉及 curl 和这类东西的电报 API webhook 交互。在查看电报机器人文档的示例后,我解决了我的问题。看这个例子https://core.telegram.org/bots/samples/hellobot

<?php
//telegram example
define('BOT_TOKEN', '12345678:replace-me-with-real-token');
define('API_URL', 'https://api.telegram.org/bot'.BOT_TOKEN.'/');

function apiRequestWebhook($method, $parameters) 
  if (!is_string($method)) 
    error_log("Method name must be a string\n");
    return false;
  

  if (!$parameters) 
    $parameters = array();
   else if (!is_array($parameters)) 
    error_log("Parameters must be an array\n");
    return false;
  

  $parameters["method"] = $method;

  header("Content-Type: application/json");
  echo json_encode($parameters);
  return true;


function exec_curl_request($handle) 
  $response = curl_exec($handle);

  if ($response === false) 
    $errno = curl_errno($handle);
    $error = curl_error($handle);
    error_log("Curl returned error $errno: $error\n");
    curl_close($handle);
    return false;
  

  $http_code = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE));
  curl_close($handle);

  if ($http_code >= 500) 
    // do not wat to DDOS server if something goes wrong
    sleep(10);
    return false;
   else if ($http_code != 200) 
    $response = json_decode($response, true);
    error_log("Request has failed with error $response['error_code']: $response['description']\n");
    if ($http_code == 401) 
      throw new Exception('Invalid access token provided');
    
    return false;
   else 
    $response = json_decode($response, true);
    if (isset($response['description'])) 
      error_log("Request was successfull: $response['description']\n");
    
    $response = $response['result'];
  

  return $response;


function apiRequest($method, $parameters) 
  if (!is_string($method)) 
    error_log("Method name must be a string\n");
    return false;
  

  if (!$parameters) 
    $parameters = array();
   else if (!is_array($parameters)) 
    error_log("Parameters must be an array\n");
    return false;
  

  foreach ($parameters as $key => &$val) 
    // encoding to JSON array parameters, for example reply_markup
    if (!is_numeric($val) && !is_string($val)) 
      $val = json_encode($val);
    
  
  $url = API_URL.$method.'?'.http_build_query($parameters);

  $handle = curl_init($url);
  curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
  curl_setopt($handle, CURLOPT_TIMEOUT, 60);

  return exec_curl_request($handle);


function apiRequestJson($method, $parameters) 
  if (!is_string($method)) 
    error_log("Method name must be a string\n");
    return false;
  

  if (!$parameters) 
    $parameters = array();
   else if (!is_array($parameters)) 
    error_log("Parameters must be an array\n");
    return false;
  

  $parameters["method"] = $method;

  $handle = curl_init(API_URL);
  curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
  curl_setopt($handle, CURLOPT_TIMEOUT, 60);
  curl_setopt($handle, CURLOPT_POSTFIELDS, json_encode($parameters));
  curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));

  return exec_curl_request($handle);


function processMessage($message) 
  // process incoming message
  $message_id = $message['message_id'];
  $chat_id = $message['chat']['id'];
  if (isset($message['text'])) 
    // incoming text message
    $text = $message['text'];

    if (strpos($text, "/start") === 0) 
      apiRequestJson("sendMessage", array('chat_id' => $chat_id, "text" => 'Hello', 'reply_markup' => array(
        'keyboard' => array(array('Hello', 'Hi')),
        'one_time_keyboard' => true,
        'resize_keyboard' => true)));
     else if ($text === "Hello" || $text === "Hi") 
      apiRequest("sendMessage", array('chat_id' => $chat_id, "text" => 'Nice to meet you'));
     else if (strpos($text, "/stop") === 0) 
      // stop now
     else 
      apiRequestWebhook("sendMessage", array('chat_id' => $chat_id, "reply_to_message_id" => $message_id, "text" => 'Cool'));
    
   else 
    apiRequest("sendMessage", array('chat_id' => $chat_id, "text" => 'I understand only text messages'));
  



define('WEBHOOK_URL', 'https://my-site.example.com/secret-path-for-webhooks/');

if (php_sapi_name() == 'cli') 
  // if run from console, set or delete webhook
  apiRequest('setWebhook', array('url' => isset($argv[1]) && $argv[1] == 'delete' ? '' : WEBHOOK_URL));
  exit;



$content = file_get_contents("php://input");
$update = json_decode($content, true);

if (!$update) 
  // receive wrong update, must not happen
  exit;


if (isset($update["message"])) 
  processMessage($update["message"]);

?>

【讨论】:

我在很多地方读过问题是我的证书。当我尝试这个例子时,它完美地工作。非常感谢您指出这一点。【参考方案4】:

这是因为你没有这样设置证书

curl -F "url=https://bot.sapamatech.com/tg" -F "certificate=@/etc/apache2/ssl/bot.pem" https://api.telegram.org/bot265033849:AAHAs6vKVlY7UyqWFUHoE7Toe2TsGvu0sf4/setWebhook

查看link 了解如何设置电报自签名证书

【讨论】:

【参考方案5】:

这可能对使用 Laravel Telegram SDK 的人有所帮助。 我在 Laravel 5.3 中遇到了自签名 webhook 的问题。 设置并从 Telegram 获得“Webhook 已设置”消息的 OK 结果后,它不起作用。 该问题与 CSRF 验证有关。所以我将 webhook url 添加到 CSRF 异常中,现在一切都像魅力一样工作。

【讨论】:

【参考方案6】:

试试这个代码。如果您的网络主机上有一个有效的 SSL 并且您已经正确运行了 setWebhook,它应该可以工作(对我有用)。确保你创建了一个名为“log.txt”的文件并给它写权限:

<?php 
define('BOT_TOKEN', '????');
define('API_URL', 'https://api.telegram.org/bot'.BOT_TOKEN.'/');

// read incoming info and grab the chatID
$content    = file_get_contents("php://input");
$update     = json_decode($content, true);
$chatID     = $update["message"]["chat"]["id"];
$message    = $update["message"]["text"];

// compose reply
$reply ="";
switch ($message) 
    case "/start":
        $reply =  "Welcome to Siamaks's bot. Type /help to see commands";
        break;
    case "/test":
        $reply =  "test complete";
        break;
    case "/hi":
        $reply =  "hey there";
        break;
    case "/help":
        $reply =  "commands: /start , /test , /hi , /help ";
        break;
    default:
        $reply =  "NoNo, I don't understand you";


// send reply
$sendto =API_URL."sendmessage?chat_id=".$chatID."&text=".$reply;
file_get_contents($sendto);

// Create a debug log.txt to check the response/repy from Telegram in JSON format.
// You can disable it by commenting checkJSON.
checkJSON($chatID,$update);
function checkJSON($chatID,$update)

    $myFile = "log.txt";
    $updateArray = print_r($update,TRUE);
    $fh = fopen($myFile, 'a') or die("can't open file");
    fwrite($fh, $chatID ."nn");
    fwrite($fh, $updateArray."nn");
    fclose($fh);

【讨论】:

【参考方案7】:

我也遇到了这个问题,不知何故电报没有运行我的机器人,所以我尝试更新证书并再次设置网络挂钩,但它再次不起作用,所以我更新了我的 VPS(yum update)然后更新我的证书并再次设置网络挂钩。在这些之后它又开始工作了。

【讨论】:

【参考方案8】:

我也有这个问题。就我而言,声明我的 API 方法是错误的。我一开始创建了 GET 方法而不是 POST。

@api.route('/my-webhook-url')
class TelegramWebhook(Resource):
    def post(self):  # POST, Carl!
        # ...
        return response

【讨论】:

【参考方案9】:

再等一下,为什么您的 webhook 不起作用

就我而言,原因在于 allowed_updates webhook 参数。

通过调用:

https://api.telegram.org/bot<your_bot_token>/getWebhookInfo

你可以看到


  "ok": true,
  "result": 
    "url": "<your webhook url should be here>",
    "has_custom_certificate": false,
    "pending_update_count": 0,
    "max_connections": 40,
    "allowed_updates": [          
      "callback_query"
    ]
  

这意味着,您的机器人无法对您的短信做出反应,并且您将不会收到任何 webhook

您可以注意到,“allowed_updates”包含数组。因此,目前它只会对内联按钮事件做出反应(作为键盘布局传递!)。根据setWebhook 文档,allowed_updates 是一个“可选”参数。

要开始接收短信,您需要将“message”添加到“allowed_updates”属性中。为此,只需再次设置您的 webhook 并将其添加到查询中。喜欢这里:

https://api.telegram.org/bot<your_token>/setWebHook?url=<your_url>&allowed_updates=["callback_query","message"]

您会收到类似“url 已添加”的信息,但不用担心,即使在这种情况下,allowed_updates 也会更新。只需尝试将您的消息键入机器人并测试您的 webhook。

就是这样,现在,telegram 将向您发送给您的机器人的每条直接消息发送 webhook。希望,它可以帮助某人。

【讨论】:

你说的很对!当我执行 getWebhookInfo 时,我没有在 JSON 响应中看到“allowed_updates”键,更新后我能够获取消息。这个帖子应该更高。【参考方案10】:

在我的情况下,错误是由于 PHP 配置(使用 cPanel)引起的

[26-Jan-2021 09:38:17 UTC] PHP Warning: file_get_contents(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /home/myUsername/public_html/mydomain.com/my_bot_file.php on line 40

[26-Jan-2021 09:38:17 UTC] PHP Warning: file_get_contents(https://api.telegram.org/bot&lt;my-bot-id&gt;/sendmessage?chat_id=647778451&amp;amp;text=hello charlie ! k99 ): failed to open stream: no suitable wrapper could be found in /home/myUsername/public_html/myDomain.com/my_bot_file.php on line 40

所以 - 这是非常不言自明的。

php 配置中的allow_url_fopen=0 var 实际上禁用了所需的操作。

但无论如何,最好的办法是查看服务器上的error log,看看脚本或服务器配置中是否还有其他错误。

【讨论】:

以上是关于连接到 Telegram Bot API 的 webhook 出现问题的主要内容,如果未能解决你的问题,请参考以下文章

node-telegram-bot-api 中的错误未找到模块:无法解析 node-telegram-bot-api 中的“fs”、“net”、“tls”

Telegram API vs Bot API [关闭]

如何使用 Telegram Bot API 获取 Telegram 频道用户列表

使用 Express、Socket.io 和 Node-Telegram-Bot-Api 结束 Mocha 测试

如何向 Telegram bot API 发送请求?

Telegram 通过 bot api 将成员添加到频道