From bfb21cd1698bb1151fa5ccb13a6869c0e8ba2f50 Mon Sep 17 00:00:00 2001 From: Slipstream Date: Tue, 6 May 2025 19:09:20 -0600 Subject: [PATCH] aa --- global_bot_accessor.py | 26 ++ main.py | 4 + settings_manager.py | 643 +++++++++++++++++++++-------------------- 3 files changed, 362 insertions(+), 311 deletions(-) create mode 100644 global_bot_accessor.py diff --git a/global_bot_accessor.py b/global_bot_accessor.py new file mode 100644 index 0000000..ffa583f --- /dev/null +++ b/global_bot_accessor.py @@ -0,0 +1,26 @@ +# discordbot/global_bot_accessor.py +""" +This module provides a global accessor for the bot instance. +It helps to avoid circular dependencies when other modules need access +to the bot instance, especially its shared resources like connection pools. +""" + +_bot_instance = None + +def set_bot_instance(bot_instance_ref): + """ + Sets the global bot instance. Should be called once from main.py + after the bot object is created. + """ + global _bot_instance + if _bot_instance is not None and _bot_instance != bot_instance_ref : + # This might indicate an issue if called multiple times with different instances + print(f"WARNING: Global bot instance is being overwritten. Old ID: {id(_bot_instance)}, New ID: {id(bot_instance_ref)}") + _bot_instance = bot_instance_ref + +def get_bot_instance(): + """ + Retrieves the global bot instance. + Returns None if set_bot_instance has not been called. + """ + return _bot_instance diff --git a/main.py b/main.py index 4e08e1b..7f2ec43 100644 --- a/main.py +++ b/main.py @@ -22,6 +22,7 @@ from utils import reload_script import settings_manager # Import the settings manager from db import mod_log_db # Import the new mod log db functions import command_customization # Import command customization utilities +from discordbot.global_bot_accessor import set_bot_instance # Import the new accessor # Import the unified API service runner and the sync API module import sys @@ -558,6 +559,9 @@ async def main(args): # Pass parsed args else: bot.ai_cogs_to_skip = [] # Ensure it exists even if empty + set_bot_instance(bot) # Set the global bot instance + log.info(f"Global bot instance set in global_bot_accessor. Bot ID: {id(bot)}") + # Pool initialization and cog loading are now handled in MyBot.setup_hook() try: diff --git a/settings_manager.py b/settings_manager.py index 281f195..b39fd57 100644 --- a/settings_manager.py +++ b/settings_manager.py @@ -6,6 +6,8 @@ import asyncio from dotenv import load_dotenv from typing import Dict +from discordbot.global_bot_accessor import get_bot_instance # Import the accessor + # Load environment variables load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), '.env')) @@ -22,45 +24,46 @@ DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST REDIS_URL = f"redis://{':' + REDIS_PASSWORD + '@' if REDIS_PASSWORD else ''}{REDIS_HOST}:{REDIS_PORT}/0" # Use DB 0 for settings cache # --- Module-level Connection Pools (to be set by the bot) --- -_active_pg_pool = None -_active_redis_pool = None +# _active_pg_pool = None # Removed +# _active_redis_pool = None # Removed # --- Logging --- log = logging.getLogger(__name__) # --- Connection Management --- -def set_bot_pools(pg_pool_instance, redis_pool_instance): - """ - Sets the active PostgreSQL and Redis pools for this module. - This function should be called by the main bot application (e.g., in setup_hook) - after it has initialized the pools on the correct event loop. - """ - global _active_pg_pool, _active_redis_pool - log.info(f"settings_manager.set_bot_pools called. PG Pool Instance: {pg_pool_instance}, Redis Pool Instance: {redis_pool_instance}") - _active_pg_pool = pg_pool_instance - _active_redis_pool = redis_pool_instance - current_loop = asyncio.get_event_loop() - if _active_pg_pool: - log.info(f"settings_manager: Global PostgreSQL pool set. ID: {id(_active_pg_pool)}, Loop: {current_loop}, Pool Loop: {getattr(_active_pg_pool, '_loop', 'N/A')}") - else: - log.warning("settings_manager: Global PostgreSQL pool was NOT set (received None).") - if _active_redis_pool: - log.info(f"settings_manager: Global Redis pool set. ID: {id(_active_redis_pool)}, Loop: {current_loop}") # Redis pool might not have _loop - else: - log.warning("settings_manager: Global Redis pool was NOT set (received None).") +# def set_bot_pools(pg_pool_instance, redis_pool_instance): # Removed +# """ +# Sets the active PostgreSQL and Redis pools for this module. +# This function should be called by the main bot application (e.g., in setup_hook) +# after it has initialized the pools on the correct event loop. +# """ +# global _active_pg_pool, _active_redis_pool +# log.info(f"settings_manager.set_bot_pools called. PG Pool Instance: {pg_pool_instance}, Redis Pool Instance: {redis_pool_instance}") +# _active_pg_pool = pg_pool_instance +# _active_redis_pool = redis_pool_instance +# current_loop = asyncio.get_event_loop() +# if _active_pg_pool: +# log.info(f"settings_manager: Global PostgreSQL pool set. ID: {id(_active_pg_pool)}, Loop: {current_loop}, Pool Loop: {getattr(_active_pg_pool, '_loop', 'N/A')}") +# else: +# log.warning("settings_manager: Global PostgreSQL pool was NOT set (received None).") +# if _active_redis_pool: +# log.info(f"settings_manager: Global Redis pool set. ID: {id(_active_redis_pool)}, Loop: {current_loop}") # Redis pool might not have _loop +# else: +# log.warning("settings_manager: Global Redis pool was NOT set (received None).") # initialize_pools and close_pools are removed as pool lifecycle is managed by the bot. # --- Database Schema Initialization --- async def run_migrations(): """Run database migrations to update schema.""" - if not _active_pg_pool: - log.error("PostgreSQL pool not initialized in settings_manager. Cannot run migrations.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error("Bot instance or PostgreSQL pool not available in settings_manager. Cannot run migrations.") return log.info("Running database migrations...") try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Check if custom_command_description column exists in command_customization table column_exists = await conn.fetchval(""" SELECT EXISTS ( @@ -87,12 +90,13 @@ async def run_migrations(): async def initialize_database(): """Creates necessary tables in the PostgreSQL database if they don't exist.""" - if not _active_pg_pool: - log.error("PostgreSQL pool not initialized in settings_manager. Cannot initialize database.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error("Bot instance or PostgreSQL pool not available in settings_manager. Cannot initialize database.") return log.info("Initializing database schema...") - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: async with conn.transaction(): # Guilds table (to track known guilds, maybe store basic info later) await conn.execute(""" @@ -249,13 +253,13 @@ async def initialize_database(): async def get_starboard_settings(guild_id: int): """Gets the starboard settings for a guild.""" - log.debug(f"get_starboard_settings: _active_pg_pool is {id(_active_pg_pool)}, _active_redis_pool is {id(_active_redis_pool)}") - if _active_pg_pool is None: - log.warning(f"PostgreSQL pool is None in settings_manager (ID: {id(_active_pg_pool)}), returning None for starboard settings for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.warning(f"Bot instance or PostgreSQL pool not available in settings_manager for get_starboard_settings (guild {guild_id}).") return None try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Check if the guild exists in the starboard_settings table settings = await conn.fetchrow( """ @@ -301,9 +305,9 @@ async def update_starboard_settings(guild_id: int, **kwargs): Returns: bool: True if successful, False otherwise """ - log.info(f"update_starboard_settings called for guild {guild_id}. Current _active_pg_pool ID: {id(_active_pg_pool)}") - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager (ID: {id(_active_pg_pool)}), cannot update starboard settings for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for update_starboard_settings (guild {guild_id}).") return False valid_keys = {'enabled', 'star_emoji', 'threshold', 'starboard_channel_id', 'ignore_bots', 'self_star'} @@ -318,7 +322,7 @@ async def update_starboard_settings(guild_id: int, **kwargs): # Acquire a connection with a timeout conn = None try: - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Ensure guild exists try: @@ -328,7 +332,7 @@ async def update_starboard_settings(guild_id: int, **kwargs): log.warning(f"Connection issue when inserting guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) else: raise @@ -351,7 +355,7 @@ async def update_starboard_settings(guild_id: int, **kwargs): log.warning(f"Connection issue when updating starboard settings for guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Try again with the new connection await conn.execute( @@ -370,7 +374,7 @@ async def update_starboard_settings(guild_id: int, **kwargs): finally: # Always release the connection back to the pool if conn: - await _active_pg_pool.release(conn) + await bot.pg_pool.release(conn) except asyncio.TimeoutError: log.error(f"Timeout acquiring database connection for starboard settings update (Guild: {guild_id})") return False @@ -380,12 +384,13 @@ async def update_starboard_settings(guild_id: int, **kwargs): async def get_starboard_entry(guild_id: int, original_message_id: int): """Gets a starboard entry for a specific message.""" - if _active_pg_pool is None: - log.warning(f"PostgreSQL pool is None in settings_manager, returning None for starboard entry for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.warning(f"Bot instance or PostgreSQL pool not available in settings_manager for get_starboard_entry (guild {guild_id}).") return None try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: entry = await conn.fetchrow( """ SELECT * FROM starboard_entries @@ -402,12 +407,13 @@ async def get_starboard_entry(guild_id: int, original_message_id: int): async def create_starboard_entry(guild_id: int, original_message_id: int, original_channel_id: int, starboard_message_id: int, author_id: int, star_count: int = 1): """Creates a new starboard entry.""" - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager, cannot create starboard entry for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for create_starboard_entry (guild {guild_id}).") return False try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) @@ -430,15 +436,16 @@ async def create_starboard_entry(guild_id: int, original_message_id: int, origin async def update_starboard_entry(guild_id: int, original_message_id: int, star_count: int): """Updates the star count for an existing starboard entry.""" - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager, cannot update starboard entry for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for update_starboard_entry (guild {guild_id}).") return False try: # Acquire a connection with a timeout conn = None try: - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) await conn.execute( """ @@ -454,7 +461,7 @@ async def update_starboard_entry(guild_id: int, original_message_id: int, star_c finally: # Always release the connection back to the pool if conn: - await _active_pg_pool.release(conn) + await bot.pg_pool.release(conn) except asyncio.TimeoutError: log.error(f"Timeout acquiring database connection for starboard entry update (Guild: {guild_id}, Message: {original_message_id})") return False @@ -464,15 +471,16 @@ async def update_starboard_entry(guild_id: int, original_message_id: int, star_c async def delete_starboard_entry(guild_id: int, original_message_id: int): """Deletes a starboard entry.""" - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager, cannot delete starboard entry for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for delete_starboard_entry (guild {guild_id}).") return False try: # Acquire a connection with a timeout conn = None try: - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Delete the entry await conn.execute( @@ -497,7 +505,7 @@ async def delete_starboard_entry(guild_id: int, original_message_id: int): finally: # Always release the connection back to the pool if conn: - await _active_pg_pool.release(conn) + await bot.pg_pool.release(conn) except asyncio.TimeoutError: log.error(f"Timeout acquiring database connection for starboard entry deletion (Guild: {guild_id}, Message: {original_message_id})") return False @@ -507,15 +515,16 @@ async def delete_starboard_entry(guild_id: int, original_message_id: int): async def clear_starboard_entries(guild_id: int): """Clears all starboard entries for a guild.""" - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager, cannot clear starboard entries for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for clear_starboard_entries (guild {guild_id}).") return False try: # Acquire a connection with a timeout conn = None try: - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Get all starboard entries for this guild entries = await conn.fetch( @@ -549,7 +558,7 @@ async def clear_starboard_entries(guild_id: int): finally: # Always release the connection back to the pool if conn: - await _active_pg_pool.release(conn) + await bot.pg_pool.release(conn) except asyncio.TimeoutError: log.error(f"Timeout acquiring database connection for clearing starboard entries (Guild: {guild_id})") return False @@ -559,8 +568,9 @@ async def clear_starboard_entries(guild_id: int): async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): """Records a user's star reaction to a message.""" - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager, cannot add starboard reaction for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for add_starboard_reaction (guild {guild_id}).") return False # Use a timeout to prevent hanging on database operations @@ -568,7 +578,7 @@ async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): # Acquire a connection with a timeout conn = None try: - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Ensure guild exists try: @@ -578,7 +588,7 @@ async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): log.warning(f"Connection issue when inserting guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) else: raise @@ -597,7 +607,7 @@ async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): log.warning(f"Connection issue when adding reaction for message {message_id} in guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Try again with the new connection await conn.execute( @@ -626,7 +636,7 @@ async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): log.warning(f"Connection issue when counting reactions for message {message_id} in guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Try again with the new connection count = await conn.fetchval( @@ -643,7 +653,7 @@ async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): # Always release the connection back to the pool if conn: try: - await _active_pg_pool.release(conn) + await bot.pg_pool.release(conn) except Exception as e: log.warning(f"Error releasing connection: {e}") except asyncio.TimeoutError: @@ -655,8 +665,9 @@ async def add_starboard_reaction(guild_id: int, message_id: int, user_id: int): async def remove_starboard_reaction(guild_id: int, message_id: int, user_id: int): """Removes a user's star reaction from a message.""" - if _active_pg_pool is None: - log.error(f"PostgreSQL pool is None in settings_manager, cannot remove starboard reaction for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for remove_starboard_reaction (guild {guild_id}).") return False # Use a timeout to prevent hanging on database operations @@ -664,7 +675,7 @@ async def remove_starboard_reaction(guild_id: int, message_id: int, user_id: int # Acquire a connection with a timeout conn = None try: - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Remove the reaction record try: @@ -680,7 +691,7 @@ async def remove_starboard_reaction(guild_id: int, message_id: int, user_id: int log.warning(f"Connection issue when removing reaction for message {message_id} in guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Try again with the new connection await conn.execute( @@ -708,7 +719,7 @@ async def remove_starboard_reaction(guild_id: int, message_id: int, user_id: int log.warning(f"Connection issue when counting reactions for message {message_id} in guild {guild_id}: {e}") # Try to reset the connection await conn.close() - conn = await asyncio.wait_for(_active_pg_pool.acquire(), timeout=5.0) + conn = await asyncio.wait_for(bot.pg_pool.acquire(), timeout=5.0) # Try again with the new connection count = await conn.fetchval( @@ -725,7 +736,7 @@ async def remove_starboard_reaction(guild_id: int, message_id: int, user_id: int # Always release the connection back to the pool if conn: try: - await _active_pg_pool.release(conn) + await bot.pg_pool.release(conn) except Exception as e: log.warning(f"Error releasing connection: {e}") except asyncio.TimeoutError: @@ -737,12 +748,13 @@ async def remove_starboard_reaction(guild_id: int, message_id: int, user_id: int async def get_starboard_reaction_count(guild_id: int, message_id: int): """Gets the count of star reactions for a message.""" - if _active_pg_pool is None: - log.warning(f"PostgreSQL pool is None in settings_manager, returning 0 for starboard reaction count for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.warning(f"Bot instance or PostgreSQL pool not available in settings_manager for get_starboard_reaction_count (guild {guild_id}).") return 0 try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: count = await conn.fetchval( """ SELECT COUNT(*) FROM starboard_reactions @@ -758,12 +770,13 @@ async def get_starboard_reaction_count(guild_id: int, message_id: int): async def has_user_reacted(guild_id: int, message_id: int, user_id: int): """Checks if a user has already reacted to a message.""" - if _active_pg_pool is None: - log.warning(f"PostgreSQL pool is None in settings_manager, returning False for user reaction check for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.warning(f"Bot instance or PostgreSQL pool not available in settings_manager for has_user_reacted (guild {guild_id}).") return False try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: result = await conn.fetchval( """ SELECT EXISTS( @@ -791,8 +804,9 @@ def _get_redis_key(guild_id: int, key_type: str, identifier: str = None) -> str: async def get_guild_prefix(guild_id: int, default_prefix: str) -> str: """Gets the command prefix for a guild, checking cache first.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning default prefix.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for get_guild_prefix (guild {guild_id}).") return default_prefix cache_key = _get_redis_key(guild_id, "prefix") @@ -800,7 +814,7 @@ async def get_guild_prefix(guild_id: int, default_prefix: str) -> str: # Try to get from cache with timeout and error handling try: # Use a timeout to prevent hanging on Redis operations - cached_prefix = await asyncio.wait_for(_active_redis_pool.get(cache_key), timeout=2.0) + cached_prefix = await asyncio.wait_for(bot.redis.get(cache_key), timeout=2.0) if cached_prefix is not None: log.debug(f"Cache hit for prefix (Guild: {guild_id})") return cached_prefix @@ -817,7 +831,7 @@ async def get_guild_prefix(guild_id: int, default_prefix: str) -> str: # Cache miss or Redis error, get from database log.debug(f"Cache miss for prefix (Guild: {guild_id})") try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: prefix = await conn.fetchval( "SELECT setting_value FROM guild_settings WHERE guild_id = $1 AND setting_key = 'prefix'", guild_id @@ -829,7 +843,7 @@ async def get_guild_prefix(guild_id: int, default_prefix: str) -> str: try: # Use a timeout to prevent hanging on Redis operations await asyncio.wait_for( - _active_redis_pool.set(cache_key, final_prefix, ex=3600), # Cache for 1 hour + bot.redis.set(cache_key, final_prefix, ex=3600), # Cache for 1 hour timeout=2.0 ) except asyncio.TimeoutError: @@ -849,13 +863,14 @@ async def get_guild_prefix(guild_id: int, default_prefix: str) -> str: async def set_guild_prefix(guild_id: int, prefix: str): """Sets the command prefix for a guild and updates the cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set prefix.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for set_guild_prefix (guild {guild_id}).") return False # Indicate failure cache_key = _get_redis_key(guild_id, "prefix") try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Upsert the setting @@ -869,14 +884,14 @@ async def set_guild_prefix(guild_id: int, prefix: str): ) # Update cache - await _active_redis_pool.set(cache_key, prefix, ex=3600) # Cache for 1 hour + await bot.redis.set(cache_key, prefix, ex=3600) # Cache for 1 hour log.info(f"Set prefix for guild {guild_id} to '{prefix}'") return True # Indicate success except Exception as e: log.exception(f"Database or Redis error setting prefix for guild {guild_id}: {e}") # Attempt to invalidate cache on error to prevent stale data try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for prefix (Guild: {guild_id}): {redis_err}") return False # Indicate failure @@ -885,8 +900,9 @@ async def set_guild_prefix(guild_id: int, prefix: str): async def get_setting(guild_id: int, key: str, default=None): """Gets a specific setting for a guild, checking cache first.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning default for setting '{key}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for get_setting (guild {guild_id}, key '{key}').") return default cache_key = _get_redis_key(guild_id, "setting", key) @@ -894,7 +910,7 @@ async def get_setting(guild_id: int, key: str, default=None): # Try to get from cache with timeout and error handling try: # Use a timeout to prevent hanging on Redis operations - cached_value = await asyncio.wait_for(_active_redis_pool.get(cache_key), timeout=2.0) + cached_value = await asyncio.wait_for(bot.redis.get(cache_key), timeout=2.0) if cached_value is not None: # Note: Redis stores everything as strings. Consider type conversion if needed. log.debug(f"Cache hit for setting '{key}' (Guild: {guild_id})") @@ -915,7 +931,7 @@ async def get_setting(guild_id: int, key: str, default=None): # Cache miss or Redis error, get from database log.debug(f"Cache miss for setting '{key}' (Guild: {guild_id})") try: - async with _active_pg_pool.acquire() as conn: # Use _active_pg_pool + async with bot.pg_pool.acquire() as conn: value = await conn.fetchval( "SELECT setting_value FROM guild_settings WHERE guild_id = $1 AND setting_key = $2", guild_id, key @@ -924,38 +940,14 @@ async def get_setting(guild_id: int, key: str, default=None): except Exception as e: log.exception(f"Database error getting setting '{key}' for guild {guild_id}: {e}") return default # Fall back to default on database error - # finally block for db_conn removed as we are using pool context manager - # Cache the result (even if None or default, cache the absence or default value) - # Store None as a special marker, e.g., "None" string, or handle appropriately - value_to_cache = final_value if final_value is not None else "__NONE__" # Marker for None - - # Try to cache the result with timeout and error handling + # Cache the result (even if None or default, cache the absence or default value) + value_to_cache = final_value if final_value is not None else "__NONE__" # Marker for None + if bot.redis: # Ensure redis is available before trying to cache try: # Use a timeout to prevent hanging on Redis operations await asyncio.wait_for( - _active_redis_pool.set(cache_key, value_to_cache, ex=3600), # Cache for 1 hour - timeout=2.0 - ) - except asyncio.TimeoutError: - log.warning(f"Redis timeout setting cache for setting '{key}' for guild {guild_id}") - except RuntimeError as e: - if "got Future" in str(e) and "attached to a different loop" in str(e): - log.warning(f"Redis event loop error setting cache for setting '{key}' for guild {guild_id}: {e}") - else: - log.exception(f"Redis error setting cache for setting '{key}' for guild {guild_id}: {e}") - except Exception as e: - log.exception(f"Redis error setting cache for setting '{key}' for guild {guild_id}: {e}") - - # Cache the result (even if None or default, cache the absence or default value) - # Store None as a special marker, e.g., "None" string, or handle appropriately - value_to_cache = final_value if final_value is not None else "__NONE__" # Marker for None - - # Try to cache the result with timeout and error handling - try: - # Use a timeout to prevent hanging on Redis operations - await asyncio.wait_for( - _active_redis_pool.set(cache_key, value_to_cache, ex=3600), # Cache for 1 hour + bot.redis.set(cache_key, value_to_cache, ex=3600), # Cache for 1 hour timeout=2.0 ) except asyncio.TimeoutError: @@ -968,19 +960,21 @@ async def get_setting(guild_id: int, key: str, default=None): except Exception as e: log.exception(f"Redis error setting cache for setting '{key}' for guild {guild_id}: {e}") + # This block was duplicated, removed the second instance of caching logic. return final_value async def set_setting(guild_id: int, key: str, value: str | None): """Sets a specific setting for a guild and updates/invalidates the cache. Setting value to None effectively deletes the setting.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set setting '{key}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for set_setting (guild {guild_id}, key '{key}').") return False # Indicate failure cache_key = _get_redis_key(guild_id, "setting", key) try: - async with _active_pg_pool.acquire() as conn: # Use _active_pg_pool + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) @@ -994,9 +988,8 @@ async def set_setting(guild_id: int, key: str, value: str | None): """, guild_id, key, str(value) # Ensure value is string ) - # Update cache if _active_redis_pool is available - if _active_redis_pool: - await _active_redis_pool.set(cache_key, str(value), ex=3600) + # Update cache + await bot.redis.set(cache_key, str(value), ex=3600) log.info(f"Set setting '{key}' for guild {guild_id}") else: # Delete the setting if value is None @@ -1004,29 +997,28 @@ async def set_setting(guild_id: int, key: str, value: str | None): "DELETE FROM guild_settings WHERE guild_id = $1 AND setting_key = $2", guild_id, key ) - # Invalidate cache if _active_redis_pool is available - if _active_redis_pool: - await _active_redis_pool.delete(cache_key) + # Invalidate cache + await bot.redis.delete(cache_key) log.info(f"Deleted setting '{key}' for guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error setting setting '{key}' for guild {guild_id}: {e}") - # Attempt to invalidate cache on error if _active_redis_pool is available - if _active_redis_pool: + # Attempt to invalidate cache on error + if bot.redis: try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for setting '{key}' (Guild: {guild_id}): {redis_err}") return False - # finally block for db_conn removed as we are using pool context manager # --- Cog Enablement Functions --- async def is_cog_enabled(guild_id: int, cog_name: str, default_enabled: bool = True) -> bool: """Checks if a cog is enabled for a guild, checking cache first. Uses default_enabled if no specific setting is found.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning default for cog '{cog_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for is_cog_enabled (guild {guild_id}, cog '{cog_name}').") return default_enabled cache_key = _get_redis_key(guild_id, "cog_enabled", cog_name) @@ -1034,7 +1026,7 @@ async def is_cog_enabled(guild_id: int, cog_name: str, default_enabled: bool = T # Try to get from cache with timeout and error handling try: # Use a timeout to prevent hanging on Redis operations - cached_value = await asyncio.wait_for(_active_redis_pool.get(cache_key), timeout=2.0) + cached_value = await asyncio.wait_for(bot.redis.get(cache_key), timeout=2.0) if cached_value is not None: log.debug(f"Cache hit for cog enabled status '{cog_name}' (Guild: {guild_id})") return cached_value == "True" # Redis stores strings @@ -1052,7 +1044,7 @@ async def is_cog_enabled(guild_id: int, cog_name: str, default_enabled: bool = T log.debug(f"Cache miss for cog enabled status '{cog_name}' (Guild: {guild_id})") db_enabled_status = None try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: db_enabled_status = await conn.fetchval( "SELECT enabled FROM enabled_cogs WHERE guild_id = $1 AND cog_name = $2", guild_id, cog_name @@ -1061,21 +1053,22 @@ async def is_cog_enabled(guild_id: int, cog_name: str, default_enabled: bool = T final_status = db_enabled_status if db_enabled_status is not None else default_enabled # Try to cache the result with timeout and error handling - try: - # Use a timeout to prevent hanging on Redis operations - await asyncio.wait_for( - _active_redis_pool.set(cache_key, str(final_status), ex=3600), # Cache for 1 hour - timeout=2.0 - ) - except asyncio.TimeoutError: - log.warning(f"Redis timeout setting cache for cog enabled status '{cog_name}' (Guild: {guild_id})") - except RuntimeError as e: - if "got Future" in str(e) and "attached to a different loop" in str(e): - log.warning(f"Redis event loop error setting cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {e}") - else: + if bot.redis: + try: + # Use a timeout to prevent hanging on Redis operations + await asyncio.wait_for( + bot.redis.set(cache_key, str(final_status), ex=3600), # Cache for 1 hour + timeout=2.0 + ) + except asyncio.TimeoutError: + log.warning(f"Redis timeout setting cache for cog enabled status '{cog_name}' (Guild: {guild_id})") + except RuntimeError as e: + if "got Future" in str(e) and "attached to a different loop" in str(e): + log.warning(f"Redis event loop error setting cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {e}") + else: + log.exception(f"Redis error setting cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {e}") + except Exception as e: log.exception(f"Redis error setting cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {e}") - except Exception as e: - log.exception(f"Redis error setting cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {e}") return final_status except Exception as e: @@ -1086,13 +1079,14 @@ async def is_cog_enabled(guild_id: int, cog_name: str, default_enabled: bool = T async def set_cog_enabled(guild_id: int, cog_name: str, enabled: bool): """Sets the enabled status for a cog in a guild and updates the cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set cog enabled status for '{cog_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for set_cog_enabled (guild {guild_id}, cog '{cog_name}').") return False cache_key = _get_redis_key(guild_id, "cog_enabled", cog_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Upsert the enabled status @@ -1106,24 +1100,26 @@ async def set_cog_enabled(guild_id: int, cog_name: str, enabled: bool): ) # Update cache - await _active_redis_pool.set(cache_key, str(enabled), ex=3600) + await bot.redis.set(cache_key, str(enabled), ex=3600) log.info(f"Set cog '{cog_name}' enabled status to {enabled} for guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error setting cog enabled status for '{cog_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache on error - try: - await _active_redis_pool.delete(cache_key) - except Exception as redis_err: - log.exception(f"Failed to invalidate Redis cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {redis_err}") + if bot.redis: + try: + await bot.redis.delete(cache_key) + except Exception as redis_err: + log.exception(f"Failed to invalidate Redis cache for cog enabled status '{cog_name}' (Guild: {guild_id}): {redis_err}") return False async def is_command_enabled(guild_id: int, command_name: str, default_enabled: bool = True) -> bool: """Checks if a command is enabled for a guild, checking cache first. Uses default_enabled if no specific setting is found.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning default for command '{command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for is_command_enabled (guild {guild_id}, command '{command_name}').") return default_enabled cache_key = _get_redis_key(guild_id, "cmd_enabled", command_name) @@ -1131,7 +1127,7 @@ async def is_command_enabled(guild_id: int, command_name: str, default_enabled: # Try to get from cache with timeout and error handling try: # Use a timeout to prevent hanging on Redis operations - cached_value = await asyncio.wait_for(_active_redis_pool.get(cache_key), timeout=2.0) + cached_value = await asyncio.wait_for(bot.redis.get(cache_key), timeout=2.0) if cached_value is not None: log.debug(f"Cache hit for command enabled status '{command_name}' (Guild: {guild_id})") return cached_value == "True" # Redis stores strings @@ -1149,7 +1145,7 @@ async def is_command_enabled(guild_id: int, command_name: str, default_enabled: log.debug(f"Cache miss for command enabled status '{command_name}' (Guild: {guild_id})") db_enabled_status = None try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: db_enabled_status = await conn.fetchval( "SELECT enabled FROM enabled_commands WHERE guild_id = $1 AND command_name = $2", guild_id, command_name @@ -1158,21 +1154,22 @@ async def is_command_enabled(guild_id: int, command_name: str, default_enabled: final_status = db_enabled_status if db_enabled_status is not None else default_enabled # Try to cache the result with timeout and error handling - try: - # Use a timeout to prevent hanging on Redis operations - await asyncio.wait_for( - _active_redis_pool.set(cache_key, str(final_status), ex=3600), # Cache for 1 hour - timeout=2.0 - ) - except asyncio.TimeoutError: - log.warning(f"Redis timeout setting cache for command enabled status '{command_name}' (Guild: {guild_id})") - except RuntimeError as e: - if "got Future" in str(e) and "attached to a different loop" in str(e): - log.warning(f"Redis event loop error setting cache for command enabled status '{command_name}' (Guild: {guild_id}): {e}") - else: + if bot.redis: + try: + # Use a timeout to prevent hanging on Redis operations + await asyncio.wait_for( + bot.redis.set(cache_key, str(final_status), ex=3600), # Cache for 1 hour + timeout=2.0 + ) + except asyncio.TimeoutError: + log.warning(f"Redis timeout setting cache for command enabled status '{command_name}' (Guild: {guild_id})") + except RuntimeError as e: + if "got Future" in str(e) and "attached to a different loop" in str(e): + log.warning(f"Redis event loop error setting cache for command enabled status '{command_name}' (Guild: {guild_id}): {e}") + else: + log.exception(f"Redis error setting cache for command enabled status '{command_name}' (Guild: {guild_id}): {e}") + except Exception as e: log.exception(f"Redis error setting cache for command enabled status '{command_name}' (Guild: {guild_id}): {e}") - except Exception as e: - log.exception(f"Redis error setting cache for command enabled status '{command_name}' (Guild: {guild_id}): {e}") return final_status except Exception as e: @@ -1183,13 +1180,14 @@ async def is_command_enabled(guild_id: int, command_name: str, default_enabled: async def set_command_enabled(guild_id: int, command_name: str, enabled: bool): """Sets the enabled status for a command in a guild and updates the cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set command enabled status for '{command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for set_command_enabled (guild {guild_id}, command '{command_name}').") return False cache_key = _get_redis_key(guild_id, "cmd_enabled", command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Upsert the enabled status @@ -1203,28 +1201,30 @@ async def set_command_enabled(guild_id: int, command_name: str, enabled: bool): ) # Update cache - await _active_redis_pool.set(cache_key, str(enabled), ex=3600) + await bot.redis.set(cache_key, str(enabled), ex=3600) log.info(f"Set command '{command_name}' enabled status to {enabled} for guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error setting command enabled status for '{command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache on error - try: - await _active_redis_pool.delete(cache_key) - except Exception as redis_err: - log.exception(f"Failed to invalidate Redis cache for command enabled status '{command_name}' (Guild: {guild_id}): {redis_err}") + if bot.redis: + try: + await bot.redis.delete(cache_key) + except Exception as redis_err: + log.exception(f"Failed to invalidate Redis cache for command enabled status '{command_name}' (Guild: {guild_id}): {redis_err}") return False async def get_all_enabled_commands(guild_id: int) -> Dict[str, bool]: """Gets all command enabled statuses for a guild. Returns a dictionary of command_name -> enabled status.""" - if _active_pg_pool is None: - log.error(f"Database pool is None in settings_manager, cannot get command enabled statuses for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for get_all_enabled_commands (guild {guild_id}).") return {} try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: records = await conn.fetch( "SELECT command_name, enabled FROM enabled_commands WHERE guild_id = $1", guild_id @@ -1238,12 +1238,13 @@ async def get_all_enabled_commands(guild_id: int) -> Dict[str, bool]: async def get_all_enabled_cogs(guild_id: int) -> Dict[str, bool]: """Gets all cog enabled statuses for a guild. Returns a dictionary of cog_name -> enabled status.""" - if _active_pg_pool is None: - log.error(f"Database pool is None in settings_manager, cannot get cog enabled statuses for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool: + log.error(f"Bot instance or PostgreSQL pool not available in settings_manager for get_all_enabled_cogs (guild {guild_id}).") return {} try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: records = await conn.fetch( "SELECT cog_name, enabled FROM enabled_cogs WHERE guild_id = $1", guild_id @@ -1257,13 +1258,14 @@ async def get_all_enabled_cogs(guild_id: int) -> Dict[str, bool]: async def add_command_permission(guild_id: int, command_name: str, role_id: int) -> bool: """Adds permission for a role to use a command and invalidates cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot add permission for command '{command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for add_command_permission (guild {guild_id}, command '{command_name}').") return False cache_key = _get_redis_key(guild_id, "cmd_perms", command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Add the permission rule @@ -1277,28 +1279,30 @@ async def add_command_permission(guild_id: int, command_name: str, role_id: int) ) # Invalidate cache after DB operation succeeds - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) log.info(f"Added permission for role {role_id} to use command '{command_name}' in guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error adding permission for command '{command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache even on error - try: - await _active_redis_pool.delete(cache_key) - except Exception as redis_err: - log.exception(f"Failed to invalidate Redis cache for command permissions '{command_name}' (Guild: {guild_id}): {redis_err}") + if bot.redis: + try: + await bot.redis.delete(cache_key) + except Exception as redis_err: + log.exception(f"Failed to invalidate Redis cache for command permissions '{command_name}' (Guild: {guild_id}): {redis_err}") return False async def remove_command_permission(guild_id: int, command_name: str, role_id: int) -> bool: """Removes permission for a role to use a command and invalidates cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot remove permission for command '{command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for remove_command_permission (guild {guild_id}, command '{command_name}').") return False cache_key = _get_redis_key(guild_id, "cmd_perms", command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists (though unlikely to be needed for delete) # await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Remove the permission rule @@ -1311,16 +1315,17 @@ async def remove_command_permission(guild_id: int, command_name: str, role_id: i ) # Invalidate cache after DB operation succeeds - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) log.info(f"Removed permission for role {role_id} to use command '{command_name}' in guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error removing permission for command '{command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache even on error - try: - await _active_redis_pool.delete(cache_key) - except Exception as redis_err: - log.exception(f"Failed to invalidate Redis cache for command permissions '{command_name}' (Guild: {guild_id}): {redis_err}") + if bot.redis: + try: + await bot.redis.delete(cache_key) + except Exception as redis_err: + log.exception(f"Failed to invalidate Redis cache for command permissions '{command_name}' (Guild: {guild_id}): {redis_err}") return False @@ -1329,8 +1334,9 @@ async def check_command_permission(guild_id: int, command_name: str, member_role Returns True if allowed, False otherwise. If no permissions are set for the command in the DB, it defaults to allowed by this check. """ - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, defaulting to allowed for command '{command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for check_command_permission (guild {guild_id}, command '{command_name}').") return True # Default to allowed if system isn't ready cache_key = _get_redis_key(guild_id, "cmd_perms", command_name) @@ -1338,8 +1344,8 @@ async def check_command_permission(guild_id: int, command_name: str, member_role try: # Check cache first - stores a set of allowed role IDs as strings - if await _active_redis_pool.exists(cache_key): - cached_roles = await _active_redis_pool.smembers(cache_key) + if await bot.redis.exists(cache_key): + cached_roles = await bot.redis.smembers(cache_key) # Handle the empty set marker if cached_roles == {"__EMPTY_SET__"}: log.debug(f"Cache hit (empty set) for cmd perms '{command_name}' (Guild: {guild_id}). Command allowed by default.") @@ -1349,7 +1355,7 @@ async def check_command_permission(guild_id: int, command_name: str, member_role else: # Cache miss - fetch from DB log.debug(f"Cache miss for cmd perms '{command_name}' (Guild: {guild_id})") - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: records = await conn.fetch( "SELECT allowed_role_id FROM command_permissions WHERE guild_id = $1 AND command_name = $2", guild_id, command_name @@ -1358,17 +1364,18 @@ async def check_command_permission(guild_id: int, command_name: str, member_role allowed_role_ids_str = {str(record['allowed_role_id']) for record in records} # Cache the result (even if empty) - try: - async with _active_redis_pool.pipeline(transaction=True) as pipe: - pipe.delete(cache_key) # Ensure clean state - if allowed_role_ids_str: - pipe.sadd(cache_key, *allowed_role_ids_str) - else: - pipe.sadd(cache_key, "__EMPTY_SET__") # Marker for empty set - pipe.expire(cache_key, 3600) # Cache for 1 hour - await pipe.execute() - except Exception as e: - log.exception(f"Redis error setting cache for cmd perms '{command_name}' (Guild: {guild_id}): {e}") + if bot.redis: + try: + async with bot.redis.pipeline(transaction=True) as pipe: + pipe.delete(cache_key) # Ensure clean state + if allowed_role_ids_str: + pipe.sadd(cache_key, *allowed_role_ids_str) + else: + pipe.sadd(cache_key, "__EMPTY_SET__") # Marker for empty set + pipe.expire(cache_key, 3600) # Cache for 1 hour + await pipe.execute() + except Exception as e: + log.exception(f"Redis error setting cache for cmd perms '{command_name}' (Guild: {guild_id}): {e}") except Exception as e: log.exception(f"Error checking command permission for '{command_name}' (Guild: {guild_id}): {e}") @@ -1392,15 +1399,16 @@ async def check_command_permission(guild_id: int, command_name: str, member_role async def get_command_permissions(guild_id: int, command_name: str) -> set[int] | None: """Gets the set of allowed role IDs for a specific command, checking cache first. Returns None on error.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot get permissions for command '{command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for get_command_permissions (guild {guild_id}, command '{command_name}').") return None cache_key = _get_redis_key(guild_id, "cmd_perms", command_name) try: # Check cache first - if await _active_redis_pool.exists(cache_key): - cached_roles_str = await _active_redis_pool.smembers(cache_key) + if await bot.redis.exists(cache_key): + cached_roles_str = await bot.redis.smembers(cache_key) if cached_roles_str == {"__EMPTY_SET__"}: log.debug(f"Cache hit (empty set) for cmd perms '{command_name}' (Guild: {guild_id}).") return set() # Return empty set if explicitly empty @@ -1413,7 +1421,7 @@ async def get_command_permissions(guild_id: int, command_name: str) -> set[int] log.debug(f"Cache miss for cmd perms '{command_name}' (Guild: {guild_id})") try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: records = await conn.fetch( "SELECT allowed_role_id FROM command_permissions WHERE guild_id = $1 AND command_name = $2", guild_id, command_name @@ -1421,18 +1429,19 @@ async def get_command_permissions(guild_id: int, command_name: str) -> set[int] allowed_role_ids = {record['allowed_role_id'] for record in records} # Cache the result - try: - allowed_role_ids_str = {str(role_id) for role_id in allowed_role_ids} - async with _active_redis_pool.pipeline(transaction=True) as pipe: - pipe.delete(cache_key) # Ensure clean state - if allowed_role_ids_str: - pipe.sadd(cache_key, *allowed_role_ids_str) - else: - pipe.sadd(cache_key, "__EMPTY_SET__") # Marker for empty set - pipe.expire(cache_key, 3600) # Cache for 1 hour - await pipe.execute() - except Exception as e: - log.exception(f"Redis error setting cache for cmd perms '{command_name}' (Guild: {guild_id}): {e}") + if bot.redis: + try: + allowed_role_ids_str = {str(role_id) for role_id in allowed_role_ids} + async with bot.redis.pipeline(transaction=True) as pipe: + pipe.delete(cache_key) # Ensure clean state + if allowed_role_ids_str: + pipe.sadd(cache_key, *allowed_role_ids_str) + else: + pipe.sadd(cache_key, "__EMPTY_SET__") # Marker for empty set + pipe.expire(cache_key, 3600) # Cache for 1 hour + await pipe.execute() + except Exception as e: + log.exception(f"Redis error setting cache for cmd perms '{command_name}' (Guild: {guild_id}): {e}") return allowed_role_ids except Exception as e: @@ -1468,8 +1477,9 @@ def _get_log_toggle_cache_key(guild_id: int) -> str: async def get_all_log_event_toggles(guild_id: int) -> Dict[str, bool]: """Gets all logging event toggle settings for a guild, checking cache first.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager, cannot get log toggles for guild {guild_id}.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager, cannot get log toggles for guild {guild_id}.") return {} cache_key = _get_log_toggle_cache_key(guild_id) @@ -1477,7 +1487,7 @@ async def get_all_log_event_toggles(guild_id: int) -> Dict[str, bool]: # Try cache first try: - cached_toggles = await asyncio.wait_for(_active_redis_pool.hgetall(cache_key), timeout=2.0) + cached_toggles = await asyncio.wait_for(bot.redis.hgetall(cache_key), timeout=2.0) if cached_toggles: log.debug(f"Cache hit for log toggles (Guild: {guild_id})") # Convert string bools back to boolean @@ -1490,7 +1500,7 @@ async def get_all_log_event_toggles(guild_id: int) -> Dict[str, bool]: # Cache miss or error, get from DB log.debug(f"Cache miss for log toggles (Guild: {guild_id})") try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: records = await conn.fetch( "SELECT event_key, enabled FROM logging_event_toggles WHERE guild_id = $1", guild_id @@ -1502,14 +1512,14 @@ async def get_all_log_event_toggles(guild_id: int) -> Dict[str, bool]: # Convert boolean values to strings for Redis Hash toggles_to_cache = {key: str(value) for key, value in toggles.items()} if toggles_to_cache: # Only set if there are toggles, otherwise cache remains empty - async with _active_redis_pool.pipeline(transaction=True) as pipe: + async with bot.redis.pipeline(transaction=True) as pipe: pipe.delete(cache_key) # Clear potentially stale data pipe.hset(cache_key, mapping=toggles_to_cache) pipe.expire(cache_key, 3600) # Cache for 1 hour await pipe.execute() else: # If DB is empty, ensure cache is also empty (or set a placeholder if needed) - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as e: log.exception(f"Redis error setting cache for log toggles (Guild: {guild_id}): {e}") @@ -1521,15 +1531,16 @@ async def get_all_log_event_toggles(guild_id: int) -> Dict[str, bool]: async def is_log_event_enabled(guild_id: int, event_key: str, default_enabled: bool = True) -> bool: """Checks if a specific logging event is enabled for a guild.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning default for log event '{event_key}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for guild {guild_id}, returning default for log event '{event_key}'.") return default_enabled cache_key = _get_log_toggle_cache_key(guild_id) # Try cache first try: - cached_value = await asyncio.wait_for(_active_redis_pool.hget(cache_key, event_key), timeout=2.0) + cached_value = await asyncio.wait_for(bot.redis.hget(cache_key, event_key), timeout=2.0) if cached_value is not None: # log.debug(f"Cache hit for log event '{event_key}' status (Guild: {guild_id})") return cached_value == 'True' @@ -1545,7 +1556,7 @@ async def is_log_event_enabled(guild_id: int, event_key: str, default_enabled: b # log.debug(f"Cache miss for log event '{event_key}' (Guild: {guild_id})") db_enabled_status = None try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: db_enabled_status = await conn.fetchval( "SELECT enabled FROM logging_event_toggles WHERE guild_id = $1 AND event_key = $2", guild_id, event_key @@ -1557,11 +1568,11 @@ async def is_log_event_enabled(guild_id: int, event_key: str, default_enabled: b if db_enabled_status is not None: # Only cache if it was explicitly set in DB try: await asyncio.wait_for( - _active_redis_pool.hset(cache_key, event_key, str(final_status)), + bot.redis.hset(cache_key, event_key, str(final_status)), timeout=2.0 ) # Ensure the hash key itself has an expiry - await _active_redis_pool.expire(cache_key, 3600, nx=True) # Set expiry only if it doesn't exist + await bot.redis.expire(cache_key, 3600, nx=True) # Set expiry only if it doesn't exist except asyncio.TimeoutError: log.warning(f"Redis timeout setting cache for log event '{event_key}' (Guild: {guild_id})") except Exception as e: @@ -1574,13 +1585,14 @@ async def is_log_event_enabled(guild_id: int, event_key: str, default_enabled: b async def set_log_event_enabled(guild_id: int, event_key: str, enabled: bool) -> bool: """Sets the enabled status for a specific logging event type.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set log event '{event_key}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for guild {guild_id}, cannot set log event '{event_key}'.") return False cache_key = _get_log_toggle_cache_key(guild_id) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Upsert the toggle status @@ -1594,16 +1606,16 @@ async def set_log_event_enabled(guild_id: int, event_key: str, enabled: bool) -> ) # Update cache - await _active_redis_pool.hset(cache_key, event_key, str(enabled)) + await bot.redis.hset(cache_key, event_key, str(enabled)) # Ensure the hash key itself has an expiry - await _active_redis_pool.expire(cache_key, 3600, nx=True) # Set expiry only if it doesn't exist + await bot.redis.expire(cache_key, 3600, nx=True) # Set expiry only if it doesn't exist log.info(f"Set log event '{event_key}' enabled status to {enabled} for guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error setting log event '{event_key}' in guild {guild_id}: {e}") # Attempt to invalidate cache field on error try: - await _active_redis_pool.hdel(cache_key, event_key) + await bot.redis.hdel(cache_key, event_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache field for log event '{event_key}' (Guild: {guild_id}): {redis_err}") return False @@ -1642,13 +1654,14 @@ async def get_bot_guild_ids() -> set[int] | None: async def get_custom_command_name(guild_id: int, original_command_name: str) -> str | None: """Gets the custom command name for a guild, checking cache first. Returns None if no custom name is set.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning None for custom command name '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for guild {guild_id}, returning None for custom command name '{original_command_name}'.") return None cache_key = _get_redis_key(guild_id, "cmd_custom", original_command_name) try: - cached_value = await _active_redis_pool.get(cache_key) + cached_value = await bot.redis.get(cache_key) if cached_value is not None: log.debug(f"Cache hit for custom command name '{original_command_name}' (Guild: {guild_id})") return None if cached_value == "__NONE__" else cached_value @@ -1656,7 +1669,7 @@ async def get_custom_command_name(guild_id: int, original_command_name: str) -> log.exception(f"Redis error getting custom command name for '{original_command_name}' (Guild: {guild_id}): {e}") log.debug(f"Cache miss for custom command name '{original_command_name}' (Guild: {guild_id})") - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: custom_name = await conn.fetchval( "SELECT custom_command_name FROM command_customization WHERE guild_id = $1 AND original_command_name = $2", guild_id, original_command_name @@ -1665,7 +1678,7 @@ async def get_custom_command_name(guild_id: int, original_command_name: str) -> # Cache the result (even if None) try: value_to_cache = custom_name if custom_name is not None else "__NONE__" - await _active_redis_pool.set(cache_key, value_to_cache, ex=3600) # Cache for 1 hour + await bot.redis.set(cache_key, value_to_cache, ex=3600) # Cache for 1 hour except Exception as e: log.exception(f"Redis error setting cache for custom command name '{original_command_name}' (Guild: {guild_id}): {e}") @@ -1675,13 +1688,14 @@ async def get_custom_command_name(guild_id: int, original_command_name: str) -> async def get_custom_command_description(guild_id: int, original_command_name: str) -> str | None: """Gets the custom command description for a guild, checking cache first. Returns None if no custom description is set.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning None for custom command description '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for guild {guild_id}, returning None for custom command description '{original_command_name}'.") return None cache_key = _get_redis_key(guild_id, "cmd_desc", original_command_name) try: - cached_value = await _active_redis_pool.get(cache_key) + cached_value = await bot.redis.get(cache_key) if cached_value is not None: log.debug(f"Cache hit for custom command description '{original_command_name}' (Guild: {guild_id})") return None if cached_value == "__NONE__" else cached_value @@ -1689,7 +1703,7 @@ async def get_custom_command_description(guild_id: int, original_command_name: s log.exception(f"Redis error getting custom command description for '{original_command_name}' (Guild: {guild_id}): {e}") log.debug(f"Cache miss for custom command description '{original_command_name}' (Guild: {guild_id})") - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: custom_desc = await conn.fetchval( "SELECT custom_command_description FROM command_customization WHERE guild_id = $1 AND original_command_name = $2", guild_id, original_command_name @@ -1698,7 +1712,7 @@ async def get_custom_command_description(guild_id: int, original_command_name: s # Cache the result (even if None) try: value_to_cache = custom_desc if custom_desc is not None else "__NONE__" - await _active_redis_pool.set(cache_key, value_to_cache, ex=3600) # Cache for 1 hour + await bot.redis.set(cache_key, value_to_cache, ex=3600) # Cache for 1 hour except Exception as e: log.exception(f"Redis error setting cache for custom command description '{original_command_name}' (Guild: {guild_id}): {e}") @@ -1708,13 +1722,14 @@ async def get_custom_command_description(guild_id: int, original_command_name: s async def set_custom_command_name(guild_id: int, original_command_name: str, custom_command_name: str | None) -> bool: """Sets a custom command name for a guild and updates the cache. Setting custom_command_name to None removes the customization.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set custom command name for '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for guild {guild_id}, cannot set custom command name for '{original_command_name}'.") return False cache_key = _get_redis_key(guild_id, "cmd_custom", original_command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) @@ -1729,7 +1744,7 @@ async def set_custom_command_name(guild_id: int, original_command_name: str, cus guild_id, original_command_name, custom_command_name ) # Update cache - await _active_redis_pool.set(cache_key, custom_command_name, ex=3600) + await bot.redis.set(cache_key, custom_command_name, ex=3600) log.info(f"Set custom command name for '{original_command_name}' to '{custom_command_name}' for guild {guild_id}") else: # Delete the customization if value is None @@ -1738,7 +1753,7 @@ async def set_custom_command_name(guild_id: int, original_command_name: str, cus guild_id, original_command_name ) # Update cache to indicate no customization - await _active_redis_pool.set(cache_key, "__NONE__", ex=3600) + await bot.redis.set(cache_key, "__NONE__", ex=3600) log.info(f"Removed custom command name for '{original_command_name}' for guild {guild_id}") return True @@ -1746,7 +1761,7 @@ async def set_custom_command_name(guild_id: int, original_command_name: str, cus log.exception(f"Database or Redis error setting custom command name for '{original_command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache on error try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for custom command name '{original_command_name}' (Guild: {guild_id}): {redis_err}") return False @@ -1755,13 +1770,14 @@ async def set_custom_command_name(guild_id: int, original_command_name: str, cus async def set_custom_command_description(guild_id: int, original_command_name: str, custom_command_description: str | None) -> bool: """Sets a custom command description for a guild and updates the cache. Setting custom_command_description to None removes the description.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set custom command description for '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for guild {guild_id}, cannot set custom command description for '{original_command_name}'.") return False cache_key = _get_redis_key(guild_id, "cmd_desc", original_command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) @@ -1792,7 +1808,7 @@ async def set_custom_command_description(guild_id: int, original_command_name: s guild_id, original_command_name, custom_command_description ) # Update cache - await _active_redis_pool.set(cache_key, custom_command_description, ex=3600) + await bot.redis.set(cache_key, custom_command_description, ex=3600) log.info(f"Set custom command description for '{original_command_name}' for guild {guild_id}") else: if exists: @@ -1806,7 +1822,7 @@ async def set_custom_command_description(guild_id: int, original_command_name: s guild_id, original_command_name ) # Update cache to indicate no description - await _active_redis_pool.set(cache_key, "__NONE__", ex=3600) + await bot.redis.set(cache_key, "__NONE__", ex=3600) log.info(f"Removed custom command description for '{original_command_name}' for guild {guild_id}") return True @@ -1814,7 +1830,7 @@ async def set_custom_command_description(guild_id: int, original_command_name: s log.exception(f"Database or Redis error setting custom command description for '{original_command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache on error try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for custom command description '{original_command_name}' (Guild: {guild_id}): {redis_err}") return False @@ -1823,13 +1839,14 @@ async def set_custom_command_description(guild_id: int, original_command_name: s async def get_custom_group_name(guild_id: int, original_group_name: str) -> str | None: """Gets the custom command group name for a guild, checking cache first. Returns None if no custom name is set.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning None for custom group name '{original_group_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for guild {guild_id}, returning None for custom group name '{original_group_name}'.") return None cache_key = _get_redis_key(guild_id, "group_custom", original_group_name) try: - cached_value = await _active_redis_pool.get(cache_key) + cached_value = await bot.redis.get(cache_key) if cached_value is not None: log.debug(f"Cache hit for custom group name '{original_group_name}' (Guild: {guild_id})") return None if cached_value == "__NONE__" else cached_value @@ -1837,7 +1854,7 @@ async def get_custom_group_name(guild_id: int, original_group_name: str) -> str log.exception(f"Redis error getting custom group name for '{original_group_name}' (Guild: {guild_id}): {e}") log.debug(f"Cache miss for custom group name '{original_group_name}' (Guild: {guild_id})") - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: custom_name = await conn.fetchval( "SELECT custom_group_name FROM command_group_customization WHERE guild_id = $1 AND original_group_name = $2", guild_id, original_group_name @@ -1846,7 +1863,7 @@ async def get_custom_group_name(guild_id: int, original_group_name: str) -> str # Cache the result (even if None) try: value_to_cache = custom_name if custom_name is not None else "__NONE__" - await _active_redis_pool.set(cache_key, value_to_cache, ex=3600) # Cache for 1 hour + await bot.redis.set(cache_key, value_to_cache, ex=3600) # Cache for 1 hour except Exception as e: log.exception(f"Redis error setting cache for custom group name '{original_group_name}' (Guild: {guild_id}): {e}") @@ -1856,13 +1873,14 @@ async def get_custom_group_name(guild_id: int, original_group_name: str) -> str async def set_custom_group_name(guild_id: int, original_group_name: str, custom_group_name: str | None) -> bool: """Sets a custom command group name for a guild and updates the cache. Setting custom_group_name to None removes the customization.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot set custom group name for '{original_group_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for guild {guild_id}, cannot set custom group name for '{original_group_name}'.") return False cache_key = _get_redis_key(guild_id, "group_custom", original_group_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) @@ -1877,7 +1895,7 @@ async def set_custom_group_name(guild_id: int, original_group_name: str, custom_ guild_id, original_group_name, custom_group_name ) # Update cache - await _active_redis_pool.set(cache_key, custom_group_name, ex=3600) + await bot.redis.set(cache_key, custom_group_name, ex=3600) log.info(f"Set custom group name for '{original_group_name}' to '{custom_group_name}' for guild {guild_id}") else: # Delete the customization if value is None @@ -1886,7 +1904,7 @@ async def set_custom_group_name(guild_id: int, original_group_name: str, custom_ guild_id, original_group_name ) # Update cache to indicate no customization - await _active_redis_pool.set(cache_key, "__NONE__", ex=3600) + await bot.redis.set(cache_key, "__NONE__", ex=3600) log.info(f"Removed custom group name for '{original_group_name}' for guild {guild_id}") return True @@ -1894,7 +1912,7 @@ async def set_custom_group_name(guild_id: int, original_group_name: str, custom_ log.exception(f"Database or Redis error setting custom group name for '{original_group_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache on error try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for custom group name '{original_group_name}' (Guild: {guild_id}): {redis_err}") return False @@ -1902,13 +1920,14 @@ async def set_custom_group_name(guild_id: int, original_group_name: str, custom_ async def add_command_alias(guild_id: int, original_command_name: str, alias_name: str) -> bool: """Adds an alias for a command in a guild and invalidates cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot add alias for command '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for guild {guild_id}, cannot add alias for command '{original_command_name}'.") return False cache_key = _get_redis_key(guild_id, "cmd_aliases", original_command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Ensure guild exists await conn.execute("INSERT INTO guilds (guild_id) VALUES ($1) ON CONFLICT (guild_id) DO NOTHING;", guild_id) # Add the alias @@ -1922,14 +1941,14 @@ async def add_command_alias(guild_id: int, original_command_name: str, alias_nam ) # Invalidate cache after DB operation succeeds - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) log.info(f"Added alias '{alias_name}' for command '{original_command_name}' in guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error adding alias for command '{original_command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache even on error try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for command aliases '{original_command_name}' (Guild: {guild_id}): {redis_err}") return False @@ -1937,13 +1956,14 @@ async def add_command_alias(guild_id: int, original_command_name: str, alias_nam async def remove_command_alias(guild_id: int, original_command_name: str, alias_name: str) -> bool: """Removes an alias for a command in a guild and invalidates cache.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.error(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, cannot remove alias for command '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.error(f"Bot instance or pools not available in settings_manager for guild {guild_id}, cannot remove alias for command '{original_command_name}'.") return False cache_key = _get_redis_key(guild_id, "cmd_aliases", original_command_name) try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: # Remove the alias await conn.execute( """ @@ -1954,14 +1974,14 @@ async def remove_command_alias(guild_id: int, original_command_name: str, alias_ ) # Invalidate cache after DB operation succeeds - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) log.info(f"Removed alias '{alias_name}' for command '{original_command_name}' in guild {guild_id}") return True except Exception as e: log.exception(f"Database or Redis error removing alias for command '{original_command_name}' in guild {guild_id}: {e}") # Attempt to invalidate cache even on error try: - await _active_redis_pool.delete(cache_key) + await bot.redis.delete(cache_key) except Exception as redis_err: log.exception(f"Failed to invalidate Redis cache for command aliases '{original_command_name}' (Guild: {guild_id}): {redis_err}") return False @@ -1970,14 +1990,15 @@ async def remove_command_alias(guild_id: int, original_command_name: str, alias_ async def get_command_aliases(guild_id: int, original_command_name: str) -> list[str] | None: """Gets the list of aliases for a command in a guild, checking cache first. Returns empty list if no aliases are set, None on error.""" - if _active_pg_pool is None or _active_redis_pool is None: - log.warning(f"Pools not initialized (PG: {id(_active_pg_pool)}, Redis: {id(_active_redis_pool)}) in settings_manager for guild {guild_id}, returning None for command aliases '{original_command_name}'.") + bot = get_bot_instance() + if not bot or not bot.pg_pool or not bot.redis: + log.warning(f"Bot instance or pools not available in settings_manager for guild {guild_id}, returning None for command aliases '{original_command_name}'.") return None cache_key = _get_redis_key(guild_id, "cmd_aliases", original_command_name) try: # Check cache first - cached_aliases = await _active_redis_pool.lrange(cache_key, 0, -1) + cached_aliases = await bot.redis.lrange(cache_key, 0, -1) if cached_aliases is not None: if len(cached_aliases) == 1 and cached_aliases[0] == "__EMPTY_LIST__": log.debug(f"Cache hit (empty list) for command aliases '{original_command_name}' (Guild: {guild_id}).") @@ -1990,7 +2011,7 @@ async def get_command_aliases(guild_id: int, original_command_name: str) -> list log.debug(f"Cache miss for command aliases '{original_command_name}' (Guild: {guild_id})") try: - async with _active_pg_pool.acquire() as conn: + async with bot.pg_pool.acquire() as conn: records = await conn.fetch( "SELECT alias_name FROM command_aliases WHERE guild_id = $1 AND original_command_name = $2", guild_id, original_command_name @@ -1999,7 +2020,7 @@ async def get_command_aliases(guild_id: int, original_command_name: str) -> list # Cache the result try: - async with _active_redis_pool.pipeline(transaction=True) as pipe: + async with bot.redis.pipeline(transaction=True) as pipe: pipe.delete(cache_key) # Ensure clean state if aliases: pipe.rpush(cache_key, *aliases) @@ -2119,12 +2140,12 @@ async def set_mod_log_channel_id(guild_id: int, channel_id: int | None) -> bool: return await set_setting(guild_id, 'mod_log_channel_id', value_to_set) # --- Getter functions for direct pool access if absolutely needed --- -def get_pg_pool(): - """Returns the active PostgreSQL pool instance.""" - log.debug(f"get_pg_pool called. Returning _active_pg_pool with ID: {id(_active_pg_pool)}") - return _active_pg_pool +# def get_pg_pool(): # Removed +# """Returns the active PostgreSQL pool instance.""" +# log.debug(f"get_pg_pool called. Returning _active_pg_pool with ID: {id(_active_pg_pool)}") +# return _active_pg_pool -def get_redis_pool(): - """Returns the active Redis pool instance.""" - log.debug(f"get_redis_pool called. Returning _active_redis_pool with ID: {id(_active_redis_pool)}") - return _active_redis_pool +# def get_redis_pool(): # Removed +# """Returns the active Redis pool instance.""" +# log.debug(f"get_redis_pool called. Returning _active_redis_pool with ID: {id(_active_redis_pool)}") +# return _active_redis_pool