discord.py基于JSON实现的temprole命令重启后失效问题求助
问题根因
你当前的实现存在两个核心缺陷导致重启后失效:
asyncio.sleep()是基于当前进程内存的异步等待任务,机器人重启后所有未执行完的等待任务会被直接销毁,后续的移除身份组逻辑自然不会运行- 你的JSON文件仅存储了原始时长字符串(如
1h),没有存储绝对到期时间戳,重启后无法判断已有临时身份组的剩余有效时间
修复方案
分三步修改:
- 调整JSON存储结构,统一用字符串类型的用户ID为键,存储到期Unix时间戳、身份组ID、所属服务器ID三个核心字段,放弃存储没用的角色mention字段
- 在机器人的
on_ready事件中添加初始化逻辑,启动时遍历JSON所有记录,对已到期的直接移除身份组并清理记录,对未到期的创建后台异步任务等待到期执行移除 - 修改原temp命令逻辑,不再在命令函数内直接等待,将到期移除逻辑拆分为独立的异步任务函数,避免命令阻塞
完整修正代码
import json import time import asyncio import discord from discord.ext import commands time_convert = {"s":1, "m":60, "h":3600,"d":86400} client = commands.Bot(command_prefix="!", intents=discord.Intents.all()) # 独立的到期移除身份组任务 async def remove_temp_role(guild_id: int, member_id: int, role_id: int, expire_at: float): # 计算剩余等待时间 wait_time = expire_at - time.time() if wait_time > 0: await asyncio.sleep(wait_time) # 执行移除逻辑 guild = client.get_guild(guild_id) if not guild: # 服务器不存在直接清记录 with open("data.json", "r") as f: data = json.load(f) if str(member_id) in data: del data[str(member_id)] with open("data.json", "w") as f: json.dump(data, f) return member = guild.get_member(member_id) role = guild.get_role(role_id) if member and role and role in member.roles: await member.remove_roles(role) # 清理JSON记录 with open("data.json", "r") as f: data = json.load(f) if str(member_id) in data: del data[str(member_id)] with open("data.json", "w") as f: json.dump(data, f) @client.event async def on_ready(): # 启动时初始化所有未到期的临时身份组任务 try: with open("data.json", "r") as f: data = json.load(f) except FileNotFoundError: # 文件不存在就初始化空字典 with open("data.json", "w") as f: json.dump({}, f) data = {} for member_id_str, item in data.items(): member_id = int(member_id_str) guild_id = item["guild_id"] role_id = item["role_id"] expire_at = item["expire_at"] # 为每个未到期的创建后台任务 client.loop.create_task(remove_temp_role(guild_id, member_id, role_id, expire_at)) print(f"已登录为 {client.user}") @client.command() @commands.guild_only() async def temp(ctx, member:discord.Member=None, time2=None): guild = ctx.guild role = discord.utils.get(guild.roles, name="Role") if not role: return await ctx.send("未找到对应身份组") if member == None: return await ctx.send("You have to mention the member that you want to add the role to!") if time2 == None: return await ctx.send("You have to type the time for which you want to give the role to the mentioned member!") # 读取JSON数据 try: with open("data.json", "r") as f: data = json.load(f) except FileNotFoundError: data = {} if str(member.id) in data: return await ctx.send("hmm") # 计算到期时间戳 try: duration = int(time2[:-1]) * time_convert[time2[-1]] except (ValueError, KeyError): return await ctx.send("时间格式错误,示例:10s、5m、2h、1d") expire_at = time.time() + duration # 写入JSON data[str(member.id)] = { "guild_id": guild.id, "role_id": role.id, "expire_at": expire_at } with open("data.json", "w") as f: json.dump(data, f) # 添加身份组 await member.add_roles(role) await ctx.send(f"Successfully added the `{role.name}` role to **{member.name}#{member.discriminator}** for {time2}") # 创建后台移除任务 client.loop.create_task(remove_temp_role(guild.id, member.id, role.id, expire_at)) client.run("你的机器人Token")
注意事项
- 必须开启机器人的成员网关意图(Privileged Gateway Intents里的Server Members Intent),否则无法正常获取服务器成员列表
- 代码里的命令前缀、身份组名称根据自己的需求修改即可
- 如果服务器较多可以给JSON结构加服务器ID层级,避免不同服务器同用户ID冲突
内容的提问来源于stack exchange,提问作者Captain Underpants
相关产品推荐
相关产品推荐

