如何在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
关键说明
- Raw事件的优势:
on_raw_reaction_add和on_raw_reaction_remove直接监听Discord的原始事件推送,不管消息是否在本地缓存,只要用户操作了反应就会触发。 - 角色权限检查:确保机器人拥有
Manage Roles权限,并且机器人的角色位置要高于需要分配的角色,否则无法完成角色的添加/移除操作。 - 表情格式兼容:自定义表情会被转换为
<:emoji_name:emoji_id>格式的字符串,和你存储的普通Unicode表情格式兼容,无需额外处理。
内容的提问来源于stack exchange,提问作者G1rby
相关产品推荐
相关产品推荐

