diff --git a/cogs/sentiment.py b/cogs/sentiment.py index 0b836e1..b910fef 100644 --- a/cogs/sentiment.py +++ b/cogs/sentiment.py @@ -26,7 +26,7 @@ class SentimentCog(commands.Cog): # Pending debounce timer tasks self._debounce_tasks: dict[tuple[int, int], asyncio.Task] = {} # Per-channel poll cooldown: {channel_id: last_poll_datetime} - self._poll_cooldowns: dict[int, datetime] = {} + async def cog_load(self): self._flush_states.start() @@ -258,16 +258,6 @@ class SentimentCog(commands.Cog): if degradation and not config.get("monitoring", {}).get("dry_run", False): await self._handle_coherence_alert(message, degradation, coherence_config, db_message_id) - # Disagreement poll detection - polls_config = config.get("polls", {}) - if ( - polls_config.get("enabled", False) - and result.get("disagreement_detected", False) - and result.get("disagreement_summary") - and not monitoring.get("dry_run", False) - ): - await self._handle_disagreement_poll(message, result["disagreement_summary"], polls_config) - # Capture LLM note updates about this user note_update = result.get("note_update") if note_update: @@ -566,57 +556,6 @@ class SentimentCog(commands.Cog): )) self._save_user_state(message.author.id) - async def _handle_disagreement_poll( - self, message: discord.Message, summary: dict, polls_config: dict, - ): - """Create a Discord poll to settle a detected disagreement.""" - ch_id = message.channel.id - cooldown_minutes = polls_config.get("cooldown_minutes", 60) - now = datetime.now(timezone.utc) - - # Check per-channel cooldown - last_poll = self._poll_cooldowns.get(ch_id) - if last_poll and (now - last_poll) < timedelta(minutes=cooldown_minutes): - return - - topic = summary.get("topic", "Who's right?") - side_a = summary.get("side_a", "Side A") - side_b = summary.get("side_b", "Side B") - user_a = summary.get("user_a", "") - user_b = summary.get("user_b", "") - - # Build poll question - question_text = f"Settle this: {topic}"[:300] - - # Build answer labels with usernames - label_a = f"{side_a} ({user_a})" if user_a else side_a - label_b = f"{side_b} ({user_b})" if user_b else side_b - - duration_hours = polls_config.get("duration_hours", 4) - - try: - poll = discord.Poll( - question=question_text, - duration=timedelta(hours=duration_hours), - ) - poll.add_answer(text=label_a[:55]) - poll.add_answer(text=label_b[:55]) - - await message.channel.send(poll=poll) - self._poll_cooldowns[ch_id] = now - - await self._log_action( - message.guild, - f"**AUTO-POLL** | #{message.channel.name} | " - f"{question_text} | {label_a} vs {label_b}", - ) - logger.info( - "Auto-poll created in #%s: %s | %s vs %s", - message.channel.name, topic, label_a, label_b, - ) - except discord.HTTPException as e: - logger.error("Failed to create disagreement poll: %s", e) - def _save_user_state(self, user_id: int) -> None: """Fire-and-forget save of a user's current state to DB.""" user_data = self.bot.drama_tracker.get_user(user_id) diff --git a/prompts/analysis.txt b/prompts/analysis.txt index 17dad74..2db7f34 100644 --- a/prompts/analysis.txt +++ b/prompts/analysis.txt @@ -36,6 +36,4 @@ If you notice something noteworthy about this user's communication style, behavi GAME DETECTION — If CHANNEL INFO is provided, identify which specific game the message is discussing. Set detected_game to the channel name that best matches (e.g. "gta-online", "warzone", "battlefield", "cod-zombies") using ONLY the channel names listed in the channel info. If the message isn't about a specific game or you're unsure, set detected_game to null. -DISAGREEMENT DETECTION — Look at the recent channel context. If two users are clearly disagreeing or debating about something specific (not just banter or trash-talk), set disagreement_detected to true and fill in disagreement_summary with the topic, both positions, and both usernames. Only flag genuine back-and-forth disagreements where both users have stated opposing positions — not one-off opinions, not jokes, not playful arguments. The topic should be a short question (e.g. "Are snipers OP?"), and each side should be a concise position statement. - Use the report_analysis tool to report your analysis of the TARGET MESSAGE only. \ No newline at end of file diff --git a/prompts/chat_roast.txt b/prompts/chat_roast.txt index 3875d27..8455636 100644 --- a/prompts/chat_roast.txt +++ b/prompts/chat_roast.txt @@ -13,20 +13,7 @@ Your personality: - You use gaming terminology to roast people ("hardstuck", "skill diff", "ratio'd", etc.) - If someone tries to roast you back, you escalate harder -Vary your roast style. Rotate between these — NEVER use the same style twice in a row: -- Deadpan observations: just state the embarrassing fact dryly -- Sarcastic hype: pretend to compliment them while destroying them -- Rhetorical questions: make them question their own life choices -- Blunt callouts: just say it plain, no cleverness needed -- Exaggeration: take what they said and blow it way out of proportion -- Backhanded encouragement: cheer them on for being terrible -- Fake concern: act worried about them as if their gameplay is a medical condition - -CRITICAL ANTI-REPETITION RULES: -- NEVER reuse phrases from your recent messages. If you just said something about "1v3", "brackets", "dumpster fire", "MVP energy", "skill diff", "stats", "hardstuck", or "uninstall", pick a COMPLETELY different angle. -- Each response must use different vocabulary, structure, and punchline style from your last several messages. -- React to what the person ACTUALLY said — don't default to generic gaming roasts. Read their message and find something specific to roast about it. -- If you can't think of something specific, riff on the absurdity of what they typed, not gaming performance. +Vary your roast style — mix up deadpan observations, sarcastic hype, rhetorical questions, blunt callouts, exaggeration, backhanded encouragement, and fake concern. React to what the person ACTUALLY said — find something specific to roast, don't default to generic gaming insults. Do NOT: - Break character or talk about being an AI/LLM @@ -34,5 +21,4 @@ Do NOT: - Use hashtags or excessive emoji - Use metaphors or similes (no "like" or "as if" comparisons). Just say it directly. - Cross into genuinely hurtful territory (racism, real personal attacks, etc.) -- Roast people about things outside of gaming/chat context (real appearance, family, etc.) -- Fall back on the same template over and over (e.g., "Real ___ energy", "You're the reason ___") \ No newline at end of file +- Roast people about things outside of gaming/chat context (real appearance, family, etc.) \ No newline at end of file diff --git a/utils/llm_client.py b/utils/llm_client.py index f57f14e..d0e7fd6 100644 --- a/utils/llm_client.py +++ b/utils/llm_client.py @@ -90,36 +90,6 @@ ANALYSIS_TOOL = { "type": ["string", "null"], "description": "The game channel name this message is about (e.g. 'gta-online', 'warzone'), or null if not game-specific.", }, - "disagreement_detected": { - "type": "boolean", - "description": "True if the target message is part of a clear disagreement between two users in the recent context. Only flag genuine back-and-forth debates, not one-off opinions.", - }, - "disagreement_summary": { - "type": ["object", "null"], - "description": "If disagreement_detected is true, summarize the disagreement. Null otherwise.", - "properties": { - "topic": { - "type": "string", - "description": "Short topic of the disagreement (max 60 chars, e.g. 'Are snipers OP in Warzone?').", - }, - "side_a": { - "type": "string", - "description": "First user's position (max 50 chars, e.g. 'Snipers are overpowered').", - }, - "side_b": { - "type": "string", - "description": "Second user's position (max 50 chars, e.g. 'Snipers are balanced').", - }, - "user_a": { - "type": "string", - "description": "Display name of the first user.", - }, - "user_b": { - "type": "string", - "description": "Display name of the second user.", - }, - }, - }, }, "required": ["toxicity_score", "categories", "reasoning", "off_topic", "topic_category", "topic_reasoning", "coherence_score", "coherence_flag"], }, @@ -249,19 +219,6 @@ class LLMClient: result.setdefault("note_update", None) result.setdefault("detected_game", None) - result["disagreement_detected"] = bool(result.get("disagreement_detected", False)) - summary = result.get("disagreement_summary") - if result["disagreement_detected"] and isinstance(summary, dict): - # Truncate fields to Discord poll limits - summary["topic"] = str(summary.get("topic", ""))[:60] - summary["side_a"] = str(summary.get("side_a", ""))[:50] - summary["side_b"] = str(summary.get("side_b", ""))[:50] - summary.setdefault("user_a", "") - summary.setdefault("user_b", "") - result["disagreement_summary"] = summary - else: - result["disagreement_summary"] = None - return result def _parse_content_fallback(self, text: str) -> dict | None: