Discord Vokabeltrainer-bot

Du hast eine Idee für ein Projekt?
Antworten
Mulciber
User
Beiträge: 1
Registriert: Montag 27. Januar 2020, 18:32

Hallo,

ich habe kürlich damit angefangen, in Python zu Programmieren, bin Schüler und Technik-Interessiert.
Dabei bin ich dann auf die Idee gekommen, mir einen Disord-Bot zu Programmieren, mit dem ich(und vielleicht andere) Vokabeln lernen kann.

Das hier ist dabei herausgekommen:

Code: Alles auswählen

import discord
import random
from googletrans import Translator

try:
    with open("vokabeln_g.txt", "r") as vokabulary_file:
        vokabeln_g = [line.strip() for line in vokabulary_file]
    with open("vokabeln_e.txt", "r") as vokabulary_file:
        vokabeln_e = [line.strip() for line in vokabulary_file]

except FileNotFoundError:
    print("nothing found")
    vokabeln_g = []
    vokabeln_e = []
    
HELP_MSG = """
Du kannst folgende Befehle verwenden:

!trv um dich alle Vokabeln abfragen zu lassen.
!trl <Text> <Zielsprachenkürzel(de, fr, en, etc) >um etwas in eine andere Sprache zu Übersetzen

Genauere Informatitonen zu den Befehlen findest du mit:
!help <Befehl> 
"""
#TODO Genauere Infos zu den Befehlen schreiben


def add_vokabulary(ger_v, eng_v):
    vokabeln_g.append(ger_v)
    vokabeln_e.append(eng_v)
    with open("vokabeln_g.txt", "a") as vokabulary_file:
        vokabulary_file.writelines(ger_v)
    with open("vokabeln_e.txt", "a") as vokabulary_file:
        vokabulary_file.writelines(eng_v)
    return

class MyClient(discord.Client):
    async def on_ready(self):
        print("Ich habe mich eingeloggt. Beep boop.")
        self.i = 0
        self.reihenfolge = list(i for i in range(len(vokabeln_g)))
        random.shuffle(self.reihenfolge)
        self.lastmsg = None
        self.lang_of_answer = None

    async def on_message(self, message):
        if message.author == client.user:
            return
            
        elif message.content.startswith("!addv") or message.content.startswith("!Addv"):
            vokg = message.content.split(" ")[1]
            voke = message.content.split(" ")[2]
            add_vokabulary(vokg, voke)
            self.reihenfolge = list(i for i in range(len(vokabeln_g)))
            print(self.reihenfolge)
            random.shuffle(self.reihenfolge)

        elif message.content == "!listv":
            await message.author.send("\n".join("{} - {}".format(vg, ve) for vg, ve in zip(vokabeln_g, vokabeln_e)))

        elif message.content.startswith("!trl"):
            input_text = message.content[5:-2]
            target_lang = message.content[-2:]
            translator = Translator()
            await message.author.send("Übersetzung von %s:\n" %input_text + translator.translate(input_text, target_lang).text)

        elif message.content == "!help":
            await message.author.send(HELP_MSG)

        elif message.content.lower() == "!trv":
            if random.randint(0, 1) == 0:
                self.vokabel = vokabeln_g[self.reihenfolge[self.i]]
                self.lang_of_answer = "Englische"
                self.vokabel_answer = vokabeln_e[self.reihenfolge[self.i]]
            else:
                self.vokabel = vokabeln_e[self.reihenfolge[self.i]]
                self.lang_of_answer = "Deutsche"
                self.vokabel_answer = vokabeln_g[self.reihenfolge[self.i]]

            self.lastmsg = message.content.lower()
            await message.author.send("{} Übersetzung von ".format(self.lang_of_answer) + self.vokabel)

        elif message.content == "!ue":
            self.i += 1

        elif self.lastmsg == "!trv":
            await message.author.send("Richtig!!!")

            if message.content == self.vokabel_answer:
                if len(vokabeln_g) == self.i+1:
                    await message.author.send("Du kannst alle Vokabeln!")
                    self.lastmsg = None
                    self.i = 0

                else:
                    self.i += 1

                    if random.randint(0, 1) == 0:
                        self.vokabel = vokabeln_g[self.reihenfolge[self.i]]
                        self.lang_of_answer = "Englische"
                        self.vokabel_answer = vokabeln_e[self.reihenfolge[self.i]]
                        print(self.vokabel, self.lang_of_answer, self.vokabel_answer)
                    else:
                        self.vokabel = vokabeln_e[self.reihenfolge[self.i]]
                        self.lang_of_answer = "Deutsche"
                        self.vokabel_answer = vokabeln_g[self.reihenfolge[self.i]]
                        print(self.vokabel, self.lang_of_answer, self.vokabel_answer)
                    await message.author.send("{} Übersetzung von ".format(self.lang_of_answer) + self.vokabel)
            else:
                await message.author.send("Leider Falsch.")

        elif message.content.startswith("!"):
            await message.author.send("Unkown command.")

if __name__ == '__main__':
    client = MyClient()
    client.run("Das Bot Token")
Ich freue mich auf eure Verbesserungsvorschläge!
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Mulciber: Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Das heisst das Laden der Vokabeln und das was im ``if __name__ …``-Zweig steht, gehört in eine Funktion.

Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Das muss man mit den Vokabeln machen und `on_message()` darf nicht auf das vorher globale `client`-Objekt zugreifen. Das ist doch das Objekt selbst, da braucht man keinen Namen von aussen.

Sowohl im Programm als auch erst recht auf der Festplatte sollten die zusammengehörigen Werte nicht in verschiedenen Datenstrukturen oder gar Dateien gespeichert werden. Das ist fehleranfällig. Jeweils zusammengehörende Vokabeln sollte man auch zusammen speichern. Im Speicher beispielsweise als Tupel in *einer* Liste, und in einer Datei beispielsweise in einer CSV-Datei als *ein* Datensatz. Für CSV-Dateien gibt es in der Standardbibliothek das `csv`-Modul.

