From 42f2a64967757ae32d1287cb068b628545b0329f Mon Sep 17 00:00:00 2001 From: Jacob Henry Date: Thu, 8 Nov 2018 02:11:58 -0500 Subject: [PATCH] Semi-stable? Pingme works. Stability of websockets under asyncio uncertain --- main.py | 46 +++++++++++++++++++++++++++++----------------- slack_util.py | 20 ++++++++++++-------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/main.py b/main.py index 53785a2..4740989 100644 --- a/main.py +++ b/main.py @@ -1,17 +1,16 @@ -import typing -from typing import List +import asyncio +from typing import List, Any, Match, AsyncGenerator from slackclient import SlackClient # Obvious import channel_util import identifier import job_nagger +import job_signoff import management_commands import scroll_util import slack_util import slavestothemachine -import job_signoff -import asyncio from dummy import FakeClient # Read api token from file @@ -58,7 +57,7 @@ def main() -> None: wrap.add_hook(slack_util.Hook(help_callback, pattern=management_commands.bot_help_pattern)) # Schedule? - async def do_later(slk: SlackClient, msg: dict, match: typing.Match): + async def do_later(slk: SlackClient, msg: dict, match: Match): time = int(match.group(1)) await asyncio.sleep(time) slack_util.reply(slk, msg, "hello!") @@ -92,11 +91,13 @@ class ClientWrapper(object): def add_hook(self, hook: slack_util.Hook) -> None: self.hooks.append(hook) - async def listen(self) -> None: - feed = self.async_message_feed() - async for msg in feed: - print(msg) + async def listen(self): + async for _ in self.spool_tasks(): + print("Handling a message...!") + async def spool_tasks(self) -> AsyncGenerator[asyncio.Task, Any]: + async for msg in self.async_message_feed(): + # Preprocess msg # We only care about standard messages, not subtypes, as those usually just channel activity if msg.get("subtype") is not None: continue @@ -105,10 +106,10 @@ class ClientWrapper(object): if msg.get("channel") == channel_util.GENERAL: continue - # Handle Message + # Strip garbage msg['text'] = msg['text'].strip() - # If first few letters DEBUG, use debug slack + # Handle debug if msg['text'][:6] == "DEBUG ": slack_to_use = self.debug_slack msg['text'] = msg['text'][6:] @@ -116,16 +117,27 @@ class ClientWrapper(object): else: slack_to_use = self.slack - success = False + # Msg is good + # Find which hook, if any, satisfies + sat_hook = None + sat_match = None for hook in self.hooks: - if await hook.check(slack_to_use, msg): - success = True + match = hook.check(msg) + if match is not None: + sat_match = match + sat_hook = hook break - if not success: - print("No hit on {}".format(msg['text'])) + # If no hooks, continue + if not sat_hook: + continue - async def async_message_feed(self) -> typing.AsyncGenerator[dict, None]: + # Throw up as a task, otherwise + coro = sat_hook.invoke(slack_to_use, msg, sat_match) + task = asyncio.create_task(coro) + yield task + + async def async_message_feed(self) -> AsyncGenerator[dict, None]: """ Async wrapper around the message feed. Yields messages awaitably forever. diff --git a/slack_util.py b/slack_util.py index db7cc2f..d84dc08 100644 --- a/slack_util.py +++ b/slack_util.py @@ -1,3 +1,4 @@ +import asyncio import re from time import sleep from typing import Any, Optional, Generator, Match, Callable, List, Coroutine @@ -110,24 +111,27 @@ class Hook(object): else: raise Exception("Cannot whitelist and blacklist") - async def check(self, slack: SlackClient, msg: dict) -> bool: + def check(self, msg: dict) -> Optional[Match]: + """ + Returns whether a message should be handled by this dict, returning a Match if so, or None + """ # Fail if pattern invalid match = re.match(self.pattern, msg['text'], flags=re.IGNORECASE) if match is None: # print("Missed pattern") - return False + return None # 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 + return None # 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 + return None - # Otherwise do callback and return success - print("Matched on pattern {} callback {}".format(self.pattern, self.callback)) - await self.callback(slack, msg, match) - return True + return match + + def invoke(self, slack: SlackClient, msg: dict, match: Match): + return self.callback(slack, msg, match)