Discord-Autorole wird nicht zugewiesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

Hallo,
mein Discord Bot soll bei Beitritt eines neuen Mitglieds diesem automatisch eine vorher definierte Rolle zuweisen. So sieht der Code aus:

Code: Alles auswählen

@client.event
async def on_member_join(member):
    await client.send_message(member, "**Hey! %s**\n Willkommen auf dem %s Discord Server von %s! \n Viel Spaß!"
                              % (member.name, member.server.name, member.server.owner.mention))
    role = cmd_autorole.get(member.server)
    if role is not None:
        await client.add_roles(member, role)
        try:
            await client.send_message(member, "Dir wurde automatisch die Rolle " + role.mention + "zugewiesen!")
        except Exception:
            await client.send_message(member, "Entschuldigung, der Bot besitzt keine Rechte um dir die Rolle "
                                      + role.name + " zuzuordnen! \nBitte kontaktiere den Besitzer des Servers, "
                                      + member.server.owner.mention + "!")
            raise Exception
und hier der im Code erwähnte cmd_autorole:

Code: Alles auswählen

async def error(content, channel, client):
    await client.send_message(channel, embed=discord.Embed(color=discord.Color.red(), description=content))


async def get(server):
    f = "SETTINGS/" + server.id + "/autorole"
    if path.isfile(f):
        with open(f) as f:
            return discord.utils.get(server.roles, id=f.read())
    else:
        return None
    


async def savefile(id, server):
    if not path.isdir("SETTINGS/" + server.id):
        os.makedirs("SETTINGS/" + server.id)
    with open("SETTINGS/" + server.id + "/autorole", "w") as f:
        f.write(id)
        f.close()
    


async def ex(args, message, client, invoke):
    if len(args) > 0:
        rolename = args.__str__()[1:-1].replace(",", "").replace("'", "")
        print(rolename)
        role = discord.utils.get(message.server.roles, name=rolename)
        if role == None:
            await error("Bitte eine -auf diesem Server existierende- Rolle eingeben", message.channel, client)
        else:
            try:
                await savefile(role.id, message.server)
                await client.send_message(message.channel, embed=discord.Embed(color=discord.Color.green(),
                                                                               description=("Automatische Rolle wurde "
                                                                                            "geändert zu Rolle: %s!"
                                                                                            % role.name)))
            except Exception:
                await error("Unbekannter Fehler beim Speichern der Autorolle!", message.channel, client)
                raise Exception
Also, am Anfang muss der Owner des Server mit ~autorole ROLENAME festelgen, was die Autorolle ist. Ab dann soll automatisch die Rolle einem jedem neuen Mitglied zugewiesen werden. wenn nun ein neues Mitglied joint, wird aber nichts neues zugewiesen, nur dieser Error hier erscheint:

Ignoring exception in on_member_join
Traceback (most recent call last):
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "/home/phobit/PycharmProjects/DiscordSippenBot/Discord/Main.py", line 189, in on_member_join
await client.add_roles(member, role)
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py", line 2915, in add_roles
new_roles = utils._unique(role.id for role in itertools.chain(member.roles, roles))
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/utils.py", line 232, in _unique
return [x for x in iterable if not (x in seen or adder(x))]
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/utils.py", line 232, in <listcomp>
return [x for x in iterable if not (x in seen or adder(x))]
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py", line 2915, in <genexpr>
new_roles = utils._unique(role.id for role in itertools.chain(member.roles, roles))
AttributeError: 'coroutine' object has no attribute 'id'
/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py:314: RuntimeWarning: coroutine 'get' was never awaited
pass
Ignoring exception in on_member_join
Traceback (most recent call last):
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py", line 307, in _run_event
yield from getattr(self, event)(*args, **kwargs)
File "/home/phobit/PycharmProjects/DiscordSippenBot/Discord/Main.py", line 189, in on_member_join
await client.add_roles(member, role)
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py", line 2915, in add_roles
new_roles = utils._unique(role.id for role in itertools.chain(member.roles, roles))
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/utils.py", line 232, in _unique
return [x for x in iterable if not (x in seen or adder(x))]
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/utils.py", line 232, in <listcomp>
return [x for x in iterable if not (x in seen or adder(x))]
File "/home/phobit/PycharmProjects/DiscordSippenBot/venv/lib/python3.6/site-packages/discord/client.py", line 2915, in <genexpr>
new_roles = utils._unique(role.id for role in itertools.chain(member.roles, roles))
AttributeError: 'coroutine' object has no attribute 'id'



Ich habe den Code eigentlich aus einem Tutorial, deswegen wundert mich dieser Error.
Der Bot besitzt die Rechte, diese Rolle zuzuornden, falls jemand denkt es liegt daran!
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Phobit: weißt Du, was dieses `async` vor dem `def` bedeutet?

Zudem: Pfade setzt man mit `os.path.join` zusammen und nicht mit `+`. Statt mit ispath zu prüfen, ob die Datei existiert (was sowieso nicht 100% funktioniert), besser die Exception IOError abfangen.
Ebenso funktioniert das mit isdir und makedirs in `savefile` nicht 100% zuverlässig. Dafür kennt aber makedirs das Argument exist_ok.

