0
# Connection Management
1
2
Connection factories, SSL support, IPv6 compatibility, rate limiting, and automatic ping/pong handling for robust IRC connections. This module provides the networking foundation for IRC client connections.
3
4
## Capabilities
5
6
### Connection Factories
7
8
Factory classes that create and configure socket connections for IRC clients.
9
10
```python { .api }
11
class Factory:
12
"""Socket connection factory for synchronous IRC connections."""
13
14
def __init__(self, bind_address=None, wrapper=None, ipv6: bool = False):
15
"""
16
Initialize connection factory.
17
18
Parameters:
19
- bind_address: tuple, optional local address to bind (host, port)
20
- wrapper: callable, socket wrapper function (for SSL, etc.)
21
- ipv6: bool, whether to use IPv6 (default: False)
22
"""
23
24
@property
25
def family(self) -> int:
26
"""Socket family (AF_INET or AF_INET6)."""
27
28
@property
29
def bind_address(self) -> tuple:
30
"""Local bind address."""
31
32
@property
33
def wrapper(self) -> callable:
34
"""Socket wrapper function."""
35
36
def connect(self, server_address: tuple):
37
"""
38
Create connection to server.
39
40
Parameters:
41
- server_address: tuple, (hostname, port)
42
43
Returns:
44
Connected socket object
45
"""
46
47
def __call__(self, server_address: tuple):
48
"""
49
Callable interface for connection creation.
50
51
Parameters:
52
- server_address: tuple, (hostname, port)
53
54
Returns:
55
Connected socket object
56
"""
57
58
class AioFactory:
59
"""Connection factory for asyncio IRC connections."""
60
61
def __init__(self, **kwargs):
62
"""
63
Initialize asyncio connection factory.
64
65
Parameters:
66
- **kwargs: connection arguments passed to asyncio.create_connection
67
"""
68
69
@property
70
def connection_args(self) -> dict:
71
"""Asyncio connection arguments."""
72
73
async def connect(self, protocol_instance, server_address: tuple):
74
"""
75
Create asyncio connection to server.
76
77
Parameters:
78
- protocol_instance: asyncio protocol instance
79
- server_address: tuple, (hostname, port)
80
81
Returns:
82
(transport, protocol) tuple
83
"""
84
85
async def __call__(self, protocol_instance, server_address: tuple):
86
"""
87
Callable interface for asyncio connection creation.
88
89
Parameters:
90
- protocol_instance: asyncio protocol instance
91
- server_address: tuple, (hostname, port)
92
93
Returns:
94
(transport, protocol) tuple
95
"""
96
97
def identity(x):
98
"""
99
Identity function for socket wrapper.
100
101
Parameters:
102
- x: input value
103
104
Returns:
105
Unchanged input value
106
"""
107
```
108
109
### Rate Limiting
110
111
Control outgoing message rate to comply with server limits and avoid flooding.
112
113
```python { .api }
114
class ServerConnection:
115
def set_rate_limit(self, frequency: float):
116
"""
117
Set rate limiting for outgoing messages.
118
119
Limits the number of messages sent per second to prevent
120
server disconnection due to excess flooding.
121
122
Parameters:
123
- frequency: float, maximum messages per second (e.g., 1.0 = 1 msg/sec)
124
"""
125
126
class AioConnection:
127
def set_rate_limit(self, frequency: float):
128
"""
129
Set rate limiting for outgoing messages (asyncio version).
130
131
Parameters:
132
- frequency: float, maximum messages per second
133
"""
134
```
135
136
### Keep-Alive Management
137
138
Automatic ping/pong handling to maintain connection health.
139
140
```python { .api }
141
class ServerConnection:
142
def set_keepalive(self, interval: int):
143
"""
144
Set keepalive ping interval.
145
146
Automatically sends PING messages to server at regular intervals
147
to detect connection issues and prevent timeout disconnections.
148
149
Parameters:
150
- interval: int, seconds between keepalive pings (0 to disable)
151
"""
152
153
def _ping_ponger(connection, event):
154
"""
155
Default ping response handler.
156
157
Automatically responds to server PING messages with appropriate PONG.
158
This handler is installed by default on all connections.
159
160
Parameters:
161
- connection: ServerConnection, IRC connection
162
- event: Event, PING event
163
"""
164
```
165
166
### Connection State Management
167
168
Properties and methods for monitoring and controlling connection state.
169
170
```python { .api }
171
class ServerConnection:
172
@property
173
def connected(self) -> bool:
174
"""Whether connection is established and active."""
175
176
@property
177
def socket(self):
178
"""Underlying socket object."""
179
180
def get_server_name(self) -> str:
181
"""
182
Get connected server name.
183
184
Returns:
185
str, server hostname or name
186
"""
187
188
def is_connected(self) -> bool:
189
"""
190
Check if connected to server.
191
192
Returns:
193
bool, True if connection is active
194
"""
195
196
def reconnect(self):
197
"""
198
Reconnect to the same server using stored parameters.
199
200
Attempts to re-establish connection with the same server,
201
port, nickname, and other connection parameters.
202
"""
203
204
def close(self):
205
"""
206
Close connection immediately without sending QUIT.
207
208
Forcibly closes the socket connection without proper IRC logout.
209
Use disconnect() for graceful disconnection.
210
"""
211
212
def disconnect(self, message: str = ""):
213
"""
214
Gracefully disconnect from server.
215
216
Sends QUIT message and closes connection properly.
217
218
Parameters:
219
- message: str, quit message sent to server
220
"""
221
```
222
223
### SSL/TLS Support
224
225
Secure connection support using SSL/TLS encryption.
226
227
```python { .api }
228
import ssl
229
230
# SSL context creation
231
ssl_context = ssl.create_default_context()
232
233
# SSL factory for synchronous connections
234
ssl_factory = Factory(wrapper=ssl_context.wrap_socket)
235
236
# SSL factory for asyncio connections
237
aio_ssl_factory = AioFactory(ssl=ssl_context)
238
```
239
240
### IPv6 Support
241
242
Native IPv6 connectivity for modern network environments.
243
244
```python { .api }
245
# IPv6 factory for synchronous connections
246
ipv6_factory = Factory(ipv6=True)
247
248
# IPv6 with SSL
249
ipv6_ssl_factory = Factory(ipv6=True, wrapper=ssl_context.wrap_socket)
250
```
251
252
## Usage Examples
253
254
### Basic Connection Factory
255
256
```python
257
import irc.client
258
from irc.connection import Factory
259
260
# Create custom connection factory
261
factory = Factory()
262
263
# Connect with custom factory
264
client = irc.client.SimpleIRCClient()
265
client.connect("irc.libera.chat", 6667, "factorybot", connect_factory=factory)
266
client.start()
267
```
268
269
### SSL Connection
270
271
```python
272
import irc.client
273
import ssl
274
from irc.connection import Factory
275
276
# Create SSL context
277
ssl_context = ssl.create_default_context()
278
279
# Optional: customize SSL settings
280
ssl_context.check_hostname = False # For self-signed certificates
281
ssl_context.verify_mode = ssl.CERT_NONE # Disable certificate verification
282
283
# Create SSL factory
284
ssl_factory = Factory(wrapper=ssl_context.wrap_socket)
285
286
def on_connect(connection, event):
287
print("Connected via SSL!")
288
connection.join("#secure")
289
290
def on_join(connection, event):
291
print(f"Joined {event.target} securely")
292
293
client = irc.client.SimpleIRCClient()
294
client.connection.add_global_handler("welcome", on_connect)
295
client.connection.add_global_handler("join", on_join)
296
297
# Connect using SSL on port 6697
298
client.connect("irc.libera.chat", 6697, "sslbot", connect_factory=ssl_factory)
299
client.start()
300
```
301
302
### IPv6 Connection
303
304
```python
305
import irc.client
306
from irc.connection import Factory
307
308
# Create IPv6 factory
309
ipv6_factory = Factory(ipv6=True)
310
311
def on_connect(connection, event):
312
print("Connected via IPv6!")
313
connection.join("#ipv6test")
314
315
client = irc.client.SimpleIRCClient()
316
client.connection.add_global_handler("welcome", on_connect)
317
318
# Connect using IPv6
319
client.connect("irc.libera.chat", 6667, "ipv6bot", connect_factory=ipv6_factory)
320
client.start()
321
```
322
323
### Rate Limiting and Keep-Alive
324
325
```python
326
import irc.client
327
328
def on_connect(connection, event):
329
print("Connected, configuring rate limiting and keep-alive...")
330
331
# Set rate limit to 1 message per second
332
connection.set_rate_limit(1.0)
333
334
# Send keep-alive ping every 60 seconds
335
connection.set_keepalive(60)
336
337
connection.join("#ratelimited")
338
339
def on_join(connection, event):
340
# These messages will be rate-limited
341
for i in range(5):
342
connection.privmsg(event.target, f"Rate limited message {i+1}")
343
344
client = irc.client.SimpleIRCClient()
345
client.connection.add_global_handler("welcome", on_connect)
346
client.connection.add_global_handler("join", on_join)
347
348
client.connect("irc.libera.chat", 6667, "ratebot")
349
client.start()
350
```
351
352
### Connection Monitoring and Reconnection
353
354
```python
355
import irc.client
356
import time
357
358
class ReconnectingBot:
359
def __init__(self):
360
self.client = irc.client.SimpleIRCClient()
361
self.server = "irc.libera.chat"
362
self.port = 6667
363
self.nickname = "reconnectbot"
364
self.channels = ["#test"]
365
self.setup_handlers()
366
367
def setup_handlers(self):
368
"""Set up event handlers."""
369
def on_connect(connection, event):
370
print(f"Connected to {connection.get_server_name()}")
371
372
# Configure connection
373
connection.set_rate_limit(2.0) # 2 messages per second max
374
connection.set_keepalive(30) # Ping every 30 seconds
375
376
# Join channels
377
for channel in self.channels:
378
connection.join(channel)
379
380
def on_disconnect(connection, event):
381
print("Disconnected from server")
382
self.attempt_reconnection()
383
384
def on_ping(connection, event):
385
print(f"Received ping from {event.source}")
386
# Automatic pong is handled by default ping_ponger
387
388
def on_pong(connection, event):
389
print(f"Received pong from {event.source}")
390
391
def on_pubmsg(connection, event):
392
message = event.arguments[0]
393
channel = event.target
394
395
if message == "!status":
396
status = "Connected" if connection.is_connected() else "Disconnected"
397
server = connection.get_server_name()
398
connection.privmsg(channel, f"Status: {status} to {server}")
399
400
elif message == "!reconnect":
401
connection.privmsg(channel, "Reconnecting...")
402
connection.reconnect()
403
404
self.client.connection.add_global_handler("welcome", on_connect)
405
self.client.connection.add_global_handler("disconnect", on_disconnect)
406
self.client.connection.add_global_handler("ping", on_ping)
407
self.client.connection.add_global_handler("pong", on_pong)
408
self.client.connection.add_global_handler("pubmsg", on_pubmsg)
409
410
def attempt_reconnection(self):
411
"""Attempt to reconnect with exponential backoff."""
412
max_attempts = 5
413
base_delay = 2
414
415
for attempt in range(max_attempts):
416
delay = base_delay * (2 ** attempt) # Exponential backoff
417
print(f"Reconnection attempt {attempt + 1} in {delay} seconds...")
418
419
time.sleep(delay)
420
421
try:
422
self.client.connection.reconnect()
423
print("Reconnection successful!")
424
return
425
except Exception as e:
426
print(f"Reconnection failed: {e}")
427
428
print("All reconnection attempts failed")
429
430
def start(self):
431
"""Start the bot."""
432
self.client.connect(self.server, self.port, self.nickname)
433
self.client.start()
434
435
# Usage
436
bot = ReconnectingBot()
437
bot.start()
438
```
439
440
### Asyncio SSL Connection
441
442
```python
443
import asyncio
444
import ssl
445
from irc.client_aio import AioSimpleIRCClient
446
from irc.connection import AioFactory
447
448
async def main():
449
# Create SSL context
450
ssl_context = ssl.create_default_context()
451
452
# Create asyncio SSL factory
453
ssl_factory = AioFactory(ssl=ssl_context)
454
455
client = AioSimpleIRCClient()
456
457
def on_connect(connection, event):
458
print("Connected via SSL (asyncio)!")
459
connection.join("#asyncssl")
460
461
def on_join(connection, event):
462
connection.privmsg(event.target, "Hello from asyncio SSL bot!")
463
464
client.connection.add_global_handler("welcome", on_connect)
465
client.connection.add_global_handler("join", on_join)
466
467
# Connect with SSL
468
await client.connect(
469
"irc.libera.chat",
470
6697,
471
"asyncsslbot",
472
connect_factory=ssl_factory
473
)
474
475
# Keep running
476
await asyncio.Event().wait()
477
478
asyncio.run(main())
479
```
480
481
### Custom Socket Wrapper
482
483
```python
484
import irc.client
485
import socket
486
from irc.connection import Factory
487
488
def logging_wrapper(sock):
489
"""Custom socket wrapper that logs all data."""
490
491
class LoggingSocket:
492
def __init__(self, socket):
493
self._socket = socket
494
495
def send(self, data):
496
print(f"SEND: {data}")
497
return self._socket.send(data)
498
499
def recv(self, size):
500
data = self._socket.recv(size)
501
print(f"RECV: {data}")
502
return data
503
504
def __getattr__(self, name):
505
return getattr(self._socket, name)
506
507
return LoggingSocket(sock)
508
509
# Create factory with custom wrapper
510
logging_factory = Factory(wrapper=logging_wrapper)
511
512
def on_connect(connection, event):
513
connection.join("#logging")
514
515
client = irc.client.SimpleIRCClient()
516
client.connection.add_global_handler("welcome", on_connect)
517
518
# All network traffic will be logged
519
client.connect("irc.libera.chat", 6667, "loggingbot", connect_factory=logging_factory)
520
client.start()
521
```
522
523
### Multi-Server Connection Management
524
525
```python
526
import irc.client
527
from irc.connection import Factory
528
import ssl
529
530
class MultiServerBot:
531
def __init__(self):
532
self.reactor = irc.client.Reactor()
533
self.connections = {}
534
self.setup_servers()
535
536
def setup_servers(self):
537
"""Set up connections to multiple servers."""
538
servers = [
539
{
540
"name": "libera",
541
"host": "irc.libera.chat",
542
"port": 6697,
543
"ssl": True,
544
"channels": ["#test1", "#libera"]
545
},
546
{
547
"name": "oftc",
548
"host": "irc.oftc.net",
549
"port": 6667,
550
"ssl": False,
551
"channels": ["#test2", "#oftc"]
552
}
553
]
554
555
for server_config in servers:
556
self.setup_server_connection(server_config)
557
558
def setup_server_connection(self, config):
559
"""Set up connection to a single server."""
560
connection = self.reactor.server()
561
562
# Create appropriate factory
563
if config["ssl"]:
564
ssl_context = ssl.create_default_context()
565
factory = Factory(wrapper=ssl_context.wrap_socket)
566
else:
567
factory = Factory()
568
569
def on_connect(conn, event):
570
print(f"Connected to {config['name']}")
571
conn.set_rate_limit(1.0)
572
conn.set_keepalive(60)
573
574
for channel in config["channels"]:
575
conn.join(channel)
576
577
def on_pubmsg(conn, event):
578
message = event.arguments[0]
579
channel = event.target
580
nick = event.source.nick
581
582
print(f"[{config['name']}:{channel}] <{nick}> {message}")
583
584
if message == "!servers":
585
server_list = list(self.connections.keys())
586
conn.privmsg(channel, f"Connected to: {', '.join(server_list)}")
587
588
connection.add_global_handler("welcome", on_connect)
589
connection.add_global_handler("pubmsg", on_pubmsg)
590
591
# Connect to server
592
connection.connect(
593
config["host"],
594
config["port"],
595
f"multibot_{config['name']}",
596
connect_factory=factory
597
)
598
599
self.connections[config["name"]] = connection
600
601
def start(self):
602
"""Start processing events for all connections."""
603
print("Starting multi-server bot...")
604
self.reactor.process_forever()
605
606
# Usage
607
bot = MultiServerBot()
608
bot.start()
609
```