from typing import Any, Mapping, Optional, Tuple from zerver.lib.exceptions import UnexpectedWebhookEventType SUPPORTED_BOARD_ACTIONS = [ 'removeMemberFromBoard', 'addMemberToBoard', 'createList', 'updateBoard', ] REMOVE_MEMBER = 'removeMemberFromBoard' ADD_MEMBER = 'addMemberToBoard' CREATE_LIST = 'createList' CHANGE_NAME = 'changeName' TRELLO_BOARD_URL_TEMPLATE = '[{board_name}]({board_url})' ACTIONS_TO_MESSAGE_MAPPER = { REMOVE_MEMBER: 'removed {member_name} from {board_url_template}.', ADD_MEMBER: 'added {member_name} to {board_url_template}.', CREATE_LIST: 'added {list_name} list to {board_url_template}.', CHANGE_NAME: 'renamed the board from {old_name} to {board_url_template}.', } def process_board_action(payload: Mapping[str, Any], action_type: Optional[str]) -> Optional[Tuple[str, str]]: action_type = get_proper_action(payload, action_type) if action_type is not None: return get_subject(payload), get_body(payload, action_type) return None def get_proper_action(payload: Mapping[str, Any], action_type: Optional[str]) -> Optional[str]: if action_type == 'updateBoard': data = get_action_data(payload) # we don't support events for when a board's background # is changed if data['old'].get('prefs', {}).get('background') is not None: return None elif data['old']['name']: return CHANGE_NAME raise UnexpectedWebhookEventType("Trello", action_type) return action_type def get_subject(payload: Mapping[str, Any]) -> str: return get_action_data(payload)['board']['name'] def get_body(payload: Mapping[str, Any], action_type: str) -> str: message_body = ACTIONS_TO_FILL_BODY_MAPPER[action_type](payload, action_type) creator = payload['action']['memberCreator']['fullName'] return f'{creator} {message_body}' def get_managed_member_body(payload: Mapping[str, Any], action_type: str) -> str: data = { 'member_name': payload['action']['member']['fullName'], } return fill_appropriate_message_content(payload, action_type, data) def get_create_list_body(payload: Mapping[str, Any], action_type: str) -> str: data = { 'list_name': get_action_data(payload)['list']['name'], } return fill_appropriate_message_content(payload, action_type, data) def get_change_name_body(payload: Mapping[str, Any], action_type: str) -> str: data = { 'old_name': get_action_data(payload)['old']['name'], } return fill_appropriate_message_content(payload, action_type, data) def fill_appropriate_message_content(payload: Mapping[str, Any], action_type: str, data: Mapping[str, Any] = {}) -> str: data = dict(data) if 'board_url_template' not in data: data['board_url_template'] = get_filled_board_url_template(payload) message_body = get_message_body(action_type) return message_body.format(**data) def get_filled_board_url_template(payload: Mapping[str, Any]) -> str: return TRELLO_BOARD_URL_TEMPLATE.format(board_name=get_board_name(payload), board_url=get_board_url(payload)) def get_board_name(payload: Mapping[str, Any]) -> str: return get_action_data(payload)['board']['name'] def get_board_url(payload: Mapping[str, Any]) -> str: return 'https://trello.com/b/{}'.format(get_action_data(payload)['board']['shortLink']) def get_message_body(action_type: str) -> str: return ACTIONS_TO_MESSAGE_MAPPER[action_type] def get_action_data(payload: Mapping[str, Any]) -> Mapping[str, Any]: return payload['action']['data'] ACTIONS_TO_FILL_BODY_MAPPER = { REMOVE_MEMBER: get_managed_member_body, ADD_MEMBER: get_managed_member_body, CREATE_LIST: get_create_list_body, CHANGE_NAME: get_change_name_body, }