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

如何清理Discord服务器已离开成员的经济数据(d.py)

解决Discord经济系统清理已离开玩家数据的RuntimeError问题

我在Discord服务器的经济系统里遇到了问题:排行榜仍显示已离开服务器的玩家统计数据。为此我尝试创建clean_economy命令,让机器人自动移除不在服务器内玩家的JSON数据(我知晓JSON的替代方案,但该机器人仅用于单个服务器,目前用户量较少)。以下是当前代码(位于cog中,已启用所有intents):

import discord
from discord.ext import commands
from discord import app_commands
import json
import random
import time
import asyncio
import datetime
from typing import Literal
import datetime
from datetime import timedelta

...

@commands.hybrid_command(name="clean_economy", description="Removes the people who are no longer in the server from the economy module.")
@commands.has_role("*")
async def clean_economy(self, ctx: commands.Context) -> None:
    with open("cogs/bank.json", "r") as f:
        bank = json.load(f)
    await ctx.send("Are you SURE?")
    def check(m):
        return m.author == ctx.author and m.channel == ctx.channel
    msg = await self.client.wait_for("message", check=check, timeout=60.0)
    if msg.content.lower() == "yes":
        await ctx.send("Cleaning economy...")
        await asyncio.sleep(2.0)
        users_to_remove = []
        for user_id, user_data in bank.items():
            member = ctx.guild.get_member(user_id)
            if member is None:
                users_to_remove.append(user_id)
            for user_id in users_to_remove:
                del bank[user_id]
        with open("cogs/bank.json", "w") as f:
            json.dump(bank, f)
        await ctx.send("Done!")
    else:
        await ctx.send("Cancelled!")

...

运行时出现错误:

Traceback (most recent call last):
  File "/home/runner/Capitol-Hill-Bot-1/.pythonlibs/lib/python3.10/site-packages/discord/ext/commands/core.py", line 235, in wrapped
    ret = await coro(*args, **kwargs)
  File "/home/runner/Capitol-Hill-Bot-1/cogs/economy.py", line 984, in clean_economy
    for user_id, user_data in bank.items():
RuntimeError: dictionary changed size during iteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/runner/Capitol-Hill-Bot-1/.pythonlibs/lib/python3.10/site-packages/discord/ext/commands/bot.py", line 1350, in invoke
    await ctx.command.invoke(ctx)
  File "/home/runner/Capitol-Hill-Bot-1/.pythonlibs/lib/python3.10/site-packages/discord/ext/commands/core.py", line 1029, in invoke
    await injected(*ctx.args, **ctx.kwargs)  # type: ignore
  File "/home/runner/Capitol-Hill-Bot-1/.pythonlibs/lib/python3.10/site-packages/discord/ext/commands/core.py", line 244, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: RuntimeError: dictionary changed size during iteration

我尝试通过users_to_remove列表规避该错误,但未成功,需要仅移除已离开服务器的成员数据,而非全部用户。


问题原因

你在遍历字典bank.items()的同时,在循环内部执行了删除字典元素的操作,导致字典大小在迭代过程中改变,触发了RuntimeError。虽然用了users_to_remove列表,但删除操作被嵌套在遍历字典的循环里,本质还是在迭代时修改字典。

修正后的代码

@commands.hybrid_command(name="clean_economy", description="Removes the people who are no longer in the server from the economy module.")
@commands.has_role("*")
async def clean_economy(self, ctx: commands.Context) -> None:
    with open("cogs/bank.json", "r") as f:
        bank = json.load(f)
    await ctx.send("Are you SURE?")
    
    def check(m):
        return m.author == ctx.author and m.channel == ctx.channel
    
    # 增加超时处理,避免命令卡住
    try:
        msg = await self.client.wait_for("message", check=check, timeout=60.0)
    except asyncio.TimeoutError:
        await ctx.send("操作超时,已取消!")
        return
        
    if msg.content.lower() == "yes":
        await ctx.send("Cleaning economy...")
        await asyncio.sleep(2.0)
        users_to_remove = []
        
        # 先遍历所有用户,收集需要删除的ID(注意JSON中ID是字符串,需转int验证)
        for user_id_str, user_data in bank.items():
            user_id = int(user_id_str)
            member = ctx.guild.get_member(user_id)
            if member is None:
                users_to_remove.append(user_id_str)
        
        # 统一删除收集到的用户数据,避开迭代时修改字典
        for user_id_str in users_to_remove:
            del bank[user_id_str]
        
        # 保存时增加缩进,让JSON更易读
        with open("cogs/bank.json", "w") as f:
            json.dump(bank, f, indent=4)
            
        await ctx.send(f"清理完成!共移除了{len(users_to_remove)}个已离开服务器的玩家数据。")
    else:
        await ctx.send("已取消!")

关键修正点

  1. 分离遍历与删除操作:先收集所有需要删除的用户ID,再统一执行删除,避免迭代字典时修改其大小。
  2. 类型转换处理:JSON文件中存储的用户ID是字符串类型,ctx.guild.get_member()需要整数类型的ID,必须做类型转换,否则会误判所有用户为已离开。
  3. 增加超时处理:避免用户长时间不响应导致命令卡住。
  4. 优化JSON格式:保存时添加indent=4,让JSON文件结构更清晰。

内容的提问来源于stack exchange,提问作者Ethan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 18:05:26