PHP Telegram Bot调用sendMessage时键盘不显示问题求助
Telegram Bot API 键盘不显示问题排查
问题现象
通过PHP向Telegram Bot API发送消息请求,服务器返回发送成功,但聊天窗口仅显示文本Work in progress...,配置的Reply Keyboard或Inline Keyboard完全不显示。
现有参数与代码
打印的参数数组
Array ( [chat_id] => (omitted) [text] => Work in progress... [reply_markup] => Array ( [keyboard] => Array ( [0] => Array ( [0] => Array ( [text] => btn ) ) ) ) )
请求发送核心代码
//... curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->arguments)); //... $result = curl_exec($ch);
补充完整代码
Message类示例
class Message extends Type { private $message_id; /* ... */ /* constructor, getters, setters... */ }
抽象Method类
abstract class Method { private $methodUrl = "https://api.telegram.org/bot"; private $method; private $arguments = array(); private $response; private function extractFields($container, $refclassmethods = null) { $iteratingOnMethod = !is_null($refclassmethods); if (is_object($container)) { $methods = get_class_methods($container); foreach ($methods as $method) { if (is_getter($method) && !is_null($container->$method()) && (!$iteratingOnMethod || in_array($method, $refclassmethods)) ) { $varname = lcfirst(str_replace("get", "", $method)); $val = $container->$method(); $ret[$varname] = $this->extractFields($val); } } } elseif (is_array($container)) { foreach ($container as $content) { $ret[] = $this->extractFields($content); } } else /* it is a "final", atomic value (int, string et similia) */ { return $container; } return $ret; } public function execute() { $refclass = new ReflectionClass($this::class); $this->method = $refclass->getShortName(); $this->arguments = ($this->extractFields($this, filterRefClassMethods($refclass))); $this->methodUrl .= API_TOKEN . "/" . $this->method . "?"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->methodUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->arguments)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); } function is_getter(string $method): string { return str_starts_with($method, "get"); } function filterRefClassMethods($refclass) { $methods = []; foreach ($refclass->getMethods() as $method) { if ($method->class === $refclass->getName()) { $methods[] = $method->name; } } return $methods; }
sendMessage类
class sendMessage extends Method { protected $chat_id; protected $message_thread_id; protected $text; /* ... */ /* getters and setters... */ }
问题原因
- API参数格式不匹配:Telegram API要求
reply_markup必须是JSON格式字符串,但当前用http_build_query()将PHP数组直接编码为URL格式(如reply_markup[keyboard][0][0][text]=btn),这种格式无法被API正确解析为键盘结构。 - 请求方式局限性:使用
application/x-www-form-urlencoded格式传递复杂嵌套参数时,容易出现解析异常,官方更推荐用application/json格式发送完整请求体。
解决方案
方案一:单独转换reply_markup为JSON(兼容现有请求格式)
在生成参数数组后,单独对reply_markup进行JSON编码:
public function execute() { $refclass = new ReflectionClass($this::class); $this->method = $refclass->getShortName(); $this->arguments = ($this->extractFields($this, filterRefClassMethods($refclass))); // 新增:将reply_markup转为JSON字符串 if (isset($this->arguments['reply_markup'])) { $this->arguments['reply_markup'] = json_encode($this->arguments['reply_markup']); } $this->methodUrl .= API_TOKEN . "/" . $this->method . "?"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->methodUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->arguments)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); }
方案二:改用JSON格式发送完整请求(官方推荐)
直接将整个参数数组转为JSON,设置正确的请求头,避免嵌套参数解析问题:
public function execute() { $refclass = new ReflectionClass($this::class); $this->method = $refclass->getShortName(); $this->arguments = ($this->extractFields($this, filterRefClassMethods($refclass))); // 修改URL:移除末尾的?,参数将放在POST体中 $this->methodUrl .= API_TOKEN . "/" . $this->method; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->methodUrl); curl_setopt($ch, CURLOPT_POST, true); // 发送JSON格式的请求体 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->arguments)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 设置正确的Content-Type头 curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json' ]); $result = curl_exec($ch); curl_close($ch); }
额外检查:键盘结构正确性
确保键盘结构符合官方规范,示例如下:
// Reply Keyboard 正确结构示例 $reply_markup = [ 'keyboard' => [ ['按钮1', '按钮2'], // 一行两个按钮 ['按钮3'] // 一行一个按钮 ], 'resize_keyboard' => true, // 可选:让键盘适配屏幕大小 'one_time_keyboard' => true // 可选:点击后自动隐藏键盘 ];
内容的提问来源于stack exchange,提问作者G. Ianni
相关产品推荐
相关产品推荐

