Start working on files in cogs.
build_develop / build (push) Successful in 1m24s

This commit is contained in:
2024-05-22 17:35:43 -04:00
parent d9914529ca
commit 6500f68d86
8 changed files with 95 additions and 109 deletions
+24 -21
View File
@@ -1,26 +1,30 @@
"""
File contains logic for creating a webhook listener, pulling from git, and updating all messages.
"""
"""File contains logic for creating a webhook listener, pulling from git, and updating all messages."""
import os
from pathlib import Path
from typing import TYPE_CHECKING
import nextcord
import uvicorn
from fastapi import FastAPI
from git import Repo
from nextcord.ext import commands
from classes.gitea_webhook import GiteaWebhook
from config import DakinyConfig
from utils import REPO_DIR, edit_clan_council_info, update_clan_messages
if TYPE_CHECKING:
import nextcord
from config import DakinyConfig
app = FastAPI()
REPO_URL = "https://gitea.cheezers.org/imcompiling/dakiny-messages.git"
class ClanMessages(commands.Cog, name="Clan Messages"):
"""
Cog to be loaded into the bot. Used to be able to have a fastapi post listener and access to the bot at
Cog to be loaded into the bot.
Used to be able to have a fastapi post listener and access to the bot at
the same time.
"""
@@ -30,23 +34,24 @@ class ClanMessages(commands.Cog, name="Clan Messages"):
self.repo_setup()
@commands.Cog.listener()
async def on_ready(self):
"""
Triggers when the bot is in ready state. Adds the webhook listener to FastAPI and serves.
"""
async def on_ready(self) -> None:
"""Triggers when the bot is in ready state. Adds the webhook listener to FastAPI and serves."""
app.add_api_route("/messages/", self.update_messages, methods=["POST"])
config = uvicorn.Config(app=app, host="0.0.0.0", port=5000, log_level="info")
# Bind to all interfaces, don't know the docker interface ahead of time.
config = uvicorn.Config(app=app, host="0.0.0.0", port=5000, log_level="info") # noqa: S104
server = uvicorn.Server(config)
await server.serve()
async def update_messages(self, hook_info: GiteaWebhook):
async def update_messages(self, hook_info: GiteaWebhook) -> dict:
"""
Triggered by webhook.
Pulls from git and then calls method to update all the discord messages as described by the
files in the git repo.
"""
# Get all modified/added files
modified_files: list[str] = [
file for list in [commit.modified + commit.added for commit in hook_info.commits] for file in list
file for files in [commit.modified + commit.added for commit in hook_info.commits] for file in files
]
template_updates = {file.split("/")[1] for file in modified_files if "message_templates" in file}
@@ -63,12 +68,10 @@ class ClanMessages(commands.Cog, name="Clan Messages"):
return {"message": "Successfully updated all discord messages."}
def repo_setup(self):
"""
Checks if the repo exists and creates repo object, clones if it does not.
"""
def repo_setup(self) -> None:
"""Checks if the repo exists and creates repo object, clones if it does not."""
# check if repo has been cloned. open repo object
if os.path.isdir(REPO_DIR):
if Path(REPO_DIR).is_dir():
self.repo = Repo(REPO_DIR)
else:
self.repo = Repo.clone_from(REPO_URL, REPO_DIR)
@@ -85,6 +88,6 @@ class ClanMessages(commands.Cog, name="Clan Messages"):
# This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot.
def setup(bot: commands.Bot):
"""Adds function to bot"""
def setup(bot: commands.Bot) -> None:
"""Adds function to bot."""
bot.add_cog(ClanMessages(bot))
+3 -7
View File
@@ -1,6 +1,4 @@
"""
Cog loader. Adds all command cogs to the bot.
"""
"""Cog loader. Adds all command cogs to the bot."""
from nextcord.ext import commands
@@ -15,10 +13,8 @@ from cogs.commands.view import ViewCog
# This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot.
def setup(bot: commands.Bot):
"""
Adds all command classes to bot.
"""
def setup(bot: commands.Bot) -> None:
"""Adds all command classes to bot."""
bot.add_cog(RecordCog(bot))
bot.add_cog(RefreshCog(bot))
bot.add_cog(RemoveCog())
+25 -33
View File
@@ -1,6 +1,4 @@
"""
Contained commands and views for record commands.
"""
"""Contained commands and views for record commands."""
import nextcord
from discord import TextInputStyle
@@ -11,22 +9,16 @@ from utils import record_event_points
class RecordCog(commands.Cog, name="Record Commands"):
"""
Class holds record commands.
"""
"""Class holds record commands."""
def __init__(self, bot) -> None:
"""
Default constructor for RecordCog class. Does nothing.
"""
def __init__(self, bot: commands.Bot) -> None:
"""Default constructor for RecordCog class. Does nothing."""
super().__init__()
self.bot: commands.Bot = bot
@nextcord.slash_command()
async def record(self, interaction: nextcord.Interaction):
"""
Parent slash command for record. Never called.
"""
async def record(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for record. Never called."""
@record.subcommand(description="Record an event and its participants.")
async def event(
@@ -37,8 +29,10 @@ class RecordCog(commands.Cog, name="Record Commands"):
required=False,
description="An alternative host if you are recording event for someone else.",
),
):
) -> None:
"""
Create and send recording event.
This command creates the base event recording embed and sends a message with it and the EventView
for gathering more information on the event.
@@ -73,7 +67,9 @@ class RecordCog(commands.Cog, name="Record Commands"):
class EventView(nextcord.ui.View):
"""
Class handles gathering information on an event. Has buttons to edit the information, cancel, and submit.
Class handles gathering information on an event.
Has buttons to edit the information, cancel, and submit.
Also contains a UserSelect object to pick discord members.
"""
@@ -89,33 +85,31 @@ class EventView(nextcord.ui.View):
self.add_item(self.event_participants)
@nextcord.ui.button(label="Cancel", style=nextcord.ButtonStyle.red, row=1)
async def cancel(self, button: nextcord.ui.Button, interaction: nextcord.Interaction):
"""
Cancels event submission. Removes embed and view.
"""
async def cancel(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""Cancels event submission. Removes embed and view."""
await interaction.response.edit_message("Canceled", ephemeral=True, embed=None, view=None)
self.stop()
@nextcord.ui.button(label="Edit", style=nextcord.ButtonStyle.blurple, row=1)
async def edit(self, button: nextcord.ui.Button, interaction: nextcord.Interaction):
"""
Calls modal to update the text input information of the event.
"""
async def edit(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""Calls modal to update the text input information of the event."""
await interaction.response.send_modal(ParticipantModal(interaction.message.embeds[0]))
@nextcord.ui.button(label="Confirm", style=nextcord.ButtonStyle.green, row=1)
async def confirm(self, button: nextcord.ui.Button, interaction: nextcord.Interaction):
async def confirm(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""
Submits the event embed to the event channel. Removes view and embed from recording message.
Submits the event embed to the event channel.
Removes view and embed from recording message.
Sends point information to database.
"""
discord_users = self.event_participants.values.members
discord_users = self.event_participants.values.members # noqa: PD011 not a pandas object
if discord_users:
discord_users_list: list[str] = [str(user.id) for user in discord_users if user.id is not self.host_id]
record_event_points(
self.config,
discord_users_list,
True,
True, # noqa: FBT003
self.config.params.event_participate_points,
)
@@ -175,7 +169,7 @@ class ParticipantModal(nextcord.ui.Modal):
self.add_item(self.description)
self.add_item(self.participants)
async def callback(self, interaction: nextcord.Interaction):
async def callback(self, interaction: nextcord.Interaction) -> None:
self.embed.title = self.name.value
self.embed.description = self.description.value
self.embed.set_field_at(index=1, name="Non-Discord Participants", value=self.participants.value)
@@ -184,9 +178,7 @@ class ParticipantModal(nextcord.ui.Modal):
class ParticipantSelect(nextcord.ui.UserSelect):
"""
A selection drop down for discord members. Updates the embed with all users selected.
"""
"""A selection drop down for discord members. Updates the embed with all users selected."""
def __init__(self) -> None:
super().__init__(
@@ -196,7 +188,7 @@ class ParticipantSelect(nextcord.ui.UserSelect):
row=0,
)
async def callback(self, interaction: nextcord.Interaction):
async def callback(self, interaction: nextcord.Interaction) -> None:
embed: nextcord.Embed = interaction.message.embeds[0]
participants: str = "\n".join([f"<@{participant.id}>" for participant in self.values.members])
embed.set_field_at(0, name="Participants", value=participants)
+3 -7
View File
@@ -1,6 +1,4 @@
"""
Initializing code for all loops.
"""
"""Initializing code for all loops."""
from nextcord.ext import commands
@@ -8,9 +6,7 @@ from cogs.loops.bot_presence import BotPresence
from cogs.loops.daily_loop import DailyLoop
def setup(bot: commands.Bot):
"""
Adds functions to bot.
"""
def setup(bot: commands.Bot) -> None:
"""Adds functions to bot."""
bot.add_cog(BotPresence(bot))
bot.add_cog(DailyLoop(bot))
+6 -9
View File
@@ -1,6 +1,4 @@
"""
Functions for controlling the bot's presence. Also contains the loop.
"""
"""Functions for controlling the bot's presence. Also contains the loop."""
import nextcord
from nextcord.ext import commands, tasks
@@ -17,23 +15,22 @@ class BotPresence(commands.Cog, name="Bot Presence"):
self.bot = bot
@commands.Cog.listener()
async def on_guild_join(self, guild: nextcord.Guild): # pylint: disable=unused-argument
async def on_guild_join(self, guild: nextcord.Guild) -> None: # noqa: ARG002
"""Listener for Guild Join. Updated presence on guild join."""
await self.change_presence()
@commands.Cog.listener()
async def on_ready(self):
async def on_ready(self) -> None:
"""Starts the loop when bot is ready."""
await self.bot.wait_until_ready()
# pylint doesn't recognize that periodic_change_presense is subclassed from tasks.loop.
self.periodic_change_presence.start() # pylint: disable=no-member
self.periodic_change_presence.start()
@tasks.loop(minutes=5)
async def periodic_change_presence(self):
async def periodic_change_presence(self) -> None:
"""Loops every 5 minutes and changes presence."""
await self.change_presence()
async def change_presence(self):
async def change_presence(self) -> None:
"""Randomly chooses a presence and updates bot's presence."""
with Session(engine) as session:
presence = session.scalar(select(Presences.Presence).order_by(func.random()))
+27 -25
View File
@@ -1,27 +1,29 @@
"""
Defines the daily loop that runs the Dakiny bot functions.
"""
"""Defines the daily loop that runs the Dakiny bot functions."""
import random
from datetime import UTC, datetime, time
from typing import TYPE_CHECKING
import nextcord
from nextcord.ext import commands, tasks
from pytz import timezone
from sqlalchemy import select
from sqlalchemy.orm import Session
from classes.election_cycle import ElectionCycle
from config import DakinyConfig
from database_setup.sql_data_classes import UserBirthday, engine
from main import birthday_list
from utils import get_current_election_cycle
if TYPE_CHECKING:
import nextcord
from config import DakinyConfig
KANDY = 239547993106284544
class DailyLoop(commands.Cog, name="Daily Loop"):
"""
Class for performing daily functions.
"""
"""Class for performing daily functions."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@@ -31,10 +33,12 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
# pylint doesn't recognize that daily_loop is subclassed from tasks.loop.
self.daily_loop.start() # pylint: disable=no-member
@tasks.loop(time=time(hour=0, tzinfo=timezone("GMT")))
async def daily_loop(self):
@tasks.loop(time=time(hour=0, tzinfo=timezone("UTC")))
async def daily_loop(self) -> None:
"""
Runs daily. Checks if the current election is None. If so, checks if there is an active election. If there is
Runs daily.
Checks if the current election is None. If so, checks if there is an active election. If there is
an active election, runs the check_date() function for the election to perform actions. Finally checks if the
election is over to delete the current election object.
"""
@@ -48,18 +52,16 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
self.update_birthdays()
if 239547993106284544 in birthday_list:
if KANDY in birthday_list:
# inform kandy that it is her "birthday"
kandy: nextcord.User = await self.bot.fetch_user(239547993106284544)
kandy: nextcord.User = await self.bot.fetch_user(KANDY)
await kandy.send('Happy "Birthday"!')
await self.cakedays()
@daily_loop.before_loop
async def before_loop(self):
"""
Actions run before the loop starts. Want to wait for bot to be ready and get the current cycle.
"""
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()
# Bot needs to know guilds to get the current cycle.
@@ -67,8 +69,9 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
def get_election_cycle(self) -> ElectionCycle:
"""
Creates an object for the current election cycle if one is active. An election is considered active
between the election start and the term start.
Creates an object for the current election cycle if one is active.
An election is considered active between the election start and the term start.
Returns:
ElectionCycle: Current election cycle, None if there is no election happening.
@@ -83,13 +86,11 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
return ElectionCycle(cycle, config, self.bot.get_guild(config.params.guild_id))
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."""
birthday_list.clear()
# kandy's 1% chance
if random.randint(0, 99) == 0:
if random.randint(0, 99) == 0: # noqa: S311 isn't cryptography related.
birthday_list.append(239547993106284544)
today = datetime.now(tz=UTC).date()
@@ -102,8 +103,9 @@ class DailyLoop(commands.Cog, name="Daily Loop"):
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.
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()
guild: nextcord.Guild = self.bot.get_guild(self.bot.config.params.guild_id)
+4 -6
View File
@@ -1,6 +1,4 @@
"""
Defines cog for on message listener.
"""
"""Defines cog for on message listener."""
import nextcord
from nextcord.ext import commands
@@ -15,7 +13,7 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message: nextcord.Message):
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:
@@ -26,8 +24,8 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
# This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot.
def setup(bot: commands.Bot):
"""Adds function to bot"""
def setup(bot: commands.Bot) -> None:
"""Adds function to bot."""
bot.add_cog(SubmissionListener(bot))
+3 -1
View File
@@ -51,6 +51,7 @@ exclude = [
"node_modules",
"site-packages",
"venv",
"dakiny-messages", # Don't need to lint this git repo.
]
# Same as Black.
@@ -67,9 +68,10 @@ target-version = "py311"
select = ["ALL"]
ignore = [
"ANN101",
"B008", # I build SlashOptions as arguments.
"COM812",
"COM819",
"D107", # don't want docstrings on init functions
"D107", # don't want docstrings on init functions
"D203",
"D206",
"D212",