0
# Event System
1
2
Comprehensive event handling system supporting all IRC protocol events, custom handlers with priorities, and extensible event processing for advanced IRC applications. The event system is the core of the IRC library's architecture.
3
4
## Capabilities
5
6
### Event Class
7
8
Represents IRC events with structured data including source, target, arguments, and IRCv3 message tags.
9
10
```python { .api }
11
class Event:
12
def __init__(self, type: str, source, target, arguments: list = None, tags: list = None):
13
"""
14
Initialize IRC event.
15
16
Parameters:
17
- type: str, event type (e.g., "pubmsg", "join", "privmsg")
18
- source: NickMask or str, event source (user who triggered event)
19
- target: str, event target (channel or user)
20
- arguments: list, event-specific arguments
21
- tags: list, IRCv3 message tags
22
"""
23
24
@property
25
def type(self) -> str:
26
"""Event type identifier."""
27
28
@property
29
def source(self):
30
"""Event source (NickMask or server name)."""
31
32
@property
33
def target(self) -> str:
34
"""Event target (channel, user, or server)."""
35
36
@property
37
def arguments(self) -> list:
38
"""List of event-specific arguments."""
39
40
@property
41
def tags(self) -> list:
42
"""IRCv3 message tags list."""
43
```
44
45
### Event Handler Management
46
47
Functions for registering and managing event handlers with priority support.
48
49
```python { .api }
50
def add_global_handler(event: str, handler, priority: int = 0):
51
"""
52
Add global event handler for all connections.
53
54
Parameters:
55
- event: str, event type to handle
56
- handler: callable, handler function with signature (connection, event)
57
- priority: int, handler priority (lower numbers = higher priority)
58
"""
59
60
def remove_global_handler(event: str, handler):
61
"""
62
Remove global event handler.
63
64
Parameters:
65
- event: str, event type
66
- handler: callable, handler function to remove
67
"""
68
69
class PrioritizedHandler:
70
"""Handler with priority for event processing ordering."""
71
priority: int
72
callback: callable
73
```
74
75
### IRC Protocol Events
76
77
Standard IRC protocol events that can be handled by the event system.
78
79
```python { .api }
80
# Protocol events (generated from IRC messages)
81
protocol_events = [
82
"error", # Server error message
83
"join", # User joined channel
84
"kick", # User kicked from channel
85
"mode", # Mode change (user or channel)
86
"part", # User left channel
87
"ping", # Server ping
88
"privmsg", # Private message to user
89
"privnotice", # Private notice to user
90
"pubmsg", # Public message in channel
91
"pubnotice", # Public notice in channel
92
"quit", # User quit IRC
93
"invite", # Channel invitation
94
"pong", # Server pong response
95
"action", # CTCP ACTION message (/me)
96
"topic", # Channel topic change
97
"nick" # Nickname change
98
]
99
100
# Generated events (library-specific)
101
generated_events = [
102
"dcc_connect", # DCC connection established
103
"dcc_disconnect", # DCC connection closed
104
"dccmsg", # DCC message received
105
"disconnect", # Server disconnection
106
"ctcp", # CTCP query received
107
"ctcpreply", # CTCP reply received
108
"login_failed" # Authentication failed
109
]
110
111
# Numeric events (IRC numeric codes)
112
numeric_events = [
113
"welcome", # 001 - Welcome message
114
"yourhost", # 002 - Host information
115
"created", # 003 - Server creation date
116
"myinfo", # 004 - Server information
117
"isupport", # 005 - Server features
118
"statsconn", # 250 - Connection statistics
119
"luserclient", # 251 - User statistics
120
"luserop", # 252 - Operator count
121
"luserunknown", # 253 - Unknown connections
122
"luserchannels", # 254 - Channel count
123
"luserme", # 255 - Local user count
124
"namreply", # 353 - Channel user list
125
"endofnames", # 366 - End of names list
126
"motdstart", # 375 - MOTD start
127
"motd", # 372 - MOTD line
128
"endofmotd", # 376 - MOTD end
129
"whoisuser", # 311 - WHOIS user info
130
"whoisserver", # 312 - WHOIS server info
131
"whoisoperator", # 313 - WHOIS operator
132
"whoisidle", # 317 - WHOIS idle time
133
"endofwhois", # 318 - End of WHOIS
134
"whoischannels", # 319 - WHOIS channels
135
"liststart", # 321 - Channel list start
136
"list", # 322 - Channel list entry
137
"listend", # 323 - Channel list end
138
"topic", # 332 - Channel topic
139
"topicinfo", # 333 - Topic set info
140
"inviting", # 341 - Invitation sent
141
"version", # 351 - Server version
142
"whoreply", # 352 - WHO reply
143
"endofwho", # 315 - End of WHO
144
"banlist", # 367 - Ban list entry
145
"endofbanlist", # 368 - End of ban list
146
"youreoper", # 381 - You are operator
147
"rehashing", # 382 - Rehashing config
148
"time", # 391 - Server time
149
"nomotd", # 422 - No MOTD
150
"nicknameinuse", # 433 - Nickname in use
151
"nickcollision", # 436 - Nickname collision
152
"unavailresource", # 437 - Resource unavailable
153
"usernotinchannel", # 441 - User not in channel
154
"notonchannel", # 442 - Not on channel
155
"useronchannel", # 443 - User already on channel
156
"nologin", # 444 - No login
157
"summondisabled", # 445 - SUMMON disabled
158
"usersdisabled", # 446 - USERS disabled
159
"notregistered", # 451 - Not registered
160
"needmoreparams", # 461 - Need more parameters
161
"alreadyregistred", # 462 - Already registered
162
"nopermforhost", # 463 - No permission for host
163
"passwdmismatch", # 464 - Password mismatch
164
"yourebannedcreep", # 465 - You are banned
165
"youwillbebanned", # 466 - You will be banned
166
"keyset", # 467 - Channel key set
167
"channelisfull", # 471 - Channel is full
168
"unknownmode", # 472 - Unknown mode
169
"inviteonlychan", # 473 - Invite only channel
170
"bannedfromchan", # 474 - Banned from channel
171
"badchannelkey", # 475 - Bad channel key
172
"badchanmask", # 476 - Bad channel mask
173
"nochanmodes", # 477 - No channel modes
174
"banlistfull", # 478 - Ban list full
175
"noprivileges", # 481 - No privileges
176
"chanoprivsneeded", # 482 - Channel operator privileges needed
177
"cantkillserver", # 483 - Can't kill server
178
"restricted", # 484 - Connection restricted
179
"uniqopprivsneeded",# 485 - Unique operator privileges needed
180
"nooperhost", # 491 - No operator host
181
"umodeunknownflag", # 501 - Unknown user mode flag
182
"usersdontmatch" # 502 - Users don't match
183
]
184
185
# All available events
186
all_events = protocol_events + generated_events + numeric_events
187
```
188
189
### Command Processing
190
191
IRC command representation and numeric code mapping for event processing.
192
193
```python { .api }
194
class Command:
195
"""IRC command with numeric code mapping."""
196
197
def __init__(self, code: int, name: str):
198
"""
199
Initialize IRC command.
200
201
Parameters:
202
- code: int, numeric IRC code
203
- name: str, command name
204
"""
205
206
@property
207
def code(self) -> int:
208
"""Numeric IRC code."""
209
210
def __int__(self) -> int:
211
"""Return numeric code when converted to int."""
212
213
@staticmethod
214
def lookup(raw: str) -> str:
215
"""
216
Look up command name from raw IRC data.
217
218
Parameters:
219
- raw: str, raw IRC command
220
221
Returns:
222
str, event type name
223
"""
224
225
# Mapping of numeric codes to command names
226
numeric_codes = {
227
1: "welcome",
228
2: "yourhost",
229
3: "created",
230
4: "myinfo",
231
5: "isupport",
232
# ... (full numeric mapping available)
233
}
234
235
# Mapping of command names to numeric codes
236
command_codes = {
237
"welcome": 1,
238
"yourhost": 2,
239
"created": 3,
240
# ... (reverse mapping)
241
}
242
```
243
244
## Usage Examples
245
246
### Basic Event Handling
247
248
```python
249
import irc.client
250
251
def on_connect(connection, event):
252
"""Handle successful connection."""
253
print(f"Connected to {event.source}")
254
connection.join("#test")
255
256
def on_join(connection, event):
257
"""Handle channel join."""
258
channel = event.target
259
nick = event.source.nick
260
261
if nick == connection.get_nickname():
262
print(f"Joined {channel}")
263
else:
264
print(f"{nick} joined {channel}")
265
connection.privmsg(channel, f"Welcome {nick}!")
266
267
def on_pubmsg(connection, event):
268
"""Handle public channel messages."""
269
channel = event.target
270
nick = event.source.nick
271
message = event.arguments[0]
272
273
print(f"[{channel}] <{nick}> {message}")
274
275
# Respond to mentions
276
bot_nick = connection.get_nickname()
277
if bot_nick.lower() in message.lower():
278
connection.privmsg(channel, f"{nick}: You mentioned me!")
279
280
def on_privmsg(connection, event):
281
"""Handle private messages."""
282
nick = event.source.nick
283
message = event.arguments[0]
284
285
print(f"PM from {nick}: {message}")
286
connection.privmsg(nick, f"Echo: {message}")
287
288
# Create client and add handlers
289
client = irc.client.SimpleIRCClient()
290
client.connection.add_global_handler("welcome", on_connect)
291
client.connection.add_global_handler("join", on_join)
292
client.connection.add_global_handler("pubmsg", on_pubmsg)
293
client.connection.add_global_handler("privmsg", on_privmsg)
294
295
client.connect("irc.libera.chat", 6667, "eventbot")
296
client.start()
297
```
298
299
### Priority-Based Event Handling
300
301
```python
302
import irc.client
303
304
def high_priority_handler(connection, event):
305
"""High priority handler - runs first."""
306
message = event.arguments[0]
307
if message.startswith("!stop"):
308
print("Emergency stop requested!")
309
connection.quit("Emergency stop")
310
return "stop_processing" # Can halt further processing
311
312
def medium_priority_handler(connection, event):
313
"""Medium priority handler."""
314
message = event.arguments[0]
315
if message.startswith("!"):
316
print(f"Command detected: {message}")
317
318
def low_priority_handler(connection, event):
319
"""Low priority handler - runs last."""
320
print(f"Logging message: {event.arguments[0]}")
321
322
client = irc.client.SimpleIRCClient()
323
324
# Add handlers with different priorities (lower = higher priority)
325
client.connection.add_global_handler("pubmsg", high_priority_handler, priority=0)
326
client.connection.add_global_handler("pubmsg", medium_priority_handler, priority=5)
327
client.connection.add_global_handler("pubmsg", low_priority_handler, priority=10)
328
329
client.connect("irc.libera.chat", 6667, "prioritybot")
330
client.start()
331
```
332
333
### Comprehensive Event Logger
334
335
```python
336
import irc.client
337
import datetime
338
import json
339
340
class EventLogger:
341
def __init__(self, filename="irc_events.log"):
342
self.filename = filename
343
self.client = irc.client.SimpleIRCClient()
344
self.setup_handlers()
345
346
def log_event(self, event_type, connection, event):
347
"""Log event to file."""
348
log_entry = {
349
"timestamp": datetime.datetime.now().isoformat(),
350
"event_type": event_type,
351
"server": connection.get_server_name(),
352
"source": str(event.source) if event.source else None,
353
"target": event.target,
354
"arguments": event.arguments,
355
"tags": event.tags
356
}
357
358
with open(self.filename, "a") as f:
359
f.write(json.dumps(log_entry) + "\n")
360
361
print(f"[{event_type}] {event.source} -> {event.target}: {event.arguments}")
362
363
def setup_handlers(self):
364
"""Set up event handlers for all event types."""
365
events_to_log = [
366
"welcome", "join", "part", "quit", "kick", "mode", "topic",
367
"pubmsg", "privmsg", "pubnotice", "privnotice", "nick",
368
"disconnect", "error"
369
]
370
371
for event_type in events_to_log:
372
handler = lambda conn, evt, etype=event_type: self.log_event(etype, conn, evt)
373
self.client.connection.add_global_handler(event_type, handler)
374
375
def connect_and_join(self, server, port, nickname, channels):
376
"""Connect to server and join channels."""
377
def on_connect(connection, event):
378
for channel in channels:
379
connection.join(channel)
380
381
self.client.connection.add_global_handler("welcome", on_connect)
382
self.client.connect(server, port, nickname)
383
self.client.start()
384
385
# Usage
386
logger = EventLogger("irc_events.log")
387
logger.connect_and_join("irc.libera.chat", 6667, "logbot", ["#test", "#logging"])
388
```
389
390
### Event-Driven Bot Framework
391
392
```python
393
import irc.client
394
from functools import wraps
395
396
class EventBot:
397
def __init__(self):
398
self.client = irc.client.SimpleIRCClient()
399
self.commands = {}
400
self.event_handlers = {}
401
self.setup_base_handlers()
402
403
def command(self, trigger):
404
"""Decorator for command handlers."""
405
def decorator(func):
406
self.commands[trigger] = func
407
return func
408
return decorator
409
410
def event_handler(self, event_type, priority=5):
411
"""Decorator for event handlers."""
412
def decorator(func):
413
if event_type not in self.event_handlers:
414
self.event_handlers[event_type] = []
415
self.event_handlers[event_type].append((priority, func))
416
return func
417
return decorator
418
419
def setup_base_handlers(self):
420
"""Set up base event handling."""
421
def handle_pubmsg(connection, event):
422
message = event.arguments[0]
423
channel = event.target
424
nick = event.source.nick
425
426
# Check for commands
427
if message.startswith("!"):
428
command = message[1:].split()[0]
429
if command in self.commands:
430
try:
431
self.commands[command](connection, event)
432
except Exception as e:
433
connection.privmsg(channel, f"Error: {e}")
434
435
# Call custom event handlers
436
if "pubmsg" in self.event_handlers:
437
handlers = sorted(self.event_handlers["pubmsg"], key=lambda x: x[0])
438
for priority, handler in handlers:
439
try:
440
handler(connection, event)
441
except Exception as e:
442
print(f"Handler error: {e}")
443
444
self.client.connection.add_global_handler("pubmsg", handle_pubmsg)
445
446
def connect(self, server, port, nickname):
447
"""Connect to IRC server."""
448
def on_connect(connection, event):
449
print(f"Connected as {nickname}")
450
451
self.client.connection.add_global_handler("welcome", on_connect)
452
self.client.connect(server, port, nickname)
453
454
def start(self):
455
"""Start bot."""
456
self.client.start()
457
458
# Example bot using the framework
459
bot = EventBot()
460
461
@bot.command("hello")
462
def hello_command(connection, event):
463
"""Say hello to user."""
464
nick = event.source.nick
465
channel = event.target
466
connection.privmsg(channel, f"Hello {nick}!")
467
468
@bot.command("time")
469
def time_command(connection, event):
470
"""Show current time."""
471
import datetime
472
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
473
connection.privmsg(event.target, f"Current time: {now}")
474
475
@bot.event_handler("join", priority=1)
476
def welcome_users(connection, event):
477
"""Welcome new users."""
478
nick = event.source.nick
479
channel = event.target
480
481
if nick != connection.get_nickname():
482
connection.privmsg(channel, f"Welcome to {channel}, {nick}!")
483
484
@bot.event_handler("pubmsg", priority=10)
485
def message_counter(connection, event):
486
"""Count messages (low priority)."""
487
if not hasattr(message_counter, "count"):
488
message_counter.count = 0
489
message_counter.count += 1
490
491
if message_counter.count % 100 == 0:
492
connection.privmsg(event.target, f"Message #{message_counter.count}!")
493
494
# Connect and start
495
bot.connect("irc.libera.chat", 6667, "eventbot")
496
bot.start()
497
```
498
499
### IRCv3 Message Tags Handler
500
501
```python
502
import irc.client
503
504
def handle_tagged_message(connection, event):
505
"""Handle messages with IRCv3 tags."""
506
if event.tags:
507
print(f"Message tags: {event.tags}")
508
509
# Check for specific tags
510
if "account" in event.tags:
511
print(f"User is authenticated as: {event.tags['account']}")
512
513
if "time" in event.tags:
514
print(f"Message timestamp: {event.tags['time']}")
515
516
if "batch" in event.tags:
517
print(f"Part of batch: {event.tags['batch']}")
518
519
# Process message normally
520
channel = event.target
521
nick = event.source.nick
522
message = event.arguments[0]
523
print(f"[{channel}] <{nick}> {message}")
524
525
def handle_cap_ack(connection, event):
526
"""Handle capability acknowledgment."""
527
caps = event.arguments[1].split()
528
print(f"Server acknowledged capabilities: {caps}")
529
530
def on_connect(connection, event):
531
"""Request IRCv3 capabilities on connect."""
532
connection.send_raw("CAP LS 302") # Request capability list
533
connection.send_raw("CAP REQ :message-tags server-time batch") # Request specific caps
534
connection.send_raw("CAP END") # End capability negotiation
535
connection.join("#test")
536
537
client = irc.client.SimpleIRCClient()
538
client.connection.add_global_handler("welcome", on_connect)
539
client.connection.add_global_handler("pubmsg", handle_tagged_message)
540
client.connection.add_global_handler("cap", handle_cap_ack)
541
542
client.connect("irc.libera.chat", 6667, "tagbot")
543
client.start()
544
```