diff options
Diffstat (limited to 'bot/exts')
-rw-r--r-- | bot/exts/events/trivianight/_questions.py | 48 | ||||
-rw-r--r-- | bot/exts/events/trivianight/_scoreboard.py | 41 | ||||
-rw-r--r-- | bot/exts/events/trivianight/trivianight.py | 55 |
3 files changed, 125 insertions, 19 deletions
diff --git a/bot/exts/events/trivianight/_questions.py b/bot/exts/events/trivianight/_questions.py index 2bbff1d7..eaabed4f 100644 --- a/bot/exts/events/trivianight/_questions.py +++ b/bot/exts/events/trivianight/_questions.py @@ -32,7 +32,13 @@ class QuestionButton(Button): super().__init__(label=label, style=discord.ButtonStyle.green) async def callback(self, interaction: Interaction) -> None: - """When a user interacts with the button, this will be called.""" + """ + When a user interacts with the button, this will be called. + + Parameters: + - interaction: an instance of discord.Interaction representing the interaction between the user and the + button. + """ original_message = interaction.message original_embed = original_message.embeds[0] @@ -84,8 +90,13 @@ class QuestionView(View): self.active_question = False - def create_current_question(self) -> Union[Embed, None]: - """Helper function to create the embed for the current question.""" + def create_current_question(self) -> tuple[Embed, int]: + """ + Helper function to create the embed for the current question. + + Returns an embed containing the question along with each answer choice in the form of a view, + along with the integer representing the time limit of the current question. + """ self.current_labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[:len(self.current_question["answers"])] question_embed = Embed( title=f"Question {self.current_question['number']}", @@ -110,8 +121,16 @@ class QuestionView(View): return question_embed, time_limit - def end_question(self) -> tuple[dict, Embed]: - """Returns the dictionaries from the corresponding buttons for those who got it correct.""" + def end_question(self) -> tuple[dict, Embed, int, int]: + """ + Ends the question and displays the statistics on who got the question correct, awards points, etc. + + Returns: + - a dictionary containing all the people who answered and whether or not they got it correct + - an embed displaying the correct answers and the % of people that chose each answer. + - an integer showing the time limit of the current question in seconds + - an integer showing the amount of points the question will award* to those that got it correct + """ labels = self.current_labels label = labels[self.current_question["answers"].index(self.current_question["correct"])] return_dict = {name: (*info, info[0] == label) for name, info in self.users_picked.items()} @@ -165,7 +184,13 @@ class Questions: self.questions = [] def set_questions(self, questions: list) -> None: - """Setting `self.questions` dynamically via a function to set it.""" + """ + Setting `self.questions` dynamically via a function to set it. + + Parameters: + - questions: a list representing all the questions, which is essentially the JSON provided + to load the questions + """ self.questions = questions def next_question(self, number: int = None) -> Union[Embed, None]: @@ -173,6 +198,9 @@ class Questions: Chooses a random unvisited question from the question bank. If the number parameter is specified, it'll head to that specific question. + + Parameters: + - number: An optional integer representing the question number (only used for `.trivianight question` calls) """ if all("visited" in question.keys() for question in self.questions) and number is None: return Embed( @@ -227,7 +255,13 @@ class Questions: return self.view.create_current_question(), self.view def end_question(self) -> Embed: - """Terminates answering of the question and displays the correct answer.""" + """ + Terminates answering of the question and displays the correct answer. + + The function returns an embed containing the information about the question such as the following: + - % of people that chose each option + - the correct answer + """ scores, answer_embed, time_limit, total_points = self.view.end_question() for user, score in scores.items(): time_taken = score[2] diff --git a/bot/exts/events/trivianight/_scoreboard.py b/bot/exts/events/trivianight/_scoreboard.py index 635660a2..40f93475 100644 --- a/bot/exts/events/trivianight/_scoreboard.py +++ b/bot/exts/events/trivianight/_scoreboard.py @@ -20,7 +20,13 @@ class ScoreboardView(View): self.speed = {} async def create_main_leaderboard(self) -> Embed: - """Helper function that iterates through `self.points` to generate the main leaderboard embed.""" + """ + Helper function that iterates through `self.points` to generate the main leaderboard embed. + + The main leaderboard would be formatted like the following: + **1**. @mention of the user (# of points) + along with the 29 other users who made it onto the leaderboard. + """ formatted_string = "" self.points = dict(sorted(self.points.items(), key=lambda item: item[-1], reverse=True)) self.speed = dict(sorted(self.speed.items(), key=lambda item: item[-1])) @@ -44,7 +50,13 @@ class ScoreboardView(View): return main_embed async def _create_speed_embed(self) -> Embed: - """Helper function that iterates through `self.speed` to generate a leaderboard embed.""" + """ + Helper function that iterates through `self.speed` to generate a leaderboard embed. + + The speed leaderboard would be formatted like the following: + **1**. @mention of the user ([average speed as a float with the precision of one decimal point]s) + along with the 29 other users who made it onto the leaderboard. + """ formatted_string = "" for current_placement, (user, time_taken) in enumerate(self.speed.items()): @@ -65,7 +77,12 @@ class ScoreboardView(View): return speed_embed def _get_rank(self, member: Member) -> Embed: - """Gets the member's rank for the points leaderboard and speed leaderboard.""" + """ + Gets the member's rank for the points leaderboard and speed leaderboard. + + Parameters: + - member: An instance of discord.Member representing the person who is trying to get their rank. + """ rank_embed = Embed(title=f"Ranks for {member.display_name}", color=Colours.python_blue) # These are stored as strings so that the last digit can be determined to choose the suffix try: @@ -99,12 +116,26 @@ class ScoreboardView(View): @discord.ui.button(label="Scoreboard for Speed", style=ButtonStyle.green) async def speed_leaderboard(self, button: Button, interaction: Interaction) -> None: - """Send an ephemeral message with the speed leaderboard embed.""" + """ + Send an ephemeral message with the speed leaderboard embed. + + Parameters: + - button: The discord.ui.Button instance representing the `Speed Leaderboard` button. + - interaction: The discord.Interaction instance containing information on the interaction between the user + and the button. + """ await interaction.response.send_message(embed=await self._create_speed_embed(), ephemeral=True) @discord.ui.button(label="What's my rank?", style=ButtonStyle.blurple) async def rank_button(self, button: Button, interaction: Interaction) -> None: - """Send an ephemeral message with the user's rank for the overall points/average speed.""" + """ + Send an ephemeral message with the user's rank for the overall points/average speed. + + Parameters: + - button: The discord.ui.Button instance representing the `What's my rank?` button. + - interaction: The discord.Interaction instance containing information on the interaction between the user + and the button. + """ await interaction.response.send_message(embed=self._get_rank(interaction.user), ephemeral=True) diff --git a/bot/exts/events/trivianight/trivianight.py b/bot/exts/events/trivianight/trivianight.py index ed2bfdbe..a86bd73f 100644 --- a/bot/exts/events/trivianight/trivianight.py +++ b/bot/exts/events/trivianight/trivianight.py @@ -37,7 +37,12 @@ class TriviaNight(commands.Cog): @staticmethod def unicodeify(text: str) -> str: - """Takes `text` and adds zero-width spaces to prevent copy and pasting the question.""" + """ + Takes `text` and adds zero-width spaces to prevent copy and pasting the question. + + Parameters: + - text: A string that represents the question description to 'unicodeify' + """ return "".join( f"{letter}\u200b" if letter not in ('\n', '\t', '`', 'p', 'y') else letter for idx, letter in enumerate(text) @@ -45,7 +50,11 @@ class TriviaNight(commands.Cog): @commands.group(aliases=["tn"], invoke_without_command=True) async def trivianight(self, ctx: commands.Context) -> None: - """No-op subcommand group for organizing different commands.""" + """ + The command group for the Python Discord Trivia Night. + + If invoked without a subcommand (i.e. simply .trivianight), it will explain what the Trivia Night event is. + """ cog_description = Embed( title="What is .trivianight?", description=( @@ -88,6 +97,8 @@ class TriviaNight(commands.Cog): json_text = (await message.attachments[0].read()).decode("utf8") else: json_text = message.content.replace("```", "").replace("json", "").replace("\n", "") + else: + json_text = to_load.replace("```", "").replace("json", "").replace("\n", "") try: serialized_json = loads(json_text) @@ -128,7 +139,12 @@ class TriviaNight(commands.Cog): @trivianight.command() @commands.has_any_role(*TRIVIA_NIGHT_ROLES) async def next(self, ctx: commands.Context) -> None: - """Gets a random question from the unanswered question list and lets user choose the answer.""" + """ + Gets a random question from the unanswered question list and lets the user(s) choose the answer. + + This command will continuously count down until the time limit of the question is exhausted. + However, if `.trivianight stop` is invoked, the counting down is interrupted to show the final results. + """ if self.questions.view.active_question is True: error_embed = Embed( title=choice(NEGATIVE_REPLIES), @@ -162,7 +178,15 @@ class TriviaNight(commands.Cog): @trivianight.command() @commands.has_any_role(*TRIVIA_NIGHT_ROLES) async def question(self, ctx: commands.Context, question_number: int) -> None: - """Gets a question from the question bank depending on the question number provided.""" + """ + Gets a question from the question bank depending on the question number provided. + + The logic of this command is similar to `.trivianight next`, with the only difference being that you need to + specify the question number. + + Parameters: + - question_number: An integer represents the question number to go to (i.e. .trivianight question 5). + """ question = self.questions.next_question(question_number) if isinstance(question, Embed): await ctx.send(embed=question) @@ -187,7 +211,12 @@ class TriviaNight(commands.Cog): @trivianight.command() @commands.has_any_role(*TRIVIA_NIGHT_ROLES) async def list(self, ctx: commands.Context) -> None: - """Displays all the questions from the question bank.""" + """ + Displays all the questions from the question bank. + + Questions are displayed in the following format: + Q(number): Question description | :white_check_mark: if the question was used otherwise :x:. + """ question_list = self.questions.list_questions() if isinstance(question_list, Embed): await ctx.send(embed=question_list) @@ -197,7 +226,11 @@ class TriviaNight(commands.Cog): @trivianight.command() @commands.has_any_role(*TRIVIA_NIGHT_ROLES) async def stop(self, ctx: commands.Context) -> None: - """End the ongoing question to show the correct question.""" + """ + End the ongoing question to show the correct question. + + This command should be used if the question should be ended early or if the time limit fails + """ if self.questions.view.active_question is False: error_embed = Embed( title=choice(NEGATIVE_REPLIES), @@ -212,7 +245,15 @@ class TriviaNight(commands.Cog): @trivianight.command() @commands.has_any_role(*TRIVIA_NIGHT_ROLES) async def end(self, ctx: commands.Context) -> None: - """Ends the trivia night event and displays the scoreboard.""" + """ + Ends the trivia night event and displays the scoreboard view. + + The scoreboard view consists of the two scoreboards with the 30 players who got the highest points and the + 30 players who had the fastest average response time to a question where they got the question right. + + The scoreboard view also has a button where the user can see their own rank, points and average speed if they + didn't make it onto the leaderboard. + """ scoreboard_embed, scoreboard_view = await self.scoreboard.display() await ctx.send(embed=scoreboard_embed, view=scoreboard_view) |