From 898937acb4f0508ef6279ff23e283b2b4638ba46 Mon Sep 17 00:00:00 2001 From: pacnpal <183241239+pacnpal@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:28:51 -0500 Subject: [PATCH] Update .gitignore to exclude additional Python cache files; implement reset personality command with user permissions check --- .gitignore | 12 +++++ discord_glhf/bot.py | 21 ++++++--- discord_glhf/config.py | 5 +++ discord_glhf/database.py | 12 +++++ discord_glhf/handlers/event_handler.py | 62 ++++++++++++++++++++------ 5 files changed, 93 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 439a503..1979375 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,15 @@ discord_glhf/handlers/__pycache__/message_handler.cpython-313.pyc discord_glhf/handlers/__pycache__/tool_handler.cpython-313.pyc discord_glhf/web/__pycache__/app.cpython-313.pyc .env +discord_glhf/__pycache__/__init__.cpython-313.pyc +discord_glhf/__pycache__/config.cpython-313.pyc +discord_glhf/__pycache__/database.cpython-313.pyc +discord_glhf/__pycache__/main.cpython-313.pyc +discord_glhf/__pycache__/queue_manager.cpython-313.pyc +discord_glhf/__pycache__/queue_state.cpython-313.pyc +discord_glhf/__pycache__/queue.cpython-313.pyc +discord_glhf/__pycache__/training.cpython-313.pyc +discord_glhf/handlers/event_handler.py +discord_glhf/handlers/__pycache__/__init__.cpython-313.pyc +discord_glhf/handlers/__pycache__/image_handler.cpython-313.pyc +discord_glhf/web/__pycache__/__init__.cpython-313.pyc diff --git a/discord_glhf/bot.py b/discord_glhf/bot.py index 8667010..45c6b7f 100644 --- a/discord_glhf/bot.py +++ b/discord_glhf/bot.py @@ -40,6 +40,7 @@ class DiscordBot: self.db_pool = DatabasePool() self.db_manager = DatabaseManager(self.db_pool) self._initialized = False + self._running = True self._init_lock = asyncio.Lock() # Initialize handler references @@ -251,20 +252,28 @@ class DiscordBot: logger.error( f"Error before event_handler initialization: {exc_value}") + self._running = True try: async with self.bot: await self.bot.start(token) - while True: + while self._running: try: await self._handle_connection(token) except (aiohttp.ClientError, socket.gaierror) as e: - logger.error(f"Connection error: {e}") - await asyncio.sleep(5) # Wait before reconnecting + if self._running: # Only log and retry if we're still meant to be running + logger.error(f"Connection error: {e}") + await asyncio.sleep(5) # Wait before reconnecting + else: + break except KeyboardInterrupt: - raise + self._running = False + break except Exception as e: - logger.error(f"Unexpected error: {e}") - await asyncio.sleep(5) # Wait before reconnecting + if self._running: + logger.error(f"Unexpected error: {e}") + await asyncio.sleep(5) # Wait before reconnecting + else: + break except Exception as e: logger.error(f"Failed to start bot: {e}") raise diff --git a/discord_glhf/config.py b/discord_glhf/config.py index 8027c81..d08505c 100644 --- a/discord_glhf/config.py +++ b/discord_glhf/config.py @@ -198,6 +198,11 @@ BOT_OWNER_ID = int(os.getenv("BOT_OWNER_ID")) # Required AUTO_RESPONSE_CHANNEL_ID = int( os.getenv("AUTO_RESPONSE_CHANNEL_ID")) # Required +# Get allowed users for reset command +RESET_ALLOWED_USERS = [int(id.strip()) for id in os.getenv("RESET_ALLOWED_USERS", "").split(",") if id.strip()] +if not RESET_ALLOWED_USERS: + RESET_ALLOWED_USERS = [BOT_OWNER_ID] # Default to bot owner if not configured + # Load system prompt SYSTEM_PROMPT = os.getenv("SYSTEM_PROMPT") or load_system_prompt() diff --git a/discord_glhf/database.py b/discord_glhf/database.py index 3f0e458..fd1e7b4 100644 --- a/discord_glhf/database.py +++ b/discord_glhf/database.py @@ -605,6 +605,18 @@ class DatabaseManager: except Exception as e: logger.error(f"Failed to update thread activity: {e}") + async def clear_all_messages(self) -> None: + """Clear all messages from the database.""" + try: + async with self.pool.acquire() as conn: + async with conn.cursor() as cursor: + await cursor.execute("DELETE FROM messages") + await conn.commit() + logger.info("All message history cleared") + except Exception as e: + logger.error(f"Failed to clear message history: {e}") + raise + async def cleanup_old_messages(self): """Clean up messages older than MESSAGE_CLEANUP_DAYS.""" cleanup_date = datetime.now() - timedelta(days=MESSAGE_CLEANUP_DAYS) diff --git a/discord_glhf/handlers/event_handler.py b/discord_glhf/handlers/event_handler.py index 0b6a834..317d63e 100644 --- a/discord_glhf/handlers/event_handler.py +++ b/discord_glhf/handlers/event_handler.py @@ -9,7 +9,7 @@ from datetime import datetime from discord import Message, RawReactionActionEvent from ..config import ( - logger, AUTO_RESPONSE_CHANNEL_ID, SYSTEM_PROMPT, BOT_OWNER_ID + logger, AUTO_RESPONSE_CHANNEL_ID, SYSTEM_PROMPT, BOT_OWNER_ID, RESET_ALLOWED_USERS ) from .message_handler import MessageHandler from .image_handler import ImageHandler @@ -41,7 +41,7 @@ class EventHandler: f"<@{mention[2:-1]}>" # Raw mention format ] pattern = '|'.join(patterns) - + # Replace all mention formats with the proper mention return re.sub(pattern, mention, response) @@ -150,6 +150,7 @@ class EventHandler: history = await self.db_manager.get_conversation_history( user_id=0, channel_id=payload.channel_id, + limit=50 # Limit to recent context ) logger.debug(f"Retrieved {len(history)} messages for context") @@ -202,7 +203,8 @@ class EventHandler: created_at=datetime.utcnow() ) - logger.info(f"Adding reaction response to queue from {user.display_name}") + logger.info( + f"Adding reaction response to queue from {user.display_name}") # Queue the reaction like a regular message await self.queue_manager.add_message( channel=channel, @@ -222,6 +224,30 @@ class EventHandler: return try: + # Handle !reset_personality command + if message.content.strip() == "!reset_personality": + # Check if user is allowed (in allowed list, has admin, or is bot owner) + is_allowed = ( + message.author.id in RESET_ALLOWED_USERS or + message.author.id == BOT_OWNER_ID or + (hasattr(message.author, 'guild_permissions') and message.author.guild_permissions.administrator) + ) + + if not is_allowed: + logger.warning(f"Unauthorized reset attempt by {message.author.name} ({message.author.id})") + return + + try: + # React with checkmark + await message.add_reaction("✅") + # Clear all messages + await self.db_manager.clear_all_messages() + logger.info(f"Personality reset by {message.author.name} ({message.author.id})") + return + except Exception as e: + logger.error(f"Failed to reset personality: {e}") + return + # Only respond in configured channel or its threads if (message.channel.id != AUTO_RESPONSE_CHANNEL_ID and (not hasattr(message.channel, 'parent_id') or @@ -230,7 +256,8 @@ class EventHandler: # Early duplicate checks before any processing if any(item.message.id == message.id for item in self.queue_manager.message_queue.processing): - logger.debug(f"Message {message.id} already in processing, skipping") + logger.debug( + f"Message {message.id} already in processing, skipping") return # Get current queue size @@ -244,7 +271,8 @@ class EventHandler: message_id=message.id ) if message_processed: - logger.debug(f"Message {message.id} already processed, skipping") + logger.debug( + f"Message {message.id} already processed, skipping") return # Check for duplicate content in history @@ -257,7 +285,8 @@ class EventHandler: hist_content = hist_msg.get("content", {}).get("content", "") if isinstance( hist_msg.get("content"), dict) else hist_msg.get("content", "") if hist_content == current_content: - logger.debug(f"Duplicate message content detected for message {message.id}, skipping") + logger.debug( + f"Duplicate message content detected for message {message.id}, skipping") return # Update user activity in database @@ -351,7 +380,8 @@ class EventHandler: if hist_content == formatted_content: message_in_history = True if isinstance(hist_msg.get("metadata"), dict): - stored_uuid = hist_msg["metadata"].get("message_uuid") + stored_uuid = hist_msg["metadata"].get( + "message_uuid") break # Use stored UUID if found, otherwise use new UUID @@ -381,7 +411,8 @@ class EventHandler: await self.store_message( user_id=item.message.author.id, role="user", - content={"content": formatted_content, "metadata": message_metadata}, + content={"content": formatted_content, + "metadata": message_metadata}, channel_id=item.channel.id, message_uuid=message_uuid, ) @@ -404,7 +435,10 @@ class EventHandler: } messages = [system_message] - messages.extend(history) + + # Include conversation history + if history: + messages.extend(history) # Always add current message to the API call # Add timeout_env to the context @@ -483,7 +517,8 @@ class EventHandler: ) if thread_id: await self.db_manager.update_thread_activity(thread_id) - logger.info(f"Created and stored thread '{args['name']}' in database") + logger.info( + f"Created and stored thread '{args['name']}' in database") except Exception as e: logger.error(f"Error executing tool {tool_name}: {e}") @@ -491,7 +526,8 @@ class EventHandler: # Send the response if final_response: author = item.message.author - owner_tag = ' [BOT OWNER]' if int(author.id) == BOT_OWNER_ID else '' + owner_tag = ' [BOT OWNER]' if int( + author.id) == BOT_OWNER_ID else '' logger.info( f"Bot response to {author.display_name} " f"({author.name}#{author.discriminator})" @@ -501,7 +537,7 @@ class EventHandler: reference = None if hasattr(item.message, '_state'): # Check if it's a real Discord Message reference = item.message - + sent_message = await self.message_handler.safe_send( item.channel, final_response, reference=reference ) @@ -545,7 +581,7 @@ class EventHandler: source_info = {"type": "web"} else: source_info = {"type": "discord"} - + response_metadata = { "response_id": response_uuid, "user_info": {