forked from pajbot/pajbot
/
base.py
165 lines (133 loc) · 5.55 KB
/
base.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
154
155
156
157
158
159
160
161
162
163
164
165
import json
import logging
from pajbot.tbutil import find
log = logging.getLogger(__name__)
class ModuleSetting:
"""
A single setting for a Module.
Available types:
* text - A text input
Available constraints:
* min_str_len
* max_str_len
* number - A number input (Integer)
* min_value
* max_value
* boolean - A checkbox input
"""
def __init__(self, key, label, type, required=False,
placeholder='', default=None, constraints={}):
self.key = key
self.label = label
self.type = type
self.required = required
self.placeholder = placeholder
self.default = default
self.constraints = constraints
def validate(self, value):
""" Validate the input for this module.
This will call the relevant submethod, located as validate_{type}.
You always get a tuple back, with the first value being True or False depending
on if the input value was validated properly.
The second value is the properly parsed value.
So for example, calling validate('50') on a number setting would return (True, 50)
"""
validator = getattr(self, 'validate_{}'.format(self.type), None)
if validator:
return validator(value)
else:
log.info('No validator available for type {}'.format(type))
return True, value
def validate_text(self, value):
""" Validate a text value """
value = value.strip()
if 'min_str_len' in self.constraints and len(value) < self.constraints['min_str_len']:
return False, 'needs to be at least {} characters long'.format(self.constraints['min_str_len'])
if 'max_str_len' in self.constraints and len(value) > self.constraints['max_str_len']:
return False, 'needs to be at most {} characters long'.format(self.constraints['max_str_len'])
return True, value
def validate_number(self, value):
""" Validate a number value """
try:
value = int(value)
except ValueError:
return False, 'Not a valid integer'
if 'min_value' in self.constraints and value < self.constraints['min_value']:
return False, 'needs to have a value that is at least {}'.format(self.constraints['min_value'])
if 'max_value' in self.constraints and value > self.constraints['max_value']:
return False, 'needs to have a value that is at most {}'.format(self.constraints['max_value'])
return True, value
def validate_boolean(self, value):
""" Validate a boolean value """
return True, value == 'on'
class BaseModule:
"""
This class will include all the basics that a module needs
to be operable.
"""
ID = __name__.split('.')[-1]
NAME = 'Base Module'
DESCRIPTION = 'This is the description for the base module. ' + \
'It\'s what will be shown on the website where you can enable ' + \
'and disable modules.'
SETTINGS = []
ENABLED_DEFAULT = False
PARENT_MODULE = None
def __init__(self):
""" Initialize any dictionaries the module might or might not use. """
self.commands = {}
self.default_settings = {}
self.settings = {}
self.submodules = []
self.parent_module = None
# We store a dictionary with the default settings for convenience
for setting in self.SETTINGS:
self.default_settings[setting.key] = setting.default
def load(self, **options):
""" This method will load everything from the module into
their proper dictionaries, which we can then use later. """
self.load_settings(options.get('settings', {}))
self.load_commands(**options)
return self
def load_settings(self, settings):
self.settings = settings if settings else {}
# Load any unset settings
for setting in self.SETTINGS:
if setting.key not in self.settings:
self.settings[setting.key] = setting.default
def load_commands(self, **options):
pass
def parse_settings(self, **in_settings):
ret = {}
for key, value in in_settings.items():
setting = find(lambda setting: setting.key == key, self.SETTINGS)
if setting is None:
# We were passed a setting that's not available for this module
return False
print('{}: {}'.format(key, value))
res, new_value = setting.validate(value)
if res is False:
# Something went wrong when validating one of the settings
log.warn(new_value)
return False
ret[key] = new_value
return ret
def enable(self, bot):
pass
def disable(self, bot):
pass
def on_loaded(self):
pass
def get_phrase(self, key, **arguments):
if key not in self.settings:
log.error('{} is not in this modules settings.')
return 'KeyError in get_phrase'
try:
return self.settings[key].format(**arguments)
except (IndexError, ValueError, KeyError):
log.warning('An error occured when formatting phrase "{}". Arguments: ({})'.format(self.settings[key], arguments))
try:
return self.default_settings[key].format(**arguments)
except:
log.exception('ABORT - The default phrase {} is BAD. Arguments: ({})'.format(self.default_settings[key], arguments))
return 'FatalError in get_phrase'