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

discord.py基于JSON实现的temprole命令重启后失效问题求助

问题根因

你当前的实现存在两个核心缺陷导致重启后失效:

  • asyncio.sleep() 是基于当前进程内存的异步等待任务,机器人重启后所有未执行完的等待任务会被直接销毁,后续的移除身份组逻辑自然不会运行
  • 你的JSON文件仅存储了原始时长字符串(如1h),没有存储绝对到期时间戳,重启后无法判断已有临时身份组的剩余有效时间

修复方案

分三步修改:

  1. 调整JSON存储结构,统一用字符串类型的用户ID为键,存储到期Unix时间戳、身份组ID、所属服务器ID三个核心字段,放弃存储没用的角色mention字段
  2. 在机器人的on_ready事件中添加初始化逻辑,启动时遍历JSON所有记录,对已到期的直接移除身份组并清理记录,对未到期的创建后台异步任务等待到期执行移除
  3. 修改原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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 12:18:03