You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Discord机器人技术问题求助:嵌入消息发送失败与Mute命令时长异常

问题一:所有Embed消息失效,触发DiscordAPIError: Cannot send an empty message

原因分析

这个报错通常有两个核心诱因:

  1. Discord.js版本兼容性问题:从v13版本开始,Discord.js彻底更改了Embed的发送规则——不再支持直接将Embed对象传入send()方法,必须使用{ embeds: [embedInstance] }的格式。如果你的代码里还保留着旧版本的写法(比如message.channel.send(embed)),就会触发这个错误。
  2. Embed对象无有效内容:Discord官方不允许发送完全空白的Embed,要是你的Embed没有设置标题、描述、字段、颜色等任何非空属性,API会直接拒绝并返回“空消息”错误。

解决方案

  • 统一Embed发送格式:检查所有发送Embed的代码,确保都采用v13+的标准写法:
    // 正确示例(v13+)
    const embed = new Discord.MessageEmbed()
      .setDescription("这是一条有效的Embed消息")
      .setColor("#00ff00");
    message.channel.send({ embeds: [embed] });
    
    如果你仍在使用v12或更早版本,保持直接传递Embed对象的写法即可,但建议升级到稳定版以获得更好的兼容性。
  • 确保Embed有内容:每个Embed至少设置一个非空属性(比如setTitlesetDescriptionaddField),避免创建完全空白的Embed实例。
  • 排查拼写错误:检查Embed方法的拼写(比如把setDescription写成setDesciption),这类低级错误会导致属性未被正确设置,最终生成空Embed。

问题二:Mute命令时长参数被忽略,默认仅禁言1秒

原因分析

看你的代码,核心问题出在参数解析的索引错误

  1. 执行arguments.shift()移除目标用户参数后,剩下的arguments数组结构是[时长, 理由片段1, 理由片段2...],但你错误地取了arguments[2]作为时长——这时候arguments[2]几乎总是undefined(除非你输入了3个以上的参数)。
  2. ms()库解析undefined或无效字符串时,会返回null,而setTimeout会把null当作0处理(部分环境下会默认转为1秒),导致禁言立即失效。
  3. 额外问题:你用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

火山引擎 最新活动