or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mddatabase.mdindex.mdirc-protocol.mdplugin-development.mdutilities.md

configuration.mddocs/

0

# Bot Configuration

1

2

Sopel's configuration system provides hierarchical, section-based configuration with type validation, environment variable support, and runtime management. The system is built around the `Config` class and `StaticSection` subclasses that define configuration schemas.

3

4

## Capabilities

5

6

### Configuration Management

7

8

Core configuration functionality for loading, saving, and managing bot settings.

9

10

```python { .api }

11

class Config:

12

"""Main configuration management class."""

13

14

def __init__(self, filename: str, validate: bool = True):

15

"""

16

Initialize configuration from file.

17

18

Args:

19

filename (str): Path to configuration file

20

validate (bool): Whether to validate configuration on load

21

"""

22

23

@property

24

def filename(self) -> str:

25

"""Path to the configuration file."""

26

27

@property

28

def basename(self) -> str:

29

"""Configuration file basename without extension."""

30

31

@property

32

def homedir(self) -> str:

33

"""Configuration home directory path."""

34

35

@property

36

def parser(self) -> 'configparser.RawConfigParser':

37

"""Underlying configuration parser object."""

38

39

def save(self) -> None:

40

"""

41

Save all configuration changes to file.

42

43

Note: This removes any comments from the configuration file.

44

"""

45

46

def add_section(self, name: str) -> None | bool:

47

"""

48

Add a new empty section to configuration.

49

50

Args:

51

name (str): Section name (should use snake_case)

52

53

Returns:

54

None if successful, False if section already exists

55

"""

56

57

def define_section(self, name: str, cls_, validate: bool = True) -> None:

58

"""

59

Define a configuration section with a StaticSection class.

60

61

Args:

62

name (str): Section name (should use snake_case)

63

cls_: StaticSection subclass defining the section schema

64

validate (bool): Whether to validate section values

65

66

Raises:

67

ValueError: If section already defined with different class

68

"""

69

70

def get_defined_sections(self) -> list:

71

"""

72

Get all defined StaticSection instances.

73

74

Returns:

75

List of (name, section) tuples for all defined sections

76

"""

77

78

def option(self, question: str, default: bool = False) -> bool:

79

"""

80

Ask user a yes/no question interactively.

81

82

Args:

83

question (str): Question to ask user

84

default (bool): Default answer (True for 'y', False for 'n')

85

86

Returns:

87

User's boolean response

88

"""

89

90

def get(self, section: str, option: str) -> str:

91

"""Shortcut to parser.get() method."""

92

```

93

94

### Section Definition

95

96

Base classes and system for defining structured configuration sections.

97

98

```python { .api }

99

class StaticSection:

100

"""

101

Base class for defining configuration sections with typed attributes.

102

103

Subclass this to create configuration sections with validation and

104

type conversion.

105

"""

106

107

def __init__(self, config: Config, section_name: str, validate: bool = True):

108

"""

109

Initialize section from configuration.

110

111

Args:

112

config (Config): Parent configuration object

113

section_name (str): Name of this section

114

validate (bool): Whether to validate attributes

115

"""

116

```

117

118

### Configuration Attributes

119

120

Typed attribute classes for configuration values with validation and conversion.

121

122

