使用Pycord实现星标板Cog:编辑Embed添加新增反应用户
问题诊断与修复方案
核心问题
你的代码存在两个致命逻辑错误,直接导致更新功能失效:
- 嵌套判断矛盾:
if reactions and reactions.count > 1被放在if reactions and reactions.count == 1的分支内部——这种逻辑下只有当反应数同时等于1和大于1时才会执行,完全不可能触发,所以更新代码永远不会运行。 - 无消息关联存储:首次发送星标消息后,你没有记录该消息与原消息的对应关系,后续添加反应时根本找不到需要编辑的目标消息。
另外还有两处细节瑕疵:
- 用
payload.emoji.name匹配表情不如str(payload.emoji)准确,部分表情的名称与字符串表现存在差异; - 第二次触发
on_raw_reaction_add时,代码不会进入count == 1的分支,自然不会执行任何更新操作。
修复后的完整代码
from discord.ext import commands import discord class Starboard(commands.Cog): def __init__(self, bot): self.bot = bot # 内存字典存储原消息ID与星标消息ID的映射,重启会丢失,生产环境建议用数据库持久化 self.starboard_map = {} @commands.Cog.listener() async def on_raw_reaction_add(self, payload): # 仅处理⭐表情 if str(payload.emoji) != "⭐": return # 跳过机器人的反应 user = self.bot.get_user(payload.user_id) if user.bot: return # 获取原消息所在频道 channel = self.bot.get_channel(payload.channel_id) if not channel: return # 获取原消息,处理消息已被删除的情况 try: message = await channel.fetch_message(payload.message_id) except discord.NotFound: return # 获取该消息的⭐反应数据 star_reaction = discord.utils.get(message.reactions, emoji="⭐") if not star_reaction: return starboard_channel_id = 1141847109528735866 starboard_channel = self.bot.get_channel(starboard_channel_id) if not starboard_channel: return # 提取所有非机器人打星用户的提及字符串 reacted_users = await star_reaction.users().flatten() mentions = ' '.join([u.mention for u in reacted_users if not u.bot]) # 复用Embed构建逻辑,避免重复代码 def build_star_embed(): embed = discord.Embed(description=message.content, color=discord.Color.blurple()) embed.set_author( name=message.author.display_name, icon_url=message.author.avatar.url if message.author.avatar else None ) embed.add_field(name="打星用户", value=mentions if mentions else "无", inline=True) embed.add_field(name="来源频道", value=channel.mention, inline=True) embed.add_field(name="跳转原消息", value=f"[点击这里]({message.jump_url})", inline=True) if message.author.avatar: embed.set_thumbnail(url=message.author.avatar.url) return embed # 分场景处理:首次打星发送新消息,后续打星编辑已有消息 if payload.message_id not in self.starboard_map: # 首次打星,发送星标消息并记录映射 if star_reaction.count == 1: star_msg = await starboard_channel.send(embed=build_star_embed()) self.starboard_map[payload.message_id] = star_msg.id else: # 已有星标消息,找到并更新 star_msg_id = self.starboard_map[payload.message_id] try: star_msg = await starboard_channel.fetch_message(star_msg_id) await star_msg.edit(embed=build_star_embed()) print(f"已更新星标消息,当前打星用户:{mentions}") except discord.NotFound: # 星标消息已被删除,移除无效映射 del self.starboard_map[payload.message_id]
关键修复点说明
- 拆分逻辑分支:将首次发送与后续更新逻辑分离,通过
starboard_map判断原消息是否已有对应星标消息。 - 存储消息映射:用字典记录原消息ID到星标消息ID的关联,确保后续能准确找到需要编辑的消息。
- 复用Embed构建:把Embed生成逻辑封装成函数,保证首次发送与更新的格式完全一致,减少冗余代码。
- 排除机器人用户:过滤掉机器人的反应,避免无效用户被计入打星列表。
- 异常处理:增加原消息或星标消息不存在时的容错逻辑,防止程序崩溃。
- 表情判断优化:用
str(payload.emoji)匹配⭐,避免表情名称匹配的误差。
注意事项
- 内存字典
starboard_map在机器人重启后会丢失所有映射,生产环境建议使用SQLite、MySQL等数据库持久化存储该关联关系; - 若星标频道的消息被手动删除,代码会自动移除对应的映射,下次打星时会重新发送新的星标消息。
内容的提问来源于stack exchange,提问作者Ramo A.
相关产品推荐
相关产品推荐

