Telegram Bot内联键盘开发报错:Cannot read property 'chat' of undefined
解决Telegram Bot内联键盘的TypeError与回调关联问题
问题根源分析
你遇到的TypeError: Cannot read property 'chat' of undefined,核心原因是Telegram Webhook的请求结构不止一种:
- 当用户发送文本消息时,请求里包含
req.body.message - 但当用户点击内联键盘按钮时,Telegram发送的是
req.body.callback_query,此时req.body.message是undefined,直接访问req.body.message.chat.id就会抛出错误。
另外,你的代码目前只处理了手动输入的文本指令(比如/news),完全没有处理内联键盘的callback_data,所以点击键盘按钮后没有任何响应,无法关联到对应的帖子功能。
分步解决方案
1. 统一处理请求类型,获取核心数据
先判断请求是普通消息还是回调查询,统一拿到聊天ID和触发指令,避免直接访问不存在的属性:
// 在POST路由开头添加这段逻辑 let chatId, triggerText, username; if (req.body.message) { // 处理普通文本消息 chatId = req.body.message.chat.id; triggerText = req.body.message.text; username = req.body.message.from.username; console.log(`Utente ${username} in chat ${chatId} ha scritto '${triggerText}'`); } else if (req.body.callback_query) { // 处理内联键盘回调 const callbackQuery = req.body.callback_query; chatId = callbackQuery.message.chat.id; triggerText = callbackQuery.data; // 取按钮的callback_data,比如"/news" username = callbackQuery.from.username; console.log(`Utente ${username} in chat ${chatId} ha cliccato il bottone: '${triggerText}'`); // 必须回复回调查询,否则按钮会一直显示加载状态 axios.post(`https://api.telegram.org/bot${process.env.BOTTOKEN}/answerCallbackQuery`, { callback_query_id: callbackQuery.id }).catch(err => console.log('Errore nel risposta callback:', err)); } else { // 其他未知请求类型,直接返回 return res.end('ok'); }
2. 重构指令逻辑,复用代码
把重复的发送消息、获取内容逻辑提取成独立函数,让普通指令和回调按钮能触发相同的功能:
// 通用发送Telegram消息的函数 function sendTgMessage(chatId, text) { return axios.post(`https://api.telegram.org/bot${process.env.BOTTOKEN}/sendMessage`, { chat_id: chatId, text: text }); } // 发送新闻的复用函数 function sendNewsContent(chatId, res) { axios.get(URL, { params: { categories: '8240' } }) .then(response => { // 用Promise.all批量处理消息发送,避免重复调用res.end const messageTasks = response.data.map(item => { const content = `${item.title.rendered}\n\t${item.link}\n\tAutore : ${item.author}`; return sendTgMessage(chatId, content); }); Promise.all(messageTasks) .then(() => { console.log('Tutte le news sono state inviate'); res.end('ok'); }) .catch(err => { console.log('Errore nell\'invio delle news:', err); res.end('Errore'); }); }) .catch(err => { console.log('Errore nel recupero delle news:', err); res.end('Errore'); }); } // 同理可以提取发送recensioni、guide、games的函数...
3. 修复代码中的细节问题
- 最后一个else分支里,
axios.post的URL少了闭合引号,修正为:'https://api.telegram.org/bot'+ process.env.BOTTOKEN+'/sendMessage', - 不要在循环里调用
res.end('ok'),因为res.end只能执行一次,会导致后续请求报错,改用Promise.all批量处理后统一结束响应 - 设置Webhook的逻辑不需要每次POST请求都执行,移到应用启动时执行一次即可
修改后的完整代码示例
const util = require('util'); const express = require('express'); const app = express(); const axios = require('axios'); const bodyparser = require('body-parser'); app.use(bodyparser.json()); app.get('/', function (req, res) { res.status(200).json({ status: 'ok' }); }); // 通用发送Telegram消息函数 function sendTgMessage(chatId, text) { return axios.post(`https://api.telegram.org/bot${process.env.BOTTOKEN}/sendMessage`, { chat_id: chatId, text: text }); } // 发送新闻内容 function sendNewsContent(chatId, res) { axios.get(URL, { params: { categories: '8240' } }) .then(response => { const messageTasks = response.data.map(item => { const content = `${item.title.rendered}\n\t${item.link}\n\tAutore : ${item.author}`; return sendTgMessage(chatId, content); }); Promise.all(messageTasks) .then(() => { console.log('Tutte le news inviate con successo'); res.end('ok'); }) .catch(err => { console.log('Errore nell\'invio delle news:', err); res.end('Errore'); }); }) .catch(err => { console.log('Errore nel recupero delle news:', err); res.end('Errore'); }); } // 发送评测内容(示例) function sendRecensioniContent(chatId, res) { axios.get(URL, { params: { categories: '8241' } }) .then(response => { const messageTasks = response.data.map(item => { const content = `${item.title.rendered}\n\t${item.link}\n\tAutore : ${item.author}`; return sendTgMessage(chatId, content); }); Promise.all(messageTasks) .then(() => { console.log('Tutte le recensioni inviate con successo'); res.end('ok'); }) .catch(err => { console.log('Errore nell\'invio delle recensioni:', err); res.end('Errore'); }); }) .catch(err => { console.log('Errore nel recupero delle recensioni:', err); res.end('Errore'); }); } app.post('/', function (req, res) { console.log("Richiesta ricevuta: " + JSON.stringify(req.body)); let chatId, triggerText, username; // 处理两种请求类型 if (req.body.message) { chatId = req.body.message.chat.id; triggerText = req.body.message.text; username = req.body.message.from.username; console.log(`Utente ${username} in chat ${chatId} ha scritto '${triggerText}'`); } else if (req.body.callback_query) { const callbackQuery = req.body.callback_query; chatId = callbackQuery.message.chat.id; triggerText = callbackQuery.data; username = callbackQuery.from.username; console.log(`Utente ${username} in chat ${chatId} ha cliccato il bottone: '${triggerText}'`); // 回复回调查询,避免按钮加载状态 axios.post(`https://api.telegram.org/bot${process.env.BOTTOKEN}/answerCallbackQuery`, { callback_query_id: callbackQuery.id }).catch(err => console.log('Errore risposta callback:', err)); } else { console.log('Tipo di richiesta sconosciuto'); return res.end('ok'); } // 指令处理逻辑 if (triggerText.match(/start/i)) { const welcomeText = `Benvenuto ${username} in ViktecNews, scopri le ultime news sul mondo della tecnologia, guide e recensioni. Per qualsiasi cosa vai su info`; axios.post(`https://api.telegram.org/bot${process.env.BOTTOKEN}/sendMessage`, { chat_id: chatId, text: welcomeText, reply_markup: JSON.stringify({ inline_keyboard: [[ { text: 'news', callback_data: '/news' }, { text: 'recensioni', callback_data: '/recensioni' }, { text: 'guide', callback_data: '/guide' }, { text: 'info', callback_data: '/info' } ]] }) }) .then(() => { console.log('Messaggio di benvenuto inviato'); res.end('ok'); }) .catch(err => { console.log('Errore invio messaggio benvenuto:', err); res.end('Errore'); }); } else if (triggerText.match(/info/i)) { sendTgMessage(chatId, 'ViktecNews è un bot programmato per riportarti le ultime dieci news, guide e recensioni. Per qualsiasi problema contatta @viktec') .then(() => { console.log('Messaggio info inviato'); res.end('ok'); }) .catch(err => { console.log('Errore invio messaggio info:', err); res.end('Errore'); }); } else if (triggerText.match(/\/news/i)) { sendNewsContent(chatId, res); } else if (triggerText.match(/\/recensioni/i)) { sendRecensioniContent(chatId, res); } else if (triggerText.match(/\/guide/i)) { // 调用对应的发送guide的函数 res.end('ok'); } else if (triggerText.match(/\/games/i)) { // 调用对应的发送games的函数 res.end('ok'); } else { sendTgMessage(chatId, "Carattere errato, utilizza i comandi!") .then(() => { console.log('Messaggio errore inviato'); res.end('ok'); }) .catch(err => { console.log('Errore invio messaggio errore:', err); res.end('Errore'); }); } }); // 启动时设置Webhook一次即可 axios.post(`https://api.telegram.org/bot${process.env.BOTTOKEN}/setWebhook`, { url: 'https://viktecnews.glitch.me/' }) .then(() => console.log('Webhook configurato con successo')) .catch(err => console.log('Errore configurazione Webhook:', err)); const listener = app.listen(process.env.PORT, function() { console.log('Il tuo bot è in ascolto sulla porta ' + listener.address().port); });
额外建议
- 把所有分类的内容发送逻辑都提取成独立函数,进一步减少代码冗余
- 可以添加错误捕获逻辑,避免单个消息发送失败导致整个流程中断
- 对于批量发送消息,可以考虑添加延迟,避免触发Telegram的API频率限制
内容的提问来源于stack exchange,提问作者Vik
相关产品推荐
相关产品推荐

