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

如何在pycord/discord.py中载入旧消息至缓存实现反应角色?

问题:Discord机器人重启后无法响应旧反应角色消息的交互

开发Discord机器人的反应角色功能时,因为无法让机器人全天候运行,所以把反应角色消息的数据存在JSON文件里,重启后可以读取这些数据。但遇到问题:重启后,用户对重启前发送的反应角色消息进行反应,机器人完全没有响应。推测是消息不在缓存中,尝试在on_ready里fetch消息,但问题依旧。

相关代码

@bot.command()
@commands.has_permissions(administrator=True)
async def react(ctx, title, description, *args):
    if len(args) % 2 != 0:
        return

    fields = []
    for i in range(0, len(args), 2):
        role_id = int(args[i][3:-1])
        role = discord.utils.get(ctx.guild.roles, id=role_id)
        emoji = args[i+1]
        fields.append(
            {
                'name': f'{emoji} {role}',
                'value': '',
                'inline': True
            },
        )
    message = await ctx.send(embed=makeembed(
    title=title,
    description=description,
    fields=fields,
    ))

    for i in range(0, len(args), 2):
        await message.add_reaction(args[i+1])

    reaction_messages = load_data('reaction_messages.json')
    set_value(reaction_messages, str(message.id), {
        'message_id': str(message.id),
        'channel_id': str(message.channel.id),
        'guild_id': str(message.guild.id),
        'roles': [
            {
                'role_id': int(args[i][3:-1]),
                'emoji': args[i+1]
            }
            for i in range(0, len(args), 2)
        ]
    })
    save_data(reaction_messages, 'reaction_messages.json')


# Reaction handling here...

@bot.event
async def on_ready():
    reaction_messages = load_data('reaction_messages.json')
    for item in reaction_messages:
        message = get_value(reaction_messages,item)
        channel = bot.get_channel(int(message['channel_id']))
        await channel.fetch_message(int(message['message_id']))

JSON存储文件内容

{"1124400465283665950": {"message_id": "1124400465283665950", "channel_id": "1122126932755087382", "guild_id": "1122126932000116746", "roles": [{"role_id": 1122127011738026085, "emoji": "\ud83d\udc80"}, {"role_id": 1122127049289646152, "emoji": "\ud83d\udc7f"}]}}

解决方案

问题出在你使用的普通反应事件(on_reaction_add/on_reaction_remove)依赖机器人本地缓存,重启后旧消息不在缓存中,即使fetch消息也无法触发这些事件。Discord的普通反应事件只会推送缓存内消息的交互,而raw反应事件则会捕获所有用户的反应操作,不受缓存限制。

替换为Raw反应事件处理

把原来的反应处理代码替换成以下内容:

@bot.event
async def on_raw_reaction_add(payload):
    # 忽略机器人自身的反应
    if payload.user_id == bot.user.id:
        return

    # 从JSON读取配置,检查当前消息是否属于反应角色消息
    reaction_messages = load_data('reaction_messages.json')
    msg_key = str(payload.message_id)
    if msg_key not in reaction_messages:
        return

    msg_data = reaction_messages[msg_key]
    # 获取服务器和成员对象
    guild = bot.get_guild(int(msg_data['guild_id']))
    if not guild:
        return

    member = guild.get_member(payload.user_id)
    if not member:
        member = await guild.fetch_member(payload.user_id)

    # 匹配表情对应的角色并添加
    emoji_str = str(payload.emoji)
    for role_item in msg_data['roles']:
        if emoji_str == role_item['emoji']:
            role = guild.get_role(role_item['role_id'])
            if role:
                await member.add_roles(role)
            break


@bot.event
async def on_raw_reaction_remove(payload):
    if payload.user_id == bot.user.id:
        return

    reaction_messages = load_data('reaction_messages.json')
    msg_key = str(payload.message_id)
    if msg_key not in reaction_messages:
        return

    msg_data = reaction_messages[msg_key]
    guild = bot.get_guild(int(msg_data['guild_id']))
    if not guild:
        return

    member = guild.get_member(payload.user_id)
    if not member:
        member = await guild.fetch_member(payload.user_id)

    # 匹配表情对应的角色并移除
    emoji_str = str(payload.emoji)
    for role_item in msg_data['roles']:
        if emoji_str == role_item['emoji']:
            role = guild.get_role(role_item['role_id'])
            if role:
                await member.remove_roles(role)
            break

关键说明

  1. Raw事件的优势:on_raw_reaction_add和on_raw_reaction_remove直接监听Discord的原始事件推送,不管消息是否在本地缓存,只要用户操作了反应就会触发。
  2. 角色权限检查:确保机器人拥有Manage Roles权限,并且机器人的角色位置要高于需要分配的角色,否则无法完成角色的添加/移除操作。
  3. 表情格式兼容:自定义表情会被转换为<:emoji_name:emoji_id>格式的字符串,和你存储的普通Unicode表情格式兼容,无需额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:45:05