What it really is @client.event? discord.py

ArtFiNCH picture ArtFiNCH · Oct 7, 2018 · Viewed 7.2k times · Source

A few days ago I became interested in programming discord bots a bit. In the syntax of these programs I noticed a lot of unintelligible issues that I can not find an answer to. That's why I am asking you for help in understanding them.

All questions are based on this code:

import discord
import asyncio
from discord.ext import commands

botToken = '***'

client = commands.Bot(command_prefix = '.')

@client.event
async def on_ready():
    print('Bot is ready!')

@client.event
async def on_message(message):
    author = message.author
    if message.content =='Hello':
        await client.send_message(message.channel, 'Welcome again {}!'.format(author))


client.run(botToken)

What is @client.event? I found that is a event handler, but how is it worki? Why is it needed to run program? Is it somehow connected to a asyncio?

Answer

Sam Rockett picture Sam Rockett · Oct 7, 2018

When a Client receives an event from Discord, It works out what that event is and generates, or locates, the objects that were sent by the event, such as a discord.Message for any MESSAGE_RECEIVE events, or a discord.Reaction for REACTION_ADD etc.
The client then sends the objects into the method that handles those events, but you first need to tell the client what those methods are. This is where the event decorators come in.


Decorators are, in essence, functions that take other functions as arguments. The most common one you'll see is @property. This says that the function you define should be passed into the property() function

@property
def name(self):
    return self._name

is the same as

def name(self):
    return self._name

name = property(name)

This may be a bit confusing to wrap your head around, but this is how discord.py handles its events.


When you use the @client.event decorator on your on_message, what you are actually doing is saying on_message = client.event(on_message)

The internal code of discord.py for on_event is this

def event(self, coro):
    # Validation we don't need to worry about
    setattr(self, coro.__name__, coro)
    return coro

Which means that it takes the function as its parameter, and sets a new attribute on the client itself. For our on_message example, we pass our on_message function into client.event() and it makes the client define a new client.on_message method that is the same method as our on_message.

Note: func.__name__ returns the name of that function. on_message.__name__ will return "on_message".
setattr(obj, name, value) sets an attribute on an object, so setattr(self, "foo", 100) means that self.foo will be 100.

Now that the client knows our on_message, when it receives an event saying that a message was sent, it creates the discord.Message object and passes that into client.on_message, which as we already established, is the same as our own on_message

If you wanted, you could even just skip the decorator and do this after your function, but it is less elegant than a decorator is:

on_message = client.event(on_message)