Why can't I have multiple on_message
events?
import discord
client = discord.Client()
@client.event
async def on_ready():
print('in on_ready')
@client.event
async def on_message(message):
print("in on_message #1")
@client.event
async def on_message(message):
print("in on_message #2")
@client.event
async def on_message(message):
print("in on_message #3")
client.run("TOKEN")
For example, if I typed anything in discord, it's always only the last on_message
that gets triggered. How can I get all three to work?
It's not possible with the native Client
You can only have one on_message
, if you have multiple, only the last one will be called for the on_message
event. You'll just need to combine your three on_message
.
import discord
client = discord.Client()
@client.event
async def on_message(message):
print("in on_message #1")
print("in on_message #2")
print("in on_message #3")
client.run("TOKEN")
Like any Python variable/function (unless the decorator stores your function, @client.event
does it by keeping only the most recent callback), if multiple names are the same, the most recently will be kept, and all others will get overwritten.
This is a simple example I wrote to give you a broad understanding of how events in discord.py work (note: the actual code isn't exactly like this, as it's rewritten and significantly reduced).
class Client:
def event(self, func):
if func.__name__ == "on_message":
self.on_message_handle = func
return func
def receive_message(self, msg):
func = getattr(self, "on_message_handle", None)
if func is not None:
func(msg)
else:
self.process_commands(msg)
client = Client()
@client.event
def on_message(msg):
print("in on_message #1")
@client.event
def on_message(msg):
print("in on_message #2")
client.receive_message("hello")
# "in on_message #2"
As you can see client.event
only keep one instance of on_message
.
You can with Bot
instances
Alternatively, if you're using the ext.commands
extension of discord.py, there is a native way to have multiple on_message
callbacks. You do so by using defining them as a listener
. You can have at most one on_message
event, and infinite amounts of on_message
listeners.
from discord.ext import commands
bot = commands.Bot('.')
@bot.event
async def on_message(msg):
print("in on_message #1")
await bot.process_commands(msg) # so `Command` instances will still get called
@bot.listen()
async def on_message(msg):
print("in on_message #2")
@bot.listen()
async def on_message(msg):
print("in on_message #3")
bot.run("TOKEN")
When a message is received, all on_message #1-3
will all get printed.
In python, functions are just objects.
>>> def foo():
... print ("hi")
defines an object called foo
, You can see this using a Python shell.
>>> foo
<function foo at 0x...>
>>> foo()
hi
If you define a new method after, or redefine the variable foo
, you lose access to the initial function.
>>> foo = "hi"
>>> foo
hi
>>> foo()
Traceback ...:
file "<stdin>" ...
TypeError: 'str' object is not callable
How the @client.event
decorator works is it tells your client that new messages should be piped into the messages, and well, if the method gets redefined, it means the old method is lost.
>>> @bot.event
... async def on_message(m):
... print(1)
...
>>> bot.on_message(None) # @bot.event makes the bot define it's own method
1
>>> @bot.event
... async def on_message(m):
... print(2)
...
>>> bot.on_message(None) # bot's own method was redefined.
2