tux.app
¶
TuxApp: Orchestration and lifecycle management for the Tux Discord bot.
Classes:
Name | Description |
---|---|
TuxApp | Orchestrates the startup, shutdown, and environment for the Tux bot. |
Functions:
Name | Description |
---|---|
get_prefix | Resolve the command prefix for a guild or use the default prefix. |
Classes¶
TuxApp()
¶
Orchestrates the startup, shutdown, and environment for the Tux bot.
Initialize the TuxApp with no bot instance yet.
Methods:
Name | Description |
---|---|
run | Run the Tux bot application (entrypoint for CLI). |
setup_sentry | Initialize Sentry for error monitoring and tracing. |
setup_signals | Set up signal handlers for graceful shutdown. |
handle_sigterm | Handle SIGTERM/SIGINT by raising KeyboardInterrupt for graceful shutdown. |
validate_config | Validate that all required configuration is present. |
start | Start the Tux bot, handling setup, errors, and shutdown. |
shutdown | Gracefully shut down the bot and flush Sentry. |
Source code in tux/app.py
Functions¶
run() -> None
¶
setup_sentry() -> None
¶
Initialize Sentry for error monitoring and tracing.
Source code in tux/app.py
def setup_sentry(self) -> None:
"""Initialize Sentry for error monitoring and tracing."""
if not CONFIG.SENTRY_DSN:
logger.warning("No Sentry DSN configured, skipping Sentry setup")
return
logger.info("Setting up Sentry...")
try:
sentry_sdk.init(
dsn=CONFIG.SENTRY_DSN,
release=CONFIG.BOT_VERSION,
environment=get_current_env(),
enable_tracing=True,
attach_stacktrace=True,
send_default_pii=False,
traces_sample_rate=1.0,
profiles_sample_rate=1.0,
)
logger.info(f"Sentry initialized: {sentry_sdk.is_initialized()}")
except Exception as e:
logger.error(f"Failed to initialize Sentry: {e}")
setup_signals() -> None
¶
handle_sigterm(signum: int, frame: FrameType | None) -> None
¶
Handle SIGTERM/SIGINT by raising KeyboardInterrupt for graceful shutdown.
Source code in tux/app.py
def handle_sigterm(self, signum: int, frame: FrameType | None) -> None:
"""Handle SIGTERM/SIGINT by raising KeyboardInterrupt for graceful shutdown."""
logger.info(f"Received signal {signum}")
if sentry_sdk.is_initialized():
with sentry_sdk.push_scope() as scope:
scope.set_tag("signal.number", signum)
scope.set_tag("lifecycle.event", "termination_signal")
sentry_sdk.add_breadcrumb(
category="lifecycle",
message=f"Received termination signal {signum}",
level="info",
)
raise KeyboardInterrupt
validate_config() -> bool
¶
Validate that all required configuration is present.
start() -> None
async
¶
Start the Tux bot, handling setup, errors, and shutdown.
Source code in tux/app.py
async def start(self) -> None:
"""Start the Tux bot, handling setup, errors, and shutdown."""
self.setup_sentry()
self.setup_signals()
if not self.validate_config():
return
self.bot = Tux(
command_prefix=get_prefix,
strip_after_prefix=True,
case_insensitive=True,
intents=discord.Intents.all(),
owner_ids={CONFIG.BOT_OWNER_ID, *CONFIG.SYSADMIN_IDS},
allowed_mentions=discord.AllowedMentions(everyone=False),
help_command=TuxHelp(),
activity=None,
status=discord.Status.online,
)
try:
await self.bot.start(CONFIG.BOT_TOKEN, reconnect=True)
except KeyboardInterrupt:
logger.info("Shutdown requested (KeyboardInterrupt)")
except Exception as e:
logger.critical(f"Bot failed to start: {e}")
await self.shutdown()
finally:
await self.shutdown()
shutdown() -> None
async
¶
Gracefully shut down the bot and flush Sentry.
Functions¶
get_prefix(bot: Tux, message: discord.Message) -> list[str]
async
¶
Resolve the command prefix for a guild or use the default prefix.
Source code in tux/app.py
async def get_prefix(bot: Tux, message: discord.Message) -> list[str]:
"""Resolve the command prefix for a guild or use the default prefix."""
prefix: str | None = None
if message.guild:
try:
from tux.database.controllers import DatabaseController
prefix = await DatabaseController().guild_config.get_guild_prefix(message.guild.id)
except Exception as e:
logger.error(f"Error getting guild prefix: {e}")
return [prefix or CONFIG.DEFAULT_PREFIX]