如何开发Discord机器人实现频道内所有消息反应自动分配角色
Python实现Discord指定频道全消息反应自动发角色机器人
前置准备
- 安装依赖:执行
pip install discord.py,请使用2.x版本,1.x版本已停止维护不兼容新接口 - 前往Discord开发者门户你的机器人应用页,开启必要权限配置:
- 开启「Server Members Intent」「Message Content Intent」两个特权网关意图
- 邀请机器人进服务器时勾选「管理角色」权限,且机器人在服务器角色列表的排序必须高于你要自动分配的目标角色,否则接口会报权限错误无法分配角色
- 提前记录3个核心配置的ID:目标频道ID、要求用户添加的反应表情、完成后发放的角色ID(ID获取方式:Discord设置里开开发者模式,右键对应对象就能复制ID)
核心实现逻辑
- 机器人启动时拉取目标频道的所有历史消息,存入缓存作为需要完成反应的任务列表
- 监听目标频道的新消息,自动加入任务列表,避免新消息不纳入统计
- 监听全局反应添加事件,过滤掉非目标频道、非指定表情、机器人自己的操作
- 对触发校验的用户,统计他已经给多少条目标频道的消息加了指定反应,数量匹配总任务数就自动发放对应角色,跳过已经拥有角色的用户避免重复操作
可直接运行的代码
import discord from discord.ext import commands # -------------------------- 配置项 替换为你自己的参数 -------------------------- TARGET_CHANNEL_ID = 0 # 替换为目标频道的数字ID TARGET_EMOJI = "✅" # 默认表情直接填字符,自定义表情填<:表情名:表情ID>格式 REWARD_ROLE_ID = 0 # 替换为要自动发放的角色数字ID BOT_TOKEN = "你的机器人Token" # 替换为开发者门户里的机器人Token # ----------------------------------------------------------------------------- # 初始化网关意图,缺了对应意图收不到事件 intents = discord.Intents.default() intents.members = True intents.message_content = True intents.reactions = True bot = commands.Bot(command_prefix="!", intents=intents) # 缓存集合:存目标频道所有需要完成反应的消息ID task_message_ids = set() @bot.event async def on_ready(): print(f"机器人登录成功:{bot.user}") target_channel = bot.get_channel(TARGET_CHANNEL_ID) if not target_channel: raise ValueError("配置的目标频道不存在,请检查ID是否正确") # 拉取目标频道全量历史消息加入任务列表 async for msg in target_channel.history(limit=None): task_message_ids.add(msg.id) print(f"初始化完成,共加载{len(task_message_ids)}条待反应消息") @bot.event async def on_message(message): # 目标频道发的新非机器人消息,自动加入任务列表 if message.channel.id == TARGET_CHANNEL_ID and not message.author.bot: task_message_ids.add(message.id) await bot.process_commands(message) @bot.event async def on_raw_reaction_add(payload): # 过滤无效触发 if payload.user_id == bot.user.id: return if payload.channel_id != TARGET_CHANNEL_ID: return if str(payload.emoji) != TARGET_EMOJI: return guild = bot.get_guild(payload.guild_id) member = guild.get_member(payload.user_id) reward_role = guild.get_role(REWARD_ROLE_ID) target_channel = guild.get_channel(TARGET_CHANNEL_ID) # 已经有对应角色的用户直接跳过,不重复处理 if reward_role in member.roles: return # 统计用户已完成的反应数量 finished_count = 0 for msg_id in task_message_ids.copy(): try: msg = await target_channel.fetch_message(msg_id) except discord.NotFound: # 消息已被删除的话从任务列表移除 task_message_ids.remove(msg_id) continue # 检查当前消息上用户是否加了目标反应 for reaction in msg.reactions: if str(reaction.emoji) == TARGET_EMOJI: async for reacter in reaction.users(): if reacter.id == member.id: finished_count += 1 break break # 完成数匹配任务总数,发放角色 if finished_count >= len(task_message_ids): await member.add_roles(reward_role, reason="完成指定频道全消息反应,自动分配角色") # 可选:发私信通知,用户关了私信就跳过 try: await member.send(f"恭喜你完成{target_channel.name}频道的全部反应任务,已为你开通【{reward_role.name}】角色") except discord.Forbidden: pass if __name__ == "__main__": bot.run(BOT_TOKEN)
踩坑提示
- 这里用
on_raw_reaction_add而不是普通的on_reaction_add事件,是因为raw事件不依赖本地消息缓存,机器人重启后也能正常捕获历史消息上的反应,不会漏触发 - 自定义表情的格式获取:在Discord聊天框输入
\后点击要使用的自定义表情,发送后得到的文本就是正确的格式,直接复制到TARGET_EMOJI配置即可 - Discord接口默认单次拉频道历史消息最多返回1000条,如果你的目标频道消息量超过1000条,需要自己写按时间锚点分页拉取的逻辑,普通新手场景消息量不大的话默认写法足够用
- 如果频道消息量很大,每次反应都全量遍历统计会有性能损耗,可以额外加一层用户完成记录的缓存,用户每完成一条消息的反应就记到对应集合里,不用每次全量扫描,先跑通基础逻辑再做优化即可
- 如果需要实现「用户取消任意一条消息的反应就自动收回角色」的逻辑,新增
on_raw_reaction_remove事件,反向统计完成数,不满足阈值时移除对应角色就行
内容的提问来源于stack exchange,提问作者Hi_Samu
相关产品推荐
相关产品推荐