Dass das Verzeichnis SETTINGS relativ zu irgendeinem aktuellen Verzeichnis erzeugt wird, sieht komisch aus.

In `ex` arbeitest Du mit der Stringrepresentation einer Liste, was man nie machen sollte, weil die nur für Debuggingzwecke da ist. Suchst Du einfach nur `rolename = args[0]`?
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

weißt Du, was dieses `async` vor dem `def` bedeutet?
Heisst meines wissens nach dass der Block asyncron ausgeführt wird, alles was nicht mit async und dann mit await ausgeführt wird, wird von Discord einfach ignoriert.
Zudem: Pfade setzt man mit `os.path.join` zusammen und nicht mit `+`. Statt mit ispath zu prüfen, ob die Datei existiert (was sowieso nicht 100% funktioniert), besser die Exception IOError abfangen.
Ebenso funktioniert das mit isdir und makedirs in `savefile` nicht 100% zuverlässig. Dafür kennt aber makedirs das Argument exist_ok.
Das klappt allerdings. Wenn ich die Rolle mit ~autorole ROLENMAE setze, wird in meinem Settings Ordner die passende Datei angelegt. Es hängt jetzt nur an diesem AttributeError!
In `ex` arbeitest Du mit der Stringrepresentation einer Liste, was man nie machen sollte, weil die nur für Debuggingzwecke da ist.
Naja, ich arbeite jetzt seit ca 3 Monaten an diesem Bot, bis jetzt hat ausnahmslos ALLES funktioniert, nur diese Autorole eben funktioniert nicht.
Suchst Du einfach nur `rolename = args[0]`?
Da versteh ich jetzt nicht ganz was du genau meinst, sorry, bin begriffsstuzig :K
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Phobit: Auch wenn der Code mit dem Verarbeiten der Zeichenkettenrepräsentation einer Liste funktioniert ist der *falsch*. Das ist undurchsichtig, fehleranfällig, und es bleibt auch die Frage ob das *wirklich* funktioniert, oder ob Dir einfach ein Fehleverhalten noch nicht aufgefallen ist. Die `__str__()`-Methode selbst aufzurufen ist schon ein No-Go.

@Sirius3: Ich denke es wird eher ein ``''.join(args)`` gemacht, eventuell mit einem `map()` und `str` beziehungsweise `repr` um am Ende 100% bei der gleichen Semantik heraus zu kommen, wobei genau das `repr` im jetztigen Ansatz ja *eine* Quelle von Problemen sein kann ­— wenn die Einzelteile mal nicht nur ”Unproblematisches” enthalten.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Phobit hat geschrieben: Donnerstag 16. August 2018, 09:52Heisst meines wissens nach dass der Block asyncron ausgeführt wird, alles was nicht mit async und dann mit await ausgeführt wird, wird von Discord einfach ignoriert.
Dass das bei Discord so sein muß, weil es eine asynchrone Bibliothek ist, mag sein, bei `savefile` hast Du aber etwas gemacht, was so nicht funktioniert.
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

achso, darf das savefile also nicht asnyc sein?


EDIT:
Also daran kann es eigentlich nicht liegen, da die Autorole sich ja korrekt abspeichert.
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann hast Du ›async‹ noch nicht verstanden. Das ist nur syntaktischer Zucker für Generatoren.
Septias
User
Beiträge: 80
Registriert: Freitag 24. Juni 2016, 19:15

Es gibt einen extra Discord-API-Server, bei dem du eventuell deine Fragen posten kannst (#python_discord-py). Ist auf Englisch, aber du bekommst fast immer nach 2min eine Antwort und dort kennen sich die Meisten auch echt gut aus.

https://discord.gg/discord-api
Für alle meine Codebeispiele gilt: Äußert bitte jegliche Art von Verbesserungsvorschlägen. Ich versuche immer meinen Stil zu verbessern und wenn man mir einfach sagt, was ich falsch machen, ist es um einiges einfacher, als wenn ich es mühselig selber herausfinden muss :-)
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

