From dbc3f9e6a833814e98a2a5b6ec0f7e53b9f0e518 Mon Sep 17 00:00:00 2001 From: deathbybandaid Date: Wed, 2 Oct 2019 11:27:07 -0400 Subject: [PATCH] channel changes --- sopel_modules/SpiceBot/Channels.py | 268 ++++++++++++------ sopel_modules/SpiceBot/Prerun.py | 2 +- .../SpiceBot_Core_Commands/Channels.py | 33 +-- .../SpiceBot_Core_Commands/Messaging.py | 2 +- .../SpiceBot_Core_Processing/Channels.py | 22 +- .../StartupMonologue.py | 4 +- 6 files changed, 212 insertions(+), 119 deletions(-) diff --git a/sopel_modules/SpiceBot/Channels.py b/sopel_modules/SpiceBot/Channels.py index 160cd25..01cb762 100644 --- a/sopel_modules/SpiceBot/Channels.py +++ b/sopel_modules/SpiceBot/Channels.py @@ -12,7 +12,6 @@ import threading from .Config import config as botconfig from .Database import db as botdb -from .Users import users as botusers class SpiceBot_Channels_MainSection(StaticSection): @@ -21,19 +20,30 @@ class SpiceBot_Channels_MainSection(StaticSection): operadmin = ValidatedAttribute('operadmin', default=False) chanignore = ListAttribute('chanignore') +# replace process lock with uuid list system + class BotChannels(): """This Logs all channels known to the server""" def __init__(self): + + # bot config setup self.setup_channels() - self.who_reqs = {} + + # Locking self.lock = threading.Lock() self.channel_lock = False - self.channels = [] - self.dict = { - "list": {}, - "InitialProcess": False - } + + # list of channels + self.chandict = {} + + # list of uuids to process + self.server_recieved = [] + + # initial process + self.InitialProcess = False + + self.who_reqs = {} def __getattr__(self, name): ''' will only get called for undefined attributes ''' @@ -43,22 +53,51 @@ class BotChannels(): else: raise Exception('Channel dict does not contain a function or key ' + str(name.lower())) - def get_channel_users(self, channel, idtype="nick"): + def total_channels(self): + totalcount = len(list(self.chandict.keys())) + return totalcount - if channel.lower() not in list(self.dict["list"].keys()): - raise Exception('Channel ' + str(channel.lower()) + " does not seem to be on this server") - if 'users' not in list(self.dict["list"][channel.lower()].keys()) or not self.check_channel_bot(channel): - raise Exception('Channel ' + str(channel.lower()) + " does not seem to be a channel the bot is in") + def total_bot_channels(self): + totalcount = 0 + for channel in list(self.chandict.keys()): + if self.chandict[channel]["joined"]: + totalcount += 1 + return totalcount - nick_ids = self.dict["list"][channel.lower()]['users'] - if idtype.upper() == "ID": - return nick_ids + def chanlist(self): + return list(self.chandict.keys()) + + def chanlist_bot(self): + chanlist_return = [] + for channel in list(self.chandict.keys()): + if self.chandict[channel]["joined"]: + chanlist_return.append(channel) + return chanlist_return + + def ischannel(self, channel): + + # channel names case-sensitivity should never be an issue + channel = str(channel).lower() + + if channel in list(self.chandict.keys()): + return True else: - nick_list = [] - for nick_id in nick_ids: - nickname = botusers.ID(nick_id) - nick_list.append(nickname) - return nick_list + return False + + def get_channel_users(self, channel): + + # channel names case-sensitivity should never be an issue + channel = str(channel).lower() + + # channel not on server + if channel not in list(self.chandict.keys()): + raise Exception('Channel ' + str(channel) + " does not seem to be on this server") + + # bot is not in channel + if not self.chandict[channel]["joined"]: + raise Exception('Channel ' + str(channel) + " does not seem to be a channel the bot is in") + + return self.chandict[channel]["users"] def setup_channels(self): botconfig.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False) @@ -70,81 +109,120 @@ class BotChannels(): self.channel_lock = True def channel_list_recieve_input(self, trigger): - self.lock.acquire() + + # parse info channel, _, topic = trigger.args[1:] - if channel.lower() not in list(self.dict['list'].keys()): - self.dict['list'][channel.lower()] = dict() - self.dict['list'][channel.lower()]['name'] = channel - self.dict['list'][channel.lower()]['topic'] = self.topic_compile(topic) + + # check channel existance + if channel not in list(self.chandict.keys()): + self.channeldict_append(channel) + + self.lock.acquire() + + # get proper name of channel + self.chandict[channel.lower()]['name'] = channel + self.chandict[channel.lower()]['topic'] = self.topic_compile(topic) self.channel_lock = True self.lock.release() def channel_list_recieve_finish(self): self.lock.acquire() - if not self.dict['InitialProcess']: - self.dict['InitialProcess'] = True - self.lock.release() + if not self.InitialProcess: + self.InitialProcess = True self.channel_lock = False + self.lock.release() def topic_compile(self, topic): actual_topic = re.compile(r'^\[\+[a-zA-Z]+\] (.*)') topic = re.sub(actual_topic, r'\1', topic) + if topic.isspace(): + topic = None return topic - def add_channel(self, channel): + def channeldict_append(self, channel): + + # channel names case-sensitivity should never be an issue channel = str(channel).lower() + + # create basic components of the channel dict self.lock.acquire() - if channel not in self.channels: - self.channels.append(channel) + if channel not in list(self.chandict.keys()): + self.chandict[channel] = { + "name": channel, + "topic": None, + "joined": False, + "reason": "appended", + "users": [], + } self.lock.release() - def remove_channel(self, channel): + def add_channel(self, channel, reason=None): + + # channel names case-sensitivity should never be an issue channel = str(channel).lower() + + # check channel existance + if channel not in list(self.chandict.keys()): + self.channeldict_append(channel) + self.lock.acquire() - if channel in self.channels: - self.channels.remove(channel) + if reason != "check": + self.chandict[channel]["joined"] = True + self.chandict[channel]["reason"] = reason + self.lock.release() + + def remove_channel(self, channel, reason=None): + + # channel names case-sensitivity should never be an issue + channel = str(channel).lower() + + # check channel existance + if channel not in list(self.chandict.keys()): + self.channeldict_append(channel) + + self.lock.acquire() + if reason != "check": + self.chandict[channel]["joined"] = False + self.chandict[channel]["reason"] = reason self.lock.release() def bot_part_empty(self, bot): """Don't stay in empty channels""" - ignorepartlist = [] + ignorepartlist = [] # TODO add bot.config channels to ignore if bot.config.core.logging_channel: ignorepartlist.append(bot.config.core.logging_channel) for channel in list(bot.channels.keys()): - if len(list(bot.channels[channel].privileges.keys())) == 1 and channel not in ignorepartlist and channel.startswith("#"): - bot.part(channel, "Leaving Empty Channel") - if channel.lower() in self.dict['list']: - self.lock.acquire() - del self.dict['list'][channel.lower()] - self.lock.release() - self.remove_channel(channel) + if channel.startswith("#") and channel not in ignorepartlist: + if len(list(bot.channels[channel].privileges.keys())) == 1: + bot.part(channel, "Leaving Empty Channel") + if channel.lower() in list(self.chandict.keys()): + self.lock.acquire() + self.chandict[channel]["joined"] = False + self.chandict[channel]["reason"] = "empty_chan_part" + self.lock.release() + self.remove_channel(channel) def join_all_channels(self, bot): if botconfig.SpiceBot_Channels.joinall: - for channel in list(self.dict['list'].keys()): - if channel.startswith("#"): - if channel not in list(bot.channels.keys()) and channel not in botconfig.SpiceBot_Channels.chanignore: - bot.write(('JOIN', bot.nick, self.dict['list'][channel]['name'])) - if channel not in list(bot.channels.keys()) and botconfig.SpiceBot_Channels.operadmin: - bot.write(('SAJOIN', bot.nick, self.dict['list'][channel]['name'])) + for channel in list(self.chandict.keys()): + if channel.startswith("#") and channel not in botconfig.SpiceBot_Channels.chanignore: + if channel.lower() not in [chan.lower() for chan in list(bot.channels.keys())]: + self.bot_join(bot, channel) + if channel.lower() not in [chan.lower() for chan in list(bot.channels.keys())]: + self.bot_sajoin(bot, channel) - if channel in list(bot.channels.keys()): - self.add_channel(channel) + def bot_join(self, bot, channel): + bot.write(('JOIN', bot.nick, channel)) - def chanadmin_all_channels(self, bot): + def bot_sajoin(self, bot, channel): + if botconfig.SpiceBot_Channels.operadmin: + bot.write(('SAJOIN', bot.nick, channel)) + + def chanadmin_channel(self, bot, channel): # Chan ADMIN +a - for channel in list(bot.channels.keys()): - if channel.startswith("#"): - self.add_channel(channel) - if channel not in botconfig.SpiceBot_Channels.chanignore: - if botconfig.SpiceBot_Channels.operadmin: - if not bot.channels[channel].privileges[bot.nick] < sopel.module.ADMIN: - bot.write(('SAMODE', channel, "+a", bot.nick)) - else: - bot.part(channel) - - if channel not in list(bot.channels.keys()): - self.remove_channel(channel) + if botconfig.SpiceBot_Channels.operadmin: + if not bot.channels[channel].privileges[bot.nick] < sopel.module.ADMIN: + bot.write(('SAMODE', channel, "+a", bot.nick)) def whois_ident(self, nick): nick = Identifier(nick) @@ -152,43 +230,48 @@ class BotChannels(): return nick_id def add_to_channel(self, channel, nick, nick_id=None): + + # channel names case-sensitivity should never be an issue channel = str(channel).lower() + + # Verify Channel is in list + if channel not in list(self.chandict.keys()): + self.add_channel(channel, "error_handling") + self.lock.acquire() if not nick_id: nick_id = self.whois_ident(nick) - if channel.lower() not in list(self.dict['list'].keys()): - self.dict['list'][channel.lower()] = dict() - if 'users' not in self.dict['list'][channel.lower()]: - self.dict['list'][channel.lower()]['users'] = [] - if int(nick_id) not in self.dict['list'][channel.lower()]['users']: - self.dict['list'][channel.lower()]['users'].append(int(nick_id)) - if channel.lower() not in self.channels: - self.channels.append(channel.lower()) + if int(nick_id) not in self.chandict[channel]["users"]: + self.chandict[channel]["users"].append(int(nick_id)) self.lock.release() def remove_from_channel(self, channel, nick, nick_id=None): + + # channel names case-sensitivity should never be an issue channel = str(channel).lower() + + # Verify Channel is in list + if channel not in list(self.chandict.keys()): + self.add_channel(channel, "error_handling") + self.lock.acquire() if not nick_id: nick_id = self.whois_ident(nick) - if channel.lower() not in list(self.dict['list'].keys()): - self.dict['list'][channel.lower()] = dict() - if 'users' not in self.dict['list'][channel.lower()]: - self.dict['list'][channel.lower()]['users'] = [] - if int(nick_id) in self.dict['list'][channel.lower()]['users']: - self.dict['list'][channel.lower()]['users'].remove(int(nick_id)) + if int(nick_id) in self.chandict[channel.lower()]['users']: + self.chandict[channel.lower()]['users'].remove(int(nick_id)) self.lock.release() def remove_all_from_channel(self, channel): + + # channel names case-sensitivity should never be an issue channel = str(channel).lower() + + # Verify Channel is in list + if channel not in list(self.chandict.keys()): + self.add_channel(channel, "error_handling") + self.lock.acquire() - if channel.lower() not in list(self.dict['list'].keys()): - self.dict['list'][channel.lower()] = dict() - if 'users' not in self.dict['list'][channel.lower()]: - self.dict['list'][channel.lower()]['users'] = [] - self.dict['list'][channel.lower()]['users'] = [] - if channel.lower() in self.channels: - self.channels.remove(channel.lower()) + self.chandict[channel.lower()]['users'] = [] self.lock.release() def channel_scan(self, bot): @@ -199,6 +282,7 @@ class BotChannels(): def join(self, bot, trigger): # bot block if trigger.nick == bot.nick: + self.chanadmin_channel(bot, trigger.sender) for user in list(bot.channels[trigger.sender].privileges.keys()): self.add_to_channel(trigger.sender, user) return @@ -211,6 +295,10 @@ class BotChannels(): # bot block if trigger.nick == bot.nick: self.remove_all_from_channel(trigger.sender) + self.lock.acquire() + self.chandict[trigger.sender]["joined"] = False + self.chandict[trigger.sender]["reason"] = "quit" + self.lock.release() return # Identify nick_id = self.whois_ident(trigger.nick) @@ -221,6 +309,10 @@ class BotChannels(): # bot block if trigger.nick == bot.nick: self.remove_all_from_channel(trigger.sender) + self.lock.acquire() + self.chandict[trigger.sender]["joined"] = False + self.chandict[trigger.sender]["reason"] = "bot_part" + self.lock.release() return # Identify nick_id = self.whois_ident(trigger.nick) @@ -232,6 +324,10 @@ class BotChannels(): # bot block if targetnick == bot.nick: self.remove_all_from_channel(trigger.sender) + self.lock.acquire() + self.chandict[trigger.sender]["joined"] = False + self.chandict[trigger.sender]["reason"] = "kicked" + self.lock.release() return # Identify nick_id = self.whois_ident(targetnick) @@ -287,9 +383,9 @@ class BotChannels(): self.add_to_channel(channel, nick) def check_channel_bot(self, channel, allchan=False): - if str(channel).lower() in self.channels: + if str(channel).lower() in self.chanlist_bot(): return True - elif allchan and str(channel).lower() in list(self.dict['list'].keys()): + elif allchan and str(channel).lower() in self.chanlist(): return True else: return False diff --git a/sopel_modules/SpiceBot/Prerun.py b/sopel_modules/SpiceBot/Prerun.py index 7a5e5c7..305c443 100644 --- a/sopel_modules/SpiceBot/Prerun.py +++ b/sopel_modules/SpiceBot/Prerun.py @@ -212,7 +212,7 @@ def verify_user(bot, trigger): def verify_channel(trigger): - botchannels.add_channel(trigger.sender) + botchannels.add_channel(trigger.sender, "check") # Identify nick_id = botchannels.whois_ident(trigger.nick) # Verify nick is in the channel list diff --git a/sopel_modules/SpiceBot_Core_Commands/Channels.py b/sopel_modules/SpiceBot_Core_Commands/Channels.py index 83cd269..ccd3d9b 100644 --- a/sopel_modules/SpiceBot_Core_Commands/Channels.py +++ b/sopel_modules/SpiceBot_Core_Commands/Channels.py @@ -22,23 +22,23 @@ def nickname_comand_channels(bot, trigger, botcom): botcom.dict['args'] = spicemanip(botcom.dict['args'], '2+', 'list') if commandused == 'list': - botcount = len(list(bot.channels.keys())) - servercount = len(list(SpiceBot.channels.dict['list'].keys())) + botcount = SpiceBot.channels.total_bot_channels() + servercount = SpiceBot.channels.total_channels() displayval = "I am in " + str(botcount) + " of " + str(servercount) + " channel(s) available on this server." - chanlist = spicemanip(list(bot.channels.keys()), 'andlist') + chanlist = spicemanip(SpiceBot.channels.chanlist_bot(), 'andlist') bot.osd([displayval, "You can find me in " + chanlist]) return elif commandused == 'total': - botcount = len(list(bot.channels.keys())) - servercount = len(list(SpiceBot.channels.dict['list'].keys())) + botcount = SpiceBot.channels.total_bot_channels() + servercount = SpiceBot.channels.total_channels() bot.osd("I am in " + str(botcount) + " of " + str(servercount) + " channel(s) available on this server.") return elif commandused == 'random': - channel = spicemanip(SpiceBot.channels.dict['list'], 'random') - topic = SpiceBot.channels.dict['list'][channel]['topic'] - msg = ["Random channel for you: {}.".format(SpiceBot.channels.dict['list'][channel]['name'])] + channel = spicemanip(list(SpiceBot.channels.chandict), 'random') + topic = SpiceBot.channels.chandict[channel]['topic'] + msg = ["Random channel for you: {}.".format(SpiceBot.channels.chandict[channel]['name'])] if topic and not topic.isspace(): msg.append("The topic is: {}".format(topic)) else: @@ -47,7 +47,7 @@ def nickname_comand_channels(bot, trigger, botcom): return elif commandused == 'update': - if not trigger.admin: + if not trigger.admin: # TODO SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "You do not have permission to update the channel listing.") return SpiceBot.channels.bot_part_empty(bot) @@ -56,7 +56,7 @@ def nickname_comand_channels(bot, trigger, botcom): while SpiceBot.channels.channel_lock: pass SpiceBot.channels.join_all_channels(bot) - foundchannelcount = len(list(SpiceBot.channels.dict['list'].keys())) + foundchannelcount = len(list(SpiceBot.channels.chandict.keys())) bot.osd("[SpiceBot_Channels]", "Channel listing finished!", str(foundchannelcount) + " channel(s) found.") SpiceBot.channels.bot_part_empty(bot) return @@ -66,12 +66,13 @@ def nickname_comand_channels(bot, trigger, botcom): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "Channel name input missing.") return channel = spicemanip(botcom.dict['args'], 1) - if not SpiceBot.inlist(channel.lower(), list(SpiceBot.channels.dict['list'].keys())): + if not SpiceBot.inlist(channel.lower(), list(SpiceBot.channels.chandict.keys())): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "Channel name {} not valid.".format(channel)) return - topic = SpiceBot.channels.dict['list'][channel.lower()]['topic'] - channel = SpiceBot.channels.dict['list'][channel.lower()]['name'] - bot.osd("Topic for {}: {}".format(channel, topic)) + channel = str(channel).lower() + topic = SpiceBot.channels.chandict[channel]['topic'] + channelname = SpiceBot.channels.chandict[channel]['name'] + bot.osd("Topic for {}: {}".format(channelname, topic)) return if commandused.upper() in ['OP', 'HOP', 'VOICE', 'OWNER', 'ADMIN']: @@ -83,7 +84,7 @@ def nickname_comand_channels(bot, trigger, botcom): channel = trigger.sender else: channel = spicemanip(botcom.dict['args'], 1) - if not SpiceBot.inlist(channel.lower(), list(SpiceBot.channels.dict['list'].keys())): + if not SpiceBot.inlist(channel.lower(), list(SpiceBot.channels.chandict.keys())): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "Channel name {} not valid.".format(channel)) return if not SpiceBot.inlist(channel.lower(), list(bot.channels.keys())): @@ -103,7 +104,7 @@ def nickname_comand_channels(bot, trigger, botcom): # Users List if commandused == 'users': channel = spicemanip(botcom.dict['args'], 1) - if not SpiceBot.inlist(channel.lower(), list(SpiceBot.channels.dict['list'].keys())): + if not SpiceBot.inlist(channel.lower(), list(SpiceBot.channels.chandict.keys())): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "Channel name {} not valid.".format(channel)) return if not SpiceBot.inlist(channel.lower(), list(bot.channels.keys())): diff --git a/sopel_modules/SpiceBot_Core_Commands/Messaging.py b/sopel_modules/SpiceBot_Core_Commands/Messaging.py index 29e59a6..f89783b 100644 --- a/sopel_modules/SpiceBot_Core_Commands/Messaging.py +++ b/sopel_modules/SpiceBot_Core_Commands/Messaging.py @@ -42,7 +42,7 @@ def bot_command_process(bot, trigger, botcom): target = spicemanip(botcom.dict['args'], 1) if (target not in ['allchans', 'allnicks'] - and not SpiceBot.inlist(target.lower(), list(SpiceBot.channels.dict['list'].keys())) + and not SpiceBot.channels.ischannel(target) and not SpiceBot.inlist(target.lower(), bot.users)): SpiceBot.messagelog.messagelog_error(botcom.dict["log_id"], "Channel/nick name {} not valid.".format(target)) return diff --git a/sopel_modules/SpiceBot_Core_Processing/Channels.py b/sopel_modules/SpiceBot_Core_Processing/Channels.py index c82ec7e..c8f4dd6 100644 --- a/sopel_modules/SpiceBot_Core_Processing/Channels.py +++ b/sopel_modules/SpiceBot_Core_Processing/Channels.py @@ -31,21 +31,17 @@ def request_channels_list_initial(bot, trigger): starttime = time.time() # wait 60 seconds for initial population of information - while not SpiceBot.channels.dict['InitialProcess']: + while not SpiceBot.channels.InitialProcess: if time.time() - starttime >= 60: SpiceBot.logs.log('SpiceBot_Channels', "Initial Channel list populating Timed Out") - SpiceBot.channels.dict['InitialProcess'] = True + SpiceBot.channels.InitialProcess = True else: pass - foundchannelcount = len(list(SpiceBot.channels.dict['list'].keys())) + foundchannelcount = SpiceBot.channels.total_channels() SpiceBot.logs.log('SpiceBot_Channels', "Channel listing finished! " + str(foundchannelcount) + " channel(s) found.") SpiceBot.channels.join_all_channels(bot) - SpiceBot.channels.chanadmin_all_channels(bot) - - if "*" in SpiceBot.channels.dict['list'].keys(): - del SpiceBot.channels.dict['list']["*"] SpiceBot.channels.bot_part_empty(bot) SpiceBot.events.trigger(bot, SpiceBot.events.BOT_CHANNELS, "SpiceBot_Channels") @@ -76,13 +72,17 @@ def trigger_channel_list_recurring(bot, trigger): time.sleep(1800) SpiceBot.channels.bot_part_empty(bot) - oldlist = list(SpiceBot.channels.dict['list'].keys()) + oldlist = SpiceBot.channels.chanlist() SpiceBot.channels.channel_list_request(bot) while SpiceBot.channels.channel_lock: pass - newlist = [item.lower() for item in oldlist if item.lower() not in list(SpiceBot.channels.dict['list'].keys())] + currentlist = SpiceBot.channels.chanlist() + newlist = [] + for channel in currentlist: + if channel not in oldlist: + newlist.append(channel) if "*" in newlist: newlist.remove("*") if len(newlist) and SpiceBot.config.SpiceBot_Channels.announcenew: @@ -90,10 +90,6 @@ def trigger_channel_list_recurring(bot, trigger): SpiceBot.channels.join_all_channels(bot) - SpiceBot.channels.chanadmin_all_channels(bot) - - if "*" in list(SpiceBot.channels.dict['list'].keys()): - del SpiceBot.channels.dict['list']["*"] SpiceBot.channels.bot_part_empty(bot) diff --git a/sopel_modules/SpiceBot_Core_Processing/StartupMonologue.py b/sopel_modules/SpiceBot_Core_Processing/StartupMonologue.py index 2e50bdb..61fb9d9 100644 --- a/sopel_modules/SpiceBot_Core_Processing/StartupMonologue.py +++ b/sopel_modules/SpiceBot_Core_Processing/StartupMonologue.py @@ -30,8 +30,8 @@ def bot_startup_monologue_sopel_version(bot, trigger): @sopel.module.event(SpiceBot.events.BOT_CHANNELS) @sopel.module.rule('.*') def bot_startup_monologue_channels(bot, trigger): - botcount = len(list(bot.channels.keys())) - servercount = len(list(SpiceBot.channels.dict['list'].keys())) + botcount = SpiceBot.channels.total_bot_channels() + servercount = SpiceBot.channels.total_channels() displayval = "I am in " + str(botcount) + " of " + str(servercount) + " channel(s) available on this server." SpiceBot.startupmonologue.dict["channels"] = displayval SpiceBot.logs.log('SpiceBot_StartupMonologue', displayval)