0
# Asynchronous IRC Client
1
2
Asyncio-based IRC client implementation providing full async/await support for modern Python applications. Uses asyncio protocols for non-blocking network communication and integrates seamlessly with asyncio event loops.
3
4
## Capabilities
5
6
### AioReactor
7
8
Asyncio-based reactor for managing IRC connections in asynchronous applications. Integrates with asyncio event loops for non-blocking event processing.
9
10
```python { .api }
11
class AioReactor:
12
def __init__(self, on_connect=None, on_disconnect=None, loop=None):
13
"""
14
Initialize asyncio IRC reactor.
15
16
Parameters:
17
- on_connect: callable, called when connection established
18
- on_disconnect: callable, called when connection lost
19
- loop: asyncio event loop, uses current loop if None
20
"""
21
22
@property
23
def loop(self):
24
"""Asyncio event loop used by reactor."""
25
26
def server(self) -> AioConnection:
27
"""
28
Create new AioConnection for IRC server communication.
29
30
Returns:
31
AioConnection instance
32
"""
33
34
async def process_forever(self):
35
"""
36
Process events indefinitely using asyncio.
37
38
This method runs until all connections are closed.
39
"""
40
41
def add_global_handler(self, event: str, handler, priority: int = 0):
42
"""
43
Add global event handler for all connections.
44
45
Parameters:
46
- event: str, event type to handle
47
- handler: callable, handler function (connection, event)
48
- priority: int, handler priority (lower = higher priority)
49
"""
50
51
def remove_global_handler(self, event: str, handler):
52
"""
53
Remove global event handler.
54
55
Parameters:
56
- event: str, event type
57
- handler: callable, handler function to remove
58
"""
59
60
def disconnect_all(self, message: str = ""):
61
"""
62
Disconnect all managed connections.
63
64
Parameters:
65
- message: str, quit message
66
"""
67
```
68
69
### AioConnection
70
71
Asyncio-based IRC server connection using asyncio protocols for non-blocking network communication.
72
73
```python { .api }
74
class AioConnection:
75
@property
76
def connected(self) -> bool:
77
"""Whether connection is established."""
78
79
@property
80
def features(self) -> FeatureSet:
81
"""Server-announced features and capabilities."""
82
83
async def connect(self, server, port, nickname, password=None,
84
username=None, ircname=None, connect_factory=connection.AioFactory()):
85
"""
86
Connect to IRC server asynchronously.
87
88
Parameters:
89
- server: server hostname
90
- port: server port
91
- nickname: desired nickname
92
- password: optional server password
93
- username: optional username (defaults to nickname)
94
- ircname: optional real name (defaults to nickname)
95
- connect_factory: optional connection factory (defaults to AioFactory())
96
"""
97
98
def process_data(self, new_data: str):
99
"""
100
Process incoming IRC data.
101
102
Parameters:
103
- new_data: str, raw IRC protocol data
104
"""
105
106
def send_raw(self, string: str):
107
"""
108
Send raw IRC command.
109
110
Parameters:
111
- string: str, raw IRC protocol message
112
"""
113
114
def disconnect(self, message: str = ""):
115
"""
116
Disconnect from server.
117
118
Parameters:
119
- message: str, quit message
120
"""
121
122
def get_server_name(self) -> str:
123
"""Get connected server name."""
124
125
def get_nickname(self) -> str:
126
"""Get current nickname."""
127
128
def is_connected(self) -> bool:
129
"""Check if connected to server."""
130
131
def set_rate_limit(self, frequency: float):
132
"""
133
Set rate limiting for outgoing messages.
134
135
Parameters:
136
- frequency: float, maximum messages per second
137
"""
138
139
def set_keepalive(self, interval: int):
140
"""
141
Set keepalive ping interval.
142
143
Parameters:
144
- interval: int, seconds between keepalive pings
145
"""
146
```
147
148
### AioSimpleIRCClient
149
150
High-level asyncio IRC client that manages a single server connection with simplified event handling for async applications.
151
152
```python { .api }
153
class AioSimpleIRCClient:
154
@property
155
def reactor(self) -> AioReactor:
156
"""Access to underlying asyncio reactor."""
157
158
@property
159
def connection(self) -> AioConnection:
160
"""Active server connection."""
161
162
def __init__(self):
163
"""Initialize asyncio simple IRC client."""
164
165
async def connect(self, server: str, port: int, nickname: str, **kwargs):
166
"""
167
Connect to IRC server asynchronously.
168
169
Parameters:
170
- server: str, server hostname
171
- port: int, server port
172
- nickname: str, desired nickname
173
- **kwargs: additional connection parameters
174
"""
175
```
176
177
### IrcProtocol
178
179
Asyncio protocol implementation for IRC communication handling low-level network operations.
180
181
```python { .api }
182
class IrcProtocol:
183
def __init__(self, connection, loop):
184
"""
185
Initialize IRC protocol handler.
186
187
Parameters:
188
- connection: AioConnection, associated connection
189
- loop: asyncio event loop
190
"""
191
192
def data_received(self, data: bytes):
193
"""
194
Handle received network data.
195
196
Parameters:
197
- data: bytes, raw network data
198
"""
199
200
def connection_lost(self, exc):
201
"""
202
Handle connection loss.
203
204
Parameters:
205
- exc: Exception, optional exception that caused disconnection
206
"""
207
```
208
209
## IRC Protocol Commands (Async)
210
211
All standard IRC protocol commands are available on AioConnection with the same interface as ServerConnection:
212
213
```python { .api }
214
class AioConnection:
215
def nick(self, newnick: str):
216
"""Change nickname."""
217
218
def user(self, username: str, ircname: str):
219
"""Send USER command during registration."""
220
221
def join(self, channel: str, key: str = ""):
222
"""Join IRC channel."""
223
224
def part(self, channel: str, message: str = ""):
225
"""Leave IRC channel."""
226
227
def privmsg(self, target: str, text: str):
228
"""Send private message to user or channel."""
229
230
def notice(self, target: str, text: str):
231
"""Send notice to user or channel."""
232
233
def quit(self, message: str = ""):
234
"""Quit IRC server."""
235
236
def ping(self, target: str):
237
"""Send PING to server or user."""
238
239
def pong(self, target: str):
240
"""Send PONG response."""
241
242
def kick(self, channel: str, nick: str, comment: str = ""):
243
"""Kick user from channel."""
244
245
def mode(self, target: str, command: str):
246
"""Set user or channel mode."""
247
248
def topic(self, channel: str, new_topic: str = None):
249
"""Get or set channel topic."""
250
251
def whois(self, target: str):
252
"""Query user information."""
253
254
def who(self, target: str, op: str = ""):
255
"""Query user list."""
256
```
257
258
## Usage Examples
259
260
### Basic Asyncio IRC Client
261
262
```python
263
import asyncio
264
from irc.client_aio import AioSimpleIRCClient
265
266
async def main():
267
client = AioSimpleIRCClient()
268
269
def on_connect(connection, event):
270
print("Connected to server")
271
connection.join("#test")
272
273
def on_join(connection, event):
274
print(f"Joined {event.target}")
275
connection.privmsg(event.target, "Hello from async bot!")
276
277
def on_pubmsg(connection, event):
278
print(f"<{event.source.nick}> {event.arguments[0]}")
279
# Echo messages back
280
connection.privmsg(event.target, f"Echo: {event.arguments[0]}")
281
282
# Add event handlers
283
client.connection.add_global_handler("welcome", on_connect)
284
client.connection.add_global_handler("join", on_join)
285
client.connection.add_global_handler("pubmsg", on_pubmsg)
286
287
# Connect to server
288
await client.connect("irc.libera.chat", 6667, "asyncbot")
289
290
# Keep running
291
await asyncio.Event().wait()
292
293
asyncio.run(main())
294
```
295
296
### Multiple Async Connections
297
298
```python
299
import asyncio
300
from irc.client_aio import AioReactor
301
302
async def main():
303
reactor = AioReactor()
304
305
# Create multiple connections
306
conn1 = reactor.server()
307
conn2 = reactor.server()
308
309
def on_connect(connection, event):
310
if connection == conn1:
311
connection.join("#channel1")
312
else:
313
connection.join("#channel2")
314
315
def on_pubmsg(connection, event):
316
print(f"[{connection.get_server_name()}] <{event.source.nick}> {event.arguments[0]}")
317
318
reactor.add_global_handler("welcome", on_connect)
319
reactor.add_global_handler("pubmsg", on_pubmsg)
320
321
# Connect to multiple servers
322
await conn1.connect("irc.libera.chat", 6667, "bot1")
323
await conn2.connect("irc.oftc.net", 6667, "bot2")
324
325
# Process events for all connections
326
await reactor.process_forever()
327
328
asyncio.run(main())
329
```
330
331
### Async Bot with Periodic Tasks
332
333
```python
334
import asyncio
335
from irc.client_aio import AioSimpleIRCClient
336
337
async def periodic_announce(client):
338
"""Send periodic announcements."""
339
while True:
340
await asyncio.sleep(300) # 5 minutes
341
if client.connection.is_connected():
342
client.connection.privmsg("#test", "Periodic announcement!")
343
344
async def main():
345
client = AioSimpleIRCClient()
346
347
def on_connect(connection, event):
348
connection.join("#test")
349
print("Connected and joined channel")
350
351
def on_pubmsg(connection, event):
352
message = event.arguments[0]
353
if message.startswith("!time"):
354
import datetime
355
now = datetime.datetime.now().strftime("%H:%M:%S")
356
connection.privmsg(event.target, f"Current time: {now}")
357
358
client.connection.add_global_handler("welcome", on_connect)
359
client.connection.add_global_handler("pubmsg", on_pubmsg)
360
361
# Connect to server
362
await client.connect("irc.libera.chat", 6667, "timebot")
363
364
# Start periodic task
365
announce_task = asyncio.create_task(periodic_announce(client))
366
367
try:
368
# Wait indefinitely
369
await asyncio.Event().wait()
370
except KeyboardInterrupt:
371
announce_task.cancel()
372
client.connection.quit("Bot shutting down")
373
374
asyncio.run(main())
375
```
376
377
### Async Connection with SSL
378
379
```python
380
import asyncio
381
import ssl
382
from irc.client_aio import AioSimpleIRCClient
383
from irc.connection import AioFactory
384
385
async def main():
386
# Create SSL context
387
ssl_context = ssl.create_default_context()
388
389
# Create SSL connection factory
390
ssl_factory = AioFactory(ssl=ssl_context)
391
392
client = AioSimpleIRCClient()
393
394
def on_connect(connection, event):
395
connection.join("#secure")
396
print("Connected via SSL")
397
398
client.connection.add_global_handler("welcome", on_connect)
399
400
# Connect with SSL on port 6697
401
await client.connect(
402
"irc.libera.chat",
403
6697,
404
"sslbot",
405
connect_factory=ssl_factory
406
)
407
408
await asyncio.Event().wait()
409
410
asyncio.run(main())
411
```
412
413
### Integration with Web Framework
414
415
```python
416
import asyncio
417
from aiohttp import web
418
from irc.client_aio import AioSimpleIRCClient
419
420
class IRCBot:
421
def __init__(self):
422
self.client = AioSimpleIRCClient()
423
self.messages = []
424
425
async def start(self):
426
def on_connect(connection, event):
427
connection.join("#webapp")
428
429
def on_pubmsg(connection, event):
430
self.messages.append({
431
'nick': event.source.nick,
432
'message': event.arguments[0],
433
'timestamp': asyncio.get_event_loop().time()
434
})
435
# Keep only last 100 messages
436
self.messages = self.messages[-100:]
437
438
self.client.connection.add_global_handler("welcome", on_connect)
439
self.client.connection.add_global_handler("pubmsg", on_pubmsg)
440
441
await self.client.connect("irc.libera.chat", 6667, "webapp_bot")
442
443
def send_message(self, message):
444
self.client.connection.privmsg("#webapp", message)
445
446
bot = IRCBot()
447
448
async def send_message(request):
449
data = await request.json()
450
bot.send_message(data['message'])
451
return web.json_response({'status': 'sent'})
452
453
async def get_messages(request):
454
return web.json_response({'messages': bot.messages})
455
456
async def init_app():
457
await bot.start()
458
459
app = web.Application()
460
app.router.add_post('/send', send_message)
461
app.router.add_get('/messages', get_messages)
462
463
return app
464
465
if __name__ == '__main__':
466
web.run_app(init_app(), host='0.0.0.0', port=8080)
467
```