From 8b58939ff54b0c01582fbfdaf281f6cfd14e96bc Mon Sep 17 00:00:00 2001 From: Jacob Henry Date: Fri, 14 Sep 2018 01:04:41 -0400 Subject: [PATCH] Added black/white lists --- channel_util.py | 9 ++++---- dummy.py | 2 +- identifier.py | 16 ++++++++------ job_nagger.py | 12 +++++----- main.py | 50 ++++++++++++++++++------------------------ management_commands.py | 21 +++++++++--------- scroll_util.py | 12 +++++----- slack_util.py | 46 +++++++++++++++++++++++++++++++++++++- slavestothemachine.py | 0 9 files changed, 103 insertions(+), 65 deletions(-) create mode 100644 slavestothemachine.py diff --git a/channel_util.py b/channel_util.py index 5028097..848f2c1 100644 --- a/channel_util.py +++ b/channel_util.py @@ -10,10 +10,8 @@ BOTZONE = "C3BF2MFKM" NOT_ALLOWED_HERE = "There's a time and place for everything, but not here!" -# Define our patterns -channel_check_pattern = r"channel id\s*(.*)" - +# Callback for telling what channel we in def channel_check_callback(slack, msg, match): # Sets the users scroll # with shelve.open(DB_NAME) as db: @@ -26,4 +24,7 @@ def channel_check_callback(slack, msg, match): response = "" response += "Channel id: {}\n".format(msg["channel"]) response += "Escaped message: {}\n".format(rest_of_msg) - slack_util.reply(slack, msg, response) \ No newline at end of file + slack_util.reply(slack, msg, response) + + +channel_check_hook = slack_util.Hook(channel_check_callback, pattern=r"channel id\s*(.*)") diff --git a/dummy.py b/dummy.py index 8bd43b1..e570d3a 100644 --- a/dummy.py +++ b/dummy.py @@ -5,7 +5,7 @@ msg = { "type": "message", "channel": channel_util.COMMAND_CENTER_ID, "user": "U0Q1PKL92", - "text": "nagjobs tuesday", + "text": "my name", "ts": "1355517523.000005" } diff --git a/identifier.py b/identifier.py index 77f0248..b4cb148 100644 --- a/identifier.py +++ b/identifier.py @@ -10,13 +10,7 @@ import scroll_util # The following db maps SLACK_USER_ID -> SCROLL_INTEGER DB_NAME = "user_scrolls" -# Initialize the db -identify_pattern = r"i am (.*)" -identify_other_pattern = r"<@(.*)>\s+has scroll\s+(.*)" -check_pattern = r"my scroll.*" -name_pattern = r"my name.*" - - +# Initialize the hooks NON_REG_MSG = ("You currently have no scroll registered. To register, type\n" "i am 666\n" "except with your scroll instead of 666") @@ -61,6 +55,7 @@ def identify_other_callback(slack, msg, match): slack_util.reply(slack, msg, result) +# noinspection PyUnusedLocal def check_callback(slack, msg, match): # Tells the user their current scroll with shelve.open(DB_NAME) as db: @@ -72,6 +67,7 @@ def check_callback(slack, msg, match): slack_util.reply(slack, msg, result) +# noinspection PyUnusedLocal def name_callback(slack, msg, match): # Tells the user what slack thinks their name is with shelve.open(DB_NAME) as db: @@ -116,3 +112,9 @@ def lookup_brother_userids(brother): result.append(user_id) return result + + +identify_hook = slack_util.Hook(identify_callback, pattern=r"i am (.*)") +identify_other_hook = slack_util.Hook(identify_other_callback, pattern=r"<@(.*)>\s+has scroll\s+(.*)") +check_hook = slack_util.Hook(check_callback, pattern=r"my scroll") +name_hook = slack_util.Hook(name_callback, pattern=r"my name") diff --git a/job_nagger.py b/job_nagger.py index d45264c..3c90bcd 100644 --- a/job_nagger.py +++ b/job_nagger.py @@ -4,11 +4,8 @@ import slack_util import google_api import channel_util -nag_pattern = r"nagjobs\s*(.*)" - - SHEET_ID = "1lPj9GjB00BuIq9GelOWh5GmiGsheLlowPnHLnWBvMOM" -eight_job_range = "House Jobs!A2:C25" # Format: Job Day Bro +eight_job_range = "House Jobs!A2:C25" # Format: Job Day Bro fiftythree_job_range = "House Jobs!E2:G6" @@ -48,10 +45,13 @@ def nag_callback(slack, msg, match): response += "({}) {} -- ".format(job.house, job.job_name) ids = job.lookup_brother_slack_id() if ids: - for id in ids: - response += "<@{}> ".format(id) + for slack_id in ids: + response += "<@{}> ".format(slack_id) else: response += "{} (scroll missing. Please register for @ pings!)".format(job.brother_name) response += "\n" slack_util.reply(slack, msg, response, in_thread=False, to_channel=channel_util.GENERAL) + + +nag_hook = slack_util.Hook(nag_callback, pattern=r"nagjobs\s*(.*)") diff --git a/main.py b/main.py index aff3350..7e743fb 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,11 @@ -from collections import OrderedDict - -import google_api as google # For read drive from slackclient import SlackClient # Obvious -import slack_util -import scroll_util -import identifier -import re import channel_util +import identifier import job_nagger import management_commands +import scroll_util +import slack_util from dummy import FakeClient # Read api token from file @@ -22,39 +18,38 @@ DEBUG_MODE = False def main(): - wrapper = ClientWrapper() + wrap = ClientWrapper() # DEBUG: Add blanked handling # wrapper.add_hook(".*", print) # Add scroll handling - wrapper.add_hook(scroll_util.command_pattern, scroll_util.callback) + wrap.add_hook(scroll_util.scroll_hook) # Add id handling - wrapper.add_hook(identifier.check_pattern, identifier.check_callback) - wrapper.add_hook(identifier.identify_pattern, identifier.identify_callback) - wrapper.add_hook(identifier.identify_other_pattern, identifier.identify_other_callback) - wrapper.add_hook(identifier.name_pattern, identifier.name_callback) + wrap.add_hook(identifier.check_hook) + wrap.add_hook(identifier.identify_hook) + wrap.add_hook(identifier.identify_other_hook) + wrap.add_hook(identifier.name_hook) # Added channel utility - wrapper.add_hook(channel_util.channel_check_pattern, channel_util.channel_check_callback) + wrap.add_hook(channel_util.channel_check_hook) # Add nagging functionality - wrapper.add_hook(job_nagger.nag_pattern, job_nagger.nag_callback) + wrap.add_hook(job_nagger.nag_hook) # Add kill switch - wrapper.add_hook(management_commands.reboot_pattern, management_commands.reboot_callback) + wrap.add_hook(management_commands.reboot_hook) # Add help - help_callback = management_commands.list_hooks_callback_gen(wrapper.hooks.keys()) - wrapper.add_hook(management_commands.bot_help_pattern, help_callback) + help_callback = management_commands.list_hooks_callback_gen(wrap.hooks) + wrap.add_hook(slack_util.Hook(help_callback, pattern=management_commands.bot_help_pattern)) - wrapper.listen() + wrap.listen() # Callback to list command hooks - class ClientWrapper(object): def __init__(self): # Init slack @@ -64,10 +59,10 @@ class ClientWrapper(object): self.slack = SlackClient(SLACK_API) # Hooks go regex -> callback on (slack, msg, match) - self.hooks = OrderedDict() + self.hooks = [] - def add_hook(self, pattern, callback): - self.hooks[pattern] = callback + def add_hook(self, hook): + self.hooks.append(hook) def listen(self): feed = slack_util.message_stream(self.slack) @@ -78,19 +73,16 @@ class ClientWrapper(object): if msg.get("subtype") is not None: continue - # Never deal with general + # Never deal with general, EVER! if msg.get("channel") == channel_util.GENERAL: continue # Handle Message text = msg['text'].strip() success = False - for regex, callback in self.hooks.items(): - match = re.match(regex, text, flags=re.IGNORECASE) - if match: + for hook in self.hooks: + if hook.check(self.slack, msg): success = True - print("Matched on callback {}".format(callback)) - callback(self.slack, msg, match) break if not success: diff --git a/management_commands.py b/management_commands.py index f969c47..8cedd93 100644 --- a/management_commands.py +++ b/management_commands.py @@ -1,23 +1,17 @@ -import identifier -import scroll_util -import slack_util -import google_api import channel_util - -bot_help_pattern = r"bot help" +import slack_util -def list_hooks_callback_gen(hook_strings): +def list_hooks_callback_gen(hooks): + # noinspection PyUnusedLocal def callback(slack, msg, match): - slack_util.reply(slack, msg, "\n".join(hook_strings)) + slack_util.reply(slack, msg, "\n".join(hook.pattern for hook in hooks)) return callback # Gracefully reboot to reload code changes -reboot_pattern = r"reboot" - - +# noinspection PyUnusedLocal def reboot_callback(slack, msg, match): if msg["channel"] != channel_util.COMMAND_CENTER_ID: response = channel_util.NOT_ALLOWED_HERE @@ -29,3 +23,8 @@ def reboot_callback(slack, msg, match): slack_util.reply(slack, msg, response) if reboot: exit(0) + + +# Make hooks +bot_help_pattern = r"bot help" # Can't init this directly, as it relies on us knowing all other hooks. handle in main +reboot_hook = slack_util.Hook(reboot_callback, pattern=r"reboot") diff --git a/scroll_util.py b/scroll_util.py index 69be0d7..5b92828 100644 --- a/scroll_util.py +++ b/scroll_util.py @@ -7,14 +7,11 @@ import re from fuzzywuzzy import process -from slack_util import reply +import slack_util # load the family tree familyfile = open("sortedfamilytree.txt", 'r') - -command_pattern = r"scroll\s+(.*)" - # Parse out brother_match = re.compile(r"([0-9]*)~(.*)") brothers = [brother_match.match(line) for line in familyfile] @@ -25,7 +22,7 @@ brothers = [{ } for m in brothers] -def callback(slack, msg, match): +def scroll_callback(slack, msg, match): # Get the query query = match.group(1).strip() @@ -41,7 +38,7 @@ def callback(slack, msg, match): result = "Couldn't find brother {}".format(query) # Respond - reply(slack, msg, result) + slack_util.reply(slack, msg, result) def find_by_scroll(scroll): @@ -57,3 +54,6 @@ def find_by_name(name): # Do fuzzy match return process.extractOne(name, brothers, processor=lambda b: b["name"])[0] + + +scroll_hook = slack_util.Hook(scroll_callback, pattern=r"scroll\s+(.*)") diff --git a/slack_util.py b/slack_util.py index 336a742..a9f37e8 100644 --- a/slack_util.py +++ b/slack_util.py @@ -1,4 +1,5 @@ from time import sleep +import re """ Slack helpers. Separated for compartmentalization @@ -38,4 +39,47 @@ def message_stream(slack): yield item sleep(15) - print("Connection failed - retrying") \ No newline at end of file + print("Connection failed - retrying") + + +class Hook(object): + def __init__(self, callback, pattern=None, channel_whitelist=None, channel_blacklist=None): + # Save all + self.pattern = pattern + self.channel_whitelist = channel_whitelist + self.channel_blacklist = channel_blacklist + self.callback = callback + + # Remedy some sensible defaults + if self.pattern is None: + self.pattern = ".*" + + if self.channel_blacklist is None: + import channel_util + self.channel_blacklist = [channel_util.GENERAL] + elif self.channel_whitelist is None: + pass # We leave as none to show no whitelisting in effect + else: + raise Exception("Cannot whitelist and blacklist") + + def check(self, slack, msg): + # Fail if pattern invalid + match = re.match(self.pattern, msg['text'], flags=re.IGNORECASE) + if match is None: + print("Missed pattern") + return False + + # Fail if whitelist defined, and we aren't there + if self.channel_whitelist is not None and msg["channel"] not in self.channel_whitelist: + print("Missed whitelist") + return False + + # Fail if blacklist defined, and we are there + if self.channel_blacklist is not None and msg["channel"] in self.channel_blacklist: + print("Hit blacklist") + return False + + # Otherwise do callback and return success + print("Matched on pattern {} callback {}".format(self.pattern, self.callback)) + self.callback(slack, msg, match) + return True diff --git a/slavestothemachine.py b/slavestothemachine.py new file mode 100644 index 0000000..e69de29