def notify_users(team_name): team = Team.query.filter_by(name=team_name).first() # Get all active users for this team users = (db.session.query(User).join(Team.user).filter( Team.id == team.id, User.is_active)).all() for user in users: num_teams = len(user.team) todays_datetime = datetime(datetime.today().year, datetime.today().month, datetime.today().day) submissions = user.submission.filter( Submission.created_at >= todays_datetime).all() # TODO: This assumes submissions for all teams. It should check # submission for only this team (requested in the API request) # This will need to submission object to have reference to # the standup it's associated with. if len(submissions) < num_teams: text, blocks = utils.prepare_notification_message(user) client.chat_postMessage(channel=user.user_id, text=text, blocks=blocks) return jsonify({"success": True})
def onboarding(user, channel=None): """ Onboards a new user or a existing user that hasn't been onboarded yet. """ # Check if user is in the database if utils.query_user_exists(user): return # Retrieve admin status from slack user_info = client.users_info(user=user) is_admin = user_info["user"]["is_admin"] is_owner = user_info["user"]["is_owner"] # Add user to the database utils.add_new_user(user, is_admin=is_admin, is_owner=is_owner) # Retrieve channel if channel is None: channel = client.conversations_open(users=[user])["channel"]["id"] # Post the message in the channel client.chat_postMessage(channel=channel, text='IMPORTANT: CSESoc Slack Onboarding!!', blocks=blocks.onboarding(user))
def open_standup_view(**kwargs): user_id = kwargs.get("user_id") data = kwargs.get("data", None) trigger_type = kwargs.get("trigger_type", constants.BUTTON_TRIGGER) try: user = User.query.filter_by(user_id=user_id).first() if trigger_type == constants.BUTTON_TRIGGER: team = (db.session.query(Team).join( User.team).filter(User.id == user.id).first()) else: team_name = data.get("text") print(team_name) if not team_name: message = f"Slash command format is `/standup <team-name>`.\nYour commands: {', '.join(utils.get_user_slash_commands(user))}" client.chat_postMessage(channel=user.user_id, text=message) team = Team.query.filter_by(name=team_name).first() # TODO: Check if this user it allowed in this team's standup especially # in the case of slash command trigger. standup = team.standup if submission := utils.submission_exists(user, standup): client.views_open(trigger_id=data.get("trigger_id"), view=open_edit_view(standup, submission)) client.views_open(trigger_id=data.get("trigger_id"), view=utils.get_standup_view(standup)) return make_response("", 200)
def submit_standup_handler(**kwargs): payload = kwargs.get("data") standup_submission = json.dumps(payload.get("view")) if payload and utils.is_submission_eligible(payload): user_payload = payload.get("user", {}) _, team_name = payload.get("view", {}).get("callback_id", "").split("%") user = User.query.filter_by(user_id=user_payload.get("id")).first() standup = Standup.query.filter(Standup.trigger == team_name).first() todays_datetime = datetime(datetime.today().year, datetime.today().month, datetime.today().day) is_edit = False if submission := utils.submission_exists(user, standup): client.chat_postMessage(channel=user.user_id, text=constants.SUBMISSION_UPDATED_MESSAGE) submission.standup_submission = standup_submission is_edit = True else: submission = Submission(user_id=user.id, standup_submission=standup_submission, standup_id=standup.id, standup=standup) db.session.add(submission) db.session.commit()
def trivia_leaderboard(channel, players): for player in players: player_info = client.users_info(user=player['player'])['user'] player['name'] = player_info['profile']['display_name_normalized'] if ( player_info['profile']['display_name_normalized'] != "" ) else player_info['profile']['real_name_normalized'] client.chat_postMessage(channel=channel, text="Trivia is over!", blocks=triviaBoard(players))
def publish_standup(team_name): try: todays_datetime = datetime(datetime.today().year, datetime.today().month, datetime.today().day) team = Team.query.filter_by(name=team_name).first() if not team: return make_response(f'Team "{team_name}" does not exist', 404) # Get all active users for this team users = (db.session.query(User).join(Team.user).filter( Team.id == team.id, User.is_active)) standup = Standup.query.filter(Standup.trigger == team_name).first() submissions = Submission.query.filter( and_( Submission.created_at >= todays_datetime, Submission.user_id.in_([user.id for user in users]), Submission.standup == standup, )) no_submission_users = utils.post_publish_stat(users) message_response = client.chat_postMessage( channel=team.standup.publish_channel, text="Standup complete", blocks=[STANDUP_INFO_SECTION] + utils.users_left_section(no_submission_users), ) standup_thread = StandupThread(standup=standup, standup_id=standup.id, thread_id=message_response.get("ts")) db.session.add(standup_thread) db.session.commit() blocks_chunk = utils.chunk_blocks( utils.build_standup(submissions, True), BLOCK_SIZE) for blocks in blocks_chunk: client.chat_postMessage( channel=team.standup.publish_channel, text="Standup complete", thread_ts=message_response.get("ts"), blocks=blocks, ) if POST_PUBLISH_STATS: no_submit_users = utils.post_publish_stat(users) message = f"{NO_USER_SUBMIT_MESSAGE} {', '.join(no_submit_users)}" client.chat_postMessage(channel=team.standup.publish_channel, text=message) return make_response(json.dumps(utils.build_standup(submissions)), 200) except SlackApiError as e: code = e.response["error"] return make_response(f"Failed due to {code}", 200)
def reply_im(user, channel, message): if message.lower() not in greetings: return user_info = client.users_info(user=user)['user'] user_name = user_info['profile']['display_name_normalized'] if ( user_info['profile']['display_name_normalized'] != "" ) else user_info['profile']['real_name_normalized'] message = f"Hi {user_name}, it's me slackbot!" client.chat_postMessage(channel=channel, text=message, blocks=imBlock(user_name))
def reply_mention(user, channel): user_info = client.users_info(user=user)['user'] user_name = user_info['profile']['display_name_normalized'] if ( user_info['profile']['display_name_normalized'] != "" ) else user_info['profile']['real_name_normalized'] channel_info = client.conversations_info(channel=channel)['channel'] channel_name = channel_info['name'] message = f"Hi {user_name}, it's me slackbot!" client.chat_postMessage(channel=channel, text=message, blocks=mentionBlock( user_name, channel_name, is_channel=channel_info['is_channel']))
def karma_message(channel_id): toppers = utils.get_top_karma() # display name, profile picture, karma count leaders = [] for user in toppers: player_info = client.users_info(user=user.id)['user'] player_name = player_info['profile']['display_name_normalized'] if ( player_info['profile']['display_name_normalized'] != "" ) else player_info['profile']['real_name_normalized'] leaders.append({ 'name': player_name, 'karma': user.karma, 'pfp': player_info['profile']['image_72'] }) client.chat_postMessage(channel=channel_id, text="Karma boards", blocks=karmaBoard(leaders))
def after_submission(submission: Submission, is_edit: bool = False) -> None: now = datetime.now().time() publish_time = submission.standup.publish_time blocks = build_standup([submission], True) if now > publish_time: todays_datetime = datetime(datetime.today().year, datetime.today().month, datetime.today().day) thread = StandupThread.query.filter( and_( StandupThread.standup == submission.standup, StandupThread.created_at >= todays_datetime, )).first() channel = submission.standup.publish_channel client.chat_postMessage( channel=channel, thread_ts=thread.thread_id, blocks=blocks, ) update_users_left_info(channel, thread.thread_id, submission.standup.team.id) if not is_edit: client.chat_postMessage( channel=submission.user.user_id, blocks=[SUBMIT_TEMPLATE_SECTION_1] + add_optional_block(submission.user.post_submit_action)) # TODO: Add edit options for people in multiple teams. This ensures no # edits right now because button click events are not designed to handle # different scenarios if len(submission.user.team) > 1: blocks = [SUBMIT_TEMPLATE_SECTION_3] + blocks + [APP_CONTEXT_SECTION] else: edit_dialog_block = dict(EDIT_DIALOG_SECTION) edit_dialog_block[ "block_id"] = f"open_dialog%{submission.standup.trigger}" blocks = [SUBMIT_TEMPLATE_SECTION_3] + blocks + \ [edit_dialog_block] + [APP_CONTEXT_SECTION] client.chat_postMessage( channel=submission.user.user_id, blocks=blocks, )
def trivia_failure(user_id, trigger_id): client.chat_postMessage(channel=user_id, text="Your attempt to create a trivia game failed")
def review_confirm(user_id, course_code): client.chat_postMessage(channel=user_id, text="Review confirmed!", blocks=reviewConfirm(course_code))
def say(payload): """ Say something as the SlackBot """ client.chat_postMessage(channel=payload["channel_id"], text=payload["text"])
def interactions(payload): """ The main function for enabling interactions with shortcuts, modals, or interactive components (such as buttons, select menus, and datepickers). See https://api.slack.com/reference/interaction-payloads for more details on how to handle interaction payloads. """ # Extract data trigger_id = payload["trigger_id"] user = payload["user"]["id"] # Received when a user clicks a Block Kit interactive component. if payload["type"] == "shortcut": callback_id = payload["callback_id"] # Opens the "edit_profile" view with prefilled information if callback_id == "anonymous_messaging": client.views_open(trigger_id=trigger_id, view=get_anonymous_modal()) elif callback_id == "trivia": init_trivia(trigger_id, user) # Received when a modal is submitted. if payload["type"] == "view_submission": # if 'trivia_start_' in payload['view']['callback_id']: # try: # game_id = payload['view']['callback_id'].replace('trivia_start_', '') # trivia_q_number(game_id, int(payload['view']['state']['values']['number_questions']['number_questions']['value'])) # trivia_player_list(game_id, payload['view']['state']['values']['users_playing']['users_playing']['selected_users']) # if trivia_finalise(game_id, trigger_id): # try: # with a.app_context(): # resp = jsonify({'response_action': 'push', 'view': trivia_customs(game_id, trigger_id)}) # resp.headers['Authorization'] = Config.SLACK_BOT_TOKEN # return resp # except Exception as err: # print(err) # except: # trivia_failure(game_id, trigger_id) # if 'custom_questions_' in payload['view']['callback_id']: # trivia_custom_questions(payload['view']['callback_id'].replace('custom_questions_', ''), payload['view']['state']['values']) if 'course_review_' in payload['view']['callback_id']: u_id = payload['view']['callback_id'].replace("course_review_", "") course = review_submit(u_id, payload['view']['state']['values']) review_confirm(u_id, course) view = payload["view"] callback_id = view["callback_id"] state = view["state"] # Store submitted profile data if callback_id == "edit_profile_modal": values = view["state"]["values"] for key in ["favourite_course", "favourite_programming_language", "favourite_netflix_show", "favourite_food", \ "overrated", "underrated", "biggest_flex", "enrolled_courses", "completed_courses", "general_interests"]: value = utils.extract_value(values, key, key) utils.add_profile_details(user, key, value) app_home({"user": user}) # Purge messages if callback_id == "purge_confirmation": # Extract metadata metadata = blocks.json.loads(view["private_metadata"]) # Signal the purge is starting client.chat_postEphemeral(channel=metadata["channel_id"], user=user, text="Purging messages...") # Retrieve data from metadata number_of_messages = metadata["number_of_messages"] target_user_id = metadata["user"][2:13] oldest = int( utils.time.time() ) - metadata["time_period"] if metadata["time_period"] != -1 else 0 channel_id = metadata["channel_id"] text_snippet = metadata["text_snippet"] # Get messages from channel conversations_history = client.conversations_history( channel=channel_id, oldest=oldest).data # Iterate through messages and delete messages until we run out of messages to delete or reach our target count_deleted = 0 while count_deleted < number_of_messages: # Iterate through messages for msg in conversations_history["messages"]: # Skip messages that are not actual messages (e.g event messages) if msg["type"] != "message": continue # Delete if no user specified or user of message matches target if (target_user_id == "" or target_user_id == msg["user"] ) and (text_snippet == "" or text_snippet in blocks.json.dumps(msg)): try: user_client.chat_delete(channel=channel_id, ts=msg["ts"]) count_deleted += 1 except Exception as e: # Serve error back to user for debugging client.chat_postEphemeral( channel=metadata["channel_id"], user=user, text=str(e)) quit() # Break loop if target number of messages is reached if count_deleted >= number_of_messages: break # Check that there are more messages to retrieve if conversations_history["has_more"] is False: break # Retrieve next set of messages cursor = conversations_history["response_metadata"][ "next_cursor"] conversations_history = client.conversations_history( channel=channel_id, oldest=oldest, cursor=cursor) # Signal the purge is complete client.chat_postEphemeral(channel=metadata["channel_id"], user=user, text="Purge complete") # Store submitted profile data if callback_id == "anonymous_messaging_modal": print(view["state"]) controls = [] # Unwrap control for control in list(state["values"].items()): control = control[1] control = list(control.items())[0][1] print(control) controls.append(control) users = controls[0]["selected_users"] message = controls[1]["value"] msg_ids = utils.create_anon_message(user, users, message) for i, selected_user in enumerate(users): block = get_anonymous_message(message, msg_ids[i]) client.chat_postMessage(channel=selected_user, blocks=block) elif callback_id.startswith("anonymous_messaging_modal_reply"): view = payload["view"] state = view["state"] message_id = callback_id.split("=")[1] # Unwrap control control = list(state["values"].items()) control = control[0][1] control = list(control.items())[0][1] reply_msg = control["value"] print(reply_msg) reply = utils.reply_anon_message(user, message_id, reply_msg) block = get_anonymous_message(reply_msg, reply.id) client.chat_postMessage(channel=reply.target_id, blocks=block) elif callback_id.startswith("report_messaging_modal_reply"): view = payload["view"] state = view["state"] message_id = callback_id.split("=")[1] # Unwrap control control = list(state["values"].items()) control = control[0][1] control = list(control.items())[0][1] report_msg = control["value"] print(report_msg) report_id = utils.report_message(message_id, report_msg) client.chat_postMessage( channel=payload["user"]["id"], text="Message Reported, to follow up " "provide the following report id: R{}".format(report_id)) if payload["type"] == "block_actions": print(payload) if "trivia_custom_" in payload['actions'][0]['action_id']: trivia_customs( payload['actions'][0]['action_id'].replace( "trivia_custom_", ""), payload['trigger_id']) return elif "accept_trivia_" in payload['actions'][0]['action_id']: trivia_reply( payload['user']['id'], True, payload['actions'][0]['action_id'].replace( "accept_trivia_", ""), payload['trigger_id']) return elif "forfeit_trivia_" in payload['actions'][0]['action_id']: trivia_reply( payload['user']['id'], False, payload['actions'][0]['action_id'].replace( "forfeit_trivia_", ""), payload['trigger_id']) return elif 'view' in payload and "trivia_start_" in payload['view'][ 'callback_id']: if "default_trivia_" in payload['actions'][0]['action_id']: trivia_set_qs( payload['actions'][0]['action_id'].replace( "default_trivia_", ""), payload['actions'][0] ['selected_option']['value'] == "true") return elif 'view' in payload and "trivia_channel_" in payload['actions'][ 0]['action_id']: trivia_set_channel( payload['actions'][0]['action_id'].replace( "trivia_channel_", ""), payload['actions'][0]['selected_channel']) return elif 'view' in payload and "trivia_question_" in payload['view'][ 'callback_id']: trivia_response(payload['user']['id'], payload['actions'][0]['value'] == 'correct', payload['trigger_id']) return elif "course_overall_" in payload['actions'][0]['action_id']: review_overall( payload['actions'][0]['action_id'].replace( "course_overall_", ""), payload['actions'][0]['selected_option']['value']) elif "course_difficulty_" in payload['actions'][0]['action_id']: review_difficulty( payload['actions'][0]['action_id'].replace( "course_difficulty_", ""), payload['actions'][0]['selected_option']['value']) elif "course_time_" in payload['actions'][0]['action_id']: review_time( payload['actions'][0]['action_id'].replace("course_time_", ""), payload['actions'][0]['selected_option']['value']) # Received when a user clicks a Block Kit interactive component. actions = payload["actions"] value = actions[0]["value"] # Opens the "report" view if value == "click_report": message_id = payload["message"]["blocks"][0]["block_id"] client.views_open(trigger_id=trigger_id, view=get_report_modal(message_id)) # Opens the "anonymous_reply" modal if value == "click_reply": message_id = payload["message"]["blocks"][0]["block_id"] client.views_open(trigger_id=trigger_id, view=get_anonymous_reply_modal(message_id)) # Close Report if value.startswith("remove_report_"): report_id = value.split("_")[-1] utils.close_report(report_id) client.chat_postMessage( channel=payload["user"]["id"], text="Successfully removed report R{}".format(report_id)) # No modal is expected if value == "pass": pass # Opens the "commands_help" view if value == "commands_help": client.views_open(trigger_id=trigger_id, view=blocks.commands_help()) # Opens the "edit_profile" view with prefilled information if value == "edit_profile": values = utils.retrieve_profile_details(user) client.views_open(trigger_id=trigger_id, view=blocks.edit_profile(values))