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

connection-management.mddocs/

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

```