Compare commits

..
1 Commits
Author SHA1 Message Date
imcompiling 09041f69db Merge branch '21-edit-configs-from-discord'
build_release / build (push) Has been cancelled
build_latest / build (push) Successful in 15s
2024-09-03 19:51:49 -04:00
19 changed files with 118 additions and 2730 deletions
+1
View File
@@ -105,6 +105,7 @@ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
poetry.lock
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
-2
View File
@@ -3,8 +3,6 @@
"aiofiles", "aiofiles",
"autofilled", "autofilled",
"blurple", "blurple",
"CAKEDAY",
"cakedays",
"CLANLEADER", "CLANLEADER",
"Dakiny", "Dakiny",
"DISCORDMANAGER", "DISCORDMANAGER",
+15 -19
View File
@@ -12,23 +12,22 @@ class ChannelIds:
"""Class holds all channel ids needed by bot.""" """Class holds all channel ids needed by bot."""
admin: int # might want to rename this... admin: int # might want to rename this...
announcement: int announcement_category: int
clan_info: int clan_info: int
event_post: int event_post: int
general: int general: int
voting: int voting_parent: int
@dataclass @dataclass
class Election: class Election:
"""Class defines relevant election dates.""" """Class defines relevant election dates."""
active: bool name: str
election_start: str election_start: str
highcouncil_election_announce: str highcouncil_election_announce: str
highcouncil_election_end: str highcouncil_election_end: str
highcouncil_election_start: str highcouncil_election_start: str
name: str
term_end: str term_end: str
term_start: str term_start: str
voting_end: str voting_end: str
@@ -44,38 +43,34 @@ class Params:
guild_id: int guild_id: int
@dataclass
class PointSettings:
"""Class defines point settings editable by Clan Council."""
clan_points: int
clan_points_weekly_limit: int
events_weekly_limit: int
host_points: int
participate_points: int
verify_clan_points: bool
verify_events: bool
@dataclass @dataclass
class RoleIds: class RoleIds:
"""Class holds all role ids needed by bot.""" """Class holds all role ids needed by bot."""
clan_council: int
clan_council_elect: int clan_council_elect: int
clan_council: int
clan_member: int clan_member: int
event_host: int event_host: int
high_council: int high_council: int
@dataclass
class EventSettings:
"""Class defines settings editable by Clan Council."""
host_points: int
participate_points: int
weekly_limit: int
@dataclass @dataclass
class DakinySettings(YAMLWizard): class DakinySettings(YAMLWizard):
"""Class defines all configs required for Dakiny Bot.""" """Class defines all configs required for Dakiny Bot."""
channel_ids: ChannelIds channel_ids: ChannelIds
election_cycles: list[Election] election_cycles: list[Election]
event_settings: EventSettings
params: Params params: Params
point_settings: PointSettings
role_ids: RoleIds role_ids: RoleIds
def save(self) -> None: def save(self) -> None:
@@ -84,3 +79,4 @@ class DakinySettings(YAMLWizard):
settings = DakinySettings.from_yaml_file(CONFIG_LOCATION) settings = DakinySettings.from_yaml_file(CONFIG_LOCATION)
settings.save()
+1 -1
View File
@@ -14,6 +14,6 @@ COPY classes classes/
COPY cogs cogs/ COPY cogs cogs/
COPY database_setup database_setup/ COPY database_setup database_setup/
RUN pip install --upgrade pip && pip install poetry==1.8.3 && poetry export -f requirements.txt --output requirements.txt && pip install -r requirements.txt RUN pip install --upgrade pip && pip install poetry && poetry export -f requirements.txt --output requirements.txt && pip install -r requirements.txt
CMD ["python3", "-u", "./main.py"] CMD ["python3", "-u", "./main.py"]
+2 -3
View File
@@ -49,7 +49,6 @@ class Election:
""" """
self.cycle = cycle self.cycle = cycle
self.election_cycle = settings.election_cycles[self.cycle] self.election_cycle = settings.election_cycles[self.cycle]
self.active = self.election_cycle.active
self.election_start: datetime = get_datetime(self.election_cycle.election_start) self.election_start: datetime = get_datetime(self.election_cycle.election_start)
self.term_start: datetime = get_datetime(self.election_cycle.term_start) self.term_start: datetime = get_datetime(self.election_cycle.term_start)
self.term_end: datetime = get_datetime(self.election_cycle.term_end) self.term_end: datetime = get_datetime(self.election_cycle.term_end)
@@ -161,7 +160,7 @@ class Election:
async def election_start_action(self) -> None: async def election_start_action(self) -> None:
"""Performs required actions for when the election cycle starts.""" """Performs required actions for when the election cycle starts."""
# create new channel for election # create new channel for election
parent: nextcord.TextChannel = self.guild.get_channel(settings.channel_ids.voting) parent: nextcord.TextChannel = self.guild.get_channel(settings.channel_ids.voting_parent)
quarter_name: str = get_election_name() quarter_name: str = get_election_name()
thread_msg: nextcord.Message = await parent.send(content=quarter_name) thread_msg: nextcord.Message = await parent.send(content=quarter_name)
@@ -200,7 +199,7 @@ class Election:
election_thread: nextcord.Thread = self.guild.get_thread(self.get_election_info().ThreadId) election_thread: nextcord.Thread = self.guild.get_thread(self.get_election_info().ThreadId)
await election_thread.send(message) await election_thread.send(message)
vote_msg = await election_thread.send( vote_msg = await election_thread.send(
build_votes_str(self.guild), build_votes_str(settings, self.guild),
view=VoteView(self.guild), view=VoteView(self.guild),
) )
+1 -1
View File
@@ -66,7 +66,7 @@ class VoteSelect(nextcord.ui.StringSelect):
session.execute(stmt) session.execute(stmt)
session.commit() session.commit()
vote_message_str = build_votes_str(interaction.guild) vote_message_str = build_votes_str(settings, interaction.guild)
vote_msg: nextcord.Message = await interaction.channel.fetch_message(vote_msg_id) vote_msg: nextcord.Message = await interaction.channel.fetch_message(vote_msg_id)
await vote_msg.edit(content=vote_message_str) await vote_msg.edit(content=vote_message_str)
-2
View File
@@ -7,7 +7,6 @@ from cogs.commands.refresh import RefreshCog
from cogs.commands.remove import RemoveCog from cogs.commands.remove import RemoveCog
from cogs.commands.request import RequestCog from cogs.commands.request import RequestCog
from cogs.commands.set import SetCog from cogs.commands.set import SetCog
from cogs.commands.settings import SettingsCog
from cogs.commands.test import TestCog from cogs.commands.test import TestCog
from cogs.commands.time import TimeCog from cogs.commands.time import TimeCog
from cogs.commands.view import ViewCog from cogs.commands.view import ViewCog
@@ -22,7 +21,6 @@ def setup(bot: commands.Bot) -> None:
bot.add_cog(RemoveCog()) bot.add_cog(RemoveCog())
bot.add_cog(RequestCog(bot)) bot.add_cog(RequestCog(bot))
bot.add_cog(SetCog()) bot.add_cog(SetCog())
bot.add_cog(SettingsCog())
bot.add_cog(TestCog(bot)) bot.add_cog(TestCog(bot))
bot.add_cog(TimeCog()) bot.add_cog(TimeCog())
bot.add_cog(ViewCog()) bot.add_cog(ViewCog())
+24 -99
View File
@@ -5,7 +5,7 @@ from discord import TextInputStyle
from nextcord.ext import commands from nextcord.ext import commands
from DakinySettings import settings from DakinySettings import settings
from utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, PointType, record_points from utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, record_event_points
class RecordCog(commands.Cog, name="Record Commands"): class RecordCog(commands.Cog, name="Record Commands"):
@@ -117,18 +117,16 @@ class RecordCog(commands.Cog, name="Record Commands"):
await interaction.response.send_message("Please only provide 1 recipient.", ephemeral=True) await interaction.response.send_message("Please only provide 1 recipient.", ephemeral=True)
return return
elif recipient is not None: # noqa: RET505 elif recipient is not None: # noqa: RET505
record_points( record_event_points(
[str(recipient.id)], [str(recipient.id)],
settings.point_settings.clan_points, settings.event_settings.participate_points,
PointType.CLAN,
is_member=True, is_member=True,
) )
recipient_str = f"<@{recipient.id}>" recipient_str = f"<@{recipient.id}>"
elif non_discord_recipient is not None: elif non_discord_recipient is not None:
record_points( record_event_points(
[non_discord_recipient], [non_discord_recipient],
settings.point_settings.clan_points, settings.event_settings.participate_points,
PointType.CLAN,
is_member=False, is_member=False,
) )
recipient_str = f'"{non_discord_recipient}"' recipient_str = f'"{non_discord_recipient}"'
@@ -154,7 +152,7 @@ class EventView(nextcord.ui.View):
""" """
def __init__(self, name: str, host_id: int): def __init__(self, name: str, host_id: int):
super().__init__(timeout=60 * 60 * 2) # timeout 2 hours super().__init__()
self.name: str = name self.name: str = name
self.host_id: int = host_id self.host_id: int = host_id
@@ -182,74 +180,31 @@ class EventView(nextcord.ui.View):
Removes view and embed from recording message. Removes view and embed from recording message.
Sends point information to database. Sends point information to database.
""" """
if settings.point_settings.verify_events: discord_users = self.event_participants.values.members # noqa: PD011 not a pandas object
verification_channel: nextcord.TextChannel = interaction.guild.get_channel(settings.channel_ids.admin) if discord_users:
verification_view = EventVerification( discord_users_list: list[str] = [str(user.id) for user in discord_users if user.id is not self.host_id]
self.event_participants.values.members, interaction.message.embeds[0].fields[1].value, self.host_id record_event_points(
) discord_users_list,
await verification_channel.send( settings.event_settings.participate_points,
content="Event record request.", embed=interaction.message.embeds[0], view=verification_view is_member=True,
)
submission_response = "Event has been submitted to admins for approval."
else:
process_event(
self.event_participants.values.members,
interaction.message.embeds[0].fields[1].value,
self.host_id,
) )
event_channel: nextcord.TextChannel = interaction.guild.get_channel(settings.channel_ids.event_post) non_discord_users = interaction.message.embeds[0].fields[1].value
await event_channel.send(embed=interaction.message.embeds[0]) if non_discord_users:
submission_response = "Event has been submitted." non_discord_users_list: list[str] = non_discord_users.split("\n")
record_event_points(
non_discord_users_list,
settings.event_settings.participate_points,
is_member=False,
)
await interaction.response.edit_message(content=submission_response, view=None, embed=None) record_event_points([self.host_id], settings.event_settings.host_points, is_member=True)
self.stop()
class EventVerification(nextcord.ui.View):
"""Class contains code to approve/deny event submissions."""
def __init__(
self,
discord_users: list[nextcord.Member],
non_discord_users: str,
host_id: int,
):
super().__init__(timeout=None, auto_defer=True)
self.discord_users: list[nextcord.Member] = discord_users
self.non_discord_users: str = non_discord_users
self.host_id: int = host_id
@nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green)
async def approve(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""
Approve button for an event submission.
Calls the process event function. Records who approved the event.
Removes the view from the interaction message.
Args:
button (nextcord.ui.Button): Unused.
interaction (nextcord.Interaction): The interaction of the user clicking the button.
"""
process_event(self.discord_users, self.non_discord_users, self.host_id)
event_channel: nextcord.TextChannel = interaction.guild.get_channel(settings.channel_ids.event_post) event_channel: nextcord.TextChannel = interaction.guild.get_channel(settings.channel_ids.event_post)
await event_channel.send(embed=interaction.message.embeds[0]) await event_channel.send(embed=interaction.message.embeds[0])
await interaction.response.edit_message(content="Event has been submitted.", view=None, embed=None)
content = f"\nApproved by <@{interaction.user.id}>." self.stop()
await interaction.response.edit_message(content=content, view=None)
@nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red)
async def deny(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""
Deny button for an event submission. Just removes the view and logs who denied the request.
Args:
button (nextcord.ui.Button): Unused.
interaction (nextcord.Interaction): The interaction of the user clicking the button.
"""
content = f"\nDenied by <@{interaction.user.id}>."
await interaction.response.edit_message(content=content, view=None)
class ParticipantModal(nextcord.ui.Modal): class ParticipantModal(nextcord.ui.Modal):
@@ -326,33 +281,3 @@ class ParticipantSelect(nextcord.ui.UserSelect):
participants: str = "\n".join([f"<@{participant.id}>" for participant in self.values.members]) participants: str = "\n".join([f"<@{participant.id}>" for participant in self.values.members])
embed.set_field_at(0, name="Participants", value=participants) embed.set_field_at(0, name="Participants", value=participants)
await interaction.response.edit_message(embed=embed) await interaction.response.edit_message(embed=embed)
def process_event(discord_users: list[nextcord.Member], non_discord_users: str, host_id: int) -> None:
"""
Processes all points to give to participants and host for an event.
Args:
discord_users (list[nextcord.Member]): List of discord members that participated in event.
non_discord_users (str): Newline separated list of non discord members that participated in event.
host_id (int): Discord ID of host.
"""
if discord_users:
discord_users_list: list[str] = [str(user.id) for user in discord_users if user.id is not host_id]
record_points(
discord_users_list,
settings.point_settings.participate_points,
PointType.EVENT,
is_member=True,
)
if non_discord_users:
non_discord_users_list: list[str] = non_discord_users.split("\n")
record_points(
non_discord_users_list,
settings.point_settings.participate_points,
PointType.EVENT,
is_member=False,
)
record_points([host_id], settings.point_settings.host_points, PointType.EVENT, is_member=True)
+1 -1
View File
@@ -109,7 +109,7 @@ class UsernameRequest(nextcord.ui.View):
await interaction.followup.edit_message(interaction.message.id, content=content, view=None) await interaction.followup.edit_message(interaction.message.id, content=content, view=None)
else: else:
await interaction.response.edit_message( await interaction.response.edit_message(
content="An error occurred while changing the user's nickname.", content="An error occured while changing the user's nickname.",
view=None, view=None,
) )
-379
View File
@@ -1,379 +0,0 @@
"""Contains commands and views for settings commands."""
import nextcord
from nextcord.ext import commands
from DakinySettings import settings
from utils import GUILDS
class SettingsCog(commands.Cog, name="Settings Commands"):
"""Class holds settings commands."""
def __init__(self) -> None:
"""Default constructor for SettingsCog class. Does nothing."""
@nextcord.slash_command(guild_ids=GUILDS, default_member_permissions=nextcord.Permissions(administrator=True))
async def settings(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for settings. Never called."""
@settings.subcommand()
async def channel_ids(self, interaction: nextcord.Interaction) -> None:
"""Subcommand parent. Never called."""
@channel_ids.subcommand(description="Sets channel that bot sends verification requests to admins.")
async def admin(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
required=False, description="Channel to set to. If left blank, uses current channel."
),
) -> None:
"""
Sets DakinySettings channel_ids.admin value to given channel id or current channel id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
channel (nextcord.TextChannel, optional): Channel to set in settings.
"""
channel_id = interaction.channel_id if channel is None else channel.id
settings.channel_ids.admin = channel_id
settings.save()
await interaction.response.send_message(f"Admin verification channel set to <#{channel_id}>.", ephemeral=True)
@channel_ids.subcommand(description="Sets announcement channel.")
async def announcement(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
required=False, description="Channel to set to. If left blank, uses current channel."
),
) -> None:
"""
Sets DakinySettings channel_ids.announcement value to given channel id or current channel id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
channel (nextcord.TextChannel, optional): Channel to set in settings.
"""
channel_id = interaction.channel_id if channel is None else channel.id
settings.channel_ids.announcement = channel_id
settings.save()
await interaction.response.send_message(f"Announcement channel set to <#{channel_id}>.", ephemeral=True)
@channel_ids.subcommand(description="Sets clan info channel. Channel used for tracking current council.")
async def clan_info(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
required=False, description="Channel to set to. If left blank, uses current channel."
),
) -> None:
"""
Sets DakinySettings channel_ids.clan_info value to given channel id or current channel id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
channel (nextcord.TextChannel, optional): Channel to set in settings.
"""
channel_id = interaction.channel_id if channel is None else channel.id
settings.channel_ids.clan_info = channel_id
settings.save()
await interaction.response.send_message(f"Clan information channel set to <#{channel_id}>.", ephemeral=True)
@channel_ids.subcommand(
description="Sets event post channel. Channel is where event tracking information is posted."
)
async def event_post(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
required=False, description="Channel to set to. If left blank, uses current channel."
),
) -> None:
"""
Sets DakinySettings channel_ids.event_post value to given channel id or current channel id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
channel (nextcord.TextChannel, optional): Channel to set in settings.
"""
channel_id = interaction.channel_id if channel is None else channel.id
settings.channel_ids.event_post = channel_id
settings.save()
await interaction.response.send_message(f"Event posting channel set to <#{channel_id}>.", ephemeral=True)
@channel_ids.subcommand(description="Sets general channel.")
async def general(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
required=False, description="Channel to set to. If left blank, uses current channel."
),
) -> None:
"""
Sets DakinySettings channel_ids.general value to given channel id or current channel id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
channel (nextcord.TextChannel, optional): Channel to set in settings.
"""
channel_id = interaction.channel_id if channel is None else channel.id
settings.channel_ids.general = channel_id
settings.save()
await interaction.response.send_message(f"General channel set to <#{channel_id}>.", ephemeral=True)
@channel_ids.subcommand(description="Sets voting channel. Channel used as parent for voting threads.")
async def voting(
self,
interaction: nextcord.Interaction,
channel: nextcord.TextChannel = nextcord.SlashOption(
required=False, description="Channel to set to. If left blank, uses current channel."
),
) -> None:
"""
Sets DakinySettings channel_ids.voting value to given channel id or current channel id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
channel (nextcord.TextChannel, optional): Channel to set in settings.
"""
channel_id = interaction.channel_id if channel is None else channel.id
settings.channel_ids.voting = channel_id
settings.save()
await interaction.response.send_message(f"Voting channel set to <#{channel_id}>.", ephemeral=True)
@settings.subcommand()
async def point_settings(self, interaction: nextcord.Interaction) -> None:
"""Subcommand parent. Never called."""
@point_settings.subcommand(description="Sets Points value.")
async def clan_points(
self,
interaction: nextcord.Interaction,
points: int = nextcord.SlashOption(
required=True, description="Number of Clan Points the recipient earns.", min_value=0
),
) -> None:
"""
Sets DakinySettings point_settings.clan_points to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
points (int): Number of points to assign to PointSettings.points.
"""
settings.point_settings.clan_points = points
settings.save()
await interaction.response.send_message(f"Points set to {points}.", ephemeral=True)
@point_settings.subcommand(description="Sets Event Weekly Limit value for Clan Points.")
async def clan_points_weekly_limit(
self,
interaction: nextcord.Interaction,
points: int = nextcord.SlashOption(
required=True, description="Number of Clan Points a clan member can earn in a week.", min_value=0
),
) -> None:
"""
Sets DakinySettings point_settings.clan_points_weekly_limit to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
points (int): Number of points to set.
"""
settings.point_settings.clan_points_weekly_limit = points
settings.save()
await interaction.response.send_message(f"Clan Points Weekly Limit set to {points}.", ephemeral=True)
@point_settings.subcommand(description="Sets Event Weekly Limit value.")
async def events_weekly_limit(
self,
interaction: nextcord.Interaction,
points: int = nextcord.SlashOption(
required=True, description="Number of Event Points a clan member can earn in a week.", min_value=0
),
) -> None:
"""
Sets DakinySettings point_settings.events_weekly_limit to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
points (int): Number of points to set.
"""
settings.point_settings.events_weekly_limit = points
settings.save()
await interaction.response.send_message(f"Event Weekly Limit set to {points}.", ephemeral=True)
@point_settings.subcommand(description="Sets Event Host Points value.")
async def host_points(
self,
interaction: nextcord.Interaction,
points: int = nextcord.SlashOption(
required=True, description="Number of Event Points a host earns when hosting event.", min_value=0
),
) -> None:
"""
Sets DakinySettings point_settings.host_points to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
points (int): Number of points to set.
"""
settings.point_settings.host_points = points
settings.save()
await interaction.response.send_message(f"Event Host Points set to {points}.", ephemeral=True)
@point_settings.subcommand(description="Sets Event Participation Points value.")
async def participate_points(
self,
interaction: nextcord.Interaction,
points: int = nextcord.SlashOption(
required=True,
description="Number of Event Points a participant earns when attending an event.",
min_value=0,
),
) -> None:
"""
Sets DakinySettings point_settings.participate_points to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
points (int): Number of points to set.
"""
settings.point_settings.participate_points = points
settings.save()
await interaction.response.send_message(f"Event Participation Points set to {points}.", ephemeral=True)
@point_settings.subcommand(description="Sets whether to verify a clan point entry.")
async def verify_clan_points(
self,
interaction: nextcord.Interaction,
do_verification: bool = nextcord.SlashOption( # noqa: FBT001
required=True,
description="Whether an event should be verified or not.",
),
) -> None:
"""
Sets DakinySettings point_settings.verify_clan_points to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
do_verification (bool): If the bot should verify events.
"""
settings.point_settings.verify_clan_points = do_verification
settings.save()
await interaction.response.send_message(f"Event verification set to {do_verification}.", ephemeral=True)
@point_settings.subcommand(description="Sets whether an event.")
async def verify_events(
self,
interaction: nextcord.Interaction,
do_verification: bool = nextcord.SlashOption( # noqa: FBT001
required=True,
description="Whether an event should be verified or not.",
),
) -> None:
"""
Sets DakinySettings point_settings.verify_events to the given number of points.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
do_verification (bool): If the bot should verify events.
"""
settings.point_settings.verify_events = do_verification
settings.save()
await interaction.response.send_message(f"Event verification set to {do_verification}.", ephemeral=True)
@settings.subcommand()
async def role_ids(self, interaction: nextcord.Interaction) -> None:
"""Subcommand parent. Never called."""
@role_ids.subcommand(description="Sets Clan Council role id.")
async def clan_council(
self,
interaction: nextcord.Interaction,
role: nextcord.Role = nextcord.SlashOption(required=True, description="Role to set."),
) -> None:
"""
Sets DakinySettings role_ids.clan_council to the given role id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
role (nextcord.TextChannel): Role to set in settings.
"""
role_id = role.id
settings.role_ids.clan_council = role_id
settings.save()
await interaction.response.send_message(f"Clan Council role set to <@&{role_id}>.", ephemeral=True)
@role_ids.subcommand(description="Sets Clan Council Elect role id.")
async def clan_council_elect(
self,
interaction: nextcord.Interaction,
role: nextcord.Role = nextcord.SlashOption(required=True, description="Role to set."),
) -> None:
"""
Sets DakinySettings role_ids.clan_council_elect to the given role id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
role (nextcord.TextChannel): Role to set in settings.
"""
role_id = role.id
settings.role_ids.clan_council_elect = role_id
settings.save()
await interaction.response.send_message(f"Clan Council Elect role set to <@&{role_id}>.", ephemeral=True)
@role_ids.subcommand(description="Sets Clan Member role id.")
async def clan_member(
self,
interaction: nextcord.Interaction,
role: nextcord.Role = nextcord.SlashOption(required=True, description="Role to set."),
) -> None:
"""
Sets DakinySettings role_ids.clan_member to the given role id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
role (nextcord.TextChannel): Role to set in settings.
"""
role_id = role.id
settings.role_ids.clan_member = role_id
settings.save()
await interaction.response.send_message(f"Clan Member role set to <@&{role_id}>.", ephemeral=True)
@role_ids.subcommand(description="Sets Event Host role id.")
async def event_host(
self,
interaction: nextcord.Interaction,
role: nextcord.Role = nextcord.SlashOption(required=True, description="Role to set."),
) -> None:
"""
Sets DakinySettings role_ids.event_host to the given role id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
role (nextcord.TextChannel): Role to set in settings.
"""
role_id = role.id
settings.role_ids.event_host = role_id
settings.save()
await interaction.response.send_message(f"Event Host role set to <@&{role_id}>.", ephemeral=True)
@role_ids.subcommand(description="Sets High Council role id.")
async def high_council(
self,
interaction: nextcord.Interaction,
role: nextcord.Role = nextcord.SlashOption(required=True, description="Role to set."),
) -> None:
"""
Sets DakinySettings role_ids.high_council to the given role id.
Args:
interaction (nextcord.Interaction): Discord interaction of command.
role (nextcord.TextChannel): Role to set in settings.
"""
role_id = role.id
settings.role_ids.high_council = role_id
settings.save()
await interaction.response.send_message(f"High Council role set to <@&{role_id}>.", ephemeral=True)
+8 -20
View File
@@ -5,7 +5,7 @@ from nextcord.ext import application_checks, commands
from classes.election import Election from classes.election import Election
from DakinySettings import settings from DakinySettings import settings
from utils import GUILDS, get_active_election_cycle, get_quarter_name from utils import GUILDS, get_current_election_cycle
ME = 206602713511886849 ME = 206602713511886849
@@ -40,7 +40,7 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.election_start_action() await cycle.election_start_action()
@@ -56,7 +56,7 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.voting_start_action() await cycle.voting_start_action()
@@ -72,7 +72,7 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.voting_end_action() await cycle.voting_end_action()
@@ -88,7 +88,7 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.highcouncil_election_start_action() await cycle.highcouncil_election_start_action()
@@ -104,7 +104,7 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.election_reminder() await cycle.election_reminder()
@@ -120,7 +120,7 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.check_date() await cycle.check_date()
@@ -136,20 +136,8 @@ class TestCog(commands.Cog, name="Test commands."):
interaction (nextcord.Interaction): Interaction with the message. interaction (nextcord.Interaction): Interaction with the message.
""" """
cycle = Election( cycle = Election(
get_active_election_cycle(), get_current_election_cycle(),
self.bot.get_guild(settings.params.guild_id), self.bot.get_guild(settings.params.guild_id),
) )
await cycle.term_start_action() await cycle.term_start_action()
await interaction.send("Test command sent.", ephemeral=True) await interaction.send("Test command sent.", ephemeral=True)
@test.subcommand()
@is_me()
async def get_quarter_name(self, interaction: nextcord.Interaction) -> None:
"""
Tests getting the quarter name.
Args:
interaction (nextcord.Interaction): Interaction with the message.
"""
name = get_quarter_name()
await interaction.send(name)
+28 -110
View File
@@ -7,12 +7,12 @@ from sqlalchemy import select
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from database_setup.sql_data_classes import ( from database_setup.sql_data_classes import (
Points, EventPoints,
Presences, Presences,
Votes, Votes,
engine, engine,
) )
from utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, PointType, VoteType from utils import MAX_DISCORD_MESSAGE_LENGTH, VoteType
class ViewCog(commands.Cog, name="View Commands"): class ViewCog(commands.Cog, name="View Commands"):
@@ -21,7 +21,7 @@ class ViewCog(commands.Cog, name="View Commands"):
def __init__(self) -> None: def __init__(self) -> None:
"""Default constructor for ViewCog class. Does nothing.""" """Default constructor for ViewCog class. Does nothing."""
@nextcord.slash_command(guild_ids=GUILDS) @nextcord.slash_command()
async def view(self, interaction: nextcord.Interaction) -> None: async def view(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for set. Never called.""" """Parent slash command for set. Never called."""
@@ -49,12 +49,12 @@ class ViewCog(commands.Cog, name="View Commands"):
with Session(engine) as session: with Session(engine) as session:
if quarter_name: if quarter_name:
ppdf = pd.read_sql_query( ppdf = pd.read_sql_query(
sql=session.query(Points).where(Points.QuarterName.startswith(quarter_name)).statement, sql=session.query(EventPoints).where(EventPoints.QuarterName.startswith(quarter_name)).statement,
con=engine, con=engine,
) )
else: else:
ppdf = pd.read_sql_query( ppdf = pd.read_sql_query(
sql=session.query(Points).statement, sql=session.query(EventPoints).statement,
con=engine, con=engine,
) )
@@ -63,53 +63,30 @@ class ViewCog(commands.Cog, name="View Commands"):
return return
quarters = ppdf.groupby("QuarterName") quarters = ppdf.groupby("QuarterName")
content = create_points_string(interaction, quarters, PointType.EVENT) content = ""
for quarter in quarters.groups:
content += f"**{quarter}**\n"
unsorted_all = quarters.get_group(quarter).groupby("DiscordMember")
members = unsorted_all.get_group(name=True)
if len(content) > MAX_DISCORD_MESSAGE_LENGTH: # there can be no non-discord members sometimes.
await interaction.followup.send("Message length is too long, please select search to limit results.") try:
return sorted_non_members = unsorted_all.get_group(name=False).sort_values(by="UserId")
except KeyError:
await interaction.followup.send(content=content, ephemeral=True) sorted_non_members = None
sorted_members = members.sort_values(
@view.subcommand(description="Show event points.") by="UserId",
async def clan_points( key=lambda user_ids: (
self, [interaction.guild.get_member(int(user_id)).display_name.lower() for user_id in user_ids]
interaction: nextcord.Interaction, ),
quarter_name: str = nextcord.SlashOption(
description="The quarter to view",
required=False,
),
) -> None:
"""
View the clan points.
Builds a message with the clan points for the quarter that matches with quarter_name.
Args:
interaction (nextcord.Interaction): Interaction with the message.
quarter_name (str, optional): Same of quarter to autofill with. Is SlashOption.
"""
# just in case
await interaction.response.defer(ephemeral=True)
with Session(engine) as session:
if quarter_name:
ppdf = pd.read_sql_query(
sql=session.query(Points).where(Points.QuarterName.startswith(quarter_name)).statement,
con=engine,
) )
else: sorted_all = pd.concat([sorted_members, sorted_non_members], ignore_index=True, sort=False)
ppdf = pd.read_sql_query( for row in sorted_all.itertuples():
sql=session.query(Points).statement, userid = row.UserId
con=engine, points = row.Points
) discord_member = row.DiscordMember
content += f"<@{userid}>: {points}\n" if discord_member else f"{userid}: {points}\n"
if ppdf.empty: content += "\n"
await interaction.followup.send(f'Quarter "{quarter_name}" is invalid.', ephemeral=True)
return
quarters = ppdf.groupby("QuarterName")
content = create_points_string(interaction, quarters, PointType.CLAN)
if len(content) > MAX_DISCORD_MESSAGE_LENGTH: if len(content) > MAX_DISCORD_MESSAGE_LENGTH:
await interaction.followup.send("Message length is too long, please select search to limit results.") await interaction.followup.send("Message length is too long, please select search to limit results.")
@@ -215,7 +192,6 @@ class ViewCog(commands.Cog, name="View Commands"):
await interaction.response.send_autocomplete(autofilled_elections[:25]) await interaction.response.send_autocomplete(autofilled_elections[:25])
@event_points.on_autocomplete("quarter_name") @event_points.on_autocomplete("quarter_name")
@clan_points.on_autocomplete("quarter_name")
async def event_points_autocomplete(self, interaction: nextcord.Interaction, quarter_name: str) -> None: async def event_points_autocomplete(self, interaction: nextcord.Interaction, quarter_name: str) -> None:
""" """
Collects all elections that start with the election string. Collects all elections that start with the election string.
@@ -225,7 +201,7 @@ class ViewCog(commands.Cog, name="View Commands"):
quarter_name (str): String to run autocomplete on. quarter_name (str): String to run autocomplete on.
""" """
with Session(engine) as session: with Session(engine) as session:
stmt = select(Points.QuarterName).distinct() stmt = select(EventPoints.QuarterName).distinct()
quarters = session.scalars(stmt) quarters = session.scalars(stmt)
# if empty compare string # if empty compare string
@@ -237,61 +213,3 @@ class ViewCog(commands.Cog, name="View Commands"):
quarter for quarter in quarters if quarter.lower().startswith(quarter_name.lower()) quarter for quarter in quarters if quarter.lower().startswith(quarter_name.lower())
] ]
await interaction.response.send_autocomplete(autofilled_quarters[:25]) await interaction.response.send_autocomplete(autofilled_quarters[:25])
def create_points_string(interaction: nextcord.Interaction, quarters: any, point_type: PointType) -> str:
"""
Creates the string to show how many clan/event points users have.
Args:
interaction (nextcord.Interaction): Discord interaction of the command.
quarters (any): Pandas objects grouped by quarters.
point_type (PointType): The type of points to create the message for.
Returns:
str: The string with the message content.
"""
content: str = ""
for quarter in quarters.groups:
content += f"**{quarter}**\n"
unsorted_all = quarters.get_group(quarter).groupby("DiscordMember")
members = unsorted_all.get_group(name=True)
# there can be no non-discord members sometimes.
try:
sorted_non_members = unsorted_all.get_group(name=False).sort_values(by="UserId")
except KeyError:
sorted_non_members = None
sorted_members = members.sort_values(
by="UserId",
key=lambda user_ids: ([member_lambda_string(interaction.guild, user_id) for user_id in user_ids]),
)
sorted_all = pd.concat([sorted_members, sorted_non_members], ignore_index=True, sort=False)
for row in sorted_all.itertuples():
points = row.EventPoints if point_type == PointType.EVENT else row.ClanPoints
discord_member = row.DiscordMember
if points > 0:
content += f"<@{row.UserId}>: {points}\n" if discord_member else f"{row.UserId}: {points}\n"
content += "\n"
return content
def member_lambda_string(guild: nextcord.Guild, user_id: int) -> str:
"""
Finds a user in a guild and returns the display name.
Returns the user id in string form if user is no longer in guild.
I don't know if I like this name very much.
Args:
guild (nextcord.Guild): Guild object to find the user in.
user_id (int): User id to get display name of.
Returns:
str: String to use for sorting.
"""
try:
return guild.get_member(int(user_id)).display_name.lower()
except AttributeError:
# The user is no longer a part of the guild
return str(user_id)
+4 -12
View File
@@ -13,15 +13,13 @@ from classes.election import Election
from DakinySettings import settings from DakinySettings import settings
from database_setup.sql_data_classes import UserBirthday, engine from database_setup.sql_data_classes import UserBirthday, engine
from main import birthday_list from main import birthday_list
from utils import get_active_election_cycle from utils import get_current_election_cycle
if TYPE_CHECKING: if TYPE_CHECKING:
import nextcord import nextcord
KANDY = 239547993106284544 KANDY = 239547993106284544
IGNORE_CAKEDAY = [266378407682506754]
class DailyLoop(commands.Cog, name="Daily Loop"): class DailyLoop(commands.Cog, name="Daily Loop"):
"""Class for performing daily functions.""" """Class for performing daily functions."""
@@ -77,15 +75,13 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
Returns: Returns:
Election: Current election cycle, None if there is no election happening. Election: Current election cycle, None if there is no election happening.
""" """
cycle = get_active_election_cycle() cycle = get_current_election_cycle()
# prevent exception from using cycle as an Int in an array # prevent exception from using cycle as an Int in an array
if cycle is None: if cycle is None:
return None return None
election = Election(cycle, self.bot.get_guild(settings.params.guild_id)) return Election(cycle, self.bot.get_guild(settings.params.guild_id))
return election if election.active else None
def update_birthdays(self) -> None: def update_birthdays(self) -> None:
"""Goes through database to collect all birthdays that are today. Adds user's id to birthday list.""" """Goes through database to collect all birthdays that are today. Adds user's id to birthday list."""
@@ -114,11 +110,7 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
announce_channel: nextcord.TextChannel = guild.get_channel(settings.channel_ids.general) announce_channel: nextcord.TextChannel = guild.get_channel(settings.channel_ids.general)
cakedays: list[nextcord.Member] = [ cakedays: list[nextcord.Member] = [
human human for human in guild.humans if human.joined_at.month == today.month and human.joined_at.day == today.day
for human in guild.humans
if human.joined_at.month == today.month
and human.joined_at.day == today.day
and human.id not in IGNORE_CAKEDAY
] ]
for member in cakedays: for member in cakedays:
+4 -6
View File
@@ -41,12 +41,11 @@ CREATE TABLE Presences (
Presence varchar(100) not null Presence varchar(100) not null
); );
CREATE TABLE Points ( CREATE TABLE EventPoints (
QuarterName varchar(30) not null, QuarterName varchar(30) not null,
UserId varchar(32) not null, UserId varchar(32) not null,
DiscordMember boolean not null, DiscordMember boolean not null,
EventPoints int unsigned DEFAULT 0, Points int unsigned DEFAULT 0,
ClanPoints int unsigned DEFAULT 0,
PRIMARY KEY (QuarterName, UserId) PRIMARY KEY (QuarterName, UserId)
); );
@@ -55,10 +54,9 @@ CREATE TABLE Messages (
Id bigint unsigned not null Id bigint unsigned not null
); );
CREATE TABLE PointsLimit ( CREATE TABLE EventPointsLimit (
UserId varchar(32) not null, UserId varchar(32) not null,
EventPoints int unsigned DEFAULT 0, Points int unsigned DEFAULT 0,
ClanPoints int unsigned DEFAULT 0,
Week varchar(7) not null, Week varchar(7) not null,
PRIMARY KEY (UserId, Week) PRIMARY KEY (UserId, Week)
); );
+2 -2
View File
@@ -10,11 +10,11 @@ Base = automap_base()
Base.prepare(autoload_with=engine) Base.prepare(autoload_with=engine)
# define the classes # define the classes
Points = Base.classes.Points
PointsLimit = Base.classes.PointsLimit
CouncilMembers = Base.classes.CouncilMembers CouncilMembers = Base.classes.CouncilMembers
ElectionInformation = Base.classes.ElectionInformation ElectionInformation = Base.classes.ElectionInformation
EventPointsLimit = Base.classes.EventPointsLimit
Messages = Base.classes.Messages Messages = Base.classes.Messages
EventPoints = Base.classes.EventPoints
Presences = Base.classes.Presences Presences = Base.classes.Presences
UserBirthday = Base.classes.UserBirthday UserBirthday = Base.classes.UserBirthday
Votes = Base.classes.Votes Votes = Base.classes.Votes
Generated
-2006
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -20,7 +20,7 @@ dataclass-wizard = "^0.22.3"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
ruff = "^0.6.9" ruff = "^0.6.3"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
@@ -85,7 +85,6 @@ ignore = [
"E117", "E117",
"ISC001", "ISC001",
"ISC002", "ISC002",
"PD011", # doesn't like me using '.values' on embeds....
"Q000", "Q000",
"Q001", "Q001",
"Q002", "Q002",
-20
View File
@@ -1,20 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"lockFileMaintenance": {
"automerge": true,
"enabled": true
},
"minor": {
"automerge": true,
"automergeType": "branch"
},
"patch": {
"automerge": true,
"automergeType": "branch"
},
"pin": {
"automerge": true,
"automergeType": "branch"
},
"ignoreTests": true
}
+26 -45
View File
@@ -15,9 +15,9 @@ from sqlalchemy.orm import Session
from DakinySettings import settings from DakinySettings import settings
from database_setup.sql_data_classes import ( from database_setup.sql_data_classes import (
CouncilMembers, CouncilMembers,
EventPoints,
EventPointsLimit,
Messages, Messages,
Points,
PointsLimit,
Votes, Votes,
engine, engine,
) )
@@ -30,7 +30,6 @@ MAX_DISCORD_MESSAGE_LENGTH = 2000
GUILDS = [1000247361639030804, 239758371358638080] GUILDS = [1000247361639030804, 239758371358638080]
VoteType = StrEnum("VoteType", ["COUNCIL", "DISCORDMANAGER", "CLANLEADER"]) VoteType = StrEnum("VoteType", ["COUNCIL", "DISCORDMANAGER", "CLANLEADER"])
PointType = StrEnum("PointType", ["CLAN", "EVENT"])
def get_datetime(day: str) -> datetime: def get_datetime(day: str) -> datetime:
@@ -49,10 +48,10 @@ def get_datetime(day: str) -> datetime:
day = int(day.split("/")[1]) day = int(day.split("/")[1])
# ensure that year rolls over if current month is December and the next month is less than 12. # ensure that year rolls over if current month is December and the next month is less than 12.
if today.month == calendar.DECEMBER and month < calendar.OCTOBER: if today.month == calendar.DECEMBER and month < calendar.DECEMBER:
year += 1 year += 1
if today.month == calendar.JANUARY and month > calendar.MARCH: if today.month == calendar.JANUARY and month > calendar.JANUARY:
year -= 1 year -= 1
return datetime(year, month, day, tzinfo=UTC) return datetime(year, month, day, tzinfo=UTC)
@@ -65,7 +64,7 @@ def get_election_name() -> str:
Returns: Returns:
str: the name of the election cycle str: the name of the election cycle
""" """
cycle = get_active_election_cycle() cycle = get_current_election_cycle()
current_cycle = settings.election_cycles[cycle] current_cycle = settings.election_cycles[cycle]
return f"vote-{get_datetime(current_cycle.term_start).year}-{current_cycle.name}" return f"vote-{get_datetime(current_cycle.term_start).year}-{current_cycle.name}"
@@ -83,7 +82,7 @@ def get_quarter_name() -> str:
return f"{get_datetime(current_cycle.term_start).year}-{current_cycle.name}" return f"{get_datetime(current_cycle.term_start).year}-{current_cycle.name}"
def get_active_election_cycle() -> int: def get_current_election_cycle() -> int:
""" """
Returns index for active election. An election is considered active between the election start and the term start. Returns index for active election. An election is considered active between the election start and the term start.
@@ -105,9 +104,7 @@ def get_active_election_cycle() -> int:
def get_quarter_cycle() -> int: def get_quarter_cycle() -> int:
""" """
Returns the index for the current cycle/quarter. Defined as between term start and end. Returns the index for the current cycle/quarter. Defined as between term start and end. Shouldn't return none.
Can return None if entire year isn't covered by cycles.
Returns: Returns:
int: cycle index for current cycle. int: cycle index for current cycle.
@@ -291,46 +288,41 @@ def decorate_dm_nomination(user_id: int, speech: str) -> str:
return msg return msg
def record_points(users: Sequence[str], add_points: int, point_type: PointType, *, is_member: bool) -> None: def record_event_points(users: Sequence[str], add_points: int, *, is_member: bool) -> None:
""" """
Adds points to all users listed. Adds event points to all users listed.
Checks if they don't exist in order to make a new entry, otherwise Checks if they don't exist in order to make a new entry, otherwise
adds points to the existing value. Can be used for hosting or participating in events or for the clan adds points to the existing value. Can be used for hosting or participating.
points.
Args: Args:
users (Sequence[int]): users that participated and need points. users (Sequence[int]): users that participated and need points.
add_points (int): number of points to add add_points (int): number of points to add
point_type (PointType): which point type to add to
is_member (bool): whether the users are a discord member or not is_member (bool): whether the users are a discord member or not
""" """
quarter_name = get_quarter_name() quarter_name = get_quarter_name()
with Session(engine) as session: with Session(engine) as session:
for user in users: for user in users:
stmt = select(Points).where(Points.UserId == user).where(Points.QuarterName == quarter_name) stmt = select(EventPoints).where(EventPoints.UserId == user).where(EventPoints.QuarterName == quarter_name)
points_obj = session.scalar(stmt) points = session.scalar(stmt)
add_points = get_allowed_points(session, point_type, user, add_points) if points is None:
add_clan_points = add_points if point_type == PointType.CLAN else 0 add_points = get_allowed_points(session, settings.event_settings.weekly_limit, user, add_points)
add_event_points = add_points if point_type == PointType.EVENT else 0 new_points = EventPoints(
if points_obj is None:
new_points = Points(
QuarterName=quarter_name, QuarterName=quarter_name,
UserId=user, UserId=user,
DiscordMember=is_member, DiscordMember=is_member,
EventPoints=add_event_points, Points=add_points,
ClanPoints=add_clan_points,
) )
session.add(new_points) session.add(new_points)
else: else:
points_obj.EventPoints += add_event_points add_points = get_allowed_points(session, settings.event_settings.weekly_limit, user, add_points)
points_obj.ClanPoints += add_clan_points points.Points += add_points
session.commit() session.commit()
def get_allowed_points(session: Session, point_type: PointType, user: str, points: int) -> int: def get_allowed_points(session: Session, points_limit: int, user: str, points: int) -> int:
""" """
Gets the maximum number of points a user can be given. Gets the maximum number of points a user can be given.
@@ -340,7 +332,7 @@ def get_allowed_points(session: Session, point_type: PointType, user: str, point
Args: Args:
session (Session): Open connection to database. session (Session): Open connection to database.
point_type (PointType): Which type of points to check the limit for. points_limit (int): Maximum number of points a user can be given in a week.
user (str): user to be given points. Used to check a user's limit. user (str): user to be given points. Used to check a user's limit.
points (int): maximum number of points to give to user. points (int): maximum number of points to give to user.
@@ -350,29 +342,18 @@ def get_allowed_points(session: Session, point_type: PointType, user: str, point
# check for limit # check for limit
today = datetime.now(tz=UTC).date() today = datetime.now(tz=UTC).date()
this_week = f"{today.isocalendar().year}-{today.isocalendar().week}" this_week = f"{today.isocalendar().year}-{today.isocalendar().week}"
points_limit = ( stmt = select(EventPointsLimit).where(EventPointsLimit.UserId == user).where(EventPointsLimit.Week == this_week)
settings.point_settings.events_weekly_limit
if point_type == PointType.EVENT
else settings.point_settings.clan_points_weekly_limit
)
stmt = select(PointsLimit).where(PointsLimit.UserId == user).where(PointsLimit.Week == this_week)
# if under limit, can add up to however many points would make the current limit equal to the # if under limit, can add up to however many points would make the current limit equal to the
# allowed limit. # allowed limit.
limit_obj = session.scalar(stmt) limit = session.scalar(stmt)
if limit_obj is None: if limit is None:
clan_points = points if point_type == PointType.CLAN else 0 new_limit = EventPointsLimit(UserId=user, Points=points, Week=this_week)
event_points = points if point_type == PointType.EVENT else 0
new_limit = PointsLimit(UserId=user, Week=this_week, EventPoints=event_points, ClanPoints=clan_points)
add_points = points add_points = points
session.add(new_limit) session.add(new_limit)
else: else:
curr_points = limit_obj.EventPoints if point_type == PointType.EVENT else limit_obj.ClanPoints add_points = points if limit.Points + points <= points_limit else (max(0, points_limit - limit.Points))
add_points = points if curr_points + points <= points_limit else (max(0, points_limit - curr_points)) limit.Points += add_points
if point_type == PointType.EVENT:
limit_obj.EventPoints += add_points
else:
limit_obj.ClanPoints += add_points
session.commit() session.commit()