```python { .api }

123

class ValidatedAttribute:

124

"""

125

Base attribute class with validation and type conversion.

126

127

Use for configuration values that need type checking or validation.

128

"""

129

130

def __init__(self, name: str, cls_=None, default=None, parse=None):

131

"""

132

Create a validated configuration attribute.

133

134

Args:

135

name (str): Configuration key name

136

cls_: Type class for validation (int, str, bool, etc.)

137

default: Default value if not set

138

parse (callable): Custom parsing function

139

"""

140

141

class ListAttribute(ValidatedAttribute):

142

"""

143

Attribute for configuration values that are lists.

144

145

Automatically splits comma-separated values into lists.

146

"""

147

148

def __init__(self, name: str, strip: bool = True, default=None):

149

"""

150

Create a list configuration attribute.

151

152

Args:

153

name (str): Configuration key name

154

strip (bool): Whether to strip whitespace from list items

155

default: Default list value

156

"""

157

158

class ChoiceAttribute(ValidatedAttribute):

159

"""

160

Attribute that must be one of a predefined set of choices.

161

"""

162

163

def __init__(self, name: str, choices: list, default=None):

164

"""

165

Create a choice configuration attribute.

166

167

Args:

168

name (str): Configuration key name

169

choices (list): List of valid choices

170

default: Default choice value

171

"""

172

173

class FilenameAttribute(ValidatedAttribute):

174

"""

175

Attribute for file path configuration values.

176

177

Handles path resolution and validation.

178

"""

179

180

def __init__(self, name: str, relative: bool = True, directory: str = None, default=None):

181

"""

182

Create a filename configuration attribute.

183

184

Args:

185

name (str): Configuration key name

186

relative (bool): Whether paths are relative to config directory

187

directory (str): Base directory for relative paths

188

default: Default filename

189

"""

190

```

191

192

### Core Configuration Section

193

194

The built-in core configuration section that defines essential bot settings.

195

196

```python { .api }

197

class CoreSection(StaticSection):

198

"""Core bot configuration section with essential settings."""

199

200

# Connection settings

201

nick: str # Bot's nickname

202

host: str # IRC server hostname

203

port: int # IRC server port (default: 6667)

204

use_ssl: bool # Whether to use SSL/TLS (default: False)

205

verify_ssl: bool # Whether to verify SSL certificates (default: True)

206

207

# Authentication

208

password: str # Server password (optional)

209

auth_method: str # Authentication method

210

auth_username: str # Authentication username

211

auth_password: str # Authentication password

212

213

# Bot identity

214

user: str # IRC username/ident

215

name: str # Real name field

216

channels: list # Auto-join channels

217

218

# Bot operators

219

owner: str # Bot owner nickname

220

admins: list # Bot admin nicknames

221

222

# Database

223

db_type: str # Database type (sqlite, mysql, postgresql, etc.)

224

db_filename: str # Database filename (SQLite only)

225

db_host: str # Database host

226

db_port: int # Database port

227

db_user: str # Database username

228

db_pass: str # Database password

229

db_name: str # Database name

230

231

# Logging

232

logdir: str # Log directory

233

logging_level: str # Logging level (DEBUG, INFO, WARNING, ERROR)

234

logging_format: str # Log message format

235

236

# Plugin settings

237

extra: list # Additional directories to search for plugins

238

exclude: list # Plugins to exclude from loading

239

240

# Runtime settings

241

homedir: str # Bot home directory

242

pid_dir: str # Directory for PID files

243

help_prefix: str # Prefix for help commands

244

reply_errors: bool # Whether to reply with error messages

245

```

246

247

## Usage Examples

248

249

### Basic Configuration Setup

250

251

```python

252

from sopel.config import Config

253

254

# Load configuration from file

255

config = Config('/path/to/bot.cfg')

256

257

# Access core settings

258

print(f"Bot nick: {config.core.nick}")

259

print(f"IRC server: {config.core.host}")

260

print(f"Channels: {config.core.channels}")

261

262

# Save configuration changes

263

config.core.nick = "NewBotName"

264

config.save()

265

```

266

267

### Creating Custom Configuration Sections

268

269

```python

270

from sopel import config

271

272

class WeatherSection(config.types.StaticSection):

273

"""Configuration for weather plugin."""

274

275

api_key = config.types.ValidatedAttribute('api_key')

276

default_location = config.types.ValidatedAttribute('default_location', default='London')

277

units = config.types.ChoiceAttribute('units', choices=['metric', 'imperial'], default='metric')

278

max_locations = config.types.ValidatedAttribute('max_locations', int, default=5)

279

timeout = config.types.ValidatedAttribute('timeout', int, default=30)

280

281

# In plugin setup function

282

def setup(bot):

283

"""Setup weather plugin configuration."""

284

bot.settings.define_section('weather', WeatherSection)

285

286

# Validate required settings

287

if not bot.settings.weather.api_key:

288

raise Exception("Weather plugin requires api_key in [weather] section")

289

290

# In plugin functions

291

@plugin.command('weather')

292

def weather_command(bot, trigger):

293

"""Get weather using configured settings."""

294

api_key = bot.settings.weather.api_key

295

units = bot.settings.weather.units

296

timeout = bot.settings.weather.timeout

297

298

# Use configuration values...

299

```

