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
GENERAL = "C0CFHPNEM"
RANDOM = "C0CFDQWUW"
COMMAND_CENTER_ID = "GCR631LQ1"
SLAVES_TO_THE_MACHINE_ID = "C9WUQBYNP"
BOTZONE = "C3BF2MFKM"
@ -26,4 +27,5 @@ async def channel_check_callback(slack: SlackClient, msg: dict, match: Match) ->
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
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+(.*)")
check_hook = slack_util.Hook(check_callback, pattern=r"my scroll")
name_hook = slack_util.Hook(name_callback, pattern=r"my name")
check_hook = slack_util.Hook(check_callback, pattern=r"what is my scroll")
name_hook = slack_util.Hook(name_callback, pattern=r"what is my name")

36
main.py
View File

@ -1,5 +1,5 @@
import asyncio
from typing import List, Any, Match, AsyncGenerator
from typing import List, Any, AsyncGenerator
from slackclient import SlackClient # Obvious
@ -8,6 +8,7 @@ import identifier
import job_nagger
import job_signoff
import management_commands
import periodicals
import scroll_util
import slack_util
import slavestothemachine
@ -56,16 +57,14 @@ def main() -> None:
help_callback = management_commands.list_hooks_callback_gen(wrap.hooks)
wrap.add_hook(slack_util.Hook(help_callback, pattern=management_commands.bot_help_pattern))
# Schedule?
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!")
wrap.add_hook(slack_util.Hook(do_later, "pingme\s+(\d+)"))
# Add boozebot
wrap.add_passive(periodicals.ItsTenPM())
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):
@ -88,10 +87,26 @@ class ClientWrapper(object):
# Hooks go regex -> callback on (slack, msg, match)
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:
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():
print("Handling a message...!")
@ -108,6 +123,7 @@ class ClientWrapper(object):
# Strip garbage
msg['text'] = msg['text'].strip()
print("Recv: \"{}\"".format(msg['text']))
# Handle 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
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.scroll = scroll
def __repr__(self):
return "<Brother {}-{}>".format(self.name, self.scroll)
# load the family tree
familyfile = open("sortedfamilytree.txt", 'r')

View File

@ -1,7 +1,7 @@
import asyncio
import re
from asyncio import Task
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
@ -70,6 +70,7 @@ def message_stream(slack: SlackClient) -> Generator[dict, None, None]:
"""
# Do forever
while True:
try:
if slack.rtm_connect(with_team_state=False, auto_reconnect=True):
print("Waiting for messages")
while True:
@ -78,6 +79,11 @@ def message_stream(slack: SlackClient) -> Generator[dict, None, None]:
for item in update:
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)
print("Connection failed - retrying")
@ -90,7 +96,7 @@ Callback = Callable[[SlackClient, dict, Match], MsgAction]
class Hook(object):
def __init__(self,
callback: Callback,
pattern: str = None,
pattern: str,
channel_whitelist: Optional[List[str]] = None,
channel_blacklist: Optional[List[str]] = None):
# Save all
@ -100,9 +106,6 @@ class Hook(object):
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]
@ -135,3 +138,12 @@ class Hook(object):
def invoke(self, slack: SlackClient, msg: dict, match: 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
count_work_hook = slack_util.Hook(count_work_callback, channel_whitelist=[channel_util.SLAVES_TO_THE_MACHINE_ID])
dump_work_hook = slack_util.Hook(dump_work_callback, pattern="dump towel data",
count_work_hook = slack_util.Hook(count_work_callback,
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])