Events¶
In bottom, an event is simply a string and set of **kwargs
to be passed to
any handlers listening for that event:
@client.on('any string is fine')
def handle(**kwargs):
if 'potato' in kwargs:
print("Found a potato!")
IRC Events¶
While connected, a client will trigger events for valid IRC commands that it
receives, with kwargs according to that command’s structure. For example, the
“part” event will always include the nickmask (nick
, user
, host
),
message
, and channel
kwargs, even if the message was empty:
@client.on("part")
def handle(nick, user, host, message, channel, **kwargs):
out = "User {}!{}@{} left {} with '{}'"
print(out.format(nick, user, host, channel, message))
Because kwargs
contains those fields, we could also use:
@client.on("part")
def handle(**kwargs):
out = ("User {nick}!{user}@{host} left"
" {channel} with '{message}'")
print(out.format(**kwargs))
Triggering¶
The same mechanism that the client uses to dispatch events can be invoked manually, either for custom events or to simulate receiving an irc command:
@client.on("privmsg")
def handle(**kwargs):
print("Someone sent a message!")
client.trigger("privmsg")
Running the above won’t print anything, however. Triggering an event only schedules the registered handlers (like the function we defined) to run in the future. Until we run the event loop, the triggered handlers won’t be invoked. Let’s see that print statement:
client.loop.run_forever()
We can pass arbitrary kwargs to handlers through trigger
:
client.trigger("event")
client.trigger("event", **some_dict)
client.trigger("event", nick="bot", message="hello, world")
Waiting¶
Sometimes we need to wait for another event to occur before continuing. For example, consider a reconnect handler that wants to trigger the “reconnect” event for some plugins, but only after the connection has actually been established. The following will incorrectly signal that the reconnect has completed, while in reality the client has only scheduled a connection for the future:
@client.on("client_disconnect")
def reconnect(**kwargs):
client.connect()
client.trigger("reconnect", reconnect_msg="May not be connected!")
@client.on("reconnect")
def handle_reconnect(reconnect_msg="", **kwargs):
if reconnect_msg:
client.send("privmsg", target=CHANNEL, message=reconnect_msg)
Because both client.send
and client.connect
schedule coroutines, the
event loop may reorder (or process out of order). In reconnect
what we
really want to do is wait until the client_connect event is emitted, and then
trigger the reconnect event:
@client.on("client_disconnect")
async def reconnect(**kwargs):
client.connect()
await client.wait("client_connect")
client.trigger("reconnect", reconnect_msg="May not be connected!")
Whenever an event triggers, an asyncio.Event
is set and cleared, which
allows any code that is waiting on that event to continue. Be careful using
client.wait
- because we can call trigger with any string, wait
will
allow us to wait (forever) for events that may never trigger.
Supported Events¶
# Local only events
client.trigger('CLIENT_CONNECT')
client.trigger('CLIENT_DISCONNECT')
PING
JOIN
PART
PRIVMSG
NOTICE
USERMODE (renamed from MODE)
CHANNELMODE (renamed from MODE)
RPL_WELCOME (001)
RPL_YOURHOST (002)
RPL_CREATED (003)
RPL_MYINFO (004)
RPL_BOUNCE (005)
RPL_MOTDSTART (375)
RPL_MOTD (372)
RPL_ENDOFMOTD (376)
RPL_LUSERCLIENT (251)
RPL_LUSERME (255)
RPL_LUSEROP (252)
RPL_LUSERUNKNOWN (253)
RPL_LUSERCHANNELS (254)
ERR_NOMOTD (422)