300

301

### Advanced Configuration with Validation

302

303

```python

304

from sopel import config

305

import os

306

307

class DatabaseSection(config.types.StaticSection):

308

"""Advanced database configuration with validation."""

309

310

type = config.types.ChoiceAttribute(

311

'type',

312

choices=['sqlite', 'mysql', 'postgresql'],

313

default='sqlite'

314

)

315

316

filename = config.types.FilenameAttribute(

317

'filename',

318

relative=True,

319

default='bot.db'

320

)

321

322

host = config.types.ValidatedAttribute('host', default='localhost')

323

port = config.types.ValidatedAttribute('port', int, default=5432)

324

username = config.types.ValidatedAttribute('username')

325

password = config.types.ValidatedAttribute('password')

326

name = config.types.ValidatedAttribute('name', default='sopel')

327

328

# Custom validation

329

def __init__(self, config, section_name, validate=True):

330

super().__init__(config, section_name, validate)

331

332

if validate and self.type != 'sqlite':

333

if not self.username or not self.password:

334

raise ValueError("Database username and password required for non-SQLite databases")

335

336

# Using the configuration

337

def setup(bot):

338

bot.settings.define_section('database', DatabaseSection)

339

340

# Access configuration

341

db_config = bot.settings.database

342

if db_config.type == 'sqlite':

343

db_path = os.path.join(bot.settings.core.homedir, db_config.filename)

344

print(f"Using SQLite database: {db_path}")

345

else:

346

print(f"Using {db_config.type} database on {db_config.host}:{db_config.port}")

347

```

348

349

### Environment Variable Overrides

350

351

```python

352

# Configuration values can be overridden with environment variables

353

# Format: SOPEL_<SECTION>_<OPTION>

354

355

# Override core.nick

356

# export SOPEL_CORE_NICK="MyBot"

357

358

# Override custom section values

359

# export SOPEL_WEATHER_API_KEY="your_api_key_here"

360

361

from sopel.config import Config

362

363

# Load config with environment overrides

364

config = Config('/path/to/bot.cfg')

365

366

# Values are automatically overridden from environment

367

print(config.core.nick) # Uses SOPEL_CORE_NICK if set

368

```

369

370

### Interactive Configuration

371

372

```python

373

from sopel.config import Config

374

375

def configure_weather_plugin(config):

376

"""Interactively configure weather plugin."""

377

378

# Ask yes/no questions

379

enable_weather = config.option("Enable weather plugin?", default=True)

380

if not enable_weather:

381

return

382

383

# Get user input for settings

384

api_key = input("Enter weather API key: ")

385

default_location = input("Enter default location [London]: ") or "London"

386

387

# Save configuration

388

if 'weather' not in config:

389

config.add_section('weather')

390

391

config.weather.api_key = api_key

392

config.weather.default_location = default_location

393

config.save()

394

395

print("Weather plugin configured successfully!")

396

```

397

398

## Types

399

400

### Configuration Constants

401

402

```python { .api }

403

# Default configuration directory

404

DEFAULT_HOMEDIR: str = "~/.sopel"

405

```

406

407

### Configuration Exceptions

408

409

```python { .api }

410

class ConfigurationError(Exception):

411

"""Base exception for configuration errors."""

412

413

def __init__(self, value: str):

414

"""

415

Initialize configuration error.

416

417

Args:

418

value (str): Error description

419

"""

420

421

class ConfigurationNotFound(ConfigurationError):

422

"""Exception raised when configuration file cannot be found."""

423

424

def __init__(self, filename: str):

425

"""

426

Initialize configuration not found error.

427

428

Args:

429

filename (str): Path to missing configuration file

430

"""

431

432

@property

433

def filename(self) -> str:

434

"""Path to the configuration file that could not be found."""

435

```