Files
drb-server-discord-bot/app/bot.py
Logan Cusano f34241acac Init
2025-05-24 02:09:35 -04:00

169 lines
7.5 KiB
Python

import discord
from discord.ext import commands
import os
import logging
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Define intents
# You might need to adjust these based on the features your bot uses
intents = discord.Intents.default()
intents.message_content = True # Required to read message content in most cases
intents.members = True # Required for member-related events and fetching members
intents.presences = True
# Initialize the bot
# command_prefix is the character(s) that trigger bot commands (e.g., !command)
bot = commands.Bot(command_prefix='!', intents=intents)
# --- Event Handlers ---
@bot.event
async def on_ready():
"""Logs when the bot is ready and connected to Discord."""
logging.info(f'Logged in as {bot.user.name} ({bot.user.id})')
logging.info('------')
logging.info('Loading Modules')
logging.info('------')
await load_extensions() # Load command modules when the bot is ready
logging.info('Registering slash commands')
logging.info('------')
for server in bot.guilds:
num_synced = await bot.tree.sync(guild=discord.Object(id=server.id))
logging.info(f"Registered {num_synced} commands for server ID: '{server.id}'")
@bot.event
async def on_command_error(ctx, error):
"""Handles errors that occur when running commands."""
if isinstance(error, commands.CommandNotFound):
# Ignore CommandNotFound errors to avoid spamming the console/chat
return
elif isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f'Error: Missing required argument(s). Usage: `{ctx.command.usage or "No usage info provided."}`')
elif isinstance(error, commands.BadArgument):
await ctx.send(f'Error: Invalid argument(s) provided. Usage: `{ctx.command.usage or "No usage info provided."}`')
elif isinstance(error, commands.MissingPermissions):
await ctx.send("Error: You don't have the necessary permissions to run this command.")
elif isinstance(error, commands.BotMissingPermissions):
await ctx.send("Error: I don't have the necessary permissions to run this command.")
else:
# Log other errors for debugging
logging.error(f'Ignoring exception in command {ctx.command}:', exc_info=error)
await ctx.send(f'An unexpected error occurred: {error}')
# --- Extension (Command Module) Loading ---
async def load_extensions():
"""Loads all command modules from the 'commands' directory."""
logging.info("Attempting to load extensions...")
commands_dir = 'commands'
if not os.path.exists(commands_dir):
logging.warning(f"'{commands_dir}' directory not found. No commands will be loaded.")
return
for filename in os.listdir(commands_dir):
# Check if the file is a Python file and not a hidden file
if filename.endswith('.py') and not filename.startswith('_'):
# Construct the module path (e.g., commands.example)
module_name = f'{commands_dir}.{filename[:-3]}'
try:
await bot.load_extension(module_name)
logging.info(f'Successfully loaded extension: {module_name}')
except Exception as e:
logging.error(f'Failed to load extension {module_name}: {e}', exc_info=True)
async def unload_extensions():
"""Unloads all loaded command modules."""
logging.info("Attempting to unload extensions...")
for extension in list(bot.extensions): # Iterate over a copy as unloading modifies the list
try:
await bot.unload_extension(extension)
logging.info(f'Successfully unloaded extension: {extension}')
except Exception as e:
logging.error(f'Failed to unload extension {extension}: {e}', exc_info=True)
async def reload_extensions():
"""Reloads all loaded command modules."""
logging.info("Attempting to reload extensions...")
await unload_extensions()
await load_extensions()
logging.info("Extensions reloaded.")
# --- Admin Commands (Optional, for managing extensions) ---
# You might want to restrict these commands to specific users or roles
@bot.command(name='load', hidden=True)
@commands.is_owner() # Requires the user to be the bot owner (set via application settings)
async def load_command(ctx, extension_name: str):
"""Loads a specific command extension."""
module_name = f'commands.{extension_name}'
try:
await bot.load_extension(module_name)
await ctx.send(f'Successfully loaded extension: `{module_name}`')
logging.info(f'Manual load: Successfully loaded extension: {module_name}')
except commands.ExtensionAlreadyLoaded:
await ctx.send(f'Extension `{module_name}` is already loaded.')
except commands.ExtensionNotFound:
await ctx.send(f'Extension `{module_name}` not found.')
except Exception as e:
await ctx.send(f'Failed to load extension `{module_name}`: {e}')
logging.error(f'Manual load: Failed to load extension {module_name}: {e}', exc_info=True)
@bot.command(name='unload', hidden=True)
@commands.is_owner()
async def unload_command(ctx, extension_name: str):
"""Unloads a specific command extension."""
module_name = f'commands.{extension_name}'
try:
await bot.unload_extension(module_name)
await ctx.send(f'Successfully unloaded extension: `{module_name}`')
logging.info(f'Manual unload: Successfully unloaded extension: {module_name}')
except commands.ExtensionNotFound:
await ctx.send(f'Extension `{module_name}` not found or not loaded.')
except Exception as e:
await ctx.send(f'Failed to unload extension `{module_name}`: {e}')
logging.error(f'Manual unload: Failed to unload extension {module_name}: {e}', exc_info=True)
@bot.command(name='reload', hidden=True)
@commands.is_owner()
async def reload_command(ctx, extension_name: str = None):
"""Reloads a specific command extension or all extensions."""
if extension_name:
module_name = f'commands.{extension_name}'
try:
await bot.reload_extension(module_name)
await ctx.send(f'Successfully reloaded extension: `{module_name}`')
logging.info(f'Manual reload: Successfully reloaded extension: {module_name}')
except commands.ExtensionNotFound:
await ctx.send(f'Extension `{module_name}` not found or not loaded.')
except Exception as e:
await ctx.send(f'Failed to reload extension `{module_name}`: {e}')
logging.error(f'Manual reload: Failed to reload extension {module_name}: {e}', exc_info=True)
else:
# Reload all extensions if no specific name is provided
await reload_extensions()
await ctx.send('All extensions reloaded.')
# --- Running the Bot ---
if __name__ == "__main__":
# Get the bot token from environment variables
# It's recommended to use environment variables for sensitive information
DISCORD_BOT_TOKEN = os.getenv('DISCORD_BOT_TOKEN')
if DISCORD_BOT_TOKEN is None:
logging.error("DISCORD_BOT_TOKEN environment variable not set.")
logging.error("Please set the DISCORD_BOT_TOKEN environment variable with your bot's token.")
else:
try:
# Run the bot
bot.run(DISCORD_BOT_TOKEN)
except discord.errors.LoginFailure:
logging.error("Invalid Discord bot token provided. Please check your token.")
except Exception as e:
logging.error(f"An error occurred while running the bot: {e}", exc_info=True)