def dadjoke_cmd(channel: str, message: str, use_api: bool = True) -> None: """Send the pinged user a dad joke. Or just send everybody a joke.""" parsed_message = message.split() ping_username, msg = None, None try: if use_api: joke = requests.get("https://icanhazdadjoke.com/", headers={ "Accept": "text/plain" }).content.decode() else: raise Exception("Testing mode -- just use fallback.") except: # noqa: E722 joke = i18n["slack"]["dadjoke"]["fallback_joke"] if len(parsed_message) == 2: if parsed_message[1].startswith("<"): ping_username = parsed_message[1].upper() if ping_username: msg = i18n["slack"]["dadjoke"]["message"].format( ping_username, joke) if not msg: msg = joke client.chat_postMessage(channel=channel, text=msg, link_names=True)
def process_action(data: Dict) -> None: """Process a Slack action, e.g. a button press.""" value = data["actions"][0].get("value") if "approve" in value or "remove" in value: process_submission_report_update(data) else: client.chat_postMessage( channel=data["channel"]["id"], text=i18n["slack"]["errors"]["unknown_payload"].format(value), )
def info_cmd(channel: str, message: str) -> None: """Send info about a user to slack.""" parsed_message = message.split() if len(parsed_message) == 1: # they just sent an empty info message, create a summary response data = Summary().generate_summary() client.chat_postMessage( channel=channel, text=i18n["slack"]["server_summary"].format("\n".join( dict_to_table(data))), ) return elif len(parsed_message) == 2: if user := BlossomUser.objects.filter( username__iexact=parsed_message[1]).first(): v_data = VolunteerSerializer(user).data msg = i18n["slack"]["user_info"].format( user.username, "\n".join(dict_to_table(v_data))) else: msg = i18n["slack"]["errors"]["no_user_by_that_name"]
def _check_for_rank_up(user: BlossomUser, submission: Submission = None) -> None: """ Check if a volunteer has changed rank and, if so, notify Slack. Because gamma is calculated off of transcriptions and the `done` endpoint is called after the transcription is posted, by the time that we go to calculate the gamma of the user, their gamma has already changed... so we'll just subtract one from their current score and see if that changes anything. """ current_rank = user.get_rank() if user.get_rank(override=user.gamma - 1) != current_rank: msg = ( f"Congrats to {user.username} on achieving the rank of {current_rank}!!" f" {submission.tor_url}" ) try: slack.chat_postMessage(channel=settings.SLACK_RANK_UP_CHANNEL, text=msg) except: # noqa logger.warning(f"Cannot post message to slack. Msg: {msg}") pass
def watch_cmd(channel: str, message: str) -> None: """Overwrite the transcription check percentage of a user.""" parsed_message = message.split() if len(parsed_message) == 1: # they didn't give a username msg = i18n["slack"]["errors"]["missing_username"] elif len(parsed_message) <= 3: username = clean_links(parsed_message[1]) if user := BlossomUser.objects.filter( username__iexact=username).first(): if len(parsed_message) == 2: # they didn't give a percentage, default to 100% decimal_percentage = 1 else: # parse the provided percentage percentage = parsed_message[2] try: # Try to parse the new check percentage percentage = int(percentage.rstrip(" %")) if percentage < 0 or percentage > 100: raise ValueError decimal_percentage = percentage / 100 except ValueError: # The percentage is invalid msg = i18n["slack"]["watch"]["invalid_percentage"].format( percentage=percentage) client.chat_postMessage(channel=channel, text=msg) return # Overwrite the check percentage user.overwrite_check_percentage = decimal_percentage user.save() msg = i18n["slack"]["watch"]["success"].format( user=user.username, percentage=decimal_percentage) else: msg = i18n["slack"]["errors"]["unknown_username"]
def _worker() -> None: """Create worker for background tasks.""" while True: func, args, kwargs = _queue.get() try: func(*args, **kwargs) except: # noqa: E722 import traceback # prevent circular dependency from api.slack import client details = traceback.format_exc() message = f"Background worker exception: ```{details}```" if settings.ENABLE_SLACK: client.chat_postMessage( channel=settings.SLACK_DEFAULT_CHANNEL, text=message, ) log.error(message) finally: _queue.task_done() # so we can join at exit
def send_github_sponsors_message(data: Dict, action: str) -> None: """ Process the POST request from GitHub Sponsors. Every time someone performs an action on GitHub Sponsors, we'll get a POST request with the intent and some other information. This translates the GitHub call to something that Slack can understand, then forwards it to the #org_running channel. """ emote = ":tada:" if action == "cancelled" or action == "pending_cancellation": emote = ":sob:" if (action == "edited" or action == "tier_changed" or action == "pending_tier_change"): emote = ":rotating_light:" username = data["sponsorship"]["sponsor"]["login"] sponsorlevel = data["sponsorship"]["tier"]["name"] msg = i18n["slack"]["github_sponsor_update"].format( emote, action, username, sponsorlevel) client.chat_postMessage(channel=settings.SLACK_GITHUB_SPONSORS_CHANNEL, text=msg)
def watchlist_cmd(channel: str, message: str) -> None: """Send a list of users who are currently being watched.""" parsed_message = message.split() sorting = parsed_message[1] if len(parsed_message) > 1 else "percentage" response_msg = "**List of all watched users:**\n\n" watched_users: List[BlossomUser] = list( BlossomUser.objects.filter(overwrite_check_percentage__isnull=False)) if len(watched_users) == 0: # No users are watched yet response_msg += ( "None yet. Use `@Blossom watch <username> <percentage>` to watch a user." ) client.chat_postMessage(channel=channel, text=response_msg) return else: response_msg += "```\n" if sorting == "percentage": # Group the users by percentages watched_users.sort(key=lambda u: u.overwrite_check_percentage, reverse=True) last_percentage = None for usr in watched_users: if usr.overwrite_check_percentage == last_percentage: response_msg += " " * 6 + f"u/{usr.username}\n" else: response_msg += "{}: u/{}\n".format( f"{usr.overwrite_check_percentage:.0%}".rjust(4, " "), usr.username) last_percentage = usr.overwrite_check_percentage elif sorting == "alphabetical": # Sort the users alphabetically watched_users.sort(key=lambda u: u.username.casefold()) for usr in watched_users: response_msg += "u/{} ({:.0%})\n".format( usr.username, usr.overwrite_check_percentage) else: # Invalid sorting response_msg = (f"Invalid sorting '{sorting}'. " "Use either 'percentage' or 'alphabetical'.") client.chat_postMessage(channel=channel, text=response_msg) return response_msg += "```" client.chat_postMessage(channel=channel, text=response_msg.strip())
def process_command(data: Dict) -> None: """Process a Slack command.""" e = data.get("event") # noqa: VNE001 channel = e.get("channel") message = get_message(data) actions = data.get("actions") if not message and not actions: client.chat_postMessage( channel=channel, text=i18n["slack"]["errors"]["message_parse_error"], ) return if not message: client.chat_postMessage( channel=channel, text=i18n["slack"]["errors"]["empty_message_error"], ) return # format: first word command -> function to call # Reformatted this way because E228 hates the if / elif routing tree. options = { "ping": pong_cmd, "help": help_cmd, "reset": reset_cmd, "info": info_cmd, "blacklist": blacklist_cmd, "watch": watch_cmd, "unwatch": unwatch_cmd, "watchlist": watchlist_cmd, "dadjoke": dadjoke_cmd, } tokens = message.split() if len(tokens) > 0: # Find the command corresponding to the message cmd_name = tokens[0].casefold() for key in options.keys(): if cmd_name == key: options[key](channel, message) return # if we fall through here, we got a message that we don't understand. client.chat_postMessage(channel=channel, text=i18n["slack"]["errors"]["unknown_request"])
def ask_about_removing_post(submission: Submission, reason: str) -> None: """Ask Slack if we want to remove a reported submission or not.""" # Check if this got already sent to mod chat, we don't want duplicates if (submission.report_slack_channel_id is not None or submission.report_slack_message_ts is not None): return submission.report_reason = reason submission.save(skip_extras=True) response = client.chat_postMessage( channel=settings.SLACK_REPORTED_POST_CHANNEL, blocks=_construct_report_message_blocks(submission, ReportMessageStatus.REPORTED), ) if not response["ok"]: logger.warning( f"Could not send report for submission {submission.id} to Slack!") return # See https://api.slack.com/methods/chat.postMessage submission.report_slack_channel_id = response["channel"] submission.report_slack_message_ts = response["message"]["ts"] submission.save()
def help_cmd(channel: str, _message: str) -> None: """Post a help message to slack.""" client.chat_postMessage(channel=channel, text=i18n["slack"]["help_message"])
def pong_cmd(channel: str, _message: str) -> None: """Respond to pings.""" client.chat_postMessage(channel=channel, text="PONG")
dict_to_table(data))), ) return elif len(parsed_message) == 2: if user := BlossomUser.objects.filter( username__iexact=parsed_message[1]).first(): v_data = VolunteerSerializer(user).data msg = i18n["slack"]["user_info"].format( user.username, "\n".join(dict_to_table(v_data))) else: msg = i18n["slack"]["errors"]["no_user_by_that_name"] else: msg = i18n["slack"]["errors"]["too_many_params"] client.chat_postMessage(channel=channel, text=msg) def pong_cmd(channel: str, _message: str) -> None: """Respond to pings.""" client.chat_postMessage(channel=channel, text="PONG") def reset_cmd(channel: str, message: str) -> None: """Reset the CoC status for a given volunteer.""" parsed_message = message.split() if len(parsed_message) == 1: # they didn't give a username msg = i18n["slack"]["errors"]["missing_username"] elif len(parsed_message) == 2: username = clean_links(parsed_message[1])