discord.py warn命令无法发送embed及相关报错与mongodb问题求解
我正在尝试使用discord.py开发警告(warn)命令,采用MongoDB作为数据库存储处罚记录、以及日志embed需要发送的频道配置。当前代码如下:
@commands.command() @commands.has_permissions(manage_messages=True) async def warn(self, ctx, member: discord.Member, *, reason=None): ctx.logging = cluster["Logging"]["Punishments logs"] self.channel = cluster["Logging"]["logchannels"] embed = discord.Embed(title="\u200b", color=0x008000) warn_embed = discord.Embed( colour=discord.Color.magenta() ) warn_embed.set_author(name='Punishment Notifcation') warn_embed.add_field(name=f'You have been warned in **{ctx.guild.name}**', value=f'For `{reason}.`', inline=True) if member == ctx.author: await ctx.channel.send("You can't warn yourself :person_facepalming:") return elif reason == None: reason = "No stated reason" await member.send(embed=warn_embed) await ctx.send(f'{member.mention} has been warned.') print(f'{ctx.author} warned {member.mention} for {reason}') ctx.logging.insert_one({"guild_id": ctx.guild.id, "action": "warn", "moderator": ctx.author.id, "user": member.id, "reason": reason}) channel = self.setup.find_one({"guild_id": ctx.guild.id}) if channel is not None: elif_embed = discord.Embed( title=f'Warn | Case (case number in future)', color=discord.Color.magenta() ) elif_embed.set_author(name=member, url=discord.Embed.Empty, icon_url=member.avatar_url) elif_embed.add_field(name=f'**Offender:**', value=f'{member} {member.mention}') elif_embed.add_field(name=f'**Reason:**', value=f'{reason}') elif_embed.add_field(name='**Responsible Moderator:**', value=f'{ctx.author} {ctx.author.mention}') await ctx.send(embed=elif_embed) else: await ctx.send('This command cannot be logged as moderation logs have not been enabled. To enable them do: **+setupmod <channel>**') else: await member.send(embed=warn_embed) await ctx.send(f'{member.mention} has been warned by {ctx.author.mention}') print(f'{ctx.author} warned {member} for {reason}') ctx.logging.insert_one({"guild_id": ctx.guild.id, "action": "warn", "moderator": ctx.author.id, "user": member.id, "reason": reason}) channel = self.setup.find_one({"guild_id": ctx.guild.id}) if channel is not None: else_embed = discord.Embed( title=f'Warn | Case (case number in future)', color=discord.Color.magenta() ) else_embed.set_author(name=member, url=discord.Embed.Empty, icon_url=member.avatar_url) else_embed.add_field(name=f'**Offender:**', value=f'{member} {member.mention}') else_embed.add_field(name=f'**Reason:**', value=f'`{reason}`') else_embed.add_field(name='**Responsible Moderator:**', value=f'{ctx.author} {ctx.author.mention}') await ctx.send(embed=else_embed) else: await ctx.send('This command cannot be logged as moderation logs have not been enabled. To enable them do: **+setupmod <channel>**')
预期效果
- 在命令执行的频道发送操作成功的确认消息
- 向被警告用户发送私信告知其被警告
- 若已配置日志频道,则向该频道发送日志embed
实际运行结果
前两项功能正常运行,但embed无法发送到已设置的对应频道,同时触发以下报错:
Ignoring exception in on_message Traceback (most recent call last): File "C:\Users\name\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\client.py", line 343, in _run_event await coro(*args, **kwargs) File "c:\Users\name\Documents\Discord Bot\Testcode.py", line 98, in on_message guild = setup.find_one({"guild_id": message.guild.id}) AttributeError: 'NoneType' object has no attribute 'id' Discord Username #1234 warned <@!759497745886085132> for No stated reason Ignoring exception in on_command_error Traceback (most recent call last): File "C:\Users\name\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 85, in wrapped ret = await coro(*args, **kwargs) File "c:\Users\name\Documents\Discord Bot\cogs\moderation.py", line 100, in warn channel = self.setup.find_one({"guild_id": ctx.guild.id}) AttributeError: 'Moderation' object has no attribute 'setup' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\name\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\client.py", line 343, in _run_event await coro(*args, **kwargs) File "c:\Users\name\Documents\Discord Bot\Testcode.py", line 231, in on_command_error raise error File "C:\Users\name\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\bot.py", line 939, in invoke await ctx.command.invoke(ctx) File "C:\Users\name\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 863, in invoke await injected(*ctx.args, **ctx.kwargs) File "C:\Users\name\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\ext\commands\core.py", line 94, in wrapped raise CommandInvokeError(exc) from exc discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'Moderation' object has no attribute 'setup'
我已经尝试用print命令排查错误没有结果,在Discord支持服务器求助也未得到解决,查阅过往可运行代码及discord.py官方文档也没找到解决方法,需要相关解决方案。
另外如果您了解MongoDB,也麻烦提供为每条处罚记录分配ID的实现思路,谢谢。
补充说明
报错第一部分指向的代码片段如下:
@bot.event async def on_message(message): setup = cluster["Atomic_Developer_Bot"]["prefix"] guild = setup.find_one({"guild_id": message.guild.id}) prefix = guild["prefix"] if message.content.startswith('<@850794452364951554>'): await message.channel.send(f'The current prefix is **{prefix}**') else: print(prefix) await bot.process_commands(message)
1. 修复AttributeError: 'Moderation' object has no attribute 'setup'报错
错误原因是你在Moderation cog类中没有定义setup实例属性,但代码中调用了self.setup.find_one()。你在warn函数开头已经定义了存储日志频道配置的集合self.channel = cluster["Logging"]["logchannels"],直接把所有self.setup.find_one替换为self.channel.find_one即可。
另外注意你当前查到频道配置后,是调用ctx.send往命令执行频道发embed,不是往配置的日志频道发,需要修改为:
# 查到配置后先获取频道对象,cog内用self.bot获取bot实例 log_channel = self.bot.get_channel(channel["channel_id"]) if log_channel: # 加判断避免频道被删报错 await log_channel.send(embed=elif_embed) # 或者else_embed
额外优化提醒:你当前的warn_embed创建在reason赋值之前,当用户未填写reason时,用户收到的私信里reason会显示为None.,建议把warn_embed的创建逻辑放到reason赋值完成之后,避免这个问题。
2. 修复AttributeError: 'NoneType' object has no attribute 'id'报错
错误出现在on_message事件中,原因是私信场景下message.guild为None,访问id属性就会报错。同时需要增加服务器未配置前缀时的默认值判断,避免查询结果为空时报错。修改后的代码如下:
@bot.event async def on_message(message): # 非服务器消息直接跳过前缀处理 if not message.guild: await bot.process_commands(message) return setup = cluster["Atomic_Developer_Bot"]["prefix"] guild = setup.find_one({"guild_id": message.guild.id}) # 没有配置前缀时使用默认前缀,替换为你自己的默认前缀即可 prefix = guild["prefix"] if guild else "+" if message.content.startswith('<@850794452364951554>'): await message.channel.send(f'The current prefix is **{prefix}**') else: print(prefix) await bot.process_commands(message)
MongoDB处罚记录分配ID实现思路
MongoDB插入文档时会自动生成唯一的_id字段作为主键,如果你需要可读性更高的自增数字案件ID,可以通过单独的计数器集合实现:
- 新建
counters集合存储各业务的当前最大ID,初始化一条处罚ID计数记录:
{"_id": "punishment_id", "sequence_value": 0}
- 编写获取下一个可用ID的函数:
def get_next_case_id(): result = cluster["Logging"]["counters"].find_one_and_update( {"_id": "punishment_id"}, {"$inc": {"sequence_value": 1}}, return_document=True ) return result["sequence_value"]
- 插入处罚记录时新增case_id字段即可:
case_id = get_next_case_id() ctx.logging.insert_one({ "case_id": case_id, "guild_id": ctx.guild.id, "action": "warn", "moderator": ctx.author.id, "user": member.id, "reason": reason })
内容的提问来源于stack exchange,提问作者Emily S

