I need help with my Discord Bot (wallet/bank-> save)

So I tried everything I could to make my Discord Bot save the amount of money the users have that use him. But every time my Discord bot goes offline, every user is losing his whole money. here is the code: (the important part with saving the wallet and stuff is in the top)

import discord
from discord.ext import commands,tasks  # Import 'tasks' separately
from datetime import datetime, timedelta
import random
import json
import youtube_dl
import os
import atexit

intents = discord.Intents.default()
intents.typing = False
intents.presences = False

DATA_FILE = os.path.join(os.path.expanduser('~'), 'Desktop', 'wallet_data.json')

if not os.path.exists(DATA_FILE):
    with open(DATA_FILE, 'w') as file:
        json.dump({}, file)

bot = commands.Bot(command_prefix='.', intents=discord.Intents.all())
TOKEN = 'MTE4NDkzMTMxNTM2NDE0NzI4Mg.GWByxt.jB2ofEdhI4VCKKI870Ao2maa2WWT-qkt7VA3Eg'  # Replace with your bot token

user_wallets = {}
last_saved_wallets = {}  # Keep track of the last saved wallet state

@tasks.loop(seconds=10)
async def autosave_wallet():
    global user_wallets, last_saved_wallets

    if user_wallets != last_saved_wallets:
        try:
            with open(DATA_FILE, 'w') as file:
                json.dump(user_wallets, file, indent=4)
            last_saved_wallets = user_wallets
            print("Wallet data saved successfully.")
        except Exception as e:
            print(f"Error saving wallet data: {e}")

@autosave_wallet.before_loop
async def before_autosave_wallet():
    print('Waiting for bot to be ready...')
    await bot.wait_until_ready()

def save_wallet_on_exit():
    global user_wallets
    print('Bot is shutting down. Saving wallet data...')
    try:
        with open(DATA_FILE, 'w') as file:
            json.dump(user_wallets, file, indent=4)
    except Exception as e:
        print(f"Error saving wallet data: {e}")

atexit.register(save_wallet_on_exit)

@bot.event
async def on_ready():
    global user_wallets
    print(f'We have logged in as {bot.user}')
    try:
        with open(DATA_FILE, 'r') as file:
            user_wallets = json.load(file)
    except FileNotFoundError:
        user_wallets = {}
        print("No wallet data file found. Starting with empty wallet data.")

    # Ensure autosave loop is started
    autosave_wallet.start()

user_command_usage = {}

# File to store command usage data
COMMANDS_FILE = 'commands_data.json'

@bot.event
async def on_ready():
    print(f'We have logged in as {bot.user}')

# Load user command usage data from the file
try:
    with open(COMMANDS_FILE, 'r') as file:
        user_command_usage = json.load(file)
except FileNotFoundError:
    user_command_usage = {}

@bot.event
async def on_disconnect():
    # Save user command usage data to the file when the bot goes offline
    with open(COMMANDS_FILE, 'w') as file:
        json.dump(user_command_usage, file, indent=4)

@bot.event
async def on_disconnect():
    # Save user wallet data to the file when the bot goes offline
    with open(DATA_FILE, 'w') as file:
        json.dump(user_wallets, file, indent=4)

@bot.event
async def on_shutdown():
    # Save user wallet data to the file when the bot shuts down
    with open(DATA_FILE, 'w') as file:
        json.dump(user_wallets, file, indent=4)

@bot.command()
async def globalstats(ctx):
    top_users = sorted(user_command_usage.items(), key=lambda x: x[1], reverse=True)[:10]

    # Prepare the leaderboard message
    leaderboard_message = "**Top 10 Users Global (Commands Used):**\n"
    for index, (user_id, command_count) in enumerate(top_users):
        user = bot.get_user(int(user_id))
        username = user.display_name if user else "Unknown User"
        
        if index == 0:
            leaderboard_message += f":first_place: {username}: {command_count} commands\n"
        elif index == 1:
            leaderboard_message += f":second_place: {username}: {command_count} commands\n"
        elif index == 2:
            leaderboard_message += f":third_place: {username}: {command_count} commands\n"
        else:
            leaderboard_message += f"{username}: {command_count} commands\n"

    await ctx.send(leaderboard_message)

