Compare commits

..
Author SHA1 Message Date
alwayscompiling 0acfa77492 Change delete_scores to a slash command. 2022-08-08 22:07:12 -04:00
alwayscompiling ffefd08761 Updated presense to account for slash commands. 2022-08-08 21:54:27 -04:00
alwayscompiling a61662429a Remove help command. Doesn't work with slash commands. 2022-08-08 21:51:25 -04:00
alwayscompiling 57244bcbfe Update post highscores command to slash command. 2022-08-08 21:51:06 -04:00
alwayscompiling 3fd9ff46a3 imports cleanup. 2022-08-08 21:36:45 -04:00
alwayscompiling d4ad727f2a Correcting error statement. 2022-08-06 22:58:43 -04:00
alwayscompiling 516ff7feeb Update minimum python version. Nextcord v2.0 requires python 3.8. 2022-08-06 22:26:55 -04:00
alwayscompiling 0eb332aefd Added verify and submission register subcommands. Removed bot argument from Register class. 2022-08-06 22:21:08 -04:00
alwayscompiling ff07787a9a Create register group and highscore slash subcommand. 2022-08-06 20:14:30 -04:00
alwayscompiling b5959ccc33 Change constructor of score. Create new function to add score value from a string. 2022-08-06 17:32:17 -04:00
alwayscompiling b80f160914 Update verification view to use Table instead of Boss. 2022-08-06 16:55:06 -04:00
alwayscompiling 3b866be3ab Corrected typo in submission_state setter. 2022-08-06 16:45:05 -04:00
alwayscompiling e55b22c821 Easier to read submission access. 2022-08-06 16:41:50 -04:00
alwayscompiling f06e6f1e1e Create function to know if a user has a submission. 2022-08-06 16:30:49 -04:00
alwayscompiling 5f3e7361b4 Convert old submission_objects to new class. 2022-08-06 16:30:31 -04:00
alwayscompiling 835611a11e Created submission classes. 2022-08-06 16:10:19 -04:00
alwayscompiling ee9a23ca79 Renamed data folder to classes. 2022-08-06 14:59:58 -04:00
17 changed files with 511 additions and 319 deletions
+7 -3
View File
@@ -2,7 +2,7 @@
import json
from data.score import Score, ScoreTime # pylint: disable=import-error
from classes.score import Score, ScoreTime # pylint: disable=import-error
class Category:
@@ -46,9 +46,13 @@ class Category:
category.time_record = data["time_record"]
for user, score in data["scores"].items():
if category.time_record:
category.add_score(ScoreTime(user, score))
score_obj: ScoreTime = ScoreTime(user)
score_obj.value = float(score)
category.add_score(score_obj)
else:
category.add_score(Score(user, score))
score_obj: Score = Score(user)
score_obj.value = int(score)
category.add_score(score_obj)
return category
+1 -1
View File
@@ -2,7 +2,7 @@
import json
from data.table import Table # pylint: disable=import-error
from classes.table import Table # pylint: disable=import-error
class Guild:
@@ -3,9 +3,9 @@
import os
import json
from data.guild import Guild # pylint: disable=import-error
from data.table import Table # pylint: disable=import-error
from data.category import Category # pylint: disable=import-error
from classes.guild import Guild # pylint: disable=import-error
from classes.table import Table # pylint: disable=import-error
from classes.category import Category # pylint: disable=import-error
HIGHSCORES_BACKUP_DIR = "deleted_backups"
+25 -4
View File
@@ -8,7 +8,7 @@ class Score:
# no user setter, it should never change.
def __init__(self, user: str, value: int) -> None:
def __init__(self, user: str) -> None:
"""
Default constructor for Score class.
@@ -17,7 +17,7 @@ class Score:
value (int): Score's value
"""
self._user: str = user
self._value: int = value
self._value: int = -1
def __str__(self) -> str:
"""
@@ -67,6 +67,15 @@ class Score:
"""
return str(self._value)
def set_value_str(self, value: str) -> None:
"""
Setter for score value from string.
Args:
value (str): int as a string
"""
self._value = int(value)
def to_str(self) -> str:
"""
Returns Score object as a string.
@@ -80,7 +89,7 @@ class Score:
class ScoreTime(Score):
"""Class for a time based score. Inherits from Score."""
def __init__(self, user: str, score: float) -> None: # pylint: disable=super-init-not-called
def __init__(self, user: str) -> None: # pylint: disable=super-init-not-called
"""
Default constructor for ScoreTime class.
@@ -90,7 +99,7 @@ class ScoreTime(Score):
"""
# store time score as seconds.
self._user: str = user
self._score: float = score
self._score: float = -1.0
@property
def value(self) -> float:
@@ -121,3 +130,15 @@ class ScoreTime(Score):
"""
milliseconds = round((self.value - int(self.value)) * 10)
return time.strftime(f"%-M:%S.{milliseconds}", time.gmtime(self.value))
def set_value_str(self, value: str) -> None:
"""
Setter for score value from a string.
Args:
value (str): string representation of time
"""
if value.find(":"):
self._score = int(value.split(":")[0]) * 60 + float(value.split(":")[1])
else:
self._score = float(value)
+256
View File
@@ -0,0 +1,256 @@
"""
Contains classes and enum for tracking data on submissions.
"""
from enum import Enum
from nextcord import Embed
class SubmissionState(Enum):
"""
Enum for which state a submission is in
"""
CATEGORY = 1
SCORE = 2
PROOF = 3
NAME = 4
SUBMIT = 5
VERIFY = 6
class Submission:
"""
Class for an individual submission. Contains submission information such as
guild id, message id, table, category, username, score and submission state.
"""
def __init__(self, guild_id: int, table: str, username: str) -> None:
"""
Default constructor for Submission. Just need to know guild_id, table, and username.
Score and category are defaulted at empty, message id is 0, and submission state
is SubmissionState.CATEGORY.
"""
self._category: str = ""
self._guild_id: int = guild_id
self._message_id: int = 0
self._score: str = ""
self._submission_state: SubmissionState = SubmissionState.CATEGORY
self._table: str = table
self._username: str = username
@property
def category(self) -> str:
"""
Getter for Submission's Category name.
Returns:
str: Category name.
"""
return self._category
@category.setter
def category(self, category: str) -> None:
"""
Setter for Submission's Category name.
Args:
category (str): Category name
"""
self._category = category
@property
def guild_id(self) -> int:
"""
Getter for guild id Submission is happening in. Will not change.
Returns:
int: guild id
"""
return self._guild_id
@property
def message_id(self) -> int:
"""
Getter for message id that Submission has a view on.
Returns:
int: message id
"""
return self._message_id
@message_id.setter
def message_id(self, m_id: int) -> None:
"""
Setter for message id that Submission has a view on.
Args:
m_id (int): message id
"""
self._message_id = m_id
@property
def score(self) -> str:
"""
Getter for score.
Returns:
str: score
"""
return self._score
@score.setter
def score(self, score: str) -> None:
"""
Setter for score.
Args:
score (str): score
"""
self._score = score
@property
def submission_state(self) -> SubmissionState:
"""
Getter for state Submission is in.
Returns:
SubmissionState: State the Submission is in
"""
return self._submission_state
@submission_state.setter
def submission_state(self, state: SubmissionState) -> None:
"""
Setter for state Submission is in.
Args:
state (SubmissionState): State to change to
"""
self._submission_state = state
@property
def table(self) -> str:
"""
Getter for table Submission is for.
Returns:
str: table name
"""
return self._table
@table.setter
def table(self, table: str) -> None:
"""
Getter for table Submission is for.
Args:
table (str): table name
"""
self._table = table
@property
def username(self) -> str:
"""
Getter for Submission owner's username.
Returns:
str: Submission owner's username
"""
return self._username
@username.setter
def username(self, username: str) -> None:
"""
Setter for Submission owner's username.
Args:
username (str): Submission owner's username
"""
self._username = username
def get_embed(self) -> Embed:
"""Creates an embed based off of the submission state and fields filled out."""
state: SubmissionState = self.submission_state
if state is SubmissionState.CATEGORY:
description = "Step 1: Choose your category!"
elif state is SubmissionState.SCORE:
description = "Step 2: Input your score!"
elif state is SubmissionState.PROOF:
description = "Step 3: Submit your proof!"
elif state is SubmissionState.NAME:
description = (
"Step 4 (Optional): Change name on submission! (proceed to next if skipping)"
)
elif state is SubmissionState.SUBMIT:
description = "Step 5: Submit if everything looks correct!"
elif state is SubmissionState.VERIFY:
description = "Review fields and accept."
embed = Embed(title="Highscores Submission", description=description)
embed.add_field(name="User Name", value=self.username, inline=False)
embed.add_field(name="Table", value=self.table, inline=False)
if self.category:
embed.add_field(name="Category", value=self.category, inline=False)
if self.score:
embed.add_field(name="Score", value=self.score, inline=False)
return embed
class SubmissionMap:
"""
Tracks all active submission.
"""
def __init__(self) -> None:
"""
Default constructor for SubmissionMap. Created empty map.
"""
self._submission_map: dict = {}
def add_submission(self, user_id: int, submission: Submission) -> None:
"""
Adds new submission to submission map. User id is used as a key, a user should only
have one submission at a time.
Args:
user_id (int): User id of submitter. Unique, used as key.
submission (Submission): Submission object
"""
self._submission_map[user_id] = submission
def get_user_submission(self, user_id: int) -> Submission:
"""
Getter for a user's Submission.
Args:
user_id (int): user id
Returns:
Submission: user's submission
"""
return self._submission_map[user_id]
def has_user(self, user_id: int) -> bool:
"""
Tells if user has an active submission in submission map.
Args:
user_id (int): user id
Returns:
bool: bool indicating if user id is in submission map
"""
return user_id in self._submission_map
def remove_user_submission(self, user_id: int) -> None:
"""
Removes a user's Submission. Use when Submission is complete.
Args:
user_id (int): user id to identify user's Submission
"""
self._submission_map.pop(user_id)
+1 -1
View File
@@ -2,7 +2,7 @@
import json
from data.category import Category # pylint: disable=import-error
from classes.category import Category # pylint: disable=import-error
class Table:
+24 -15
View File
@@ -2,8 +2,8 @@
Bot command for deleting the guild's scores.
"""
from nextcord import Embed
from nextcord.ext import commands
import nextcord
from highscores import highscores_data # pylint: disable=import-error
@@ -14,29 +14,38 @@ class DeleteScores(commands.Cog, name="Delete Scores"):
Deletes this guild's scores.
"""
def __init__(self, bot: commands.Bot):
self.bot = bot
def __init__(self):
"""
Default constructor for DeleteScores class. Does nothing.
"""
@commands.command(name="delete-scores")
@commands.has_permissions(administrator=True)
async def delete_scores(self, ctx: commands.Context):
"""A command which deletes all scores for this guild.
Usage:
```
?delete_scores
```
@nextcord.slash_command(
description="Deletes all scores for this guild. Used for testing.",
default_member_permissions=nextcord.Permissions(administrator=True),
)
async def delete_scores(self, interaction: nextcord.Interaction):
"""
A command which deletes all scores in all tables for this guild.
Command limits to admins to prevent devs from usings on non-test servers.
"""
# only allow AlwaysCompiling to use.
# If you are a developer and want to use command, can add name to list
if ctx.author.id == 206602713511886849:
for table in highscores_data.get_guild(ctx.guild.id).tables:
if interaction.user.id == 206602713511886849:
for table in highscores_data.get_guild(interaction.guild_id).tables:
for category in table.categories:
category.clear()
highscores_data.save_guild_data(highscores_data.get_guild(ctx.guild.id))
highscores_data.save_guild_data(highscores_data.get_guild(interaction.guild_id))
await interaction.response.send_message(
f"Cleared score data for {interaction.guild.name}.", ephemeral=True
)
else:
await interaction.response.send_message(
"You shouldn't be using this message.", ephemeral=True
)
# This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot.
def setup(bot: commands.Bot):
"""Adds function to bot"""
bot.add_cog(DeleteScores(bot))
bot.add_cog(DeleteScores())
-26
View File
@@ -1,26 +0,0 @@
"""
Help command
"""
from nextcord.ext import commands
from .help_command import NewHelpCommand
class HelpCog(commands.Cog):
"""Help Command"""
def __init__(self, bot: commands.Bot):
self.bot = bot
self._original_help_command = bot.help_command
bot.help_command = NewHelpCommand()
bot.help_command.cog = self
def cog_unload(self):
self.bot.help_command = self._original_help_command
# setup functions for bot
def setup(bot: commands.Bot):
"""Adds function to bot"""
bot.add_cog(HelpCog(bot))
-84
View File
@@ -1,84 +0,0 @@
"""
Help command. Copied from discord-bot-template.
"""
import nextcord
from nextcord.embeds import EmptyEmbed
from nextcord.ext import commands
import config # pylint: disable=import-error
class NewHelpCommand(commands.MinimalHelpCommand):
"""Custom help command override using embeds"""
COLOUR = nextcord.Colour.blurple()
def get_ending_note(self):
"""Returns note to display at the bottom"""
invoked_with = self.invoked_with
return f"Use {config.BOT_PREFIX}{invoked_with} [command] for more info on a command."
def get_command_signature(self, command: commands.core.Command):
"""Retrieves the signature portion of the help page."""
return f"{command.qualified_name} {command.signature}"
async def send_bot_help(self, mapping: dict):
"""implements bot command help page"""
embed = nextcord.Embed(title="Bot Commands", colour=self.COLOUR)
avatar = self.context.bot.user.avatar
avatar_url = avatar.url if avatar else EmptyEmbed
embed.set_author(name=self.context.bot.user.name, icon_url=avatar_url)
description = self.context.bot.description
if description:
embed.description = description
for cog, commands in mapping.items(): # pylint: disable=redefined-outer-name
name = "No Category" if cog is None else cog.qualified_name
filtered = await self.filter_commands(commands, sort=True)
if filtered:
# \u2002 = middle dot
value = "\u2002".join(f"{config.BOT_PREFIX}{c.name}" for c in filtered)
if cog and cog.description:
value = f"{cog.description}\n{value}"
embed.add_field(name=name, value=value)
embed.set_footer(text=self.get_ending_note())
await self.get_destination().send(embed=embed)
async def send_cog_help(self, cog: commands.Cog):
"""implements cog help page"""
embed = nextcord.Embed(title=f"{cog.qualified_name} Commands", colour=self.COLOUR)
if cog.description:
embed.description = cog.description
filtered = await self.filter_commands(cog.get_commands(), sort=True)
for command in filtered:
embed.add_field(
name=self.get_command_signature(command),
value=command.short_doc or "...",
inline=False,
)
embed.set_footer(text=self.get_ending_note())
await self.get_destination().send(embed=embed)
async def send_group_help(self, group: commands.Group):
"""implements group help page and command help page"""
embed = nextcord.Embed(title=group.qualified_name, colour=self.COLOUR)
if group.help:
embed.description = group.help
if isinstance(group, commands.Group):
filtered = await self.filter_commands(group.commands, sort=True)
for command in filtered:
embed.add_field(
name=self.get_command_signature(command),
value=command.short_doc or "...",
inline=False,
)
embed.set_footer(text=self.get_ending_note())
await self.get_destination().send(embed=embed)
# Use the same function as group help for command help
send_command_help = send_group_help # type: ignore
+11 -14
View File
@@ -9,10 +9,7 @@ import re
from nextcord.ext import commands
import nextcord
from cogs.submission.submission_views import ( # pylint: disable=import-error
SubmissionState,
get_submission_embed,
)
from classes.submission import SubmissionState # pylint: disable=import-error
from highscores import highscores_data, submission_objects # pylint: disable=import-error
@@ -38,8 +35,9 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
if chance == 1:
await message.channel.send("Happy Birthday Chris <@642330132958281750>")
if message.author.id in submission_objects:
message_id = submission_objects[message.author.id]["message_id"]
if submission_objects.has_user(message.author.id):
submission = submission_objects.get_user_submission(message.author.id)
message_id = submission.message_id
submission_channel_id = highscores_data.get_guild(guild_id).submission_channel
submission_message: nextcord.Message = (
await self.bot.get_guild(guild_id)
@@ -49,14 +47,14 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
# need check if correct user and correct channel
if message.channel.id == submission_channel_id:
state = submission_objects[message.author.id]["submission_state"]
state = submission.submission_state
if state == SubmissionState.SCORE and message.content:
# check with regex if score matches correctly
is_time_record = (
highscores_data.get_guild(guild_id)
.get_table(submission_objects[message.author.id]["boss_name"])
.get_category(submission_objects[message.author.id]["category"])
.get_table(submission.table)
.get_category(submission.category)
.time_record
)
@@ -89,9 +87,8 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
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")
submission_objects[message.author.id]["score"] = score
embed = get_submission_embed(message.author.id)
submission.score = score
embed = submission.get_embed()
await submission_message.edit(
embed=embed,
)
@@ -100,10 +97,10 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
files=[await attch.to_file() for attch in message.attachments],
)
elif state == SubmissionState.NAME and message.content:
submission_objects[message.author.id]["username"] = message.content[
submission.username = message.content[
: highscores_data.get_guild(guild_id).username_length
]
embed = get_submission_embed(message.author.id)
embed = submission.get_embed()
await submission_message.edit(
embed=embed,
)
+30 -19
View File
@@ -13,35 +13,44 @@ from utilities.utils import send_highscore_message # pylint: disable=import-err
class PostHighscores(commands.Cog, name="Post Highscores"):
"""Posts Highscores."""
def __init__(self, bot: commands.Bot):
def __init__(self, bot: commands.Bot) -> None:
"""
Default constructor for PostHighscores class
Args:
bot (commands.Bot): Bot object. Required to register submission button view.
"""
self.bot = bot
@commands.command(name="post-scores")
@commands.has_permissions(administrator=True)
async def post_highscores(self, ctx: commands.Context):
@nextcord.slash_command(
description="Triggers post/update of highscores to registered highscore channel.",
default_member_permissions=nextcord.Permissions(administrator=True),
)
async def post_highscore_tables(self, interaction: nextcord.Interaction) -> None:
"""
A command which posts the highscore in the designated channel.
A command which posts the highscore in the registered channel.
Sends warning if no channel has been registered.
Usage:
```
?post-scores
```
"""
guild = highscores_data.get_guild(ctx.guild.id)
guild = highscores_data.get_guild(interaction.guild_id)
print(f"Printing out highscores information in channel {guild.highscore_channel}")
channel = ctx.guild.get_channel(guild.highscore_channel)
channel = interaction.guild.get_channel(guild.highscore_channel)
# Error checking for registered highscore channel
error_response = "Registered Highscores channel does not exist or was never registered. "
error_response += 'Register with "?register" command.'
assert channel is not None, await ctx.send(error_response)
error_response += 'Use "/register" to register channels.'
assert channel is not None, await interaction.response(error_response, ephemeral=True)
if len(highscores_data.get_guild(ctx.guild.id).tables) < 1:
await channel.send(
"No tables have been created for this guild. Please create some tables."
)
return
# Error checking for tables existing
# TODO fill out response string once functionality created.
error_response = "No tables have been created for this guild. Please create some tables."
assert (
len(highscores_data.get_guild(interaction.guild_id).tables) > 0
), await interaction.response(error_response, ephemeral=True)
for table in highscores_data.get_guild(ctx.guild.id).tables:
# awaiting response because posting scores takes a while
await interaction.response.defer(ephemeral=True)
for table in highscores_data.get_guild(interaction.guild_id).tables:
message: nextcord.Message = await send_highscore_message(channel, guild, table)
await message.edit(view=SubmissionCreateButton(self.bot))
@@ -49,6 +58,8 @@ class PostHighscores(commands.Cog, name="Post Highscores"):
# messages have been sent.
highscores_data.save_guild_data(guild)
await interaction.followup.send(f"Posted all tables to <#{channel.id}>.", ephemeral=True)
# This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot.
+7 -4
View File
@@ -35,14 +35,17 @@ class BotPresence(commands.Cog, name="Bot Presence"):
async def change_presence(self):
"""Randomly chooses a presence and updates bot's presence."""
statuses = [
f"?help | {len(self.bot.guilds)} servers",
f"Vuln Pls. | {len(self.bot.guilds)} servers",
f"All your base are belong to us. | {len(self.bot.guilds)} servers",
f"Double plus good. | {len(self.bot.guilds)} servers",
"Now with slash commands!",
"Vuln Pls.",
"All your base are belong to us.",
"Double plus good.",
"That's a lot of damage!",
]
status = random.choice(statuses)
status += f" | {len(self.bot.guilds)} servers"
await self.bot.change_presence(activity=nextcord.Game(status))
+94 -49
View File
@@ -2,63 +2,108 @@
Bot command for registering various channels to the bot.
"""
from nextcord import Embed
from nextcord.ext import commands
import nextcord
from highscores import highscores_data # pylint: disable=import-error
class RegisterVerificationChannel(commands.Cog, name="Register"):
"""Bot command for registering various channels to the bot."""
class Register(commands.Cog, name="Register"):
"""
Class holds various register commands.
"""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@commands.command(name="register")
@commands.has_permissions(administrator=True)
async def register_highscores(self, ctx: commands.Context, registration_type: str) -> None:
"""A command which registers current channel as for various uses.
Usage:
```
?register verify (Use in channel you want to verify submissions in.)
?register highscore (Use in channel you want to show highscore messages in.)
?register submission (Use in channel you want submission messages to be send it.)
?register help (Shows help message.)
```
def __init__(self) -> None:
"""
Default constructor for Register class. Does nothing.
"""
# respond to the message
channel_id = ctx.channel.id
guild = highscores_data.get_guild(ctx.guild.id)
if registration_type == "verify":
await ctx.send(f"Registered <#{channel_id}> as channel to post verifications in.")
guild.verification_channel = channel_id
elif registration_type == "highscore":
await ctx.send(f"Registered <#{channel_id}> as channel to post highscores in.")
guild.highscore_channel = channel_id
elif registration_type == "submission":
await ctx.send(f"Registered <#{channel_id}> as channel to post submissions in.")
guild.submission_channel = channel_id
else:
embed = Embed(
title="Register command help", description="How to use the register command."
)
embed.add_field(
name="?register verify", value="Use in channel you want to verify submissions in."
)
embed.add_field(
name="?register highscore",
value="Use in channel you want to show highscore messages in.",
)
embed.add_field(
name="?register submission",
value="Use in channel you want submission messages to be send it.",
)
embed.add_field(name="?register help", value="Use to show this message.")
await ctx.reply(embed=embed)
# return, don't need to save data
return
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
async def register(self):
"""
Parent slash command, never called.
"""
@register.subcommand(description="Registers the channel for perminent highscores messages.")
async def highscore(
self,
interaction: nextcord.Interaction,
channel: nextcord.abc.GuildChannel = nextcord.SlashOption(
description="Channel to register",
required=False,
channel_types=[nextcord.ChannelType.text],
default=None,
),
) -> None:
"""
Command function to register the highscore printing channel.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command
channel (nextcord.abc.GuildChannel, optional): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None.
"""
if not channel:
channel = interaction.channel
channel_id = channel.id
guild = highscores_data.get_guild(interaction.guild_id)
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
guild.highscore_channel = channel_id
highscores_data.save_guild_data(guild)
@register.subcommand(description="Registers the channel for submission messages.")
async def submission(
self,
interaction: nextcord.Interaction,
channel: nextcord.abc.GuildChannel = nextcord.SlashOption(
description="Channel to register",
required=False,
channel_types=[nextcord.ChannelType.text],
default=None,
),
) -> None:
"""
Command function to register the submission message channel.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command
channel (nextcord.abc.GuildChannel, optional): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None.
"""
if not channel:
channel = interaction.channel
channel_id = channel.id
guild = highscores_data.get_guild(interaction.guild_id)
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
guild.submission_channel = channel_id
highscores_data.save_guild_data(guild)
@register.subcommand(description="Registers the channel for verification messages.")
async def verify(
self,
interaction: nextcord.Interaction,
channel: nextcord.abc.GuildChannel = nextcord.SlashOption(
description="Channel to register",
required=False,
channel_types=[nextcord.ChannelType.text],
default=None,
),
) -> None:
"""
Command function to register the verification message channel.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command
channel (nextcord.abc.GuildChannel, optional): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None.
"""
if not channel:
channel = interaction.channel
channel_id = channel.id
guild = highscores_data.get_guild(interaction.guild_id)
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
guild.verification_channel = channel_id
highscores_data.save_guild_data(guild)
@@ -66,4 +111,4 @@ class RegisterVerificationChannel(commands.Cog, name="Register"):
# It is necessary to add these functions to the bot.
def setup(bot: commands.Bot) -> None:
"""Adds function to bot"""
bot.add_cog(RegisterVerificationChannel(bot))
bot.add_cog(Register())
+34 -81
View File
@@ -1,12 +1,11 @@
"""Views used for submission process."""
from typing import Optional
from enum import Enum
import nextcord
from nextcord import Embed
from nextcord.ext import commands
from classes.submission import Submission, SubmissionState # pylint: disable=import-error
from highscores import ( # pylint: disable=import-error
highscores_data,
highscores_message_map,
@@ -15,45 +14,6 @@ from highscores import ( # pylint: disable=import-error
from .verification_view import VerificationView
class SubmissionState(Enum):
"""Enum for which state a submission is in"""
CATEGORY = 1
SCORE = 2
PROOF = 3
NAME = 4
SUBMIT = 5
VERIFY = 6
def get_submission_embed(userid: int) -> nextcord.Embed:
"""Creates an embed based off of the submission state and fields filled out."""
usersubmission: dict = submission_objects[userid]
state: SubmissionState = usersubmission["submission_state"]
if state is SubmissionState.CATEGORY:
description = "Step 1: Choose your category!"
elif state is SubmissionState.SCORE:
description = "Step 2: Input your score!"
elif state is SubmissionState.PROOF:
description = "Step 3: Submit your proof!"
elif state is SubmissionState.NAME:
description = "Step 4 (Optional): Change name on submission! (proceed to next if skipping)"
elif state is SubmissionState.SUBMIT:
description = "Step 5: Submit if everything looks correct!"
elif state is SubmissionState.VERIFY:
description = "Review fields and accept."
embed = Embed(title="Highscores Submission", description=description)
embed.add_field(name="User Name", value=usersubmission["username"], inline=False)
embed.add_field(name="Boss", value=usersubmission["boss_name"], inline=False)
if usersubmission["category"]:
embed.add_field(name="Category", value=usersubmission["category"], inline=False)
if usersubmission["score"]:
embed.add_field(name="Score", value=usersubmission["score"], inline=False)
return embed
def category_select_options(guild_id: int, boss_name: str) -> "list[nextcord.SelectOption]":
"""Returns list of categories for a given boss."""
options: "list[nextcord.SelectOption]" = []
@@ -81,8 +41,9 @@ class SubmissionDropdown(nextcord.ui.Select):
self._bot = bot
async def callback(self, interaction: nextcord.Interaction):
submission_objects[interaction.user.id]["category"] = self.values[0]
embed = get_submission_embed(interaction.user.id)
submission = submission_objects.get_user_submission(interaction.user.id)
submission.category = self.values[0]
embed = submission.get_embed()
await interaction.message.edit(
embed=embed,
)
@@ -96,10 +57,11 @@ class BackButton(nextcord.ui.Button):
self._bot = bot
async def callback(self, interaction: nextcord.Interaction):
state: SubmissionState = submission_objects[interaction.user.id]["submission_state"]
submission = submission_objects.get_user_submission(interaction.user.id)
state: SubmissionState = submission.submission_state
state = SubmissionState(state.value - 1)
submission_objects[interaction.user.id]["submission_state"] = state
embed = get_submission_embed(interaction.user.id)
submission.submission_state = state
embed = submission.get_embed()
await interaction.edit(embed=embed, view=SubmissionView(self._bot, interaction.user.id))
@@ -110,7 +72,7 @@ class CancelButton(nextcord.ui.Button):
super().__init__(label="Cancel", style=nextcord.ButtonStyle.red, custom_id="cancel")
async def callback(self, interaction: nextcord.Interaction):
submission_objects.pop(interaction.user.id)
submission_objects.remove_user_submission(interaction.user.id)
await interaction.message.delete()
@@ -123,22 +85,18 @@ class NextButton(nextcord.ui.Button):
async def callback(self, interaction: nextcord.Interaction):
# check if fields have been filled out for each state
state: SubmissionState = submission_objects[interaction.user.id]["submission_state"]
if (
state == SubmissionState.CATEGORY
and not submission_objects[interaction.user.id]["category"]
):
submission = submission_objects.get_user_submission(interaction.user.id)
state: SubmissionState = submission.submission_state
if state == SubmissionState.CATEGORY and not submission.category:
await interaction.send("Please choose a category.", ephemeral=True)
elif (
state == SubmissionState.SCORE and not submission_objects[interaction.user.id]["score"]
):
elif state == SubmissionState.SCORE and not submission.score:
await interaction.send("Please send a score.", ephemeral=True)
elif state == SubmissionState.PROOF and not len(interaction.message.attachments) > 0:
await interaction.send("Please attach picture for proof.", ephemeral=True)
else:
state = SubmissionState(state.value + 1)
submission_objects[interaction.user.id]["submission_state"] = state
embed = get_submission_embed(interaction.user.id)
submission.submission_state = state
embed = submission.get_embed()
await interaction.edit(embed=embed, view=SubmissionView(self._bot, interaction.user.id))
@@ -154,14 +112,15 @@ class SubmitButton(nextcord.ui.Button):
interaction.guild_id
).verification_channel
channel = interaction.guild.get_channel(verification_channel_id)
submission = submission_objects.get_user_submission(interaction.user.id)
error_response = "Registered Verification channel does not exist or was never registered. \
Contact server admins."
assert channel is not None, await channel.send(error_response)
submission_objects[interaction.user.id]["submission_state"] = SubmissionState.VERIFY
submission.submission_state = SubmissionState.VERIFY
embed = get_submission_embed(interaction.user.id)
embed = submission.get_embed()
await channel.send(
files=[await attch.to_file() for attch in interaction.message.attachments],
@@ -170,7 +129,7 @@ class SubmitButton(nextcord.ui.Button):
content=f"Submission from {interaction.user.display_name}",
)
await interaction.message.edit(content=f"<@{interaction.user.id}> Submitted!", view=None)
submission_objects.pop(interaction.user.id)
submission_objects.remove_user_submission(interaction.user.id)
############ Submission Views ############
@@ -191,31 +150,25 @@ class SubmissionCreateButton(nextcord.ui.View):
): # pylint: disable=unused-argument
"""Button for score submission."""
# Don't allow a user to start a new submission if one already exists
if interaction.user.id in submission_objects:
if submission_objects.has_user(interaction.user.id):
await interaction.send("Please finish in progress submission first.", ephemeral=True)
return
boss_name = highscores_message_map[interaction.guild.id][interaction.message.id]
table = highscores_message_map[interaction.guild.id][interaction.message.id]
channel = interaction.guild.get_channel(
highscores_data.get_guild(interaction.guild_id).submission_channel
)
# a user should only have 1 submission active at once.
# submitting with dummy message_id until message created
# split username on pipe.
submission_objects[interaction.user.id] = {
"message_id": 0,
"guild_id": interaction.guild_id,
"submission_state": SubmissionState.CATEGORY,
"boss_name": boss_name,
"score": "",
"category": "",
"username": interaction.user.display_name.split("|")[0][
: highscores_data.get_guild(interaction.guild_id).username_length
],
}
# split username on pipe then limit length.
username: str = interaction.user.display_name.split("|")[0][
: highscores_data.get_guild(interaction.guild_id).username_length
]
submission: Submission = Submission(interaction.guild_id, table, username)
embed = get_submission_embed(interaction.user.id)
submission_objects.add_submission(interaction.user.id, submission)
embed = submission.get_embed()
submission_message = await channel.send(
f"<@{interaction.user.id}>",
@@ -227,7 +180,7 @@ class SubmissionCreateButton(nextcord.ui.View):
)
# update the message_id once its created
submission_objects[interaction.user.id]["message_id"] = submission_message.id
submission.message_id = submission_message.id
class SubmissionView(nextcord.ui.View):
@@ -243,9 +196,9 @@ class SubmissionView(nextcord.ui.View):
super().__init__(timeout=timeout)
self._submission_user = submission_user
self._bot = bot
self._state = submission_objects[self._submission_user]["submission_state"]
self._boss_name = submission_objects[self._submission_user]["boss_name"]
self._guild_id = submission_objects[self._submission_user]["guild_id"]
self._state = submission_objects.get_user_submission(self._submission_user).submission_state
self._boss_name = submission_objects.get_user_submission(self._submission_user).table
self._guild_id = submission_objects.get_user_submission(self._submission_user).guild_id
# add items depending on given state
if self._state is SubmissionState.CATEGORY:
@@ -275,4 +228,4 @@ class SubmissionView(nextcord.ui.View):
async def on_timeout(self) -> None:
# remove user from submission objects after time out. Allows user to start new submission.
submission_objects.pop(self._submission_user)
submission_objects.remove_user_submission(self._submission_user)
+8 -6
View File
@@ -1,7 +1,7 @@
"""View for verification buttons"""
import nextcord
from data.score import Score, ScoreTime # pylint: disable=import-error
from classes.score import Score, ScoreTime # pylint: disable=import-error
from highscores import highscores_data # pylint: disable=import-error
from utilities.utils import submit_score # pylint: disable=import-error
@@ -32,8 +32,8 @@ class VerificationView(nextcord.ui.View):
content = interaction.message.embeds[0].to_dict()
for field in content["fields"]:
if field["name"] == "Boss":
boss_name = field["value"]
if field["name"] == "Table":
table = field["value"]
elif field["name"] == "Category":
category = field["value"]
elif field["name"] == "Score":
@@ -43,12 +43,14 @@ class VerificationView(nextcord.ui.View):
try:
submission_guild = highscores_data.get_guild(interaction.guild_id)
submission_table = submission_guild.get_table(boss_name)
submission_table = submission_guild.get_table(table)
submission_category = submission_table.get_category(category)
if submission_category.time_record:
submission_score: ScoreTime = ScoreTime(user, float(score))
submission_score: ScoreTime = ScoreTime(user)
submission_score.set_value_str(score)
else:
submission_score: Score = Score(user, int(score))
submission_score: Score = Score(user)
submission_score.set_value_str(score)
await submit_score(
interaction,
+6 -5
View File
@@ -6,11 +6,12 @@ import logging
import os
import sys
import nextcord
from nextcord.ext import commands
import nextcord
import config
from data.highscores_data import HighscoresData
from classes.highscores_data import HighscoresData
from classes.submission import SubmissionMap
logger = logging.getLogger("nextcord")
logger.setLevel(logging.DEBUG)
@@ -27,14 +28,14 @@ for guild in highscores_data.guilds:
for table in guild.tables:
highscores_message_map[guild.guild_id][table.message_id] = table.name
submission_objects: dict = {}
submission_objects: SubmissionMap = SubmissionMap()
def main():
"""Main function for bot."""
min_python_version = (3, 7)
min_python_version = (3, 8)
if sys.version_info < min_python_version:
print("ERROR: Python version must be >= 3.7")
print("ERROR: Python version must be >= 3.8")
sys.exit()
# Allows privledged intents for monitoring members joining, roles editing, and role assignments
+4 -4
View File
@@ -4,10 +4,10 @@ Contains various utlity functions for highscores bot
import nextcord
from highscores import highscores_data, highscores_message_map # pylint: disable=import-error
from data.score import Score # pylint: disable=import-error
from data.guild import Guild # pylint: disable=import-error
from data.category import Category # pylint: disable=import-error
from data.table import Table # pylint: disable=import-error
from classes.score import Score # pylint: disable=import-error
from classes.guild import Guild # pylint: disable=import-error
from classes.category import Category # pylint: disable=import-error
from classes.table import Table # pylint: disable=import-error
def format_highscore_message(guild: Guild, table: Table) -> str: