discord.py Cogs开发中on_guild_join触发Guild无guild属性报错的修复
问题定位
直接触发当前堆栈的错误
on_guild_join是discord.py内置网关事件,事件触发时传入的第一个参数就是机器人刚加入的Guild对象本身,不存在命令上下文(ctx)。原代码错误将传入的Guild对象当作ctx实例,调用ctx.guild属性,自然抛出AttributeError: 'Guild' object has no attribute 'guild'。
修复上述报错后会依次触发的隐藏问题
- 作用域混乱:代码中多处引用未定义的
member变量,实际操作对象就是事件传入的Guild实例 - 对象访问逻辑错误:将局部赋值的变量(如
Muted_role、verify_role)错误绑定到不存在的ctx对象上调用,例如写ctx.Muted_role - 流程缺失:判断Tickets分类不存在时调用创建方法,但创建后没有将返回的分类对象赋值回
ticket_category,后续统计分类子频道数量时会操作空对象 - API属性拼写错误:查找频道时使用
guild.channel,实际Guild类的全频道列表属性是guild.channels(复数形式) - 前置依赖缺失:代码尝试获取名为"Member"的验证角色,但从未在流程中创建该角色,通过工具方法查到的只会是空值,对空值对象设置权限会直接报错
- 冗余操作:创建Muted角色后又遍历全服角色查找刚创建的对象,完全可以直接接收
create_role方法的返回值,省去查找步骤 - 权限操作对象错误:设置语音频道权限时,引用了不存在的
ctx.guild.verify_role属性,实际要操作的是之前获取的Member角色对象 - 消息发送逻辑错误:原代码直接调用
ctx.send()发送嵌入消息,但机器人刚入服时不存在可直接发送消息的上下文,既没有默认发送渠道,也没有权限保证 - 全局变量缺失:嵌入消息footer中引用的
client对象未在Cog作用域内定义,会抛出NameError
修复后的可运行实现
import discord from discord.ext import commands class WelcomeCog(commands.Cog): def __init__(self, bot): self.bot = bot @commands.Cog.listener() async def on_guild_join(self, guild: discord.Guild): # 创建Muted角色,直接接收返回值,无需二次遍历查找 muted_role = await guild.create_role(name="Muted") # 为Muted角色配置禁言类权限 await muted_role.set_permissions( send_messages=False, add_reactions=False, create_public_threads=False, create_private_threads=False, send_messages_in_threads=False ) # 获取或创建Member验证角色 verify_role = discord.utils.get(guild.roles, name="Member") if not verify_role: verify_role = await guild.create_role(name="Member") # 为Member角色配置基础发言、连接权限 await verify_role.set_permissions( send_messages=True, connect=True ) # 获取或创建Tickets频道分类 ticket_category = discord.utils.get(guild.categories, name="Tickets") if not ticket_category: ticket_category = await guild.create_category_channel(name="Tickets") # 创建欢迎文本频道 await guild.create_text_channel(name="welcome") # 构建入服欢迎嵌入消息 guild_join_embed = discord.Embed(title="Thanks for adding me") guild_join_embed.add_field( name="Thanks for adding me!", value="It helps alot. I will provide my best support", inline=True ) guild_join_embed.set_footer(text=f"Right now I am in {len(self.bot.guilds)} guilds/servers") # 校验系统频道权限后再发送欢迎消息,避免无权限报错 if guild.system_channel and guild.system_channel.permissions_for(guild.me).send_messages: await guild.system_channel.send(embed=guild_join_embed) # 配置统计类语音频道的基础权限覆盖:默认角色、Member角色均无法连接 stat_channel_overwrites = { guild.default_role: discord.PermissionOverwrite(connect=False), verify_role: discord.PermissionOverwrite(connect=False), guild.me: discord.PermissionOverwrite(connect=True) } # 创建三类统计语音频道,创建时直接写入权限配置,无需二次查找修改 await guild.create_voice_channel( name=f"Total Member Count: {guild.member_count}", overwrites=stat_channel_overwrites ) await guild.create_voice_channel( name=f"Boost Count: {guild.premium_subscription_count}", overwrites=stat_channel_overwrites ) await guild.create_voice_channel( name=f"Ticket Count: {len(ticket_category.channels)}", overwrites=stat_channel_overwrites )
注意事项
- 机器人邀请时必须携带
Manage Server、Manage Roles、Manage Channels权限,且自身最高角色的位置高于所有需要创建、修改权限的角色,否则所有创建操作会被Discord API拦截- 上述代码中的权限参数仅覆盖原业务逻辑涉及的范围,可根据实际需求调整细粒度权限
- 统计类语音频道的数值不会自动实时更新,若需要同步服务器实时数据,需额外搭配定时任务逻辑,定期修改频道名称完成数值刷新
内容的提问来源于stack exchange,提问作者Jerry Zheng
相关产品推荐
相关产品推荐

