You need to enable JavaScript to run this app. discord.py Cog 中 on_message 发送消息不生效及指定频道发送方法
优惠活动
大模型
产品
解决方案
定价
更多

discord.py Cog 中 on_message 发送消息不生效及指定频道发送方法

Discord.py Cog 等级系统通知失效排查与指定频道消息实现指南

升级通知无法正常触发的核心原因排查

  • 等级判断逻辑存在设计缺陷
    原代码通过"xp减去等级阈值后等于0"判断升级,没有在数据库中持久化存储用户当前等级:
    1. 新用户首次初始化xp记录后,直接跳过了后续的等级计算流程,哪怕刚好达到升级阈值也不会触发通知
    2. 用户一次发言累加xp后,如果跨越了多个等级,减去阈值后的剩余xp为正数,永远无法匹配xp == 0的判断条件,直接漏发通知
    3. 初始计算的xp阈值存在偏差,当用户累计xp非整百数时,剩余xp值永远不会等于0
  • 同步数据库操作阻塞事件循环
    代码中使用的find_one、insert_one、update_one都是PyMongo的同步阻塞方法,在异步运行的on_message监听器中执行这类代码时,会阻塞整个Discord事件循环,导致后续的send协程无法被调度执行,最终表现为消息发不出去
  • 执行顺序错位
    原代码在未过滤机器人消息的阶段就发起数据库查询,且所有等级计算逻辑都嵌套在频道判断分支内,一旦出现异常,整个消息发送链路会直接中断

正确实现的参考代码

# 在Cog初始化阶段统一管理配置
class LevelSystemCog(commands.Cog):
    def __init__(self, client):
        self.client = client
        # 配置项集中管理
        self.upgrade_notify_channel_id = 797863482380124170
        self.allowed_talk_channels = talk_channel
        self.level_db = levelling

    @commands.Cog.listener()
    async def on_message(self, message):
        # 第一步快速过滤无效消息,减少不必要的数据库查询
        if message.author.bot or message.channel.id not in self.allowed_talk_channels:
            return

        # 查询用户等级数据
        user_data = self.level_db.find_one({"id": message.author.id})
        if not user_data:
            # 新用户初始化,同时记录初始等级
            new_user_record = {
                "id": message.author.id,
                "xp": 100,
                "level": 0
            }
            self.level_db.insert_one(new_user_record)
            return

        # 累加经验值
        current_total_xp = user_data["xp"] + 100
        # 计算当前xp对应的等级
        calc_level = 0
        while True:
            xp_requirement = 50 * (calc_level ** 2) + 50 * calc_level
            if current_total_xp < xp_requirement:
                break
            calc_level += 1

        # 对比原有等级判断是否升级
        user_old_level = user_data.get("level", 0)
        if calc_level > user_old_level:
            # 更新数据库内的xp和最新等级
            self.level_db.update_one(
                {"id": message.author.id},
                {"$set": {
                    "xp": current_total_xp,
                    "level": calc_level
                }}
            )
            # 向指定ID频道发送升级通知
            target_channel = self.client.get_channel(self.upgrade_notify_channel_id)
            if target_channel:
                await target_channel.send(f"{message.author.mention} 恭喜升级到 Lv.{calc_level}!")
        else:
            # 未升级时仅更新xp数值
            self.level_db.update_one(
                {"id": message.author.id},
                {"$set": {"xp": current_total_xp}}
            )

指定频道发消息的关键实现要点

  • 统一在Cog初始化阶段管理频道ID配置,不要在监听器逻辑里硬编码,后续调整时不需要改动核心业务逻辑
  • 调用get_channel获取频道对象后,必须校验返回值是否为None,避免频道被删除、机器人无访问权限时触发代码异常
  • 当get_channel无法获取到频道对象时,可改用异步方法await self.client.fetch_channel(channel_id)直接从Discord接口拉取频道信息,稳定性高于本地缓存获取,但需要控制请求频率避免触发接口限流
  • 提前确认机器人在目标频道的权限:必须拥有查看频道、发送消息基础权限,私有频道还需要给机器人分配对应的访问角色
  • 对高并发场景,建议将PyMongo替换为异步驱动Motor,从根源上避免同步代码阻塞事件循环导致的消息发送失效

内容的提问来源于stack exchange,提问作者문준영

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.03 13:45:09