PYTHONPython

client

real world projects / discord bot / bot

PYTHON
client.py🐍
"""
Main bot client setup.
"""

import discord
from discord.ext import commands
import logging

from .config import settings
from .database import Database

logger = logging.getLogger(__name__)


class MyBot(commands.Bot):
    """Custom bot class with additional features."""
    
    def __init__(self):
        intents = discord.Intents.default()
        intents.message_content = True
        intents.members = True
        
        super().__init__(
            command_prefix=settings.BOT_PREFIX,
            description=settings.BOT_DESCRIPTION,
            intents=intents,
            help_command=commands.DefaultHelpCommand()
        )
        
        self.db = Database(settings.DATABASE_PATH)
    
    async def setup_hook(self):
        """Called when bot is starting up."""
        # Initialize database
        await self.db.initialize()
        
        # Load cogs
        cog_modules = [
            'bot.cogs.admin',
            'bot.cogs.fun',
            'bot.cogs.moderation',
            'bot.cogs.reminders',
            'bot.cogs.polls',
            'bot.cogs.stats',
        ]
        
        for cog in cog_modules:
            try:
                await self.load_extension(cog)
                logger.info(f"Loaded cog: {cog}")
            except Exception as e:
                logger.error(f"Failed to load cog {cog}: {e}")
    
    async def on_ready(self):
        """Called when bot is ready."""
        logger.info(f"Logged in as {self.user} (ID: {self.user.id})")
        logger.info(f"Connected to {len(self.guilds)} guilds")
        
        # Set presence
        activity = discord.Activity(
            type=discord.ActivityType.watching,
            name=f"{settings.BOT_PREFIX}help"
        )
        await self.change_presence(activity=activity)
    
    async def on_guild_join(self, guild: discord.Guild):
        """Called when bot joins a new server."""
        logger.info(f"Joined guild: {guild.name} (ID: {guild.id})")
        
        # Send welcome message to system channel
        if guild.system_channel:
            embed = discord.Embed(
                title="👋 Thanks for adding me!",
                description=f"Use `{settings.BOT_PREFIX}help` to see available commands.",
                color=discord.Color.green()
            )
            await guild.system_channel.send(embed=embed)
    
    async def on_command_error(self, ctx: commands.Context, error: commands.CommandError):
        """Global error handler for 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.MissingRequiredArgument):
            await ctx.send(f"❌ Missing argument: `{error.param.name}`")
            return
        
        if isinstance(error, commands.BadArgument):
            await ctx.send(f"❌ Invalid argument: {error}")
            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 error occurred while processing your command.")
    
    async def close(self):
        """Cleanup when bot shuts down."""
        await self.db.close()
        await super().close()


def create_bot() -> MyBot:
    """Factory function to create bot instance."""
    return MyBot()
PreviousNext