Ejemplo n.º 1
0
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})
Ejemplo n.º 2
0
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))
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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()
Ejemplo n.º 5
0
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))
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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))
Ejemplo n.º 8
0
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']))
Ejemplo n.º 9
0
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))
Ejemplo n.º 10
0
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,
    )
Ejemplo n.º 11
0
def trivia_failure(user_id, trigger_id):
    client.chat_postMessage(channel=user_id,
                            text="Your attempt to create a trivia game failed")
Ejemplo n.º 12
0
def review_confirm(user_id, course_code):
    client.chat_postMessage(channel=user_id,
                            text="Review confirmed!",
                            blocks=reviewConfirm(course_code))
Ejemplo n.º 13
0
def say(payload):
    """
    Say something as the SlackBot
    """
    client.chat_postMessage(channel=payload["channel_id"],
                            text=payload["text"])
Ejemplo n.º 14
0
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))