Semi-stable? Pingme works. Stability of websockets under asyncio uncertain

This commit is contained in:
Jacob Henry 2018-11-08 20:05:20 -05:00
parent 42f2a64967
commit 5f834e3409
8 changed files with 99 additions and 31 deletions

View File

@ -6,6 +6,7 @@ import slack_util
# Useful channels # Useful channels
GENERAL = "C0CFHPNEM" GENERAL = "C0CFHPNEM"
RANDOM = "C0CFDQWUW"
COMMAND_CENTER_ID = "GCR631LQ1" COMMAND_CENTER_ID = "GCR631LQ1"
SLAVES_TO_THE_MACHINE_ID = "C9WUQBYNP" SLAVES_TO_THE_MACHINE_ID = "C9WUQBYNP"
BOTZONE = "C3BF2MFKM" BOTZONE = "C3BF2MFKM"
@ -26,4 +27,5 @@ async def channel_check_callback(slack: SlackClient, msg: dict, match: Match) ->
slack_util.reply(slack, msg, response) slack_util.reply(slack, msg, response)
channel_check_hook = slack_util.Hook(channel_check_callback, pattern=r"channel id\s*(.*)") channel_check_hook = slack_util.Hook(channel_check_callback,
pattern=r"channel id\s*(.*)")

View File

@ -136,7 +136,7 @@ def lookup_brother_userids(brother: scroll_util.Brother) -> List[str]:
return result return result
identify_hook = slack_util.Hook(identify_callback, pattern=r"i am (.*)") identify_hook = slack_util.Hook(identify_callback, pattern=r"my scroll is (.*)")
identify_other_hook = slack_util.Hook(identify_other_callback, pattern=r"<@(.*)>\s+has scroll\s+(.*)") 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") check_hook = slack_util.Hook(check_callback, pattern=r"what is my scroll")
name_hook = slack_util.Hook(name_callback, pattern=r"my name") name_hook = slack_util.Hook(name_callback, pattern=r"what is my name")

36
main.py
View File

@ -1,5 +1,5 @@
import asyncio import asyncio
from typing import List, Any, Match, AsyncGenerator from typing import List, Any, AsyncGenerator
from slackclient import SlackClient # Obvious from slackclient import SlackClient # Obvious
@ -8,6 +8,7 @@ import identifier
import job_nagger import job_nagger
import job_signoff import job_signoff
import management_commands import management_commands
import periodicals
import scroll_util import scroll_util
import slack_util import slack_util
import slavestothemachine import slavestothemachine
@ -56,16 +57,14 @@ def main() -> None:
help_callback = management_commands.list_hooks_callback_gen(wrap.hooks) help_callback = management_commands.list_hooks_callback_gen(wrap.hooks)
wrap.add_hook(slack_util.Hook(help_callback, pattern=management_commands.bot_help_pattern)) wrap.add_hook(slack_util.Hook(help_callback, pattern=management_commands.bot_help_pattern))
# Schedule? # Add boozebot
async def do_later(slk: SlackClient, msg: dict, match: Match): wrap.add_passive(periodicals.ItsTenPM())
time = int(match.group(1))
await asyncio.sleep(time)
slack_util.reply(slk, msg, "hello!")
wrap.add_hook(slack_util.Hook(do_later, "pingme\s+(\d+)"))
event_loop = asyncio.get_event_loop() event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(wrap.listen()) message_handling = wrap.respond_messages()
passive_handling = wrap.run_passives()
both = asyncio.gather(message_handling, passive_handling)
event_loop.run_until_complete(both)
class ClientWrapper(object): class ClientWrapper(object):
@ -88,10 +87,26 @@ class ClientWrapper(object):
# Hooks go regex -> callback on (slack, msg, match) # Hooks go regex -> callback on (slack, msg, match)
self.hooks: List[slack_util.Hook] = [] self.hooks: List[slack_util.Hook] = []
# Periodicals are just wrappers around an iterable, basically
self.passives: List[slack_util.Passive] = []
# Scheduled events handling
def add_passive(self, per: slack_util.Passive) -> None:
self.passives.append(per)
async def run_passives(self) -> None:
# Make a task to repeatedly spawn each event
awaitables = [p.run(self.slack) for p in self.passives]
await asyncio.gather(*awaitables)
# Message handling
def add_hook(self, hook: slack_util.Hook) -> None: def add_hook(self, hook: slack_util.Hook) -> None:
self.hooks.append(hook) self.hooks.append(hook)
async def listen(self): async def respond_messages(self) -> None:
"""
Asynchronous tasks that eternally reads and responds to messages.
"""
async for _ in self.spool_tasks(): async for _ in self.spool_tasks():
print("Handling a message...!") print("Handling a message...!")
@ -108,6 +123,7 @@ class ClientWrapper(object):
# Strip garbage # Strip garbage
msg['text'] = msg['text'].strip() msg['text'] = msg['text'].strip()
print("Recv: \"{}\"".format(msg['text']))
# Handle debug # Handle debug
if msg['text'][:6] == "DEBUG ": if msg['text'][:6] == "DEBUG ":

