diff --git a/src/cogs/about.py b/src/cogs/about.py index d5696c8..c67035e 100644 --- a/src/cogs/about.py +++ b/src/cogs/about.py @@ -2,6 +2,8 @@ import discord from discord import app_commands from discord.ext import commands from cogs.base_cog import Base_Cog +from cogs.maintenance import Maintenance_Command + import logging from utils.portal import Portal @@ -11,6 +13,7 @@ class About_Command(Base_Cog): super().__init__(logging.getLogger("cmds.about")) @app_commands.command(name = "about", description = "Provides general information about the bot") + @app_commands.check(Maintenance_Command.handle_check) async def about(self, ctx: discord.Interaction): portal:Portal = Portal.instance() embed = discord.Embed(title=f"POST IT - `{portal.PROGRAM_VERSION}`", @@ -27,19 +30,4 @@ class About_Command(Base_Cog): await ctx.response.send_message(embed = embed, ephemeral = True) async def setup(bot:commands.Bot): - await bot.add_cog(About_Command(bot)) - -# class AddCog(commands.Cog): -# def __init__(self, bot): -# self.bot = bot - -# @app_commands.command(name="add", description="Füge ein neues Wort zur Liste hinzu") -# async def add(self, interaction: discord.Interaction, new_word: str): -# if new_word.lower() not in self.bot.known_words: -# self.bot.known_words.append(new_word.lower()) -# await interaction.response.send_message(f"Wort '{new_word}' wurde zur Liste hinzugefügt.") -# else: -# await interaction.response.send_message(f"Wort '{new_word}' existiert bereits.") - -# async def setup(bot): -# await bot.add_cog(AddCog(bot)) + await bot.add_cog(About_Command(bot)) \ No newline at end of file diff --git a/src/cogs/debug.py b/src/cogs/debug.py index b7e7378..dc44426 100644 --- a/src/cogs/debug.py +++ b/src/cogs/debug.py @@ -2,6 +2,8 @@ import discord from discord import app_commands from discord.ext import commands from cogs.base_cog import Base_Cog +from cogs.maintenance import Maintenance_Command + import logging from utils.portal import Portal from datetime import datetime @@ -14,6 +16,7 @@ class Debug_Command(Base_Cog): super().__init__(logging.getLogger("cmds.debug")) @app_commands.command(name = "debug", description = "Provides debug informations about the bot, useful for troubleshooting") + @app_commands.check(Maintenance_Command.handle_check) @log_command_execution async def debug(self, ctx:discord.Interaction): portal:Portal = Portal.instance() diff --git a/src/cogs/maintenance.py b/src/cogs/maintenance.py new file mode 100644 index 0000000..019d3cc --- /dev/null +++ b/src/cogs/maintenance.py @@ -0,0 +1,61 @@ +import discord +from discord.ext import commands +from discord import app_commands +from cogs.base_cog import Base_Cog + +import logging + + +class Maintenance_Command(Base_Cog): + def __init__(self, bot): + self.__bot = bot + self.__maintenance_mode_enabled = False + super().__init__(logging.getLogger("cmds.maintenance")) + + def enable_global_maintenance(self): + self.__maintenance_mode_enabled = True + + def disable_gloabal_maintenance(self): + self.__maintenance_mode_enabled = False + + @staticmethod + async def handle_check(ctx: discord.Integration): + """Check function called for every command""" + instance = ctx.client.get_cog("Maintenance_Command") + if instance and instance.__maintenance_mode_enabled: + await instance.startup(ctx) + return False + return True + + async def startup(self, ctx: discord.Interaction): + """Returns an message, explaining that the bot is still starting up""" + embed = discord.Embed( + title = ":construction: Bot is still starting up! :construction:", + description = "While the bot is starting, all commands are locked and unable to be executed", + color = 0xED4337) + await ctx.response.send_message(embed = embed) + + @commands.Cog.listener() + async def on_command_error(self, ctx, error): + if isinstance(error, app_commands.CheckFailure): + # Suppress the traceback and inform the user about maintenance mode + return # Already handled in the `startup` method + + cmds_group = app_commands.Group(name="maintenance", description="Put the bot into maintenance mode and control its alternative behaviour") + @cmds_group.command(name = "enable", description = "Enables the maintenance mode globally") + async def maintenance_enable(self, ctx: discord.Interaction): + if self.__maintenance_mode_enabled: + await ctx.response.send_message("Maintenance mode was allready enabled", ephemeral = True) + else: + await ctx.response.send_message("Maintenance mode is now enabled", ephemeral = True) + self.__maintenance_mode_enabled = True + + async def maintenance_disable(self, ctx: discord.Interaction): + if self.__maintenance_mode_enabled: + await ctx.response.send_message("Maintenance mode is now disabled", ephemeral = True) + else: + await ctx.response.send_message("Maintenance mode was allready disabled", ephemeral = True) + self.__maintenance_mode_enabled = False + +async def setup(bot:commands.Bot): + await bot.add_cog(Maintenance_Command(bot)) \ No newline at end of file diff --git a/src/cogs/post.py b/src/cogs/post.py index b63d4ca..28fddca 100644 --- a/src/cogs/post.py +++ b/src/cogs/post.py @@ -15,6 +15,7 @@ class Post_Command(Base_Cog): super().__init__(logging.getLogger("cmds.post")) @app_commands.command(name = "post", description = "Post an embed in the Current Channel with a link to the content") + @app_commands.check(Maintenance_Command.handle_check) async def post(self, ctx:discord.Interaction, url:str): domain_info = urlparse(url) portal = Portal.instance() diff --git a/src/cogs/reload.py b/src/cogs/reload.py index f138f51..69d1f74 100644 --- a/src/cogs/reload.py +++ b/src/cogs/reload.py @@ -2,6 +2,8 @@ import discord from discord import app_commands from discord.ext import commands from cogs.base_cog import Base_Cog +from cogs.maintenance import Maintenance_Command + import logging from datetime import datetime from utils.datetime_tools import get_elapsed_time_milliseconds @@ -36,6 +38,7 @@ class Reload_Command(Base_Cog): return choices @app_commands.command(name = "reload_all", description = "Reloads all cogs") + @app_commands.check(Maintenance_Command.handle_check) async def reload_all(self, ctx: discord.Interaction): task_start = datetime.now().timestamp() cog_names = list(self.__bot.extensions.keys()) @@ -69,6 +72,7 @@ class Reload_Command(Base_Cog): await ctx.response.send_message(embed = embed, ephemeral = True) @app_commands.command(name = "reload", description = "Reload specific cog") + @app_commands.check(Maintenance_Command.handle_check) @app_commands.autocomplete(cog_name = autocomplete_cog) async def reload(self, ctx: discord.Interaction, cog_name:str): task_start = datetime.now().timestamp() diff --git a/src/main.py b/src/main.py index 333fa65..a82f126 100644 --- a/src/main.py +++ b/src/main.py @@ -32,6 +32,7 @@ from utils.portal import Portal import asyncio from typing import Union from platforms.reddit import Reddit_Adapter +from cogs.maintenance import Maintenance_Command source_path = Path(__file__).resolve() base_path = source_path.parents[1] @@ -51,7 +52,7 @@ class MyBot(commands.Bot): async def setup_hook(self): # Register cogs to handle commands - for cog_name in ["about", "reload", "debug"]: + for cog_name in ["maintenance", "about", "debug", "reload", "post"]: await self.load_extension(f"cogs.{cog_name}") await self.tree.sync() @@ -78,6 +79,8 @@ class MyBot(commands.Bot): async def on_connect(self): """A coroutine to be called to setup the bot, after the bot is logged in but before it has connected to the Websocket""" if not self.__first_on_ready: + instance: Maintenance_Command = self.get_cog("Maintenance_Command") + instance.enable_global_maintenance() startup_logger.info(f"Beginning startup routine ...") routine_begin = datetime.now().timestamp() await self.change_presence(status = discord.Status.dnd, activity = discord.CustomActivity("Executing pre startup routine")) @@ -95,11 +98,10 @@ class MyBot(commands.Bot): portal.reddit_adapter = Reddit_Adapter(platforms_config["REDDIT"]["CLIENT_ID"], platforms_config["REDDIT"]["CLIENT_SECRET"]) startup_logger.info(f"Created reddit adapter after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}") - await asyncio.sleep(15) - await self.change_presence(status = discord.Status.online, activity = None) startup_logger.info(f"Startup routine finished after {get_elapsed_time_milliseconds(datetime.now().timestamp() - routine_begin)}") + instance.disable_gloabal_maintenance() self.__first_on_ready = True else: startup_logger.info("Startup routine allready executed, omitting this execution")