Compare commits

..
Author SHA1 Message Date
renovate-bot 030d01f480 Update dependency pytz to v2026 2026-10-11 00:01:26 +00:00
renovate-bot aaef1f249f Update ghcr.io/astral-sh/uv Docker tag to v0.9.28
build_latest / build (push) Successful in 47s
2026-02-01 00:01:18 +00:00
renovate-bot 4936013697 Update dependency setuptools to <80.10.3,>=80.10.2
build_latest / build (push) Has been cancelled
2026-02-01 00:01:11 +00:00
renovate-bot 945f5766d5 Update dependency setuptools to <80.10.2,>=80.10.1
build_latest / build (push) Successful in 1m25s
2026-01-25 00:01:57 +00:00
renovate-bot 8727c0d6f6 Update ghcr.io/astral-sh/uv Docker tag to v0.9.26
build_latest / build (push) Successful in 48s
2026-01-18 00:00:53 +00:00
renovate-bot 64e2c10b39 Update ghcr.io/astral-sh/uv Docker tag to v0.9.24
build_latest / build (push) Successful in 42s
2026-01-11 00:00:50 +00:00
renovate-bot 3d44c5c905 Update ghcr.io/astral-sh/uv Docker tag to v0.9.21
build_latest / build (push) Successful in 1m4s
2026-01-04 00:01:02 +00:00
renovate-bot 6653bb9bdd Update ghcr.io/astral-sh/uv Docker tag to v0.9.18
build_latest / build (push) Successful in 43s
2025-12-21 00:00:56 +00:00
renovate-bot f5b45b8cab Update ghcr.io/astral-sh/uv Docker tag to v0.9.17
build_latest / build (push) Successful in 45s
2025-12-14 00:00:58 +00:00
renovate-bot 412a81db6e Update ghcr.io/astral-sh/uv Docker tag to v0.9.16
build_latest / build (push) Successful in 49s
2025-12-07 00:00:58 +00:00
renovate-bot 8069176128 Update ghcr.io/astral-sh/uv Docker tag to v0.9.13
build_latest / build (push) Successful in 46s
2025-11-30 00:00:56 +00:00
renovate-bot 5b5e699f7a Update ghcr.io/astral-sh/uv Docker tag to v0.9.11
build_latest / build (push) Successful in 36s
2025-11-23 00:00:46 +00:00
renovate-bot 3b0535663a Update ghcr.io/astral-sh/uv Docker tag to v0.9.9
build_latest / build (push) Successful in 40s
2025-11-16 00:00:45 +00:00
renovate-bot 6fd61c79cc Update ghcr.io/astral-sh/uv Docker tag to v0.9.8
build_latest / build (push) Successful in 40s
2025-11-09 00:00:46 +00:00
imcompiling c2c2b132e1 Merge pull request 'Update ghcr.io/astral-sh/uv Docker tag to v0.9.5' (#107) from renovate/ghcr.io-astral-sh-uv-0.x into main
build_latest / build (push) Successful in 42s
Reviewed-on: #107
2025-11-01 17:01:06 -04:00
renovate-bot 66f3d797fd Update ghcr.io/astral-sh/uv Docker tag to v0.9.5 2025-10-26 00:00:38 +00:00
renovate-bot ccc489dbaf Update ghcr.io/astral-sh/uv Docker tag to v0.8.23
build_latest / build (push) Successful in 55s
2025-10-05 04:01:27 +00:00
renovate-bot d0961bb9f0 Update ghcr.io/astral-sh/uv Docker tag to v0.8.22
build_latest / build (push) Successful in 36s
2025-09-28 04:01:28 +00:00
renovate-bot 83b11206da Update ghcr.io/astral-sh/uv Docker tag to v0.8.19
build_latest / build (push) Successful in 41s
2025-09-21 04:01:29 +00:00
renovate-bot 6f28f09b66 Update ghcr.io/astral-sh/uv Docker tag to v0.8.17
build_latest / build (push) Successful in 1m1s
2025-09-14 04:01:27 +00:00
renovate-bot e1508e6f31 Update ghcr.io/astral-sh/uv Docker tag to v0.8.15
build_latest / build (push) Successful in 42s
2025-09-07 04:01:27 +00:00
renovate-bot be88f1e179 Update ghcr.io/astral-sh/uv Docker tag to v0.8.14
build_latest / build (push) Successful in 1m28s
2025-08-31 04:02:06 +00:00
renovate-bot ae64ab6dde Update python Docker tag to v3.13.7
build_latest / build (push) Successful in 59s
2025-08-17 04:00:55 +00:00
renovate-bot a76df955ec Update ghcr.io/astral-sh/uv Docker tag to v0.8.11
build_latest / build (push) Waiting to run
2025-08-17 04:00:49 +00:00
renovate-bot 2473d258ff Update python Docker tag to v3.13.6
build_latest / build (push) Successful in 58s
2025-08-10 04:01:01 +00:00
renovate-bot 9c94704fbd Update ghcr.io/astral-sh/uv Docker tag to v0.8.8
build_latest / build (push) Waiting to run
2025-08-10 04:00:54 +00:00
renovate-bot 0c62678797 Update ghcr.io/astral-sh/uv Docker tag to v0.8.4
build_latest / build (push) Successful in 40s
2025-08-03 04:01:12 +00:00
renovate-bot 0528eea5cd Update ghcr.io/astral-sh/uv Docker tag to v0.8.3
build_latest / build (push) Successful in 40s
2025-07-27 04:00:44 +00:00
imcompiling 2836237b76 Merge pull request 'Update ghcr.io/astral-sh/uv Docker tag to v0.8.0' (#104) from renovate/ghcr.io-astral-sh-uv-0.x into main
build_latest / build (push) Successful in 37s
Reviewed-on: #104
Reviewed-by: imcompiling <[email protected]>
2025-07-21 09:15:20 -04:00
renovate-bot 4c741136cc Update ghcr.io/astral-sh/uv Docker tag to v0.8.0 2025-07-20 04:00:49 +00:00
imcompiling b221f1e438 Feat: Update the ban message.
build_develop / build (push) Successful in 37s
build_latest / build (push) Successful in 21s
build_release / build (push) Successful in 22s
For some reason, the user's name was None. I'm guessing because they were
banned and are no longer a member or something. Don't need to record the member's
name anyway though, as its already a part of the audit log.
2025-07-16 21:24:03 -04:00
imcompiling 1b08ba1355 Feat: Set delete messages to 30 minutes.
build_develop / build (push) Successful in 32s
2025-07-16 16:03:26 -04:00
imcompiling e191df1a38 Fix: spelling and ignore pylance warning.
build_develop / build (push) Successful in 32s
2025-07-16 15:57:31 -04:00
imcompiling e35a2a8c8d Feat: Add autoban
build_develop / build (push) Successful in 38s
2025-07-16 15:55:09 -04:00
imcompiling d7046e4595 Add: config for ban channel id. 2025-07-16 15:45:35 -04:00
10 changed files with 59 additions and 245 deletions
+1 -2
View File
@@ -9,7 +9,6 @@
"Dakiny",
"DISCORDMANAGER",
"highcouncil",
"nextcord",
"ondelete"
"nextcord"
]
}
+2 -2
View File
@@ -1,10 +1,10 @@
FROM python:3.13.5-slim
FROM python:3.13.7-slim
# install dependencies
RUN apt-get update && apt-get -y upgrade && apt-get -y install git sqlite3
# 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
+1
View File
@@ -17,6 +17,7 @@ class ChannelIds:
event_post: int
general: int
voting: int
ban: int
@dataclass
+11 -2
View File
@@ -4,6 +4,7 @@ import nextcord
from nextcord.ext import commands
from dakiny_bot.app import birthday_list
from dakiny_bot.DakinySettings import settings
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:
"""Listener for message creation. Adds score and attachments to submission."""
# 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
await birthday_action(message)
@@ -31,7 +40,7 @@ def setup(bot: commands.Bot) -> 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:
message (nextcord.Message): The message to check for a birthday person.
+13
View File
@@ -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()
-13
View File
@@ -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)
-214
View File
@@ -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)
+20
View File
@@ -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
+2 -3
View File
@@ -6,14 +6,13 @@ dependencies = [
"fastapi<1.0.0,>=0.111.0",
"nextcord<3.0.0,>=2.6.0",
"pandas<3.0.0,>=2.2.2",
"pytz<2025.3,>=2025.2",
"pytz<2026.6,>=2026.5",
"sqlalchemy<3.0.0,>=2.0.30",
"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",
"uvicorn>=0.34.0",
"audioop-lts>=0.2.1",
"sqlmodel>=0.0.24",
]
name = "dakiny-bot"
version = "0.1.0"
Generated
+9 -9
View File
@@ -1,5 +1,5 @@
version = 1
revision = 2
revision = 5
requires-python = ">=3.13, <4.0"
[[package]]
@@ -188,8 +188,8 @@ requires-dist = [
{ name = "gitpython", specifier = ">=3.1.43,<4.0.0" },
{ name = "nextcord", specifier = ">=2.6.0,<3.0.0" },
{ name = "pandas", specifier = ">=2.2.2,<3.0.0" },
{ name = "pytz", specifier = ">=2025.2,<2025.3" },
{ name = "setuptools", specifier = ">=80.9.0,<80.9.1" },
{ name = "pytz", specifier = ">=2026.5,<2026.6" },
{ name = "setuptools", specifier = ">=80.10.2,<80.10.3" },
{ name = "sqlalchemy", specifier = ">=2.0.30,<3.0.0" },
{ name = "uvicorn", specifier = ">=0.34.0" },
]
@@ -458,11 +458,11 @@ wheels = [
[[package]]
name = "pytz"
version = "2025.2"
version = "2026.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
sdist = { url = "https://files.pythonhosted.org/packages/14/21/d83d6ef28c4c912c4bb4d1dcf591f7b8c6bde87b9c66f9f454677314e16d/pytz-2026.5.tar.gz", hash = "sha256:fa23724b9c486543b9ff54a327ee7569ac83ade54bb9afd0fc18676620401c86", size = 318572, upload-time = "2026-10-04T02:37:58.719Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
{ url = "https://files.pythonhosted.org/packages/4f/ef/c66110d46fb800dda0bf33164182dfadabe26a90e4476844d502a23dca8e/pytz-2026.5-py2.py3-none-any.whl", hash = "sha256:e658af3757f9e26a9d25dd2aff38335acd92bc9104f890a894b2c1ba28311b03", size = 506342, upload-time = "2026-10-04T02:37:56.814Z" },
]
[[package]]
@@ -509,11 +509,11 @@ wheels = [
[[package]]
name = "setuptools"
version = "80.9.0"
version = "80.10.2"
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 = [
{ 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]]