first commit

This commit is contained in:
2026-01-10 21:46:27 +01:00
parent d00593415d
commit 561f1a8fb1
30 changed files with 1932 additions and 1 deletions

View File

@@ -0,0 +1,89 @@
"""Main Discord bot class."""
import logging
from pathlib import Path
import discord
from discord.ext import commands
from daemon_boyfriend.config import settings
logger = logging.getLogger(__name__)
class DaemonBoyfriend(commands.Bot):
"""The main bot class for Daemon Boyfriend."""
def __init__(self) -> None:
intents = discord.Intents.default()
intents.message_content = True
intents.guilds = True
intents.members = True
super().__init__(
command_prefix=settings.command_prefix,
intents=intents,
help_command=None, # We use slash commands instead
)
async def setup_hook(self) -> None:
"""Load cogs and sync commands on startup."""
# Load all cogs
cogs_path = Path(__file__).parent / "cogs"
for cog_file in cogs_path.glob("*.py"):
if cog_file.name.startswith("_"):
continue
cog_name = f"daemon_boyfriend.cogs.{cog_file.stem}"
try:
await self.load_extension(cog_name)
logger.info(f"Loaded cog: {cog_name}")
except Exception as e:
logger.error(f"Failed to load cog {cog_name}: {e}")
# Sync slash commands
if settings.discord_guild_id:
# Sync to specific guild for faster testing (instant)
guild = discord.Object(id=settings.discord_guild_id)
self.tree.copy_global_to(guild=guild)
await self.tree.sync(guild=guild)
logger.info(f"Synced commands to guild {settings.discord_guild_id}")
else:
# Global sync (can take up to 1 hour to propagate)
await self.tree.sync()
logger.info("Synced commands globally")
async def on_ready(self) -> None:
"""Called when the bot is ready."""
if self.user is None:
return
logger.info(f"Logged in as {self.user} (ID: {self.user.id})")
logger.info(f"Connected to {len(self.guilds)} guild(s)")
# Set activity status
activity = discord.Activity(
type=discord.ActivityType.watching,
name="over the MSC group",
)
await self.change_presence(activity=activity)
async def on_command_error(
self,
ctx: commands.Context,
error: commands.CommandError, # type: ignore[type-arg]
) -> None:
"""Global error handler for prefix commands."""
if isinstance(error, commands.CommandNotFound):
return # Ignore unknown commands
if isinstance(error, commands.MissingPermissions):
await ctx.send("You don't have permission to use this command.")
return
if isinstance(error, commands.CommandOnCooldown):
await ctx.send(f"Command on cooldown. Try again in {error.retry_after:.1f}s")
return
# Log unexpected errors
logger.error(f"Command error: {error}", exc_info=error)
await ctx.send("An unexpected error occurred.")