fixed, fuully bug free
This commit is contained in:
145
bot.py
145
bot.py
@@ -4,6 +4,8 @@ import os
|
||||
from dotenv import load_dotenv
|
||||
from database import Database
|
||||
import logging
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
# OC Bot - Coded by Ball Studios 🎨
|
||||
# A Discord bot for managing and roleplaying as original characters
|
||||
@@ -11,10 +13,34 @@ import logging
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
# Setup logging with more verbosity
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Log startup info
|
||||
logger.info('=' * 60)
|
||||
logger.info('Starting OC Bot...')
|
||||
logger.info(f'Python: {sys.version}')
|
||||
logger.info(f'discord.py: {discord.__version__}')
|
||||
|
||||
# Verify required environment variables
|
||||
required_env_vars = ['DISCORD_TOKEN', 'DB_HOST', 'DB_USER', 'DB_PASSWORD', 'DB_NAME']
|
||||
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
|
||||
if missing_vars:
|
||||
logger.error(f'❌ Missing required environment variables: {", ".join(missing_vars)}')
|
||||
for var in missing_vars:
|
||||
logger.error(f' - {var}')
|
||||
sys.exit(1)
|
||||
|
||||
logger.info('✓ All required environment variables found')
|
||||
logger.info(f'Database Host: {os.getenv("DB_HOST")}')
|
||||
logger.info(f'Database Name: {os.getenv("DB_NAME")}')
|
||||
logger.info(f'Database User: {os.getenv("DB_USER")}')
|
||||
logger.info('=' * 60)
|
||||
|
||||
# Bot setup
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
@@ -25,26 +51,53 @@ bot = commands.Bot(command_prefix=',', intents=intents)
|
||||
# Database instance
|
||||
db = None
|
||||
|
||||
@bot.command(name='divide')
|
||||
async def divide_command(ctx):
|
||||
"""Sends a Discord GIF link when you type ,divide"""
|
||||
gif_url = "https://cdn.discordapp.com/attachments/1478216070660755569/1516884782188069057/togif.gif?ex=6a34448a&is=6a32f30a&hm=5f505a453c242673c212999ed8f2b666bf455678b3762312fa3eda31b761fc73&"
|
||||
await ctx.send(gif_url)
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
global db
|
||||
logger.info(f'{bot.user} has connected to Discord!')
|
||||
print('started')
|
||||
logger.info(f'✓ Discord connection established: {bot.user}')
|
||||
|
||||
if db is None:
|
||||
logger.info('Initializing database connection...')
|
||||
db = Database()
|
||||
await db.connect()
|
||||
try:
|
||||
await db.connect()
|
||||
bot.db = db # Make database accessible to cogs
|
||||
logger.info('✓✓✓ DATABASE CONNECTION SUCCESSFUL ✓✓✓')
|
||||
except Exception as e:
|
||||
logger.critical(f'❌❌❌ FAILED TO CONNECT TO DATABASE ❌❌❌')
|
||||
logger.critical(f'Error: {type(e).__name__}: {str(e)}')
|
||||
logger.critical(f'Full traceback:\n{traceback.format_exc()}')
|
||||
logger.critical('Bot cannot function without database. Exiting...')
|
||||
await bot.close()
|
||||
return
|
||||
|
||||
await load_cogs()
|
||||
|
||||
async def load_cogs():
|
||||
"""Load all command cogs"""
|
||||
cogs_dir = 'cogs'
|
||||
for filename in os.listdir(cogs_dir):
|
||||
if filename.endswith('.py') and not filename.startswith('_'):
|
||||
try:
|
||||
await bot.load_extension(f'cogs.{filename[:-3]}')
|
||||
logger.info(f'Loaded cog: {filename}')
|
||||
except Exception as e:
|
||||
logger.error(f'Failed to load cog {filename}: {e}')
|
||||
if not os.path.exists(cogs_dir):
|
||||
logger.warning(f'Cogs directory not found: {cogs_dir}')
|
||||
return
|
||||
|
||||
cog_files = [f for f in os.listdir(cogs_dir) if f.endswith('.py') and not f.startswith('_')]
|
||||
logger.info(f'Found {len(cog_files)} cog(s) to load')
|
||||
|
||||
for filename in cog_files:
|
||||
try:
|
||||
cog_name = filename[:-3]
|
||||
await bot.load_extension(f'cogs.{cog_name}')
|
||||
logger.info(f'✓ Loaded cog: {cog_name}')
|
||||
except Exception as e:
|
||||
logger.error(f'❌ Failed to load cog {filename}:')
|
||||
logger.error(f' {type(e).__name__}: {str(e)}')
|
||||
logger.error(f' Traceback:\n{traceback.format_exc()}')
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
@@ -61,23 +114,34 @@ async def on_message(message):
|
||||
msg_content = parts[1] if len(parts) > 1 else ''
|
||||
|
||||
# Check if this is a registered command
|
||||
if trigger in ['oc', 'help', 'ping', 'stats', 'invite', 'support', 'about', 'reload', 'debug', 'admin', 'config']:
|
||||
# ----- ADDED 'divide' TO THE LIST -----
|
||||
if trigger in ['oc', 'help', 'ping', 'stats', 'invite', 'support', 'about', 'reload', 'debug', 'admin', 'config', 'divide']:
|
||||
await bot.process_commands(message)
|
||||
return
|
||||
# -------------------------------------
|
||||
|
||||
# Try to find a character with this trigger
|
||||
if db:
|
||||
if db is None:
|
||||
logger.warning('Database not connected - cannot process character trigger')
|
||||
return
|
||||
|
||||
try:
|
||||
character = await db.get_character_by_trigger(message.author.id, trigger)
|
||||
if character:
|
||||
logger.info(f'Triggered character: {character.get("name", "Unknown")} (trigger: {trigger}, user: {message.author.id})')
|
||||
# Delete original message
|
||||
try:
|
||||
await message.delete()
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.warning(f'Could not delete message: {e}')
|
||||
|
||||
# Create webhook and send message
|
||||
await send_character_message(message.channel, character, msg_content)
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error(f'Error processing character trigger "{trigger}":')
|
||||
logger.error(f' {type(e).__name__}: {str(e)}')
|
||||
logger.error(f' Traceback:\n{traceback.format_exc()}')
|
||||
|
||||
# Process other commands
|
||||
await bot.process_commands(message)
|
||||
@@ -85,6 +149,8 @@ async def on_message(message):
|
||||
async def send_character_message(channel, character, content):
|
||||
"""Send a message through webhook as a character"""
|
||||
try:
|
||||
logger.info(f'Sending message as {character.get("name")} to {channel}')
|
||||
|
||||
# Get existing webhooks
|
||||
webhooks = await channel.webhooks()
|
||||
webhook = None
|
||||
@@ -97,25 +163,58 @@ async def send_character_message(channel, character, content):
|
||||
|
||||
# Create webhook if doesn't exist
|
||||
if not webhook:
|
||||
logger.info(f'Creating webhook for channel {channel}')
|
||||
webhook = await channel.create_webhook(name='OC-Bot')
|
||||
|
||||
# Send message through webhook
|
||||
await webhook.send(
|
||||
# Get avatar URL safely (fallback to None if not present)
|
||||
avatar_url = character.get('avatar_url') # can be None
|
||||
|
||||
# Send message through webhook, wait=True ensures we get a Message object
|
||||
msg = await webhook.send(
|
||||
content=content,
|
||||
username=character['name'],
|
||||
avatar_url=character['avatar_url']
|
||||
avatar_url=avatar_url,
|
||||
wait=True
|
||||
)
|
||||
|
||||
# Defensive check: webhook.send() might return None in some edge cases
|
||||
if msg is None:
|
||||
logger.error(f'❌ webhook.send() returned None for character {character["name"]}')
|
||||
logger.error(f' content: {content[:50]}, avatar_url: {avatar_url}')
|
||||
return
|
||||
|
||||
logger.info(f'✓ Message sent successfully: {msg.id}')
|
||||
except discord.HTTPException as e:
|
||||
# Specific HTTP error from Discord API
|
||||
logger.error(f'❌ HTTP error sending webhook message: {e.status} {e.text}')
|
||||
except Exception as e:
|
||||
logger.error(f'Failed to send character message: {e}')
|
||||
logger.error(f'❌ Failed to send character message:')
|
||||
logger.error(f' Character: {character.get("name", "Unknown")}')
|
||||
logger.error(f' Channel: {channel}')
|
||||
logger.error(f' {type(e).__name__}: {str(e)}')
|
||||
logger.error(f' Traceback:\n{traceback.format_exc()}')
|
||||
|
||||
@bot.event
|
||||
async def on_command_error(ctx, error):
|
||||
"""Error handling for commands"""
|
||||
logger.error(f'Command error: {error}')
|
||||
logger.error(f'❌ Command error in {ctx.command}:')
|
||||
logger.error(f' {type(error).__name__}: {str(error)}')
|
||||
logger.error(f' Traceback:\n{traceback.format_exc()}')
|
||||
|
||||
# Run bot
|
||||
if __name__ == '__main__':
|
||||
TOKEN = os.getenv('DISCORD_TOKEN')
|
||||
if not TOKEN:
|
||||
raise ValueError('DISCORD_TOKEN not found in .env file')
|
||||
bot.run(TOKEN)
|
||||
logger.critical('❌ DISCORD_TOKEN not found in .env file')
|
||||
sys.exit(1)
|
||||
|
||||
logger.info('Starting bot with Discord token...')
|
||||
try:
|
||||
bot.run(TOKEN)
|
||||
except KeyboardInterrupt:
|
||||
logger.info('Bot interrupted by user')
|
||||
except Exception as e:
|
||||
logger.critical(f'❌ Fatal error running bot:')
|
||||
logger.critical(f' {type(e).__name__}: {str(e)}')
|
||||
logger.critical(f' Traceback:\n{traceback.format_exc()}')
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user