aaa
This commit is contained in:
parent
9c60dd70fa
commit
33e15df5be
@ -498,7 +498,7 @@ async def get_ai_response(cog: 'GurtCog', message: discord.Message, model: Optio
|
||||
messages_for_follow_up.extend(tool_results_for_api)
|
||||
messages_for_follow_up.append({
|
||||
"role": "user",
|
||||
"content": f"Okay, the requested tools have been executed. Here are the results. Now, generate the final user-facing response based on these results and the previous conversation context. **CRITICAL: Your response MUST be ONLY the raw JSON object matching the standard schema (should_respond, content, react_with_emoji). Do NOT include the 'tool_requests' field this time.**\n\n**Ensure nothing precedes or follows the JSON.**{message_length_guidance}"
|
||||
"content": f"Okay, the requested tools have been executed. Here are the results. Now, generate the final user-facing response based on these results and the previous conversation context. **CRITICAL: Your response MUST be ONLY the raw JSON object matching the standard schema (should_respond, content, react_with_emoji). Do NOT include the 'tool_requests' field this time.**\n\n**IMPORTANT: If you set 'should_respond' to true, you MUST provide a non-empty string for the 'content' field.**\n\n**Ensure nothing precedes or follows the JSON.**{message_length_guidance}"
|
||||
})
|
||||
|
||||
follow_up_payload = {
|
||||
|
38
gurt/cog.py
38
gurt/cog.py
@ -131,12 +131,14 @@ class GurtCog(commands.Cog, name="Gurt"): # Added explicit Cog name
|
||||
|
||||
# --- Setup Commands and Listeners ---
|
||||
# Add commands defined in commands.py
|
||||
setup_commands(self)
|
||||
self.command_functions = setup_commands(self)
|
||||
# Store command functions for reference
|
||||
self.registered_commands = [func.__name__ for func in self.command_functions]
|
||||
# Add listeners defined in listeners.py
|
||||
# Note: Listeners need to be added to the bot instance, not the cog directly in this pattern.
|
||||
# We'll add them in cog_load or the main setup function.
|
||||
|
||||
print("GurtCog initialized.")
|
||||
print(f"GurtCog initialized with commands: {self.registered_commands}")
|
||||
|
||||
async def cog_load(self):
|
||||
"""Create aiohttp session, initialize DB, load baselines, start background task"""
|
||||
@ -177,6 +179,20 @@ class GurtCog(commands.Cog, name="Gurt"): # Added explicit Cog name
|
||||
|
||||
print("GurtCog: Listeners added.")
|
||||
|
||||
# Sync commands with Discord
|
||||
try:
|
||||
print("GurtCog: Syncing commands with Discord...")
|
||||
synced = await self.bot.tree.sync()
|
||||
print(f"GurtCog: Synced {len(synced)} command(s)")
|
||||
|
||||
# List the synced commands
|
||||
gurt_commands = [cmd.name for cmd in self.bot.tree.get_commands() if cmd.name.startswith("gurt")]
|
||||
print(f"GurtCog: Available Gurt commands: {', '.join(gurt_commands)}")
|
||||
except Exception as e:
|
||||
print(f"GurtCog: Failed to sync commands: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Start background task
|
||||
if self.background_task is None or self.background_task.done():
|
||||
self.background_task = asyncio.create_task(background_processing_task(self))
|
||||
@ -314,6 +330,24 @@ class GurtCog(commands.Cog, name="Gurt"): # Added explicit Cog name
|
||||
|
||||
return stats
|
||||
|
||||
async def sync_commands(self):
|
||||
"""Manually sync commands with Discord."""
|
||||
try:
|
||||
print("GurtCog: Manually syncing commands with Discord...")
|
||||
synced = await self.bot.tree.sync()
|
||||
print(f"GurtCog: Synced {len(synced)} command(s)")
|
||||
|
||||
# List the synced commands
|
||||
gurt_commands = [cmd.name for cmd in self.bot.tree.get_commands() if cmd.name.startswith("gurt")]
|
||||
print(f"GurtCog: Available Gurt commands: {', '.join(gurt_commands)}")
|
||||
|
||||
return synced, gurt_commands
|
||||
except Exception as e:
|
||||
print(f"GurtCog: Failed to sync commands: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return [], []
|
||||
|
||||
|
||||
# Setup function for loading the cog
|
||||
async def setup(bot):
|
||||
|
@ -125,7 +125,10 @@ def format_stats_embeds(stats: Dict[str, Any]) -> List[discord.Embed]:
|
||||
def setup_commands(cog: 'GurtCog'):
|
||||
"""Adds Gurt-specific commands to the cog."""
|
||||
|
||||
# Example using app_commands - adapt existing commands if needed
|
||||
# Create a list to store command functions for proper registration
|
||||
command_functions = []
|
||||
|
||||
# --- Gurt Mood Command ---
|
||||
@cog.bot.tree.command(name="gurtmood", description="Check or set Gurt's current mood.")
|
||||
@app_commands.describe(mood="Optional: Set Gurt's mood to one of the available options.")
|
||||
@app_commands.choices(mood=[
|
||||
@ -133,6 +136,11 @@ def setup_commands(cog: 'GurtCog'):
|
||||
])
|
||||
async def gurtmood(interaction: discord.Interaction, mood: Optional[app_commands.Choice[str]] = None):
|
||||
"""Handles the /gurtmood command."""
|
||||
# Check if user is the bot owner for mood setting
|
||||
if mood and interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.response.send_message("⛔ Only the bot owner can change Gurt's mood.", ephemeral=True)
|
||||
return
|
||||
|
||||
if mood:
|
||||
cog.current_mood = mood.value
|
||||
cog.last_mood_change = time.time()
|
||||
@ -141,6 +149,9 @@ def setup_commands(cog: 'GurtCog'):
|
||||
time_since_change = time.time() - cog.last_mood_change
|
||||
await interaction.response.send_message(f"Gurt's current mood is: {cog.current_mood} (Set {int(time_since_change // 60)} minutes ago)", ephemeral=True)
|
||||
|
||||
command_functions.append(gurtmood)
|
||||
|
||||
# --- Gurt Memory Command ---
|
||||
@cog.bot.tree.command(name="gurtmemory", description="Interact with Gurt's memory.")
|
||||
@app_commands.describe(
|
||||
action="Choose an action: add_user, add_general, get_user, get_general",
|
||||
@ -161,6 +172,11 @@ def setup_commands(cog: 'GurtCog'):
|
||||
target_user_id = str(user.id) if user else None
|
||||
action_value = action.value
|
||||
|
||||
# Check if user is the bot owner for modification actions
|
||||
if (action_value in ["add_user", "add_general"]) and interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.followup.send("⛔ Only the bot owner can add facts to Gurt's memory.", ephemeral=True)
|
||||
return
|
||||
|
||||
if action_value == "add_user":
|
||||
if not target_user_id or not fact:
|
||||
await interaction.followup.send("Please provide both a user and a fact to add.", ephemeral=True)
|
||||
@ -207,10 +223,17 @@ def setup_commands(cog: 'GurtCog'):
|
||||
else:
|
||||
await interaction.followup.send("Invalid action specified.", ephemeral=True)
|
||||
|
||||
command_functions.append(gurtmemory)
|
||||
|
||||
# --- Gurt Stats Command ---
|
||||
@cog.bot.tree.command(name="gurtstats", description="Display Gurt's internal statistics.")
|
||||
@cog.bot.tree.command(name="gurtstats", description="Display Gurt's internal statistics. (Owner only)")
|
||||
async def gurtstats(interaction: discord.Interaction):
|
||||
"""Handles the /gurtstats command."""
|
||||
# Check if user is the bot owner
|
||||
if interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.response.send_message("⛔ Only the bot owner can view detailed stats.", ephemeral=True)
|
||||
return
|
||||
|
||||
await interaction.response.defer(ephemeral=True) # Defer as stats collection might take time
|
||||
try:
|
||||
stats_data = await cog.get_gurt_stats()
|
||||
@ -222,5 +245,38 @@ def setup_commands(cog: 'GurtCog'):
|
||||
traceback.print_exc()
|
||||
await interaction.followup.send("An error occurred while fetching Gurt's stats.", ephemeral=True)
|
||||
|
||||
command_functions.append(gurtstats)
|
||||
|
||||
print("Gurt commands setup in cog.")
|
||||
# --- Sync Gurt Commands (Owner Only) ---
|
||||
@cog.bot.tree.command(name="gurtsync", description="Sync Gurt commands with Discord (Owner only)")
|
||||
async def gurtsync(interaction: discord.Interaction):
|
||||
"""Handles the /gurtsync command to force sync commands."""
|
||||
# Check if user is the bot owner
|
||||
if interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.response.send_message("⛔ Only the bot owner can sync commands.", ephemeral=True)
|
||||
return
|
||||
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
try:
|
||||
# Sync commands
|
||||
synced = await cog.bot.tree.sync()
|
||||
|
||||
# Get list of commands after sync
|
||||
commands_after = []
|
||||
for cmd in cog.bot.tree.get_commands():
|
||||
if cmd.name.startswith("gurt"):
|
||||
commands_after.append(cmd.name)
|
||||
|
||||
await interaction.followup.send(f"✅ Successfully synced {len(synced)} commands!\nGurt commands: {', '.join(commands_after)}", ephemeral=True)
|
||||
except Exception as e:
|
||||
print(f"Error in /gurtsync command: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
await interaction.followup.send(f"❌ Error syncing commands: {str(e)}", ephemeral=True)
|
||||
|
||||
command_functions.append(gurtsync)
|
||||
|
||||
print(f"Gurt commands setup in cog: {[func.__name__ for func in command_functions]}")
|
||||
|
||||
# Return the command functions for proper registration
|
||||
return command_functions
|
||||
|
@ -240,62 +240,92 @@ async def on_message_listener(cog: 'GurtCog', message: discord.Message):
|
||||
await message.channel.send(random.choice(["...", "*confused gurting*", "brain broke sorry"]))
|
||||
return
|
||||
|
||||
# Determine which response to use (prefer final if available)
|
||||
response_to_use = final_response if final_response else initial_response
|
||||
# --- Process and Send Responses ---
|
||||
sent_any_message = False
|
||||
reacted = False
|
||||
|
||||
if response_to_use and isinstance(response_to_use, dict):
|
||||
sent_message = False
|
||||
reacted = False
|
||||
|
||||
# Handle Reaction
|
||||
emoji_to_react = response_to_use.get("react_with_emoji")
|
||||
if emoji_to_react and isinstance(emoji_to_react, str):
|
||||
try:
|
||||
# Basic validation for standard emoji
|
||||
if 1 <= len(emoji_to_react) <= 4 and not re.match(r'<a?:.+?:\d+>', emoji_to_react):
|
||||
await message.add_reaction(emoji_to_react)
|
||||
reacted = True
|
||||
print(f"Bot reacted to message {message.id} with {emoji_to_react}")
|
||||
else: print(f"Invalid emoji format: {emoji_to_react}")
|
||||
except Exception as e: print(f"Error adding reaction '{emoji_to_react}': {e}")
|
||||
|
||||
# Handle Text Response
|
||||
if response_to_use.get("should_respond") and response_to_use.get("content"):
|
||||
response_text = response_to_use["content"]
|
||||
# Helper function to handle sending a single response text and caching
|
||||
async def send_response_content(response_data: Optional[Dict[str, Any]], response_label: str) -> bool:
|
||||
nonlocal sent_any_message # Allow modification of the outer scope variable
|
||||
if response_data and isinstance(response_data, dict) and \
|
||||
response_data.get("should_respond") and response_data.get("content"):
|
||||
response_text = response_data["content"]
|
||||
print(f"Attempting to send {response_label} content...")
|
||||
if len(response_text) > 1900:
|
||||
filepath = f'gurt_response_{message.id}.txt'
|
||||
filepath = f'gurt_{response_label}_{message.id}.txt'
|
||||
try:
|
||||
with open(filepath, 'w', encoding='utf-8') as f: f.write(response_text)
|
||||
await message.channel.send("Response too long:", file=discord.File(filepath))
|
||||
sent_message = True
|
||||
except Exception as file_e: print(f"Error writing/sending long response file: {file_e}")
|
||||
await message.channel.send(f"{response_label.capitalize()} response too long:", file=discord.File(filepath))
|
||||
sent_any_message = True
|
||||
print(f"Sent {response_label} content as file.")
|
||||
return True
|
||||
except Exception as file_e: print(f"Error writing/sending long {response_label} response file: {file_e}")
|
||||
finally:
|
||||
try: os.remove(filepath)
|
||||
except OSError as os_e: print(f"Error removing temp file {filepath}: {os_e}")
|
||||
else:
|
||||
async with message.channel.typing():
|
||||
await simulate_human_typing(cog, message.channel, response_text) # Use simulation
|
||||
sent_msg = await message.channel.send(response_text)
|
||||
sent_message = True
|
||||
# Cache this bot response
|
||||
bot_response_cache_entry = format_message(cog, sent_msg)
|
||||
cog.message_cache['by_channel'][channel_id].append(bot_response_cache_entry)
|
||||
cog.message_cache['global_recent'].append(bot_response_cache_entry)
|
||||
cog.message_cache['replied_to'][channel_id].append(bot_response_cache_entry) # Track replies
|
||||
cog.bot_last_spoke[channel_id] = time.time()
|
||||
# Track participation topic
|
||||
identified_topics = identify_conversation_topics(cog, [bot_response_cache_entry])
|
||||
if identified_topics:
|
||||
topic = identified_topics[0]['topic'].lower().strip()
|
||||
cog.gurt_participation_topics[topic] += 1
|
||||
print(f"Tracked Gurt participation in topic: '{topic}'")
|
||||
try:
|
||||
async with message.channel.typing():
|
||||
await simulate_human_typing(cog, message.channel, response_text) # Use simulation
|
||||
sent_msg = await message.channel.send(response_text)
|
||||
sent_any_message = True
|
||||
# Cache this bot response
|
||||
bot_response_cache_entry = format_message(cog, sent_msg)
|
||||
cog.message_cache['by_channel'][channel_id].append(bot_response_cache_entry)
|
||||
cog.message_cache['global_recent'].append(bot_response_cache_entry)
|
||||
# cog.message_cache['replied_to'][channel_id].append(bot_response_cache_entry) # Maybe track replies differently?
|
||||
cog.bot_last_spoke[channel_id] = time.time()
|
||||
# Track participation topic
|
||||
identified_topics = identify_conversation_topics(cog, [bot_response_cache_entry])
|
||||
if identified_topics:
|
||||
topic = identified_topics[0]['topic'].lower().strip()
|
||||
cog.gurt_participation_topics[topic] += 1
|
||||
print(f"Tracked Gurt participation ({response_label}) in topic: '{topic}'")
|
||||
print(f"Sent {response_label} content.")
|
||||
return True
|
||||
except Exception as send_e:
|
||||
print(f"Error sending {response_label} content: {send_e}")
|
||||
return False
|
||||
|
||||
# Send initial response content if valid
|
||||
sent_initial_message = await send_response_content(initial_response, "initial")
|
||||
|
||||
if response_to_use.get("should_respond") and not sent_message and not reacted:
|
||||
print(f"Warning: AI response intended but no valid content/reaction. Data: {response_to_use}")
|
||||
# Send final response content if valid (and different from initial, if initial was sent)
|
||||
sent_final_message = False
|
||||
# Ensure initial_response exists before accessing its content for comparison
|
||||
initial_content = initial_response.get("content") if initial_response else None
|
||||
if final_response and (not sent_initial_message or initial_content != final_response.get("content")):
|
||||
sent_final_message = await send_response_content(final_response, "final")
|
||||
|
||||
elif not error_msg: # No valid response and no critical error
|
||||
print(f"Warning: No valid response generated for message {message.id}, and no critical error reported.")
|
||||
# Handle Reaction (prefer final response for reaction if it exists)
|
||||
reaction_source = final_response if final_response else initial_response
|
||||
if reaction_source and isinstance(reaction_source, dict):
|
||||
emoji_to_react = reaction_source.get("react_with_emoji")
|
||||
if emoji_to_react and isinstance(emoji_to_react, str):
|
||||
try:
|
||||
# Basic validation for standard emoji
|
||||
if 1 <= len(emoji_to_react) <= 4 and not re.match(r'<a?:.+?:\d+>', emoji_to_react):
|
||||
# Only react if we haven't sent any message content (avoid double interaction)
|
||||
if not sent_any_message:
|
||||
await message.add_reaction(emoji_to_react)
|
||||
reacted = True
|
||||
print(f"Bot reacted to message {message.id} with {emoji_to_react}")
|
||||
else:
|
||||
print(f"Skipping reaction {emoji_to_react} because a message was already sent.")
|
||||
else: print(f"Invalid emoji format: {emoji_to_react}")
|
||||
except Exception as e: print(f"Error adding reaction '{emoji_to_react}': {e}")
|
||||
|
||||
# Log if response was intended but nothing was sent/reacted
|
||||
# Check if initial response intended action but nothing happened
|
||||
initial_intended_action = initial_response and initial_response.get("should_respond")
|
||||
initial_action_taken = sent_initial_message or (reacted and reaction_source == initial_response)
|
||||
# Check if final response intended action but nothing happened
|
||||
final_intended_action = final_response and final_response.get("should_respond")
|
||||
final_action_taken = sent_final_message or (reacted and reaction_source == final_response)
|
||||
|
||||
if (initial_intended_action and not initial_action_taken) or \
|
||||
(final_intended_action and not final_action_taken):
|
||||
print(f"Warning: AI response intended action but nothing sent/reacted. Initial: {initial_response}, Final: {final_response}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Exception in on_message listener main block: {str(e)}")
|
||||
|
Loading…
x
Reference in New Issue
Block a user