You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Discord.py 如何在on_guild_join等监听器事件中调用自定义命令?

报错根源定位

你的报错是两个层面的问题共同导致的:

  • bot.invoke() 的设计要求是接收一个已经完成初始化的Context上下文对象作为入参。你现在把bot.get_command('setup')返回的Command实例直接传给了invoke,方法内部第一行就会尝试校验传入对象的ctx.command属性,而Command实例本身根本不存在这个属性,因此直接抛出AttributeError: 'Command' object has no attribute 'command'。
  • 你之前的判断完全准确:on_guild_join是生命周期事件,本身不会携带任何消息交互上下文,天然没有可以直接传给invoke的现成ctx对象,硬套invoke流程本质上违背了这个方法的设计初衷。
最优实现方案:抽离公共业务逻辑

社区通用的正确做法是不要硬凑模拟ctx走invoke流程,这种写法耦合度高、跨版本兼容性差,属于明确的反模式。
正确的代码复用逻辑非常简单:

  • 把原setup命令里的所有业务操作抽成一个独立的协程,这个协程不依赖Context对象,只接收必要的参数(比如guild实例)。
  • 不管是用户手动发消息触发setup命令,还是机器人加入服务器时自动执行初始化,都直接调用这个独立协程即可。

参考实现代码:

class SetupTasksCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    # 独立的可复用业务逻辑:不依赖任何ctx属性
    async def run_guild_initialization(self, guild: discord.Guild):
        # 这里迁移原来setup命令里的全部逻辑:创建默认角色、配置频道权限、写入数据库默认配置等
        await guild.create_role(name="已验证用户")
        await guild.create_text_channel("官方公告")
        # 其他初始化操作...

    # 用户手动触发的setup命令,仅负责桥接ctx和公共逻辑
    @commands.command()
    async def setup(self, ctx: commands.Context):
        await self.run_guild_initialization(ctx.guild)

    # 机器人入服事件触发的初始化,直接调用公共逻辑
    @commands.Cog.listener()
    async def on_guild_join(self, guild: discord.Guild):
        await self.run_guild_initialization(guild)
不推荐的兼容写法:手动构造模拟ctx走invoke

如果你的setup命令逻辑极其复杂,抽离改造成本太高,必须走invoke的完整命令执行流,就一定要构造符合规范的ctx对象,不能直接传Command实例:

  • 先定位当前服务器内机器人有发消息权限的频道,构造一个模拟的Message对象,再通过bot.get_context()生成合法的ctx实例,最后把ctx传给invoke。
  • 这种写法的局限性非常明显:模拟消息的作者是机器人自身,所有命令内置的权限校验、用户身份判断逻辑都会按机器人的权限值运行,很容易出现逻辑偏差;而且构造模拟消息需要调用discord.py的内部连接状态接口,跨版本更新时极易失效。

参考实现代码:

@commands.Cog.listener()
async def on_guild_join(self, guild: discord.Guild):
    # 定位机器人有权限发消息的频道
    target_channel = guild.system_channel or next(
        (ch for ch in guild.text_channels if ch.permissions_for(guild.me).send_messages),
        None
    )
    if not target_channel:
        return
    # 构造模拟消息对象
    fake_message = discord.Message(
        state=self.bot._connection,
        channel=target_channel,
        data={
            "id": 0,
            "content": "!setup",
            "author": self.bot.user,
            "type": 0,
            "tts": False,
            "mentions": [],
            "mention_roles": [],
            "mention_everyone": False,
            "attachments": [],
            "embeds": [],
            "pinned": False
        }
    )
    # 基于模拟消息生成合法ctx
    ctx = await self.bot.get_context(fake_message)
    # 传入ctx给invoke,即可避免之前的属性报错
    await self.bot.invoke(ctx)

内容的提问来源于stack exchange,提问作者c0dezer019

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 19:06:02