Discord机器人技术问题求助:嵌入消息发送失败与Mute命令时长异常
问题一:所有Embed消息失效,触发
DiscordAPIError: Cannot send an empty message 原因分析
这个报错通常有两个核心诱因:
- Discord.js版本兼容性问题:从v13版本开始,Discord.js彻底更改了Embed的发送规则——不再支持直接将Embed对象传入
send()方法,必须使用{ embeds: [embedInstance] }的格式。如果你的代码里还保留着旧版本的写法(比如message.channel.send(embed)),就会触发这个错误。 - Embed对象无有效内容:Discord官方不允许发送完全空白的Embed,要是你的Embed没有设置标题、描述、字段、颜色等任何非空属性,API会直接拒绝并返回“空消息”错误。
解决方案
- 统一Embed发送格式:检查所有发送Embed的代码,确保都采用v13+的标准写法:
如果你仍在使用v12或更早版本,保持直接传递Embed对象的写法即可,但建议升级到稳定版以获得更好的兼容性。// 正确示例(v13+) const embed = new Discord.MessageEmbed() .setDescription("这是一条有效的Embed消息") .setColor("#00ff00"); message.channel.send({ embeds: [embed] }); - 确保Embed有内容:每个Embed至少设置一个非空属性(比如
setTitle、setDescription、addField),避免创建完全空白的Embed实例。 - 排查拼写错误:检查Embed方法的拼写(比如把
setDescription写成setDesciption),这类低级错误会导致属性未被正确设置,最终生成空Embed。
问题二:Mute命令时长参数被忽略,默认仅禁言1秒
原因分析
看你的代码,核心问题出在参数解析的索引错误:
- 执行
arguments.shift()移除目标用户参数后,剩下的arguments数组结构是[时长, 理由片段1, 理由片段2...],但你错误地取了arguments[2]作为时长——这时候arguments[2]几乎总是undefined(除非你输入了3个以上的参数)。 ms()库解析undefined或无效字符串时,会返回null,而setTimeout会把null当作0处理(部分环境下会默认转为1秒),导致禁言立即失效。- 额外问题:你用
arguments.join(' ')生成理由时,会把时长参数也包含进去,导致理由不符合预期。
解决方案
修正参数解析逻辑,正确分离时长和理由,同时优化命令的健壮性:
const mongo = require('../mongo.js') const muteSchema = require('../schemas/mute-schema.js') const Discord = require('discord.js') const ms = require("ms") module.exports = { commands: 'mute', minArgs: 2, expectedArgs: "<Target user's @> <time> <reason>", requiredRoles: ['Staff'], callback: async (message, arguments) => { // 1. 正确获取目标成员(必须用GuildMember,User对象没有roles属性) const targetMember = message.mentions.members.first() || message.guild.members.cache.get(arguments[0]) if (!targetMember) { message.channel.send('请指定要禁言的用户。') return } const { guild, channel } = message arguments.shift() // 移除目标用户参数 // 2. 提取并验证时长参数 const timeString = arguments[0] const duration = ms(timeString) if (!duration) { return message.channel.send('无效的时长格式!请使用类似"10m"、"2h"或"1d"的格式。') } arguments.shift() // 移掉时长参数 // 3. 提取理由(剩余参数拼接) const reason = arguments.join(' ') || '无理由' // 检查muted角色是否存在 const mutedRole = message.guild.roles.cache.find(role => role.name === 'muted'); if (!mutedRole) { return message.channel.send('未找到名为"muted"的角色,请先创建该角色!') } const guildId = guild.id const userId = targetMember.id const mute = { author: message.member.user.tag, timestamp: new Date().getTime(), reason, } // 4. 数据库操作:不要每次都关闭连接(连接池会自动管理) try { await mongo().then(async (mongoose) => { await muteSchema.findOneAndUpdate( { guildId, userId }, { guildId, userId, $push: { mutes: mute }, }, { upsert: true } ) }) } catch (dbError) { console.error('数据库错误:', dbError) return message.channel.send('保存禁言记录到数据库失败。') } // 5. 执行禁言操作 try { await targetMember.roles.add(mutedRole) // 发送成功提示Embed const successEmbed = new Discord.MessageEmbed() .setDescription(`✅ **${targetMember.user.tag} 已被禁言 || ${reason}**`) .setColor('#004d00') channel.send({ embeds: [successEmbed] }) // 定时解除禁言 setTimeout(() => { targetMember.roles.remove(mutedRole).catch(err => console.error('解除禁言失败:', err)) }, duration) } catch (error) { channel.send('禁言用户失败!请确保机器人的角色权限高于muted角色。') } message.delete().catch(err => console.error('删除命令消息失败:', err)) }, }
额外优化说明
- 使用
GuildMember操作角色:之前的target是User对象,User没有roles属性,必须用GuildMember才能完成角色添加/移除操作。 - 避免频繁关闭数据库连接:
mongoose.connection.close()会断开连接池,导致后续数据库操作失败,应该让Mongoose的连接池自动管理连接。 - 添加异常处理:增加了muted角色检查、数据库操作异常处理、角色操作异常处理,提升了命令的稳定性和容错性。
内容的提问来源于stack exchange,提问作者united-corn




