Job signoffs now functional under this branch

This commit is contained in:
Jacob Henry 2019-03-01 00:25:33 -05:00
parent e74c9dcaa6
commit 7f1e105843
2 changed files with 57 additions and 42 deletions

View File

@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Match, Callable, TypeVar, Optional, Iterable from typing import List, Match, Callable, TypeVar, Optional, Iterable, Any, Coroutine
from fuzzywuzzy import fuzz from fuzzywuzzy import fuzz
@ -60,7 +60,7 @@ class _ModJobContext:
async def _mod_jobs(event: slack_util.Event, async def _mod_jobs(event: slack_util.Event,
relevance_scorer: Callable[[house_management.JobAssignment], Optional[float]], relevance_scorer: Callable[[house_management.JobAssignment], Optional[float]],
modifier: Callable[[_ModJobContext], None], modifier: Callable[[_ModJobContext], Coroutine[Any, Any, None]],
no_job_msg: str = None no_job_msg: str = None
) -> None: ) -> None:
""" """
@ -98,7 +98,7 @@ async def _mod_jobs(event: slack_util.Event,
context = _ModJobContext(signer, fresh_targ_assign) context = _ModJobContext(signer, fresh_targ_assign)
# Modify it # Modify it
modifier(context) await modifier(context)
# Re-upload # Re-upload
await house_management.export_assignments(fresh_assigns) await house_management.export_assignments(fresh_assigns)
@ -164,13 +164,13 @@ async def signoff_callback(event: slack_util.Event, match: Match) -> None:
return r return r
# Set the assigner, and notify # Set the assigner, and notify
def modifier(context: _ModJobContext): async def modifier(context: _ModJobContext):
context.assign.signer = context.signer context.assign.signer = context.signer
# Say we did it wooo! # Say we did it wooo!
slack_util.get_slack().reply(event, "Signed off {} for {}".format(context.assign.assignee.name, slack_util.get_slack().reply(event, "Signed off {} for {}".format(context.assign.assignee.name,
context.assign.job.name)) context.assign.job.name))
alert_user(context.assign.assignee, "{} signed you off for {}.".format(context.assign.signer.name, await alert_user(context.assign.assignee, "{} signed you off for {}.".format(context.assign.signer.name,
context.assign.job.pretty_fmt())) context.assign.job.pretty_fmt()))
# Fire it off # Fire it off
@ -192,13 +192,13 @@ async def undo_callback(event: slack_util.Event, match: Match) -> None:
return r return r
# Set the assigner to be None, and notify # Set the assigner to be None, and notify
def modifier(context: _ModJobContext): async def modifier(context: _ModJobContext):
context.assign.signer = None context.assign.signer = None
# Say we did it wooo! # Say we did it wooo!
slack_util.get_slack().reply(event, "Undid signoff of {} for {}".format(context.assign.assignee.name, slack_util.get_slack().reply(event, "Undid signoff of {} for {}".format(context.assign.assignee.name,
context.assign.job.name)) context.assign.job.name))
alert_user(context.assign.assignee, "{} undid your signoff off for {}.\n" await alert_user(context.assign.assignee, "{} undid your signoff off for {}.\n"
"Must have been a mistake".format(context.assign.signer.name, "Must have been a mistake".format(context.assign.signer.name,
context.assign.job.pretty_fmt())) context.assign.job.pretty_fmt()))
@ -221,7 +221,7 @@ async def late_callback(event: slack_util.Event, match: Match) -> None:
return r return r
# Just set the assigner # Just set the assigner
def modifier(context: _ModJobContext): async def modifier(context: _ModJobContext):
context.assign.late = not context.assign.late context.assign.late = not context.assign.late
# Say we did it # Say we did it
@ -253,7 +253,7 @@ async def reassign_callback(event: slack_util.Event, match: Match) -> None:
return r return r
# Change the assignee # Change the assignee
def modifier(context: _ModJobContext): async def modifier(context: _ModJobContext):
context.assign.assignee = to_bro context.assign.assignee = to_bro
# Say we did it # Say we did it
@ -266,8 +266,8 @@ async def reassign_callback(event: slack_util.Event, match: Match) -> None:
reassign_msg = "Job {} reassigned from {} to {}".format(context.assign.job.pretty_fmt(), reassign_msg = "Job {} reassigned from {} to {}".format(context.assign.job.pretty_fmt(),
from_bro, from_bro,
to_bro) to_bro)
alert_user(from_bro, reassign_msg) await alert_user(from_bro, reassign_msg)
alert_user(to_bro, reassign_msg) await alert_user(to_bro, reassign_msg)
# Fire it off # Fire it off
await _mod_jobs(event, scorer, modifier) await _mod_jobs(event, scorer, modifier)
@ -360,7 +360,7 @@ signoff_hook = slack_util.ChannelHook(signoff_callback,
r"signoff\s+(.*)", r"signoff\s+(.*)",
r"sign off\s+(.*)", r"sign off\s+(.*)",
], ],
channel_whitelist=["#housejobs"]) channel_whitelist=["#botzone"])
undo_hook = slack_util.ChannelHook(undo_callback, undo_hook = slack_util.ChannelHook(undo_callback,
patterns=[ patterns=[

View File

@ -1,5 +1,5 @@
from __future__ import annotations from __future__ import annotations
from aiohttp import web
import asyncio import asyncio
import re import re
import sys import sys
@ -129,6 +129,23 @@ def message_stream(slack: SlackClient) -> Generator[Event, None, None]:
# Handle each # Handle each
for update in update_list: for update in update_list:
print("Message received: {}".format(update)) print("Message received: {}".format(update))
yield dict_to_event(update)
except (SlackNotConnected, OSError) as e:
print("Error while reading messages:")
print(e)
except (ValueError, TypeError) as e:
print("Malformed message... Restarting connection")
print(e)
sleep(5)
print("Connection failed - retrying")
def dict_to_event(update: dict) -> Event:
"""
Converts a dict update to an actual event.
"""
event = Event() event = Event()
# Big logic folks # Big logic folks
@ -144,20 +161,9 @@ def message_stream(slack: SlackClient) -> Generator[Event, None, None]:
if "thread_ts" in update: if "thread_ts" in update:
event.thread = ThreadContext(update["thread_ts"]) event.thread = ThreadContext(update["thread_ts"])
# TODO: Handle more types # TODO: Handle more types of events, including http data etc.
# We need to
yield event return event
except (SlackNotConnected, OSError) as e:
print("Error while reading messages:")
print(e)
except (ValueError, TypeError) as e:
print("Malformed message... Restarting connection")
print(e)
sleep(5)
print("Connection failed - retrying")
""" """
@ -254,8 +260,19 @@ class ClientWrapper(object):
yield await asyncio.get_running_loop().run_in_executor(None, get_one) yield await asyncio.get_running_loop().run_in_executor(None, get_one)
async def http_event_feed(self) -> AsyncGenerator[Event, None]: async def http_event_feed(self) -> AsyncGenerator[Event, None]:
# Create a callback to convert requests to events
async def interr(request: web.Request):
return web.Response()
# Create the server # Create the server
pass app = web.Application()
app.add_routes([web.get('/bothttpcallback/', interr)])
# Asynchronously serve that boy up
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, 'localhost', 8080)
await site.start()
while True: while True:
await asyncio.sleep(30) await asyncio.sleep(30)
@ -293,14 +310,14 @@ class ClientWrapper(object):
def get_conversation_by_name(self, conversation_identifier: str) -> Optional[Conversation]: def get_conversation_by_name(self, conversation_identifier: str) -> Optional[Conversation]:
# If looking for a direct message, first lookup user, then fetch # If looking for a direct message, first lookup user, then fetch
if conversation_identifier[0] == "@": if conversation_identifier[0] == "@":
user_name = conversation_identifier[1:] user_name = conversation_identifier
# Find the user by their name # Find the user by their name
raise NotImplementedError("There wasn't a clear use case for this yet, so we've opted to just not use it") raise NotImplementedError("There wasn't a clear use case for this yet, so we've opted to just not use it")
# If looking for a channel, just lookup normally # If looking for a channel, just lookup normally
elif conversation_identifier[0] == "#": elif conversation_identifier[0] == "#":
channel_name = conversation_identifier[1:] channel_name = conversation_identifier
# Find the channel in the dict # Find the channel in the dict
for channel in self.conversations.values(): for channel in self.conversations.values():
@ -383,10 +400,10 @@ class ClientWrapper(object):
for channel_dict in channel_dicts["channels"]: for channel_dict in channel_dicts["channels"]:
if channel_dict["is_im"]: if channel_dict["is_im"]:
new_channel = DirectMessage(id=channel_dict["id"], new_channel = DirectMessage(id=channel_dict["id"],
user_id=channel_dict["user"]) user_id="@" + channel_dict["user"])
else: else:
new_channel = Channel(id=channel_dict["id"], new_channel = Channel(id=channel_dict["id"],
name=channel_dict["name"]) name="#" + channel_dict["name"])
new_dict[new_channel.id] = new_channel new_dict[new_channel.id] = new_channel
# Fetch the cursor # Fetch the cursor
@ -513,8 +530,6 @@ class ChannelHook(AbsHook):
if not (event.conversation and event.message and isinstance(event.conversation.get_conversation(), Channel)): if not (event.conversation and event.message and isinstance(event.conversation.get_conversation(), Channel)):
return None return None
# Fail if pattern invalid # Fail if pattern invalid
match = None match = None
for p in self.patterns: for p in self.patterns: