0
# Events & Listeners
1
2
Comprehensive event system covering Discord gateway events and internal bot events.
3
4
## Event System Overview
5
6
The interactions library provides a rich event system with two main categories:
7
- **Discord Events**: Gateway events from Discord (messages, guilds, users, etc.)
8
- **Internal Events**: Bot lifecycle and framework events (startup, command completion, errors, etc.)
9
10
## Listening to Events
11
12
### Basic Event Listener
13
14
```python
15
from interactions import listen, Client, events
16
17
bot = Client(token="TOKEN")
18
19
@listen()
20
async def on_message_create(event: events.MessageCreate):
21
"""Listen to all message create events"""
22
message = event.message
23
print(f"Message from {message.author}: {message.content}")
24
25
@listen(events.GuildJoin)
26
async def on_guild_join(event: events.GuildJoin):
27
"""Listen to guild join events"""
28
guild = event.guild
29
print(f"Joined guild: {guild.name} ({guild.id})")
30
```
31
32
### Multiple Event Types
33
34
```python
35
@listen(events.MessageCreate, events.MessageUpdate)
36
async def on_message_event(event):
37
"""Listen to both message create and update events"""
38
if isinstance(event, events.MessageCreate):
39
print(f"New message: {event.message.content}")
40
elif isinstance(event, events.MessageUpdate):
41
print(f"Message edited: {event.after.content}")
42
```
43
44
### Class-Based Listeners
45
46
```python
47
from interactions import Extension, listen
48
49
class EventExtension(Extension):
50
@listen()
51
async def on_ready(self, event: events.Ready):
52
"""Extension-based event listener"""
53
print(f"Bot ready! Logged in as {self.bot.user}")
54
55
@listen(events.MemberAdd)
56
async def on_member_join(self, event: events.MemberAdd):
57
"""Welcome new members"""
58
member = event.member
59
channel = member.guild.system_channel
60
if channel:
61
await channel.send(f"Welcome {member.mention} to {member.guild.name}!")
62
```
63
64
## Discord Gateway Events
65
66
### Connection Events
67
68
```python
69
@listen(events.Ready)
70
async def on_ready(event: events.Ready):
71
"""Bot is ready and connected"""
72
user = event.user
73
guilds = len(event.guilds)
74
print(f"Ready! Logged in as {user} in {guilds} guilds")
75
76
@listen(events.Connect)
77
async def on_connect(event: events.Connect):
78
"""Bot connected to gateway"""
79
print("Connected to Discord gateway")
80
81
@listen(events.Disconnect)
82
async def on_disconnect(event: events.Disconnect):
83
"""Bot disconnected from gateway"""
84
print("Disconnected from Discord gateway")
85
86
@listen(events.Resume)
87
async def on_resume(event: events.Resume):
88
"""Gateway connection resumed"""
89
print("Gateway connection resumed")
90
```
91
92
### Guild Events
93
94
```python
95
@listen(events.GuildJoin)
96
async def on_guild_join(event: events.GuildJoin):
97
"""Bot joins a new guild"""
98
guild = event.guild
99
print(f"Joined guild: {guild.name} with {guild.member_count} members")
100
101
@listen(events.GuildLeft)
102
async def on_guild_leave(event: events.GuildLeft):
103
"""Bot leaves/gets kicked from guild"""
104
guild = event.guild
105
print(f"Left guild: {guild.name}")
106
107
@listen(events.GuildUpdate)
108
async def on_guild_update(event: events.GuildUpdate):
109
"""Guild settings updated"""
110
before = event.before
111
after = event.after
112
print(f"Guild {after.name} was updated")
113
114
@listen(events.GuildAvailable)
115
async def on_guild_available(event: events.GuildAvailable):
116
"""Guild becomes available after outage"""
117
guild = event.guild
118
print(f"Guild {guild.name} is now available")
119
120
@listen(events.GuildUnavailable)
121
async def on_guild_unavailable(event: events.GuildUnavailable):
122
"""Guild becomes unavailable due to outage"""
123
guild = event.guild
124
print(f"Guild {guild.name} is now unavailable")
125
```
126
127
### Member Events
128
129
```python
130
@listen(events.MemberAdd)
131
async def on_member_join(event: events.MemberAdd):
132
"""New member joins guild"""
133
member = event.member
134
guild = member.guild
135
136
# Send welcome message
137
if guild.system_channel:
138
embed = Embed(
139
title="Welcome!",
140
description=f"Welcome {member.mention} to {guild.name}!",
141
color=0x00ff00
142
)
143
await guild.system_channel.send(embed=embed)
144
145
@listen(events.MemberRemove)
146
async def on_member_leave(event: events.MemberRemove):
147
"""Member leaves guild"""
148
member = event.member
149
print(f"{member} left {member.guild.name}")
150
151
@listen(events.MemberUpdate)
152
async def on_member_update(event: events.MemberUpdate):
153
"""Member profile updated (roles, nick, etc.)"""
154
before = event.before
155
after = event.after
156
157
# Check if roles changed
158
if before.roles != after.roles:
159
added_roles = set(after.roles) - set(before.roles)
160
removed_roles = set(before.roles) - set(after.roles)
161
162
for role in added_roles:
163
print(f"{after} gained role: {role.name}")
164
for role in removed_roles:
165
print(f"{after} lost role: {role.name}")
166
```
167
168
### Message Events
169
170
```python
171
@listen(events.MessageCreate)
172
async def on_message(event: events.MessageCreate):
173
"""New message sent"""
174
message = event.message
175
176
# Ignore bot messages
177
if message.author.bot:
178
return
179
180
# Auto-react to messages with "python"
181
if "python" in message.content.lower():
182
await message.add_reaction("π")
183
184
@listen(events.MessageUpdate)
185
async def on_message_edit(event: events.MessageUpdate):
186
"""Message was edited"""
187
before = event.before
188
after = event.after
189
190
if before and after and before.content != after.content:
191
print(f"Message edited in #{after.channel.name}")
192
print(f"Before: {before.content}")
193
print(f"After: {after.content}")
194
195
@listen(events.MessageDelete)
196
async def on_message_delete(event: events.MessageDelete):
197
"""Message was deleted"""
198
message = event.message
199
print(f"Message deleted in #{message.channel.name}: {message.content}")
200
201
@listen(events.MessageDeleteBulk)
202
async def on_bulk_delete(event: events.MessageDeleteBulk):
203
"""Multiple messages deleted"""
204
messages = event.messages
205
channel = messages[0].channel if messages else None
206
print(f"Bulk deleted {len(messages)} messages in #{channel.name}")
207
```
208
209
### Reaction Events
210
211
```python
212
@listen(events.MessageReactionAdd)
213
async def on_reaction_add(event: events.MessageReactionAdd):
214
"""User adds reaction to message"""
215
reaction = event.reaction
216
user = event.user
217
message = event.message
218
219
# Role reaction system example
220
if str(reaction.emoji) == "π" and not user.bot:
221
role = message.guild.get_role(THUMBS_UP_ROLE_ID)
222
if role:
223
await user.add_role(role)
224
225
@listen(events.MessageReactionRemove)
226
async def on_reaction_remove(event: events.MessageReactionRemove):
227
"""User removes reaction from message"""
228
reaction = event.reaction
229
user = event.user
230
231
if str(reaction.emoji) == "π" and not user.bot:
232
role = event.message.guild.get_role(THUMBS_UP_ROLE_ID)
233
if role:
234
await user.remove_role(role)
235
236
@listen(events.MessageReactionRemoveAll)
237
async def on_reactions_clear(event: events.MessageReactionRemoveAll):
238
"""All reactions removed from message"""
239
message = event.message
240
print(f"All reactions cleared from message in #{message.channel.name}")
241
```
242
243
### Channel Events
244
245
```python
246
@listen(events.ChannelCreate)
247
async def on_channel_create(event: events.ChannelCreate):
248
"""New channel created"""
249
channel = event.channel
250
print(f"New channel created: #{channel.name}")
251
252
@listen(events.ChannelUpdate)
253
async def on_channel_update(event: events.ChannelUpdate):
254
"""Channel settings updated"""
255
before = event.before
256
after = event.after
257
print(f"Channel #{after.name} was updated")
258
259
@listen(events.ChannelDelete)
260
async def on_channel_delete(event: events.ChannelDelete):
261
"""Channel deleted"""
262
channel = event.channel
263
print(f"Channel deleted: #{channel.name}")
264
265
@listen(events.ChannelPinsUpdate)
266
async def on_pins_update(event: events.ChannelPinsUpdate):
267
"""Channel pins updated"""
268
channel = event.channel
269
print(f"Pins updated in #{channel.name}")
270
```
271
272
### Thread Events
273
274
```python
275
@listen(events.ThreadCreate)
276
async def on_thread_create(event: events.ThreadCreate):
277
"""New thread created"""
278
thread = event.thread
279
print(f"Thread created: {thread.name}")
280
281
@listen(events.ThreadUpdate)
282
async def on_thread_update(event: events.ThreadUpdate):
283
"""Thread updated"""
284
before = event.before
285
after = event.after
286
print(f"Thread updated: {after.name}")
287
288
@listen(events.ThreadDelete)
289
async def on_thread_delete(event: events.ThreadDelete):
290
"""Thread deleted"""
291
thread = event.thread
292
print(f"Thread deleted: {thread.name}")
293
294
@listen(events.ThreadMemberUpdate)
295
async def on_thread_member_update(event: events.ThreadMemberUpdate):
296
"""User's thread membership updated"""
297
member = event.member
298
thread = event.thread
299
print(f"{member.user} updated in thread {thread.name}")
300
```
301
302
### Voice Events
303
304
```python
305
@listen(events.VoiceStateUpdate)
306
async def on_voice_state_update(event: events.VoiceStateUpdate):
307
"""User's voice state changed"""
308
before = event.before
309
after = event.after
310
member = after.member if after else before.member
311
312
# User joined voice channel
313
if not before.channel and after.channel:
314
print(f"{member} joined voice channel: {after.channel.name}")
315
316
# User left voice channel
317
elif before.channel and not after.channel:
318
print(f"{member} left voice channel: {before.channel.name}")
319
320
# User moved between channels
321
elif before.channel != after.channel:
322
print(f"{member} moved from {before.channel.name} to {after.channel.name}")
323
324
# Convenience voice events
325
@listen(events.VoiceUserJoin)
326
async def on_voice_join(event: events.VoiceUserJoin):
327
"""User joins voice channel (convenience event)"""
328
member = event.member
329
channel = event.channel
330
print(f"{member} joined {channel.name}")
331
332
@listen(events.VoiceUserLeave)
333
async def on_voice_leave(event: events.VoiceUserLeave):
334
"""User leaves voice channel (convenience event)"""
335
member = event.member
336
channel = event.channel
337
print(f"{member} left {channel.name}")
338
339
@listen(events.VoiceUserMove)
340
async def on_voice_move(event: events.VoiceUserMove):
341
"""User moves between voice channels"""
342
member = event.member
343
before_channel = event.before_channel
344
after_channel = event.after_channel
345
print(f"{member} moved from {before_channel.name} to {after_channel.name}")
346
```
347
348
### Role Events
349
350
```python
351
@listen(events.RoleCreate)
352
async def on_role_create(event: events.RoleCreate):
353
"""New role created"""
354
role = event.role
355
guild = event.guild
356
print(f"Role created in {guild.name}: {role.name}")
357
358
@listen(events.RoleUpdate)
359
async def on_role_update(event: events.RoleUpdate):
360
"""Role updated"""
361
before = event.before
362
after = event.after
363
print(f"Role updated: {after.name}")
364
365
@listen(events.RoleDelete)
366
async def on_role_delete(event: events.RoleDelete):
367
"""Role deleted"""
368
role = event.role
369
guild = event.guild
370
print(f"Role deleted from {guild.name}: {role.name}")
371
```
372
373
### Ban Events
374
375
```python
376
@listen(events.BanCreate)
377
async def on_member_ban(event: events.BanCreate):
378
"""Member banned from guild"""
379
ban = event.ban
380
user = ban.user
381
guild = event.guild
382
reason = ban.reason or "No reason provided"
383
print(f"{user} was banned from {guild.name}: {reason}")
384
385
@listen(events.BanRemove)
386
async def on_member_unban(event: events.BanRemove):
387
"""Member unbanned from guild"""
388
user = event.user
389
guild = event.guild
390
print(f"{user} was unbanned from {guild.name}")
391
```
392
393
### Interaction Events
394
395
```python
396
@listen(events.InteractionCreate)
397
async def on_interaction(event: events.InteractionCreate):
398
"""Any interaction received"""
399
interaction = event.interaction
400
print(f"Interaction received: {interaction.type}")
401
```
402
403
## Internal Framework Events
404
405
### Bot Lifecycle Events
406
407
```python
408
@listen(events.Startup)
409
async def on_startup(event: events.Startup):
410
"""Bot starting up"""
411
print("Bot is starting up...")
412
413
@listen(events.Login)
414
async def on_login(event: events.Login):
415
"""Bot logged in"""
416
print("Bot has logged in to Discord")
417
418
@listen(events.Ready)
419
async def on_ready(event: events.Ready):
420
"""Bot is ready to receive events"""
421
print(f"Bot is ready! Logged in as {event.user}")
422
423
@listen(events.WebsocketReady)
424
async def on_websocket_ready(event: events.WebsocketReady):
425
"""WebSocket connection established"""
426
print("WebSocket connection ready")
427
```
428
429
### Command Events
430
431
```python
432
@listen(events.CommandCompletion)
433
async def on_command_complete(event: events.CommandCompletion):
434
"""Command completed successfully"""
435
ctx = event.ctx
436
command = event.command
437
print(f"Command {command.name} completed by {ctx.author}")
438
439
@listen(events.CommandError)
440
async def on_command_error(event: events.CommandError):
441
"""Command encountered an error"""
442
ctx = event.ctx
443
error = event.error
444
command = event.command
445
446
print(f"Error in command {command.name}: {error}")
447
448
# Send error message to user
449
await ctx.send(f"An error occurred: {str(error)}", ephemeral=True)
450
```
451
452
### Component Events
453
454
```python
455
@listen(events.Component)
456
async def on_component_interaction(event: events.Component):
457
"""Generic component interaction"""
458
ctx = event.ctx
459
print(f"Component interaction: {ctx.custom_id}")
460
461
@listen(events.ComponentCompletion)
462
async def on_component_complete(event: events.ComponentCompletion):
463
"""Component interaction completed"""
464
ctx = event.ctx
465
print(f"Component {ctx.custom_id} completed")
466
467
@listen(events.ComponentError)
468
async def on_component_error(event: events.ComponentError):
469
"""Component interaction error"""
470
ctx = event.ctx
471
error = event.error
472
print(f"Component error in {ctx.custom_id}: {error}")
473
474
@listen(events.ButtonPressed)
475
async def on_button_press(event: events.ButtonPressed):
476
"""Button was pressed (convenience event)"""
477
ctx = event.ctx
478
print(f"Button pressed: {ctx.custom_id}")
479
480
@listen(events.Select)
481
async def on_select_menu(event: events.Select):
482
"""Select menu used (convenience event)"""
483
ctx = event.ctx
484
values = ctx.values
485
print(f"Select menu {ctx.custom_id} used with values: {values}")
486
```
487
488
### Modal Events
489
490
```python
491
@listen(events.ModalCompletion)
492
async def on_modal_complete(event: events.ModalCompletion):
493
"""Modal submitted successfully"""
494
ctx = event.ctx
495
print(f"Modal {ctx.custom_id} submitted")
496
497
@listen(events.ModalError)
498
async def on_modal_error(event: events.ModalError):
499
"""Modal submission error"""
500
ctx = event.ctx
501
error = event.error
502
print(f"Modal error in {ctx.custom_id}: {error}")
503
```
504
505
### Extension Events
506
507
```python
508
@listen(events.ExtensionLoad)
509
async def on_extension_load(event: events.ExtensionLoad):
510
"""Extension loaded"""
511
extension = event.extension
512
print(f"Extension loaded: {extension.name}")
513
514
@listen(events.ExtensionUnload)
515
async def on_extension_unload(event: events.ExtensionUnload):
516
"""Extension unloaded"""
517
extension = event.extension
518
print(f"Extension unloaded: {extension.name}")
519
```
520
521
### Error Events
522
523
```python
524
@listen(events.Error)
525
async def on_general_error(event: events.Error):
526
"""General error occurred"""
527
error = event.error
528
print(f"General error: {error}")
529
530
@listen(events.AutocompleteError)
531
async def on_autocomplete_error(event: events.AutocompleteError):
532
"""Autocomplete error"""
533
ctx = event.ctx
534
error = event.error
535
print(f"Autocomplete error: {error}")
536
```
537
538
## Event Base Classes
539
540
### BaseEvent
541
542
```python
543
class BaseEvent:
544
"""Base class for all events"""
545
bot: Client
546
547
class RawGatewayEvent(BaseEvent):
548
"""Raw gateway event data"""
549
data: dict
550
sequence: int
551
event_name: str
552
553
class GuildEvent(BaseEvent):
554
"""Base for guild-related events"""
555
guild: Guild
556
```
557
558
## Advanced Event Features
559
560
### Event Filtering
561
562
```python
563
@listen(events.MessageCreate)
564
async def on_message_in_channel(event: events.MessageCreate):
565
"""Only process messages in specific channel"""
566
message = event.message
567
568
# Filter by channel
569
if message.channel.id != SPECIFIC_CHANNEL_ID:
570
return
571
572
# Process message
573
await process_message(message)
574
575
@listen(events.MemberAdd)
576
async def on_member_join_specific_guild(event: events.MemberAdd):
577
"""Only process joins in specific guild"""
578
member = event.member
579
580
if member.guild.id != SPECIFIC_GUILD_ID:
581
return
582
583
await welcome_member(member)
584
```
585
586
### Conditional Event Handling
587
588
```python
589
@listen(events.MessageCreate)
590
async def on_message_with_conditions(event: events.MessageCreate):
591
"""Message event with multiple conditions"""
592
message = event.message
593
594
# Skip bot messages
595
if message.author.bot:
596
return
597
598
# Only process messages with attachments
599
if not message.attachments:
600
return
601
602
# Only process in guild channels
603
if not message.guild:
604
return
605
606
# Process the message
607
await handle_attachment_message(message)
608
```
609
610
### Event Data Access
611
612
```python
613
@listen(events.MessageUpdate)
614
async def on_message_edit_detailed(event: events.MessageUpdate):
615
"""Access detailed message edit data"""
616
before = event.before # Message before edit (may be None if not cached)
617
after = event.after # Message after edit
618
619
if before and after:
620
# Compare content changes
621
if before.content != after.content:
622
print(f"Content changed from: {before.content}")
623
print(f"Content changed to: {after.content}")
624
625
# Check embed changes
626
if before.embeds != after.embeds:
627
print("Embeds were modified")
628
```
629
630
## Event Listener Patterns
631
632
### Class Method Listeners
633
634
```python
635
class MyBot(Client):
636
def __init__(self):
637
super().__init__(token="TOKEN")
638
639
@listen()
640
async def on_ready(self, event: events.Ready):
641
"""Method-based event listener"""
642
print(f"{self.user} is ready!")
643
644
@listen(events.MessageCreate)
645
async def handle_messages(self, event: events.MessageCreate):
646
"""Handle messages in class context"""
647
message = event.message
648
if message.content.startswith(f"<@{self.user.id}>"):
649
await message.reply("You mentioned me!")
650
```
651
652
### Decorator Stacking
653
654
```python
655
@listen(events.GuildJoin)
656
@listen(events.GuildAvailable)
657
async def on_guild_accessible(event):
658
"""Listen to multiple related events"""
659
guild = event.guild
660
print(f"Guild accessible: {guild.name}")
661
```