Seite 1 von 1

JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 18:26
von Buck_ClaSh
Also ich habe für meinen Discord Bot ein Message Rank System geschrieben, jedoch wird aus irgendeinem Grund ständig ein neues JSON Object erstellt. Ich vermute dies liegt an der update_data(), jedoch verstehe ich nicht ganz wieso dies falsch ist :(

Ich bedanke mich schonmal im vorraus für eure Hilfe :)

Code: Alles auswählen

import os
import discord
import json
from dotenv import load_dotenv
from discord.ext import commands
from user import User

############## Init Variables #############

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
DATA = os.getcwd() + '\\data\\'

bot = commands.Bot(command_prefix='+')
bot.remove_command('help')

rank_list = []

############## Main Code #############
@bot.event
async def on_ready():
    for guild in bot.guilds:
        if guild.name == GUILD:
            break

    print(
        f'{bot.user} is connected to the following guild:\n'
        f'{guild.name}(id: {guild.id})'
    )

    if(os.path.isfile(DATA + 'users.json')):
        print('Database File for Rank System exists and is ready for processing!')
    else:
        print('Database File doesnt exist and will be created....')
        create_file()
        print('File is created and ready for use!')

@bot.event
async def on_message(message):
    #rank_system(message)

    await bot.process_commands(message)

    if message.author.id != 747719515135017001:
        with open(os.path.join(DATA, "users.json"),'r') as tmp:
            users = json.load(tmp)
        
        await update_data(users, message.author)
        await add_xp(users, message.author, 5)
        await level_up(users, message.author, message)

        with open(os.path.join(DATA, "users.json"),'w') as tmp:
            json.dump(users, tmp)


@bot.event
async def on_member_join(member):
    with open(os.path.join(DATA, "users.json"),'r') as tmp:
        users = json.load(tmp)
    
    await update_data(users, member)

    with open(os.path.join(DATA, "users.json"),'w') as tmp:
        json.dump(users, tmp)

############## Commands ################

@bot.command(name='help')
async def help_command(ctx):
    embed = discord.Embed(
        title = "Bot Commands",
        description = "Hier werden alle Commands von diesem Bot aufgelistet. Alle nachfolgenden Commands müssen mit dem Präfix '+' aufgerufen werden.",
        color = discord.Color.blue()
    )
    embed.add_field(name="Algorithmen", value="Lexikon", inline=True)
    embed.add_field(name="Datenstrukturen", value="Lexikon", inline=True)
    embed.add_field(name="macarena", value="Fun Commands", inline=True)
    await ctx.send(embed=embed)
    

@bot.command(name='rank')
async def get_rank(message):
    id = str(message.author.id)
    with open(os.path.join(DATA, "users.json"),"r") as tmp:
        users = json.load(tmp)
    
    lvl = users[id]['level']
    experience = users[id]['xp']
    response = f"You are Rank {lvl} and you have {experience}."
    await message.channel.send(response)
    

@bot.command(name='Algorithmen')
async def algo_command(message):
    response = "Es gibt viele verschiedene Algorithmen hier eine kurze Auflistung von den bekanntesten:\n\n- Bubble Sort\n- Quick Sort\n- Merge Sort"
    await message.channel.send(response)

@bot.command(name='Datenstrukturen')
async def datenstrukturen_command(message):
    response = "Es gibt viele verschiedene Datenstrukturen hier eine kurze Auflistung von den bekanntesten:\n\n- Stack\n- Queue\n- List"
    await message.channel.send(response)


@bot.command()
async def macarena(message):
    print("Funktioniert")
    response = "Hast du ernsthaft so viel Langeweile, dass du diesen Command ausprobierst....Schäm dich ;)"
    await message.channel.send(response)

#Sound Board


############  Helper Functions #############
async def update_data(users, user):
    if not(user.id in users):
        users[user.id] = {}
        users[user.id]['xp'] = 0
        users[user.id]['level'] = 1

