Issue
I've been attempting to solve an issue I have in my server's economy where the leaderboard still shows stats of players who left. To this end, I am trying to create a clean_economy
command, where the bot automatically removes the json information from players no longer in the server. (Yes, I know alternatives to JSON exist, but I only use this bot for one server, and it's relatively lightweight in terms of users so far.)
Here is the code I currently have. Note that it is in a cog, and all intents are enabled:
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!")
...
The error I keep getting is:
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
I've tried to get around this error by adding the users_to_remove
portion, but to no avail. I need this to JUST remove those who are no longer in the server, not everyone.
Solution
Your error is in these lines:
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]
You're removing items from bank
while looping over the items view. What you can do instead is to use a list of keys as reference, separate from the dict:
user_ids = list(bank.keys())
for user_id in user_ids:
user_data = bank.get(user_id)
member = ctx.guild.get_member(user_id)
if member is None:
del bank[user_id]
Your idea with users_to_remove
could have worked, if you had changed its indentation to outside of the loop on bank.items()
:
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]
Answered By - Sébastien Vercammen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.