-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain_bot.py
executable file
·206 lines (180 loc) · 9.38 KB
/
main_bot.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#!/usr/bin/python3 -i
from config.twitch_config import username, channel
# component config file is deprecated
try:
from config.component_config import config as component_config
except ImportError:
component_config = None
from twitchircclient import TwitchIrcClient, MockIrcClient
from twitchapi import TwitchApi
from commands_helper import CommandsHelper
import db_helper
import settings_db
import importlib
import os
import glob
from os.path import basename, isfile
from collections import defaultdict
if __name__ == '__main__':
class Lepebot:
"""
Instance of the bot to share access to all components, utils for the
database and irc
"""
def __init__(self):
"""Init a new bot """
self.debug = bool(os.getenv('DEBUG'))
self.mock = bool(os.getenv('MOCK'))
# Init maybe useful variables
self.channel = channel
self.username = username
# Database connection:
self.database = db_helper
# Get all settings
settings_db.db_create_table()
self.settings = settings_db.db_select_all()
# Set up TwitchApi
self.twitch_api = TwitchApi()
self.twitch_api.refresh_oauth_token()
if self.twitch_api.twitch_id == '':
user = self.twitch_api.get_user(self.channel)
if user is None:
raise Exception('Channel {} doesn\'t exist',
self.channel)
self.twitch_api.twitch_id = user['_id']
print('Your twitch_id is {}, please insert it in the twitch_config.py in twitch_id'.format(self.twitch_api.twitch_id))
# create main instance of the ircConnection
if self.mock:
# TODO
self.irc = MockIrcClient(username, 'mock',
debug=self.debug)
else:
self.irc = TwitchIrcClient(username,
"oauth:"+self.twitch_api.oauth.token['access_token'],
debug=self.debug)
# Detect all components in the component directory
# (but not the default empty component)
files = glob.glob('components/*.py')
componentnames = [basename(f)[:-3] for f in files
if isfile(f) and not f.endswith('empty_component.py')]
# Load components
self.components = {}
defaultsettings = []
for compname in componentnames:
self.components[compname] = importlib.import_module('components.'+compname).Component(self)
defaultsettings.append(
settings_db.Setting(compname, 'active','0'))
for key, value in self.components[compname].get_default_settings().items():
defaultsettings.append(
settings_db.Setting(compname, key, value))
# Apply legacy settings file
if component_config is not None:
filesettings = []
for comp, setts in component_config['components'].items():
filesettings.append(settings_db.Setting(comp, 'active', '1' if setts['active'] else '0'))
if 'config' in setts:
for key, value in setts['config'].items():
filesettings.append(settings_db.Setting(comp, key, value))
difference = settings_db.generate_diff(self.settings, filesettings)
for actual, default in difference:
if actual is None:
self.settings.append(default)
settings_db.db_insert_setting(default)
# Apply defaults
difference = settings_db.generate_diff(self.settings, defaultsettings)
for actual, default in difference:
if actual is None:
self.settings.append(default)
settings_db.db_insert_setting(default)
# Set up up util for easy use of commandnames in privmsg and whisper
self.privmsg_commands = {}
self.whisper_commands = {}
# Set up CommandsHelper
self.commands_helper = CommandsHelper()
self.irc.messagespreader.add(self.commands_helper.privmsg_listener)
self.irc.whisperspreader.add(self.commands_helper.whisper_listener)
dictsettings = Lepebot.settingslist_to_dict(self.settings)
# Set up the Components
for compname, component in self.components.items():
if dictsettings[compname]['active'] == '1':
print('loaded '+compname)
try:
component.load(dictsettings[compname])
except Exception as e:
print('Exception while loading {}: {}'.format(compname, e))
else:
print('component {} is not active'.format(compname))
# Join the specified channel and start the connection
self.irc.create_connection()
self.irc.join(channel)
# Register database change hook
db_helper.add_db_change_listener(self._databaseupdate)
def register_privmsg_command(self, name, func, channel_cooldown=0, user_cooldown=0, mod_only=False, broadcaster_only=False, enabled=True):
"""
Add a new privmsg-command
Params:
name (str): Name of the command. Note that the name is automatically prefixed with '!',
if you want the Command to be executed when the user types in '!wr' in chat,
name ist 'wr'
func (function): Function to handle the command, params: (username, channel, message, tags)
channel_cooldown (int, optional): Number of seconds of cooldown, after the command was used in the channel
user_cooldown (int, optional): Number of seconds of cooldown, after a user can use this command again
mod_only(bool, default=False): IF only mods or the broadcaster can use this command
broadcaster_only(bool, default=False): If only broadcaster can use this command
enabled(bool, default=True): If the command can be used in chat, otherwise it's useless
"""
self.commands_helper.add_privmsg_command(name, func, channel_cooldown, user_cooldown, mod_only, broadcaster_only, enabled)
def register_whisper_command(self, name, func, user_cooldown=0):
"""
Add a new whisper-command
Params:
name (str): Name of the command. Note that the name is automatically prefixed with '!',
if you want the Command to be executed when the user types in '!wr' in chat,
name ist 'wr'
func (function): Function to handle the command, params: (username, channel, message, tags)
user_cooldown (int, optional): Number of seconds of cooldown, after a user can use this command again
"""
self.commands_helper.add_whisper_command(name, func, user_cooldown)
def unregister_privmsg_command(self, name):
self.commands_helper.remove_privmsg_command(name)
def unregister_whisper_command(self, name):
self.commands_helper.remove_whisper_command(name)
def shutdown(self):
for name, component in self.components.items():
try:
component.unload()
except Exception as e:
print('Error unloading module '+name+':\n', e)
self.irc.shutdown()
self.database.close()
@staticmethod
def settingslist_to_dict(settingslist):
dictsettings = defaultdict(dict)
for setting in settingslist:
dictsettings[setting.module][setting.key] = setting.value
return dictsettings
def _databaseupdate(self):
newsettings = settings_db.db_select_all()
settingsdiff = settings_db.generate_diff(self.settings, newsettings)
if len(settingsdiff) == 0:
return
self.settings = newsettings
changedsettings = (set_tup[1] for set_tup in settingsdiff if not set_tup[1] is None)
settingsdict = Lepebot.settingslist_to_dict(changedsettings)
for modname, settings in settingsdict.items():
if 'active' in settings:
if settings['active'] == '1':
# component is now active! Get all settings from this module and load it
compsettings = Lepebot.settingslist_to_dict(settings_db.db_select_for_module(modname))
self.components[modname].load(compsettings[modname])
else:
# unload component
self.components[modname].unload()
elif modname == 'main':
# TODO, this is for the main component and currently
# not used, only for the oauth tokens which shouldn't
# be updated externally
pass
else:
self.components[modname].on_update_settings(settings.keys(),settings)
bot = Lepebot()