Discord.py技术问题:将消息反应用户ID分存至JSON文件
Discord机器人反应收集功能修复问题
需求概述
开发Discord机器人,实现以下功能:
- 新成员加入服务器时,发送带倒计时的Embed消息,用户可通过✅和❌投注
- 5分钟投注结束后,将投✅的用户ID存入
reaction_tick.json,投❌的存入reaction_cross.json
问题现状
reactioncheck函数运行后无任何效果且无报错,无法收集对应反应的用户ID并写入JSON文件。
问题代码片段
for reactions in message.reactions: if str(reactions) == "✅": user_list = [user async for user in message.reactions.users() if user != client.user] for user in user_list: users = user.id + "\n" with open ("reaction_tick.json", "w") as f: json.dump(users,f) return True for reactions in message.reactions: if str(reactions) == "❌": user_list = [user async for user in message.reactions.users() if user != client.user] for user in user_list: users = user.id + "\n" with open ("reaction_cross.json", "w") as f: json.dump(users,f) return True
完整代码
@client.event async def on_member_join(member): channel = client.get_channel(1003203232824307745) embed=discord.Embed(title=f'{member.name}#{member.discriminator} has joined the server!', description="Will they stay or will they leave? Place your bets!", color=0xf1c40f, inline=True) embed.add_field(name='Will they stay longer than 1 hour?', value=f'React stay ✅') embed.add_field(name='Will they leave before 1 hour is up?', value=f'React leave ❌') embed.add_field(name='Betting will close in:', value=f'5 minutes', inline=False) embed.add_field(name='Results will be posted in', value=f'1 hour!', inline=True) message = await channel.send(embed=embed) await message.add_reaction('✅') await message.add_reaction('❌') time = 3600 id = member.id while time >= 0: if time <= 0: embed.remove_field(index=3) embed.insert_field_at(index=3, name='Betting complete! Results have been posted!', value=f'Ended at {datetime.datetime.now().strftime("%B %d, %I:%M %p")}', inline=False) await message.edit(embed=embed) await membercheck(id, channel) await reactioncheck(message) return elif 1 <= time < 50: embed.remove_field(index=3) embed.insert_field_at(index=3, name='Results in:', value=f'{time:.1f} seconds', inline=False) await message.edit(embed=embed) time -= 10 await asyncio.sleep(10) elif 60 == time: embed.remove_field(index=3) embed.insert_field_at(index=3, name='Results in:', value=f'{time/60:.1f} minute', inline=False) await message.edit(embed=embed) time -= 10 await asyncio.sleep(10) elif 60 <= time < 3290: embed.remove_field(index=3) embed.insert_field_at(index=3, name='Results in:', value=f'{time/60:.1f} minutes', inline=False) await message.edit(embed=embed) time -= 10 await asyncio.sleep(10) elif 3300 == time: await reactioncheck(message) #This is the function that I was having trouble with above. embed.remove_field(index=2) embed.insert_field_at(index=2, name='Betting has closed:', value=f'Ended at {datetime.datetime.now().strftime("%B %d, %I:%M %p")}', inline=False) await message.edit(embed=embed) await message.clear_reactions() time -= 10 await asyncio.sleep(10) elif 3300 <= time < 3600: await reactioncheck(message) embed.remove_field(index=2) embed.insert_field_at(index=2, name='Betting will close in:', value=f'{time/60 - 55:.1f} minutes', inline=False) embed.remove_field(index=3) embed.insert_field_at(index=3, name='Results in:', value=f'{time/60:.1f} minutes', inline=False) await message.edit(embed=embed) time -= 10 await asyncio.sleep(10) async def reactioncheck(message): rmsg = message #the message with reactions for reaction in rmsg.reactions: users = "" #the string to dump if str(reaction) == "✅": user_list = [user async for user in reaction.users() if user != client.user] #it should be reaction.users() and not <message>.reactions.users() for user in user_list: users = users + str(user.id) + "\n" #converting ID to str and adding instead of overwriting with open("reaction_tick.json", "w") as f: json.dump(users, f) return True for reaction in rmsg.reactions: if str(reaction) == "❌": user_list = [user async for user in reaction.users() if user != client.user] for user in user_list: users = users + str(user.id) + "\n" with open("reaction_cross.json", "w") as f: json.dump(users, f) return True
问题分析与修复
原reactioncheck函数存在以下问题:
- 提前终止函数:处理第一个用户后就执行
return True,导致仅收集到第一个用户ID就停止 - 文件写入逻辑错误:循环内每次写入文件会覆盖之前内容,最终只保留最后一个用户ID
- 变量未初始化:处理❌反应时
users变量未定义,会引发运行错误
修正后的reactioncheck函数
async def reactioncheck(message): # 初始化列表存储用户ID,便于JSON序列化和后续读取 tick_users = [] cross_users = [] for reaction in message.reactions: # 收集✅反应的用户ID if str(reaction) == "✅": async for user in reaction.users(): if user != client.user: tick_users.append(str(user.id)) # 收集❌反应的用户ID elif str(reaction) == "❌": async for user in reaction.users(): if user != client.user: cross_users.append(str(user.id)) # 批量写入✅用户ID到JSON文件 with open("reaction_tick.json", "w") as f: json.dump(tick_users, f) # 批量写入❌用户ID到JSON文件 with open("reaction_cross.json", "w") as f: json.dump(cross_users, f) return True
额外优化建议
- 避免重复调用:原代码在
3300 <= time < 3600区间每10秒调用一次reactioncheck,只需在投注关闭时(3300 == time分支)调用一次即可 - 依赖模块检查:确保代码顶部导入
json、asyncio、datetime等必要模块,避免运行报错 - 数据结构优化:用列表存储用户ID比字符串拼接更规范,JSON序列化后结构清晰,便于后续业务处理
内容的提问来源于stack exchange,提问作者Swag Snags
相关产品推荐
相关产品推荐