@bot.command()
async def daily(ctx):
    user_id = str(ctx.author.id)
    if user_id not in user_wallets:
        user_wallets[user_id] = 0

    cooldown = 20  # hours
    last_daily_time = user_wallets.get(user_id + "_daily", datetime.min)
    remaining_time = last_daily_time + timedelta(hours=cooldown) - datetime.now()
    remaining_hours, remaining_minutes = divmod(remaining_time.seconds, 3600)
    remaining_minutes //= 60

    if datetime.now() - last_daily_time >= timedelta(hours=cooldown):
        amount = random.randint(250, 800)
        user_wallets[user_id] += amount
        user_wallets[user_id + "_daily"] = datetime.now()
        await ctx.send(f"Received daily reward: ${amount}")
    else:
        await ctx.send(f"Cooldown not expired for daily reward. Try again in {remaining_hours} hours and {remaining_minutes} minutes.")

@bot.command()
async def join(ctx):
    channel = ctx.author.voice.channel
    await channel.connect()

@bot.command(name='ilian', aliases=['love', 'liebe', 'angela'])
async def command_love(ctx):
    await ctx.send("Love you my Cutie <3 You will always be stuck in 2018 with me my sunshine <3")

@bot.command()
async def play(ctx, url):
    voice_channel = ctx.author.voice.channel
    vc = await voice_channel.connect()

    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegVideo',
            'preferredcodec': 'mp4',
            'preferredquality': '192',
        }],
    }

    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(url, download=False)
        url2 = info['formats'][0]['url']

    vc.play(discord.FFmpegPCMAudio(url2))

@bot.command()
async def hourly(ctx):
    user_id = str(ctx.author.id)
    if user_id not in user_wallets:
        user_wallets[user_id] = 0

    cooldown = 1  # hour
    last_hourly_time = user_wallets.get(user_id + "_hourly", datetime.min)
    remaining_time = last_hourly_time + timedelta(hours=cooldown) - datetime.now()
    remaining_hours, remaining_minutes = divmod(remaining_time.seconds, 3600)
    remaining_minutes //= 60

    if datetime.now() - last_hourly_time >= timedelta(hours=cooldown):
        amount = random.randint(34, 59)
        user_wallets[user_id] += amount
        user_wallets[user_id + "_hourly"] = datetime.now()
        await ctx.send(f"Received hourly reward: ${amount}")
    else:
        await ctx.send(f"Cooldown not expired for hourly reward. Try again in {remaining_hours} hours and {remaining_minutes} minutes.")

@bot.command()
async def loot(ctx):
    user_id = str(ctx.author.id)
    if user_id not in user_wallets:
        user_wallets[user_id] = 0

    # Determine the outcome based on the chances provided
    outcome = random.choices(
        ['spoons', 'v9_battery', 'worm', 'baseball_bat', 'glue', 'tube', 'keyboard', 'car_wheel', 'steel_helmet', 'butterfly_marble_fade'],
        weights=[10.00, 10.00, 30.00, 10.00, 10.00, 10.00, 5.00, 5.00, 9.96, 0.04]
    )[0]

    if outcome == 'spoons':
        amount = 3
        user_wallets[user_id] += amount
        await ctx.send(f"You found spoons and earned: ${amount}")
    elif outcome == 'v9_battery':
        amount = 5
        user_wallets[user_id] += amount
        await ctx.send(f"You found a V9-Battery and earned: ${amount}")
    elif outcome == 'worm':
        amount = 1
        user_wallets[user_id] += amount
        await ctx.send(f"You found a worm and earned: ${amount}")
    elif outcome == 'baseball_bat':
        amount = 7
        user_wallets[user_id] += amount
        await ctx.send(f"You found a baseball bat and earned: ${amount}")
    elif outcome == 'glue':
        amount = 4
        user_wallets[user_id] += amount
        await ctx.send(f"You found 450g of glue and earned: ${amount}")
    elif outcome == 'tube':
        amount = 2
        user_wallets[user_id] += amount
        await ctx.send(f"You found a tube and earned: ${amount}")
    elif outcome == 'keyboard':
        amount = 20
        user_wallets[user_id] += amount
        await ctx.send(f"You found a keyboard and earned: ${amount}")
    elif outcome == 'car_wheel':
        amount = 22
        user_wallets[user_id] += amount
        await ctx.send(f"You found a car wheel and earned: ${amount}")
    elif outcome == 'steel_helmet':
        amount = 25
        user_wallets[user_id] += amount
        await ctx.send(f"You found a steel helmet and earned: ${amount}")
    elif outcome == 'butterfly_marble_fade':
        amount = 3337
        user_wallets[user_id] += amount
        await ctx.send(f"You found a Butterfly Marble Fade and earned: ${amount}")

