feat(shutdown): Improve bot shutdown handling and logging

Added graceful shutdown support via signals in bot.py, improved shutdown command to use SIGTERM, and enhanced logging for shutdown events. Updated UDID command description for clarity.
This commit is contained in:
neoarz
2025-09-21 08:47:15 -04:00
parent 2e818d3453
commit f28ae00d7d
4 changed files with 65 additions and 3 deletions

1
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1 @@
print("Hello World")

49
bot.py
View File

@@ -1,8 +1,10 @@
import asyncio
import json
import logging
import os
import platform
import random
import signal
import sys
import time
@@ -113,6 +115,7 @@ class DiscordBot(commands.Bot):
self.bot_prefix = os.getenv("PREFIX")
self.invite_link = os.getenv("INVITE_LINK")
self.start_time = time.time()
self._shutdown = False
async def init_db(self) -> None:
async with aiosqlite.connect(
@@ -229,6 +232,30 @@ class DiscordBot(commands.Bot):
else:
return f"{seconds}s"
async def close(self) -> None:
if self._shutdown:
return
self._shutdown = True
self.logger.info("Starting shutdown process...")
if self.status_task and not self.status_task.is_being_cancelled():
self.status_task.cancel()
self.logger.info("Status task cancelled")
if self.database and self.database.connection:
try:
await self.database.connection.close()
self.logger.warning("Database connection closed")
except Exception as e:
self.logger.error(f"Error closing database connection: {e}")
try:
await super().close()
self.logger.critical("Bot shutdown complete")
except Exception as e:
self.logger.error(f"Error during bot shutdown: {e}")
async def on_message(self, message: discord.Message) -> None:
"""
The code in this event is executed every time someone sends a message, with or without the prefix
@@ -264,6 +291,11 @@ class DiscordBot(commands.Bot):
full_command_name = context.command.qualified_name
split = full_command_name.split(" ")
executed_command = str(split[0])
# Skip logging for shutdown command as it logs manually before shutdown
if executed_command.lower() == "shutdown":
return
if context.guild is not None:
self.logger.info(
f"Executed {executed_command} command in {context.guild.name} (ID: {context.guild.id}) by {context.author} (ID: {context.author.id})"
@@ -336,5 +368,22 @@ class DiscordBot(commands.Bot):
raise error
def signal_handler(signum, frame):
logger.info("Shutdown requested. Closing bot...")
if bot.loop and not bot.loop.is_closed():
asyncio.create_task(bot.close())
bot.loop.call_soon_threadsafe(bot.loop.stop)
bot = DiscordBot()
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
try:
bot.run(os.getenv("TOKEN"))
except KeyboardInterrupt:
pass
except:
pass

View File

@@ -1,3 +1,5 @@
import os
import signal
import discord
from discord.ext import commands
from discord.ext.commands import Context
@@ -28,6 +30,16 @@ class Shutdown(commands.Cog, name="shutdown"):
:param context: The hybrid command context.
"""
# Log command execution before shutdown starts
if context.guild is not None:
self.bot.logger.info(
f"Executed shutdown command in {context.guild.name} (ID: {context.guild.id}) by {context.author} (ID: {context.author.id})"
)
else:
self.bot.logger.info(
f"Executed shutdown command by {context.author} (ID: {context.author.id}) in DMs"
)
embed = discord.Embed(
title="Shutdown",
description="Shutting down. Bye! <a:PandaThanos:1417483671253811262>",
@@ -35,7 +47,7 @@ class Shutdown(commands.Cog, name="shutdown"):
).set_author(name="Owner", icon_url="https://yes.nighty.works/raw/zReOib.webp")
await self.send_embed(context, embed)
await self.bot.close()
os.kill(os.getpid(), signal.SIGTERM)
async def cog_command_error(self, context: Context, error) -> None:
if isinstance(error, commands.NotOwner):

View File

@@ -10,7 +10,7 @@ class Udid(commands.Cog, name="udid"):
self.bot = bot
@commands.hybrid_command(
name="udid", description="Information about SideStore UDID error"
name="udid", description="SideStore could not determine device UDID"
)
async def udid(self, context: Context) -> None:
embed = discord.Embed(