Discord.js定时发送生日Embed消息失败问题排查
Discord生日祝福Bot无法发送Embed消息的修复方案
核心问题排查
你的代码里有几个关键错误导致Embed发不出去:
- 错误获取Discord客户端实例:
const client = Client;拿的是类而不是已经初始化的客户端实例,根本无法调用频道相关方法 - MongoDB查询条件无效:存储的生日是完整的
DD.MM.YYYY格式,但查询用${day}.${month}.直接匹配,找不到任何数据 - 定时器逻辑位置错误:把
setInterval放在slash命令的execute方法里,每次用户执行命令都会新增一个定时器,导致重复触发甚至资源浪费 - 频道获取依赖缓存:只用
channels.cache.get()可能因为频道没在缓存中导致获取失败
修复后的完整代码
1. 生日命令文件(/commands/birthday.js)
const { EmbedBuilder, SlashCommandBuilder } = require('discord.js'); const Birthday = require('../../Models/Birthday'); module.exports = { data: new SlashCommandBuilder() .setName('birthday') .setDescription('保存用户生日') .addUserOption(option => option.setName('user') .setDescription('选择用户') .setRequired(true)) .addStringOption(option => option.setName('date') .setDescription('输入生日(格式:DD.MM.YYYY)') .setRequired(true)), async execute(interaction) { const user = interaction.options.getUser('user'); const date = interaction.options.getString('date'); // 简单校验日期格式(可选,增强鲁棒性) const dateRegex = /^\d{2}\.\d{2}\.\d{4}$/; if (!dateRegex.test(date)) { return interaction.reply({ content: '请输入正确的日期格式:DD.MM.YYYY', ephemeral: true }); } // 检查是否已存在该用户的生日记录,避免重复存储 const existingBirthday = await Birthday.findOne({ userId: user.id }); if (existingBirthday) { return interaction.reply({ content: `${user.username}的生日已经存在,当前记录是${existingBirthday.birthday}`, ephemeral: true }); } const birthday = new Birthday({ userId: user.id, birthday: date, }); await birthday.save(); await interaction.reply(`${user.username}的生日已保存为${date}。`); } };
2. 客户端启动文件(比如index.js)
把生日检查的定时器放在客户端启动完成后,避免重复创建:
const { Client, GatewayIntentBits } = require('discord.js'); const Birthday = require('./Models/Birthday'); require('dotenv').config(); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); client.on('ready', async () => { console.log(`已登录为 ${client.user.tag}`); // 每隔1小时检查一次生日(建议不要太频繁,比如1小时一次) setInterval(async () => { const today = new Date(); const month = String(today.getMonth() + 1).padStart(2, '0'); // 补零确保两位,比如1月变成01 const day = String(today.getDate()).padStart(2, '0'); // 查询生日匹配今日日/月的用户(忽略年份) const users = await Birthday.find({ birthday: { $regex: `^${day}\\.${month}\\.` } }); if (users.length === 0) return; // 获取目标频道,用fetch确保能拿到,不管缓存 const channelId = '1034193578450231346'; let channel; try { channel = await client.channels.fetch(channelId); } catch (err) { console.error('无法获取目标频道:', err); return; } for (const birthdayDoc of users) { // 获取用户对象,显示用户名 const user = await client.users.fetch(birthdayDoc.userId).catch(err => null); const userName = user ? user.username : `用户ID: ${birthdayDoc.userId}`; const embed = new EmbedBuilder() .setColor('#0099ff') .setTitle(`生日快乐 ${userName}! 🎉`) .setDescription(`今天是你的生日,祝你度过超棒的一天!`) .setTimestamp() .setFooter({ text: '生日祝福Bot' }); // 发送提及+Embed await channel.send({ content: user ? `<@${user.id}>` : `@${userName}`, embeds: [embed] }); } }, 1000 * 60 * 60); // 1小时间隔,可根据需求调整 }); client.login(process.env.DISCORD_TOKEN);
关键修复说明
- 客户端实例获取:直接使用初始化后的
client实例,而不是Client类,确保能正常调用Discord API - MongoDB查询优化:用正则表达式
$regex匹配生日的前5位(DD.MM.),正确筛选出今日过生日的用户 - 定时器位置调整:将检查逻辑放在客户端
ready事件中,确保只创建一个定时器,避免重复触发 - 频道获取优化:使用
channels.fetch()替代cache.get(),即使频道不在缓存中也能获取到 - 用户信息优化:通过
users.fetch()获取用户对象,在Embed中显示用户名而非冰冷的ID,提升体验 - 日期格式补零:用
padStart(2, '0')确保月份和日期都是两位,避免1月写成1导致匹配失败(比如存储的是01.05.2000,查询用1.5.就匹配不到)
内容的提问来源于stack exchange,提问作者Xhani
相关产品推荐
相关产品推荐

