Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdb54658ee | ||
|
|
4ce37ee3ea | ||
|
|
c62b87c7f7 | ||
|
|
ba04858328 | ||
|
|
a12fca6220 | ||
|
|
136702654e | ||
|
|
23adad403a | ||
|
|
3a72d9f0e7 | ||
|
|
da0acb2827 | ||
|
|
0baffe96f3 |
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install Docker
|
- name: Install Docker
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install Docker
|
- name: Install Docker
|
||||||
|
|||||||
+16
-27
@@ -1,48 +1,37 @@
|
|||||||
"""
|
"""Commands for creating items in bot."""
|
||||||
Commands for creating items in bot.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
from sqlalchemy import exc, select
|
from sqlalchemy import exc, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
# pylint: disable=import-error
|
|
||||||
from cogs.submission.submission_views import SubmissionCreateButton
|
from cogs.submission.submission_views import SubmissionCreateButton
|
||||||
from database_setup.sql_data_classes import Category, CategoryGroup, engine
|
from database_setup.sql_data_classes import Category, CategoryGroup, engine
|
||||||
from utilities.utils import MAX_LINE_LENGTH, autofill_groups, send_highscore_message
|
from utilities.utils import MAX_LINE_LENGTH, autofill_groups, send_highscore_message
|
||||||
|
|
||||||
|
|
||||||
class CreateCog(commands.Cog, name="Create Cog"):
|
class CreateCog(commands.Cog, name="Create Cog"):
|
||||||
"""
|
"""Class holds commands for creating things."""
|
||||||
Class holds commands for creating things.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, bot: commands.Bot) -> None:
|
def __init__(self, bot: commands.Bot) -> None:
|
||||||
"""
|
"""Default constructor for CreateCog class."""
|
||||||
Default constructor for CreateCog class.
|
|
||||||
"""
|
|
||||||
self._bot = bot
|
self._bot = bot
|
||||||
|
|
||||||
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
|
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
|
||||||
async def create(self, interaction: nextcord.Interaction):
|
async def create(self, interaction: nextcord.Interaction) -> None:
|
||||||
"""
|
"""Parent slash command for creating things. Never called."""
|
||||||
Parent slash command for creating things. Never called.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# need all these arguments because discord command.
|
|
||||||
# pylint: disable=too-many-arguments
|
|
||||||
@create.subcommand(description="Create a new Category. Must not exist already in Group.")
|
@create.subcommand(description="Create a new Category. Must not exist already in Group.")
|
||||||
async def category(
|
async def category( # noqa: PLR0913 need all these arguments because discord command.
|
||||||
self,
|
self,
|
||||||
interaction: nextcord.Interaction,
|
interaction: nextcord.Interaction,
|
||||||
group_id: str = nextcord.SlashOption(name="group", description="Group to add Category to.", required=True),
|
group_id: str = nextcord.SlashOption(name="group", description="Group to add Category to.", required=True),
|
||||||
category_name: str = nextcord.SlashOption(description="Name of new Category.", required=True),
|
category_name: str = nextcord.SlashOption(description="Name of new Category.", required=True),
|
||||||
is_ascending: bool = nextcord.SlashOption(description="If lower value is a better score.", required=True),
|
is_ascending: bool = nextcord.SlashOption(description="If lower value is a better score.", required=True), # noqa: FBT001
|
||||||
is_time_record: bool = nextcord.SlashOption(
|
is_time_record: bool = nextcord.SlashOption( # noqa: FBT001
|
||||||
description="If the score should be shown in time format. Cannot be changed.", required=True
|
description="If the score should be shown in time format. Cannot be changed.", required=True
|
||||||
),
|
),
|
||||||
is_validated: bool = nextcord.SlashOption(
|
is_validated: bool = nextcord.SlashOption( # noqa: FBT001
|
||||||
description="If scores need to be validated before posted.", required=True
|
description="If scores need to be validated before posted.", required=True
|
||||||
),
|
),
|
||||||
size: int = nextcord.SlashOption(
|
size: int = nextcord.SlashOption(
|
||||||
@@ -50,8 +39,9 @@ class CreateCog(commands.Cog, name="Create Cog"):
|
|||||||
),
|
),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a new category to be placed into group_id group. Fails if the category name already exists within the
|
Creates a new category to be placed into group_id group.
|
||||||
group. Then calls send_highscore_message().
|
|
||||||
|
Fails if the category name already exists within the group. Then calls send_highscore_message().
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
interaction (nextcord.Interaction): Interaction for the command.
|
interaction (nextcord.Interaction): Interaction for the command.
|
||||||
@@ -89,8 +79,6 @@ class CreateCog(commands.Cog, name="Create Cog"):
|
|||||||
f'"{category_name}" already exists in "{group.GroupName}". Please rename.', ephemeral=True
|
f'"{category_name}" already exists in "{group.GroupName}". Please rename.', ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# need all these arguments because discord command.
|
|
||||||
# pylint: disable=too-many-arguments
|
|
||||||
@create.subcommand(description="Create a new Category Group. Must not exist already for Guild.")
|
@create.subcommand(description="Create a new Category Group. Must not exist already for Guild.")
|
||||||
async def group(
|
async def group(
|
||||||
self,
|
self,
|
||||||
@@ -118,8 +106,9 @@ class CreateCog(commands.Cog, name="Create Cog"):
|
|||||||
),
|
),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a new category group to be placed into the database with the guild's id. Fails if the category group
|
Creates a new category group to be placed into the database with the guild's id.
|
||||||
name already exists within the guild. Then calls send_highscore_message().
|
|
||||||
|
Fails if the category group name already exists within the guild. Then calls send_highscore_message().
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
interaction (nextcord.Interaction): Interaction for the command.
|
interaction (nextcord.Interaction): Interaction for the command.
|
||||||
@@ -156,7 +145,7 @@ class CreateCog(commands.Cog, name="Create Cog"):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@category.on_autocomplete("group_id")
|
@category.on_autocomplete("group_id")
|
||||||
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str):
|
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
|
||||||
"""
|
"""
|
||||||
Collects list of table names and compares to input table name for autocomplete assistance.
|
Collects list of table names and compares to input table name for autocomplete assistance.
|
||||||
|
|
||||||
|
|||||||
+22
-32
@@ -1,6 +1,4 @@
|
|||||||
"""
|
"""Commands for deleting groups/categories/scores in database and messages in discord."""
|
||||||
Commands for deleting groups/categories/scores in database and messages in discord..
|
|
||||||
"""
|
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
@@ -21,26 +19,18 @@ from .views.delete_confirm import DeleteConfirmView, create_delete_id
|
|||||||
|
|
||||||
|
|
||||||
class DeleteCog(commands.Cog, name="Table Management"):
|
class DeleteCog(commands.Cog, name="Table Management"):
|
||||||
"""
|
"""Class holds commands for deleting."""
|
||||||
Class holds commands for deleting.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""
|
"""Default constructor for DeleteCog class."""
|
||||||
Default constructor for DeleteCog class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
|
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
|
||||||
async def delete(self, interaction: nextcord.Interaction):
|
async def delete(self, interaction: nextcord.Interaction) -> None:
|
||||||
"""
|
"""Parent slash command for deleting things. Never called."""
|
||||||
Parent slash command for deleting things. Never called.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@delete.subcommand()
|
@delete.subcommand()
|
||||||
async def all(self, interaction: nextcord.Interaction):
|
async def all(self, interaction: nextcord.Interaction) -> None:
|
||||||
"""
|
"""Subcommand parent for deleting all of a thing. Never called."""
|
||||||
Subcommand parent for deleting all of a thing. Never called.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@all.subcommand(description="Clears highscores channel of all table messages. Does not change data.")
|
@all.subcommand(description="Clears highscores channel of all table messages. Does not change data.")
|
||||||
async def channel_messages(
|
async def channel_messages(
|
||||||
@@ -55,6 +45,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Deletes all group messages from channel. Does not change data.
|
Deletes all group messages from channel. Does not change data.
|
||||||
|
|
||||||
Does not delete highscores channel id. Uses current channel if not supplied a channel.
|
Does not delete highscores channel id. Uses current channel if not supplied a channel.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -95,10 +86,11 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
user_name: str = nextcord.SlashOption(
|
user_name: str = nextcord.SlashOption(
|
||||||
description="User to remove scores for. Note: This is not always a user's discord name.", required=True
|
description="User to remove scores for. Note: This is not always a user's discord name.", required=True
|
||||||
),
|
),
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Searches all of guild's scores data for any score from the user. Removes. Updates table
|
Searches all of guild's scores data for any score from the user and removes.
|
||||||
message is user was removed from that table.
|
|
||||||
|
Updates table message is user was removed from that table.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
interaction (nextcord.Interaction): Interaction that invoked the command.
|
interaction (nextcord.Interaction): Interaction that invoked the command.
|
||||||
@@ -157,10 +149,8 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
await interaction.response.send_modal(DeleteConfirmView(custom_id=create_delete_id(int(group_id))))
|
await interaction.response.send_modal(DeleteConfirmView(custom_id=create_delete_id(int(group_id))))
|
||||||
|
|
||||||
@delete.subcommand()
|
@delete.subcommand()
|
||||||
async def user(self, interaction: nextcord.Interaction):
|
async def user(self, interaction: nextcord.Interaction) -> None:
|
||||||
"""
|
"""Subcommand parent for deleting user information. Never called."""
|
||||||
Subcommand parent for deleting user information. Never called.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@user.subcommand(description="Deletes a user's scores from a category.")
|
@user.subcommand(description="Deletes a user's scores from a category.")
|
||||||
async def from_category(
|
async def from_category(
|
||||||
@@ -171,7 +161,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
name="category", description="Category to delete user from.", required=True
|
name="category", description="Category to delete user from.", required=True
|
||||||
),
|
),
|
||||||
user_name: str = nextcord.SlashOption(description="User to delete.", required=True),
|
user_name: str = nextcord.SlashOption(description="User to delete.", required=True),
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Removes a user from a category. Uses autofill functions to fill out group_id, category_id, and user_name.
|
Removes a user from a category. Uses autofill functions to fill out group_id, category_id, and user_name.
|
||||||
|
|
||||||
@@ -205,9 +195,9 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
interaction: nextcord.Interaction,
|
interaction: nextcord.Interaction,
|
||||||
group_id: str = nextcord.SlashOption(name="group", description="Group to delete user from.", required=True),
|
group_id: str = nextcord.SlashOption(name="group", description="Group to delete user from.", required=True),
|
||||||
user_name: str = nextcord.SlashOption(description="User to delete.", required=True),
|
user_name: str = nextcord.SlashOption(description="User to delete.", required=True),
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Removes user from a goup. Uses autofill functions to fill out group_id and user_name.
|
Removes user from a group. Uses autofill functions to fill out group_id and user_name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
interaction (nextcord.Interaction): Interaction that invoked the command.
|
interaction (nextcord.Interaction): Interaction that invoked the command.
|
||||||
@@ -233,7 +223,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
@from_group.on_autocomplete("group_id")
|
@from_group.on_autocomplete("group_id")
|
||||||
@category.on_autocomplete("group_id")
|
@category.on_autocomplete("group_id")
|
||||||
@group.on_autocomplete("group_id")
|
@group.on_autocomplete("group_id")
|
||||||
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str):
|
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
|
||||||
"""
|
"""
|
||||||
Autocomplete function for table names.
|
Autocomplete function for table names.
|
||||||
|
|
||||||
@@ -245,7 +235,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
|
|
||||||
@from_category.on_autocomplete("category_id")
|
@from_category.on_autocomplete("category_id")
|
||||||
@category.on_autocomplete("category_id")
|
@category.on_autocomplete("category_id")
|
||||||
async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str):
|
async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str) -> None:
|
||||||
"""
|
"""
|
||||||
Autocomplete function for category names.
|
Autocomplete function for category names.
|
||||||
|
|
||||||
@@ -262,7 +252,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
@from_group.on_autocomplete("user_name")
|
@from_group.on_autocomplete("user_name")
|
||||||
async def user_autocomplete(
|
async def user_autocomplete(
|
||||||
self, interaction: nextcord.Interaction, user_name: str, group_id: str, category_id: str | None = None
|
self, interaction: nextcord.Interaction, user_name: str, group_id: str, category_id: str | None = None
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Autocomplete functions for user names.
|
Autocomplete functions for user names.
|
||||||
|
|
||||||
@@ -279,12 +269,12 @@ class DeleteCog(commands.Cog, name="Table Management"):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@users.on_autocomplete("user_name")
|
@users.on_autocomplete("user_name")
|
||||||
async def all_users_autocomplete(self, interaction: nextcord.Interaction, username: str):
|
async def all_users_autocomplete(self, interaction: nextcord.Interaction, username: str) -> None:
|
||||||
"""
|
"""
|
||||||
Sends response from autofill_users_in_guild back to update_username for autocompletion of old_username.
|
Sends response from autofill_users_in_guild back to update_username for autocompletion of old_username.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
interaction (nextcord.Interaction): Interaction with command.
|
interaction (nextcord.Interaction): Interaction with command.
|
||||||
old_username (str): String to run autocompletion against.
|
username (str): String to run autocompletion against.
|
||||||
"""
|
"""
|
||||||
await interaction.response.send_autocomplete(autofill_users_in_guild(interaction.guild_id, username))
|
await interaction.response.send_autocomplete(autofill_users_in_guild(interaction.guild_id, username))
|
||||||
|
|||||||
@@ -0,0 +1,218 @@
|
|||||||
|
"""Contains command for submitting a score."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import nextcord
|
||||||
|
from nextcord.ext import commands
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from cogs.submission.verification_view import VerificationView
|
||||||
|
from database_setup.sql_data_classes import Category, CategoryGroup, Guild, Submission, engine
|
||||||
|
from utilities.utils import autofill_categories, autofill_groups, submit_score
|
||||||
|
|
||||||
|
|
||||||
|
class SubmissionCog(commands.Cog):
|
||||||
|
"""Class holds command for submitting a score."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
@nextcord.slash_command()
|
||||||
|
async def submit(
|
||||||
|
self,
|
||||||
|
interaction: nextcord.Interaction,
|
||||||
|
group_id: str = nextcord.SlashOption(name="Group", required=True, description="Group to submit score to."),
|
||||||
|
category_id: str = nextcord.SlashOption(
|
||||||
|
name="Category", required=True, description="Category to submit score to."
|
||||||
|
),
|
||||||
|
score: str = nextcord.SlashOption(required=True, description="Score to submit"),
|
||||||
|
username: str = nextcord.SlashOption(
|
||||||
|
required=False, description="Modified username if submitting under not your discord name."
|
||||||
|
),
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Submit a highscore.
|
||||||
|
|
||||||
|
Sets the username if one isn't passed in. Checks if a submission needs verified. Ensures that the score is
|
||||||
|
following the correct format. Then either creates the verification request or submits score if verification
|
||||||
|
isn't required.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
interaction (nextcord.Interaction): Interaction of the command.
|
||||||
|
group_id (str): Is SlashOption. Group that the score category belongs in.
|
||||||
|
category_id (str): Is SlashOption. Category to submit the score in.
|
||||||
|
score (str): Is SlashOption. The score.
|
||||||
|
username (str, optional): Is SlashOption. Username to associate with the score.
|
||||||
|
"""
|
||||||
|
# fill out username if not passed in
|
||||||
|
if username is None:
|
||||||
|
username = interaction.user.display_name
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
stmt = (
|
||||||
|
select(Category).where(Category.GroupId == int(group_id)).where(Category.CategoryId == int(category_id))
|
||||||
|
)
|
||||||
|
|
||||||
|
category: Category = session.scalar(stmt)
|
||||||
|
|
||||||
|
# check if verification is required. if no picture, deny
|
||||||
|
if category.IsValidated and not interaction.message.attachments > 0:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
"This category required verification. Please attach a picture and re-submit.", ephemeral=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# check with regex if score is in correct format
|
||||||
|
score = await self.verify_score(interaction, score, is_time_record=category.IsTimeRecord)
|
||||||
|
|
||||||
|
# check if verify_score was successful. It replies with corrections required itself.
|
||||||
|
if score is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create submission verification request or submit score if verification not needed.
|
||||||
|
if not category.IsValidated:
|
||||||
|
await submit_score(interaction, group_id, category_id, username, score)
|
||||||
|
await interaction.response.send_message("Score has been submitted!", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id)
|
||||||
|
verification_channel_id = session.scalar(stmt)
|
||||||
|
|
||||||
|
channel = interaction.guild.get_channel(verification_channel_id)
|
||||||
|
|
||||||
|
if channel is None:
|
||||||
|
error_response = (
|
||||||
|
"Registered Verification channel does not exist or was never registered. Contact server admins."
|
||||||
|
)
|
||||||
|
await interaction.response.send_message(error_response, ephemeral=True)
|
||||||
|
|
||||||
|
# Send the verification message to admins
|
||||||
|
verification_message = await channel.send(
|
||||||
|
files=[await attach.to_file() for attach in interaction.message.attachments],
|
||||||
|
view=VerificationView(),
|
||||||
|
embeds=self.create_embed(interaction, group_id, category_id, score, username, session),
|
||||||
|
content=f"Submission from <@{interaction.user.id}>.",
|
||||||
|
)
|
||||||
|
await interaction.response.send_message(content="Score submitted!", ephemeral=True)
|
||||||
|
|
||||||
|
# Save the submission data for later use.
|
||||||
|
submission: Submission = Submission(
|
||||||
|
CategoryId=category_id,
|
||||||
|
GroupId=group_id,
|
||||||
|
GuildId=interaction.guild_id,
|
||||||
|
MessageId=verification_message.id,
|
||||||
|
Score=score,
|
||||||
|
UserName=username,
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(submission)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
def create_embed( # noqa: PLR0913
|
||||||
|
self,
|
||||||
|
interaction: nextcord.Interaction,
|
||||||
|
group_id: str,
|
||||||
|
category_id: str,
|
||||||
|
score: str,
|
||||||
|
username: str,
|
||||||
|
session: Session,
|
||||||
|
) -> nextcord.Embed:
|
||||||
|
"""
|
||||||
|
Creates an embed containing submission data for human verification.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
interaction (nextcord.Interaction): Discord interaction of the command.
|
||||||
|
group_id (str): Used to get group name.
|
||||||
|
category_id (str): Used to get category name.
|
||||||
|
score (str): Score to set.
|
||||||
|
username (str): Username to set.
|
||||||
|
session (Session): Open connection to database.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
nextcord.Embed: Embed containing human readable submission data.
|
||||||
|
"""
|
||||||
|
embed = nextcord.Embed(title="Highscores Submission", description="Review fields and accept.")
|
||||||
|
embed.add_field(name="User Name", value=username, inline=False)
|
||||||
|
|
||||||
|
# Get group name from database
|
||||||
|
stmt = (
|
||||||
|
select(CategoryGroup.GroupName)
|
||||||
|
.where(CategoryGroup.GuildId == interaction.guild_id)
|
||||||
|
.where(CategoryGroup.GroupId == group_id)
|
||||||
|
)
|
||||||
|
embed.add_field(name="Group", value=session.scalar(stmt), inline=False)
|
||||||
|
|
||||||
|
# Get category name from database
|
||||||
|
stmt = (
|
||||||
|
select(Category.CategoryName).where(Category.GroupId == group_id).where(Category.CategoryId == category_id)
|
||||||
|
)
|
||||||
|
embed.add_field(name="Category", value=session.scalar(stmt), inline=False)
|
||||||
|
embed.add_field(name="Score", value=score, inline=False)
|
||||||
|
return embed
|
||||||
|
|
||||||
|
async def verify_score(self, interaction: nextcord.Interaction, score: str, *, is_time_record: bool) -> str | None:
|
||||||
|
"""
|
||||||
|
Uses regex to verify that scores are in the correct format.
|
||||||
|
|
||||||
|
Handles responding to the interaction if the format is incorrect. Also standardizes the time record.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
interaction (nextcord.Interaction): Discord interaction of the command.
|
||||||
|
score (str): String representation of the score being verified.
|
||||||
|
is_time_record (bool): Whether the score needs to be verified/formatted as a time record.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: String of corrected score. None if formatted incorrectly.
|
||||||
|
"""
|
||||||
|
time_regex = re.search("^([0-9]{1,2}:[0-9]|[0-9])?[0-9](\\.[0-9])?$", score)
|
||||||
|
int_regex = re.search("^[0-9]+$", score)
|
||||||
|
if time_regex is None and is_time_record:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
"Time record must follow format MM:SS.ms, SS.ms, or SS.", ephemeral=True
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
if int_regex is None and not is_time_record:
|
||||||
|
await interaction.response.send_message("Number record must only be number.", ephemeral=True)
|
||||||
|
return None
|
||||||
|
if is_time_record:
|
||||||
|
# standardize time score format.
|
||||||
|
if re.search("^[1-9]?[0-9]:[0-9]{2}(\\.[0-9])?$", score):
|
||||||
|
# score is already fine.
|
||||||
|
pass
|
||||||
|
elif re.search("^[0-9]{2}(\\.[0-9])?$", score):
|
||||||
|
# need to add leading colon and 0
|
||||||
|
score = "0:" + score
|
||||||
|
elif re.search("^[0-9](\\.[0-9])?$", score):
|
||||||
|
# need to add 0 in seconds and leading colon and 0
|
||||||
|
score = "0:0" + score
|
||||||
|
elif re.search("^[0]?[0-9]:[0-9]{2}(\\.[0-9])?$", score):
|
||||||
|
# if there is a minute 10ths place 0, remove
|
||||||
|
score = score.lstrip("0")
|
||||||
|
|
||||||
|
return score
|
||||||
|
|
||||||
|
@submit.on_autocomplete("group_id")
|
||||||
|
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
|
||||||
|
"""
|
||||||
|
Autocomplete function for table names.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
interaction (nextcord.Interaction): Interaction that invoked the command.
|
||||||
|
group_id (str): String to be autocompleted with Group name.
|
||||||
|
"""
|
||||||
|
await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id))
|
||||||
|
|
||||||
|
@submit.on_autocomplete("category_id")
|
||||||
|
async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str) -> None:
|
||||||
|
"""
|
||||||
|
Autocomplete function for category names.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
interaction (nextcord.Interaction): Interaction that invoked the command.
|
||||||
|
category_id (str): String to be autocompleted with Category name.
|
||||||
|
group_id (str): Group to find Category from.
|
||||||
|
"""
|
||||||
|
await interaction.response.send_autocomplete(
|
||||||
|
autofill_categories(interaction.guild_id, int(group_id), category_id)
|
||||||
|
)
|
||||||
@@ -12,8 +12,8 @@ from sqlalchemy.orm import Session
|
|||||||
|
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
from classes.submission import SubmissionState
|
from classes.submission import SubmissionState
|
||||||
from main import submission_objects
|
|
||||||
from database_setup.sql_data_classes import Category, Guild, engine
|
from database_setup.sql_data_classes import Category, Guild, engine
|
||||||
|
from main import submission_objects
|
||||||
|
|
||||||
|
|
||||||
class SubmissionListener(commands.Cog, name="Message Listener"):
|
class SubmissionListener(commands.Cog, name="Message Listener"):
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from sqlalchemy.orm import Session, aliased
|
|||||||
|
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
from classes.submission import Submission, SubmissionState
|
from classes.submission import Submission, SubmissionState
|
||||||
from main import submission_objects
|
|
||||||
from database_setup.sql_data_classes import Category, CategoryGroup, Guild, engine
|
from database_setup.sql_data_classes import Category, CategoryGroup, Guild, engine
|
||||||
|
from main import submission_objects
|
||||||
|
|
||||||
from .verification_view import VerificationView
|
from .verification_view import VerificationView
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""View for verification buttons"""
|
"""View for verification buttons."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
|
|
||||||
@@ -7,18 +7,18 @@ from utilities.utils import submit_score
|
|||||||
|
|
||||||
|
|
||||||
class VerificationView(nextcord.ui.View):
|
class VerificationView(nextcord.ui.View):
|
||||||
"""Defines views for verifying score submissions"""
|
"""Defines views for verifying score submissions."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__(timeout=None)
|
super().__init__(timeout=None)
|
||||||
|
|
||||||
@nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny-button-submission")
|
@nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny-button-submission")
|
||||||
async def deny_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction): # pylint: disable=unused-argument
|
async def deny_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
|
||||||
"""Button for denying score submission."""
|
"""Button for denying score submission."""
|
||||||
await interaction.message.delete()
|
await interaction.message.delete()
|
||||||
|
|
||||||
@nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve-button-submission")
|
@nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve-button-submission")
|
||||||
async def approve_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction): # pylint: disable=unused-argument
|
async def approve_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
|
||||||
"""Button for approving score submission."""
|
"""Button for approving score submission."""
|
||||||
for embed in interaction.message.embeds:
|
for embed in interaction.message.embeds:
|
||||||
content = embed.to_dict()
|
content = embed.to_dict()
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ Base = automap_base()
|
|||||||
Base.prepare(autoload_with=engine)
|
Base.prepare(autoload_with=engine)
|
||||||
|
|
||||||
# define the classes
|
# define the classes
|
||||||
Guild = Base.classes.Guilds
|
|
||||||
CategoryGroup = Base.classes.CategoryGroups
|
|
||||||
Category = Base.classes.Categories
|
Category = Base.classes.Categories
|
||||||
|
CategoryGroup = Base.classes.CategoryGroups
|
||||||
|
Guild = Base.classes.Guilds
|
||||||
Score = Base.classes.Scores
|
Score = Base.classes.Scores
|
||||||
|
Submission = Base.classes.Submissions
|
||||||
|
|||||||
@@ -42,4 +42,13 @@ CREATE TABLE Scores(
|
|||||||
CategoryId int unsigned not null,
|
CategoryId int unsigned not null,
|
||||||
FOREIGN KEY (CategoryId) REFERENCES Categories(CategoryId) ON DELETE CASCADE,
|
FOREIGN KEY (CategoryId) REFERENCES Categories(CategoryId) ON DELETE CASCADE,
|
||||||
PRIMARY KEY (CategoryId, UserName)
|
PRIMARY KEY (CategoryId, UserName)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE Submissions(
|
||||||
|
CategoryId int unsigned not null,
|
||||||
|
GroupId int unsigned not null,
|
||||||
|
GuildId int unsigned not null,
|
||||||
|
MessageId int unsigned not null,
|
||||||
|
Score varchar(30) not null, -- Score is stored as a string until converted during submission
|
||||||
|
UserName varchar(30) not null
|
||||||
);
|
);
|
||||||
+11
-1
@@ -3,6 +3,10 @@
|
|||||||
"lockFileMaintenance": {
|
"lockFileMaintenance": {
|
||||||
"automerge": true
|
"automerge": true
|
||||||
},
|
},
|
||||||
|
"major": {
|
||||||
|
"automerge": false,
|
||||||
|
"automergeType": "pr"
|
||||||
|
},
|
||||||
"minor": {
|
"minor": {
|
||||||
"automerge": true,
|
"automerge": true,
|
||||||
"automergeType": "branch"
|
"automergeType": "branch"
|
||||||
@@ -15,5 +19,11 @@
|
|||||||
"automerge": true,
|
"automerge": true,
|
||||||
"automergeType": "branch"
|
"automergeType": "branch"
|
||||||
},
|
},
|
||||||
"ignoreTests": true
|
"ignoreTests": true,
|
||||||
|
"dockerfile": {
|
||||||
|
"minor": {
|
||||||
|
"automerge": false,
|
||||||
|
"automergeType": "pr"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user