如何用JavaScript版Discord Bot获取频道所有消息?
问题:Discord示例Bot中实现/getmessages命令获取频道消息失败
我已成功运行Discord官方入门示例项目,/test命令可正常响应,还创建了新命令/getmessages,目前仅返回固定字符串。
我希望修改该命令以获取频道内所有消息,尝试参考获取频道消息的代码方案,但实现时遇到问题。
参考代码示例:
const channel = client.channels.cache.get("Your channel ID"); channel.messages.fetch({ limit: 100 }).then(messages => { console.log(`Received ${messages.size} messages`); // 遍历消息 messages.forEach(message => console.log(message.content)) })
我将获取频道的代码添加到自己的JavaScript代码中:
if (name === 'getmessages') { // 获取当前频道所有消息 console.log("try to get messages") try{ const channel = client.channels.cache.get("961001859659759751"); }catch(err){ console.log("ERROR GETTING CHANNEL: ", err) } // 回复触发命令的频道 return res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: '2. Hello we will try to get messages ' + getRandomEmoji(), }, }); }
运行后出现错误:
ERROR GETTING CHANNEL: ReferenceError: client is not defined
我不确定这是否是在Discord示例JavaScript Bot中获取频道消息的正确方式,也不清楚当前app.js里的client变量是什么,我的完整app.js代码如下:
import 'dotenv/config'; import express from 'express'; import { InteractionType, InteractionResponseType, InteractionResponseFlags, MessageComponentTypes, ButtonStyleTypes, } from 'discord-interactions'; import { VerifyDiscordRequest, getRandomEmoji, DiscordRequest } from './utils.js'; import { getShuffledOptions, getResult } from './game.js'; // create client var //export class Client<Ready extends boolean = boolean> extends BaseClient; // 创建express应用 const app = express(); // 端口,默认3000 const PORT = process.env.PORT || 3000; // 解析请求体并验证Discord请求 app.use(express.json({ verify: VerifyDiscordRequest(process.env.PUBLIC_KEY) })); // 存储进行中的游戏,生产环境建议用数据库 const activeGames = {}; /** * Discord交互请求的端点 */ app.post('/interactions', async function (req, res) { // 交互类型和数据 const { type, id, data } = req.body; /** * 处理PING验证请求 */ if (type === InteractionType.PING) { return res.send({ type: InteractionResponseType.PONG }); } /** * 处理斜杠命令请求 */ if (type === InteractionType.APPLICATION_COMMAND) { const { name } = data; // "getMessages"命令 if (name === 'getmessages') { // 获取当前频道所有消息 console.log("try to get messages") try{ const channel = client.channels.cache.get("961001859659759751"); }catch(err){ console.log("ERROR GETTING CHANNEL: ", err) } // 回复触发命令的频道 return res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: '2. Hello we will try to get messages ' + getRandomEmoji(), }, }); } // "test"命令 if (name === 'test') { // 回复触发命令的频道 return res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: 'hello world ' + getRandomEmoji(), }, }); } // "challenge"命令 if (name === 'challenge' && id) { const userId = req.body.member.user.id; // 用户选择的物品 const objectName = req.body.data.options[0].value; // 用消息ID作为游戏ID创建游戏实例 activeGames[id] = { id: userId, objectName, }; return res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: `Rock papers scissors challenge from <@${userId}>`, components: [ { type: MessageComponentTypes.ACTION_ROW, components: [ { type: MessageComponentTypes.BUTTON, custom_id: `accept_button_${req.body.id}`, label: 'Accept', style: ButtonStyleTypes.PRIMARY, }, ], }, ], }, }); } } /** * 处理交互组件请求 */ if (type === InteractionType.MESSAGE_COMPONENT) { // 组件自定义ID const componentId = data.custom_id; if (componentId.startsWith('accept_button_')) { // 获取关联的游戏ID const gameId = componentId.replace('accept_button_', ''); // 删除原消息的端点 const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; try { await res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: 'What is your object of choice?', flags: InteractionResponseFlags.EPHEMERAL, components: [ { type: MessageComponentTypes.ACTION_ROW, components: [ { type: MessageComponentTypes.STRING_SELECT, custom_id: `select_choice_${gameId}`, options: getShuffledOptions(), }, ], }, ], }, }); // 删除之前的消息 await DiscordRequest(endpoint, { method: 'DELETE' }); } catch (err) { console.error('Error sending message:', err); } } else if (componentId.startsWith('select_choice_')) { // 获取关联的游戏ID const gameId = componentId.replace('select_choice_', ''); if (activeGames[gameId]) { // 获取响应用户的ID和选择 const userId = req.body.member.user.id; const objectName = data.values[0]; // 计算游戏结果 const resultStr = getResult(activeGames[gameId], { id: userId, objectName, }); // 从存储中移除游戏 delete activeGames[gameId]; // 更新消息的端点 const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; try { // 发送结果 await res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: resultStr }, }); // 更新临时消息 await DiscordRequest(endpoint, { method: 'PATCH', body: { content: 'Nice choice ' + getRandomEmoji(), components: [], }, }); } catch (err) { console.error('Error sending message:', err); } } } } }); app.listen(PORT, () => { console.log('Listening on port', PORT); });
解决方案
问题根源
你使用的Discord官方入门示例是基于HTTP交互模式(通过Express接收Discord的Webhook请求),而非传统的Discord.js客户端(Client类)。代码中从未初始化过client变量,因此会报错client is not defined。
正确实现方式
要在这个示例中获取频道消息,需使用Discord API的REST接口,利用示例中已有的DiscordRequest工具函数调用API即可。
修改后的/getmessages命令代码:
if (name === 'getmessages') { console.log("try to get messages"); // 从请求体中获取当前频道ID(无需硬编码) const channelId = req.body.channel_id; try { // 调用Discord API获取频道消息,单次最多获取100条 const messages = await DiscordRequest(`/channels/${channelId}/messages?limit=100`, { method: 'GET' }); // 提取消息内容并拼接成字符串 const messageContents = messages.map(msg => `${msg.author.username}: ${msg.content}`).join('\n'); // 回复用户,注意Discord消息内容有2000字符限制,超出需截断 return res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: `获取到${messages.length}条消息:\n${messageContents.length > 1900 ? messageContents.slice(0,1900)+'...' : messageContents}`, }, }); } catch(err) { console.log("ERROR GETTING MESSAGES: ", err); return res.send({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { content: '获取消息失败:'+err.message, }, }); } }
注意事项
- 确保Bot拥有
Read Message History权限 - Discord API限制单次最多获取100条消息,如需获取更多需通过
before参数分页循环请求 - 单条消息内容不能超过2000字符,超出需截断或拆分回复
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