async def add_xp(users, user, xp):
    print("Old XP: ", users[user.id]['xp'])
    users[user.id]['xp'] += xp
    print("New XP: ", users[user.id]['xp'])

async def level_up(users, user, message):
    experience = users[user.id]['xp']
    lvl_start = users[user.id]['level']
    lvl_end = int(experience ** (1/4))

    print("Experience: ", experience)
    print("Level Start: ", lvl_start)
    print("Level End: ", lvl_end)

    if lvl_start < lvl_end:
        await message.channel.send('{} ist ein Level aufgestiegen. Er ist nun {}'.format(user.mention, lvl_end))
        users[user.id]['level'] = lvl_end

def create_file():
    open(os.path.join(DATA, "users.json"), "x")


bot.run(TOKEN)

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 18:56
von Sirius3
`load_dotenv` ist irgendwie eine unsinnige Funktion, wenn sie weder Übergabe noch Rückgabewerte hat.
DATA ist eher ein DATA_PATH. Dazu sollte man sagen, dass man keine Pfade per + zusammenstückelt, sondern man benutzt pathlib.Path:

Code: Alles auswählen

DATA_PATH = Path("data").absolute()
Hier ist seltsam, dass data relativ zum aktuellen Arbeitsverzeichnis liegt, dann muß ja immer ein data-Verzeichnis existieren, in dem Pfad, wo man das Programm startet.
Dabei sollte eigentlich data/users.json die Konstante sein:

Code: Alles auswählen

USERS_PATH = DATA_PATH / "users.json"
Die Funktion `create_file` ist totaler quatsch, da hier nur eine leere Datei erzeugt wird, die ja kein gültiges JSON ist.
`update_data` ist gar keine asynchrone Funktion, `not` ist keine Funktion und sollte auch nicht so geschrieben werden. Das Wörterbuch kann man auch gleich direkt mit Daten füllen:

Code: Alles auswählen

 def update_data(users, user):
    if user.id not in users:
        users[user.id] = {
            'xp': 0,
            'level': 1,
        }
Warum heißen alle Deine File-Objekte tmp? Das hat doch gar nichts mit Temperatur zu tun.

Woran merkst Du denn, dass die Wörterbücher doppelt erstellt werden?

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 20:32
von Buck_ClaSh
Also erstmal danke für deine schnelle Rückmeldung :)

Das mit load_dotenv() soll meines Wissens nach die .env Datei (in welcher der Bot Token und der Server Name stehen) laden. Ob das so stimmt weiß ich nicht xD

Das mit dem Pfad stimmt, es ist völlig schwachsinning wie ich das implementiert habe :)

Ich habe die File Objects tmp für temporary genannt xD

Naja ich benutze das JSON as Art Datenbank und benutze die Discord User ID als Key. Wenn ich nun etwas in den Chat tippe wird meine ID 2 mal in die JSON Datei gesteckt und die XP ist beim hinzufügen von neuer XP immer auf 0 resetet. Ich vermute das liegt an der fehlerhaften if abfrage in update_data.

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 20:52
von __blackjack__
@Buck_ClaSh: Du rufst bei jeder Nachricht die Funktion auf die ein neues Wörterbuch für den Benutzer erstellt und 'xp' und 'level' darin auf 0 bzw. 1 setzt. Also wird auch bei jeder Nachricht ein neues Wörterbuch erstellt und 'xp' und 'level' darin auf 0 bzw. 1 gesetzt. Ich verstehe jetzt nicht so recht warum Dich das überrascht, dass immer neue Wörterbücher erstellt werden.

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 21:08
von Buck_ClaSh
Naja eigentlich möchte ich das ja mit der if abfrage abfangen xD Er soll nur dann ein neues Dict anlegen wenn diese ID noch nicht vorhanden ist

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 21:44
von Sirius3
Das ist ein Wörterbuch, also kann eine ID nur einmal existieren. Von welchem Typ ist denn die User-ID?

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 21:55
von Buck_ClaSh
Die User ID ist vom Typ String

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 21:58
von __blackjack__
@Buck_ClaSh: Und womit vergleichst Du? Mit einer Zahl. Eine Zahl kann niemals gleich einer Zeichenkette sein.

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 22:05
von Buck_ClaSh
Könnte ich nicht einen None Prüfung machen? Ca so: if userer[id] == None do this

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 22:31
von Sirius3
Du suchst an der falschen Stelle. Daher ja meine Frage, was für einen Typ die User-ID hat.
JSON kann nur Strings als Schlüssel speichern. Und Du legst immer einen neuen Schlüssel an, weil User-ID eine Zahl ist.

