Skip to content

Commit

Permalink
feat: Help Cog (#14)
Browse files Browse the repository at this point in the history
* feat: created help command

* fix: changed name of command

* feat: add function to get puzzles by uni

* fix: order query results by ascending order

* feat: remove puzzle_id parameter. manually create puzzle_id

* fix: check for metameta in uni argument

* feat: add admin command that lists all puzzles

* fix: attempting to fix formatter issue

* fix: further fix attempt

* fix: another formatter fix attempt

* chore: replacing comments

* feat: added to help command

* fix: add descriptions to commands

* fix: ORDER BY used incorrectly

* fix: did not return True upon success

* fix: interaction response was not sent

* feat: add function to get all puzzles in db

* fix: automatically turn puzzle_id uppercase in delete_puzzle

* fix: variable error. uni_puzzles was not iterable

* feat: edited config to take in victory role and channel

* feat: check if team has completed hunt after each submission

* fix: old code gave error (not enough values to unpack)

* feat: add check mark to solved puzzles

* fix: forgot return statements

* feat: ensured teams cannot have the same name as the exec role
  • Loading branch information
eluric authored Mar 4, 2024
1 parent 9f654a2 commit b0c44bd
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 22 deletions.
113 changes: 103 additions & 10 deletions cogs/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@

from typing import Literal

from src.queries.puzzle import get_puzzle, create_puzzle, delete_puzzle
from src.queries.puzzle import (
get_puzzle,
get_all_puzzles,
get_puzzles_by_uni,
create_puzzle,
delete_puzzle,
)

from src.config import config

Expand All @@ -17,24 +23,80 @@ class Admin(commands.GroupCog):
def __init__(self, bot: commands.Bot):
self.bot = bot

def get_id(self, num: int, uni: str):
if num > 9:
return f"{uni}-{num}"
else:
return f"{uni}-0{num}"

@app_commands.command(name="create_puzzle", description="Create a new puzzle")
@commands.has_role(EXEC_ID)
async def create_puzzle(
self,
interaction: discord.Interaction,
puzzle_id: str,
puzzle_name: str,
puzzle_answer: str,
puzzle_author: str,
puzzle_link: str,
uni: Literal["UTS", "UNSW", "USYD", "METAMETA"],
meta: bool = False,
puzzle_author: str = "",
):
await interaction.response.defer()

puzzle_exists = await get_puzzle(puzzle_id)
if puzzle_exists:
await interaction.followup.send(f"Puzzle {puzzle_id} already exists!")
return
"""
puzzle_id will be made to fit the form
[UNI]-[ID_NUM]
e.g USYD-09
UTS-02
UNSW-03
except for the meta which will be
[UNI]-M
e.g USYD-M
AND except for the metameta which will just be
METAMETA
Get all puzzles from the given uni (puzzles are given in ascending order)
start at 1 and count up through each puzzle. if the number matches the
puzzle id, then that id number is not missing and we keep going.
if we finish going through all the puzzles with none missing then
id_num will be equal to the next number in the sequence.
if we see a puzzle whose id doesn't match the current id_num, then
that means a puzzle with that id was removed and we now fill that hole.
"""

# check for metameta
puzzle_id = ""
if uni == "METAMETA":
puzzle_id = uni
metameta = await get_puzzle(uni)

if metameta:
await interaction.followup.send(f"{uni} already exists!")
return

# if the puzzle is the meta, then skip these steps
elif meta:
puzzle_id = f"{uni}-M"

# check if meta already exists
meta_puzz = await get_puzzle(puzzle_id)
if meta_puzz:
await interaction.followup.send(
f"Meta puzzle for {uni} already exists!"
)
return
else:
uni_puzzles = await get_puzzles_by_uni(uni)
id_num = 1
for puzz in uni_puzzles:
if puzz.puzzle_id == self.get_id(id_num, uni):
id_num += 1
else:
break

puzzle_id = self.get_id(id_num, uni)

await create_puzzle(
puzzle_id, puzzle_name, puzzle_answer, puzzle_author, puzzle_link, uni
Expand All @@ -49,13 +111,44 @@ async def create_puzzle(
async def delete_puzzle(self, interaction: discord.Interaction, puzzle_id: str):
await interaction.response.defer()

deleted = await delete_puzzle(puzzle_id)
deleted = await delete_puzzle(puzzle_id.upper())

if not deleted:
await interaction.followup.send(f"Puzzle {puzzle_id} does not exist.")
await interaction.followup.send(
f"Puzzle {puzzle_id.upper()} does not exist."
)
return

await interaction.followup.send(f"Puzzle {puzzle_id} deleted.")
await interaction.followup.send(f"Puzzle {puzzle_id.upper()} deleted.")

@app_commands.command(name="list_puzzles", description="List all created puzzles.")
@commands.has_role(EXEC_ID)
async def list_puzzles(self, interaction: discord.Interaction):
await interaction.response.defer()

all_puzzles = await get_all_puzzles()

all_puzzle_ids = []
all_puzzle_name_links = []
all_puzzle_answers = []
for puzzle in all_puzzles:
all_puzzle_ids.append(puzzle.puzzle_id)
all_puzzle_name_links.append(
f"[{puzzle.puzzle_name}]({puzzle.puzzle_link})"
)
all_puzzle_answers.append(f"||{puzzle.puzzle_answer}||")

puzzles_embed = discord.Embed(
colour=discord.Color.random(),
title="List of Puzzles",
description="Puzzles sorted by uni.",
)

puzzles_embed.add_field(name="IDs", value="\n".join(all_puzzle_ids))
puzzles_embed.add_field(name="Puzzles", value="\n".join(all_puzzle_name_links))
puzzles_embed.add_field(name="Answers", value="\n".join(all_puzzle_answers))

await interaction.followup.send(embed=puzzles_embed)

@app_commands.command(
name="set_hint_channel",
Expand Down
51 changes: 51 additions & 0 deletions cogs/help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import discord
from discord import app_commands
from discord.ext import commands

EXEC_ID = "Executives"


class Help(commands.GroupCog):
def __init__(self, bot: commands.Bot):
self.bot = bot

@app_commands.command(name="commands", description="Get a list of bot commands")
async def help_command(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)

user = interaction.user
user_roles = [role.name for role in user.roles]

admin_commands = [
"`/admin create_puzzle [PUZZLE_NAME] [PUZZLE_ANSWER] [PUZZLE_LINK] [UNI] [META] [PUZZLE_AUTHOR]` - Creates a new puzzle with given arguments.",
"`/admin delete_puzzle [PUZZLE_ID]` - Deletes the puzzle with the given ID.",
"`/admin list_puzzles` - Lists all created puzzles along with their answers"
"`/admin set_hint_channel` - Sets the current channel to be the new channel for receiving hint requests.",
]

team_commands = [
"`/team create [TEAM_NAME]` - Create a new team with the given name.",
"`/team leave` - Leave your current team. Deletes the team if no more members.",
"`/team invite [INVITED_USER]` - Invites the given user into your team. They can accept or reject.",
]

puzzle_commands = [
"`/puzzle submit [PUZZLE_ID] [ANSWER]` - Submits and checks your answer for the specified puzzle.",
"`/puzzle list` - Lists all the puzzles available.",
"`/puzzle hint` - Sends a hint request to the execs.",
]

help_embed = discord.Embed(description="List of Commands")

if EXEC_ID in user_roles:
help_embed.colour = discord.Color.green()
help_embed.add_field(name="Admin", value="\n".join(admin_commands))

help_embed.add_field(name="Team", value="\n".join(team_commands))
help_embed.add_field(name="Puzzle", value="\n".join(puzzle_commands))

await interaction.followup.send(embed=help_embed, ephemeral=True)


async def setup(bot: commands.Bot):
await bot.add_cog(Help(bot))
65 changes: 57 additions & 8 deletions cogs/puzzle.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
from discord.ext import commands
from discord import app_commands

from src.queries.puzzle import get_puzzle, create_puzzle
from src.queries.puzzle import get_puzzle, get_completed_puzzles
from src.queries.submission import (
create_submission,
find_submissions_by_player_id_and_puzzle_id,
)
from src.queries.player import get_player
from src.queries.team import get_team, get_team_members

from src.utils.decorators import in_team_channel
from src.context.puzzle import can_access_puzzle, get_accessible_puzzles

Expand All @@ -29,6 +31,8 @@ async def submit_answer(
self, interaction: discord.Interaction, puzzle_id: str, answer: str
):
await interaction.response.defer()

puzzle_id = puzzle_id.upper()
puzzle = await get_puzzle(puzzle_id)
player = await get_player(str(interaction.user.id))
if not puzzle or not await can_access_puzzle(puzzle, player.team_name):
Expand Down Expand Up @@ -57,6 +61,44 @@ async def submit_answer(
if not submission_is_correct:
return await interaction.followup.send("The submitted answer is incorrect!")

# check if they have solved all the metas
if puzzle_id == "UTS-M":
await interaction.followup.send(
"The submitted answer is... CORRECT! You've completed all the UTS puzzles and can now see the USYD puzzles."
)
return

elif puzzle_id == "USYD-M":
await interaction.followup.send(
"The submitted answer is... CORRECT! You've completed all the USYD puzzles and can now see the UNSW puzzles."
)
return

elif puzzle_id == "UNSW-M":
await interaction.followup.send(
"The submitted answer is... CORRECT! You've completed all the metas and can now access the METAMETA!"
)
return

elif puzzle_id == "METAMETA":
team = await get_team(player.team_name)
await interaction.client.get_channel(config["ADMIN_CHANNEL_ID"]).send(
f"Team <#{team.text_channel_id}> has finished all the puzzles!"
)

# give team the victor role
guild = interaction.guild
team_members = await get_team_members(player.team_name)

for team_member in team_members:
discord_member = guild.get_member(int(team_member.discord_id))
await discord_member.add_roles(guild.get_role(config["VICTOR_ROLE_ID"]))

await interaction.followup.send(
f"Congratulations! You've finished all the puzzles. Feel free to head over to the <#{config['VICTOR_TEXT_CHANNEL_ID']}> in the Victory Lounge."
)
return

await interaction.followup.send("The submitted answer is ...CORRECT!")

@app_commands.command(name="list", description="List the available puzzles")
Expand All @@ -67,15 +109,22 @@ async def list_puzzles(self, interaction: discord.Interaction):
puzzles = await get_accessible_puzzles(player.team_name)
embed = discord.Embed(title="Current Puzzles", color=discord.Color.greyple())

puzzle_ids, puzzle_links = zip(
*[
(puzzle.puzzle_id, f"[{puzzle.puzzle_name}]({puzzle.puzzle_link})")
for puzzle in puzzles
]
)
puzzle_ids = []
puzzle_name_links = []
for puzzle in puzzles:
submissions = await find_submissions_by_player_id_and_puzzle_id(
str(player.discord_id), puzzle.puzzle_id
)

if any([submission.submission_is_correct for submission in submissions]):
puzzle_ids.append(f":white_check_mark: {puzzle.puzzle_id}")
else:
puzzle_ids.append(puzzle.puzzle_id)

puzzle_name_links.append(f"[{puzzle.puzzle_name}]({puzzle.puzzle_link})")

embed.add_field(name="ID", value="\n".join(puzzle_ids), inline=True)
embed.add_field(name="Puzzles", value="\n".join(puzzle_links), inline=True)
embed.add_field(name="Puzzles", value="\n".join(puzzle_name_links), inline=True)
await interaction.followup.send(embed=embed)

@app_commands.command(name="hint", description="Request a hint for the puzzle!")
Expand Down
19 changes: 15 additions & 4 deletions cogs/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

BOT_ID = 1208986388226121849
MAX_TEAM_SIZE = 6
EXEC_ID = "Executives"


class Team(commands.GroupCog):
def __init__(self, bot: commands.Bot):
self.bot = bot

@app_commands.command(name="create")
@app_commands.command(name="create", description="Create a new team.")
async def create_team(self, interaction: discord.Interaction, team_name: str):
# check that user is not already in a team
user = interaction.user
Expand All @@ -31,10 +32,18 @@ async def create_team(self, interaction: discord.Interaction, team_name: str):

# check team name is not already taken
if await team_query.get_team(team_name):
return await interaction.followup.send(
await interaction.followup.send(
f'Team "{team_name}" is already taken. Please choose a different team name.',
ephemeral=True,
)
return

# additionally check that the name is not the same as the exec role
if team_name.lower() == EXEC_ID.lower():
await interaction.followup.send(
f'You cannot name yourself "{team_name}". Please choose a different team name.'
)
return

# if name not taken, add check for profanity and such
team_role = await guild.create_role(name=team_name)
Expand Down Expand Up @@ -69,7 +78,7 @@ async def create_team(self, interaction: discord.Interaction, team_name: str):
content=f'Team "{team_name}" created successfully!', ephemeral=True
)

@app_commands.command(name="leave")
@app_commands.command(name="leave", description="Leave your current team.")
async def leave_team(self, interaction: discord.Interaction):
# remove team role from user
user = interaction.user
Expand Down Expand Up @@ -124,7 +133,9 @@ async def leave_team(self, interaction: discord.Interaction):
# also delete the team
await team_query.remove_team(team_name)

@app_commands.command(name="invite")
@app_commands.command(
name="invite", description="Invite another member into your team!"
)
async def invite(
self, interaction: discord.Interaction, invited_user: discord.Member
):
Expand Down
2 changes: 2 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
"DISCORD_TOKEN": os.environ["DISCORD_TOKEN"],
"DATABASE_URL": os.environ["DATABASE_URL"],
"ADMIN_CHANNEL_ID": int(os.environ["ADMIN_CHANNEL_ID"]),
"VICTOR_ROLE_ID": int(os.environ["VICTOR_ROLE_ID"]),
"VICTOR_TEXT_CHANNEL_ID": int(os.environ["VICTOR_TEXT_CHANNEL_ID"]),
}
Loading

0 comments on commit b0c44bd

Please sign in to comment.