
在使用 discord.js 构建机器人时,我们常常需要实现定时发送消息的功能,例如定期推送新闻更新。一个常见的场景是,机器人能够响应用户命令(如输入 "news")并发送消息,但当尝试通过 node-cron 或 setinterval 等定时器自动发送消息时,消息却无法正常送达。尽管机器人拥有发送消息的权限,且目标频道 id 正确,但自动发送功能始终失效。
最初的代码可能类似于:
// ... 其他初始化代码 ...
const sendPromptNews = async () => {
if (targetGuildId) {
// 尝试从缓存中获取服务器和频道
const guild = bot.guilds.cache.get(targetGuildId);
if (guild) {
const channel = guild.channels.cache.find((channel) => channel.type === 'text' && channel.id === '1111638079103574159');
if (channel) {
try {
// ... 获取新闻数据 ...
channel.send(`Breaking Automatic News:\n\n${data}`);
} catch (error) {
console.error(error);
channel.send('An error occurred while fetching the news highlights.');
}
}
}
}
};
// 每10秒调度一次自动新闻发送
cron.schedule('*/10 * * * * *', () => {
sendPromptNews();
});
// ... 其他代码 ...在这种情况下,机器人对用户命令的响应正常,表明其基本功能和权限没有问题。然而,定时任务却未能成功发送消息。
问题的核心在于 Discord.js 的缓存机制。当机器人接收到一条消息(例如通过 messageCreate 事件)时,与该消息相关的服务器(Guild)和频道(Channel)实体会被加载到机器人的本地缓存中。因此,在响应用户命令时,这些实体通常可以直接通过 cache.get() 或 cache.find() 方法获取到。
然而,对于由定时任务触发的非事件相关操作,特别是机器人启动后未与特定服务器或频道进行过直接交互的情况下,这些实体可能尚未被加载到缓存中。此时,bot.guilds.cache.get(targetGuildId) 或 guild.channels.cache.find() 将返回 undefined,导致后续的消息发送操作无法执行。
简而言之,cache 并不是一个永久存储所有实体的地方,它只包含机器人最近交互过或明确获取过的实体。
为了解决缓存问题,我们需要显式地从 Discord API 获取服务器和频道信息,而不是仅仅依赖本地缓存。Discord.js 提供了 fetch 方法,它会向 Discord API 发送请求来获取最新的实体数据,并将其填充到缓存中。
修改后的 sendPromptNews 函数应使用 await bot.guilds.fetch(targetGuildId) 和 await guild.channels.fetch()。
const axios = require('axios');
const cheerio = require('cheerio');
const express = require('express');
const cron = require('node-cron');
const { Client, GatewayIntentBits } = require('discord.js');
const bot = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent],
});
const app = express();
const port = process.env.PORT || 5000;
const url = 'YOUR_NEWS_WEBSITE_URL'; // 请替换为实际的URL
let breakingNews = [];
let promptNews = [];
let targetGuildId = null; // 用于存储接收自动更新的服务器ID
// ... fetchData 函数(保持不变) ...
// ... app.get('/breaking-news') 和 app.get('/prompt-news') 路由(保持不变) ...
// ... bot.on('ready') 事件(保持不变) ...
bot.on('messageCreate', async (message) => {
// 确保机器人不会响应自己的消息
if (message.author.bot) return;
// 设置目标服务器ID
if (message.content.toLowerCase() === 'news' && !targetGuildId) {
targetGuildId = message.guildId;
message.reply({ content: '此服务器将开始接收自动新闻更新。' });
return; // 避免继续处理
}
// 停止自动更新
if (message.content.toLowerCase() === 'stop news' && message.guildId === targetGuildId) {
targetGuildId = null;
message.reply({ content: '此服务器的自动新闻更新已停止。' });
return; // 避免继续处理
}
// 手动获取新闻(如果需要)
if (message.content.toLowerCase() === 'news' && message.guildId === targetGuildId) {
try {
const response = await axios.get('http://localhost:5000/breaking-news');
const newsData = response.data.map((breakingNewsItem) => `${breakingNewsItem.title}\n${url}${breakingNewsItem.link}`);
newsData.forEach((data) => {
message.reply({ content: `最新新闻摘要:\n\n${data}` });
});
} catch (error) {
console.error('手动获取新闻失败:', error);
message.reply('获取新闻摘要时发生错误。');
}
}
});
// 改进后的自动发送新闻功能
const sendPromptNews = async () => {
if (targetGuildId) {
try {
// 1. 显式地从 Discord API 获取 Guild 信息
const guild = await bot.guilds.fetch(targetGuildId);
if (!guild) {
console.error(`错误:未找到目标服务器 (ID: ${targetGuildId})`);
return;
}
// 2. 显式地从 Discord API 获取所有 Channel 信息,然后查找
// 或者如果频道ID已知且固定,可以直接 await guild.channels.fetch('YOUR_CHANNEL_ID')
const channels = await guild.channels.fetch();
const targetChannelId = '1111638079103574159'; // 请替换为实际的目标频道ID
const channel = channels.find((ch) => ch.type === 0 && ch.id === targetChannelId); // type 0 代表文字频道
if (!channel) {
console.error(`错误:未找到目标频道 (ID: ${targetChannelId}) 在服务器 (ID: ${targetGuildId}) 中`);
return;
}
// 3. 获取新闻数据并发送
const response = await axios.get('http://localhost:5000/prompt-news');
const newsData = response.data.map((promptNewsItem) => `${promptNewsItem.title}\n${url}${promptNewsItem.link}`);
if (newsData.length > 0) {
for (const data of newsData) { // 使用 for...of 循环确保消息按顺序发送
await channel.send(`突发自动新闻:\n\n${data}`);
}
} else {
console.log('没有新的自动新闻可发送。');
}
} catch (error) {
console.error('发送自动新闻时发生错误:', error);
// 可以选择向某个管理频道发送错误通知
// channel.send('发送自动新闻时发生错误。'); // 如果 channel 已经获取到
}
} else {
console.log('TargetGuildId 未设置,跳过自动新闻发送。');
}
};
// 调度自动新闻每10秒发送一次
cron.schedule('*/10 * * * * *', () => {
console.log('触发自动新闻发送任务...');
sendPromptNews();
});
bot.login('YOUR_BOT_TOKEN'); // 请替换为你的机器人令牌Discord.js 机器人在处理定时任务时,由于其缓存机制的特性,直接依赖 cache.get 或 cache.find 可能会导致无法获取到目标服务器或频道。通过采用 await bot.guilds.fetch() 和 await guild.channels.fetch() 等异步获取方法,我们可以确保在发送定时消息时始终能够访问到最新的、有效的 Discord 实体。同时,结合严谨的错误处理和日志记录,将大大提高机器人的稳定性和可维护性。
以上就是解决 Discord.js 机器人定时消息发送失败问题:缓存与异步获取指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号