diff --git a/cogs/clan_messages/__init__.py b/cogs/clan_messages/__init__.py index f538ebb..fb8522f 100644 --- a/cogs/clan_messages/__init__.py +++ b/cogs/clan_messages/__init__.py @@ -9,16 +9,12 @@ import uvicorn from fastapi import FastAPI from git import Repo from nextcord.ext import commands -from sqlalchemy import select, update -from sqlalchemy.orm import Session from config import DakinyConfig -from database_setup.sql_data_classes import Messages, engine +from utils import REPO_DIR, update_clan_messages app = FastAPI() REPO_URL = "https://gitea.cheezers.org/imcompiling/dakiny-messages.git" -REPO_DIR = "./dakiny-messages" -MESSAGES_DIR = f"{REPO_DIR}/messages" class ClanMessages(commands.Cog, name="Clan Messages"): @@ -45,112 +41,17 @@ class ClanMessages(commands.Cog, name="Clan Messages"): async def update_messages(self): """ - Pulls from git and then updates all the discord messages as described by the files in the git repo. + Pulls from git and then calls method to update all the discord messages as described by the + files in the git repo. """ self.origin.pull() guild: nextcord.Guild = self.bot.get_guild(self.config.params.guild_id) - # for ever directory, sort the files by number then post - # posting involves checking if there is a message for that particular file then editing it or creating a - # new one. the message could be deleted, which would cause a new message to be sent and database updated. - dirs = [ - dir_name - for dir_name in os.listdir(MESSAGES_DIR) - if os.path.isdir(f"{MESSAGES_DIR}/{dir_name}") - ] - - with Session(engine) as session: - for dir_name in dirs: - dir_path = f"{MESSAGES_DIR}/{dir_name}" - channel = await self.get_clan_message_channel(session, dir_name, guild) - sorted_files = sorted( - os.listdir(dir_path), - key=lambda filename: int(filename.split(".")[0]), - ) - - for file_name in sorted_files: - message_name = f"{dir_name}/{file_name}" - file_path = f"{dir_path}/{file_name}" - with open(file_path, "r", encoding="utf8") as reader: - stmt = select(Messages.Id).where(Messages.Path == message_name) - msg_id = session.scalar(stmt) - if msg_id is None: - # don't have a message for this file - msg = await channel.send(reader.read(2000)) - new_message = Messages(Path=message_name, Id=msg.id) - session.add(new_message) - session.commit() - else: - try: - msg: nextcord.Message = await channel.fetch_message( - msg_id - ) - await msg.edit(reader.read(2000)) - except nextcord.NotFound: - msg = await channel.send(reader.read(2000)) - stmt = ( - update(Messages) - .where(Messages.Path == message_name) - .values(Id=msg.id) - ) - session.execute(stmt) - session.commit() + await update_clan_messages(guild) return {"message": "Successfully updated all discord messages."} - async def get_clan_message_channel( - self, session: Session, channel_name: str, guild: nextcord.Guild - ) -> nextcord.TextChannel: - """ - Used to obtain a channel from the guild object. Searches database for an id to get the channel. If that is not - found, then searches through all channels in the guild to find one with a matching name. If that also doesn't - turn up a channel, then creates it. Adds an entry into database for last two paths. - Assumes that there is an open session to the database. - - Args: - session (Session): Open connection to database. - channel_name (str): Name of channel to obtain. - guild (nextcord.Guild): Guild object to get channel from. - - Returns: - nextcord.TextChannel: Channel object with name channel_name. - """ - stmt = select(Messages.Id).where(Messages.Path == channel_name) - channel_id = session.scalar(stmt) - if channel_id is None: - ret_channel = None - # channel is not tracked. see if it exists - channels = await guild.fetch_channels() - for channel in channels: - if ( - channel.name == channel_name - and channel.__class__ is nextcord.TextChannel - ): - ret_channel = channel - - if ret_channel is None: - # channel doesn't exist, we must create it - ret_channel = await guild.create_text_channel( - channel_name, - reason="Creating channel as required for by design of Dakiny-Messages repo.", - ) - - # record channel information - new_item = Messages(Path=channel_name, Id=ret_channel.id) - session.add(new_item) - session.commit() - return ret_channel - - try: - return await guild.fetch_channel(channel_id) - except nextcord.NotFound: - # the channel was deleted for some reason - return await guild.create_text_channel( - channel_name, - reason="Creating channel as required for by design of Dakiny-Messages repo.", - ) - def repo_setup(self): """ Checks if the repo exists and creates repo object, clones if it does not. diff --git a/utils.py b/utils.py index ed43245..6ee956e 100644 --- a/utils.py +++ b/utils.py @@ -2,21 +2,25 @@ File contains util functions. """ +import os from datetime import datetime import nextcord -from sqlalchemy import select +from sqlalchemy import select, update from sqlalchemy.orm import Session from config import DakinyConfig from database_setup.sql_data_classes import ( CouncilMembers, + Messages, ParticipationPoints, Votes, engine, ) NOMINEE_SPLITTER = "========================================\n\n" +REPO_DIR = "./dakiny-messages" +MESSAGES_DIR = f"{REPO_DIR}/messages" def get_datetime(day: str) -> datetime: @@ -386,3 +390,113 @@ def record_event_host(config: DakinyConfig, user: int): points.Points += host_point_value session.commit() + + +async def update_clan_messages(guild: nextcord.Guild): + """ + Updates all clan messages to contents in MESSAGES_DIR. + Does not to any git work. Is expected to be called after git is pulled. + + for ever directory, sort the files by number then post + posting involves checking if there is a message for that particular file then editing it or creating a + new one. the message could be deleted, which would cause a new message to be sent and database updated. + + Channel fetching/management is handled by another function. + + Args: + guild (nextcord.Guild): Guild object that all channels and messages belong to. + """ + + dirs = [ + dir_name + for dir_name in os.listdir(MESSAGES_DIR) + if os.path.isdir(f"{MESSAGES_DIR}/{dir_name}") + ] + + with Session(engine) as session: + for dir_name in dirs: + dir_path = f"{MESSAGES_DIR}/{dir_name}" + channel = await get_clan_message_channel(session, dir_name, guild) + sorted_files = sorted( + os.listdir(dir_path), + key=lambda filename: int(filename.split(".")[0]), + ) + + for file_name in sorted_files: + message_name = f"{dir_name}/{file_name}" + file_path = f"{dir_path}/{file_name}" + with open(file_path, "r", encoding="utf8") as reader: + stmt = select(Messages.Id).where(Messages.Path == message_name) + msg_id = session.scalar(stmt) + if msg_id is None: + # don't have a message for this file + msg = await channel.send(reader.read(2000)) + new_message = Messages(Path=message_name, Id=msg.id) + session.add(new_message) + session.commit() + else: + try: + msg: nextcord.Message = await channel.fetch_message(msg_id) + await msg.edit(reader.read(2000)) + except nextcord.NotFound: + msg = await channel.send(reader.read(2000)) + stmt = ( + update(Messages) + .where(Messages.Path == message_name) + .values(Id=msg.id) + ) + session.execute(stmt) + session.commit() + + +async def get_clan_message_channel( + session: Session, channel_name: str, guild: nextcord.Guild +) -> nextcord.TextChannel: + """ + Used to obtain a channel from the guild object. Searches database for an id to get the channel. If that is not + found, then searches through all channels in the guild to find one with a matching name. If that also doesn't + turn up a channel, then creates it. Adds an entry into database for last two paths. + Assumes that there is an open session to the database. + + Args: + session (Session): Open connection to database. + channel_name (str): Name of channel to obtain. + guild (nextcord.Guild): Guild object to get channel from. + + Returns: + nextcord.TextChannel: Channel object with name channel_name. + """ + stmt = select(Messages.Id).where(Messages.Path == channel_name) + channel_id = session.scalar(stmt) + if channel_id is None: + ret_channel = None + # channel is not tracked. see if it exists + channels = await guild.fetch_channels() + for channel in channels: + if ( + channel.name == channel_name + and channel.__class__ is nextcord.TextChannel + ): + ret_channel = channel + + if ret_channel is None: + # channel doesn't exist, we must create it + ret_channel = await guild.create_text_channel( + channel_name, + reason="Creating channel as required for by design of Dakiny-Messages repo.", + ) + + # record channel information + new_item = Messages(Path=channel_name, Id=ret_channel.id) + session.add(new_item) + session.commit() + return ret_channel + + try: + return await guild.fetch_channel(channel_id) + except nextcord.NotFound: + # the channel was deleted for some reason + return await guild.create_text_channel( + channel_name, + reason="Creating channel as required for by design of Dakiny-Messages repo.", + )