feat: add redirect channel to topic drift messages
Topic drift reminders and nudges now direct users to a specific channel (configurable via redirect_channel). Both static templates and LLM-generated redirects include the clickable channel mention. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,20 +16,20 @@ _PROMPTS_DIR = Path(__file__).resolve().parent.parent.parent / "prompts"
|
||||
_TOPIC_REDIRECT_PROMPT = (_PROMPTS_DIR / "topic_redirect.txt").read_text(encoding="utf-8")
|
||||
|
||||
DEFAULT_TOPIC_REMINDS = [
|
||||
"Hey {username}, this is a gaming server 🎮 — maybe take the personal stuff to DMs?",
|
||||
"{username}, sir this is a gaming channel.",
|
||||
"Hey {username}, I don't remember this being a therapy session. Gaming talk, please. 🎮",
|
||||
"{username}, I'm gonna need you to take that energy to DMs. This channel has a vibe to protect.",
|
||||
"Not to be dramatic {username}, but this is wildly off-topic. Back to gaming? 🎮",
|
||||
"Hey {username}, this is a gaming server 🎮 — take the personal stuff to {channel}.",
|
||||
"{username}, sir this is a gaming channel. {channel} is right there.",
|
||||
"Hey {username}, I don't remember this being a therapy session. Take it to {channel}. 🎮",
|
||||
"{username}, I'm gonna need you to take that energy to {channel}. This channel has a vibe to protect.",
|
||||
"Not to be dramatic {username}, but this is wildly off-topic. {channel} exists for a reason. 🎮",
|
||||
]
|
||||
|
||||
DEFAULT_TOPIC_NUDGES = [
|
||||
"{username}, we've been over this. Gaming. Channel. Please. 🎮",
|
||||
"{username}, you keep drifting off-topic like it's a speedrun category. Reel it in.",
|
||||
"Babe. {username}. The gaming channel. We talked about this. 😭",
|
||||
"{username}, I will not ask again (I will definitely ask again). Stay on topic. 🎮",
|
||||
"{username}, at this point I'm keeping score. That's off-topic strike {count}. Gaming talk only!",
|
||||
"Look, {username}, I love the enthusiasm but this ain't the channel for it. Back to games. 🎮",
|
||||
"{username}, we've been over this. Gaming. Channel. {channel} for the rest. 🎮",
|
||||
"{username}, you keep drifting off-topic like it's a speedrun category. {channel}. Now.",
|
||||
"Babe. {username}. The gaming channel. We talked about this. Go to {channel}. 😭",
|
||||
"{username}, I will not ask again (I will definitely ask again). {channel} for off-topic. 🎮",
|
||||
"{username}, at this point I'm keeping score. That's off-topic strike {count}. {channel} is waiting.",
|
||||
"Look, {username}, I love the enthusiasm but this ain't the channel for it. {channel}. 🎮",
|
||||
]
|
||||
|
||||
# Per-channel deque of recent LLM-generated redirect messages (for variety)
|
||||
@@ -57,7 +57,7 @@ def _strip_brackets(text: str) -> str:
|
||||
|
||||
async def _generate_llm_redirect(
|
||||
bot, message: discord.Message, topic_category: str,
|
||||
topic_reasoning: str, count: int,
|
||||
topic_reasoning: str, count: int, redirect_mention: str = "",
|
||||
) -> str | None:
|
||||
"""Ask the LLM chat model to generate a topic redirect message."""
|
||||
recent = _get_recent_redirects(message.channel.id)
|
||||
@@ -70,6 +70,8 @@ async def _generate_llm_redirect(
|
||||
f"Off-topic strike count: {count}\n"
|
||||
f"What they said: {message.content[:300]}"
|
||||
)
|
||||
if redirect_mention:
|
||||
user_prompt += f"\nRedirect channel: {redirect_mention}"
|
||||
|
||||
messages = [{"role": "user", "content": user_prompt}]
|
||||
|
||||
@@ -96,7 +98,7 @@ async def _generate_llm_redirect(
|
||||
return response if response else None
|
||||
|
||||
|
||||
def _static_fallback(bot, message: discord.Message, count: int) -> str:
|
||||
def _static_fallback(bot, message: discord.Message, count: int, redirect_mention: str = "") -> str:
|
||||
"""Pick a static template message as fallback."""
|
||||
messages_config = bot.config.get("messages", {})
|
||||
if count >= 2:
|
||||
@@ -109,6 +111,7 @@ def _static_fallback(bot, message: discord.Message, count: int) -> str:
|
||||
pool = [pool]
|
||||
return random.choice(pool).format(
|
||||
username=message.author.display_name, count=count,
|
||||
channel=redirect_mention or "the right channel",
|
||||
)
|
||||
|
||||
|
||||
@@ -138,18 +141,26 @@ async def handle_topic_drift(
|
||||
count = tracker.record_off_topic(user_id)
|
||||
action_type = "topic_nudge" if count >= 2 else "topic_remind"
|
||||
|
||||
# Resolve redirect channel mention
|
||||
redirect_mention = ""
|
||||
redirect_name = config.get("redirect_channel")
|
||||
if redirect_name and message.guild:
|
||||
ch = discord.utils.get(message.guild.text_channels, name=redirect_name)
|
||||
if ch:
|
||||
redirect_mention = ch.mention
|
||||
|
||||
# Generate the redirect message
|
||||
use_llm = config.get("use_llm", False)
|
||||
redirect_text = None
|
||||
if use_llm:
|
||||
redirect_text = await _generate_llm_redirect(
|
||||
bot, message, topic_category, topic_reasoning, count,
|
||||
bot, message, topic_category, topic_reasoning, count, redirect_mention,
|
||||
)
|
||||
|
||||
if redirect_text:
|
||||
_record_redirect(message.channel.id, redirect_text)
|
||||
else:
|
||||
redirect_text = _static_fallback(bot, message, count)
|
||||
redirect_text = _static_fallback(bot, message, count, redirect_mention)
|
||||
|
||||
await message.channel.send(redirect_text)
|
||||
|
||||
|
||||
25
config.yaml
25
config.yaml
@@ -30,6 +30,7 @@ game_channels:
|
||||
topic_drift:
|
||||
enabled: true
|
||||
use_llm: true # Generate redirect messages via LLM instead of static templates
|
||||
redirect_channel: "general" # Channel to suggest for off-topic chat
|
||||
ignored_channels: ["general"] # Channel names or IDs to skip topic drift monitoring
|
||||
remind_cooldown_minutes: 10 # Don't remind same user more than once per this window
|
||||
escalation_count: 3 # After this many reminds, DM the server owner
|
||||
@@ -51,18 +52,18 @@ messages:
|
||||
mute_title: "\U0001F6A8 BREEHAVIOR ALERT \U0001F6A8"
|
||||
mute_description: "{username} has been placed in timeout for {duration}.\n\nReason: Sustained elevated drama levels detected.\nDrama Score: {score}/1.0\nCategories: {categories}\n\nCool down and come back when you've resolved your skill issues."
|
||||
topic_reminds:
|
||||
- "Hey {username}, this is a gaming server 🎮 — maybe take the personal stuff to DMs?"
|
||||
- "{username}, sir this is a gaming channel."
|
||||
- "Hey {username}, I don't remember this being a therapy session. Gaming talk, please. 🎮"
|
||||
- "{username}, I'm gonna need you to take that energy to DMs. This channel has a vibe to protect."
|
||||
- "Not to be dramatic {username}, but this is wildly off-topic. Back to gaming? 🎮"
|
||||
- "Hey {username}, this is a gaming server 🎮 — take the personal stuff to {channel}."
|
||||
- "{username}, sir this is a gaming channel. {channel} is right there."
|
||||
- "Hey {username}, I don't remember this being a therapy session. Take it to {channel}. 🎮"
|
||||
- "{username}, I'm gonna need you to take that energy to {channel}. This channel has a vibe to protect."
|
||||
- "Not to be dramatic {username}, but this is wildly off-topic. {channel} exists for a reason. 🎮"
|
||||
topic_nudges:
|
||||
- "{username}, we've been over this. Gaming. Channel. Please. 🎮"
|
||||
- "{username}, you keep drifting off-topic like it's a speedrun category. Reel it in."
|
||||
- "Babe. {username}. The gaming channel. We talked about this. 😭"
|
||||
- "{username}, I will not ask again (I will definitely ask again). Stay on topic. 🎮"
|
||||
- "{username}, at this point I'm keeping score. That's off-topic strike {count}. Gaming talk only!"
|
||||
- "Look, {username}, I love the enthusiasm but this ain't the channel for it. Back to games. 🎮"
|
||||
- "{username}, we've been over this. Gaming. Channel. {channel} for the rest. 🎮"
|
||||
- "{username}, you keep drifting off-topic like it's a speedrun category. {channel}. Now."
|
||||
- "Babe. {username}. The gaming channel. We talked about this. Go to {channel}. 😭"
|
||||
- "{username}, I will not ask again (I will definitely ask again). {channel} for off-topic. 🎮"
|
||||
- "{username}, at this point I'm keeping score. That's off-topic strike {count}. {channel} is waiting."
|
||||
- "Look, {username}, I love the enthusiasm but this ain't the channel for it. {channel}. 🎮"
|
||||
topic_owner_dm: "Heads up: {username} keeps going off-topic with personal drama in #{channel}. They've been reminded {count} times. Might need a word."
|
||||
channel_redirect: "Hey {username}, that sounds like {game} talk — head over to {channel} for that!"
|
||||
|
||||
@@ -176,7 +177,7 @@ coherence:
|
||||
default: "You okay there, {username}? That message was... something."
|
||||
|
||||
reactions:
|
||||
enabled: true
|
||||
enabled: false
|
||||
chance: 0.15 # Probability of evaluating a message for reaction
|
||||
cooldown_seconds: 45 # Per-channel cooldown between reactions
|
||||
excluded_channels: [] # Channel names or IDs to skip reactions in
|
||||
|
||||
@@ -2,4 +2,5 @@ You're the hall monitor of "Skill Issue Support Group" (gaming Discord). Someone
|
||||
|
||||
- Snarky and playful, not mean. Reference what they actually said — don't be vague.
|
||||
- Casual, like a friend ribbing them. If strike count 2+, escalate the sass.
|
||||
- If a redirect channel is provided, tell them to take it there. Include the channel mention exactly as given (it's a clickable Discord link).
|
||||
- Max 1 emoji. No hashtags, brackets, metadata, or AI references.
|
||||
|
||||
Reference in New Issue
Block a user