Compare commits

...
Author SHA1 Message Date
alwayscompiling ef17d3dc3e Made all scores line up properly. 2022-07-20 22:55:40 -04:00
alwayscompiling d711763c80 Standardize time score format. 2022-07-20 22:35:33 -04:00
alwayscompiling 8318bf741a Remove old TODO. 2022-07-20 22:32:59 -04:00
alwayscompiling 17c1915810 Change location of approve button. Should be on right. 2022-07-20 22:31:39 -04:00
alwayscompiling 674ea94588 Pad username out to max length. 2022-07-20 22:24:50 -04:00
alwayscompiling 090184c1e9 Tracking username length limit in data file. 2022-07-20 22:17:20 -04:00
alwayscompiling 374632bbc6 Removing TODOs. 2022-07-20 20:58:17 -04:00
alwayscompiling 8a36c9dac9 Users cannot move to next step without completing current step. 2022-07-20 20:50:29 -04:00
alwayscompiling 1900e21f72 Updating message listener to work with states. 2022-07-20 20:37:29 -04:00
alwayscompiling 2c85483db5 Buttons now change views, embed has proper steps. Added todos for what is next. 2022-07-19 22:29:06 -04:00
alwayscompiling 63db9fb7d4 Create buttons and enum for submission state navigation. 2022-07-19 21:05:37 -04:00
alwayscompiling 5155744ec5 Made config file more efficient. 2022-07-13 22:02:40 -04:00
alwayscompiling fedc8be778 Adding VerificationView to bot on cog load. 2022-07-13 20:01:21 -04:00
alwayscompiling 125939e5b3 Updating runescape_bosses.json example config to follow new config style. 2022-07-12 21:26:12 -04:00
alwayscompiling 3b410b5f90 Redid configs to allow for unique categories on each table. 2022-07-12 21:00:51 -04:00
alwayscompiling d7bac2048f Changed listener to allow for score or attachment by themselves. 2022-07-12 19:59:21 -04:00
alwayscompiling dd4bee73c6 Approved message now mentions person who approved. 2022-07-12 18:30:25 -04:00
alwayscompiling d21683fb59 Only add best score if its new, only delete worse scores if they exist. 2022-07-12 16:07:20 -04:00
alwayscompiling 44b3d7a80b Merge branch 'develop' into 4-user-score-update 2022-07-12 15:59:42 -04:00
alwayscompiling af5017c95d Changed id of verification views. Solved problem for long lasting buttons. 2022-07-12 15:59:17 -04:00
alwayscompiling 3087c6bc39 Worked out dup check method, just need to debug. 2022-07-12 15:58:39 -04:00
alwayscompiling cf0ef96edc Merge branch 'develop' into 4-user-score-update 2022-07-12 15:22:07 -04:00
alwayscompiling 9ba37c22bb Splitting username on pipe. 2022-07-12 15:18:59 -04:00
alwayscompiling 8eeda14119 Strip leading 0 and : from scores. 2022-07-12 15:15:37 -04:00
alwayscompiling 115e3dd3d0 Shortening the pad size for messages a little. 2022-07-10 21:59:11 -04:00
alwayscompiling 9ef2acf34c Capping user names to 12 characters because runescape names. Will need to revisit user name limit later. 2022-07-10 21:51:19 -04:00
alwayscompiling 82f47ac136 Temporary attempt. Makes assumption that a user will submit a better score. 2022-07-10 21:47:01 -04:00
alwayscompiling 4a15c63e8c Corrected some comments. 2022-07-10 21:11:51 -04:00
alwayscompiling 4c6279b444 Made post command admin only. 2022-07-10 21:08:30 -04:00
alwayscompiling 39f4a108ca Fixing hardmode category searching. 2022-07-10 20:33:39 -04:00
alwayscompiling 83f0bf6498 Slight time score bug fixes. 2022-07-10 20:27:56 -04:00
alwayscompiling 3f5ebdf175 Changed listener check to be more efficient. 2022-07-10 20:16:52 -04:00
alwayscompiling f98c80d200 Updated comments in message listener. 2022-07-10 20:09:27 -04:00
alwayscompiling b61008518c Moved message listener to a cog. Listener is set up on bot run. Listener listens to all messages. Pulls score and image from a message sent in submission channel if a user has an active submission. 2022-07-10 20:05:20 -04:00
alwayscompiling 198d9008b9 Fixed missing parameter. 2022-07-09 19:46:50 -04:00
11 changed files with 673 additions and 304 deletions
+58
View File
@@ -0,0 +1,58 @@
"""
Defines cog for on message listener.
"""
from nextcord.ext import commands
import nextcord
from cogs.submission.submission_views import SubmissionState # pylint: disable=import-error
from cogs.submission.submission_views import get_submission_embed # pylint: disable=import-error
from highscores import highscores_data # pylint: disable=import-error
from highscores import submission_objects # pylint: disable=import-error
class SubmissionListener(commands.Cog, name="Message Listener"):
"""Sets up message listener."""
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message: nextcord.Message):
"""Listener for message creation. Adds score and attachments to submission."""
if message.author.id in submission_objects:
message_id = submission_objects[message.author.id]["message_id"]
submission_message: nextcord.Message = await self.bot.get_channel(
highscores_data["submission_channel_id"]
).fetch_message(message_id)
# need check if correct user and correct channel
if message.channel.id == highscores_data["submission_channel_id"]:
state = submission_objects[message.author.id]["submission_state"]
if state == SubmissionState.SCORE and message.content:
submission_objects[message.author.id]["score"] = message.content
embed = get_submission_embed(message.author.id)
await submission_message.edit(
embed=embed,
)
elif state == SubmissionState.PROOF and len(message.attachments) > 0:
await submission_message.edit(
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[
: highscores_data["username_length"]
]
embed = get_submission_embed(message.author.id)
await submission_message.edit(
embed=embed,
)
await message.delete()
# 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(SubmissionListener(bot))
+7 -4
View File
@@ -5,7 +5,7 @@ Bot command to post the highscores
from nextcord.ext import commands from nextcord.ext import commands
import nextcord import nextcord
from cogs.submission.submission_views import SubmissionButton # pylint: disable=import-error from cogs.submission.submission_views import SubmissionCreateButton # pylint: disable=import-error
from highscores import highscores_config # pylint: disable=import-error from highscores import highscores_config # pylint: disable=import-error
from highscores import highscores_data # pylint: disable=import-error from highscores import highscores_data # pylint: disable=import-error
from utilities.data_storage import save_highscores_data # pylint: disable=import-error from utilities.data_storage import save_highscores_data # pylint: disable=import-error
@@ -19,6 +19,7 @@ class PostHighscores(commands.Cog, name="Post Highscores"):
self.bot = bot self.bot = bot
@commands.command(name="post-scores") @commands.command(name="post-scores")
@commands.has_permissions(administrator=True)
async def post_highscores(self, ctx: commands.Context): async def post_highscores(self, ctx: commands.Context):
""" """
A command which posts the highscore in the designated channel. A command which posts the highscore in the designated channel.
@@ -36,9 +37,11 @@ class PostHighscores(commands.Cog, name="Post Highscores"):
Register with "?register" command.' Register with "?register" command.'
assert channel is not None, await ctx.send(error_response) assert channel is not None, await ctx.send(error_response)
for boss in highscores_config["bosses"]: for boss, categories in highscores_config[ # pylint: disable=unused-variable
message: nextcord.Message = await send_highscore_message(channel, boss["boss"]) "highscore_table"
await message.edit(view=SubmissionButton()) ].items():
message: nextcord.Message = await send_highscore_message(channel, boss)
await message.edit(view=SubmissionCreateButton(self.bot))
save_highscores_data(highscores_data) save_highscores_data(highscores_data)
+2
View File
@@ -3,6 +3,7 @@ Bot command to submit a score
""" """
from nextcord.ext import commands from nextcord.ext import commands
from highscores import highscores_data # pylint: disable=import-error from highscores import highscores_data # pylint: disable=import-error
from utilities.utils import submit_score # pylint: disable=import-error from utilities.utils import submit_score # pylint: disable=import-error
from .verification_view import VerificationView from .verification_view import VerificationView
@@ -26,6 +27,7 @@ class Score(commands.Cog, name="Score"):
Usage: Usage:
``` ```
?score "Boss Name" "Category" "score" ?score "Boss Name" "Category" "score"
This command still works if you prefer to use it over the ui.
``` ```
""" """
verification_channel_id = highscores_data["verification_channel_id"] verification_channel_id = highscores_data["verification_channel_id"]
+6 -2
View File
@@ -10,7 +10,9 @@ class VerificationView(nextcord.ui.View):
def __init__(self): def __init__(self):
super().__init__(timeout=None) super().__init__(timeout=None)
@nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve") @nextcord.ui.button(
label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve-score-command"
)
async def approve_button( async def approve_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument ): # pylint: disable=unused-argument
@@ -28,7 +30,9 @@ class VerificationView(nextcord.ui.View):
files=[await attch.to_file() for attch in interaction.message.attachments], files=[await attch.to_file() for attch in interaction.message.attachments],
) )
@nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny") @nextcord.ui.button(
label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny-score-command"
)
async def deny_button( async def deny_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument ): # pylint: disable=unused-argument
+4 -2
View File
@@ -4,7 +4,8 @@ Cog for creating submission process.
from nextcord.ext import commands from nextcord.ext import commands
from .submission_views import SubmissionButton from .submission_views import SubmissionCreateButton
from .verification_view import VerificationView
class SubmissionCog(commands.Cog, name="Submission Cog"): class SubmissionCog(commands.Cog, name="Submission Cog"):
@@ -16,7 +17,8 @@ class SubmissionCog(commands.Cog, name="Submission Cog"):
@commands.Cog.listener() @commands.Cog.listener()
async def on_ready(self): async def on_ready(self):
"""Called when cog is loaded""" """Called when cog is loaded"""
self.bot.add_view(SubmissionButton(self.bot)) self.bot.add_view(SubmissionCreateButton(self.bot))
self.bot.add_view(VerificationView())
# This function will be called when this extension is loaded. # This function will be called when this extension is loaded.
+200 -73
View File
@@ -1,62 +1,68 @@
"""Views used for submission process.""" """Views used for submission process."""
from typing import Optional from typing import Optional
from enum import Enum
import nextcord import nextcord
from nextcord import Embed
from nextcord.ext import commands from nextcord.ext import commands
from utilities.data_storage import open_message_map # pylint: disable=import-error from utilities.data_storage import open_message_map # pylint: disable=import-error
from highscores import highscores_data # pylint: disable=import-error from highscores import highscores_data # pylint: disable=import-error
from highscores import submission_objects # pylint: disable=import-error
from .verification_view import VerificationView from .verification_view import VerificationView
class SubmissionButton(nextcord.ui.View): class SubmissionState(Enum):
"""Defines view for score submission button.""" """Enum for which state a submission is in"""
def __init__(self, bot: commands.Bot): CATEGORY = 1
super().__init__(timeout=None) SCORE = 2
self._bot = bot PROOF = 3
NAME = 4
SUBMIT = 5
VERIFY = 6
@nextcord.ui.button(label="Submit", style=nextcord.ButtonStyle.primary, custom_id="submit")
async def submit_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument
"""Button for score submission."""
highscores_message_map = open_message_map()
boss_name = highscores_message_map[str(interaction.message.id)]
embed = nextcord.Embed(title="Highscores Submission")
embed.add_field(name="Boss", value=boss_name, inline=False)
embed.add_field(name="User", value=interaction.user.display_name, inline=False)
channel = self._bot.get_channel(highscores_data["submission_channel_id"])
submission_message = await channel.send(
f"<@{interaction.user.id}>",
view=SubmissionDropdownView(
self._bot, interaction, await self._category_select_options(boss_name)
),
embed=embed,
)
def wait_check(check_message: nextcord.Message): def get_submission_embed(userid: int) -> nextcord.Embed:
return ( """Creates an embed based off of the submission state and fields filled out."""
interaction.user.id == check_message.author.id usersubmission: dict = submission_objects[userid]
and interaction.channel == check_message.channel state: SubmissionState = usersubmission["submission_state"]
)
proof_message = await self._bot.wait_for("message", check=wait_check) if state is SubmissionState.CATEGORY:
embed = submission_message.embeds[0] description = "Step 1: Choose your category!"
embed.add_field(name="Score", value=proof_message.content, inline=False) elif state is SubmissionState.SCORE:
await submission_message.edit( description = "Step 2: Input your score!"
files=[await attch.to_file() for attch in proof_message.attachments], embed=embed elif state is SubmissionState.PROOF:
) description = "Step 3: Submit your proof!"
await proof_message.delete() 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."
async def _category_select_options(self, boss_name: str) -> "list[nextcord.SelectOption]": embed = Embed(title="Highscores Submission", description=description)
options: list[nextcord.SelectOption] = [] # pylint: disable=unsubscriptable-object embed.add_field(name="User Name", value=usersubmission["username"], inline=False)
boss_dict = highscores_data[boss_name] embed.add_field(name="Boss", value=usersubmission["boss_name"], inline=False)
for category in boss_dict["categories"].items(): if usersubmission["category"]:
options.append(nextcord.SelectOption(label=category[0])) 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
return options
def category_select_options(boss_name: str) -> "list[nextcord.SelectOption]":
"""Returns list of categories for a given boss."""
options: "list[nextcord.SelectOption]" = []
boss_dict = highscores_data[boss_name]
for category in boss_dict["categories"].items():
options.append(nextcord.SelectOption(label=category[0]))
return options
############ Items used in submission process ############
class SubmissionDropdown(nextcord.ui.Select): class SubmissionDropdown(nextcord.ui.Select):
@@ -73,58 +79,179 @@ class SubmissionDropdown(nextcord.ui.Select):
self._bot = bot self._bot = bot
async def callback(self, interaction: nextcord.Interaction): async def callback(self, interaction: nextcord.Interaction):
embed = interaction.message.embeds[0] submission_objects[interaction.user.id]["category"] = self.values[0]
embed.add_field(name="Category", value=self.values[0], inline=False) embed = get_submission_embed(interaction.user.id)
await interaction.message.edit( await interaction.message.edit(
embed=embed, # TODO solve ordering issue. currently have to choose category after score submission. embed=embed,
) )
class SubmissionDropdownView(nextcord.ui.View): class BackButton(nextcord.ui.Button):
"""Defines submission dropdown process view""" """Defines view for back button."""
def __init__( def __init__(self, bot: commands.Bot):
self, super().__init__(label="Back", style=nextcord.ButtonStyle.primary, custom_id="back")
bot: commands.Bot, self._bot = bot
prev_interaction: nextcord.Interaction,
options: "list[nextcord.SelectOption]",
*,
timeout: Optional[float] = 180,
):
super().__init__(timeout=timeout)
self.add_item(SubmissionDropdown(bot, options))
self._prev_interaction = prev_interaction
self.bot = bot
async def interaction_check(self, interaction: nextcord.Interaction) -> bool: async def callback(self, interaction: nextcord.Interaction):
return self._prev_interaction.user == interaction.user state: SubmissionState = submission_objects[interaction.user.id]["submission_state"]
state = SubmissionState(state.value - 1)
submission_objects[interaction.user.id]["submission_state"] = state
embed = get_submission_embed(interaction.user.id)
await interaction.edit(embed=embed, view=SubmissionView(self._bot, interaction.user.id))
@nextcord.ui.button(
label="Submit Score", style=nextcord.ButtonStyle.green, custom_id="submit-score" class CancelButton(nextcord.ui.Button):
) """Deletes message to cancel interaction.."""
async def submit_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction def __init__(self):
): # pylint: disable=unused-argument super().__init__(label="Cancel", style=nextcord.ButtonStyle.red, custom_id="cancel")
"""Defines button to submit a user's score."""
async def callback(self, interaction: nextcord.Interaction):
submission_objects.pop(interaction.user.id)
await interaction.message.delete()
class NextButton(nextcord.ui.Button):
"""Defines view for next button."""
def __init__(self, bot: commands.Bot):
super().__init__(label="Next", style=nextcord.ButtonStyle.primary, custom_id="next")
self._bot = bot
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"]
):
await interaction.send("Please choose a category.", ephemeral=True)
elif (
state == SubmissionState.SCORE and not submission_objects[interaction.user.id]["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)
await interaction.edit(embed=embed, view=SubmissionView(self._bot, interaction.user.id))
class SubmitButton(nextcord.ui.Button):
"""Defines button to submit a user's score."""
def __init__(self, bot: commands.Bot):
super().__init__(label="Submit", style=nextcord.ButtonStyle.green, custom_id="submit")
self._bot = bot
async def callback(self, interaction: nextcord.Interaction):
verification_channel_id = highscores_data["verification_channel_id"] verification_channel_id = highscores_data["verification_channel_id"]
channel = self.bot.get_channel(verification_channel_id) channel = self._bot.get_channel(verification_channel_id)
error_response = "Registered Verification channel does not exist or was never registered. \ error_response = "Registered Verification channel does not exist or was never registered. \
Contact server admins." Contact server admins."
assert channel is not None, await channel.send(error_response) assert channel is not None, await channel.send(error_response)
submission_objects[interaction.user.id]["submission_state"] = SubmissionState.VERIFY
embed = get_submission_embed(interaction.user.id)
await channel.send( await channel.send(
files=[await attch.to_file() for attch in interaction.message.attachments], files=[await attch.to_file() for attch in interaction.message.attachments],
view=VerificationView(), view=VerificationView(),
embed=interaction.message.embeds[0], embed=embed,
content=f"Submission from {interaction.user.display_name}",
) )
await interaction.message.edit(content=f"<@{interaction.user.id}> Submitted!", view=None) await interaction.message.edit(content=f"<@{interaction.user.id}> Submitted!", view=None)
submission_objects.pop(interaction.user.id)
############ Submission Views ############
class SubmissionCreateButton(nextcord.ui.View):
"""Defines view for score submission button."""
def __init__(self, bot: commands.Bot):
super().__init__(timeout=None)
self._bot = bot
@nextcord.ui.button( @nextcord.ui.button(
label="Cancel", style=nextcord.ButtonStyle.red, custom_id="cancel-submission" label="Submit", style=nextcord.ButtonStyle.primary, custom_id="submitcreate"
) )
async def cancel_button( async def submitcreate_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument ): # pylint: disable=unused-argument
"""Deletes message to cancel interaction.""" """Button for score submission."""
await interaction.message.delete() highscores_message_map = open_message_map()
boss_name = highscores_message_map[str(interaction.message.id)]
# embed = nextcord.Embed(title="Highscores Submission")
# embed.add_field(name="Boss", value=boss_name, inline=False)
# embed.add_field(name="User", value=interaction.user.display_name, inline=False)
channel = self._bot.get_channel(highscores_data["submission_channel_id"])
# a user should only have 1 submission active at once.
# submitting with dummy message_id until message created
submission_objects[interaction.user.id] = {
"message_id": 0,
"submission_state": SubmissionState.CATEGORY,
"boss_name": boss_name,
"score": "",
"category": "",
"username": interaction.user.display_name.split("|")[0][
: highscores_data["username_length"]
],
}
embed = get_submission_embed(interaction.user.id)
submission_message = await channel.send(
f"<@{interaction.user.id}>",
view=SubmissionView(
self._bot,
interaction.user.id,
),
embed=embed,
)
# update the message_id once its created
submission_objects[interaction.user.id]["message_id"] = submission_message.id
class SubmissionView(nextcord.ui.View):
"""Defines view submitting. Adjusts to all states."""
def __init__(
self,
bot: commands.Bot,
submission_user: int,
*,
timeout: Optional[float] = 180,
):
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"]
# add items depending on given state
if self._state is SubmissionState.CATEGORY:
self.add_item(SubmissionDropdown(bot, category_select_options(self._boss_name)))
self.add_item(CancelButton())
self.add_item(NextButton(self._bot))
elif (
self._state is SubmissionState.SCORE
or self._state is SubmissionState.PROOF
or self._state is SubmissionState.NAME
):
self.add_item(BackButton(self._bot))
self.add_item(NextButton(self._bot))
elif self._state is SubmissionState.SUBMIT:
self.add_item(BackButton(self._bot))
self.add_item(SubmitButton(self._bot))
async def interaction_check(self, interaction: nextcord.Interaction) -> bool:
return self._submission_user == interaction.user.id
+14 -10
View File
@@ -10,7 +10,18 @@ class VerificationView(nextcord.ui.View):
def __init__(self): def __init__(self):
super().__init__(timeout=None) super().__init__(timeout=None)
@nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve") @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
"""Button for denying score submission."""
await interaction.message.delete()
@nextcord.ui.button(
label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve-button-submission"
)
async def approve_button( async def approve_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument ): # pylint: disable=unused-argument
@@ -24,19 +35,12 @@ class VerificationView(nextcord.ui.View):
category = field["value"] category = field["value"]
elif field["name"] == "Score": elif field["name"] == "Score":
score = field["value"] score = field["value"]
elif field["name"] == "User": elif field["name"] == "User Name":
user = field["value"] user = field["value"]
try: try:
await submit_score(interaction, user, boss_name, category, score) await submit_score(interaction, user, boss_name, category, score)
await interaction.message.edit("Approved.", view=None) await interaction.message.edit(f"Approved by <@{interaction.user.id}>.", view=None)
except UnboundLocalError: except UnboundLocalError:
await interaction.send("Instruct user to submit with all fields.") await interaction.send("Instruct user to submit with all fields.")
await interaction.message.edit(view=None) await interaction.message.edit(view=None)
@nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny")
async def deny_button(
self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument
"""Button for denying score submission."""
await interaction.message.delete()
+310 -189
View File
@@ -1,203 +1,324 @@
{ {
"bosses": [ "highscore_table": {
{ "The Ambassador": [
"boss": "The Ambassador", "Kill Count",
"hardmode": true "Kill Time",
}, "Group Kill Count",
{ "Group Kill Time",
"boss": "Araxxi", "Spooned Log"
"hardmode": false ],
}, "Araxxi": [
{ "Kill Count",
"boss": "Arch-Glacor", "Kill Time",
"hardmode": true "Spooned Log"
}, ],
{ "Arch-Glacor": [
"boss": "Astellarn", "Kill Count",
"hardmode": true "Kill Time",
}, "Hardmode Kill Count",
{ "Hardmode Kill Time",
"boss": "The Barrows Brothers", "Enrage",
"hardmode": false "Spooned Log"
}, ],
{ "Astellarn": [
"boss": "The Barrows: Rise of the Six", "Kill Count",
"hardmode": false "Kill Time",
}, "Group Kill Count",
{ "Group Kill Time",
"boss": "Beastmaster Durzag", "Spooned Log"
"hardmode": false ],
}, "The Barrows Brothers": [
{ "Kill Count",
"boss": "Black Stone Dragon", "Spooned Log"
"hardmode": true ],
}, "The Barrows: Rise of the Six": [
{ "Kill Count",
"boss": "Chaos Elemental", "Kill Time",
"hardmode": false "Spooned Log"
}, ],
{ "Beastmaster Durzag": [
"boss": "Commander Zilyana", "Kill Count",
"hardmode": true "Kill Time",
}, "Spooned Log"
{ ],
"boss": "Corporeal Beast", "Black Stone Dragon": [
"hardmode": false "Kill Count",
}, "Kill Time",
{ "Group Kill Count",
"boss": "Crassian Leviathan", "Group Kill Time",
"hardmode": true "Spooned Log"
}, ],
{ "Chaos Elemental": [
"boss": "Croesus", "Kill Count",
"hardmode": false "Spooned Log"
}, ],
{ "Commander Zilyana": [
"boss": "Dagannoth Kings", "Kill Count",
"hardmode": false "Kill Time",
}, "Hardmode Kill Count",
{ "Hardmode Kill Time",
"boss": "General Graardor", "Spooned Log"
"hardmode": true ],
}, "Corporeal Beast": [
{ "Kill Count",
"boss": "Giant Mole", "Kill Time",
"hardmode": true "Spooned Log"
}, ],
{ "Crassian Leviathan": [
"boss": "Gregorovic", "Kill Count",
"hardmode": true "Kill Time",
}, "Group Kill Count",
{ "Group Kill Time",
"boss": "Har-Aken", "Spooned Log"
"hardmode": false ],
}, "Croesus": [
{ "Kill Count",
"boss": "Helwyr", "Kill Time",
"hardmode": true "Spooned Log"
}, ],
{ "Dagannoth Kings": [
"boss": "Kalphite King", "Kill Count",
"hardmode": false "Spooned Log"
}, ],
{ "General Graardor": [
"boss": "Kalphite Queen", "Kill Count",
"hardmode": false "Kill Time",
}, "Hardmode Kill Count",
{ "Hardmode Kill Time",
"boss": "Kerapac", "Spooned Log"
"hardmode": true ],
}, "Giant Mole": [
{ "Kill Count",
"boss": "King Black Dragon", "Kill Time",
"hardmode": false "Hardmode Kill Count",
}, "Hardmode Kill Time",
{ "Spooned Log"
"boss": "Kree'arra", ],
"hardmode": true "Gregorovic": [
}, "Kill Count",
{ "Kill Time",
"boss": "K'ril Tsutsaroth", "Hardmode Kill Count",
"hardmode": true "Hardmode Kill Time",
}, "Spooned Log"
{ ],
"boss": "Legiones", "Har-Aken": [
"hardmode": false "Kill Count",
}, "Kill Time",
{ "Spooned Log"
"boss": "The Magister", ],
"hardmode": false "Helwyr": [
}, "Kill Count",
{ "Kill Time",
"boss": "Masuta the Ascended", "Hardmode Kill Count",
"hardmode": true "Hardmode Kill Time",
}, "Spooned Log"
{ ],
"boss": "Nex", "Kalphite King": [
"hardmode": false "Kill Count",
}, "Kill Time",
{ "Spooned Log"
"boss": "Nex - Angel of Death", ],
"hardmode": false "Kalphite Queen": [
}, "Kill Count",
{ "Kill Time",
"boss": "Queen Black Dragon", "Spooned Log"
"hardmode": false ],
}, "Kerapac": [
{ "Kill Count",
"boss": "Raksha", "Kill Time",
"hardmode": true "Hardmode Kill Count",
}, "Hardmode Kill Time",
{ "Spooned Log"
"boss": "Rex Matriarchs", ],
"hardmode": false "King Black Dragon": [
}, "Kill Count",
{ "Kill Time",
"boss": "The Sanctum Guardian", "Spooned Log"
"hardmode": true ],
}, "Kree'arra": [
{ "Kill Count",
"boss": "Seiryu the Azure Serpent", "Kill Time",
"hardmode": true "Hardmode Kill Count",
}, "Hardmode Kill Time",
{ "Spooned Log"
"boss": "Solak", ],
"hardmode": false "K'ril Tsutsaroth": [
}, "Kill Count",
{ "Kill Time",
"boss": "Taraket the Necromancer", "Hardmode Kill Count",
"hardmode": true "Hardmode Kill Time",
}, "Spooned Log"
{ ],
"boss": "Telos", "Legiones": [
"hardmode": false "Kill Count",
}, "Kill Time",
{ "Spooned Log"
"boss": "The Twin Furies", ],
"hardmode": true "The Magister": [
}, "Kill Count",
{ "Kill Time",
"boss": "TzKal-Zuk", "Spooned Log"
"hardmode": true ],
}, "Masuta the Ascended": [
{ "Kill Count",
"boss": "TzTok-Jad", "Kill Time",
"hardmode": false "Group Kill Count",
}, "Group Kill Time",
{ "Spooned Log"
"boss": "Verak Lith", ],
"hardmode": true "Nex": [
}, "Kill Count",
{ "Kill Time",
"boss": "Vindicta & Gorvek", "Spooned Log"
"hardmode": true ],
}, "Nex - Angel of Death": [
{ "Kill Count",
"boss": "Vorago", "Kill Time",
"hardmode": true "Spooned Log"
}, ],
{ "Queen Black Dragon": [
"boss": "Yakamaru", "Kill Count",
"hardmode": false "Kill Time",
}, "Spooned Log"
{ ],
"boss": "Zamorak, Lord of Chaos", "Raksha": [
"hardmode": true "Solo Kill Count",
} "Solo Kill Time",
], "Duo Kill Count",
"Duo Kill Time",
"Spooned Log"
],
"Rex Matriarchs": [
"Kill Count",
"Spooned Log"
],
"The Sanctum Guardian": [
"Kill Count",
"Kill Time",
"Group Kill Count",
"Group Kill Time",
"Spooned Log"
],
"Seiryu the Azure Serpent": [
"Kill Count",
"Kill Time",
"Group Kill Count",
"Group Kill Time",
"Spooned Log"
],
"Solak": [
"Kill Count",
"Kill Time",
"Spooned Log"
],
"Taraket the Necromancer": [
"Kill Count",
"Kill Time",
"Group Kill Count",
"Group Kill Time",
"Spooned Log"
],
"Telos": [
"Kill Count",
"Kill Time",
"Enrage",
"Spooned Log"
],
"The Twin Furies": [
"Kill Count",
"Kill Time",
"Hardmode Kill Count",
"Hardmode Kill Time",
"Spooned Log"
],
"TzKal-Zuk": [
"Kill Count",
"Kill Time",
"Hardmode Kill Count",
"Hardmode Kill Time",
"Spooned Log"
],
"TzTok-Jad": [
"Kill Count",
"Kill Time",
"Spooned Log"
],
"Verak Lith": [
"Kill Count",
"Kill Time",
"Group Kill Count",
"Group Kill Time",
"Spooned Log"
],
"Vindicta & Gorvek": [
"Kill Count",
"Kill Time",
"Hardmode Kill Count",
"Hardmode Kill Time",
"Spooned Log"
],
"Vorago": [
"Kill Count",
"Kill Time",
"Hardmode Kill Count",
"Hardmode Kill Time",
"Spooned Log"
],
"Yakamaru": [
"Kill Count",
"Kill Time",
"Spooned Log"
],
"Zamorak, Lord of Chaos (solo)": [
"Kill Count",
"Kill Time",
"Enrage",
"Spooned Log"
],
"Zamorak, Lord of Chaos (group)": [
"Kill Count",
"Kill Time",
"Enrage"
]
},
"categories": { "categories": {
"Duo Kill Count": {
"ascending": false,
"is_time_record": false
},
"Duo Kill Time": {
"ascending": true,
"is_time_record": true
},
"Enrage": {
"ascending": false,
"is_time_record": false
},
"Group Kill Count": {
"ascending": false,
"is_time_record": false
},
"Group Kill Time": {
"ascending": true,
"is_time_record": true
},
"Hardmode Kill Count": {
"ascending": false,
"is_time_record": false
},
"Hardmode Kill Time": {
"ascending": true,
"is_time_record": true
},
"Kill Count": { "Kill Count": {
"hardmode": true,
"ascending": false, "ascending": false,
"is_time_record": false "is_time_record": false
}, },
"Kill Time": { "Kill Time": {
"hardmode": true,
"ascending": true, "ascending": true,
"is_time_record": true "is_time_record": true
}, },
"Spooned Log": { "Spooned Log": {
"hardmode": false,
"ascending": true, "ascending": true,
"is_time_record": false "is_time_record": false
} }
+3
View File
@@ -24,6 +24,8 @@ highscores_config = open_highscores_config()
highscores_data = open_highscores_data() highscores_data = open_highscores_data()
highscores_message_map = open_message_map() highscores_message_map = open_message_map()
submission_objects: dict = {}
def main(): def main():
"""Main function for bot.""" """Main function for bot."""
@@ -33,6 +35,7 @@ def main():
# Required in order to read messages (eg. prefix commands) # Required in order to read messages (eg. prefix commands)
intents.message_content = True # pylint: disable=assigning-non-slot intents.message_content = True # pylint: disable=assigning-non-slot
intents.messages = True # pylint: disable=assigning-non-slot
# To enable the guilds priveleged intent: # To enable the guilds priveleged intent:
# intents.guilds = True # intents.guilds = True
+7 -10
View File
@@ -28,18 +28,15 @@ def open_highscores_data():
data = json.load(file) data = json.load(file)
else: else:
# create the json object # create the json object
data = {"highscore_channel_id": -1, "verification_channel_id": -1} data = {"highscore_channel_id": -1, "verification_channel_id": -1, "username_length": 12}
config = open_highscores_config() config: dict = open_highscores_config()
for boss in config["bosses"]: for boss, categories in config["highscore_table"].items():
data[boss["boss"]] = {"message_id": 0} data[boss] = {"message_id": 0}
boss_categories = {} boss_categories = {}
for key, value in config["categories"].items(): for category in categories: # pylint: disable=unused-variable
rank_list = [] rank_list = []
boss_categories[key] = rank_list boss_categories[category] = rank_list
if boss["hardmode"] and value["hardmode"]: data[boss]["categories"] = boss_categories
rank_list = []
boss_categories["Hardmode " + key] = rank_list
data[boss["boss"]]["categories"] = boss_categories
return data return data
+62 -14
View File
@@ -21,7 +21,7 @@ def format_highscore_message(boss_name: str):
# Gather all categories and add to string # Gather all categories and add to string
for key, value in boss_data["categories"].items(): for key, value in boss_data["categories"].items():
ret_string += f"{key}".ljust(30, " ") ret_string += f"{key}".ljust(26, " ")
ret_string += "\n" ret_string += "\n"
highscore_size = highscores_config["highscore_size"] highscore_size = highscores_config["highscore_size"]
@@ -30,10 +30,25 @@ def format_highscore_message(boss_name: str):
for i in range(highscore_size): for i in range(highscore_size):
for key, value in boss_data["categories"].items(): for key, value in boss_data["categories"].items():
if len(value) > i: if len(value) > i:
score = value[i] score_pair = value[i]
ret_string += f"{i+1}: {score[0]} - {score[1]}".ljust(30, " ")
# pad username to max length.
username: str = score_pair[0].ljust(highscores_data["username_length"], " ")
score: str = str(score_pair[1])
# standardize time score format.
if highscores_config["categories"][key]["is_time_record"]:
if score.find(":") == -1:
score = "00:" + score
elif len(score.split(":", maxsplit=1)[0]) < 2:
score = "0" + score
if score.find(".") == -1:
# insert 2 spaces to make up for period and ms
score = score + " "
line_str = f"{i+1}: {username} - "
ret_string += line_str + score.rjust(26 - len(line_str) - 1) + " "
else: else:
ret_string += f"{i+1}: submit your score".ljust(30, " ") ret_string += f"{i+1}: submit your score".ljust(26, " ")
ret_string += "\n" ret_string += "\n"
return ret_string + "```" return ret_string + "```"
@@ -72,16 +87,27 @@ async def submit_score(
@param boss: the boss to add score to @param boss: the boss to add score to
@param category: the category to add score to @param category: the category to add score to
@param user: user who submitted score @param user: user who submitted score
@param score: score (int) to add @param score: score to add
""" """
# TODO update user submission to not have dupe users
boss_category_config = highscores_config["categories"][category]
# Splitting on pipe. My discord has multiple names on account split by pipe.
# cap length to data defined length.
user = user.split("|")[0][: highscores_data["username_length"]]
# strip leading zeros and colon from score
score = score.lstrip("0:")
# create score tuple for either time or int # create score tuple for either time or int
# define sort index # define sort index
if highscores_config["categories"][category]["is_time_record"]: if boss_category_config["is_time_record"]:
minutes = score.split(":")[0] if score.find(":") == -1:
seconds = score.split(":")[1] score_seconds = float(score)
score_seconds = int(minutes) * 60 + int(seconds) else:
minutes = score.split(":")[0]
seconds = score.split(":")[1]
score_seconds = int(minutes) * 60 + float(seconds)
score_tuple = (user, score, score_seconds) score_tuple = (user, score, score_seconds)
sort_index = 2 sort_index = 2
@@ -89,13 +115,35 @@ async def submit_score(
score_tuple = (user, int(score)) score_tuple = (user, int(score))
sort_index = 1 sort_index = 1
scores = highscores_data[boss_name]["categories"][category] scores: "list[tuple]" = highscores_data[boss_name]["categories"][category]
scores.append(score_tuple)
# if user is in an existing tuple, delete it.
users_scores: "list[tuple]" = [score_tuple]
for tup in scores:
if user == tup[0]:
# Add tup to users_scores, remove from scores
users_scores.append(tup)
# sort users_scores
users_scores.sort(
key=lambda x: x[sort_index],
reverse=not boss_category_config["ascending"],
)
for index, tup in enumerate(users_scores):
if index == 0:
# first score is the best score.
if tup not in scores:
scores.append(tup)
else:
# discard all other scores from scores list.
if tup in scores:
scores.remove(tup)
# sort scores. # sort scores.
scores.sort( scores.sort(
key=lambda x: x[sort_index], key=lambda x: x[sort_index],
reverse=not highscores_config["categories"][category]["ascending"], reverse=not boss_category_config["ascending"],
) )
# enforce highscore size # enforce highscore size
@@ -110,7 +158,7 @@ async def submit_score(
error_response = 'Registered Highscores channel does not exist or was never registered. \ error_response = 'Registered Highscores channel does not exist or was never registered. \
Register with "?register" command.' Register with "?register" command.'
assert channel is not None, await interaction.send(error_response) assert channel is not None, await interaction.send(error_response, ephemeral=True)
await send_highscore_message(channel, boss_name) await send_highscore_message(channel, boss_name)