Re: JSON Object wird doppelt erstellt

Verfasst: Donnerstag 27. August 2020, 22:33
von Buck_ClaSh
Achsoooo ich könnte doch die Zahl einfach als String interpretieren und dann vergleichen oder?

Re: JSON Object wird doppelt erstellt

Verfasst: Freitag 28. August 2020, 09:16
von Buck_ClaSh
Edit: Hab die ID als String interpretiert und jetzt funktioniert alles perfekt :) Danke für eure Hilfe

Re: JSON Object wird doppelt erstellt

Verfasst: Freitag 28. August 2020, 11:13
von Buck_ClaSh
Ok eine Frage hätte ich noch...xD Ich möchte gerne alle Commands aus diesem Level System ausschließen, da es ja irgendwie doof wäre dadurch xp zu erhalten. Ich hatte auf StackOverflow gelesen, dass man mit ctx.valid prüfen kann ob eine message einen command invoked. Nun kommen wir zum eigentlichen Problem:

Theoretisch könnte man sich den Context(ctx) durch diese Code Zeile holen:

Code: Alles auswählen

ctx = await bot.get_context(message)
Das Problem hierbei ist dass dann ein TypeError: object NoneType can't be used in 'await' expression kommt...Wie kann ich das handlen?

Hier mal meine gesamte on_message():

Code: Alles auswählen

@bot.event
async def on_message(message):
    ctx = await bot.get_context(message)
    await bot.process_commands(message)

    if message.author.id != 747719515135017001 and not ctx.valid:
        with open(os.path.join(DATA_PATH, "users.json"),'r') as tmp:
            users = json.load(tmp)
        
        await update_data(users, message.author)
        await add_xp(users, message.author, 5)
        await level_up(users, message.author, message)

        with open(os.path.join(DATA_PATH, "users.json"),'w') as tmp:
            json.dump(users, tmp)

Re: JSON Object wird doppelt erstellt

Verfasst: Freitag 28. August 2020, 13:10
von Buck_ClaSh
Also es funktioniert aber ich verstehe nicht ganz why xD Es lag daran, dass update_data und add_xp keine asynchronen Methoden mehr waren :) Jetzt funktioniert es :)

Re: JSON Object wird doppelt erstellt

Verfasst: Freitag 28. August 2020, 13:55
von Sirius3
Raten hilft selten beim Programmieren, asynchronen muß eine Funktion nur sein, wenn auch irgendwo ein await vorkommt. Schau Dir doch mal users an, nachdem Du es von JSON geladen hast und nachdem Du einen neuen User mit update_data (viel zu generischer Funktionsname für das hinzufügen eines Users) einen User hinzugefügt hast und vergleiche die Alte mit der neuen Version.

Re: JSON Object wird doppelt erstellt

Verfasst: Freitag 28. August 2020, 17:41
von Buck_ClaSh
Also das bei einem await die Funktion asynchron sein muss wusste ich, jedoch verstehe ich die Fehlermeldung in diesem Zusammenhang nicht was es mit dem NoneType zu tun hat :) Naja die update_data funktion erweitert die JSON Datei um einen neuen Eintrag, sollte die ID nicht bereits existieren