如何清理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("已取消!")
关键修正点
- 分离遍历与删除操作:先收集所有需要删除的用户ID,再统一执行删除,避免迭代字典时修改其大小。
- 类型转换处理:JSON文件中存储的用户ID是字符串类型,
ctx.guild.get_member()需要整数类型的ID,必须做类型转换,否则会误判所有用户为已离开。 - 增加超时处理:避免用户长时间不响应导致命令卡住。
- 优化JSON格式:保存时添加
indent=4,让JSON文件结构更清晰。
内容的提问来源于stack exchange,提问作者Ethan
相关产品推荐
相关产品推荐

