Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c4de96c4b | ||
|
|
aaef1f249f | ||
|
|
4936013697 | ||
|
|
945f5766d5 | ||
|
|
8727c0d6f6 | ||
|
|
64e2c10b39 | ||
|
|
3d44c5c905 | ||
|
|
6653bb9bdd | ||
|
|
f5b45b8cab | ||
|
|
412a81db6e | ||
|
|
8069176128 | ||
|
|
5b5e699f7a | ||
|
|
3b0535663a | ||
|
|
6fd61c79cc | ||
|
|
c2c2b132e1 | ||
|
|
66f3d797fd | ||
|
|
ccc489dbaf | ||
|
|
d0961bb9f0 | ||
|
|
83b11206da | ||
|
|
6f28f09b66 | ||
|
|
e1508e6f31 | ||
|
|
be88f1e179 | ||
|
|
ae64ab6dde | ||
|
|
a76df955ec | ||
|
|
2473d258ff | ||
|
|
9c94704fbd | ||
|
|
0c62678797 | ||
|
|
0528eea5cd | ||
|
|
2836237b76 | ||
|
|
4c741136cc | ||
|
|
b221f1e438 | ||
|
|
1b08ba1355 | ||
|
|
e191df1a38 | ||
|
|
e35a2a8c8d | ||
|
|
d7046e4595 |
Vendored
+1
-2
@@ -9,7 +9,6 @@
|
|||||||
"Dakiny",
|
"Dakiny",
|
||||||
"DISCORDMANAGER",
|
"DISCORDMANAGER",
|
||||||
"highcouncil",
|
"highcouncil",
|
||||||
"nextcord",
|
"nextcord"
|
||||||
"ondelete"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
FROM python:3.13.5-slim
|
FROM python:3.13.7-slim
|
||||||
|
|
||||||
# install dependencies
|
# install dependencies
|
||||||
RUN apt-get update && apt-get -y upgrade && apt-get -y install git sqlite3
|
RUN apt-get update && apt-get -y upgrade && apt-get -y install git sqlite3
|
||||||
|
|
||||||
# install uv
|
# install uv
|
||||||
COPY --from=ghcr.io/astral-sh/uv:0.7.20 /uv /uvx /bin/
|
COPY --from=ghcr.io/astral-sh/uv:0.9.28 /uv /uvx /bin/
|
||||||
|
|
||||||
COPY . /home
|
COPY . /home
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class ChannelIds:
|
|||||||
event_post: int
|
event_post: int
|
||||||
general: int
|
general: int
|
||||||
voting: int
|
voting: int
|
||||||
|
ban: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -83,5 +84,4 @@ class DakinySettings(YAMLWizard):
|
|||||||
self.to_yaml_file(CONFIG_LOCATION)
|
self.to_yaml_file(CONFIG_LOCATION)
|
||||||
|
|
||||||
|
|
||||||
tmp = DakinySettings.from_yaml_file(CONFIG_LOCATION)
|
settings = DakinySettings.from_yaml_file(CONFIG_LOCATION)
|
||||||
settings: DakinySettings = tmp[0] if isinstance(tmp, list) else tmp
|
|
||||||
+12
-18
@@ -1,6 +1,6 @@
|
|||||||
"""Main file for bot."""
|
"""Main file for bot."""
|
||||||
|
|
||||||
import pathlib
|
import os
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
@@ -9,9 +9,9 @@ from dakiny_bot.classes.nominate_highcouncil_views import NominateHighCouncilVie
|
|||||||
from dakiny_bot.classes.nominate_views import NominateView
|
from dakiny_bot.classes.nominate_views import NominateView
|
||||||
from dakiny_bot.classes.voting_highcouncil_views import VoteHighcouncilView
|
from dakiny_bot.classes.voting_highcouncil_views import VoteHighcouncilView
|
||||||
from dakiny_bot.classes.voting_views import VoteView
|
from dakiny_bot.classes.voting_views import VoteView
|
||||||
from dakiny_bot.logger import dakiny_logger
|
from dakiny_bot.database import create_database
|
||||||
|
|
||||||
from .dakiny_settings import settings
|
from .DakinySettings import settings
|
||||||
|
|
||||||
birthday_list: list[int] = []
|
birthday_list: list[int] = []
|
||||||
|
|
||||||
@@ -31,25 +31,19 @@ def run() -> None:
|
|||||||
bot = commands.Bot(intents=intents)
|
bot = commands.Bot(intents=intents)
|
||||||
|
|
||||||
# Get the modules of all cogs whose directory structure is ./cogs/<module_name>
|
# Get the modules of all cogs whose directory structure is ./cogs/<module_name>
|
||||||
for folder in pathlib.Path("dakiny_bot/cogs").iterdir():
|
for folder in os.listdir("dakiny_bot/cogs"):
|
||||||
bot.load_extension(f"dakiny_bot.cogs.{folder.name}")
|
bot.load_extension(f"dakiny_bot.cogs.{folder}")
|
||||||
|
|
||||||
@bot.listen()
|
@bot.listen()
|
||||||
async def on_ready() -> None:
|
async def on_ready() -> None:
|
||||||
"""When Discord is connected."""
|
"""When Discord is connected."""
|
||||||
if bot.user is None:
|
assert bot.user is not None # noqa: S101 documentation does this
|
||||||
dakiny_logger.error("Discord connection failed.")
|
create_database # noqa: B018
|
||||||
return
|
bot.add_view(NominateView(bot.get_guild(settings.params.guild_id)))
|
||||||
|
bot.add_view(NominateHighCouncilView(bot.get_guild(settings.params.guild_id)))
|
||||||
if (guild := bot.get_guild(settings.params.guild_id)) is None:
|
bot.add_view(VoteView(bot.get_guild(settings.params.guild_id)))
|
||||||
dakiny_logger.error("Could not find Dakiny guild object.")
|
bot.add_view(VoteHighcouncilView(bot.get_guild(settings.params.guild_id)))
|
||||||
return
|
print(f"{bot.user.name} has connected to Discord!") # noqa: T201 TODO make logging to print my stuff to console
|
||||||
|
|
||||||
bot.add_view(NominateView(guild))
|
|
||||||
bot.add_view(NominateHighCouncilView(guild))
|
|
||||||
bot.add_view(VoteView(guild))
|
|
||||||
bot.add_view(VoteHighcouncilView(guild))
|
|
||||||
dakiny_logger.info("%s has connected to Discord!", bot.user.name)
|
|
||||||
|
|
||||||
# Run Discord bot
|
# Run Discord bot
|
||||||
bot.run(settings.params.discord_token)
|
bot.run(settings.params.discord_token)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from dakiny_bot.classes.nominate_highcouncil_views import NominateHighCouncilVie
|
|||||||
from dakiny_bot.classes.nominate_views import NominateView
|
from dakiny_bot.classes.nominate_views import NominateView
|
||||||
from dakiny_bot.classes.voting_highcouncil_views import VoteHighcouncilView
|
from dakiny_bot.classes.voting_highcouncil_views import VoteHighcouncilView
|
||||||
from dakiny_bot.classes.voting_views import VoteView
|
from dakiny_bot.classes.voting_views import VoteView
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import (
|
from dakiny_bot.database.sql_data_classes import (
|
||||||
CouncilMembers,
|
CouncilMembers,
|
||||||
ElectionInformation,
|
ElectionInformation,
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
"""Contains various views to be used in the high council nomination process."""
|
"""Contains various views to be used in the high council nomination process."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from nextcord.interactions import Interaction
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import CouncilMembers, engine
|
from dakiny_bot.database.sql_data_classes import CouncilMembers, engine
|
||||||
from dakiny_bot.utils import (
|
from dakiny_bot.utils import (
|
||||||
NOMINEE_SPLITTER,
|
NOMINEE_SPLITTER,
|
||||||
@@ -9,9 +13,6 @@ from dakiny_bot.utils import (
|
|||||||
decorate_dm_nomination,
|
decorate_dm_nomination,
|
||||||
get_election_name,
|
get_election_name,
|
||||||
)
|
)
|
||||||
from nextcord.interactions import Interaction
|
|
||||||
from sqlalchemy import select
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
|
|
||||||
class NominateDMModal(nextcord.ui.Modal):
|
class NominateDMModal(nextcord.ui.Modal):
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
"""Contains various views to be used in the nomination process."""
|
"""Contains various views to be used in the nomination process."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
|
||||||
from dakiny_bot.database.sql_data_classes import CouncilMembers, engine
|
|
||||||
from dakiny_bot.utils import NOMINEE_SPLITTER, decorate_nomination, get_election_name
|
|
||||||
from nextcord.interactions import Interaction
|
from nextcord.interactions import Interaction
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from dakiny_bot.DakinySettings import settings
|
||||||
|
from dakiny_bot.database.sql_data_classes import CouncilMembers, engine
|
||||||
|
from dakiny_bot.utils import NOMINEE_SPLITTER, decorate_nomination, get_election_name
|
||||||
|
|
||||||
|
|
||||||
class NominateModal(nextcord.ui.Modal):
|
class NominateModal(nextcord.ui.Modal):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
"""Contains various views to be used while voting for high council. Controls the logic when performing the vote."""
|
"""Contains various views to be used while voting for high council. Controls the logic when performing the vote."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from nextcord import SelectOption
|
||||||
|
from nextcord.interactions import Interaction
|
||||||
|
from sqlalchemy import exc, select, update
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import (
|
from dakiny_bot.database.sql_data_classes import (
|
||||||
CouncilMembers,
|
CouncilMembers,
|
||||||
ElectionInformation,
|
ElectionInformation,
|
||||||
@@ -9,10 +14,6 @@ from dakiny_bot.database.sql_data_classes import (
|
|||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
from dakiny_bot.utils import VoteType, build_highcouncil_votes_str, get_election_name
|
from dakiny_bot.utils import VoteType, build_highcouncil_votes_str, get_election_name
|
||||||
from nextcord import SelectOption
|
|
||||||
from nextcord.interactions import Interaction
|
|
||||||
from sqlalchemy import exc, select, update
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
|
|
||||||
class VoteSelect(nextcord.ui.StringSelect):
|
class VoteSelect(nextcord.ui.StringSelect):
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
"""Contains various views to be used while voting. Controls the logic when performing the vote."""
|
"""Contains various views to be used while voting. Controls the logic when performing the vote."""
|
||||||
|
|
||||||
import nextcord
|
import nextcord
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from nextcord import SelectOption
|
||||||
|
from nextcord.interactions import Interaction
|
||||||
|
from sqlalchemy import exc, select, update
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import (
|
from dakiny_bot.database.sql_data_classes import (
|
||||||
CouncilMembers,
|
CouncilMembers,
|
||||||
ElectionInformation,
|
ElectionInformation,
|
||||||
@@ -9,10 +14,6 @@ from dakiny_bot.database.sql_data_classes import (
|
|||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
from dakiny_bot.utils import VoteType, build_votes_str, get_election_name
|
from dakiny_bot.utils import VoteType, build_votes_str, get_election_name
|
||||||
from nextcord import SelectOption
|
|
||||||
from nextcord.interactions import Interaction
|
|
||||||
from sqlalchemy import exc, select, update
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
|
|
||||||
class VoteSelect(nextcord.ui.StringSelect):
|
class VoteSelect(nextcord.ui.StringSelect):
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from git.exc import GitCommandError
|
|||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
|
|
||||||
from dakiny_bot.classes.gitea_webhook import GiteaWebhook
|
from dakiny_bot.classes.gitea_webhook import GiteaWebhook
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.logger import dakiny_logger
|
from dakiny_bot.logger import dakiny_logger
|
||||||
from dakiny_bot.utils import REPO_DIR, edit_clan_council_info, update_clan_messages
|
from dakiny_bot.utils import REPO_DIR, edit_clan_council_info, update_clan_messages
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import nextcord
|
|||||||
from discord import TextInputStyle
|
from discord import TextInputStyle
|
||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
|
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, PointType, record_points
|
from dakiny_bot.utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, PointType, record_points
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import nextcord
|
|||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import Presences, engine
|
from dakiny_bot.database.sql_data_classes import Presences, engine
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import nextcord
|
import nextcord
|
||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
|
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.utils import GUILDS
|
from dakiny_bot.utils import GUILDS
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from nextcord.ext import application_checks, commands
|
|||||||
|
|
||||||
from dakiny_bot.classes.election import Election
|
from dakiny_bot.classes.election import Election
|
||||||
from dakiny_bot.cogs.loops import daily_loop
|
from dakiny_bot.cogs.loops import daily_loop
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.utils import GUILDS, get_active_election_cycle, get_quarter_name
|
from dakiny_bot.utils import GUILDS, get_active_election_cycle, get_quarter_name
|
||||||
|
|
||||||
ME = 206602713511886849
|
ME = 206602713511886849
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from sqlalchemy.orm import Session
|
|||||||
|
|
||||||
from dakiny_bot.app import birthday_list
|
from dakiny_bot.app import birthday_list
|
||||||
from dakiny_bot.classes.election import Election
|
from dakiny_bot.classes.election import Election
|
||||||
from dakiny_bot.dakiny_settings import settings
|
from dakiny_bot.DakinySettings import settings
|
||||||
from dakiny_bot.database.sql_data_classes import UserBirthday, engine
|
from dakiny_bot.database.sql_data_classes import UserBirthday, engine
|
||||||
from dakiny_bot.logger import dakiny_logger
|
from dakiny_bot.logger import dakiny_logger
|
||||||
from dakiny_bot.utils import get_active_election_cycle
|
from dakiny_bot.utils import get_active_election_cycle
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import nextcord
|
|||||||
from nextcord.ext import commands
|
from nextcord.ext import commands
|
||||||
|
|
||||||
from dakiny_bot.app import birthday_list
|
from dakiny_bot.app import birthday_list
|
||||||
|
from dakiny_bot.DakinySettings import settings
|
||||||
|
|
||||||
|
|
||||||
class SubmissionListener(commands.Cog, name="Message Listener"):
|
class SubmissionListener(commands.Cog, name="Message Listener"):
|
||||||
@@ -16,7 +17,15 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
|
|||||||
async def on_message(self, message: nextcord.Message) -> None:
|
async def on_message(self, message: nextcord.Message) -> None:
|
||||||
"""Listener for message creation. Adds score and attachments to submission."""
|
"""Listener for message creation. Adds score and attachments to submission."""
|
||||||
# don't want to process any messages from bot.
|
# don't want to process any messages from bot.
|
||||||
if message.author.id == self.bot.user.id:
|
if message.author.id == self.bot.user.id: # pyright: ignore[reportOptionalMemberAccess]
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ban all members who message in this channel. Everyone in guild is a member, so shouldn't miss a ban
|
||||||
|
# due to message.author not being a nextcord.Member object.
|
||||||
|
if message.channel.id == settings.channel_ids.ban and isinstance(message.author, nextcord.Member):
|
||||||
|
await message.author.ban(
|
||||||
|
delete_message_seconds=30 * 60, reason="Probable spam. The ban channel was messaged."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await birthday_action(message)
|
await birthday_action(message)
|
||||||
@@ -31,7 +40,7 @@ def setup(bot: commands.Bot) -> None:
|
|||||||
|
|
||||||
async def birthday_action(message: nextcord.Message) -> None:
|
async def birthday_action(message: nextcord.Message) -> None:
|
||||||
"""
|
"""
|
||||||
Checks a message's auther and mentions to see if they are in the birthday list.
|
Checks a message's author and mentions to see if they are in the birthday list.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message (nextcord.Message): The message to check for a birthday person.
|
message (nextcord.Message): The message to check for a birthday person.
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
"""Small script to create the database if it doesn't already exist."""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
if not Path("database/dakiny.db").exists():
|
||||||
|
with sqlite3.connect("database/dakiny.db") as con:
|
||||||
|
cur = con.cursor()
|
||||||
|
|
||||||
|
with Path("dakiny_bot/database/database.sql").open() as file:
|
||||||
|
database_script = file.read()
|
||||||
|
cur.executescript(database_script)
|
||||||
|
con.commit()
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
"""Simple file to set up the sqlmodel connection to database."""
|
|
||||||
|
|
||||||
from sqlmodel import SQLModel, create_engine
|
|
||||||
|
|
||||||
from dakiny_bot.logger import dakiny_logger
|
|
||||||
|
|
||||||
from . import models # noqa: F401 - Need this import for creating the models.
|
|
||||||
|
|
||||||
url = "sqlite:///database/dakiny.db"
|
|
||||||
dakiny_logger.info("Connected to local dev database.")
|
|
||||||
engine = create_engine(url)
|
|
||||||
|
|
||||||
SQLModel.metadata.create_all(engine)
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
"""File holds all model definitions of SQL tables."""
|
|
||||||
|
|
||||||
from sqlalchemy.dialects.mysql import BIGINT, BOOLEAN, INTEGER, VARCHAR
|
|
||||||
from sqlmodel import Column, Field, ForeignKey, Relationship, SQLModel
|
|
||||||
|
|
||||||
|
|
||||||
class ElectionInformation(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Represents all required information for an election.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
name: Name of the election.
|
|
||||||
thread_id: Thread of where all the election related messages take place.
|
|
||||||
council_vote_msg_id: Message where the council vote is presented.
|
|
||||||
high_council_vote_msg_id: Message where the high council vote is presented.
|
|
||||||
council_nomination_msg_id: Message where council nomination button is held.
|
|
||||||
high_council_nomination_msg_id: Message where high council nomination button is held.
|
|
||||||
council_members: List of council members who ran during this election.
|
|
||||||
votes: All votes cast during this election.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__: str = "ElectionInformation"
|
|
||||||
|
|
||||||
name: str = Field(sa_column=Column("ElectionName", VARCHAR, primary_key=True, unique=True), max_length=30)
|
|
||||||
thread_id: int = Field(sa_column=Column("ThreadId", BIGINT(unsigned=True), nullable=False))
|
|
||||||
council_vote_msg_id: int | None = Field(
|
|
||||||
sa_column=Column("CouncilVoteMsgId", BIGINT(unsigned=True), nullable=True), default=None
|
|
||||||
)
|
|
||||||
high_council_vote_msg_id: int | None = Field(
|
|
||||||
sa_column=Column("HighCouncilVoteMsgId", BIGINT(unsigned=True), nullable=True), default=None
|
|
||||||
)
|
|
||||||
council_nomination_msg_id: int | None = Field(
|
|
||||||
sa_column=Column("CouncilNominationMsgId", BIGINT(unsigned=True), nullable=True), default=None
|
|
||||||
)
|
|
||||||
high_council_nomination_msg_id: int | None = Field(
|
|
||||||
sa_column=Column("HighCouncilNominationMsgId", BIGINT(unsigned=True), nullable=True), default=None
|
|
||||||
)
|
|
||||||
|
|
||||||
council_members: list["CouncilMember"] = Relationship(back_populates="election", cascade_delete=True)
|
|
||||||
votes: list["Vote"] = Relationship(back_populates="election", cascade_delete=True)
|
|
||||||
|
|
||||||
|
|
||||||
class CouncilMember(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Represents members involved in a given election.
|
|
||||||
|
|
||||||
Records speech message ids for general election and high council position. Booleans record if the user was elected
|
|
||||||
to a council/high council position.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
member_id: User that is running for council's discord id.
|
|
||||||
speech_msg_id: Message id of user's speech.
|
|
||||||
elected: Whether the user got elected to council.
|
|
||||||
discord_manager: Whether the user got elected to the Discord Manager position.
|
|
||||||
clan_leader: Whether the user got elected to the Clan Leader position.
|
|
||||||
discord_manager_speech_id: Message id of the user's Discord Manager speech.
|
|
||||||
clan_leader_speech_id: Message id of the user's Clan Leader speech.
|
|
||||||
election: The election this user object is for.
|
|
||||||
votes: A list of votes for this user.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__: str = "CouncilMembers"
|
|
||||||
|
|
||||||
election_name: str = Field(
|
|
||||||
sa_column=Column(
|
|
||||||
"ElectionName",
|
|
||||||
VARCHAR,
|
|
||||||
ForeignKey("ElectionInformation.ElectionName", ondelete="CASCADE"),
|
|
||||||
primary_key=True,
|
|
||||||
),
|
|
||||||
max_length=30,
|
|
||||||
)
|
|
||||||
member_id: int = Field(sa_column=Column("MemberId", BIGINT(unsigned=True), primary_key=True, nullable=False))
|
|
||||||
speech_msg_id: int = Field(sa_column=Column("SpeechMsgId", BIGINT(unsigned=True), nullable=False))
|
|
||||||
elected: bool = Field(sa_column=Column("Elected", BOOLEAN), default=False)
|
|
||||||
discord_manager: bool = Field(sa_column=Column("DiscordManager", BOOLEAN), default=False)
|
|
||||||
clan_leader: bool = Field(sa_column=Column("ClanLeader", BOOLEAN), default=False)
|
|
||||||
discord_manager_speech_id: int = Field(
|
|
||||||
sa_column=Column("DiscordManagerSpeechId", BIGINT(unsigned=True), nullable=True)
|
|
||||||
)
|
|
||||||
clan_leader_speech_id: int = Field(sa_column=Column("ClanLeaderSpeechId", BIGINT(unsigned=True), nullable=True))
|
|
||||||
|
|
||||||
election: ElectionInformation | None = Relationship(back_populates="council_members")
|
|
||||||
votes: list["Vote"] = Relationship(back_populates="council_member")
|
|
||||||
|
|
||||||
|
|
||||||
class Vote(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Represents a vote cast during an election.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
election_name: Election this vote was cast in.
|
|
||||||
voter_id: User id that cast the vote.
|
|
||||||
voted_for_id: User id that was voted for.
|
|
||||||
type: Type of election, council, discordmanager, or clanleader.
|
|
||||||
election: A link to the ElectionInformation object that the vote was cast in.
|
|
||||||
council_member: A link to the CouncilMember object that the vote is for.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__: str = "Votes"
|
|
||||||
|
|
||||||
election_name: str = Field(
|
|
||||||
sa_column=Column(
|
|
||||||
"ElectionName",
|
|
||||||
VARCHAR,
|
|
||||||
ForeignKey("ElectionInformation.ElectionName", ondelete="CASCADE"),
|
|
||||||
primary_key=True,
|
|
||||||
),
|
|
||||||
max_length=30,
|
|
||||||
)
|
|
||||||
voter_id: int = Field(sa_column=Column("VoterId", BIGINT(unsigned=True), primary_key=True, nullable=False))
|
|
||||||
# might have to set this foreign key later with alembic migration...
|
|
||||||
voted_for_id: int | None = Field(
|
|
||||||
sa_column=Column("VotedForId", BIGINT(unsigned=True), ForeignKey("CouncilMembers.MemberId"), nullable=True)
|
|
||||||
)
|
|
||||||
type: str = Field(
|
|
||||||
sa_column=Column("Type", VARCHAR, primary_key=True, nullable=False, default="council"), max_length=14
|
|
||||||
)
|
|
||||||
|
|
||||||
election: ElectionInformation | None = Relationship(back_populates="votes")
|
|
||||||
council_member: CouncilMember | None = Relationship(back_populates="votes")
|
|
||||||
|
|
||||||
|
|
||||||
class UserBirthday(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Represents a user's birth month and day.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
id: User's discord id.
|
|
||||||
birthday: Month/day that is a user's birthday.
|
|
||||||
"""
|
|
||||||
|
|
||||||
id: int = Field(sa_column=Column("UserId", BIGINT(unsigned=True), primary_key=True, nullable=False))
|
|
||||||
|
|
||||||
# TODO(imcompiling): split into month and day. Also relies on Alembic migration.
|
|
||||||
# https://gitea.cheezers.org/imcompiling/dakiny-bot/issues/93
|
|
||||||
birthday: str = Field(sa_column=Column("Birthday", VARCHAR, nullable=False), max_length=5)
|
|
||||||
|
|
||||||
|
|
||||||
class Presence(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Stores presences for the bot to pull and use.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
id: Auto-incremented id for each entry.
|
|
||||||
presence: The presence itself.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__: str = "Presences"
|
|
||||||
|
|
||||||
id: int | None = Field(sa_column=Column("Id", INTEGER, primary_key=True), default=None)
|
|
||||||
presence: str = Field(sa_column=Column("Presence", VARCHAR, nullable=False), max_length=100)
|
|
||||||
|
|
||||||
|
|
||||||
class Points(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Stores activity points a user can gain by participating in clan activities.
|
|
||||||
|
|
||||||
Event points and clan points were separated out.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
quarter_name: Name of the quarter the points were earned in.
|
|
||||||
user_id: The user that earned them. Is a discord id if the user is a discord member, otherwise their rsn.
|
|
||||||
discord_member: Boolean on whether the user is a discord member or not. Used to know what the user_id
|
|
||||||
represents.
|
|
||||||
event_points: Event points earned during the quarter.
|
|
||||||
clan_points: Clan points earned during the quarter.
|
|
||||||
"""
|
|
||||||
|
|
||||||
quarter_name: str = Field(sa_column=Column("QuarterName", VARCHAR, primary_key=True, nullable=False), max_length=30)
|
|
||||||
user_id: str = Field(sa_column=Column("UserId", VARCHAR, primary_key=True, nullable=False), max_length=32)
|
|
||||||
discord_member: bool = Field(sa_column=Column("DiscordMember", BOOLEAN, nullable=False))
|
|
||||||
event_points: int = Field(sa_column=Column("EventPoints", INTEGER(unsigned=True), nullable=False), default=0)
|
|
||||||
clan_points: int = Field(sa_column=Column("ClanPoints", INTEGER(unsigned=True), nullable=False), default=0)
|
|
||||||
|
|
||||||
|
|
||||||
class Message(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Table is used to map folders and files to channels and messages in discord.
|
|
||||||
|
|
||||||
If folder name is stored in path, the corresponding id is a channel id. If the path is to a file, the id is for a
|
|
||||||
message.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
path: Either directory or file pathname.
|
|
||||||
id: Channel or message id depending on the path.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__: str = "Messages"
|
|
||||||
|
|
||||||
path: str = Field(sa_column=Column("Path", VARCHAR, primary_key=True, nullable=False), max_length=100)
|
|
||||||
id: int = Field(sa_column=Column("Id", BIGINT(unsigned=True), nullable=False))
|
|
||||||
|
|
||||||
|
|
||||||
class PointsLimit(SQLModel, table=True):
|
|
||||||
"""
|
|
||||||
Table tracks how many points a user has gained in a week.
|
|
||||||
|
|
||||||
Council didn't want users spamming points, so they added a limit to how many can be gained in a week. This table
|
|
||||||
tracks point gain per week.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
user_id: The user that earned points. Is a discord id if the user is a discord member, otherwise their rsn.
|
|
||||||
week: week number/year representation of the week. Ex: 04/2025.
|
|
||||||
event_points: Number of event points earned during this week.
|
|
||||||
clan_points: Number of clan points earned during this week.
|
|
||||||
"""
|
|
||||||
|
|
||||||
user_id: str = Field(sa_column=Column("UserId", VARCHAR, primary_key=True, nullable=False), max_length=32)
|
|
||||||
|
|
||||||
# Maybe should break this out into two columns, week and year. Better data validation and more clear.
|
|
||||||
week: str = Field(sa_column=Column("Week", VARCHAR, primary_key=True, nullable=False), max_length=7)
|
|
||||||
event_points: int = Field(sa_column=Column("EventPoints", INTEGER(unsigned=True), nullable=False), default=0)
|
|
||||||
clan_points: int = Field(sa_column=Column("ClanPoints", INTEGER(unsigned=True), nullable=False), default=0)
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
"""Simple file to set up the sqlalchemy connection to database."""
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.automap import automap_base
|
||||||
|
|
||||||
|
# connect with sql alchemy and autoload schema
|
||||||
|
URL = "sqlite+pysqlite:///database/dakiny.db"
|
||||||
|
engine = create_engine(URL)
|
||||||
|
Base = automap_base()
|
||||||
|
Base.prepare(autoload_with=engine)
|
||||||
|
|
||||||
|
# define the classes
|
||||||
|
Points = Base.classes.Points
|
||||||
|
PointsLimit = Base.classes.PointsLimit
|
||||||
|
CouncilMembers = Base.classes.CouncilMembers
|
||||||
|
ElectionInformation = Base.classes.ElectionInformation
|
||||||
|
Messages = Base.classes.Messages
|
||||||
|
Presences = Base.classes.Presences
|
||||||
|
UserBirthday = Base.classes.UserBirthday
|
||||||
|
Votes = Base.classes.Votes
|
||||||
+1
-1
@@ -22,7 +22,7 @@ from dakiny_bot.database.sql_data_classes import (
|
|||||||
)
|
)
|
||||||
from dakiny_bot.logger import dakiny_logger
|
from dakiny_bot.logger import dakiny_logger
|
||||||
|
|
||||||
from .dakiny_settings import settings
|
from .DakinySettings import settings
|
||||||
|
|
||||||
NOMINEE_SPLITTER = "========================================\n\n"
|
NOMINEE_SPLITTER = "========================================\n\n"
|
||||||
REPO_DIR = "./dakiny-messages"
|
REPO_DIR = "./dakiny-messages"
|
||||||
|
|||||||
+2
-3
@@ -9,11 +9,10 @@ dependencies = [
|
|||||||
"pytz<2025.3,>=2025.2",
|
"pytz<2025.3,>=2025.2",
|
||||||
"sqlalchemy<3.0.0,>=2.0.30",
|
"sqlalchemy<3.0.0,>=2.0.30",
|
||||||
"aiofiles<24.0.0,>=23.2.1",
|
"aiofiles<24.0.0,>=23.2.1",
|
||||||
"setuptools<80.9.1,>=80.9.0",
|
"setuptools<80.10.3,>=80.10.2",
|
||||||
"dataclass-wizard[yaml]>=0.22.3,<1.0.0",
|
"dataclass-wizard[yaml]>=1.0.0,<1.1.0",
|
||||||
"uvicorn>=0.34.0",
|
"uvicorn>=0.34.0",
|
||||||
"audioop-lts>=0.2.1",
|
"audioop-lts>=0.2.1",
|
||||||
"sqlmodel>=0.0.24",
|
|
||||||
]
|
]
|
||||||
name = "dakiny-bot"
|
name = "dakiny-bot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 2
|
revision = 3
|
||||||
requires-python = ">=3.13, <4.0"
|
requires-python = ">=3.13, <4.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -183,13 +183,13 @@ dev = [
|
|||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "aiofiles", specifier = ">=23.2.1,<24.0.0" },
|
{ name = "aiofiles", specifier = ">=23.2.1,<24.0.0" },
|
||||||
{ name = "audioop-lts", specifier = ">=0.2.1" },
|
{ name = "audioop-lts", specifier = ">=0.2.1" },
|
||||||
{ name = "dataclass-wizard", extras = ["yaml"], specifier = ">=0.22.3,<1.0.0" },
|
{ name = "dataclass-wizard", extras = ["yaml"], specifier = ">=1.0.0,<1.1.0" },
|
||||||
{ name = "fastapi", specifier = ">=0.111.0,<1.0.0" },
|
{ name = "fastapi", specifier = ">=0.111.0,<1.0.0" },
|
||||||
{ name = "gitpython", specifier = ">=3.1.43,<4.0.0" },
|
{ name = "gitpython", specifier = ">=3.1.43,<4.0.0" },
|
||||||
{ name = "nextcord", specifier = ">=2.6.0,<3.0.0" },
|
{ name = "nextcord", specifier = ">=2.6.0,<3.0.0" },
|
||||||
{ name = "pandas", specifier = ">=2.2.2,<3.0.0" },
|
{ name = "pandas", specifier = ">=2.2.2,<3.0.0" },
|
||||||
{ name = "pytz", specifier = ">=2025.2,<2025.3" },
|
{ name = "pytz", specifier = ">=2025.2,<2025.3" },
|
||||||
{ name = "setuptools", specifier = ">=80.9.0,<80.9.1" },
|
{ name = "setuptools", specifier = ">=80.10.2,<80.10.3" },
|
||||||
{ name = "sqlalchemy", specifier = ">=2.0.30,<3.0.0" },
|
{ name = "sqlalchemy", specifier = ">=2.0.30,<3.0.0" },
|
||||||
{ name = "uvicorn", specifier = ">=0.34.0" },
|
{ name = "uvicorn", specifier = ">=0.34.0" },
|
||||||
]
|
]
|
||||||
@@ -199,11 +199,11 @@ dev = [{ name = "ruff", specifier = ">=0.6.9,<1.0.0" }]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dataclass-wizard"
|
name = "dataclass-wizard"
|
||||||
version = "0.33.0"
|
version = "1.0.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0a/2a/6ae6638cd5ca919b73d393cdb4a4e53c8c3dcacc003a79d9480ecad46798/dataclass-wizard-0.33.0.tar.gz", hash = "sha256:10edbac626c390ed1f3be4f972303bc2c25c162fd453d55adfe22b05b263ee6d", size = 269911, upload-time = "2024-12-17T21:46:57.38Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b1/9d/465deff762411b97cc37fdd475402484724f695af042fa89ff221f725172/dataclass_wizard-1.0.0.tar.gz", hash = "sha256:d458e6a97585377337f1633e5738ee1c051e84ca7726276ecde78d6534e485b5", size = 449143, upload-time = "2026-07-03T03:21:14.302Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/e6/fa8b6615f988a271b1ab990f0086f90c936608762f02d02db9164cfd7ff9/dataclass_wizard-0.33.0-py2.py3-none-any.whl", hash = "sha256:d257d65a707f6ea76140ad5442189ffe6fc68e1d678dc6b3fa25754e3106160d", size = 164545, upload-time = "2024-12-17T21:46:53.835Z" },
|
{ url = "https://files.pythonhosted.org/packages/2d/8b/d4db2e85e6f0a5dd2e590eb646aea1cabbf3c4629aec2bc5a5540c7a4250/dataclass_wizard-1.0.0-py3-none-any.whl", hash = "sha256:0dc53acbfed4ca5fe6bbdc517003959afbd50c0794dec1f8405d3c4d8ae6690c", size = 305849, upload-time = "2026-07-03T03:21:12.252Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
@@ -509,11 +509,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "80.9.0"
|
version = "80.10.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
|
{ url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
Reference in New Issue
Block a user