diff --git a/sopel_modules/SpiceBot/DictComs.py b/sopel_modules/SpiceBot/DictComs.py index 266456c..3b41958 100644 --- a/sopel_modules/SpiceBot/DictComs.py +++ b/sopel_modules/SpiceBot/DictComs.py @@ -12,6 +12,7 @@ from .Config import config as botconfig from .Read import read as botread from .Commands import commands as botcommands from .Database import db as botdb +from .Tools import prerun_shared import os import copy @@ -37,7 +38,7 @@ class BotDictCommands(): "comtype", "type", "validcoms", "author", "contributors", "description", "exampleresponse", "example", - "privs" + "privs", "nonstockoptions", ] self.dictcom_load() @@ -104,6 +105,11 @@ class BotDictCommands(): if otherkeys != []: dict_from_file = self.dictcom_load_usecases(maincom, dict_from_file, otherkeys) + dict_from_file["nonstockoptions"] = [] + for command in list(dict_from_file.keys()): + if command not in prerun_shared.stockoptions: + dict_from_file["nonstockoptions"].append(command) + botcommands.register(dict_from_file) def dictcom_load_usecases(self, maincom, dict_from_file, process_list): diff --git a/sopel_modules/SpiceBot/MessageLog.py b/sopel_modules/SpiceBot/MessageLog.py index 1947bc7..5b53e30 100644 --- a/sopel_modules/SpiceBot/MessageLog.py +++ b/sopel_modules/SpiceBot/MessageLog.py @@ -100,6 +100,14 @@ class MessageLog(): self.message_display[log_id]["messages"].append(messagedict) + def messagelog_private(self, log_id, message): + + recipients = [self.message_display[log_id]["trigger"]["nick"]] + + messagedict = {"type": "private", "message": message, "recipients": recipients} + + self.message_display[log_id]["messages"].append(messagedict) + def messagelog_exit(self, bot, log_id): current_messages = [] @@ -132,6 +140,9 @@ class MessageLog(): if messagedict["type"] == 'error': bot.osd(messagedict['message'], self.message_display[log_id]["trigger"]["nick"], 'notice') + elif messagedict["type"] == 'private': + bot.osd(messagedict['message'], messagedict["recipients"], 'notice') + elif len(messagedict["recipients"]) > 1: bot.osd(messagedict['message'], messagedict["recipients"], 'notice') diff --git a/sopel_modules/SpiceBot/Prerun.py b/sopel_modules/SpiceBot/Prerun.py index 8e95fce..3dfc1e9 100644 --- a/sopel_modules/SpiceBot/Prerun.py +++ b/sopel_modules/SpiceBot/Prerun.py @@ -13,7 +13,7 @@ from word2number import w2n from sopel_modules.spicemanip import spicemanip -from .Tools import command_permissions_check, class_create +from .Tools import class_create from .Commands import commands as botcommands from .Database import db as botdb from .Channels import channels as botchannels @@ -21,9 +21,7 @@ from .MessageLog import messagelog as botmessagelog from .Config import config as botconfig from .Users import users as botusers from .Events import events as botevents - - -# TODO check identify for OPs and bot admins +from .Tools import prerun_shared def prerun(t_command_type='module', t_command_subtype=None): @@ -104,14 +102,16 @@ def prerun(t_command_type='module', t_command_subtype=None): botcom.dict["runcount"] = runcount - botcom.dict["args"], botcom.dict["hyphen_arg"] = trigger_hyphen_args(botcom.dict["args"]) + botcom.dict["args"], botcom.dict["hyphen_arg"] = trigger_hyphen_args(botcom) # special handling - botcom.dict["responsekey"] = "?default" + botcom = special_handling(botcom) + botcom = updatable_content(bot, botcom, trigger) - args_pass = trigger_hyphen_arg_handler(bot, trigger, botcom) + args_pass, botcom = trigger_hyphen_arg_handler(bot, trigger, botcom) if args_pass: + if trigger_runstatus(bot, trigger, botcom): function(bot, trigger, botcom, *args, **kwargs) botmessagelog.messagelog_exit(bot, argsdict_default["log_id"]) @@ -282,7 +282,7 @@ def trigger_runstatus_query(bot, trigger, botcom): # Allow permissions for enabling and disabling commands via hyphenargs if botcom.dict["adminswitch"]: - if command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if botusers.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): return True else: botmessagelog.messagelog_error(botcom.dict["log_id"], "The admin switch (-a) is for use by authorized nicks ONLY.") @@ -348,7 +348,7 @@ def trigger_runstatus(bot, trigger, botcom): # Allow permissions for enabling and disabling commands via hyphenargs if botcom.dict["adminswitch"]: - if command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if botusers.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): return True else: botmessagelog.messagelog_error(botcom.dict["log_id"], "The admin switch (-a) is for use by authorized nicks ONLY.") @@ -430,46 +430,67 @@ def trigger_runstatus(bot, trigger, botcom): def trigger_cant_run(bot, trigger, botcom, message=None): if message: botmessagelog.messagelog_error(botcom.dict["log_id"], message) - if command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if botusers.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): botmessagelog.messagelog_error(botcom.dict["log_id"], "You however are authorized to bypass this warning with the (-a) admin switch.") return False -def trigger_hyphen_args(trigger_args_part): - valid_hyphen_args = [ - 'check', - 'enable', 'disable', - 'block', 'unblock', - "activate", "deactivate", - "on", "off", - 'multirun', 'multiruns', - 'example', 'usage', - 'filename', 'filepath', - 'foldername', 'folderpath', - "author", - 'contribs', 'contrib', "contributors", - 'alias', 'aliases', - 'random' - ] - numdict = { - "last": -1 - } +def updatable_content(bot, botcom, trigger): + # commands that can be updated + if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"]: + customresponses = [] + if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": + customresponses = botdb.get_nick_value(str(bot.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), 'sayings') or [] + elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": + customresponses = botdb.get_nick_value(str(trigger.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), 'sayings') or [] + if not len(customresponses): + if len(botcom.dict["dict"][botcom.dict["responsekey"]]["responses"]): + savevalues = botcom.dict["dict"][botcom.dict["responsekey"]]["responses"] + else: + savevalues = ["Please use --add to add custom content!"] + if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": + botdb.set_nick_value(str(bot.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), savevalues, 'sayings') + elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": + botdb.set_nick_value(str(trigger.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), savevalues, 'sayings') + else: + botcom.dict["dict"][botcom.dict["responsekey"]]["responses"] = customresponses + return botcom + + +def special_handling(botcom): + botcom.dict["responsekey"] = "?default" + + # handling for special cases + posscom = spicemanip(botcom.dict['args'], 1) + if posscom.lower() in [command.lower() for command in botcom.dict["dict"]["nonstockoptions"]]: + for command in botcom.dict["dict"]["nonstockoptions"]: + if command.lower() == posscom.lower(): + posscom = command + botcom.dict["responsekey"] = posscom + + if botcom.dict["responsekey"] != "?default": + botcom.dict['args'] = spicemanip(botcom.dict['args'], '2+', 'list') + + return botcom + + +def trigger_hyphen_args(botcom): hyphen_args = [] trigger_args_unhyphend = [] - for worditem in trigger_args_part: + for worditem in botcom.dict["args"]: if str(worditem).startswith("--"): clipped_word = str(worditem[2:]).lower() # valid arg above - if clipped_word in valid_hyphen_args: + if clipped_word in prerun_shared.valid_hyphen_args: hyphen_args.append(clipped_word) # numbered args elif str(clipped_word).isdigit(): hyphen_args.append(int(clipped_word)) - elif clipped_word in list(numdict.keys()): - hyphen_args.append(int(numdict[clipped_word])) + elif clipped_word in list(prerun_shared.numdict.keys()): + hyphen_args.append(int(prerun_shared.numdict[clipped_word])) else: @@ -478,7 +499,7 @@ def trigger_hyphen_args(trigger_args_part): clipped_word = w2n.word_to_num(str(clipped_word)) hyphen_args.append(int(clipped_word)) - # word is not a valid arg or number + # word is not a valid arg or number except ValueError: trigger_args_unhyphend.append(worditem) else: @@ -498,19 +519,104 @@ def trigger_hyphen_arg_handler(bot, trigger, botcom): # Commands that cannot run via privmsg # TODO --check should work for commands that don't exist + botcom.dict["specified"] = None + if not botcom.dict["hyphen_arg"]: - return True + return True, botcom # handle numbered args - if str(botcom.dict["hyphen_arg"]).isdigit() or botcom.dict["hyphen_arg"] in [-1, 'random']: - return True + elif str(botcom.dict["hyphen_arg"]).isdigit() or botcom.dict["hyphen_arg"] in [-1, 'random']: + botcom.dict["specified"] = botcom.dict["hyphen_arg"] + if not botcom.dict["dict"][botcom.dict["responsekey"]]["selection_allowed"]: + botmessagelog.messagelog_error(botcom.dict["log_id"], "The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " response list cannot be specified.") + return False, botcom + else: + return True, botcom - elif botcom.dict["hyphen_arg"] in ['check']: + elif botcom.dict["hyphen_arg"] in ['check', "test"]: if botcom.dict["com"].lower() != botcom.dict["realcom"]: botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + " is a valid alias command for " + botcom.dict["realcomtext"]) else: botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + " is a valid command") - return False + return False, botcom + + elif botcom.dict["hyphen_arg"] in ['view']: + if not len(botcom.dict["dict"][botcom.dict["responsekey"]]["responses"]): + botmessagelog.messagelog(botcom.dict["log_id"], "The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " command appears to have no entries!") + else: + botmessagelog.messagelog_private(botcom.dict["log_id"], "The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " command contains:") + listnumb, relist = 0, [] + for item in botcom.dict["dict"][botcom.dict["responsekey"]]["responses"]: + listnumb += 1 + if isinstance(item, dict): + relist.append(str("[#" + str(listnumb) + "] COMPLEX dict Entry")) + elif isinstance(item, list): + relist.append(str("[#" + str(listnumb) + "] COMPLEX list Entry")) + else: + relist.append(str("[#" + str(listnumb) + "] " + str(item))) + botmessagelog.messagelog_private(botcom.dict["log_id"], relist) + return False, botcom + + elif botcom.dict["hyphen_arg"] in ['count']: + botmessagelog.messagelog(botcom.dict["log_id"], "The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " command has " + str(len(botcom.dict["dict"][botcom.dict["responsekey"]]["responses"])) + " entries.") + return False, botcom + + elif botcom.dict["hyphen_arg"] in ['special', 'options', 'list']: + if not len(botcom.dict["dict"]["nonstockoptions"]): + botmessagelog.messagelog(botcom.dict["log_id"], "There appear to be no special options for " + str(botcom.dict["realcom"]) + ".") + else: + botmessagelog.messagelog(botcom.dict["log_id"], "The special options for " + str(botcom.dict["realcom"]) + " command include: " + spicemanip(botcom.dict["dict"]["nonstockoptions"], "andlist") + ".") + return False, botcom + + elif botcom.dict["hyphen_arg"] in ['add', 'del', 'remove']: + if botcom.dict["hyphen_arg"] in ['add', 'remove']: + directionword = botcom.dict["hyphen_arg"] + elif botcom.dict["hyphen_arg"] in ['del']: + directionword = "remove" + if not botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"]: + botmessagelog.messagelog_error(botcom.dict["log_id"], "The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list cannot be updated.") + return False, botcom + + fulltext = spicemanip(botcom.dict['args'], 0) + if not fulltext: + botmessagelog.messagelog_error(botcom.dict["log_id"], "What would you like to " + directionword + " from the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list?") + return False, botcom + + if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": + customresponses = botdb.get_nick_value(str(bot.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), 'sayings') or [] + elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": + customresponses = botdb.get_nick_value(str(trigger.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), 'sayings') or [] + + if botcom.dict["hyphen_arg"] in ['add']: + if fulltext in customresponses: + botmessagelog.messagelog_error(botcom.dict["log_id"], "The following was already in the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") + return False, botcom + + customresponses.append(fulltext) + + if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": + botdb.set_nick_value(str(bot.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), customresponses, 'sayings') + elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": + botdb.set_nick_value(str(trigger.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), customresponses, 'sayings') + + botmessagelog.messagelog(botcom.dict["log_id"], "The following was added to the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") + return False, botcom + + elif botcom.dict["hyphen_arg"] in ['del', 'remove']: + if fulltext not in customresponses: + botmessagelog.messagelog_error(botcom.dict["log_id"], "The following was already not in the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") + return False, botcom + customresponses.remove(fulltext) + + if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": + botdb.set_nick_value(str(bot.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), customresponses, 'sayings') + elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": + botdb.set_nick_value(str(trigger.nick), botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), customresponses, 'sayings') + + botmessagelog.messagelog(botcom.dict["log_id"], "The following was removed from the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") + return False, botcom + + return False, botcom elif botcom.dict["hyphen_arg"] in [ 'enable', 'disable', @@ -525,50 +631,50 @@ def trigger_hyphen_arg_handler(bot, trigger, botcom): botmessagelog.messagelog_error(botcom.dict["log_id"], "Who/Where am I enabling " + str(botcom.dict["comtext"]) + " usage for?") else: botmessagelog.messagelog_error(botcom.dict["log_id"], "Who/Where am I disabling " + str(botcom.dict["comtext"]) + " usage for?") - return False + return False, botcom botcom.dict["args"] = spicemanip(botcom.dict["args"], "2+", "list") if not botdb.check_nick_id(target) and not botchannels.check_channel_bot(target, True): botmessagelog.messagelog_error(botcom.dict["log_id"], "I don't know who/what " + str(target) + " is.") - return False + return False, botcom - if not command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if not botusers.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): if target != trigger.nick: if botcom.dict["hyphen_arg"] in ['enable', 'unblock', "activate", "on"]: botmessagelog.messagelog_error(botcom.dict["log_id"], "I was unable to enable this command for " + str(target) + " due to privilege issues.") else: botmessagelog.messagelog_error(botcom.dict["log_id"], "I was unable to disable this command for " + str(target) + " due to privilege issues.") - return False + return False, botcom if botcom.dict["hyphen_arg"] in ['enable', 'unblock', "activate", "on"]: if not botcommands.check_commands_disabled(botcom.dict["realcomref"], target): botmessagelog.messagelog_error(botcom.dict["log_id"], botcom.dict["comtext"] + " is already enabled for " + str(target)) - return False + return False, botcom botcommands.unset_command_disabled(botcom.dict["realcomref"], target) botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + " is now enabled for " + str(target)) - return False + return False, botcom else: if botcommands.check_commands_disabled(botcom.dict["realcomref"], target): botmessagelog.messagelog_error(botcom.dict["log_id"], botcom.dict["comtext"] + " is already disabled for " + str(target)) - return False + return False, botcom trailingmessage = spicemanip(botcom.dict["args"], 0) or "No reason given." timestamp = str(datetime.datetime.utcnow()) botcommands.set_command_disabled(botcom.dict["realcomref"], target, timestamp, trailingmessage, trigger.nick) botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + " is now disabled for " + str(target)) - return False + return False, botcom if botcom.dict["hyphen_arg"] in ['multirun', 'multiruns']: onoff = spicemanip(botcom.dict["args"], 1) or None if not onoff or onoff not in ['enable', 'unblock', "activate", "on"]: botmessagelog.messagelog_error(botcom.dict["log_id"], "Do you want to enable or disable " + str(botcom.dict["comtext"]) + " multirun usage?") - return False + return False, botcom target = spicemanip(botcom.dict["args"], 1) or trigger.nick if not target: @@ -576,66 +682,66 @@ def trigger_hyphen_arg_handler(bot, trigger, botcom): botmessagelog.messagelog_error(botcom.dict["log_id"], "Who/Where am I enabling " + str(botcom.dict["comtext"]) + " multirun usage for?") else: botmessagelog.messagelog_error(botcom.dict["log_id"], "Who/Where am I disabling " + str(botcom.dict["comtext"]) + " multirun usage for?") - return False + return False, botcom botcom.dict["args"] = spicemanip(botcom.dict["args"], "2+", "list") if not botdb.check_nick_id(target) and not botchannels.check_channel_bot(target, True): botmessagelog.messagelog_error(botcom.dict["log_id"], "I don't know who/what " + str(target) + " is.") - return False + return False, botcom - if not command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if not botusers.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): if target != trigger.nick: if onoff in ['enable', 'unblock', "activate", "on"]: botmessagelog.messagelog_error(botcom.dict["log_id"], "I was unable to enable multirun usage on " + str(botcom.dict["comtext"]) + " for " + str(target) + " due to privilege issues.") else: botmessagelog.messagelog_error(botcom.dict["log_id"], "I was unable to enable multirun usage on " + str(botcom.dict["comtext"]) + " for " + str(target) + " due to privilege issues.") - return False + return False, botcom if onoff in ['enable', 'unblock', "activate", "on"]: if not botcommands.check_commands_disabled(botcom.dict["realcomref"], target, "multirun"): botmessagelog.messagelog_error(botcom.dict["log_id"], botcom.dict["comtext"] + " multirun is already enabled for " + str(target)) - return False + return False, botcom botcommands.unset_command_disabled(botcom.dict["realcomref"], target, "multirun") botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + " multirun is now enabled for " + str(target)) - return False + return False, botcom else: if botcommands.check_commands_disabled(botcom.dict["realcomref"], target, "multirun"): botmessagelog.messagelog_error(botcom.dict["log_id"], botcom.dict["comtext"] + " multirun is already disabled for " + str(target)) - return False + return False, botcom trailingmessage = spicemanip(botcom.dict["args"], 0) or "No reason given." timestamp = str(datetime.datetime.utcnow()) botcommands.set_command_disabled(botcom.dict["realcomref"], target, timestamp, trailingmessage, trigger.nick, "multirun") botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + " multirun is now disabled for " + str(target)) - return False + return False, botcom elif botcom.dict["hyphen_arg"] in ['example', 'usage']: botmessagelog.messagelog(botcom.dict["log_id"], botcom.dict["comtext"] + ": " + str(botcom.dict["dict"]["example"])) - return False + return False, botcom elif botcom.dict["hyphen_arg"] in ['filename', 'filepath']: botmessagelog.messagelog(botcom.dict["log_id"], "The " + str(botcom.dict["comtext"]) + " file is located at " + str(botcom.dict["dict"][botcom.dict["hyphen_arg"]])) - return False + return False, botcom elif botcom.dict["hyphen_arg"] in ['foldername', 'folderpath']: botmessagelog.messagelog(botcom.dict["log_id"], "The " + str(botcom.dict["comtext"]) + " folder is located at " + str(botcom.dict["dict"][botcom.dict["hyphen_arg"]])) - return False + return False, botcom elif botcom.dict["hyphen_arg"] in ['author']: botmessagelog.messagelog(botcom.dict["log_id"], "The author of the " + str(botcom.dict["comtext"]) + " command is " + botcom.dict["dict"]["author"] + ".") - return False + return False, botcom elif botcom.dict["hyphen_arg"] in ['contribs', 'contrib', "contributors"]: botmessagelog.messagelog(botcom.dict["log_id"], "The contributors of the " + str(botcom.dict["comtext"]) + " command are " + spicemanip(botcom.dict["dict"]["contributors"], "andlist") + ".") - return False + return False, botcom elif botcom.dict["hyphen_arg"] in ['alias', 'aliases']: botmessagelog.messagelog(botcom.dict["log_id"], "The alaises of the " + str(botcom.dict["comtext"]) + " command are " + spicemanip(botcom.dict["dict"]["validcoms"], "andlist") + ".") - return False + return False, botcom - return False + return False, botcom diff --git a/sopel_modules/SpiceBot/Read.py b/sopel_modules/SpiceBot/Read.py index 79c3ff6..e717f53 100644 --- a/sopel_modules/SpiceBot/Read.py +++ b/sopel_modules/SpiceBot/Read.py @@ -12,6 +12,7 @@ from fake_useragent import UserAgent from .Logs import logs from .Config import config as botconfig +from .Tools import prerun_shared class BotRead(): @@ -223,6 +224,44 @@ class BotRead(): if "hardcoded_channel_block" not in list(dict_from_file.keys()): dict_from_file["hardcoded_channel_block"] = [] + # check that type is set, use cases will inherit this if not set + if "type" not in list(dict_from_file.keys()): + dict_from_file["type"] = 'module' + + dict_from_file["nonstockoptions"] = ["?default"] + for command in list(dict_from_file.keys()): + if command not in prerun_shared.stockoptions: + dict_from_file["nonstockoptions"].append(command) + + for specialcase in dict_from_file["nonstockoptions"]: + + # All of the above need to be in the dict if not + if specialcase not in list(dict_from_file.keys()): + dict_from_file[specialcase] = dict() + + # verify if already there, that the key is a dict + if not isinstance(dict_from_file[specialcase], dict): + dict_from_file[specialcase] = dict() + + if "responses" not in list(dict_from_file[specialcase].keys()): + dict_from_file[specialcase]["responses"] = [] + + if "type" not in list(dict_from_file[specialcase].keys()): + dict_from_file[specialcase]["type"] = "module" + + # each usecase needs to know if it can be updated. Default is false + if "updates_enabled" not in list(dict_from_file[specialcase].keys()): + dict_from_file[specialcase]["updates_enabled"] = False + if dict_from_file[specialcase]["updates_enabled"]: + if dict_from_file[specialcase]["updates_enabled"] not in ["shared", "user"]: + dict_from_file[specialcase]["updates_enabled"] = "shared" + + if "selection_allowed" not in list(dict_from_file[specialcase].keys()): + dict_from_file[specialcase]["selection_allowed"] = True + + if "?default" in dict_from_file["nonstockoptions"]: + dict_from_file["nonstockoptions"].remove("?default") + return dict_from_file diff --git a/sopel_modules/SpiceBot/Tools.py b/sopel_modules/SpiceBot/Tools.py index e6fa9b7..07c03f3 100644 --- a/sopel_modules/SpiceBot/Tools.py +++ b/sopel_modules/SpiceBot/Tools.py @@ -20,6 +20,44 @@ from .Logs import logs from .Config import config as botconfig +class Botprerun_shared(): + def __init__(self): + + self.valid_hyphen_args = [ + 'check', 'test', + 'view', + 'count', + 'special', 'options', 'list', + 'add', 'del', 'remove', + 'enable', 'disable', + 'block', 'unblock', + "activate", "deactivate", + "on", "off", + 'multirun', 'multiruns', + 'example', 'usage', + 'filename', 'filepath', + 'foldername', 'folderpath', + "author", + 'contribs', 'contrib', "contributors", + 'alias', 'aliases', + 'random' + ] + + self.numdict = { + "last": -1 + } + + self.stockoptions = [ + "?default", "validcoms", "contributors", "author", "type", + "filepath", "filename", "hardcoded_channel_block", "description", + "exampleresponse", "example", "usage", "privs", "nonstockoptions", + 'folderpath', 'foldername', 'comtype', + ] + + +prerun_shared = Botprerun_shared() + + """Variable References""" github_dict = { @@ -56,36 +94,6 @@ def bot_privs(privtype): return botpriveval -def command_permissions_check(bot, trigger, privslist): - - commandrunconsensus = [] - - for botpriv in ["admins", "owner"]: - if botpriv in privslist: - botpriveval = bot_privs(botpriv) - if not inlist(trigger.nick, botpriveval): - commandrunconsensus.append('False') - else: - commandrunconsensus.append('True') - - if not trigger.is_privmsg: - for chanpriv in ['OP', 'HOP', 'VOICE', 'OWNER', 'ADMIN']: - if chanpriv in privslist: - chanpriveval = channel_privs(bot, trigger.sender, chanpriv) - if not inlist(trigger.nick, chanpriveval): - commandrunconsensus.append('False') - else: - commandrunconsensus.append('True') - - if not len(privslist): - commandrunconsensus.append('True') - - if 'True' not in commandrunconsensus: - return False - - return True - - """ Classes """ @@ -302,7 +310,7 @@ def stock_modules_begone(): for pathname in os.listdir(modules_dir): path = os.path.join(modules_dir, pathname) if (os.path.isfile(path) and pathname.endswith('.py') and not pathname.startswith('_')): - if not pathname in ["__init__.py", "SpiceBot_dummycommand.py"]: + if pathname not in ["__init__.py", "SpiceBot_dummycommand.py"]: os.system("sudo mv " + path + " " + stockdir) @@ -320,7 +328,7 @@ def spicebot_update(deps=False): pipcommand = "sudo pip3 install --upgrade" if not deps: - pipcommand += " --no-deps" + pipcommand += " --no-deps" pipcommand += " --force-reinstall" # pipcommand += " git+" + str(botconfig.SpiceBot_Update.gitrepo) + "@" + str(botconfig.SpiceBot_Update.gitbranch) pipcommand += " /tmp/SpiceBot/" diff --git a/sopel_modules/SpiceBot/Users.py b/sopel_modules/SpiceBot/Users.py index 1763dd2..4ccf102 100644 --- a/sopel_modules/SpiceBot/Users.py +++ b/sopel_modules/SpiceBot/Users.py @@ -6,7 +6,7 @@ import sopel from sopel.tools import Identifier from .Database import db as botdb -from .Tools import is_number, inlist, similar, array_arrangesort +from .Tools import is_number, inlist, similar, array_arrangesort, bot_privs, channel_privs from sopel_modules.spicemanip import spicemanip @@ -524,5 +524,39 @@ class BotUsers(): elif outputtype == 'random': return spicemanip(validtargs, 'random') + def command_permissions_check(self, bot, trigger, privslist): + + nick_id = self.whois_ident(trigger.nick) + if bot.config.SpiceBot_regnick.regnick: + if int(nick_id) not in self.dict["identified"]: + return False + + commandrunconsensus = [] + + for botpriv in ["admins", "owner"]: + if botpriv in privslist: + botpriveval = bot_privs(botpriv) + if not inlist(trigger.nick, botpriveval): + commandrunconsensus.append('False') + else: + commandrunconsensus.append('True') + + if not trigger.is_privmsg: + for chanpriv in ['OP', 'HOP', 'VOICE', 'OWNER', 'ADMIN']: + if chanpriv in privslist: + chanpriveval = channel_privs(bot, trigger.sender, chanpriv) + if not inlist(trigger.nick, chanpriveval): + commandrunconsensus.append('False') + else: + commandrunconsensus.append('True') + + if not len(privslist): + commandrunconsensus.append('True') + + if 'True' not in commandrunconsensus: + return False + + return True + users = BotUsers() diff --git a/sopel_modules/SpiceBot_Core_Commands/AI.py b/sopel_modules/SpiceBot_Core_Commands/AI.py index c3929ef..1f60084 100644 --- a/sopel_modules/SpiceBot_Core_Commands/AI.py +++ b/sopel_modules/SpiceBot_Core_Commands/AI.py @@ -8,6 +8,8 @@ import sopel_modules.SpiceBot as SpiceBot from sopel_modules.spicemanip import spicemanip +from word2number import w2n + # TODO split functionality for "no command" @@ -106,11 +108,59 @@ def bot_command_rule_ai(bot, trigger): if trigger_command not in commands_list: if not SpiceBot.letters_in_string(trigger_command): return - # invalid_display = ["I don't seem to have a command for " + str(trigger_command) + "!"] - # TODO - # invalid_display.append("If you have a suggestion for this command, you can run .feature ." + str(trigger_command)) - # invalid_display.append("ADD DESCRIPTION HERE!") - # bot.osd(invalid_display, trigger.nick, 'notice') + + invalid_display = ["I don't seem to have a command for " + str(trigger_command) + "!"] + + # create list of valid commands + commands_list = dict() + for commandstype in list(SpiceBot.commands.dict['commands'].keys()): + if commandstype not in ['rule', 'nickname']: + for com in list(SpiceBot.commands.dict['commands'][commandstype].keys()): + if com not in list(commands_list.keys()): + commands_list[com] = SpiceBot.commands.dict['commands'][commandstype][com] + + # hyphen args handling + hyphen_args = [] + argssplit = spicemanip(fulltrigger, "2+", 'list') + for worditem in argssplit: + if str(worditem).startswith("--"): + clipped_word = str(worditem[2:]).lower() + # valid arg above + if clipped_word in SpiceBot.prerun_shared.valid_hyphen_args: + hyphen_args.append(clipped_word) + # numbered args + elif str(clipped_word).isdigit(): + hyphen_args.append(int(clipped_word)) + elif clipped_word in list(SpiceBot.prerun_shared.numdict.keys()): + hyphen_args.append(int(SpiceBot.prerun_shared.numdict[clipped_word])) + else: + # check if arg word is a number + try: + clipped_word = w2n.word_to_num(str(clipped_word)) + hyphen_args.append(int(clipped_word)) + # word is not a valid arg or number + except ValueError: + clipped_word = None + if len(hyphen_args): + hyphenarg = hyphen_args[0] + if hyphenarg: + invalid_display.append("Hyphen Argument Not Valid.") + + closestmatches = SpiceBot.similar_list(trigger_command, list(commands_list.keys()), 10, 'reverse') + if len(closestmatches) and len(hyphen_args): # TODO + invalid_display.append("The following commands may match " + str(trigger_command) + ": " + spicemanip(closestmatches, 'andlist') + ".") + + # there is simply no command + else: + # TODO check other commands spelling, maybe there is a similar command + # invalid_display = ["I don't seem to have a command for " + str(trigger_command) + "!"] + # TODO + # invalid_display.append("If you have a suggestion for this command, you can run .feature ." + str(trigger_command)) + # invalid_display.append("ADD DESCRIPTION HERE!") + if not len(hyphen_args): + invalid_display = [] + if len(invalid_display): + bot.osd(invalid_display, trigger.nick, 'notice') return elif command_type == 'nickname': @@ -221,11 +271,38 @@ def bot_command_rule_ai(bot, trigger): # return # TODO + invalid_display = ["I don't know what you are asking me to do!"] + + # hyphen args handling + hyphen_args = [] + argssplit = spicemanip(fulltrigger, "2+", 'list') + for worditem in argssplit: + if str(worditem).startswith("--"): + clipped_word = str(worditem[2:]).lower() + # valid arg above + if clipped_word in SpiceBot.prerun_shared.valid_hyphen_args: + hyphen_args.append(clipped_word) + # numbered args + elif str(clipped_word).isdigit(): + hyphen_args.append(int(clipped_word)) + elif clipped_word in list(SpiceBot.prerun_shared.numdict.keys()): + hyphen_args.append(int(SpiceBot.prerun_shared.numdict[clipped_word])) + else: + # check if arg word is a number + try: + clipped_word = w2n.word_to_num(str(clipped_word)) + hyphen_args.append(int(clipped_word)) + # word is not a valid arg or number + except ValueError: + clipped_word = None + if len(hyphen_args): + hyphenarg = hyphen_args[0] + if hyphenarg: + invalid_display.append("Hyphen Argument Not Valid.") + closestmatches = SpiceBot.similar_list(trigger_command, list(SpiceBot.commands.dict['commands']["nickname"].keys()), 3, 'reverse') if len(closestmatches): closestmatches = spicemanip(closestmatches, "andlist") - bot.osd("I don't know what you are asking me to do! Did you mean: " + str(closestmatches) + "?") - return - else: - bot.osd("I don't know what you are asking me to do!") - return + invalid_display.append("Did you mean: " + str(closestmatches) + "?") + + bot.osd(invalid_display, trigger.nick, 'notice') diff --git a/sopel_modules/SpiceBot_Core_Commands/DictComs.py b/sopel_modules/SpiceBot_Core_Commands/DictComs.py index da9cd69..67e07ed 100644 --- a/sopel_modules/SpiceBot_Core_Commands/DictComs.py +++ b/sopel_modules/SpiceBot_Core_Commands/DictComs.py @@ -11,7 +11,6 @@ import sopel_modules.SpiceBot as SpiceBot from sopel_modules.spicemanip import spicemanip -from word2number import w2n from random import randint import time @@ -24,124 +23,14 @@ def command_dictcom(bot, trigger, botcom): return bot_dictcom_process(bot, trigger, botcom) + + # import hack TODO return time.time() def bot_dictcom_process(bot, trigger, botcom): - # use the default key, unless otherwise specified - botcom.dict["responsekey"] = "?default" - - # handling for special cases - posscom = spicemanip(botcom.dict['args'], 1) - if posscom.lower() in [command.lower() for command in list(botcom.dict["dict"].keys())]: - for command in botcom.dict["dict"].keys(): - if command.lower() == posscom.lower(): - posscom = command - botcom.dict["responsekey"] = posscom - botcom.dict['args'] = spicemanip(botcom.dict['args'], '2+', 'list') - botcom.dict["dict"][botcom.dict["responsekey"]]["type"] = botcom.dict["dict"][botcom.dict["responsekey"]]["type"] - - botcom.dict["nonstockoptions"] = [] - for command in list(botcom.dict["dict"].keys()): - if command not in ["?default", "validcoms", "type", "hardcoded_channel_block"]: - botcom.dict["nonstockoptions"].append(command) - - # This allows users to specify which reply by number by using an ! and a digit (first or last in string) - validspecifides = ['last', 'random', 'count', 'view', 'add', 'del', 'remove', 'special'] - botcom.dict["specified"] = None - argone = spicemanip(botcom.dict['args'], 1) - if str(argone).startswith("--") and len(str(argone)) > 2: - if str(argone[2:]).isdigit(): - botcom.dict["specified"] = int(argone[2:]) - elif SpiceBot.inlist(str(argone[2:]), validspecifides): - botcom.dict["specified"] = str(argone[2:]).lower() - elif SpiceBot.inlist(str(argone[2:]), botcom.dict["nonstockoptions"]): - botcom.dict["specified"] = str(argone[2:]).lower() - botcom.dict["responsekey"] = botcom.dict["specified"] - else: - try: - botcom.dict["specified"] = w2n.word_to_num(str(argone[1:])) - botcom.dict["specified"] = int(botcom.dict["specified"]) - except ValueError: - botcom.dict["specified"] = None - if botcom.dict["specified"]: - botcom.dict['args'] = spicemanip(botcom.dict['args'], '2+', 'list') - - # commands that can be updated - if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"]: - if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": - SpiceBot.dictcoms.adjust_nick_array(str(SpiceBot.config.nick), 'sayings', botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), botcom.dict["dict"][botcom.dict["responsekey"]]["responses"], 'startup') - botcom.dict["dict"][botcom.dict["responsekey"]]["responses"] = SpiceBot.db.get_nick_value(str(bot.nick), botcom.dict["dict"]["validcoms"][0] + "_" + str(botcom.dict["responsekey"]), 'sayings') or [] - elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": - SpiceBot.dictcoms.adjust_nick_array(str(trigger.nick), 'sayings', botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), botcom.dict["dict"][botcom.dict["responsekey"]]["responses"], 'startup') - botcom.dict["dict"][botcom.dict["responsekey"]]["responses"] = SpiceBot.db.get_nick_value(str(trigger.nick), botcom.dict["dict"]["validcoms"][0] + "_" + str(botcom.dict["responsekey"]), 'sayings') or [] - - if botcom.dict["specified"] == 'special': - nonstockoptions = spicemanip(botcom.dict["nonstockoptions"], "andlist") - return bot.osd("The special options for " + str(botcom.dict["realcom"]) + " command include: " + str(nonstockoptions) + ".") - - elif botcom.dict["specified"] == 'count': - return bot.osd("The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " command has " + str(len(botcom.dict["dict"][botcom.dict["responsekey"]]["responses"])) + " entries.") - - elif botcom.dict["specified"] == 'view': - if botcom.dict["dict"][botcom.dict["responsekey"]]["responses"] == []: - return bot.osd("The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " command appears to have no entries!") - else: - bot.osd("The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " command contains:", trigger.nick, 'notice') - listnumb, relist = 1, [] - for item in botcom.dict["dict"][botcom.dict["responsekey"]]["responses"]: - if isinstance(item, dict): - relist.append(str("[#" + str(listnumb) + "] COMPLEX dict Entry")) - elif isinstance(item, list): - relist.append(str("[#" + str(listnumb) + "] COMPLEX list Entry")) - else: - relist.append(str("[#" + str(listnumb) + "] " + str(item))) - bot.osd(relist, trigger.nick) - return - - elif botcom.dict["specified"] == 'add': - - if not botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"]: - return bot.osd("The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list cannot be updated.") - - fulltext = spicemanip(botcom.dict['args'], 0) - if not fulltext: - return bot.osd("What would you like to add to the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list?") - - if fulltext in botcom.dict["dict"][botcom.dict["responsekey"]]["responses"]: - return bot.osd("The following was already in the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") - - if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": - SpiceBot.dictcoms.adjust_nick_array(str(bot.nick), 'sayings', botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), fulltext, botcom.dict["specified"]) - elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": - SpiceBot.dictcoms.adjust_nick_array(str(trigger.nick), 'sayings', botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), fulltext, botcom.dict["specified"]) - - return bot.osd("The following was added to the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") - - elif botcom.dict["specified"] in ['del', 'remove']: - - if not botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"]: - return bot.osd("The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list cannot be updated.") - - fulltext = spicemanip(botcom.dict['args'], 0) - if not fulltext: - return bot.osd("What would you like to remove from the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list?") - - if fulltext not in botcom.dict["dict"][botcom.dict["responsekey"]]["responses"]: - return bot.osd("The following was already not in the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") - - if botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "shared": - SpiceBot.dictcoms.adjust_nick_array(str(bot.nick), 'sayings', botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), fulltext, botcom.dict["specified"]) - elif botcom.dict["dict"][botcom.dict["responsekey"]]["updates_enabled"] == "user": - SpiceBot.dictcoms.adjust_nick_array(str(trigger.nick), 'sayings', botcom.dict["realcom"] + "_" + str(botcom.dict["responsekey"]), fulltext, botcom.dict["specified"]) - - return bot.osd("The following was removed from the " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " entry list: '" + str(fulltext) + "'") - - elif botcom.dict["specified"] and not botcom.dict["dict"][botcom.dict["responsekey"]]["selection_allowed"]: - return bot.osd("The " + str(botcom.dict["realcom"]) + " " + str(botcom.dict["responsekey"] or '') + " response list cannot be specified.") - botcom.dict["target"] = False botcom.dict["success"] = True @@ -324,11 +213,7 @@ def bot_dictcom_reply_shared(bot, trigger, botcom): # display special options for this command if "$specialoptions" in rply: - nonstockoptions = [] - for command in list(botcom.dict["dict"].keys()): - if command not in ["?default", "validcoms", "contributors", "author", "type", "filepath", "filename", "hardcoded_channel_block", "description", "exampleresponse", "example", "usage", "privs"]: - nonstockoptions.append(command) - nonstockoptions = spicemanip(nonstockoptions, "andlist") + nonstockoptions = spicemanip(botcom.dict["dict"]["nonstockoptions"], "andlist") rply = rply.replace("$specialoptions", nonstockoptions) # saying, or action? diff --git a/sopel_modules/SpiceBot_Core_Commands/Logs.py b/sopel_modules/SpiceBot_Core_Commands/Logs.py index 2188961..f55241d 100644 --- a/sopel_modules/SpiceBot_Core_Commands/Logs.py +++ b/sopel_modules/SpiceBot_Core_Commands/Logs.py @@ -16,7 +16,7 @@ import sopel_modules.SpiceBot as SpiceBot @sopel.module.nickname_commands('logs', 'debug') def bot_command_logs(bot, trigger, botcom): - if not SpiceBot.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if not SpiceBot.users.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "I was unable to process this Bot Nick command due to privilege issues.") return diff --git a/sopel_modules/SpiceBot_Core_Commands/Messaging.py b/sopel_modules/SpiceBot_Core_Commands/Messaging.py index 510a38d..29e59a6 100644 --- a/sopel_modules/SpiceBot_Core_Commands/Messaging.py +++ b/sopel_modules/SpiceBot_Core_Commands/Messaging.py @@ -29,7 +29,7 @@ def bot_command_privmsg(bot, trigger, botcom): def bot_command_process(bot, trigger, botcom): - if not SpiceBot.command_permissions_check(bot, trigger, ['admins', 'owner']): + if not SpiceBot.users.command_permissions_check(bot, trigger, ['admins', 'owner']): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "I was unable to process this Bot Nick command due to privilege issues.") return diff --git a/sopel_modules/SpiceBot_Core_Commands/Runtime_Controls.py b/sopel_modules/SpiceBot_Core_Commands/Runtime_Controls.py index ed7ac5b..3294642 100644 --- a/sopel_modules/SpiceBot_Core_Commands/Runtime_Controls.py +++ b/sopel_modules/SpiceBot_Core_Commands/Runtime_Controls.py @@ -13,7 +13,7 @@ import sopel_modules.SpiceBot as SpiceBot @sopel.module.nickname_commands('update') def nickname_comand_update(bot, trigger, botcom): - if not SpiceBot.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): + if not SpiceBot.users.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "I was unable to process this Bot Nick command due to privilege issues.") return