def api_pagerduty_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Iterable[Dict[str, Any]]]=REQ(argument_type='body'), ) -> HttpResponse: for message in payload['messages']: message_type = message.get('type') # If the message has no "type" key, then this payload came from a # Pagerduty Webhook V2. if message_type is None: break if message_type not in PAGER_DUTY_EVENT_NAMES: raise UnexpectedWebhookEventType('Pagerduty', message_type) format_dict = build_pagerduty_formatdict(message) send_formated_pagerduty(request, user_profile, message_type, format_dict) for message in payload['messages']: event = message.get('event') # If the message has no "event" key, then this payload came from a # Pagerduty Webhook V1. if event is None: break if event not in PAGER_DUTY_EVENT_NAMES_V2: raise UnexpectedWebhookEventType('Pagerduty', event) format_dict = build_pagerduty_formatdict_v2(message) send_formated_pagerduty(request, user_profile, event, format_dict) return json_success()
def api_basecamp_webhook(request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any]=REQ(argument_type='body')) -> HttpResponse: event = get_event_type(payload) if event not in SUPPORT_EVENTS: raise UnexpectedWebhookEventType('Basecamp', event) subject = get_project_name(payload) if event.startswith('document_'): body = get_document_body(event, payload) elif event.startswith('question_answer_'): body = get_questions_answer_body(event, payload) elif event.startswith('question_'): body = get_questions_body(event, payload) elif event.startswith('message_'): body = get_message_body(event, payload) elif event.startswith('todolist_'): body = get_todo_list_body(event, payload) elif event.startswith('todo_'): body = get_todo_body(event, payload) elif event.startswith('comment_'): body = get_comment_body(event, payload) else: raise UnexpectedWebhookEventType('Basecamp', event) check_send_webhook_message(request, user_profile, subject, body) return json_success()
def get_zulip_event_name( header_event: str, payload: Dict[str, Any], branches: Optional[str], ) -> Optional[str]: """ Usually, we return an event name that is a key in EVENT_FUNCTION_MAPPER. We return None for an event that we know we don't want to handle. """ if header_event == "pull_request": action = payload['action'] if action in ('opened', 'synchronize', 'reopened', 'edited'): return 'opened_or_update_pull_request' if action in ('assigned', 'unassigned'): return 'assigned_or_unassigned_pull_request' if action == 'closed': return 'closed_pull_request' if action == 'review_requested': return "pull_request_review_requested" if action == 'ready_for_review': return 'pull_request_ready_for_review' if action in IGNORED_PULL_REQUEST_ACTIONS: return None elif header_event == "push": if is_commit_push_event(payload): if branches is not None: branch = get_branch_name_from_ref(payload['ref']) if branches.find(branch) == -1: return None return "push_commits" else: return "push_tags" elif header_event == "check_run": if payload['check_run']['status'] != 'completed': return None return header_event elif header_event == "team": action = payload["action"] if action == "edited": return "team" if action in IGNORED_TEAM_ACTIONS: # no need to spam our logs, we just haven't implemented it yet return None else: # this means GH has actually added new actions since September 2020, # so it's a bit more cause for alarm raise UnexpectedWebhookEventType( "GitHub", f"unexpected team action {action}") elif header_event in list(EVENT_FUNCTION_MAPPER.keys()): return header_event elif header_event in IGNORED_EVENTS: return None complete_event = "{}:{}".format(header_event, payload.get("action", "???")) # nocoverage raise UnexpectedWebhookEventType('GitHub', complete_event)
def api_newrelic_webhook( request: HttpRequest, user_profile: UserProfile, alert: Optional[Dict[str, Any]] = REQ(validator=check_dict([]), default=None), deployment: Optional[Dict[str, Any]] = REQ(validator=check_dict([]), default=None) ) -> HttpResponse: if alert: # Use the message as the subject because it stays the same for # "opened", "acknowledged", and "closed" messages that should be # grouped. subject = alert['message'] content = "%(long_description)s\n[View alert](%(alert_url)s)" % (alert) elif deployment: subject = "%s deploy" % (deployment['application_name']) content = """`%(revision)s` deployed by **%(deployed_by)s** %(description)s %(changelog)s""" % (deployment) else: raise UnexpectedWebhookEventType('New Relic', 'Unknown Event Type') check_send_webhook_message(request, user_profile, subject, content) return json_success()
def get_event(request: HttpRequest, payload: Dict[str, Any], branches: Optional[str]) -> Optional[str]: event = validate_extract_webhook_http_header(request, 'X_GITLAB_EVENT', 'GitLab') if event == "System Hook": # Convert the event name to a Gitlab event title event_name = payload.get('event_name', payload.get('object_kind')) event = event_name.split("__")[0].replace("_", " ").title() event = f"{event} Hook" if event in [ 'Confidential Issue Hook', 'Issue Hook', 'Merge Request Hook', 'Wiki Page Hook' ]: action = payload['object_attributes'].get('action', 'open') event = f"{event} {action}" elif event in ['Confidential Note Hook', 'Note Hook']: action = payload['object_attributes'].get('noteable_type') event = f"{event} {action}" elif event == 'Push Hook': if branches is not None: branch = get_branch_name(payload) if branches.find(branch) == -1: return None if event in list(EVENT_FUNCTION_MAPPER.keys()): return event raise UnexpectedWebhookEventType('GitLab', event)
def get_body_function_based_on_type(payload: Dict[str, Any]) -> Any: action = get_action_with_primary_id(payload) event = "{}_{}".format(action["entity_type"], action["action"]) changes = action.get("changes") if changes is not None: if changes.get("description") is not None: event = "{}_{}".format(event, "description") elif changes.get("state") is not None: event = "{}_{}".format(event, "state") elif changes.get("workflow_state_id") is not None: event = "{}_{}".format(event, "state") elif changes.get("name") is not None: event = "{}_{}".format(event, "name") elif changes.get("archived") is not None: event = "{}_{}".format(event, "archived") elif changes.get("complete") is not None: event = "{}_{}".format(event, "complete") elif changes.get("epic_id") is not None: event = "{}_{}".format(event, "epic") elif changes.get("estimate") is not None: event = "{}_{}".format(event, "estimate") elif changes.get("file_ids") is not None: event = "{}_{}".format(event, "attachment") elif changes.get("label_ids") is not None: event = "{}_{}".format(event, "label") elif changes.get("project_id") is not None: event = "{}_{}".format(event, "project") elif changes.get("story_type") is not None: event = "{}_{}".format(event, "type") else: raise UnexpectedWebhookEventType("Clubhouse", event) return EVENT_BODY_FUNCTION_MAPPER.get(event)
def get_type(request: HttpRequest, payload: Dict[str, Any]) -> str: if payload.get('push'): return 'push' elif payload.get('fork'): return 'fork' elif payload.get('comment') and payload.get('commit'): return 'commit_comment' elif payload.get('commit_status'): return 'change_commit_status' elif payload.get('issue'): if payload.get('changes'): return "issue_updated" if payload.get('comment'): return 'issue_commented' return "issue_created" elif payload.get('pullrequest'): pull_request_template = 'pull_request_{}' # Note that we only need the HTTP header to determine pullrequest events. # We rely on the payload itself to determine the other ones. event_key = validate_extract_webhook_http_header( request, "X_EVENT_KEY", "BitBucket") action = re.match('pullrequest:(?P<action>.*)$', event_key) if action: action_group = action.group('action') if action_group in PULL_REQUEST_SUPPORTED_ACTIONS: return pull_request_template.format(action_group) else: event_key = validate_extract_webhook_http_header( request, "X_EVENT_KEY", "BitBucket") if event_key == 'repo:updated': return event_key raise UnexpectedWebhookEventType('BitBucket2', event_key)
def api_clubhouse_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body') ) -> HttpResponse: # Clubhouse has a tendency to send empty POST requests to # third-party endpoints. It is unclear as to which event type # such requests correspond to. So, it is best to ignore such # requests for now. if payload is None: return json_success() event = get_event(payload) if event is None: return json_success() body_func = EVENT_BODY_FUNCTION_MAPPER.get(event) # type: Any topic_func = get_topic_function_based_on_type(payload) if body_func is None or topic_func is None: raise UnexpectedWebhookEventType('Clubhouse', event) topic = topic_func(payload) body = body_func(payload) if topic and body: check_send_webhook_message(request, user_profile, topic, body) return json_success()
def compose_notification_message(payload: Dict[str, Any]) -> str: """Composes a message that contains information on the error :param payload: Raygun payload :return: Returns a response message """ # Get the event type of the error. This can be "NewErrorOccurred", # "ErrorReoccurred", "OneMinuteFollowUp", "FiveMinuteFollowUp", ..., # "HourlyFollowUp" for notification error. event_type = payload['eventType'] # "NewErrorOccurred" and "ErrorReoccurred" contain error instance # information, meaning that it has payload['error']['instance']. The other # event type (the follow ups) doesn't have this instance. # We now split this main function again into two functions. One is for # "NewErrorOccurred" and "ErrorReoccurred", and one is for the rest. Both # functions will return a text message that is formatted for the chat. if event_type == "NewErrorOccurred" or event_type == "ErrorReoccurred": return notification_message_error_occurred(payload) elif "FollowUp" in event_type: return notification_message_follow_up(payload) else: raise UnexpectedWebhookEventType('Raygun', event_type)
def api_jira_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body') ) -> HttpResponse: event = get_event_type(payload) if event in IGNORED_EVENTS: return json_success() subject = get_issue_subject(payload) content_func = get_event_handler(event) if content_func is None: raise UnexpectedWebhookEventType('Jira', event) content = content_func(payload, user_profile) # type: str check_send_webhook_message(request, user_profile, subject, content, unquote_url_parameters=True) return json_success()
def repo_push_branch_data(payload: Dict[str, Any], change: Dict[str, Any]) -> Dict[str, str]: event_type = change["type"] repo_name = payload["repository"]["name"] user_name = payload["actor"]["name"] branch_name = change["ref"]["displayId"] branch_head = change["toHash"] if event_type == "ADD": body = get_create_branch_event_message(user_name=user_name, url=None, branch_name=branch_name) elif event_type == "UPDATE": body = BRANCH_UPDATED_MESSAGE_TEMPLATE.format(user_name=user_name, branch_name=branch_name, head=branch_head) elif event_type == "DELETE": body = get_remove_branch_event_message(user_name, branch_name) else: message = "%s.%s" % (payload["eventKey"], event_type) # nocoverage raise UnexpectedWebhookEventType("BitBucket Server", message) subject = TOPIC_WITH_BRANCH_TEMPLATE.format(repo=repo_name, branch=branch_name) return {"subject": subject, "body": body}
def get_event_type(event: Dict[str, Any]) -> str: event_type_match = re.match('check.(.*)', event['event']) if event_type_match: event_type = event_type_match.group(1) if event_type in EVENT_TYPE_BODY_MAPPER: return event_type raise UnexpectedWebhookEventType('Updown', event['event'])
def api_raygun_webhook(request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body')) -> HttpResponse: # The payload contains 'event' key. This 'event' key has a value of either # 'error_notification' or 'error_activity'. 'error_notification' happens # when an error is caught in an application, where as 'error_activity' # happens when an action is being taken for the error itself # (ignored/resolved/assigned/etc.). event = payload['event'] # Because we wanted to create a message for all of the payloads, it is best # to handle them separately. This is because some payload keys don't exist # in the other event. if event == 'error_notification': message = compose_notification_message(payload) elif event == 'error_activity': message = compose_activity_message(payload) else: raise UnexpectedWebhookEventType('Raygun', event) topic = 'test' check_send_webhook_message(request, user_profile, topic, message) return json_success()
def api_harbor_webhook(request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body')) -> HttpResponse: operator_username = u"**{}**".format(payload["operator"]) if operator_username != "auto": operator_profile = guess_zulip_user_from_harbor( operator_username, user_profile.realm) if operator_profile: operator_username = u"@**{}**".format(operator_profile.full_name) # nocoverage event = payload["type"] topic = payload["event_data"]["repository"]["repo_full_name"] if event in IGNORED_EVENTS: return json_success() content_func = EVENT_FUNCTION_MAPPER.get(event) if content_func is None: raise UnexpectedWebhookEventType('Harbor', event) content = content_func(payload, user_profile, operator_username) # type: str check_send_webhook_message(request, user_profile, topic, content, unquote_url_parameters=True) return json_success()
def api_gogs_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body'), branches: Optional[str] = REQ(default=None) ) -> HttpResponse: repo = payload['repository']['name'] event = validate_extract_webhook_http_header(request, 'X_GOGS_EVENT', 'Gogs') if event == 'push': branch = payload['ref'].replace('refs/heads/', '') if branches is not None and branches.find(branch) == -1: return json_success() body = format_push_event(payload) topic = SUBJECT_WITH_BRANCH_TEMPLATE.format(repo=repo, branch=branch) elif event == 'create': body = format_new_branch_event(payload) topic = SUBJECT_WITH_BRANCH_TEMPLATE.format(repo=repo, branch=payload['ref']) elif event == 'pull_request': body = format_pull_request_event(payload) topic = SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format( repo=repo, type='PR', id=payload['pull_request']['id'], title=payload['pull_request']['title']) else: raise UnexpectedWebhookEventType('Gogs', event) check_send_webhook_message(request, user_profile, topic, body) return json_success()
def get_subject_and_body(payload: Mapping[str, Any], action_type: str) -> Optional[Tuple[str, str]]: if action_type in SUPPORTED_CARD_ACTIONS: return process_card_action(payload, action_type) if action_type in SUPPORTED_BOARD_ACTIONS: return process_board_action(payload, action_type) raise UnexpectedWebhookEventType("Trello", f'{action_type} is not supported')
def api_jira_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body') ) -> HttpResponse: event = get_event_type(payload) if event == 'jira:issue_created': subject = get_issue_subject(payload) content = handle_created_issue_event(payload) elif event == 'jira:issue_deleted': subject = get_issue_subject(payload) content = handle_deleted_issue_event(payload) elif event == 'jira:issue_updated': subject = get_issue_subject(payload) content = handle_updated_issue_event(payload, user_profile) elif event == 'comment_created': subject = get_issue_subject(payload) content = handle_updated_issue_event(payload, user_profile) elif event in IGNORED_EVENTS: return json_success() else: raise UnexpectedWebhookEventType('Jira', event) check_send_webhook_message(request, user_profile, subject, content, unquote_url_parameters=True) return json_success()
def get_event(request: HttpRequest, payload: Dict[str, Any], branches: str) -> Optional[str]: event = validate_extract_webhook_http_header(request, 'X_GITHUB_EVENT', 'GitHub') if event == 'pull_request': action = payload['action'] if action in ('opened', 'synchronize', 'reopened', 'edited'): return 'opened_or_update_pull_request' if action in ('assigned', 'unassigned'): return 'assigned_or_unassigned_pull_request' if action == 'closed': return 'closed_pull_request' if action == 'review_requested': return '{}_{}'.format(event, action) # Unsupported pull_request events if action in ('labeled', 'unlabeled', 'review_request_removed'): return None if event == 'push': if is_commit_push_event(payload): if branches is not None: branch = get_branch_name_from_ref(payload['ref']) if branches.find(branch) == -1: return None return "push_commits" else: return "push_tags" elif event in list(EVENT_FUNCTION_MAPPER.keys()) or event == 'ping': return event elif event in IGNORED_EVENTS: return None raise UnexpectedWebhookEventType('GitHub', event)
def api_pingdom_webhook(request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any]=REQ(argument_type='body')) -> HttpResponse: check_type = get_check_type(payload) if check_type in SUPPORTED_CHECK_TYPES: subject = get_subject_for_http_request(payload) body = get_body_for_http_request(payload) else: raise UnexpectedWebhookEventType('Pingdom', check_type) check_send_webhook_message(request, user_profile, subject, body) return json_success()
def api_transifex_webhook(request: HttpRequest, user_profile: UserProfile, project: str=REQ(), resource: str=REQ(), language: str=REQ(), translated: Optional[int]=REQ(default=None), reviewed: Optional[int]=REQ(default=None)) -> HttpResponse: subject = "{} in {}".format(project, language) if translated: body = "Resource {} fully translated.".format(resource) elif reviewed: body = "Resource {} fully reviewed.".format(resource) else: raise UnexpectedWebhookEventType('Transifex', 'Unknown Event Type') check_send_webhook_message(request, user_profile, subject, body) return json_success()
def gogs_webhook_main(integration_name: str, http_header_name: str, format_pull_request_event: Callable[..., Any], request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any], branches: Optional[str], user_specified_topic: Optional[str]) -> HttpResponse: repo = payload['repository']['name'] event = validate_extract_webhook_http_header(request, http_header_name, integration_name) if event == 'push': branch = payload['ref'].replace('refs/heads/', '') if branches is not None and branch not in branches.split(','): return json_success() body = format_push_event(payload) topic = TOPIC_WITH_BRANCH_TEMPLATE.format(repo=repo, branch=branch) elif event == 'create': body = format_new_branch_event(payload) topic = TOPIC_WITH_BRANCH_TEMPLATE.format(repo=repo, branch=payload['ref']) elif event == 'pull_request': body = format_pull_request_event(payload, include_title=user_specified_topic is not None) topic = TOPIC_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format( repo=repo, type='PR', id=payload['pull_request']['id'], title=payload['pull_request']['title']) elif event == 'issues': body = format_issues_event(payload, include_title=user_specified_topic is not None) topic = TOPIC_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format( repo=repo, type='Issue', id=payload['issue']['number'], title=payload['issue']['title']) elif event == 'issue_comment': body = format_issue_comment_event(payload, include_title=user_specified_topic is not None) topic = TOPIC_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format( repo=repo, type='Issue', id=payload['issue']['number'], title=payload['issue']['title']) else: raise UnexpectedWebhookEventType('Gogs', event) check_send_webhook_message(request, user_profile, topic, body) return json_success()
def handle_issue_payload(action: str, issue: Dict[str, Any], actor: Dict[str, Any]) -> Tuple[str, str]: """ Handle either an issue type event. """ subject = issue["title"] datetime = issue["lastSeen"].split(".")[0].replace("T", " ") if issue["assignedTo"]: if issue["assignedTo"]["type"] == "team": assignee = "team {}".format(issue["assignedTo"]["name"]) else: assignee = issue["assignedTo"]["name"] else: assignee = "No one" if action == "created": context = { "title": subject, "level": issue["level"], "datetime": datetime, "assignee": assignee, } body = ISSUE_CREATED_MESSAGE_TEMPLATE.format(**context) elif action == "resolved": context = { "title": subject, "actor": actor["name"], } body = ISSUE_RESOLVED_MESSAGE_TEMPLATE.format(**context) elif action == "assigned": context = { "title": subject, "assignee": assignee, "actor": actor["name"], } body = ISSUE_ASSIGNED_MESSAGE_TEMPLATE.format(**context) elif action == "ignored": context = { "title": subject, "actor": actor["name"], } body = ISSUE_IGNORED_MESSAGE_TEMPLATE.format(**context) else: raise UnexpectedWebhookEventType("Sentry", "unknown-issue-action type") return (subject, body)
def get_team_body(payload: Dict[str, Any]) -> str: changes = payload["changes"] if "description" in changes: actor = payload["sender"]["login"] new_description = payload["team"]["description"] return "**{}** changed the team description to:\n```quote\n{}\n```".format(actor, new_description) if "name" in changes: original_name = changes["name"]["from"] new_name = payload["team"]["name"] return "Team `{}` was renamed to `{}`.".format(original_name, new_name) if "privacy" in changes: new_visibility = payload["team"]["privacy"] return "Team visibility changed to `{}`".format(new_visibility) else: # nocoverage raise UnexpectedWebhookEventType("GitHub", "Team Edited: {}".format(changes.keys()))
def get_team_body(payload: Dict[str, Any]) -> str: changes = payload["changes"] if "description" in changes: actor = payload["sender"]["login"] new_description = payload["team"]["description"] return f"**{actor}** changed the team description to:\n```quote\n{new_description}\n```" if "name" in changes: original_name = changes["name"]["from"] new_name = payload["team"]["name"] return f"Team `{original_name}` was renamed to `{new_name}`." if "privacy" in changes: new_visibility = payload["team"]["privacy"] return f"Team visibility changed to `{new_visibility}`" else: # nocoverage raise UnexpectedWebhookEventType("GitHub", f"Team Edited: {changes.keys()}")
def get_template(request: HttpRequest, payload: Dict[str, Any]) -> str: message_template = 'The build [{build_name}]({build_url}) on branch {branch_name} ' event = validate_extract_webhook_http_header(request, 'X_NETLIFY_EVENT', 'Netlify') if event == 'deploy_failed': return message_template + payload['error_message'] elif event == 'deploy_locked': return message_template + 'is now locked.' elif event == 'deploy_unlocked': return message_template + 'is now unlocked.' elif event in EVENTS: return message_template + 'is now {state}.'.format(state=payload['state']) else: raise UnexpectedWebhookEventType('Netlify', event)
def repo_push_tag_data(payload: Dict[str, Any], change: Dict[str, Any]) -> Dict[str, str]: event_type = change["type"] repo_name = payload["repository"]["name"] tag_name = change["ref"]["displayId"] if event_type == "ADD": action = "pushed" elif event_type == "DELETE": action = "removed" else: message = "%s.%s" % (payload["eventKey"], event_type) # nocoverage raise UnexpectedWebhookEventType("BitBucket Server", message) subject = BITBUCKET_TOPIC_TEMPLATE.format(repository_name=repo_name) body = get_push_tag_event_message(get_user_name(payload), tag_name, action=action) return {"subject": subject, "body": body}
def api_reviewboard_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Iterable[Dict[str, Any]]]=REQ(argument_type='body') ) -> HttpResponse: event_type = validate_extract_webhook_http_header( request, 'X_REVIEWBOARD_EVENT', 'ReviewBoard') assert event_type is not None body_function = RB_MESSAGE_FUNCTIONS.get(event_type) if body_function is not None: body = body_function(payload) topic = get_review_request_repo_title(payload) check_send_webhook_message(request, user_profile, topic, body) else: raise UnexpectedWebhookEventType('ReviewBoard', event_type) return json_success()
def api_sentry_webhook(request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type="body")) -> HttpResponse: data = payload.get("data", None) # We currently support two types of payloads: events and issues. if data: if "event" in data: subject, body = handle_event_payload(data["event"]) elif "issue" in data: subject, body = handle_issue_payload(payload["action"], data["issue"], payload["actor"]) else: raise UnexpectedWebhookEventType("Sentry", str(list(data.keys()))) else: subject, body = handle_deprecated_payload(payload) check_send_webhook_message(request, user_profile, subject, body) return json_success()
def repo_push_handler(payload: Dict[str, Any], branches: Optional[str]=None ) -> List[Dict[str, str]]: data = [] for change in payload["changes"]: event_target_type = change["ref"]["type"] if event_target_type == "BRANCH": branch = change["ref"]["displayId"] if branches: if branch not in branches: continue data.append(repo_push_branch_data(payload, change)) elif event_target_type == "TAG": data.append(repo_push_tag_data(payload, change)) else: message = "%s.%s" % (payload["eventKey"], event_target_type) # nocoverage raise UnexpectedWebhookEventType("BitBucket Server", message) return data
def api_transifex_webhook( request: HttpRequest, user_profile: UserProfile, project: str = REQ(), resource: str = REQ(), language: str = REQ(), translated: Optional[int] = REQ(validator=check_int, default=None), reviewed: Optional[int] = REQ(validator=check_int, default=None), ) -> HttpResponse: subject = f"{project} in {language}" if translated: body = f"Resource {resource} fully translated." elif reviewed: body = f"Resource {resource} fully reviewed." else: raise UnexpectedWebhookEventType('Transifex', 'Unknown Event Type') check_send_webhook_message(request, user_profile, subject, body) return json_success()