diff --git a/Installation Files/requirements-os.sh b/Installation Files/requirements-os.sh index 31f9763..0c980a2 100644 --- a/Installation Files/requirements-os.sh +++ b/Installation Files/requirements-os.sh @@ -20,7 +20,8 @@ rm dev-requirements.txt wget https://raw.githubusercontent.com/SpiceBot/SpiceBot/development/Installation%20Files/requirements.txt pip3 install -r requirements.txt rm requirements.txt -pip3 install sopel +# pip3 install sopel +pip3 install --upgrade --force-reinstall git+https://github.com/sopel-irc/sopel@master mkdir /usr/local/lib/python3.6/dist-packages/sopel/modules/stock mv /usr/local/lib/python3.6/dist-packages/sopel/modules/* /usr/local/lib/python3.6/dist-packages/sopel/modules/stock/ @@ -39,6 +40,9 @@ mkdir /run/sopel echo $$ > /run/sopel/sopel-SpiceBotSERV.pid chmod -R 777 /run/sopel/ +apt install mysql-server libmysqlclient-dev -y +pip3 install mysql + cp /home/sopel/SpiceBot/System-Files/Config/SpiceBot.cfg /home/sopel/SpiceBot.cfg cp /home/sopel/SpiceBot/System-Files/systemd/SpiceBot.service /lib/systemd/system/SpiceBot.service diff --git a/sopel_modules/SpiceBot_AI/AI.py b/sopel_modules/SpiceBot_AI/AI.py index 97adc03..b28ed08 100644 --- a/sopel_modules/SpiceBot_AI/AI.py +++ b/sopel_modules/SpiceBot_AI/AI.py @@ -7,20 +7,24 @@ import sopel.module import spicemanip from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, googlesearch, bot_privs, inlist, inlist_match, similar_list -from sopel_modules.SpiceBot_Events.System import bot_events_check +from sopel_modules.SpiceBot_Events.System import botevents +@botevents.check_ready([botevents.BOT_LOADED, botevents.BOT_COMMANDSQUERY]) @sopel.module.nickname_commands('(.*)') def bot_command_nick(bot, trigger): - while not bot_events_check(bot, ['1004', '2002']): - pass + # while not botevents.check([botevents.BOT_LOADED, botevents.BOT_COMMANDSQUERY]): + # pass triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command') if not triggercommand: return + if triggercommand[0] == "?": + return + if triggercommand in bot.memory['SpiceBot_CommandsQuery']['commands']["nickname"].keys(): return triggerargs.insert(0, triggercommand) diff --git a/sopel_modules/SpiceBot_Channels/Channels.py b/sopel_modules/SpiceBot_Channels/Channels.py index 1aec6aa..8e53d6c 100644 --- a/sopel_modules/SpiceBot_Channels/Channels.py +++ b/sopel_modules/SpiceBot_Channels/Channels.py @@ -6,8 +6,7 @@ from __future__ import unicode_literals, absolute_import, print_function, divisi import sopel.module from sopel.config.types import StaticSection, ValidatedAttribute, ListAttribute -from sopel_modules.SpiceBot_Events.System import bot_events_startup_register, bot_events_recieved, bot_events_trigger - +from sopel_modules.SpiceBot_Events.System import botevents from sopel_modules.SpiceBot_SBTools import ( join_all_channels, chanadmin_all_channels, channel_list_current, bot_logging @@ -35,7 +34,7 @@ def configure(config): def setup(bot): bot_logging(bot, 'SpiceBot_Channels', "Starting setup procedure") - bot_events_startup_register(bot, ['2001']) + botevents.startup_add([botevents.BOT_CHANNELS]) bot.config.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False) @@ -48,10 +47,10 @@ def shutdown(bot): del bot.memory["SpiceBot_Channels"] -@sopel.module.event('1003') +@sopel.module.event(botevents.BOT_CONNECTED) @sopel.module.rule('.*') def trigger_channel_list_initial(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) # Unkickable bot.write(('SAMODE', bot.nick, '+q')) @@ -75,7 +74,7 @@ def trigger_channel_list_initial(bot, trigger): channel_list_current(bot) foundchannelcount = len(bot.memory['SpiceBot_Channels']['channels'].keys()) bot_logging(bot, 'SpiceBot_Channels', "Channel listing finished! " + str(foundchannelcount) + " channel(s) found.") - bot_events_trigger(bot, 2001, "SpiceBot_Channels") + botevents.trigger(bot, botevents.BOT_CHANNELS, "SpiceBot_Channels") join_all_channels(bot) chanadmin_all_channels(bot) @@ -124,7 +123,7 @@ def bot_part_empty(bot): del bot.memory['SpiceBot_Channels']['channels'][channel.lower()] -@sopel.module.event('2001') +@sopel.module.event(botevents.BOT_CHANNELS) @sopel.module.rule('.*') def bot_events_setup(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) diff --git a/sopel_modules/SpiceBot_Channels/Channels_Command.py b/sopel_modules/SpiceBot_Channels/Channels_Command.py index 65858f9..92ae05c 100644 --- a/sopel_modules/SpiceBot_Channels/Channels_Command.py +++ b/sopel_modules/SpiceBot_Channels/Channels_Command.py @@ -6,7 +6,7 @@ import sopel.module import spicemanip -from sopel_modules.SpiceBot_Events.System import bot_events_check +from sopel_modules.SpiceBot_Events.System import botevents from sopel_modules.SpiceBot_SBTools import ( sopel_triggerargs, inlist, channel_privs, join_all_channels, channel_list_current, @@ -14,11 +14,12 @@ from sopel_modules.SpiceBot_SBTools import ( from .Channels import bot_part_empty +@botevents.check_ready([botevents.BOT_LOADED]) @sopel.module.nickname_commands('channels', 'channel') def nickname_comand_channels(bot, trigger): - while not bot_events_check(bot, '1004'): - pass + # while not botevents.check(botevents.BOT_LOADED): + # pass triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command') diff --git a/sopel_modules/SpiceBot_CommandsQuery/CommandsQuery.py b/sopel_modules/SpiceBot_CommandsQuery/CommandsQuery.py index 548d746..dac3030 100644 --- a/sopel_modules/SpiceBot_CommandsQuery/CommandsQuery.py +++ b/sopel_modules/SpiceBot_CommandsQuery/CommandsQuery.py @@ -7,7 +7,7 @@ import sopel.module import os -from sopel_modules.SpiceBot_Events.System import bot_events_startup_register, bot_events_recieved, bot_events_trigger +from sopel_modules.SpiceBot_Events.System import botevents from sopel_modules.SpiceBot_SBTools import bot_logging import spicemanip @@ -15,7 +15,7 @@ import spicemanip def setup(bot): bot_logging(bot, 'SpiceBot_CommandsQuery', "Starting setup procedure") - bot_events_startup_register(bot, ['2002']) + botevents.startup_add([botevents.BOT_COMMANDSQUERY]) if 'SpiceBot_CommandsQuery' not in bot.memory: bot.memory['SpiceBot_CommandsQuery'] = {"counts": 0, "commands": {}, "nickrules": []} @@ -97,9 +97,9 @@ def setup(bot): detected_lines.append(line) if len(detected_lines): - bot.memory['SpiceBot_CommandsQuery']['counts'] += 1 filelinelist = [] + currentsuccesslines = 0 for detected_line in detected_lines: # Commands @@ -117,31 +117,41 @@ def setup(bot): validcoms = list(validcoms) else: validcoms = [validcoms] + for regexcom in ["(.*)", '^\?(.*)']: + if regexcom in validcoms: + while regexcom in validcoms: + validcoms.remove(regexcom) - validcomdict = {"comtype": comtype, "validcoms": validcoms} - filelinelist.append(validcomdict) + if len(validcoms): + validcomdict = {"comtype": comtype, "validcoms": validcoms} + filelinelist.append(validcomdict) + currentsuccesslines += 1 - for atlinefound in filelinelist: + if currentsuccesslines: + bot.memory['SpiceBot_CommandsQuery']['counts'] += 1 - dict_from_file = dict() + if len(filelinelist): + for atlinefound in filelinelist: - comtype = atlinefound["comtype"] - validcoms = atlinefound["validcoms"] + dict_from_file = dict() - # default command to filename - if "validcoms" not in dict_from_file.keys(): - dict_from_file["validcoms"] = validcoms + comtype = atlinefound["comtype"] + validcoms = atlinefound["validcoms"] - maincom = dict_from_file["validcoms"][0] - if len(dict_from_file["validcoms"]) > 1: - comaliases = spicemanip.main(dict_from_file["validcoms"], '2+', 'list') - else: - comaliases = [] + # default command to filename + if "validcoms" not in dict_from_file.keys(): + dict_from_file["validcoms"] = validcoms - bot.memory['SpiceBot_CommandsQuery']['commands'][comtype][maincom] = dict_from_file - for comalias in comaliases: - if comalias not in bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys(): - bot.memory['SpiceBot_CommandsQuery']['commands'][comtype][comalias] = {"aliasfor": maincom} + maincom = dict_from_file["validcoms"][0] + if len(dict_from_file["validcoms"]) > 1: + comaliases = spicemanip.main(dict_from_file["validcoms"], '2+', 'list') + else: + comaliases = [] + + bot.memory['SpiceBot_CommandsQuery']['commands'][comtype][maincom] = dict_from_file + for comalias in comaliases: + if comalias not in bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys(): + bot.memory['SpiceBot_CommandsQuery']['commands'][comtype][comalias] = {"aliasfor": maincom} for comtype in ['module', 'nickname', 'rule']: bot_logging(bot, 'SpiceBot_CommandsQuery', "Found " + str(len(bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys())) + " " + comtype + " commands.", True) @@ -152,7 +162,7 @@ def setup(bot): if command not in bot.memory['SpiceBot_CommandsQuery']['nickrules']: bot.memory['SpiceBot_CommandsQuery']['nickrules'].append(command) - bot_events_trigger(bot, 2002, "SpiceBot_CommandsQuery") + botevents.trigger(bot, botevents.BOT_COMMANDSQUERY, "SpiceBot_CommandsQuery") def shutdown(bot): @@ -160,10 +170,10 @@ def shutdown(bot): del bot.memory["SpiceBot_CommandsQuery"] -@sopel.module.event('1004') +@sopel.module.event(botevents.BOT_LOADED) @sopel.module.rule('.*') def bot_events_complete(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) for comtype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys(): if comtype not in ['module', 'nickname', 'rule']: @@ -204,7 +214,7 @@ def commandsquery_register(bot, command_type, validcoms, aliasfor=None): bot.memory['SpiceBot_CommandsQuery']['commands'][command_type][comalias] = {"aliasfor": aliasfor} -@sopel.module.event('2002') +@sopel.module.event(botevents.BOT_COMMANDSQUERY) @sopel.module.rule('.*') def bot_events_setup(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) diff --git a/sopel_modules/SpiceBot_CommandsQuery/NickQueryCommand.py b/sopel_modules/SpiceBot_CommandsQuery/NickQueryCommand.py new file mode 100644 index 0000000..2e81930 --- /dev/null +++ b/sopel_modules/SpiceBot_CommandsQuery/NickQueryCommand.py @@ -0,0 +1,82 @@ +# coding=utf-8 + +from __future__ import unicode_literals, absolute_import, division, print_function + +import sopel +import sopel.module + +from sopel_modules.SpiceBot_Events.System import botevents +from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, similar_list, letters_in_string + + +import spicemanip + + +@botevents.check_ready([botevents.BOT_COMMANDSQUERY]) +@sopel.module.nickname_commands('(.*)') +def query_detection_nick(bot, trigger): + + # while not botevents.check(botevents.BOT_COMMANDSQUERY): + # pass + + triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command') + + # command issued, check if valid + if not triggercommand or not len(triggercommand): + return + + if not triggercommand[0] == "?": + return + triggercommand = triggercommand[1:] + + if not letters_in_string(triggercommand): + return + + commands_list = dict() + for com in bot.memory['SpiceBot_CommandsQuery']['commands']['nickname'].keys(): + if com not in commands_list.keys(): + commands_list[com] = bot.memory['SpiceBot_CommandsQuery']['commands']['nickname'][com] + + if triggercommand.endswith("+"): + + triggercommand = triggercommand[:-1] + if not triggercommand or not len(triggercommand): + return + + if triggercommand.lower() not in list(commands_list.keys()): + dispmsg = ["Cannot find any alias " + bot.nick + " commands: No valid commands match " + str(triggercommand) + "."] + closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse') + if len(closestmatches): + dispmsg.append("The following " + bot.nick + " commands match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".") + bot.notice(dispmsg, trigger.nick) + return + + realcom = triggercommand + if "aliasfor" in commands_list[triggercommand].keys(): + realcom = commands_list[triggercommand]["aliasfor"] + validcomlist = commands_list[realcom]["validcoms"] + bot.notice("The following " + bot.nick + " commands match " + str(triggercommand) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick) + return + + if triggercommand.endswith("?"): + + triggercommand = triggercommand[:-1] + if not triggercommand or not len(triggercommand): + return + + closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse') + if not len(closestmatches): + bot.notice("Cannot find any similar " + bot.nick + " commands for " + str(triggercommand) + ".", trigger.nick) + else: + bot.notice("The following " + bot.nick + " commands may match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick) + return + + commandlist = [] + for command in list(commands_list.keys()): + if command.lower().startswith(str(triggercommand).lower()): + commandlist.append(command) + + if not len(commandlist): + bot.notice("No " + bot.nick + " commands start with " + str(triggercommand) + ".", trigger.nick) + else: + bot.notice("The following " + bot.nick + " commands start with " + str(triggercommand) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick) diff --git a/sopel_modules/SpiceBot_CommandsQuery/QueryCommand.py b/sopel_modules/SpiceBot_CommandsQuery/QueryCommand.py index bf1f735..a4aec45 100644 --- a/sopel_modules/SpiceBot_CommandsQuery/QueryCommand.py +++ b/sopel_modules/SpiceBot_CommandsQuery/QueryCommand.py @@ -5,90 +5,76 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel import sopel.module -from difflib import SequenceMatcher -from operator import itemgetter +from sopel_modules.SpiceBot_Events.System import botevents +from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, similar_list, letters_in_string -from sopel_modules.SpiceBot_Events.System import bot_events_check import spicemanip +@botevents.check_ready([botevents.BOT_COMMANDSQUERY]) @sopel.module.rule('^\?(.*)') def query_detection(bot, trigger): - while not bot_events_check(bot, '2002'): - pass + # while not botevents.check(botevents.BOT_COMMANDSQUERY): + # pass + + triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'query_command') + + # command issued, check if valid + if not triggercommand or not len(triggercommand): + return + + if not letters_in_string(triggercommand): + return commands_list = dict() for commandstype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys(): - if commandstype != 'rule': + if commandstype not in ['rule', 'nickname']: for com in bot.memory['SpiceBot_CommandsQuery']['commands'][commandstype].keys(): if com not in commands_list.keys(): - if commandstype == 'nickname': - commands_list[str(bot.nick) + " " + com] = bot.memory['SpiceBot_CommandsQuery']['commands'][commandstype][com] - else: - commands_list[com] = bot.memory['SpiceBot_CommandsQuery']['commands'][commandstype][com] + commands_list[com] = bot.memory['SpiceBot_CommandsQuery']['commands'][commandstype][com] - triggerargsarray = spicemanip.main(trigger, 'create') + if triggercommand[:-1] == "+": - # command issued, check if valid - querycommand = spicemanip.main(triggerargsarray, 1).lower()[1:] - if len(querycommand) == 1: - commandlist = [] - for command in commands_list.keys(): - if command.lower().startswith(querycommand): - commandlist.append(command) - if commandlist == []: - bot.notice("No commands match " + str(querycommand) + ".", trigger.nick) - return - else: - bot.notice("The following commands match " + str(querycommand) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick) + triggercommand = triggercommand[:-1] + if not triggercommand or not len(triggercommand): return - elif querycommand.endswith(tuple(["+"])): - querycommand = querycommand[:-1] - if querycommand not in commands_list.keys(): - bot.notice("The " + str(querycommand) + " does not appear to be valid.") + if triggercommand.lower() not in list(commands_list.keys()): + dispmsg = ["Cannot find any alias commands: No valid commands match " + str(triggercommand) + "."] + closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse') + if len(closestmatches): + dispmsg.append("The following commands match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".") + bot.notice(dispmsg, trigger.nick) return - realcom = querycommand - if "aliasfor" in commands_list[querycommand].keys(): - realcom = commands_list[querycommand]["aliasfor"] + + realcom = triggercommand + if "aliasfor" in commands_list[triggercommand].keys(): + realcom = commands_list[triggercommand]["aliasfor"] validcomlist = commands_list[realcom]["validcoms"] - bot.notice("The following commands match " + str(querycommand) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick) + bot.notice("The following commands match " + str(triggercommand) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick) return - elif querycommand.endswith(tuple(['?'])): - querycommand = querycommand[:-1] - sim_com, sim_num = [], [] - for com in commands_list.keys(): - similarlevel = SequenceMatcher(None, querycommand.lower(), com.lower()).ratio() - sim_com.append(com) - sim_num.append(similarlevel) - sim_num, sim_com = (list(x) for x in zip(*sorted(zip(sim_num, sim_com), key=itemgetter(0)))) - closestmatch = spicemanip.main(sim_com, 'reverse', "list") - listnumb, relist = 1, [] - for item in closestmatch: - if listnumb <= 10: - relist.append(str(item)) - listnumb += 1 - bot.notice("The following commands may match " + str(querycommand) + ": " + spicemanip.main(relist, 'andlist') + ".", trigger.nick) - return + if triggercommand[:-1] == "?": - elif querycommand in commands_list.keys(): - bot.notice("The following commands match " + str(querycommand) + ": " + str(querycommand) + ".", trigger.nick) - return - - elif not querycommand: - return - - else: - commandlist = [] - for command in commands_list.keys(): - if command.lower().startswith(querycommand): - commandlist.append(command) - if commandlist == []: - bot.notice("No commands match " + str(querycommand) + ".", trigger.nick) + triggercommand = triggercommand[:-1] + if not triggercommand or not len(triggercommand): return + + closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse') + if not len(closestmatches): + bot.notice("Cannot find any similar commands for " + str(triggercommand) + ".", trigger.nick) else: - bot.notice("The following commands match " + str(querycommand) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick) - return + bot.notice("The following commands may match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick) + return + + commandlist = [] + for command in list(commands_list.keys()): + if command.lower().startswith(str(triggercommand).lower()): + commandlist.append(command) + + if not len(commandlist): + bot.notice("No commands start with " + str(triggercommand) + ".", trigger.nick) + else: + bot.notice("The following commands start with " + str(triggercommand) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick) diff --git a/sopel_modules/SpiceBot_CommandsQuery/__init__.py b/sopel_modules/SpiceBot_CommandsQuery/__init__.py index 55dadea..fcad5b8 100644 --- a/sopel_modules/SpiceBot_CommandsQuery/__init__.py +++ b/sopel_modules/SpiceBot_CommandsQuery/__init__.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals, absolute_import, division, print_functi from .CommandsQuery import * from .QueryCommand import * +from .NickQueryCommand import * __author__ = 'Sam Zick' __email__ = 'sam@deathbybandaid.net' diff --git a/sopel_modules/SpiceBot_Database_Addons/Databaseaddons.py b/sopel_modules/SpiceBot_Database_Addons/Databaseaddons.py index 7c07129..bc74932 100644 --- a/sopel_modules/SpiceBot_Database_Addons/Databaseaddons.py +++ b/sopel_modules/SpiceBot_Database_Addons/Databaseaddons.py @@ -6,72 +6,114 @@ from __future__ import unicode_literals, absolute_import, print_function, divisi import sopel.module from sopel.tools import Identifier from sopel_modules.SpiceBot_SBTools import bot_logging -from sopel.db import _deserialize, SopelDB +from sopel.db import SopelDB, NickValues, ChannelValues +from sqlalchemy.exc import SQLAlchemyError import json def setup(bot): + pass # Inject Database Functions bot_logging(bot, 'SpiceBot_Databaseaddons', "Implanting Database functions into bot") - SopelDB.reset_nick_value = SopelDBCache.reset_nick_value + SopelDB.delete_nick_value = SopelDBCache.delete_nick_value SopelDB.adjust_nick_value = SopelDBCache.adjust_nick_value - SopelDB.reset_channel_value = SopelDBCache.reset_channel_value + SopelDB.delete_channel_value = SopelDBCache.delete_channel_value SopelDB.adjust_channel_value = SopelDBCache.adjust_channel_value - SopelDB._create_table = SopelDBCache._create_table class SopelDBCache: - """Dynamic Table Creation""" - - def _create_table(self, tablename): - self.execute('CREATE TABLE IF NOT EXISTS ? (mainid STRING, key STRING, value STRING, PRIMARY KEY (mainid, key))', tablename) - """Nicks""" - def reset_nick_value(self, nick, key): - """Resets the value for a given key to be associated with the nick.""" + def delete_nick_value(self, nick, key): + """Deletes the value for a given key to be associated with the nick.""" nick = Identifier(nick) nick_id = self.get_nick_id(nick) - self.execute('DELETE FROM nick_values WHERE nick_id = ? AND key = ?', [nick_id, key]) + session = self.ssession() + try: + result = session.query(NickValues) \ + .filter(NickValues.nick_id == nick_id) \ + .filter(NickValues.key == key) \ + .one_or_none() + # NickValue exists, delete + if result: + session.delete(result) + session.commit() + except SQLAlchemyError: + session.rollback() + raise + finally: + session.close() def adjust_nick_value(self, nick, key, value): - """Adjusts the value for a given key to be associated with the nick.""" + """Sets the value for a given key to be associated with the nick.""" nick = Identifier(nick) - result = self.execute( - 'SELECT value FROM nicknames JOIN nick_values ' - 'ON nicknames.nick_id = nick_values.nick_id ' - 'WHERE slug = ? AND key = ?', - [nick.lower(), key] - ).fetchone() - if result is not None: - result = result[0] - current_value = _deserialize(result) - value = current_value + value value = json.dumps(value, ensure_ascii=False) nick_id = self.get_nick_id(nick) - self.execute('INSERT OR REPLACE INTO nick_values VALUES (?, ?, ?)', - [nick_id, key, value]) + session = self.ssession() + try: + result = session.query(NickValues) \ + .filter(NickValues.nick_id == nick_id) \ + .filter(NickValues.key == key) \ + .one_or_none() + # NickValue exists, update + if result: + result.value = result.value + value + session.commit() + # DNE - Insert + else: + new_nickvalue = NickValues(nick_id=nick_id, key=key, value=value) + session.add(new_nickvalue) + session.commit() + except SQLAlchemyError: + session.rollback() + raise + finally: + session.close() """Channels""" - def reset_channel_value(self, channel, key): - """Resets the value for a given key to be associated with a channel.""" + def delete_channel_value(self, channel, key): + """Sets the value for a given key to be associated with the channel.""" channel = Identifier(channel).lower() - self.execute('DELETE FROM channel_values WHERE channel = ? AND key = ?', [channel, key]) + session = self.ssession() + try: + result = session.query(ChannelValues) \ + .filter(ChannelValues.channel == channel)\ + .filter(ChannelValues.key == key) \ + .one_or_none() + # ChannelValue exists, update + if result: + session.delete(result) + session.commit() + except SQLAlchemyError: + session.rollback() + raise + finally: + session.close() def adjust_channel_value(self, channel, key, value): - """Adjusts the value for a given key to be associated with the channel.""" + """Sets the value for a given key to be associated with the channel.""" channel = Identifier(channel).lower() - result = self.execute( - 'SELECT value FROM channel_values WHERE channel = ? AND key = ?', - [channel, key] - ).fetchone() - if result is not None: - result = result[0] - current_value = _deserialize(result) - value = current_value + value value = json.dumps(value, ensure_ascii=False) - self.execute('INSERT OR REPLACE INTO channel_values VALUES (?, ?, ?)', - [channel, key, value]) + session = self.ssession() + try: + result = session.query(ChannelValues) \ + .filter(ChannelValues.channel == channel)\ + .filter(ChannelValues.key == key) \ + .one_or_none() + # ChannelValue exists, update + if result: + result.value = result.value + value + session.commit() + # DNE - Insert + else: + new_channelvalue = ChannelValues(channel=channel, key=key, value=value) + session.add(new_channelvalue) + session.commit() + except SQLAlchemyError: + session.rollback() + raise + finally: + session.close() diff --git a/sopel_modules/SpiceBot_Database_Tools/DatabaseTools.py b/sopel_modules/SpiceBot_Database_Tools/DatabaseTools.py index 816c56f..e074941 100644 --- a/sopel_modules/SpiceBot_Database_Tools/DatabaseTools.py +++ b/sopel_modules/SpiceBot_Database_Tools/DatabaseTools.py @@ -6,13 +6,6 @@ from __future__ import unicode_literals, absolute_import, print_function, divisi import sopel.module -"""Dyanmic Table Creation""" - - -def db_create_table(bot, tablename): - bot.db._create_table(tablename) - - """Database Direct""" @@ -58,7 +51,7 @@ def db_adjust_list(bot, nick, entries, key, adjustmentdirection): for x in adjustlistnew: if x not in adjustlist: adjustlist.append(x) - if adjustlist == []: + if not len(adjustlist): db_reset_value(bot, nick, key) else: db_set_nick_value(bot, nick, key, adjustlist) diff --git a/sopel_modules/SpiceBot_Events/Events.py b/sopel_modules/SpiceBot_Events/Events.py index 9613516..5933e42 100644 --- a/sopel_modules/SpiceBot_Events/Events.py +++ b/sopel_modules/SpiceBot_Events/Events.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.module from sopel.trigger import PreTrigger -from .System import bot_events_trigger, bot_events_recieved, bot_events_startup_check, bot_events_setup_check +from .System import botevents from sopel_modules.SpiceBot_SBTools import bot_logging import time @@ -14,59 +14,53 @@ import time @sopel.module.event('001') @sopel.module.rule('.*') def bot_startup_connection(bot, trigger): - bot_events_trigger(bot, 1001, "Welcome to the SpiceBot Events System") + botevents.trigger(bot, botevents.BOT_WELCOME, "Welcome to the SpiceBot Events System") while not len(bot.channels.keys()) > 0: pass time.sleep(1) - bot_events_trigger(bot, 1003, "Bot Connected to IRC") + botevents.trigger(bot, botevents.BOT_CONNECTED, "Bot Connected to IRC") -@sopel.module.event('1001') +@sopel.module.event(botevents.BOT_WELCOME) @sopel.module.rule('.*') def bot_events_start(bot, trigger): bot_logging(bot, 'SpiceBot_Events', trigger.args[1], True) - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) - bot_events_trigger(bot, 1002, "Ready To Process module setup procedures") + botevents.trigger(bot, botevents.BOT_READY, "Ready To Process module setup procedures") - while not bot_events_startup_check(bot): + while not botevents.startup_check(): pass - bot_events_trigger(bot, 1004, "All registered modules setup procedures have completed") + botevents.trigger(bot, botevents.BOT_LOADED, "All registered modules setup procedures have completed") -@sopel.module.event('1002') +@sopel.module.event(botevents.BOT_READY) @sopel.module.rule('.*') def bot_events_ready(bot, trigger): bot_logging(bot, 'SpiceBot_Events', trigger.args[1], True) - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) -@sopel.module.event('1003') +@sopel.module.event(botevents.BOT_CONNECTED) @sopel.module.rule('.*') def bot_events_connected(bot, trigger): bot_logging(bot, 'SpiceBot_Events', trigger.args[1], True) - bot_events_recieved(bot, trigger.event) - - bot_events_setup_check(bot) - + botevents.recieved(trigger) while True: - try: - if len(bot.memory['SpiceBot_Events']["queue"]): - number = bot.memory['SpiceBot_Events']["queue"][0]["number"] - message = bot.memory['SpiceBot_Events']["queue"][0]["message"] - pretrigger = PreTrigger( - bot.nick, - ":SpiceBot_Events " + str(number) + " " + str(bot.nick) + " :" + message - ) - bot.dispatch(pretrigger) - del bot.memory['SpiceBot_Events']["queue"][0] - except KeyError: - return + if len(botevents.SpiceBot_Events["trigger_queue"]): + number = botevents.SpiceBot_Events["trigger_queue"][0]["number"] + message = botevents.SpiceBot_Events["trigger_queue"][0]["message"] + pretrigger = PreTrigger( + bot.nick, + ":SpiceBot_Events " + str(number) + " " + str(bot.nick) + " :" + message + ) + bot.dispatch(pretrigger) + del botevents.SpiceBot_Events["trigger_queue"][0] -@sopel.module.event('1004') +@sopel.module.event(botevents.BOT_LOADED) @sopel.module.rule('.*') def bot_events_complete(bot, trigger): bot_logging(bot, 'SpiceBot_Events', trigger.args[1], True) - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) diff --git a/sopel_modules/SpiceBot_Events/System.py b/sopel_modules/SpiceBot_Events/System.py index b4e0dce..98b73ac 100644 --- a/sopel_modules/SpiceBot_Events/System.py +++ b/sopel_modules/SpiceBot_Events/System.py @@ -5,86 +5,93 @@ from __future__ import unicode_literals, absolute_import, print_function, divisi # sopel imports import sopel from sopel.trigger import PreTrigger +import functools from sopel_modules.SpiceBot_SBTools import bot_logging +class BotEvents(object): + """A dynamic listing of all the notable Bot numeric events. + + Events will be assigned a 4-digit number above 1000. + + This allows you to do, ``@module.event(botevents.BOT_WELCOME)```` + + Triggers handled by this module will be processed immediately. + Others will be placed into a queue. + + Triggers will be logged by ID and content + """ + + def __init__(self): + self.SpiceBot_Events = { + "assigned_IDs": [1000], + "triggers_recieved": {}, + "trigger_queue": [], + "startup_required": [] + } + + def __getattr__(self, name): + ''' will only get called for undefined attributes ''' + eventnumber = max(self.SpiceBot_Events["assigned_IDs"]) + 1 + self.SpiceBot_Events["assigned_IDs"].append(eventnumber) + setattr(self, name, str(eventnumber)) + return str(eventnumber) + + def trigger(self, bot, number, message="SpiceBot_Events"): + number = str(number) + if number in [self.BOT_WELCOME, self.BOT_READY, self.BOT_CONNECTED, self.BOT_LOADED]: + pretrigger = PreTrigger( + bot.nick, + ":SpiceBot_Events " + number + " " + str(bot.nick) + " :" + message + ) + bot.dispatch(pretrigger) + else: + pretriggerdict = {"number": number, "message": message} + self.SpiceBot_Events["trigger_queue"].append(pretriggerdict) + + def recieved(self, trigger): + eventnumber = str(trigger.event) + message_payload = trigger.args[1] + if eventnumber not in self.SpiceBot_Events["triggers_recieved"]: + self.SpiceBot_Events["triggers_recieved"][eventnumber] = [] + self.SpiceBot_Events["triggers_recieved"][eventnumber].append(message_payload) + + def check(self, checklist): + if not isinstance(checklist, list): + checklist = [str(checklist)] + for number in checklist: + if str(number) not in self.SpiceBot_Events["triggers_recieved"].keys(): + return False + return True + + def startup_add(self, startlist): + if not isinstance(startlist, list): + startlist = [str(startlist)] + self.SpiceBot_Events["startup_required"].extend(startlist) + + def startup_check(self): + for number in self.SpiceBot_Events["startup_required"]: + if str(number) not in self.SpiceBot_Events["triggers_recieved"].keys(): + return False + return True + + def check_ready(self, checklist): + def actual_decorator(function): + @functools.wraps(function) + def _nop(*args, **kwargs): + # Assign trigger and bot for easy access later + bot, trigger = args[0:2] + while not self.check(checklist): + pass + return function(*args, **kwargs) + return _nop + return actual_decorator + + +botevents = BotEvents() + + def setup(bot): bot_logging(bot, 'SpiceBot_Events', "Starting setup procedure") - bot_events_setup_check(bot) - bot_events_startup_register(bot, ['1001', '1002', '1003']) - - -def shutdown(bot): - if "SpiceBot_Events" in bot.memory: - del bot.memory["SpiceBot_Events"] - - -def bot_events_trigger(bot, number, message): - - bot_events_setup_check(bot) - - bot.memory["SpiceBot_Events"]["triggers"][str(number)] = message - if str(number) in ['1001', '1002', '1003']: - pretrigger = PreTrigger( - bot.nick, - ":SpiceBot_Events " + str(number) + " " + str(bot.nick) + " :" + message - ) - bot.dispatch(pretrigger) - else: - pretriggerdict = {"number": number, "message": message} - bot.memory['SpiceBot_Events']["queue"].append(pretriggerdict) - - -def bot_events_setup_check(bot): - if "SpiceBot_Events" not in bot.memory: - bot.memory["SpiceBot_Events"] = {"triggers": {}, "startup": [], "loaded": [], "queue": []} - - -def bot_events_check(bot, listreq): - - bot_events_setup_check(bot) - - if not isinstance(listreq, list): - listreq = [str(listreq)] - - for requirement in listreq: - if requirement not in bot.memory["SpiceBot_Events"]["loaded"]: - return False - - return True - - -def bot_events_startup_register(bot, addonreq): - - bot_events_setup_check(bot) - - if not isinstance(addonreq, list): - addonreq = [str(addonreq)] - - bot.memory["SpiceBot_Events"]["startup"].extend(addonreq) - - -def bot_events_recieved(bot, number): - - bot_events_setup_check(bot) - - if str(number) not in bot.memory["SpiceBot_Events"]["loaded"]: - bot.memory["SpiceBot_Events"]["loaded"].append(str(number)) - - -def bot_events_startup_check(bot): - - bot_events_setup_check(bot) - - notcomplete = [] - - for startupitem in bot.memory["SpiceBot_Events"]["startup"]: - if str(startupitem) not in bot.memory["SpiceBot_Events"]["loaded"]: - notcomplete.append(str(startupitem)) - - if len(notcomplete): - # bot_logging(bot, 'SpiceBot_Events', str(notcomplete)) - return False - else: - return True + botevents.startup_add([botevents.BOT_WELCOME, botevents.BOT_READY, botevents.BOT_CONNECTED]) diff --git a/sopel_modules/SpiceBot_GifSearch/gif.py b/sopel_modules/SpiceBot_GifSearch/gif.py index 9a93975..a33f6f6 100644 --- a/sopel_modules/SpiceBot_GifSearch/gif.py +++ b/sopel_modules/SpiceBot_GifSearch/gif.py @@ -7,19 +7,20 @@ from sopel import module from .gifsearch import getGif from sopel_modules.SpiceBot_SBTools import sopel_triggerargs -from sopel_modules.SpiceBot_Events.System import bot_events_check +from sopel_modules.SpiceBot_Events.System import botevents import spicemanip +@botevents.check_ready([botevents.BOT_GIFSEARCH]) @module.commands('gif') def gif_trigger(bot, trigger): - while not bot_events_check(bot, '2003'): - pass + # while not botevents.check(botevents.BOT_GIFSEARCH): + # pass triggerargs, triggercommand = sopel_triggerargs(bot, trigger) - if triggerargs == []: + if not len(triggerargs): return bot.osd("Please present a query to search.") query = spicemanip.main(triggerargs, 0) diff --git a/sopel_modules/SpiceBot_GifSearch/gifapi.py b/sopel_modules/SpiceBot_GifSearch/gifapi.py index 6748cae..c47d7b8 100644 --- a/sopel_modules/SpiceBot_GifSearch/gifapi.py +++ b/sopel_modules/SpiceBot_GifSearch/gifapi.py @@ -9,21 +9,22 @@ import sopel.module import spicemanip from sopel_modules.SpiceBot_SBTools import sopel_triggerargs -from sopel_modules.SpiceBot_Events.System import bot_events_check +from sopel_modules.SpiceBot_Events.System import botevents +@botevents.check_ready([botevents.BOT_GIFSEARCH]) @sopel.module.commands('(.*)') def gifapi_triggers(bot, trigger): - while not bot_events_check(bot, '2003'): - pass + # while not botevents.check(botevents.BOT_GIFSEARCH): + # pass triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'prefix_command') if triggercommand not in bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'].keys(): return - if triggerargs == []: + if not len(triggerargs): return bot.osd("Please present a query to search.") query = spicemanip.main(triggerargs, 0) diff --git a/sopel_modules/SpiceBot_GifSearch/gifsearch.py b/sopel_modules/SpiceBot_GifSearch/gifsearch.py index 7f8ba88..0ef935e 100644 --- a/sopel_modules/SpiceBot_GifSearch/gifsearch.py +++ b/sopel_modules/SpiceBot_GifSearch/gifsearch.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.module from sopel.config.types import StaticSection, ValidatedAttribute -from sopel_modules.SpiceBot_Events.System import bot_events_startup_register, bot_events_recieved, bot_events_trigger +from sopel_modules.SpiceBot_Events.System import botevents from sopel_modules.SpiceBot_CommandsQuery.CommandsQuery import commandsquery_register @@ -47,7 +47,7 @@ def configure(config): def setup(bot): bot_logging(bot, 'SpiceBot_GifSearch', "Starting Setup Procedure") - bot_events_startup_register(bot, ['2003']) + botevents.startup_add([botevents.BOT_GIFSEARCH]) if 'SpiceBot_GifSearch' not in bot.memory: bot.memory["SpiceBot_GifSearch"] = {"cache": {}, "badgiflinks": [], 'valid_gif_api_dict': {}} @@ -79,7 +79,7 @@ def setup(bot): if validgifapi not in bot.memory["SpiceBot_GifSearch"]['cache'].keys(): bot.memory["SpiceBot_GifSearch"]['cache'][validgifapi] = dict() - bot_events_trigger(bot, 2003, "SpiceBot_GifSearch") + botevents.trigger(bot, botevents.BOT_GIFSEARCH, "SpiceBot_GifSearch") def shutdown(bot): @@ -167,7 +167,7 @@ def getGif(bot, searchdict): if str(searchdict["searchquery"]) not in bot.memory["SpiceBot_GifSearch"]['cache'][currentapi].keys(): bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])] = [] - if bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])] == []: + if not len(bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])]): try: page = requests.get(url, headers=header) @@ -195,7 +195,7 @@ def getGif(bot, searchdict): resultsarray.append(cururl) # make sure there are results - if resultsarray != []: + if len(resultsarray): # Create Temp dict for every result tempresultnum = 0 @@ -215,10 +215,10 @@ def getGif(bot, searchdict): verifygoodlinks.append(gifresult) bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])] = verifygoodlinks - if bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])] != []: + if len(bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])]): gifapiresults.extend(bot.memory["SpiceBot_GifSearch"]['cache'][currentapi][str(searchdict["searchquery"])]) - if gifapiresults == []: + if not len(gifapiresults): return {"error": "No Results were found for '" + searchdict["query"] + "' in the " + str(spicemanip.main(searchdict['gifsearch'], 'orlist')) + " api(s)"} random.shuffle(gifapiresults) @@ -243,7 +243,7 @@ def getGif(bot, searchdict): newlist.append(tempdict) gifapiresults = newlist - if gifapiresults == []: + if not len(gifapiresults): return {"error": "No Results were found for '" + searchdict["query"] + "' in the " + str(spicemanip.main(searchdict['gifsearch'], 'orlist')) + " api(s)"} # return dict @@ -251,7 +251,7 @@ def getGif(bot, searchdict): return gifdict -@sopel.module.event('2003') +@sopel.module.event(botevents.BOT_GIFSEARCH) @sopel.module.rule('.*') def bot_events_setup(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) diff --git a/sopel_modules/SpiceBot_InvalidCommand/InvalidCommand.py b/sopel_modules/SpiceBot_InvalidCommand/InvalidCommand.py index 23c8231..e694bcc 100644 --- a/sopel_modules/SpiceBot_InvalidCommand/InvalidCommand.py +++ b/sopel_modules/SpiceBot_InvalidCommand/InvalidCommand.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.module from sopel_modules.SpiceBot_SBTools import sopel_triggerargs -from sopel_modules.SpiceBot_Events.System import bot_events_check, bot_events_recieved +from sopel_modules.SpiceBot_Events.System import botevents def setup(bot): @@ -18,21 +18,22 @@ def shutdown(bot): del bot.memory["SpiceBot_InvalidCommand"] -@sopel.module.event('1004') +@sopel.module.event(botevents.BOT_LOADED) @sopel.module.rule('.*') def bot_events_complete(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) for comtype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys(): bot.memory["SpiceBot_InvalidCommand"]["valid"].extend(bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys()) +@botevents.check_ready([botevents.BOT_LOADED, botevents.BOT_COMMANDSQUERY]) @sopel.module.commands('(.*)') def InvalidCommand_triggers(bot, trigger): return - while not bot_events_check(bot, ['1004', '2002']): - pass + # while not botevents.check([botevents.BOT_LOADED, botevents.BOT_COMMANDSQUERY]): + # pass triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'prefix_command') diff --git a/sopel_modules/SpiceBot_Kick/__init__.py b/sopel_modules/SpiceBot_Kick/__init__.py index c752bd4..44b8ea5 100644 --- a/sopel_modules/SpiceBot_Kick/__init__.py +++ b/sopel_modules/SpiceBot_Kick/__init__.py @@ -12,7 +12,6 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.bot from sopel import tools, module from sopel.tools import Identifier -from sopel.module import HALFOP from sopel.config.types import StaticSection, ValidatedAttribute from sopel_modules.SpiceBot_SBTools import bot_logging @@ -28,8 +27,8 @@ __version__ = '0.1.2' def configure(config): - config.define_section("MAXTARGCONFIG_KICK", MAXTARGCONFIG_KICK, validate=False) - config.MAXTARGCONFIG_KICK.configure_setting('kick', 'MAXTARG limit for KICK') + config.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False) + config.SpiceBot_Kick.configure_setting('kick', 'MAXTARG limit for KICK') def setup(bot): @@ -41,7 +40,7 @@ def setup(bot): # verify config settings for server bot_logging(bot, 'SpiceBot_Kick', "Checking for config settings") - bot.config.define_section("MAXTARGCONFIG_KICK", MAXTARGCONFIG_KICK, validate=False) + bot.config.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False) @module.event('005') @@ -63,10 +62,10 @@ def parse_event_005(bot, trigger): except IndexError: value = None if value: - bot.config.MAXTARGCONFIG_KICK.kick = int(value) + bot.config.SpiceBot_Kick.kick = int(value) -class MAXTARGCONFIG_KICK(StaticSection): +class SpiceBot_Kick(StaticSection): kick = ValidatedAttribute('kick', default=1) @@ -80,8 +79,7 @@ class SopelKICK: The bot must be a channel operator in specified channel for this to work. .. versionadded:: 7.0 """ - if self.channels[channel].privileges[self.nick] < HALFOP: - self.write(['KICK', channel, nick], text) + self.write(['KICK', channel, nick], text) class SopelWrapperKICK(object): @@ -89,9 +87,9 @@ class SopelWrapperKICK(object): def kick(self, nick, channel=None, message=None): if channel is None: if self._trigger.is_privmsg: - return + raise RuntimeError('Error: KICK requires a channel.') else: channel = self._trigger.sender if nick is None: - return + raise RuntimeError('Error: KICK requires a nick.') self._bot.kick(nick, channel, message) diff --git a/sopel_modules/SpiceBot_Logs/ErrorDisplay.py b/sopel_modules/SpiceBot_Logs/ErrorDisplay.py index 39e50a3..1deb454 100644 --- a/sopel_modules/SpiceBot_Logs/ErrorDisplay.py +++ b/sopel_modules/SpiceBot_Logs/ErrorDisplay.py @@ -4,27 +4,28 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.module -from sopel_modules.SpiceBot_Events.System import bot_events_recieved +from sopel_modules.SpiceBot_Events.System import botevents +from sopel_modules.SpiceBot_SBTools import bot_logging from .Logs import stdio_logs_fetch -@sopel.module.event('2005') +@sopel.module.event(botevents.BOT_STARTUPMONOLOGUE) @sopel.module.rule('.*') def bot_startup_monologue_start(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) debuglines = stdio_logs_fetch(bot) searchphrasefound = [] for line in debuglines: - if "modules failed to load" in str(line) and "0 modules failed to load" not in str(line): - searchphrase = str(line).replace(" modules failed to load", "") - searchphrasefound.append(str(searchphrase) + " module(s) failed") - elif "dict files failed to load" in str(line) and "0 dict files failed to load" not in str(line): - searchphrase = str(line).replace(" dict files failed to load", "") - searchphrasefound.append(str(searchphrase) + " dict file(s) failed") + if str(line).endswith("failed to load") and not str(line).startswith("0"): + searchphrasefound.append(line) if len(searchphrasefound): + for foundphase in searchphrasefound: + bot_logging(bot, 'SpiceBot_Logs', str(foundphase)) searchphrasefound.insert(0, "Notice to Bot Admins: ") searchphrasefound.append("Run the debug command for more information.") bot.osd(searchphrasefound, bot.channels.keys()) + else: + bot_logging(bot, 'SpiceBot_Logs', "No issues found at bot startup!", True) diff --git a/sopel_modules/SpiceBot_Logs/Logs.py b/sopel_modules/SpiceBot_Logs/Logs.py index 3fdc3ed..69cfc15 100644 --- a/sopel_modules/SpiceBot_Logs/Logs.py +++ b/sopel_modules/SpiceBot_Logs/Logs.py @@ -6,7 +6,7 @@ import sopel.module from sopel.config.types import StaticSection, ValidatedAttribute from sopel_modules.SpiceBot_SBTools import bot_logging -from sopel_modules.SpiceBot_Events.System import bot_events_startup_register, bot_events_recieved, bot_events_trigger +from sopel_modules.SpiceBot_Events.System import botevents import os @@ -26,11 +26,11 @@ def setup(bot): bot_logging(bot, 'SpiceBot_Logs', "Starting Setup Procedure") bot.config.define_section("SpiceBot_Logs", SpiceBot_Logs_MainSection, validate=False) - bot_events_startup_register(bot, ['2004']) + botevents.startup_add([botevents.BOT_LOGS]) bot_logs_setup_check(bot) - bot_events_trigger(bot, 2004, "SpiceBot_Logs") + botevents.trigger(bot, botevents.BOT_LOGS, "SpiceBot_Logs") def bot_logs_setup_check(bot): @@ -43,10 +43,10 @@ def shutdown(bot): del bot.memory["SpiceBot_Logs"] -@sopel.module.event('1003') +@sopel.module.event(botevents.BOT_CONNECTED) @sopel.module.rule('.*') def join_log_channel(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) if bot.config.SpiceBot_Logs.logging_channel: channel = bot.config.SpiceBot_Logs.logging_channel @@ -68,7 +68,7 @@ def join_log_channel(bot, trigger): def stdio_logs_fetch(bot): - stdio_ignore = ["Loaded:"] + stdio_ignore = [] for logtype in bot.memory['SpiceBot_Logs']["logs"].keys(): stdio_ignore.append("[" + logtype + "]") @@ -94,10 +94,15 @@ def stdio_logs_fetch(bot): filelines = [] debuglines = [] + loadedmodules = [] for line in filelines: - if not line.startswith(tuple(stdio_ignore)): - if not line.isspace(): + if line.startswith("Loaded:"): + loadedmodules.append(str(line).split("Loaded:")[-1]) + else: + if not line.startswith(tuple(stdio_ignore)) and not line.isspace(): debuglines.append(str(line)) + loadedmodules = "Loaded: " + spicemanip.main(loadedmodules, 'andlist') + debuglines.insert(1, loadedmodules) return debuglines @@ -126,7 +131,7 @@ def get_running_pid(bot): return pidnum -@sopel.module.event('2004') +@sopel.module.event(botevents.BOT_LOGS) @sopel.module.rule('.*') def bot_events_setup(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) diff --git a/sopel_modules/SpiceBot_Logs/Logs_Command.py b/sopel_modules/SpiceBot_Logs/Logs_Command.py index e1e1617..fcf83a6 100644 --- a/sopel_modules/SpiceBot_Logs/Logs_Command.py +++ b/sopel_modules/SpiceBot_Logs/Logs_Command.py @@ -6,16 +6,17 @@ import sopel.module import spicemanip -from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, command_permissions_check, inlist, inlist_match -from sopel_modules.SpiceBot_Events.System import bot_events_check +from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, command_permissions_check, inlist, inlist_match, similar_list +from sopel_modules.SpiceBot_Events.System import botevents from .Logs import systemd_logs_fetch, stdio_logs_fetch +@botevents.check_ready([botevents.BOT_LOGS]) @sopel.module.nickname_commands('logs', 'debug') def bot_command_logs(bot, trigger): - while not bot_events_check(bot, '2004'): - pass + # while not botevents.check(botevents.BOT_LOGS): + # pass if not command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): bot.say("I was unable to process this Bot Nick command due to privilege issues.") @@ -24,10 +25,19 @@ def bot_command_logs(bot, trigger): triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command') logtype = spicemanip.main(triggerargs, 1) or None - if not logtype or not inlist(bot, logtype, bot.memory['SpiceBot_Logs']["logs"].keys()): + if not logtype: bot.osd("Current valid log(s) include: " + spicemanip.main(bot.memory['SpiceBot_Logs']["logs"].keys(), 'andlist'), trigger.sender, 'action') return + if not inlist(bot, logtype, bot.memory['SpiceBot_Logs']["logs"].keys()): + closestmatches = similar_list(bot, logtype, bot.memory['SpiceBot_Logs']["logs"].keys(), 10, 'reverse') + if not len(closestmatches): + bot.notice("No valid logs match " + str(logtype) + ".", trigger.nick) + else: + bot.notice("The following commands may match " + str(logtype) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick) + + return + logtype = inlist_match(bot, logtype, bot.memory['SpiceBot_Logs']["logs"].keys()) if logtype == "Sopel_systemd": diff --git a/sopel_modules/SpiceBot_SBTools/__init__.py b/sopel_modules/SpiceBot_SBTools/__init__.py index fb4054b..1bb1bed 100644 --- a/sopel_modules/SpiceBot_SBTools/__init__.py +++ b/sopel_modules/SpiceBot_SBTools/__init__.py @@ -84,7 +84,7 @@ def command_permissions_check(bot, trigger, privslist): else: commandrunconsensus.append('True') - if privslist == []: + if not len(privslist): commandrunconsensus.append('True') if 'True' not in commandrunconsensus: @@ -236,13 +236,13 @@ def similar_list(bot, searchitem, searchlist, matchcount=1, searchorder='reverse sim_listitems, sim_num = [], [] for listitem in searchlist: - similarlevel = similar(searchitem.lower(), listitem.lower()) + similarlevel = SequenceMatcher(None, searchitem.lower(), listitem.lower()).ratio() if similarlevel >= .75: sim_listitems.append(listitem) sim_num.append(similarlevel) if len(sim_listitems) and len(sim_num): - sim_num, sim_listitems = array_arrangesort(bot, sim_num, sim_listitems) + sim_num, sim_listitems = (list(x) for x in zip(*sorted(zip(sim_num, sim_listitems), key=itemgetter(0)))) if searchorder == 'reverse': sim_listitems = spicemanip.main(sim_listitems, 'reverse', "list") @@ -257,6 +257,13 @@ def array_arrangesort(bot, sortbyarray, arrayb): return sortbyarray, arrayb +def letters_in_string(text): + if text.isupper() or text.islower(): + return True + else: + return False + + """Channel Functions""" @@ -433,10 +440,10 @@ def read_directory_json_to_dict(bot, directories, configtypename="Config File", if filecount: if bot: - bot_logging(bot, log_from, 'Registered %d %s files,' % (filecount, configtypename)) - bot_logging(bot, log_from, '%d %s files failed to load' % (fileopenfail, configtypename)) + bot_logging(bot, log_from, 'Registered %d %s dict files,' % (filecount, configtypename)) + bot_logging(bot, log_from, '%d %s dict files failed to load' % (fileopenfail, configtypename), True) else: if bot: - bot_logging(bot, log_from, "Warning: Couldn't load any %s files" % (configtypename)) + bot_logging(bot, log_from, "Warning: Couldn't load any %s dict files" % (configtypename)) return configs_dict diff --git a/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue.py b/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue.py index c036231..032162a 100644 --- a/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue.py +++ b/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue.py @@ -4,19 +4,19 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.module -from sopel_modules.SpiceBot_Events.System import bot_events_recieved, bot_events_startup_register +from sopel_modules.SpiceBot_Events.System import botevents from sopel_modules.SpiceBot_SBTools import bot_logging def setup(bot): bot_logging(bot, 'SpiceBot_StartupMonologue', "Starting setup procedure") - bot_events_startup_register(bot, ['2010', '2011', '2012']) + botevents.startup_add([botevents.BOT_STARTUPMONOLOGUE_CHANNELS, botevents.BOT_STARTUPMONOLOGUE_COMMANDSQUERY, botevents.BOT_STARTUPMONOLOGUE_CONNECTED]) if 'SpiceBot_StartupMonologue' not in bot.memory: bot.memory['SpiceBot_StartupMonologue'] = [] -@sopel.module.event('2005', '2010', '2011', '2012') +@sopel.module.event(botevents.BOT_STARTUPMONOLOGUE, botevents.BOT_STARTUPMONOLOGUE_CHANNELS, botevents.BOT_STARTUPMONOLOGUE_COMMANDSQUERY, botevents.BOT_STARTUPMONOLOGUE_CONNECTED) @sopel.module.rule('.*') def bot_events_setup(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) diff --git a/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue_Display.py b/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue_Display.py index 6f1310c..43c687e 100644 --- a/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue_Display.py +++ b/sopel_modules/SpiceBot_StartupMonologue/StartupMonologue_Display.py @@ -4,27 +4,28 @@ from __future__ import unicode_literals, absolute_import, division, print_functi import sopel.module -from sopel_modules.SpiceBot_Events.System import bot_events_recieved, bot_events_trigger +from sopel_modules.SpiceBot_Events.System import botevents from sopel_modules.SpiceBot_SBTools import humanized_time, bot_logging + import time -@sopel.module.event('1003') +@sopel.module.event(botevents.BOT_CONNECTED) @sopel.module.rule('.*') def bot_startup_monologue_start(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) # Startup bot_logging(bot, 'SpiceBot_StartupMonologue', bot.nick + " is now starting. Please wait while I load my configuration") bot.osd(" is now starting. Please wait while I load my configuration.", bot.channels.keys(), 'ACTION') - bot_events_trigger(bot, 2012, "SpiceBot_StartupMonologue") + botevents.trigger(bot, botevents.BOT_STARTUPMONOLOGUE_CONNECTED, "SpiceBot_StartupMonologue") -@sopel.module.event('2002') +@sopel.module.event(botevents.BOT_COMMANDSQUERY) @sopel.module.rule('.*') def bot_startup_monologue_commands(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) availablecomsnum, availablecomsfiles = 0, 0 for commandstype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys(): @@ -34,25 +35,25 @@ def bot_startup_monologue_commands(bot, trigger): bot.memory['SpiceBot_StartupMonologue'].append("There are " + str(availablecomsnum) + " commands available in " + str(availablecomsfiles) + " files.") bot_logging(bot, 'SpiceBot_StartupMonologue', "There are " + str(availablecomsnum) + " commands available in " + str(availablecomsfiles) + " files.") - bot_events_trigger(bot, 2011, "SpiceBot_StartupMonologue") + botevents.trigger(bot, botevents.BOT_STARTUPMONOLOGUE_COMMANDSQUERY, "SpiceBot_StartupMonologue") -@sopel.module.event('2001') +@sopel.module.event(botevents.BOT_CHANNELS) @sopel.module.rule('.*') def bot_startup_monologue_channels(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) botcount = len(bot.channels.keys()) servercount = len(bot.memory['SpiceBot_Channels']['channels'].keys()) bot.memory['SpiceBot_StartupMonologue'].append("I am in " + str(botcount) + " of " + str(servercount) + " channel(s) available on this server.") - bot_events_trigger(bot, 2010, "SpiceBot_StartupMonologue") + botevents.trigger(bot, botevents.BOT_STARTUPMONOLOGUE_CHANNELS, "SpiceBot_StartupMonologue") -@sopel.module.event('1004') +@sopel.module.event(botevents.BOT_LOADED) @sopel.module.rule('.*') def bot_startup_monologue_display(bot, trigger): - bot_events_recieved(bot, trigger.event) + botevents.recieved(trigger) timesince = humanized_time(time.time() - bot.memory["SpiceBot_Uptime"]) bot.memory['SpiceBot_StartupMonologue'].append("Startup took " + timesince) @@ -63,4 +64,5 @@ def bot_startup_monologue_display(bot, trigger): bot.osd(bot.memory['SpiceBot_StartupMonologue'], bot.channels.keys(), 'ACTION') - bot_events_trigger(bot, 2005, "SpiceBot_StartupMonologue") + botevents.trigger(bot, botevents.BOT_STARTUPMONOLOGUE, "SpiceBot_StartupMonologue") + bot_logging(bot, 'SpiceBot_StartupMonologue', "Startup Monologue has been issued to all channels.", True) diff --git a/sopel_modules/SpiceBot_osd/__init__.py b/sopel_modules/SpiceBot_osd/__init__.py index 7bd3142..905fd71 100644 --- a/sopel_modules/SpiceBot_osd/__init__.py +++ b/sopel_modules/SpiceBot_osd/__init__.py @@ -27,9 +27,13 @@ __version__ = '0.1.2' def configure(config): - config.define_section("MAXTARGCONFIG", MAXTARGCONFIG, validate=False) - config.MAXTARGCONFIG.configure_setting('notice', 'MAXTARG limit for NOTICE') - config.MAXTARGCONFIG.configure_setting('privmsg', 'MAXTARG limit for PRIVMSG') + config.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False) + config.SpiceBot_OSD.configure_setting('notice', 'MAXTARG limit for NOTICE') + config.SpiceBot_OSD.configure_setting('privmsg', 'MAXTARG limit for PRIVMSG') + config.SpiceBot_OSD.configure_setting('flood_burst_lines', 'How many messages can be sent in burst mode.') + config.SpiceBot_OSD.configure_setting('flood_empty_wait', 'How long to wait between sending messages when not in burst mode, in seconds.') + config.SpiceBot_OSD.configure_setting('flood_refill_rate', 'How quickly burst mode recovers, in messages per second.') + config.SpiceBot_OSD.configure_setting('flood_dots', 'Display dots instead of spam.') def setup(bot): @@ -57,7 +61,7 @@ def setup(bot): # verify config settings for server bot_logging(bot, 'SpiceBot_OSD', "Checking for config settings") - bot.config.define_section("MAXTARGCONFIG", MAXTARGCONFIG, validate=False) + bot.config.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False) @module.event('005') @@ -80,15 +84,29 @@ def parse_event_005(bot, trigger): value = None if value: if settingname.upper() == 'NOTICE': - bot.config.MAXTARGCONFIG.notice = int(value) + bot.config.SpiceBot_OSD.notice = int(value) elif settingname.upper() == 'PRIVMSG': - bot.config.MAXTARGCONFIG.privmsg = int(value) + bot.config.SpiceBot_OSD.privmsg = int(value) -class MAXTARGCONFIG(StaticSection): +class SpiceBot_OSD(StaticSection): + + """MAXTARG""" notice = ValidatedAttribute('notice', default=1) privmsg = ValidatedAttribute('privmsg', default=1) + flood_burst_lines = ValidatedAttribute('flood_burst_lines', int, default=4) + """How many messages can be sent in burst mode.""" + + flood_empty_wait = ValidatedAttribute('flood_empty_wait', float, default=0.7) + """How long to wait between sending messages when not in burst mode, in seconds.""" + + flood_refill_rate = ValidatedAttribute('flood_refill_rate', int, default=1) + """How quickly burst mode recovers, in messages per second.""" + + flood_dots = ValidatedAttribute('flood_dots', default=True) + """Display dots instead of spam.""" + class ToolsOSD: @@ -117,9 +135,9 @@ class ToolsOSD: raise ValueError("Recipients list empty.") if text_method == 'NOTICE': - maxtargets = bot.config.MAXTARGCONFIG.notice + maxtargets = bot.config.SpiceBot_OSD.notice elif text_method in ['PRIVMSG', 'ACTION']: - maxtargets = bot.config.MAXTARGCONFIG.privmsg + maxtargets = bot.config.SpiceBot_OSD.privmsg maxtargets = int(maxtargets) recipientgroups = [] @@ -259,17 +277,12 @@ class SopelOSD: for recipientgroup in recipientgroups: text_method = text_method_orig - # No messages within the last 3 seconds? Go ahead! - # Otherwise, wait so it's been at least 0.8 seconds + penalty - recipient_id = Identifier(recipientgroup) recipient_stack = self.stack.setdefault(recipient_id, { 'messages': [], - 'flood_left': 4, + 'flood_left': self.config.SpiceBot_OSD.flood_burst_lines, 'dots': 0, - # TODO - # 'flood_left': self.config.core.flood_burst_lines, }) recipient_stack['dots'] = 0 @@ -281,32 +294,30 @@ class SopelOSD: if not recipient_stack['flood_left']: elapsed = time.time() - recipient_stack['messages'][-1][0] - # TODO - # recipient_stack['flood_left'] = min( - # self.config.core.flood_burst_lines, - # int(elapsed) * self.config.core.flood_refill_rate) - recipient_stack['flood_left'] = min(4, int(elapsed) * 1) + recipient_stack['flood_left'] = min( + self.config.SpiceBot_OSD.flood_burst_lines, + int(elapsed) * self.config.SpiceBot_OSD.flood_refill_rate) if not recipient_stack['flood_left']: elapsed = time.time() - recipient_stack['messages'][-1][0] penalty = float(max(0, len(text) - 50)) / 70 # Never wait more than 2 seconds - # wait = min(self.config.core.flood_empty_wait + penalty, 2) # TODO - wait = min(0.7 + penalty, 2) + wait = min(self.config.SpiceBot_OSD.flood_empty_wait + penalty, 2) if elapsed < wait: time.sleep(wait - elapsed) # Loop detection messages = [m[1] for m in recipient_stack['messages'][-8:]] - # If what we about to send repeated at least 5 times in the - # last 2 minutes, replace with '...' - if messages.count(text) >= 5 and elapsed < 120: - recipient_stack['dots'] += 1 - else: - recipient_stack['dots'] = 0 + if self.config.SpiceBot_OSD.flood_dots: + # If what we about to send repeated at least 5 times in the + # last 2 minutes, replace with '...' + if messages.count(text) >= 5 and elapsed < 120: + recipient_stack['dots'] += 1 + else: + recipient_stack['dots'] = 0 - if not recipient_stack['dots'] >= 3: + if recipient_stack['dots'] <= 3: recipient_stack['flood_left'] = max(0, recipient_stack['flood_left'] - 1) recipient_stack['messages'].append((time.time(), self.safe(text))) @@ -377,7 +388,10 @@ class SopelOSD: self.osd(text, dest, 'PRIVMSG', max_messages) def msg(self, recipient, text, max_messages=1): - # Deprecated, but way too much of a pain to remove. + """ + .. deprecated:: 6.0 + Use :meth:`say` instead. Will be removed in Sopel 8. + """ self.osd(text, recipient, 'PRIVMSG', max_messages) @@ -392,19 +406,16 @@ class SopelWrapperOSD(object): if destination is None: destination = self._trigger.sender self._bot.osd(self, message, destination, 'PRIVMSG', 1) - # self._bot.say(message, destination, max_messages) def action(self, message, destination=None, max_messages=1): if destination is None: destination = self._trigger.sender self._bot.osd(self, message, destination, 'ACTION', 1) - # self._bot.action(message, destination, max_messages) def notice(self, message, destination=None, max_messages=1): if destination is None: destination = self._trigger.sender self._bot.osd(self, message, destination, 'NOTICE', 1) - # self._bot.notice(message, destination, max_messages) def reply(self, message, destination=None, reply_to=None, notice=False, max_messages=1): if destination is None: @@ -416,4 +427,3 @@ class SopelWrapperOSD(object): self._bot.osd(self, message, destination, 'NOTICE', 1) else: self._bot.osd(self, message, destination, 'PRIVMSG', 1) - # self._bot.reply(message, destination, reply_to, notice, max_messages)