This repository has been archived by the owner on Jan 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 30
/
conf.py
153 lines (126 loc) · 6.06 KB
/
conf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""
conf.py - PyLink configuration core.
This module is used to access the configuration of the current PyLink instance.
It provides simple checks for validating and loading YAML-format configurations from arbitrary files.
"""
try:
import yaml
except ImportError:
raise ImportError("PyLink requires PyYAML to function; please install it and try again.")
import logging
import os.path
import sys
from collections import defaultdict
from . import world
__all__ = ['ConfigurationError', 'conf', 'confname', 'validate', 'load_conf',
'get_database_name']
class ConfigurationError(RuntimeError):
"""Error when config conditions aren't met."""
conf = {'bot':
{
'nick': 'PyLink',
'user': 'pylink',
'realname': 'PyLink Service Client',
'serverdesc': 'Unconfigured PyLink'
},
'logging':
{
'console': 'INFO'
},
'servers':
# Wildcard defaultdict! This means that
# any network name you try will work and return
# this basic template:
defaultdict(lambda: {'ip': '0.0.0.0',
'port': 7000,
'recvpass': "unconfigured",
'sendpass': "unconfigured",
'protocol': "null",
'hostname': "pylink.unconfigured",
'sid': "000",
'maxnicklen': 20,
'sidrange': '0##'
})
}
conf['pylink'] = conf['bot']
confname = 'unconfigured'
def validate(condition, errmsg):
"""Raises ConfigurationError with errmsg unless the given condition is met."""
if not condition:
raise ConfigurationError(errmsg)
def _log(level, text, *args, logger=None, **kwargs):
if logger:
logger.log(level, text, *args, **kwargs)
else:
world._log_queue.append((level, text))
def _validate_conf(conf, logger=None):
"""Validates a parsed configuration dict."""
validate(isinstance(conf, dict),
"Invalid configuration given: should be type dict, not %s."
% type(conf).__name__)
if 'pylink' in conf and 'bot' in conf:
_log(logging.WARNING, "Since PyLink 1.2, the 'pylink:' and 'bot:' configuration sections have been condensed "
"into one. You should merge any options under these sections into one 'pylink:' block.", logger=logger)
new_block = conf['bot'].copy()
new_block.update(conf['pylink'])
conf['bot'] = conf['pylink'] = new_block
elif 'pylink' in conf:
conf['bot'] = conf['pylink']
elif 'bot' in conf:
conf['pylink'] = conf['bot']
# TODO: add a migration warning in the next release.
for section in ('pylink', 'servers', 'login', 'logging'):
validate(conf.get(section), "Missing %r section in config." % section)
# Make sure at least one form of authentication is valid.
# Also we'll warn them that login:user/login:password is deprecated
if conf['login'].get('password') or conf['login'].get('user'):
_log(logging.WARNING, "The 'login:user' and 'login:password' options are deprecated since PyLink 1.1. "
"Please switch to the new 'login:accounts' format as outlined in the example config.", logger=logger)
old_login_valid = isinstance(conf['login'].get('password'), str) and isinstance(conf['login'].get('user'), str)
newlogins = conf['login'].get('accounts', {})
validate(old_login_valid or newlogins, "No accounts were set, aborting!")
for account, block in newlogins.items():
validate(isinstance(account, str), "Bad username format %s" % account)
validate(isinstance(block.get('password'), str), "Bad password %s for account %s" % (block.get('password'), account))
validate(conf['login'].get('password') != "changeme", "You have not set the login details correctly!")
if newlogins and not old_login_valid:
validate(conf.get('permissions'), "New-style accounts enabled but no permissions block was found. You will not be able to administrate your PyLink instance!")
if conf['logging'].get('stdout'):
_log(logging.WARNING, 'The log:stdout option is deprecated since PyLink 1.2 in favour of '
'(a more correctly named) log:console. Please update your '
'configuration accordingly!', logger=logger)
return conf
def load_conf(filename, errors_fatal=True, logger=None):
"""Loads a PyLink configuration file from the filename given."""
global confname, conf, fname
# Note: store globally the last loaded conf filename, for REHASH in coremods/control.
fname = filename
# For the internal config name, strip off any .yml extensions and absolute paths
confname = os.path.splitext(os.path.basename(filename))[0]
try:
with open(filename, 'r') as f:
conf = yaml.safe_load(f)
conf = _validate_conf(conf, logger=logger)
except Exception as e:
e = 'Failed to load config from %r: %s: %s' % (filename, type(e).__name__, e)
if logger: # Prefer using the Python logger when available
logger.exception(e)
else: # Otherwise, fall back to a print() call.
print('ERROR: %s' % e, file=sys.stderr)
if errors_fatal:
sys.exit(1)
raise
else:
return conf
def get_database_name(dbname):
"""
Returns a database filename with the given base DB name appropriate for the
current PyLink instance.
This returns '<dbname>.db' if the running config name is PyLink's default
(pylink.yml), and '<dbname>-<config name>.db' for anything else. For example,
if this is called from an instance running as 'pylink testing.yml', it
would return '<dbname>-testing.db'."""
if confname != 'pylink':
dbname += '-%s' % confname
dbname += '.db'
return dbname