You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:21:54