first commit
This commit is contained in:
89
src/daemon_boyfriend/bot.py
Normal file
89
src/daemon_boyfriend/bot.py
Normal 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.")
|
||||
Reference in New Issue
Block a user