aboutsummaryrefslogtreecommitdiffstats
path: root/bot
diff options
context:
space:
mode:
Diffstat (limited to 'bot')
-rw-r--r--bot/exts/events/trivianight/_questions.py48
-rw-r--r--bot/exts/events/trivianight/_scoreboard.py41
-rw-r--r--bot/exts/events/trivianight/trivianight.py55
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)