Der Name der Textdatei sollte nur *einmal* im Programm stehen, am Anfang als Konstante.

Textdateien sollte man immer mit einer Angabe der Kodierung öffnen.

Die Vorsilbe „My“ macht keinen Sinn. Es sei denn es gibt auch ein „OurClient“ und/oder „TheirClient“ wogegen man das abgrenzen muss. Namen sollten auch keine kryptischen Abkürzungen enthalten oder gar sein.

Attribute werden in der `__init__()` angelegt, nicht in beliebigen anderen Methoden. Nach der `__init__()` sollte ein Objekt in einem initialisierten, benutzbaren Zustand sein und man sollte an der `__init__()` ablesen können welche Attribute es gibt.

``list(i for i in range(len(vokabeln)))`` ist etwas umständlich für ``list(range(len(vokabeln)))``.

Die Attribute `i`, `vokabeln`, und `reihenfolge` sind zu viel. Man kommt da mit `vokabeln` und einen Iterator über die gemischten Vokabeln aus.

Man `split()`\tet nicht mehrmals die gleiche Zeichenkette nur um jeweils ein Element vom Ergebnis zu nehmen.

Das mit den Slices bei "!trl" ist fehleranfällig und erlaubt ”komische” eingaben.

``%`` würde ich nicht mehr für Zeichenkettenformatierung verwenden. Du kennst ja die `format()`-Methode bereits. Die sollte man nicht mit ``+`` mischen wenn man dafür das `format()` verwenden kann.

Man sollte keine Daten und keinen Code wiederholen. Das macht Programme länger, schwerer zu warten, und fehleranfälliger.

"Richtig!!!" wird gesendet bevor die Antwort geprüft wird. `self.i` wird auch nicht überall richtig geprüft. Man kann mit mehrfachen "!ue" beispielsweise `self.i` zu gross werden lassen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import csv
import random

import discord
from googletrans import Translator

VOCABULARY_FILENAME = "vokabeln_de_en.csv"
HELP_MSG = """
Du kannst folgende Befehle verwenden:

!trv um dich alle Vokabeln abfragen zu lassen.
!trl <Text> <Zielsprachenkürzel(de, fr, en, etc) >um etwas in eine andere Sprache zu Übersetzen

Genauere Informatitonen zu den Befehlen findest du mit:
!help <Befehl> 
"""
#
# TODO Genauere Infos zu den Befehlen schreiben
#


def save_vocable(german, english):
    with open(VOCABULARY_FILENAME, "a", encoding="utf-8", newline="") as file:
        csv.writer(file).writerow([german, english])


class VocabularyBot(discord.Client):
    def __init__(self, vokabeln):
        discord.Client.__init__(self)
        self.vokabeln = vokabeln

        self.vokabel_iterator = None
        self.expected_answer = None

        self.init_vokabel_iterator()

    def init_vokabel_iterator(self):
        random.shuffle(self.vokabeln)
        self.vokabel_iterator = iter(self.vokabeln)

    async def next_vokabel(self, message):
        try:
            german, english = next(self.vokabel_iterator)
        except StopIteration:
            self.expected_answer = None
            await message.author.send("Du kannst alle Vokabeln!")
        else:
            if random.randint(0, 1):
                language_of_answer = "Deutsche"
                question = english
                self.expected_answer = german
            else:
                language_of_answer = "Englische"
                question = german
                self.expected_answer = english
            await message.author.send(
                f"{language_of_answer} Übersetzung von {question}"
            )

    async def on_ready(self):
        print("Ich habe mich eingeloggt. Beep boop.")

    async def on_message(self, message):
        if message.author == self.user:
            return

        if message.content.startswith("!addv"):
            _, german, english = message.content.split()
            save_vocable(german, english)
            self.vokabeln.append((german, english))
            self.init_vokabel_iterator()

        elif message.content == "!listv":
            await message.author.send(
                "\n".join(
                    f"{german} - {english}"
                    for german, english in sorted(self.vokabeln)
                )
            )

        elif message.content.startswith("!trl"):
            _, _, text = message.content.partition(" ")
            text, _, target_language = text.strip().rpartition(" ")
            await message.author.send(
                "Übersetzung von {}:\n".format(
                    text + Translator().translate(text, target_language).text
                )
            )

        elif message.content == "!help":
            await message.author.send(HELP_MSG)

        elif message.content.lower() in ["!trv", "!ue"]:
            await self.next_vokabel(message)

        elif self.expected_answer:
            if message.content == self.expected_answer:
                await message.author.send("Richtig!!!")
                await self.next_vokabel(message)
            else:
                await message.author.send("Leider falsch.")

        elif message.content.startswith("!"):
            await message.author.send("Unkown command.")


def main():
    try:
        with open(
            VOCABULARY_FILENAME, "r", encoding="utf-8", newline=""
        ) as file:
            vokabeln = list(csv.reader(file))
    except FileNotFoundError:
        vokabeln = list()

    bot = VocabularyBot(vokabeln)
    bot.run("Das Bot Token")


if __name__ == "__main__":
    main()
Ich würde `on_message()` noch auf aufteilen und wohl auch so schreiben, dass erst geprüft wird ob es sich um ein Kommando handelt, und dann das Kommando vom Rest der Zeile trennen. Dann kann man ein Wörterbuch erstellen das Kommandos auf Methoden abbildet, welche die Kommandos dann ausführen.

Was im Moment nicht möglich ist, sind Vokabeln mit Leerzeichen über den Bot einzugeben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten