or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asynchronous-client.mdbot-framework.mdconnection-management.mdevent-system.mdindex.mdprotocol-extensions.mdsynchronous-client.mdutilities.md

event-system.mddocs/

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

```