238 lines
8.9 KiB
Python
238 lines
8.9 KiB
Python
import asyncio
|
|
import threading
|
|
import discord
|
|
from discord.ext import commands
|
|
import os
|
|
from dotenv import load_dotenv
|
|
import sys
|
|
import asyncio
|
|
import subprocess
|
|
import importlib.util
|
|
import argparse
|
|
import logging
|
|
import asyncpg
|
|
import redis.asyncio as aioredis
|
|
from commands import (
|
|
load_all_cogs,
|
|
reload_all_cogs,
|
|
) # May need to modify or create a new load function
|
|
from error_handler import handle_error, patch_discord_methods, store_interaction_content
|
|
from utils import reload_script
|
|
import settings_manager
|
|
from db import mod_log_db
|
|
from global_bot_accessor import set_bot_instance
|
|
|
|
# Load environment variables from .env file
|
|
load_dotenv()
|
|
|
|
# --- Constants ---
|
|
DEFAULT_PREFIX = "!"
|
|
# Define the specific cogs for this bot
|
|
FEMDOM_TETO_COGS = {"cogs.femdom_teto_cog", "cogs.femdom_roleplay_teto_cog"}
|
|
|
|
|
|
# --- Dynamic Prefix Function ---
|
|
async def get_prefix(bot_instance, message):
|
|
"""Determines the command prefix based on guild settings or default, but disables mention as prefix."""
|
|
if not message.guild:
|
|
# Use default prefix in DMs
|
|
return DEFAULT_PREFIX
|
|
|
|
# Fetch prefix from settings manager (cache first, then DB)
|
|
# This bot might need its own prefix setting or share the main bot's
|
|
# For simplicity, let's use a fixed prefix for now or a different setting key
|
|
# Using a fixed prefix for this specific bot
|
|
return "!" # Or a different prefix like "fd!"
|
|
|
|
|
|
# --- Bot Setup ---
|
|
# Set up intents (permissions)
|
|
intents = discord.Intents.default()
|
|
intents.message_content = True
|
|
intents.members = True
|
|
|
|
|
|
# --- Custom Bot Class with setup_hook for async initialization ---
|
|
class FemdomTetoBot(commands.Bot):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.owner_id = int(os.getenv("OWNER_USER_ID")) # Assuming owner ID is the same
|
|
self.pg_pool = None # Will be initialized in setup_hook
|
|
self.redis = None # Will be initialized in setup_hook
|
|
|
|
async def setup_hook(self):
|
|
log.info("Running FemdomTetoBot setup_hook...")
|
|
|
|
# Create Postgres pool on this loop
|
|
# This bot might need its own DB or share the main bot's. Sharing is simpler for now.
|
|
self.pg_pool = await asyncpg.create_pool(
|
|
dsn=settings_manager.DATABASE_URL, min_size=1, max_size=10, loop=self.loop
|
|
)
|
|
log.info("Postgres pool initialized and attached to bot.pg_pool.")
|
|
|
|
# Create Redis client on this loop
|
|
# This bot might need its own Redis or share the main bot's. Sharing is simpler for now.
|
|
self.redis = await aioredis.from_url(
|
|
settings_manager.REDIS_URL,
|
|
max_connections=10,
|
|
decode_responses=True,
|
|
)
|
|
log.info("Redis client initialized and attached to bot.redis.")
|
|
|
|
# This bot instance also needs to be accessible for settings_manager if it uses it
|
|
# Need to decide if this bot uses the same settings as the main bot or has its own
|
|
# For now, let's assume it might need access to settings_manager
|
|
# set_bot_instance(self) # This would overwrite the main bot instance, need a different approach if both run simultaneously
|
|
|
|
# Initialize database schema and run migrations using settings_manager
|
|
# Only the main bot should likely do this. This bot will just use the existing DB.
|
|
# if self.pg_pool and self.redis:
|
|
# try:
|
|
# await settings_manager.initialize_database()
|
|
# await settings_manager.run_migrations()
|
|
# except Exception as e:
|
|
# log.exception("CRITICAL: Failed during settings_manager database setup (init/migrations).")
|
|
|
|
# Setup the moderation log table *after* pool initialization
|
|
# Only the main bot should likely do this.
|
|
# if self.pg_pool:
|
|
# try:
|
|
# await mod_log_db.setup_moderation_log_table(self.pg_pool)
|
|
# except Exception as e:
|
|
# log.exception("CRITICAL: Failed to setup moderation log table in setup_hook.")
|
|
|
|
# Load only the specific cogs for this bot
|
|
for cog_extension in FEMDOM_TETO_COGS:
|
|
try:
|
|
await self.load_extension(cog_extension)
|
|
log.info(f"Successfully loaded cog: {cog_extension}")
|
|
except commands.ExtensionAlreadyLoaded:
|
|
log.info(f"Cog already loaded: {cog_extension}")
|
|
except commands.ExtensionNotFound:
|
|
log.error(f"Cog not found: {cog_extension}")
|
|
except Exception as e:
|
|
log.exception(f"Failed to load cog {cog_extension}: {e}")
|
|
log.info(f"Specific cogs loading attempted for: {FEMDOM_TETO_COGS}")
|
|
|
|
log.info("FemdomTetoBot setup_hook completed.")
|
|
|
|
|
|
# Create bot instance using the custom class
|
|
# This bot will use a different token
|
|
femdom_teto_bot = FemdomTetoBot(command_prefix=get_prefix, intents=intents)
|
|
|
|
# --- Logging Setup ---
|
|
logging.basicConfig(
|
|
level=logging.INFO, format="%(asctime)s:%(levelname)s:%(name)s: %(message)s"
|
|
)
|
|
log = logging.getLogger(__name__) # Logger for this script
|
|
|
|
|
|
# --- Events ---
|
|
@femdom_teto_bot.event
|
|
async def on_ready():
|
|
log.info(f"{femdom_teto_bot.user.name} has connected to Discord!")
|
|
log.info(f"Bot ID: {femdom_teto_bot.user.id}")
|
|
# Set the bot's status
|
|
await femdom_teto_bot.change_presence(
|
|
activity=discord.Activity(
|
|
type=discord.ActivityType.listening, name="for commands"
|
|
)
|
|
)
|
|
log.info("Bot status set.")
|
|
|
|
# Patch Discord methods to store message content
|
|
try:
|
|
patch_discord_methods()
|
|
print("Discord methods patched to store message content for error handling")
|
|
|
|
# Make the store_interaction_content function available globally
|
|
import builtins
|
|
|
|
builtins.store_interaction_content = store_interaction_content
|
|
print("Made store_interaction_content available globally")
|
|
except Exception as e:
|
|
print(f"Warning: Failed to patch Discord methods: {e}")
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
# Sync commands - This bot only has specific commands from its cogs
|
|
try:
|
|
print("Starting command sync process for FemdomTetoBot...")
|
|
# Sync commands globally or per guild as needed for these specific cogs
|
|
# For simplicity, let's sync globally for now if the commands are global app commands
|
|
await femdom_teto_bot.tree.sync()
|
|
print("Global command sync complete for FemdomTetoBot.")
|
|
|
|
except Exception as e:
|
|
print(f"Failed to sync commands for FemdomTetoBot: {e}")
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
# Error handling - Use the same handler
|
|
@femdom_teto_bot.event
|
|
async def on_command_error(ctx, error):
|
|
await handle_error(ctx, error)
|
|
|
|
|
|
@femdom_teto_bot.tree.error
|
|
async def on_app_command_error(interaction, error):
|
|
await handle_error(interaction, error)
|
|
|
|
|
|
# --- Global Command Checks ---
|
|
# Need to decide if this bot uses the same global checks or different ones
|
|
# For now, let's skip global checks for simplicity or adapt them if needed
|
|
# @femdom_teto_bot.before_invoke
|
|
# async def global_command_checks(ctx: commands.Context):
|
|
# pass # Implement checks if necessary
|
|
|
|
|
|
async def main():
|
|
"""Main async function to load cogs and start the bot."""
|
|
TOKEN = os.getenv("FEMDOM_TETO_DISCORD_TOKEN") # Use a different token
|
|
if not TOKEN:
|
|
raise ValueError(
|
|
"No FEMDOM_TETO_DISCORD_TOKEN found. Make sure to set FEMDOM_TETO_DISCORD_TOKEN in your .env file."
|
|
)
|
|
|
|
# This bot likely doesn't need to start the Flask or unified API servers
|
|
# if API_AVAILABLE:
|
|
# print("Starting unified API service...")
|
|
# try:
|
|
# api_thread = start_api_in_thread()
|
|
# print("Unified API service started successfully")
|
|
# except Exception as e:
|
|
# print(f"Failed to start unified API service: {e}")
|
|
|
|
try:
|
|
# The bot will call setup_hook internally after login but before on_ready.
|
|
await femdom_teto_bot.start(TOKEN)
|
|
except Exception as e:
|
|
log.exception(f"An error occurred during femdom_teto_bot.start(): {e}")
|
|
finally:
|
|
# Close database/cache pools if they were initialized
|
|
if femdom_teto_bot.pg_pool:
|
|
log.info("Closing Postgres pool in main finally block...")
|
|
await femdom_teto_bot.pg_pool.close()
|
|
if femdom_teto_bot.redis:
|
|
log.info("Closing Redis pool in main finally block...")
|
|
await femdom_teto_bot.redis.close()
|
|
if not femdom_teto_bot.pg_pool and not femdom_teto_bot.redis:
|
|
log.info(
|
|
"Pools were not initialized or already closed, skipping close_pools in main."
|
|
)
|
|
|
|
|
|
# Run the main async function
|
|
if __name__ == "__main__":
|
|
try:
|
|
asyncio.run(main())
|
|
except KeyboardInterrupt:
|
|
log.info("Femdom Teto Bot stopped by user.")
|
|
except Exception as e:
|
|
log.exception(f"An error occurred running the Femdom Teto bot: {e}")
|