49 Commits
Author SHA1 Message Date
renovate-bot bb5889f7ae Update ghcr.io/astral-sh/uv Docker tag to v0.6.17
build_latest / build (push) Successful in 22s
2025-04-27 04:03:17 +00:00
renovate-bot 3c489c83e2 Update python Docker tag to v3.13.3 2025-04-13 14:14:54 +00:00
renovate-bot aeb00a107f Update ghcr.io/astral-sh/uv Docker tag to v0.6.14 2025-04-13 14:14:50 +00:00
renovate-bot c65acb094a Update ghcr.io/astral-sh/uv Docker tag to v0.6.12
build_latest / build (push) Successful in 22s
2025-04-06 04:03:39 +00:00
renovate-bot 631c42ac0b Update ghcr.io/astral-sh/uv Docker tag to v0.6.10
build_latest / build (push) Successful in 21s
2025-03-30 04:03:31 +00:00
imcompiling 7811abc33e Merge pull request 'Update actions/checkout action to v4' (#2) from renovate/actions-checkout-4.x into main
build_latest / build (push) Successful in 20s
Reviewed-on: #2
Reviewed-by: imcompiling <[email protected]>
2025-03-23 23:39:22 -04:00
imcompiling 2a01cf09ec Merge pull request 'Update python Docker tag to v3.13.2' (#1) from renovate/python-3.x into main
build_latest / build (push) Has been cancelled
Reviewed-on: #1
Reviewed-by: imcompiling <[email protected]>
2025-03-23 23:39:11 -04:00
renovate-bot 6d9325c354 Update actions/checkout action to v4 2025-03-23 04:03:22 +00:00
renovate-bot 865802eeeb Update python Docker tag to v3.13.2 2025-03-23 04:03:20 +00:00
renovate-bot 8fce10d9be Update ghcr.io/astral-sh/uv Docker tag to v0.6.9
build_latest / build (push) Successful in 22s
2025-03-23 04:03:16 +00:00
imcompiling 5c6fa4386e Correct database ignore.
build_latest / build (push) Successful in 17s
build_develop / build (push) Successful in 17s
2025-03-19 09:51:13 -04:00
imcompiling 590cb9abe4 Don't need to build latest in release workflow.
build_latest / build (push) Successful in 17s
2025-03-19 09:35:00 -04:00
imcompiling 46f518c1c4 Update uv command to not install dev dependencies and to use the already existing venv.
build_latest / build (push) Successful in 18s
2025-03-19 08:42:50 -04:00
imcompiling 30ecdab8a5 Ignore the database directory.
build_develop / build (push) Successful in 18s
build_latest / build (push) Successful in 17s
2025-03-18 22:44:21 -04:00
imcompiling 565ea73792 Convert to be more consistant like python package.
build_develop / build (push) Successful in 20s
2025-03-18 22:43:20 -04:00
imcompiling bfef75870f Change directory in dockerfile.
build_develop / build (push) Successful in 17s
2025-03-18 22:09:13 -04:00
imcompiling 56289e7038 Use home directory.
build_develop / build (push) Successful in 21s
2025-03-18 22:00:38 -04:00
imcompiling 54a011337a Fix debugger module and imports.
build_develop / build (push) Successful in 21s
2025-03-18 21:45:12 -04:00
imcompiling b3905fa15c Update uv lock. 2025-03-18 21:32:00 -04:00
imcompiling a09845dfb8 Fix import paths.
build_develop / build (push) Successful in 22s
2025-03-18 13:03:56 -04:00
imcompiling a86b01292f Add an editor config file. 2025-03-18 11:10:02 -04:00
imcompiling 1ad68f4014 Remove outdated comment.
build_develop / build (push) Successful in 22s
2025-03-18 11:07:23 -04:00
imcompiling 3aa0bf291f Remove python dotenv. 2025-03-18 11:06:10 -04:00
imcompiling b727a80e4f Fix smells in main.py.
build_develop / build (push) Successful in 26s
2025-03-18 10:11:52 -04:00
imcompiling 857b5f1669 Update module name in dockerfile. 2025-03-18 10:10:38 -04:00
imcompiling 34cd2f9975 Rename module and add init file to conform to ruff.
build_develop / build (push) Successful in 21s
2025-03-18 10:03:51 -04:00
imcompiling 6af2476339 Update settings to fix a smell. Add some spelling words.
build_develop / build (push) Successful in 22s
2025-03-18 09:50:49 -04:00
imcompiling 4a3f356b23 Update imports. 2025-03-18 09:50:36 -04:00
imcompiling 4fe2339ac1 Update launch command to launch in new location. 2025-03-18 09:49:01 -04:00
imcompiling 2069b2544f Remove old config file. Update docker file for directory change. 2025-03-18 09:37:33 -04:00
imcompiling 901bcf3c62 Move all files into off-cooldown-bot dir. 2025-03-18 09:15:22 -04:00
imcompiling 8e8f84a42e Add more to dockerignore.
build_latest / build (push) Successful in 25s
2025-03-18 08:21:14 -04:00
imcompiling cd241e6f5e Remove extra $ on accident.
build_latest / build (push) Successful in 22s
2025-03-17 21:49:13 -04:00
imcompiling 93d9b565c0 Also update in latest.
build_latest / build (push) Failing after 19s
2025-03-17 21:45:00 -04:00
imcompiling c816d3b5b8 Update docker login action.
build_latest / build (push) Failing after 16s
2025-03-17 21:44:05 -04:00
imcompiling 91900f2b00 Update docker file, add a docker ignore.
build_latest / build (push) Failing after 16s
2025-03-17 21:36:09 -04:00
imcompiling e1a7eae355 Fix birthday clear.
build_develop / build (push) Failing after 23s
build_latest / build (push) Failing after 20s
2025-03-17 21:31:44 -04:00
imcompiling 5b77abff13 Add birthday clear command. (not removing from database right now).
build_develop / build (push) Failing after 18s
2025-03-17 18:36:29 -04:00
imcompiling 467c76671b Add the message listener. 2025-03-17 18:27:31 -04:00
imcompiling 8499ae0a78 Remove hi cog. 2025-03-17 18:24:12 -04:00
imcompiling ea3204d890 Fix cog loading. 2025-03-17 18:21:19 -04:00
imcompiling e4881a74d4 Ignore the database. 2025-03-17 18:21:13 -04:00
imcompiling 892f873320 Create birthday set command. 2025-03-17 18:16:44 -04:00
imcompiling a5c1359fac Add randomized presence with mythic plus theme. 2025-03-17 17:59:51 -04:00
imcompiling bbacaa8bb9 Fix birthday loop.
build_develop / build (push) Failing after 17s
2025-03-17 17:51:28 -04:00
imcompiling eef85b424d Add pytz to project. 2025-03-17 17:51:18 -04:00
imcompiling 3a4ac066a2 Add database code. Starting with birthday table. 2025-03-17 17:40:21 -04:00
imcompiling 7fd3fb8458 Fix some smells. 2025-03-17 17:40:07 -04:00
imcompiling dae0d205e5 Add the cakeday loop and some of birthday functionality. 2025-03-17 17:29:47 -04:00
28 changed files with 421 additions and 112 deletions
+4
View File
@@ -0,0 +1,4 @@
.*
Dockerfile
renovate.json
README.md
+16
View File
@@ -0,0 +1,16 @@
# see https://editorconfig.org/
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
+2 -2
View File
@@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Docker - name: Install Docker
uses: https://github.com/papodaca/install-docker-action@v1 uses: https://github.com/papodaca/install-docker-action@v1
- name: Log into container registry - name: Log into container registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: gitea.cheezers.org registry: gitea.cheezers.org
username: imcompiling username: imcompiling
+3 -3
View File
@@ -11,17 +11,17 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Docker - name: Install Docker
uses: https://github.com/papodaca/install-docker-action@v1 uses: https://github.com/papodaca/install-docker-action@v1
- name: Log into container registry - name: Log into container registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: gitea.cheezers.org registry: gitea.cheezers.org
username: imcompiling username: imcompiling
password: $${{ secrets.PASSWORD }} password: ${{ secrets.PASSWORD }}
- name: Build Docker Image - name: Build Docker Image
run: docker build -t gitea.cheezers.org/imcompiling/off-cooldown-bot:latest . run: docker build -t gitea.cheezers.org/imcompiling/off-cooldown-bot:latest .
- name: Push Docker Image - name: Push Docker Image
+1 -3
View File
@@ -23,8 +23,6 @@ jobs:
- name: Set env - name: Set env
run: echo "GIT_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: echo "GIT_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Build Docker Images - name: Build Docker Images
run: docker build -t gitea.cheezers.org/imcompiling/off-cooldown-bot:latest -t gitea.cheezers.org/imcompiling/off-cooldown-bot:${{env.GIT_TAG}} . run: docker build -t gitea.cheezers.org/imcompiling/off-cooldown-bot:${{env.GIT_TAG}} .
- name: Push Latest Image
run: docker push gitea.cheezers.org/imcompiling/off-cooldown-bot:latest
- name: Push Tagged Image - name: Push Tagged Image
run: docker push gitea.cheezers.org/imcompiling/off-cooldown-bot:${{env.GIT_TAG}} run: docker push gitea.cheezers.org/imcompiling/off-cooldown-bot:${{env.GIT_TAG}}
+2
View File
@@ -128,3 +128,5 @@ dmypy.json
# logging # logging
nextcord.log nextcord.log
outputs/ outputs/
/database
+5 -4
View File
@@ -1,8 +1,9 @@
{ {
"recommendations": [ "recommendations": [
"ms-python.vscode-pylance", "ms-python.vscode-pylance",
"ms-python.python", "ms-python.python",
"ms-python.debugpy", "ms-python.debugpy",
"charliermarsh.ruff" "charliermarsh.ruff",
"editorconfig.editorconfig"
] ]
} }
+13 -13
View File
@@ -1,15 +1,15 @@
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Python: Module", "name": "Python: Module",
"type": "debugpy", "type": "debugpy",
"request": "launch", "request": "launch",
"module": "main", "module": "off_cooldown_bot",
"justMyCode": true "justMyCode": true,
} }
] ]
} }
+2 -1
View File
@@ -3,5 +3,6 @@
"reportOptionalMemberAccess": "warning", "reportOptionalMemberAccess": "warning",
"reportArgumentType": "information", "reportArgumentType": "information",
"reportAttributeAccessIssue": "warning" "reportAttributeAccessIssue": "warning"
} },
"cSpell.words": ["cakeday", "cakedays", "cooldown", "nextcord"]
} }
+6 -10
View File
@@ -1,16 +1,12 @@
FROM python:3.12.3-slim FROM python:3.13.3-slim
# install uv # install uv
COPY --from=ghcr.io/astral-sh/uv:0.6.5 /uv /uvx /bin/ COPY --from=ghcr.io/astral-sh/uv:0.6.17 /uv /uvx /bin/
COPY . /home
# Sync the project into a new environment, using the frozen lockfile
WORKDIR /home WORKDIR /home
COPY config.py config.py
COPY main.py main.py
COPY pyproject.toml pyproject.toml
COPY cogs cogs/
RUN uv sync --frozen --no-dev RUN uv sync --frozen --no-dev
CMD ["uv", "run", "./main.py"] CMD ["uv", "run", "--frozen", "--no-dev", "-m", "off_cooldown_bot"]
-22
View File
@@ -1,22 +0,0 @@
"""Example command file."""
import nextcord
from nextcord.ext import commands
class HiCog(commands.Cog, name="Test commands."):
"""Class holds commands for testing."""
def __init__(self, bot: nextcord.Bot) -> None:
self.bot = bot
@nextcord.slash_command()
async def hi(self, interaction: nextcord.Interaction) -> None:
"""
Says hi.
Args:
interaction (nextcord.Interaction): Interaction to reply to.
"""
await interaction.response.send_message("Hi!", ephemeral=True)
-22
View File
@@ -1,22 +0,0 @@
"""
File contains classes defining the Hydra configuraton structure.
"""
from dataclasses import dataclass
@dataclass
class Params:
"""
Class defines the various configuration parameters.
"""
discord_token: str
@dataclass
class BotConfig:
"""
Class defines all configs required for Dakiny Bot.
"""
params: Params
+1
View File
@@ -0,0 +1 @@
"""Empty init file for off_cooldown_bot module."""
+14
View File
@@ -0,0 +1,14 @@
"""Starts the app for off_cooldown_bot."""
import logging
from .app import run
nextcord_logger = logging.getLogger("nextcord")
nextcord_logger.setLevel(logging.DEBUG)
nextcord_handler = logging.FileHandler(filename="nextcord.log", encoding="utf-8", mode="w")
nextcord_handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s"))
nextcord_logger.addHandler(nextcord_handler)
if __name__ == "__main__":
run()
+13 -19
View File
@@ -1,21 +1,19 @@
"""Main file for bot.""" """Main file for bot."""
import logging from __future__ import annotations
import os
import pathlib
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from settings import settings from .logger import ocd_logger
from .settings import settings
nextcord_logger = logging.getLogger("nextcord") birthday_list: list[int] = []
nextcord_logger.setLevel(logging.DEBUG)
nextcord_handler = logging.FileHandler(filename="nextcord.log", encoding="utf-8", mode="w")
nextcord_handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s"))
nextcord_logger.addHandler(nextcord_handler)
def main() -> None: def run() -> None:
"""Set config, intents, then connects the bot to discord.""" """Set config, intents, then connects the bot to discord."""
# Allows privileged intents for monitoring members joining, roles editing, and role assignments # Allows privileged intents for monitoring members joining, roles editing, and role assignments
# These need to be enabled in the developer portal as well # These need to be enabled in the developer portal as well
@@ -25,28 +23,24 @@ def main() -> None:
intents.message_content = True intents.message_content = True
# To enable the guilds privileged intent: # To enable the guilds privileged intent:
# intents.guilds = True # intents.guilds = True # noqa: ERA001
# To enable the members privileged intent: # To enable the members privileged intent:
# intents.members = True intents.members = True
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 os.listdir("cogs"): for folder in pathlib.Path("./off_cooldown_bot/cogs").iterdir():
bot.load_extension(f"cogs.{folder}") bot.load_extension(f"off_cooldown_bot.cogs.{folder.name}")
@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: if bot.user is None:
print("Discord connection failed.") ocd_logger.critical("Discord connection failed.")
return return
print(f"{bot.user.name} has connected to Discord!") ocd_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)
if __name__ == "__main__":
main()
@@ -2,11 +2,11 @@
from nextcord.ext import commands from nextcord.ext import commands
from cogs.commands.hi import HiCog from .birthday import BirthdayCog
# This function will be called when this extension is loaded. # This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot. # It is necessary to add these functions to the bot.
def setup(bot: commands.Bot) -> None: def setup(bot: commands.Bot) -> None:
"""Add all command classes to bot.""" """Add all command classes to bot."""
bot.add_cog(HiCog(bot)) bot.add_cog(BirthdayCog())
@@ -0,0 +1,94 @@
"""Contains commands for birthday feature."""
from datetime import datetime
import nextcord
from nextcord.ext import commands
from pytz import UTC
from sqlmodel import Session, col, select
from off_cooldown_bot.app import birthday_list
from off_cooldown_bot.database.db import engine
from off_cooldown_bot.database.models import UserBirthday
class BirthdayCog(commands.Cog, name="Birthday Commands"):
"""Class holds birthday commands."""
@nextcord.slash_command(guild_ids=[1000247361639030804, 822199658306076672])
async def birthday(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for birthday. Never called."""
@birthday.subcommand(description="Set your birthday month and day for birthday fun!")
async def set(
self,
interaction: nextcord.Interaction,
month: int = nextcord.SlashOption(required=True, min_value=1, max_value=12, description="Your birth month."),
day: int = nextcord.SlashOption(required=True, min_value=1, max_value=31, description="Your birth day."),
) -> None:
"""
Set a user's birthday in the database. If birthday is today, adds to the birthday_list.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command.
month (int, optional): The month a user was born. Is SlashOption.
day (int, optional): The day a user was born. Is SlashOption.
"""
# Ensure that a person used the command.
if interaction.user is None:
return
await interaction.response.defer(ephemeral=True)
# check if valid day
today = datetime.now(tz=UTC)
try:
birthday = datetime(today.year, month, day, tzinfo=UTC)
except ValueError:
await interaction.followup.send(f"Invalid date: {month}/{day}.", ephemeral=True)
return
# If the birthday is today, add to birthday_list
if today.date() == birthday.date():
birthday_list.append(interaction.user.id)
new_birthday = UserBirthday(user_id=interaction.user.id, month=month, day=day)
with Session(engine) as session:
stmt = select(UserBirthday).where(UserBirthday.user_id == interaction.user.id)
if (new_birthday := session.scalar(stmt)) is None:
new_birthday = UserBirthday(user_id=interaction.user.id, month=month, day=day)
session.add(new_birthday)
else:
new_birthday.month = month
new_birthday.day = day
session.commit()
await interaction.followup.send(f"Birthday set to {month}/{day}.", ephemeral=True)
@birthday.subcommand(description="Remove yourself from the birthday list.")
async def clear(self, interaction: nextcord.Interaction) -> None:
"""
Remove the user from the birthday database.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command.
"""
# Ensure that a person used the command.
if interaction.user is None:
return
if interaction.user.id in birthday_list:
birthday_list.remove(interaction.user.id)
with Session(engine) as session:
stmt = select(UserBirthday).where(col(UserBirthday.user_id) == interaction.user.id)
if (user := session.scalar(stmt)) is None:
await interaction.response.send_message("No birthday to clear.", ephemeral=True)
return
session.delete(user)
session.commit()
await interaction.response.send_message("Birthday cleared.", ephemeral=True)
+12
View File
@@ -0,0 +1,12 @@
"""Initializing code for all loops."""
from nextcord.ext import commands
from .bot_presence import BotPresence
from .daily_loop import DailyLoop
def setup(bot: commands.Bot) -> None:
"""Add functions to bot."""
bot.add_cog(DailyLoop(bot))
bot.add_cog(BotPresence(bot))
@@ -0,0 +1,44 @@
"""Functions for controlling the bot's presence. Also contains the loop."""
import random
import nextcord
from nextcord.ext import commands, tasks
class BotPresence(commands.Cog, name="Bot Presence"):
"""Class for bot presence functions."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@commands.Cog.listener()
async def on_ready(self) -> None:
"""Start the loop when bot is ready."""
await self.bot.wait_until_ready()
self.periodic_change_presence.start()
@tasks.loop(minutes=30)
async def periodic_change_presence(self) -> None:
"""Loops every 30 minutes and changes presence."""
await self.change_presence()
async def change_presence(self) -> None:
"""Randomly chooses a presence and updates bot's presence."""
dungeons = [
"Cinderbrew Meadery",
"Darkflame Cleft",
"The Rookery",
"Priory of the Sacred Flame",
"Operation: Floodgate",
"The MOTHERLODE!!",
"Theater of Pain",
"Operation: Mechagon - Workshop",
]
dungeon = random.choice(dungeons) # noqa: S311
plus = random.randint(2, 12) # noqa: S311
presence = f"Running +{plus} {dungeon}"
await self.bot.change_presence(activity=nextcord.CustomActivity(presence))
+81
View File
@@ -0,0 +1,81 @@
"""Defines the daily loop that runs the Off Cooldown bot functions."""
from datetime import UTC, datetime, time
import nextcord
from nextcord.ext import commands, tasks
from pytz import timezone
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlmodel import col
from off_cooldown_bot.app import birthday_list
from off_cooldown_bot.database.db import engine
from off_cooldown_bot.database.models import UserBirthday
from off_cooldown_bot.logger import ocd_logger
from off_cooldown_bot.settings import settings
class DailyLoop(commands.Cog, name="Daily Loop"):
"""Class for performing daily functions."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.current_election = None
self.update_birthdays()
self.daily_loop.start()
@tasks.loop(time=time(hour=0, tzinfo=timezone("UTC")))
async def daily_loop(self) -> None:
"""Run daily repeated functions."""
self.update_birthdays()
await self.cakedays()
@daily_loop.before_loop
async def before_loop(self) -> None:
"""Actions run before the loop starts. Want to wait for bot to be ready and get the current cycle."""
await self.bot.wait_until_ready()
def update_birthdays(self) -> None:
"""Goes through database to collect all birthdays that are today. Adds user's id to birthday list."""
birthday_list.clear()
today = datetime.now(tz=UTC).date()
stmt = (
select(col(UserBirthday.user_id))
.where(col(UserBirthday.month) == today.month)
.where(col(UserBirthday.day) == today.day)
)
with Session(engine) as session:
to_add = session.scalars(stmt)
birthday_list.extend(to_add.all())
async def cakedays(self) -> None:
"""
Loops through the guild's humans checking for joined_at dates that match today's date.
Sends a message saying happy cakeday.
"""
today = datetime.now(tz=UTC).date()
if (guild := self.bot.get_guild(settings.params.guild_id)) is None:
ocd_logger.warning("Guild not found in bot.")
return
if (announce_channel := guild.get_channel(settings.channel_ids.general)) is None or not isinstance(
announce_channel, nextcord.TextChannel
):
ocd_logger.warning("Set general channel isn't found or isn't a text channel.")
return
cakedays: list[nextcord.Member] = [
human
for human in guild.humans
if human.joined_at is not None and human.joined_at.month == today.month and human.joined_at.day == today.day
]
for member in cakedays:
years = today.year - member.joined_at.year # type: ignore reportOptionalMemberAccess
year_str = "years" if years > 1 else "year"
msg = f"Happy Cake day! <@{member.id}> joined the discord {years} {year_str} ago!"
await announce_channel.send(msg)
@@ -0,0 +1,41 @@
"""Defines cog for on message listener."""
import nextcord
from nextcord.ext import commands
from off_cooldown_bot.app import birthday_list
class SubmissionListener(commands.Cog, name="Message Listener"):
"""Sets up message listener."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message: nextcord.Message) -> None:
"""Listen 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: # type: ignore reportOptionalMemberAccess
return
await birthday_action(message)
# This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot.
def setup(bot: commands.Bot) -> None:
"""Add function to bot."""
bot.add_cog(SubmissionListener(bot))
async def birthday_action(message: nextcord.Message) -> None:
"""
Check 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.
"""
if message.author.id in birthday_list or bool(set(message.raw_mentions) & set(birthday_list)):
await message.add_reaction("🎂")
+1
View File
@@ -0,0 +1 @@
"""Package for database interaction."""
+10
View File
@@ -0,0 +1,10 @@
"""Simple file to set up the sqlmodel connection to database."""
from sqlmodel import SQLModel, create_engine
from . import models # noqa: F401 - Need this import for creating the models.
url = "sqlite:///database/database.db"
engine = create_engine(url)
SQLModel.metadata.create_all(engine)
+20
View File
@@ -0,0 +1,20 @@
"""File holds all model definitions of SQL tables."""
from sqlalchemy.dialects.mysql import BIGINT, SMALLINT
from sqlmodel import Column, Field, SQLModel
class UserBirthday(SQLModel, table=True):
"""
Represents a user's birthday.
Attributes:
user_id: The user's discord id. Primary key.
month: The month the user was born.
day: The day the user was born.
"""
user_id: int = Field(sa_column=Column(BIGINT(unsigned=True), primary_key=True, nullable=False))
month: int = Field(sa_column=Column(SMALLINT(unsigned=True), nullable=False))
day: int = Field(sa_column=Column(SMALLINT(unsigned=True), nullable=False))
+10
View File
@@ -0,0 +1,10 @@
"""Creates logger for Off Cooldown bot."""
import logging
import sys
ocd_logger = logging.getLogger("default")
ocd_logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s"))
ocd_logger.addHandler(handler)
@@ -7,17 +7,26 @@ from dataclass_wizard import YAMLWizard
CONFIG_LOCATION = "conf/config.yaml" CONFIG_LOCATION = "conf/config.yaml"
@dataclass
class ChannelIds:
"""Class holds all channel ids needed by bot."""
general: int
@dataclass @dataclass
class Params: class Params:
"""Class defines start parameters that shouldn't be changed.""" """Class defines start parameters that shouldn't be changed."""
discord_token: str discord_token: str
guild_id: int
@dataclass @dataclass
class Settings(YAMLWizard): class Settings(YAMLWizard):
"""Class defines all configs required for off-cooldown-bot.""" """Class defines all configs required for off-cooldown-bot."""
channel_ids: ChannelIds
params: Params params: Params
def save(self) -> None: def save(self) -> None:
@@ -26,3 +35,7 @@ class Settings(YAMLWizard):
settings = Settings.from_yaml_file(CONFIG_LOCATION) settings = Settings.from_yaml_file(CONFIG_LOCATION)
# I'm not expecting a list to be returned.
if isinstance(settings, list):
settings = settings[0]
+1 -1
View File
@@ -6,9 +6,9 @@ authors = [{ name = "imcompiling", email = "[email protected]" }]
readme = "README.md" readme = "README.md"
dependencies = [ dependencies = [
"nextcord>=2.6.0", "nextcord>=2.6.0",
"python-dotenv>=1.0.1",
"sqlmodel>=0.0.22", "sqlmodel>=0.0.22",
"dataclass-wizard[yaml]>=0.22.3,<1.0.0", "dataclass-wizard[yaml]>=0.22.3,<1.0.0",
"pytz>=2025.1",
] ]
requires-python = ">=3.12" requires-python = ">=3.12"
Generated
+6 -6
View File
@@ -306,7 +306,7 @@ source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "dataclass-wizard", extra = ["yaml"] }, { name = "dataclass-wizard", extra = ["yaml"] },
{ name = "nextcord" }, { name = "nextcord" },
{ name = "python-dotenv" }, { name = "pytz" },
{ name = "sqlmodel" }, { name = "sqlmodel" },
] ]
@@ -319,7 +319,7 @@ dev = [
requires-dist = [ requires-dist = [
{ name = "dataclass-wizard", extras = ["yaml"], specifier = ">=0.22.3,<1.0.0" }, { name = "dataclass-wizard", extras = ["yaml"], specifier = ">=0.22.3,<1.0.0" },
{ name = "nextcord", specifier = ">=2.6.0" }, { name = "nextcord", specifier = ">=2.6.0" },
{ name = "python-dotenv", specifier = ">=1.0.1" }, { name = "pytz", specifier = ">=2025.1" },
{ name = "sqlmodel", specifier = ">=0.0.22" }, { name = "sqlmodel", specifier = ">=0.0.22" },
] ]
@@ -437,12 +437,12 @@ wheels = [
] ]
[[package]] [[package]]
name = "python-dotenv" name = "pytz"
version = "1.0.1" version = "2025.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 },
] ]
[[package]] [[package]]