def test_add_section(): fake_slack = Slack(slack_token="fake") fake_slack.add_section_block(text="Fake section") assert isinstance(fake_slack.blocks, list) assert "type" in fake_slack.blocks[0].keys() assert "text" in fake_slack.blocks[0].keys() assert isinstance(fake_slack.blocks[0].get("text"), dict)
def __init__(self, payload, config): self.payload = payload try: self.params = self.payload["text"][0].split() except KeyError: log.info("No parameters received. Defaulting to help action") self.params = ["help"] self.config = config self.bot_access_token = config["bot_access_token"] self.slack = Slack(slack_token=config["bot_access_token"]) self.response_url = self.payload["response_url"][0]
def test_remind_user_without_lock(): when(requests).get(url=f"{fake_config['backend_url']}/users", **kwargs).thenReturn( mock({ "status_code": 200, "text": json.dumps(dict(testuser="******")) })) when(requests).get( url=f"{fake_config['backend_url']}/users/testuser/locks", **kwargs, ).thenReturn(mock({ "status_code": 200, "text": json.dumps([]) })) when(requests).post(url="https://slack.com/api/chat.postMessage", **kwargs).thenReturn(mock({"status_code": 200})) remind_users(Slack(fake_config["bot_access_token"]), fake_config["backend_url"]) verify(requests, times=1).post(url="https://slack.com/api/chat.postMessage", **kwargs) unstub()
def remind_users(slack: Slack, backend_url: str, only_for_user_ids: Optional[List[str]] = None) -> List[str]: api_url = f"{backend_url}/users" res = requests.get(url=api_url) if res.status_code != 200: logger.error(f"Failed to load users: {res.text}") return month = last_month() reminded_users = [] for user_id, name in json.loads(res.text).items(): if only_for_user_ids and user_id not in only_for_user_ids: continue lock_url = f"{backend_url}/users/{user_id}/locks" lock_res = requests.get(url=lock_url) if res.status_code != 200: logger.error(f"Failed to load user locks {user_id}: {res.text}") continue locked = False for lock in json.loads(lock_res.text): if lock.get("event_date") == month: locked = True if not locked: slack_res = slack.post_message( channel=user_id, message=f":unlock: You have not locked {month}", as_user=False, ) if slack_res.status_code != 200: logger.error(f"Failed to notify slack: {slack_res.text}") continue reminded_users.append(user_id) return reminded_users
def test_remind_user_with_lock(): when(requests).get(url=f"{fake_config['backend_url']}/users", **kwargs).thenReturn( mock({ "status_code": 200, "text": json.dumps(dict(testuser="******")) })) lock_evt = dict(event_date=last_month(), user_id="testuser") when(requests).get( url=f"{fake_config['backend_url']}/users/testuser/locks", **kwargs, ).thenReturn(mock({ "status_code": 200, "text": json.dumps([lock_evt]) })) with expect(requests, times=0).post(...): remind_users(Slack(fake_config["bot_access_token"]), fake_config["backend_url"]) unstub()
def check_user_locks(event): if config["enable_lock_reminder"]: remind_users( slack=Slack(slack_token=config["bot_access_token"]), backend_url=config["backend_url"], )
class Action: def __init__(self, payload, config): self.payload = payload try: self.params = self.payload["text"][0].split() except KeyError: log.info("No parameters received. Defaulting to help action") self.params = ["help"] self.config = config self.bot_access_token = config["bot_access_token"] self.slack = Slack(slack_token=config["bot_access_token"]) self.response_url = self.payload["response_url"][0] def perform_action(self): """ Perform action. Supported actions are: add - Add new post in timereport edit - Not implemented yet delete - Delete post in timereport list - List posts in timereport lock - Not implemented yet help - Provide this helpful output """ self.action = self.params[0] log.debug(f"Action is: {self.action}") self.user_id = self.payload["user_id"][0] if self.action == "add": return self._add_action() if self.action == "edit": return self._edit_action() if self.action == "delete": return self._delete_action() if self.action == "list": return self._list_action() if self.action == "lock": return self._lock_action() if self.action == "help": return self._help_action() return self._unsupported_action() def _unsupported_action(self): log.info(f"Action: {self.action} is not supported") return self.send_response(message=f"Unsupported action: {self.action}") def _add_action(self): events = factory(self.payload) if not events: return self.send_response( message="Wrong arguments for add command") log.info(f"Events is: {events}") user_name = events[0].get("user_name")[0] reason = events[0].get("reason") if not reason in self.config.get('valid_reasons'): message = f"Reason {reason} is not valid" log.debug(message) self.send_response(message=message) return "" self.date_start = events[0].get("event_date") self.date_end = events[-1].get("event_date") hours = events[0].get("hours") if self.check_lock_state(): self.send_response(message="One or more of the events are locked") return "" self.send_attachment(attachment=submit_message_menu( user_name, reason, self.date_start, self.date_end, hours)) return "" def _list_action(self): """ List timereport for user. If no arguments supplied it will default to all. Supported arguments: "today" - List the event for the todays date "date" - The date as a string. """ arguments = self.params[1:] log.debug(f"Got arguments: {arguments}") try: if arguments[0] == "today": date_str = datetime.now().strftime("%Y-%m-%d") else: date_str = arguments[0] except IndexError: month = datetime.now().strftime("%Y-%m") # A hack to set the date_str to the current month date_str = f"{month}-01:{month}-31" arguments = self.params[1:] except Exception as error: log.debug(f"got unexpected exception: {error}", exc_info=True) self.send_response( message=f"Got unexpected error with arguments: {arguments}") return "" log.debug(f"The date string set to: {date_str}") list_data = self._get_events(date_str=date_str) if not list_data or list_data == '[]': log.debug(f"List returned nothing. Date string was: {date_str}") self.send_response( message= f"Sorry, nothing to list with supplied argument {arguments}") return "" self.send_response(message=f"{list_data}") return "" def _delete_action(self): date = self.params[1] self.send_attachment(attachment=delete_message_menu( self.payload.get("user_name")[0], date)) return "" def _edit_action(self): return self.send_response(message="Edit not implemented yet") def send_response(self, message): """ Send a response to slack :message: The Message to send """ log.debug("Sending message to slack") if not self.slack.is_conversation_open: log.debug("Need to open slack conversation") self.slack.open_conversation(slack_user_id=self.user_id) if not self.slack.slack_dm_channel: log.debug("Need to find slack direct message channel ID") self.slack.open_conversation(slack_user_id=self.user_id) self.slack.send_message(message=message) return "" def send_attachment(self, attachment): """ Send an message to slack using attachment :attachment: The attachment (slack specific attachment) to send """ slack_client_response = slack_client_responder( token=self.bot_access_token, user_id=self.slack.slack_dm_channel, attachment=attachment, ) if slack_client_response.status_code != 200: log.error( f"""Failed to send response to slack. Status code was: {slack_client_response.status_code}. The response from slack was: {slack_client_response.text}""") return "Slack response to user failed" else: log.debug( f"Slack client response was: {slack_client_response.text}") return slack_client_response def _help_action(self): return self.send_response(message=f"{self.perform_action.__doc__}") def check_lock_state(self): """ Go through events and check if locked Return true if any locked events found """ for event in json.loads( self._get_events( date_str=f"{self.date_start}:{self.date_end}")): if event.get("lock"): return True return False def _get_events(self, date_str): return get_list_data( f"{self.config['backend_url']}", self.user_id, date_str=date_str, ) def _lock_action(self): """ /timereport-dev lock 2019-08 """ event = create_lock(user_id=self.user_id, event_date=self.params[1]) log.debug(f"lock event: {event}") response = lock_event(url=self.config['backend_url'], event=json.dumps(event)) log.debug(f"response was: {response.text}") if response.status_code == 200: self.send_response(message=f"Lock successful! :lock: :+1:") return "" else: self.send_response(message=f"Lock failed! :cry:") return ""
def test_slack_class(): test = Slack(slack_token="fake_token") assert test.slack_token assert test.client
from chalicelib.lib.helpers import parse_config from chalicelib.action import Action app = Chalice(app_name='timereport') app.debug = True logger = logging.getLogger() dir_path = os.path.dirname(os.path.realpath(__file__)) config = parse_config(f'{dir_path}/chalicelib/config.yaml') config['backend_url'] = os.getenv('backend_url') config['bot_access_token'] = os.getenv('bot_access_token') config['signing_secret'] = os.getenv('signing_secret') logger.setLevel(config['log_level']) slack = Slack(slack_token=config["bot_access_token"]) @app.route('/interactive', methods=['POST'], content_types=['application/x-www-form-urlencoded']) def interactive(): req = app.current_request.raw_body.decode() payload = slack_payload_extractor(req) selection = payload.get('actions')[0].get('value') user_id = payload['user']['id'] if not slack.is_conversation_open: logger.debug("Need to open slack conversation") slack.open_conversation(slack_user_id=user_id)
def test_add_divider(): fake_slack = Slack(slack_token="fake") fake_slack.add_divider_block() assert isinstance(fake_slack.blocks, list) assert "type" in fake_slack.blocks[0].keys() assert "block_id" in fake_slack.blocks[0].keys()
import os from mockito import when, mock, unstub from .test_data import fake_request_body import requests from chalicelib.lib.slack import ( slack_payload_extractor, verify_token, submit_message_menu, delete_message_menu, slack_client_responder, slack_responder, Slack, ) fake_slack = Slack(slack_token="fake") def test_slack_payload_extractor_command(): fake_data = slack_payload_extractor( "command=bar&text=fake+text&user_id=fake_user_id&response_url=http://example.com&user_name=mrFaky" ) assert isinstance(fake_data, dict) assert fake_data.get("command") == "bar" assert fake_data.get("text") == "fake text" assert fake_data.get("user_id") assert fake_data.get("response_url") def test_slack_payload_extractor_payload(): fake_data = slack_payload_extractor( "payload=%7B%0D%0A+++%22fake%22%3A+%22fake+data%22%0D%0A%7D")