Init
This commit is contained in:
168
app/bot.py
Normal file
168
app/bot.py
Normal file
@@ -0,0 +1,168 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user