ia_bot/main.py
2026-08-30 16:54:49 +10:00

139 lines
4.8 KiB
Python

"""Entry point for the Discord bot.
Usage:
./start_server.sh
"""
import asyncio
import os
import sys
import logging
import random
import discord
from discord.ext import commands
from discord.app_commands import Command as AppCommand, CommandTree
# from handlers.gif_emotes import setup as setup_gif_emotes
# from handlers.reactions import setup as setup_reactions
from handlers.voice import setup as setup_voice
from handlers.voice_gen import setup as setup_voice_gen
from handlers.love_emotes import setup as setup_love_emotes
from handlers.utils import setup as setup_utils
from handlers.memes import process_meme
from commands.ping import ping
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Configure logging
logging.basicConfig(
level=logging.DEBUG if os.environ.get("APP_DEBUG") == "1" else logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler(sys.stdout),
logging.FileHandler("bot.log")]
)
# ─── Bot Setup ───────────────────────────────────────────────────────────────
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
intents.messages = True
intents.voice_states = True
bot = commands.Bot(
command_prefix="!",
intents=intents,
)
tree: CommandTree = bot.tree
# ─── Slash Commands ──────────────────────────────────────────────────────────
tree.add_command(AppCommand(name="ping", callback=ping, description="Check bot latency."))
# ─── Event Handlers ──────────────────────────────────────────────────────────
@bot.event
async def on_ready() -> None:
"""Entry point after successful login."""
assert bot.user is not None
print(f"Logged in as {bot.user} (ID: {bot.user.id})")
print(f"Bot is running with {len(bot.guilds)} guilds")
# Register handlers
await setup_voice(bot)
await setup_love_emotes(bot)
await setup_utils(bot)
await setup_voice_gen(bot)
# Sync slash commands
await tree.sync()
print("Slash commands synced.")
print("All handlers registered.")
@bot.event
async def on_command_error(ctx: commands.Context, error: commands.CommandError) -> None:
"""Global error handler for all commands."""
logger = logging.getLogger(__name__)
logger.debug(f"Command error for {ctx.author.name} ({ctx.author.id}): {error}")
if isinstance(error, commands.CommandNotFound):
await ctx.send("Command not found. Type `!list-commands` for a list of commands.")
elif isinstance(error, commands.CheckFailure):
await ctx.send("You do not have permission to use this command.")
elif isinstance(error, commands.MissingPermissions):
await ctx.send("You don't have permission to use this command.")
elif isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f"Missing argument: {error.param.name}.")
elif isinstance(error, commands.BadArgument):
await ctx.send(f"Bad argument: {error.argument}.")
elif isinstance(error, commands.CommandOnCooldown):
remaining = int(error.retry_after)
await ctx.send(
f"This command is on cooldown. Try again in {remaining} seconds."
)
else:
await ctx.send(f"An error occurred: {error.__traceback__.__str__()}")
@bot.event
async def on_message(message: discord.Message) -> None:
"""Handle text messages sent in channels (prefix commands)."""
# Ignore bot's own messages
if message.author == bot.user:
return
logging.debug(f"Got message: {message.content} with @mentions: {message.mentions}")
# Ignore messages in DMs
if message.guild is None:
return
msg = message.content
if bot.user in message.mentions:
await message.channel.send("Let the rich eat cake!")
if msg.startswith("!"):
await bot.process_commands(message)
# The meme train
if random.randint(1, 100) > 25:
await process_meme(message)
# ─── Entry Point ─────────────────────────────────────────────────────────────
def main() -> None:
"""Run the bot."""
token = os.getenv("DISCORD_TOKEN")
if not token:
print("Error: DISCORD_TOKEN environment variable is not set.")
print("Create a .env file with your Discord bot token.")
sys.exit(1)
asyncio.run(bot.start(token))
if __name__ == "__main__":
main()