Es gibt einen extra Discord-API-Server, bei dem du eventuell deine Fragen posten kannst (#python_discord-py). Ist auf Englisch, aber du bekommst fast immer nach 2min eine Antwort und dort kennen sich die Meisten auch echt gut aus.
Ah, vielen Dank, aber mitlerweile habe ich eine (temporäre Lösung) für das Problem gefunden:

Code: Alles auswählen

def error(content, channel, client):
    yield from client.send_message(channel, embed=discord.Embed(color=discord.Color.red(), description=content))


def get(server):
    f = "SETTINGS/" + server.id + "/autorole"
    if path.isfile(f):
        with open(f) as f:
            return discord.utils.get(server.roles, id=f.read())
    else:
        return None
    


def savefile(id, server):
    if not path.isdir("SETTINGS/" + server.id):
        os.makedirs("SETTINGS/" + server.id)
    with open("SETTINGS/" + server.id + "/autorole", "w") as f:
        f.write(id)
        f.close()
    


def ex(args, message, client, invoke):
    if len(args) > 0:
        rolename = args.__str__()[1:-1].replace(",", "").replace("'", "")
        print(rolename)
        role = discord.utils.get(message.server.roles, name=rolename)
        if role == None:
            yield from error("Bitte eine -auf diesem Server existierende- Rolle eingeben", message.channel, client)
        else:
            try:
                savefile(role.id, message.server)
                yield from client.send_message(message.channel, embed=discord.Embed(color=discord.Color.green(),
                                                                               description=("Automatische Rolle wurde "
                                                                                            "geändert zu Rolle: %s!"
                                                                            % role.name)))
            except Exception:
                yield from error("Unbekannter Fehler beim Speichern der Autorolle!", message.channel, client)
                 raise Exception
Hier noch der Auschnitt aus dem Hauptprogramm:

Code: Alles auswählen

@client.event
@asyncio.coroutine
def on_member_join(member):
    yield from client.send_message(AllgemeinSippe, greeting + member.name + "!")
    yield from client.send_message(member, "**Hey! %s**\n Willkommen auf dem %s Discord Server von %s! \n Viel Spaß!"
                              % (member.name, member.server.name, member.server.owner.mention))
    role = cmd_autorole.get(member.server)
    if not role == None:
        yield from client.add_roles(member, role)
        try:
            yield from client.send_message(member, "Dir wurde automatisch die Rolle " + role.name + " zugewiesen!")
        except Exception:
            yield from client.send_message(member, "Entschuldigung, der Bot besitzt keine Rechte um dir die Rolle "
                                      + role.name + " zuzuordnen! \nBitte kontaktiere den Besitzer des Servers, "
                                      + member.server.owner.mention + "!")
            asyncio.sleep(3)
            raise Exception 
Kann mir vllt jemand anhand dieses Codes sagen, wo bei mir mit async der Fehler war? Laut dem Tutorial, aus dem ich alles hier hab, muss es mit async möglich sein!
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum hast Du jetzt alle `async` gelöscht und alle `await` durch `yield from` ersetzt? Wenn Du jetzt noch herausfindest, wo sich die beiden Methoden unterscheiden, weißt Du auch, warum `savefile` nicht async sein darf. Andere Sachen, die Du zum Schlechteren verändert ist, ist .z.b das `not role == None`. Warum?

Was ist denn nun `args` in der Funktion `ex`? Sollteder das jetzt `args[0]` oder `' '.join(args)` sein?
Der autorole-Dateinamen wird an vier verschiedenen Stellen erzeugt, das sollte nur eine sein. Pfade setzt man mit os.path.join zusammen.
Für die anderen Strings benutzt man format statt +.

Du hast noch einen Einrückfehler.
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

Warum hast Du jetzt alle `async` gelöscht und alle `await` durch `yield from` ersetzt?
Wie schon erwähnt, habe ich als Vorlage ein tutorial genommen. In dem Tutorial wurde yield from benutzt, und irgendwann stieg der jenige aber auf async und await um, ohne zu zeigen was ich austauschen müsste. Deshalb hab ich jetzt wieder das yield from benutzt, da ich offensichtlich einen Fehler in den awaits/asyncs habe.
Andere Sachen, die Du zum Schlechteren verändert ist, ist .z.b das `not role == None`. Warum?
Wie gesagt, Tutorial. Aber dort verstehe ich auch das Problem nicht, immerhin funktioniert es ja, weil:
"Wenn nicht dir Rolle nichts ist" ergibt meiner Meinung nach Sinn.
Was ist denn nun `args` in der Funktion `ex`? Sollteder das jetzt `args[0]` oder `' '.join(args)` sein?
Die args sind alles, was bei einem Befehl, der im Discord ausgeführt wird, hinter dem Befehl steht, was hier ausgelesen werden muss, da der Befehl "~autorole ROLENAME" ist, und ROLENAME ist das arg das ausgelesen werden muss.
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Phobit: „Wenn nicht dir Rolle nichts ist“ ist ziemlich verquer und unverständlich ausgedrückt. Normalerweise sagt man da „Wenn die Rolle nicht ”Nichts” ist“, also ``role is not None``. Hier ``is not`` statt ``!=`` weil `None` nur ein einiges mal als Objekt existiert (Singleton) und man deshalb auf Objektidentität statt auf Gleichheit prüfen kann.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Phobit
User
Beiträge: 185
Registriert: Freitag 4. Mai 2018, 18:13

Heisst doch, dass es egal ist wie ich es vergleiche, also ist if not role == None eigentlich das selbe, nur unschöner ausgedrückt, mir würde jetzt kein Fall einfallen bei dem das nen Error werfen könnte...
Mir egal, ob der Code schön ist oder nicht.
Hauptsache er funkt!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nein, das führt so direkt nicht zu einem Fehler beim ausführen, aber egal ist es deswegen trotzdem nicht. Du schreibst Quelltext nicht für nur für den Rechner sondern auch für Menschen, inklusive Dir selbst, und da kann das dann zu falschen Vorstellungen davon führen was der Code macht, oder zumindest das Verständnis erschweren. Was dann bei Programmänderungen zu Fehlern führen kann.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten