feat: Add channel ignore functionality
Implement `/gurtignore` commands to manage channels Gurt should ignore. - Add `gurtignore add` to add a channel to the ignore list. - Add `gurtignore remove` to remove a channel from the ignore list. - Add `gurtignore list` to display currently ignored channels. - Update `GurtCog` and `config.py` to support ignored channel IDs and file management. - Ensure ignore commands are owner-only. - Modify `listeners.py` to prevent Gurt from processing messages in ignored channels.
This commit is contained in:
parent
3290b50fbd
commit
3ff850a9b3
@ -26,7 +26,8 @@ from .config import (
|
||||
PROACTIVE_RELATIONSHIP_SCORE_THRESHOLD, PROACTIVE_RELATIONSHIP_CHANCE,
|
||||
INTEREST_UPDATE_INTERVAL, INTEREST_DECAY_INTERVAL_HOURS,
|
||||
LEARNING_UPDATE_INTERVAL, TOPIC_UPDATE_INTERVAL, SENTIMENT_UPDATE_INTERVAL,
|
||||
EVOLUTION_UPDATE_INTERVAL, RESPONSE_SCHEMA, TOOLS # Import necessary configs
|
||||
EVOLUTION_UPDATE_INTERVAL, RESPONSE_SCHEMA, TOOLS, # Import necessary configs
|
||||
IGNORED_CHANNEL_IDS, update_ignored_channels_file # Import for ignored channels
|
||||
)
|
||||
# Import functions/classes from other modules
|
||||
from .memory import MemoryManager # Import from local memory.py
|
||||
@ -53,6 +54,10 @@ class GurtCog(commands.Cog, name="Gurt"): # Added explicit Cog name
|
||||
self.fallback_model = FALLBACK_MODEL # Use imported config
|
||||
self.MOOD_OPTIONS = MOOD_OPTIONS # Make MOOD_OPTIONS available as an instance attribute
|
||||
self.current_channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None # Type hint current channel
|
||||
|
||||
# Ignored channels config
|
||||
self.IGNORED_CHANNEL_IDS = IGNORED_CHANNEL_IDS
|
||||
self.update_ignored_channels_file = update_ignored_channels_file
|
||||
|
||||
# Instantiate MemoryManager
|
||||
self.memory_manager = MemoryManager(
|
||||
|
@ -14,7 +14,7 @@ from typing import TYPE_CHECKING, Optional, Dict, Any, List, Tuple # Add more ty
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .cog import GurtCog # For type hinting
|
||||
from .config import MOOD_OPTIONS # Import for choices
|
||||
from .config import MOOD_OPTIONS, IGNORED_CHANNEL_IDS, update_ignored_channels_file # Import for choices and ignored channels
|
||||
|
||||
# --- Helper Function for Embeds ---
|
||||
def create_gurt_embed(title: str, description: str = "", color=discord.Color.blue()) -> discord.Embed:
|
||||
@ -517,6 +517,73 @@ def setup_commands(cog: 'GurtCog'):
|
||||
# Add group command functions to the list for tracking (optional, but good practice)
|
||||
command_functions.extend([gurtgoal_add, gurtgoal_list, gurtgoal_update, gurtgoal_delete])
|
||||
|
||||
# --- Gurt Ignore Command Group (Owner Only) ---
|
||||
gurtignore_group = app_commands.Group(name="gurtignore", description="Manage channels Gurt should ignore. (Owner only)")
|
||||
|
||||
@gurtignore_group.command(name="add", description="Add a channel to Gurt's ignore list.")
|
||||
@app_commands.describe(channel="The channel or thread to ignore.")
|
||||
async def gurtignore_add(interaction: discord.Interaction, channel: discord.abc.GuildChannel): # Use GuildChannel to accept TextChannel, Thread, etc.
|
||||
if interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.response.send_message("⛔ Only the bot owner can modify the ignore list.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
current_ignored_ids = set(cog.IGNORED_CHANNEL_IDS) # Use cog's direct reference
|
||||
if channel.id in current_ignored_ids:
|
||||
await interaction.followup.send(f"⚠️ Channel {channel.mention} is already in the ignore list.", ephemeral=True)
|
||||
return
|
||||
|
||||
current_ignored_ids.add(channel.id)
|
||||
if cog.update_ignored_channels_file(current_ignored_ids): # Use cog's direct reference
|
||||
await interaction.followup.send(f"✅ Channel {channel.mention} added to the ignore list.", ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send(f"❌ Failed to update the ignore list file. Check bot logs.", ephemeral=True)
|
||||
|
||||
@gurtignore_group.command(name="remove", description="Remove a channel from Gurt's ignore list.")
|
||||
@app_commands.describe(channel="The channel or thread to stop ignoring.")
|
||||
async def gurtignore_remove(interaction: discord.Interaction, channel: discord.abc.GuildChannel):
|
||||
if interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.response.send_message("⛔ Only the bot owner can modify the ignore list.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
current_ignored_ids = set(cog.IGNORED_CHANNEL_IDS) # Use cog's direct reference
|
||||
if channel.id not in current_ignored_ids:
|
||||
await interaction.followup.send(f"⚠️ Channel {channel.mention} is not in the ignore list.", ephemeral=True)
|
||||
return
|
||||
|
||||
current_ignored_ids.remove(channel.id)
|
||||
if cog.update_ignored_channels_file(current_ignored_ids): # Use cog's direct reference
|
||||
await interaction.followup.send(f"✅ Channel {channel.mention} removed from the ignore list.", ephemeral=True)
|
||||
else:
|
||||
await interaction.followup.send(f"❌ Failed to update the ignore list file. Check bot logs.", ephemeral=True)
|
||||
|
||||
@gurtignore_group.command(name="list", description="List all channels Gurt is currently ignoring.")
|
||||
async def gurtignore_list(interaction: discord.Interaction):
|
||||
if interaction.user.id != cog.bot.owner_id:
|
||||
await interaction.response.send_message("⛔ Only the bot owner can view the ignore list.", ephemeral=True)
|
||||
return
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
current_ignored_ids = cog.IGNORED_CHANNEL_IDS # Use cog's direct reference
|
||||
if not current_ignored_ids:
|
||||
await interaction.followup.send("Gurt is not currently ignoring any channels.", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = create_gurt_embed("Ignored Channels", color=discord.Color.orange())
|
||||
description_lines = []
|
||||
for channel_id in current_ignored_ids:
|
||||
ch = cog.bot.get_channel(channel_id)
|
||||
if ch:
|
||||
description_lines.append(f"- {ch.mention} (`{channel_id}`)")
|
||||
else:
|
||||
description_lines.append(f"- Unknown Channel (`{channel_id}`)")
|
||||
|
||||
embed.description = "\n".join(description_lines)
|
||||
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||
|
||||
cog.bot.tree.add_command(gurtignore_group)
|
||||
command_functions.extend([gurtignore_add, gurtignore_remove, gurtignore_list])
|
||||
|
||||
# Get command names safely - Command objects don't have __name__ attribute
|
||||
command_names = []
|
||||
|
@ -31,6 +31,79 @@ DB_PATH = os.getenv("GURT_DB_PATH", "data/gurt_memory.db")
|
||||
CHROMA_PATH = os.getenv("GURT_CHROMA_PATH", "data/chroma_db")
|
||||
SEMANTIC_MODEL_NAME = os.getenv("GURT_SEMANTIC_MODEL", 'all-MiniLM-L6-v2')
|
||||
|
||||
# --- Ignored Channels ---
|
||||
IGNORED_CHANNELS_FILE_PATH = "data/ignored_channels.json"
|
||||
_loaded_ignored_channel_ids = set()
|
||||
|
||||
try:
|
||||
if os.path.exists(IGNORED_CHANNELS_FILE_PATH):
|
||||
with open(IGNORED_CHANNELS_FILE_PATH, 'r') as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data, list) and all(isinstance(cid, int) for cid in data):
|
||||
_loaded_ignored_channel_ids = set(data)
|
||||
print(f"Loaded {len(_loaded_ignored_channel_ids)} ignored channel IDs from {IGNORED_CHANNELS_FILE_PATH}")
|
||||
elif data: # If file exists but content is not a list of ints (e.g. old format or corrupt)
|
||||
print(f"Warning: {IGNORED_CHANNELS_FILE_PATH} contains invalid data. Attempting to load from ENV.")
|
||||
_loaded_ignored_channel_ids = set() # Reset to ensure fallback
|
||||
|
||||
if not _loaded_ignored_channel_ids: # If file didn't exist, was empty, or had invalid data
|
||||
IGNORED_CHANNELS_STR = os.getenv("GURT_IGNORED_CHANNELS", "")
|
||||
if IGNORED_CHANNELS_STR:
|
||||
env_ids = {int(cid.strip()) for cid in IGNORED_CHANNELS_STR.split(',') if cid.strip().isdigit()}
|
||||
if env_ids:
|
||||
_loaded_ignored_channel_ids = env_ids
|
||||
print(f"Loaded {len(_loaded_ignored_channel_ids)} ignored channel IDs from GURT_IGNORED_CHANNELS env var.")
|
||||
# Initialize the JSON file with ENV var content if JSON was missing/empty
|
||||
if not os.path.exists(IGNORED_CHANNELS_FILE_PATH) or os.path.getsize(IGNORED_CHANNELS_FILE_PATH) == 0:
|
||||
try:
|
||||
os.makedirs(os.path.dirname(IGNORED_CHANNELS_FILE_PATH), exist_ok=True)
|
||||
with open(IGNORED_CHANNELS_FILE_PATH, 'w') as f:
|
||||
json.dump(list(_loaded_ignored_channel_ids), f)
|
||||
print(f"Initialized {IGNORED_CHANNELS_FILE_PATH} with IDs from ENV var.")
|
||||
except Exception as e:
|
||||
print(f"Error initializing {IGNORED_CHANNELS_FILE_PATH}: {e}")
|
||||
else:
|
||||
print("No ignored channel IDs found in GURT_IGNORED_CHANNELS env var.")
|
||||
else:
|
||||
print(f"{IGNORED_CHANNELS_FILE_PATH} not found and GURT_IGNORED_CHANNELS env var not set or empty. No channels will be ignored by default.")
|
||||
# Ensure the file exists even if empty for commands to use later
|
||||
if not os.path.exists(IGNORED_CHANNELS_FILE_PATH):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(IGNORED_CHANNELS_FILE_PATH), exist_ok=True)
|
||||
with open(IGNORED_CHANNELS_FILE_PATH, 'w') as f:
|
||||
json.dump([], f) # Create an empty list in the JSON file
|
||||
print(f"Created empty {IGNORED_CHANNELS_FILE_PATH}.")
|
||||
except Exception as e:
|
||||
print(f"Error creating empty {IGNORED_CHANNELS_FILE_PATH}: {e}")
|
||||
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"{IGNORED_CHANNELS_FILE_PATH} not found. Will check GURT_IGNORED_CHANNELS env var.")
|
||||
# This case is handled by the 'if not _loaded_ignored_channel_ids' block above
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error decoding JSON from {IGNORED_CHANNELS_FILE_PATH}. Will check GURT_IGNORED_CHANNELS env var.")
|
||||
# This case is handled by the 'if not _loaded_ignored_channel_ids' block above
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred while loading ignored channels: {e}")
|
||||
# Fallback to empty set in case of other errors
|
||||
|
||||
IGNORED_CHANNEL_IDS = _loaded_ignored_channel_ids
|
||||
|
||||
# Function to update ignored channels at runtime (used by commands)
|
||||
def update_ignored_channels_file(channel_ids_set: set):
|
||||
"""Updates the ignored_channels.json file and the runtime config."""
|
||||
global IGNORED_CHANNEL_IDS
|
||||
try:
|
||||
os.makedirs(os.path.dirname(IGNORED_CHANNELS_FILE_PATH), exist_ok=True)
|
||||
with open(IGNORED_CHANNELS_FILE_PATH, 'w') as f:
|
||||
json.dump(list(channel_ids_set), f)
|
||||
IGNORED_CHANNEL_IDS = channel_ids_set
|
||||
print(f"Successfully updated {IGNORED_CHANNELS_FILE_PATH} with {len(channel_ids_set)} IDs.")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error updating {IGNORED_CHANNELS_FILE_PATH}: {e}")
|
||||
return False
|
||||
|
||||
# --- Memory Manager Config ---
|
||||
MAX_USER_FACTS = 20 # TODO: Load from env?
|
||||
MAX_GENERAL_FACTS = 100 # TODO: Load from env?
|
||||
|
@ -51,12 +51,18 @@ async def on_message_listener(cog: 'GurtCog', message: discord.Message):
|
||||
from .api import get_ai_response, get_proactive_ai_response
|
||||
from .utils import format_message, simulate_human_typing
|
||||
from .analysis import analyze_message_sentiment, update_conversation_sentiment, identify_conversation_topics
|
||||
from .config import GURT_RESPONSES # Import simple responses
|
||||
from .config import GURT_RESPONSES, IGNORED_CHANNEL_IDS # Import simple responses and ignored channel IDs
|
||||
|
||||
# Don't respond to our own messages
|
||||
if message.author == cog.bot.user:
|
||||
return
|
||||
|
||||
# Check if the channel is in the ignored list
|
||||
if message.channel.id in IGNORED_CHANNEL_IDS:
|
||||
# Optionally, log that a message from an ignored channel was skipped
|
||||
# print(f"Skipping message from ignored channel: {message.channel.id}")
|
||||
return
|
||||
|
||||
# Don't process commands here
|
||||
if message.content.startswith(cog.bot.command_prefix):
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user