@bot.command()
async def work(ctx):
    user_id = str(ctx.author.id)
    if user_id not in user_wallets:
        user_wallets[user_id] = 0

    cooldown = 10  # minutes
    last_work_time = user_wallets.get(user_id + "_work", datetime.min)
    remaining_time = last_work_time + timedelta(minutes=cooldown) - datetime.now()
    remaining_minutes = remaining_time.seconds // 60

    if datetime.now() - last_work_time >= timedelta(minutes=cooldown):
        amount = random.randint(5, 20)
        user_wallets[user_id] += amount
        user_wallets[user_id + "_work"] = datetime.now()
        await ctx.send(f"Earned money from work: ${amount}")
    else:
        await ctx.send(f"Cooldown not expired for work. Try again in {remaining_minutes} minutes.")

@bot.command()
async def slots(ctx, bet: int):
    user_id = str(ctx.author.id)
    if user_id not in user_wallets:
        user_wallets[user_id] = 0

    # Check if the user has enough money to place the bet
    if user_wallets[user_id] >= bet:
        user_wallets[user_id] -= bet

        # Determine the outcome based on the chances provided
        outcome = random.choices(['win', 'lose', 'blessed', 'cs_knife'], weights=[0.49, 0.49, 0.0096, 0.0004])[0]

        if outcome == 'win':
            user_wallets[user_id] += 2 * bet
            await ctx.send(f"You won! Doubled your bet: ${2 * bet}")
        elif outcome == 'blessed':
            user_wallets[user_id] += 10 * bet
            await ctx.send(f"You got blessed by the Universe -> 10x your bet!")
        elif outcome == 'cs_knife':
            user_wallets[user_id] += 500 * bet
            await ctx.send(f"x500 !!!!! The Chances of hitting this are 0.04% which is similar to pulling a CS Knife!")
        else:
            await ctx.send(f"You lost. Better luck next time!")

    else:
        await ctx.send("Insufficient funds to place the bet.")

@bot.command()
async def money(ctx):
    user_id = str(ctx.author.id)
    if user_id not in user_wallets:
        user_wallets[user_id] = 0

    await ctx.send(f"Your current wallet balance: ${user_wallets[user_id]}")

@bot.command()
async def top(ctx):
    top_users = sorted(user_wallets.items(), key=lambda x: x[1] if isinstance(x[1], int) else 0, reverse=True)[:10]

    # Prepare the leaderboard message
    leaderboard_message = "**Top 10 Users Global:**\n"
    for index, (user_id, money) in enumerate(top_users):
        user = bot.get_user(int(user_id))
        username = user.display_name if user else "Unknown User"
        
        if index == 0:
            leaderboard_message += f":first_place: {username}: ${money}\n"
        elif index == 1:
            leaderboard_message += f":second_place: {username}: ${money}\n"
        elif index == 2:
            leaderboard_message += f":third_place: {username}: ${money}\n"
        else:
            leaderboard_message += f"{username}: ${money}\n"

    await ctx.send(leaderboard_message)