View File

@ -24,4 +24,6 @@ async def reboot_callback(slack: SlackClient, msg: dict, match: Match) -> None:
# Make hooks # 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 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", channel_whitelist=[channel_util.COMMAND_CENTER_ID]) reboot_hook = slack_util.Hook(reboot_callback,
pattern=r"reboot",
channel_whitelist=[channel_util.COMMAND_CENTER_ID])

30
periodicals.py Normal file
View File

@ -0,0 +1,30 @@
import asyncio
from datetime import datetime
from slackclient import SlackClient
import channel_util
import slack_util
def seconds_until(target: datetime) -> float:
curr = datetime.now()
delta = target - curr
return delta.seconds
class ItsTenPM(slack_util.Passive):
async def run(self, slack: SlackClient) -> None:
while True:
# Get 10PM
ten_pm = datetime.now().replace(hour=21, minute=0, second=0)
# Find out how long until it, then sleep that long
delay = seconds_until(ten_pm)
await asyncio.sleep(delay)
# Crow like a rooster
slack_util.reply(slack, {}, "IT'S 10 PM!", in_thread=False, to_channel=channel_util.RANDOM)
# Wait a while before trying it again, to prevent duplicates
await asyncio.sleep(60)

View File

@ -20,6 +20,9 @@ class Brother(object):
self.name = name self.name = name
self.scroll = scroll self.scroll = scroll
def __repr__(self):
return "<Brother {}-{}>".format(self.name, self.scroll)
# load the family tree # load the family tree
familyfile = open("sortedfamilytree.txt", 'r') familyfile = open("sortedfamilytree.txt", 'r')

View File

@ -1,7 +1,7 @@
import asyncio
import re import re
from asyncio import Task
from time import sleep from time import sleep
from typing import Any, Optional, Generator, Match, Callable, List, Coroutine from typing import Any, Optional, Generator, Match, Callable, List, Coroutine, AsyncGenerator
from slackclient import SlackClient from slackclient import SlackClient
@ -70,14 +70,20 @@ def message_stream(slack: SlackClient) -> Generator[dict, None, None]:
""" """
# Do forever # Do forever
while True: while True:
if slack.rtm_connect(with_team_state=False, auto_reconnect=True): try:
print("Waiting for messages") if slack.rtm_connect(with_team_state=False, auto_reconnect=True):
while True: print("Waiting for messages")
sleep(1) while True:
update = slack.rtm_read() sleep(1)
for item in update: update = slack.rtm_read()
if item.get('type') == 'message': for item in update:
yield item if item.get('type') == 'message':
yield item
except OSError as e:
print("Error while reading messages:")
print(e)
except (ValueError, TypeError):
print("Malformed message... Restarting connection")
sleep(5) sleep(5)
print("Connection failed - retrying") print("Connection failed - retrying")
@ -90,7 +96,7 @@ Callback = Callable[[SlackClient, dict, Match], MsgAction]
class Hook(object): class Hook(object):
def __init__(self, def __init__(self,
callback: Callback, callback: Callback,
pattern: str = None, pattern: str,
channel_whitelist: Optional[List[str]] = None, channel_whitelist: Optional[List[str]] = None,
channel_blacklist: Optional[List[str]] = None): channel_blacklist: Optional[List[str]] = None):
# Save all # Save all
@ -100,9 +106,6 @@ class Hook(object):
self.callback = callback self.callback = callback
# Remedy some sensible defaults # Remedy some sensible defaults
if self.pattern is None:
self.pattern = ".*"
if self.channel_blacklist is None: if self.channel_blacklist is None:
import channel_util import channel_util
self.channel_blacklist = [channel_util.GENERAL] self.channel_blacklist = [channel_util.GENERAL]
@ -135,3 +138,12 @@ class Hook(object):
def invoke(self, slack: SlackClient, msg: dict, match: Match): def invoke(self, slack: SlackClient, msg: dict, match: Match):
return self.callback(slack, msg, match) return self.callback(slack, msg, match)
class Passive(object):
"""
Base class for Periodical tasks, such as reminders and stuff
"""
async def run(self, slack: SlackClient) -> None:
# Run this passive routed through the specified slack client.
raise NotImplementedError()

View File

@ -89,6 +89,9 @@ async def dump_work_callback(slack: SlackClient, msg: dict, match: Match) -> Non
# Make dem HOOKs # Make dem HOOKs
count_work_hook = slack_util.Hook(count_work_callback, channel_whitelist=[channel_util.SLAVES_TO_THE_MACHINE_ID]) count_work_hook = slack_util.Hook(count_work_callback,
dump_work_hook = slack_util.Hook(dump_work_callback, pattern="dump towel data", pattern=".*",
channel_whitelist=[channel_util.SLAVES_TO_THE_MACHINE_ID])
dump_work_hook = slack_util.Hook(dump_work_callback,
pattern="dump towel data",
channel_whitelist=[channel_util.COMMAND_CENTER_ID]) channel_whitelist=[channel_util.COMMAND_CENTER_ID])