Update .gitignore to exclude additional Python cache files; implement reset personality command with user permissions check

This commit is contained in:
pacnpal
2025-02-25 12:28:51 -05:00
parent de3b09bf46
commit 898937acb4
5 changed files with 93 additions and 19 deletions

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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": {