@bot.command()
async def givemoney(ctx, username: discord.Member, amount: int):
    sender_id = str(ctx.author.id)
    recipient_id = str(username.id)
    # Check if the sender and recipient are different users
    if sender_id != recipient_id:
        # Check if the sender has enough money to send
        if user_wallets.get(sender_id, 0) >= amount > 0:
            # Deduct money from the sender
            user_wallets[sender_id] -= amount
            # Add money to the recipient
            if recipient_id not in user_wallets:
                user_wallets[recipient_id] = 0
            user_wallets[recipient_id] += amount
            await ctx.send(f"Successfully sent ${amount} to {username.display_name}.")
        else:
            await ctx.send("Invalid amount or insufficient funds to send money.")
    else:
        await ctx.send("Cannot send money to yourself.")

DATA_FILE = 'wallet_data.json'

# Load user wallet data from the file
try:
    with open(DATA_FILE, 'r') as file:
        user_wallets = json.load(file)
except FileNotFoundError:
    user_wallets = {}

@bot.event
async def on_ready():
    print(f'We have logged in as {bot.user}')

@bot.event
async def on_disconnect():
    # Save user wallet data to the file when the bot goes offline
    with open(DATA_FILE, 'w') as file:
        json.dump(user_wallets, file, indent=4)

Rest of your code…

Ensure that the following line is aligned with the outermost indentation

bot.run(TOKEN)

Hello. It’s a little difficult to make heads or tails of your code as it has became intertwined with your post and only a few snippets are viewable as code. Without being able to see the indentation properly, it makes thing a little tougher. Can you perhaps link us to your GitHub repository or provide a gist?

Please tell me that it isn’t your actual Discord bot token. It’s an absolutely terrible idea to put sensitive information directly in the source code like that. I recommend using something like python-dotenv package to dynamically load sensitive information like that into your program.

My questions that need to be answered:

  • Does the "wallet_data.json file exist on your desktop?
  • If so, what are the contents?
  • What values are being loaded into memory when the application boots up?
  • Does your write function know what values are stored in the user_wallets variable?
  • Does it have the values before the commands properly run?

My initial assumption is that in the command functions is that you aren’t able to retrieve the values without setting user_wallets as a global in the function. But there is always the possibility I am missing something.

Hi @a2937 !
I have edited the code for readability. Are you able to see the whole code?

@Windler12 Is this the whole code that you intended to post?

Yes I can see the whole code now. Thank you.

Thanks for your help, no that’s not my Bot taken but I respect the warning you gave me :smiley:
I sended the whole code in BUT somehow it got cut off :confused:
Yes the wallet_data.json file does exist on my desktop
the content is only: {}
There are no values being loaded into this wallet_data.json file when the application boots up
Since I don’t know what : , * Does your write function know what values are stored in the user_wallets variable?" mean, it probably don’t know because I don’t even know what that means so I didn’t code it for sure.

Actually I did set the user_wallets as a global once, but it still didn’t save anything to wallet_data.json

I made a really small prototype of saving and loading and see it seemed fine. I will need to you to print user_wallets to the console to see what values are stored when the bot is running. I also really don’t like how you have two on_disconnect methods. It’s probably better if you combine them.

import json
import os
import random
DATA_FILE = os.path.join(os.path.expanduser(
    '~'), 'Desktop', 'test_data.json')

user_data = {}

if not os.path.exists(DATA_FILE):
    with open(DATA_FILE, 'w') as file:
        json.dump({}, file)
else:
    with open(DATA_FILE, 'r') as file:
        user_data = json.load(file)


def modify_data():
    global user_data
    index = random.randint(1, 10)
    user_data[index] = "Test"
    print(user_data)


def save_data():
    global user_data
    with open(DATA_FILE, 'w') as file:
        # the indent is optional, just makes it look nice
        json.dump(user_data, file, indent=2)


modify_data()
save_data()

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.