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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:18:35