chore: remove wordle cog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 10:48:44 -05:00
parent 33d56f8737
commit 88536b4dca
3 changed files with 2 additions and 207 deletions

View File

@@ -55,7 +55,7 @@ LLM calls use OpenAI tool-calling for structured output (`ANALYSIS_TOOL`, `CONVE
- **`cogs/sentiment.py` (SentimentCog)**: Core moderation engine. Listens to all messages, debounces per-channel (batches messages within `batch_window_seconds`), runs triage → escalation analysis, issues warnings/mutes. Also handles mention-triggered conversation scans and game channel redirects. Flushes dirty user states to DB every 5 minutes.
- **`cogs/chat.py` (ChatCog)**: Conversational AI. Responds to @mentions, replies to bot messages, proactive replies based on mode config. Handles image roasts via vision model. Strips leaked LLM metadata brackets from responses.
- **`cogs/commands.py` (CommandsCog)**: Slash commands — `/dramareport`, `/dramascore`, `/bcs-status`, `/bcs-threshold`, `/bcs-reset`, `/bcs-immune`, `/bcs-history`, `/bcs-scan`, `/bcs-test`, `/bcs-notes`, `/bcs-mode`.
- **`cogs/wordle.py` (WordleCog)**: Watches for Wordle bot messages and generates fun commentary on results.
### Key Utilities

2
bot.py
View File

@@ -138,7 +138,7 @@ class BCSBot(commands.Bot):
await self.load_extension("cogs.sentiment")
await self.load_extension("cogs.commands")
await self.load_extension("cogs.chat")
await self.load_extension("cogs.wordle")
await self.tree.sync()
logger.info("Slash commands synced.")

View File

@@ -1,205 +0,0 @@
import logging
import random
import re
from collections import deque
from pathlib import Path
import discord
from discord.ext import commands
logger = logging.getLogger("bcs.wordle")
_PROMPTS_DIR = Path(__file__).resolve().parent.parent / "prompts"
_prompt_cache: dict[str, str] = {}
def _load_prompt(filename: str) -> str:
if filename not in _prompt_cache:
_prompt_cache[filename] = (_PROMPTS_DIR / filename).read_text(encoding="utf-8")
return _prompt_cache[filename]
def _parse_wordle_embeds(message: discord.Message) -> dict | None:
"""Extract useful info from a Wordle bot message.
Returns a dict with keys like 'type', 'summary', 'scores', 'streak', 'wordle_number'
or None if this isn't a recognizable Wordle result message.
"""
if not message.embeds:
return None
full_text = ""
wordle_number = None
for embed in message.embeds:
if embed.description:
full_text += embed.description + "\n"
if embed.title:
full_text += embed.title + "\n"
m = re.search(r"Wordle No\.\s*(\d+)", embed.title)
if m:
wordle_number = int(m.group(1))
if not full_text.strip():
return None
# Detect result messages (contain score patterns like "3/6:")
score_pattern = re.findall(r"(\d/6):\s*@?(.+?)(?:\n|$)", full_text)
streak_match = re.search(r"(\d+)\s*day streak", full_text)
if score_pattern:
scores = [{"score": s[0], "player": s[1].strip()} for s in score_pattern]
return {
"type": "results",
"wordle_number": wordle_number,
"streak": int(streak_match.group(1)) if streak_match else None,
"scores": scores,
"summary": full_text.strip(),
}
# Detect "was playing" messages
if "was playing" in full_text:
return {
"type": "playing",
"wordle_number": wordle_number,
"summary": full_text.strip(),
}
return None
class WordleCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self._chat_history: dict[int, deque] = {}
def _get_active_prompt(self) -> str:
mode_config = self.bot.get_mode_config()
prompt_file = mode_config.get("prompt_file", "chat_personality.txt")
return _load_prompt(prompt_file)
def _get_wordle_config(self) -> dict:
return self.bot.config.get("wordle", {})
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if not message.author.bot:
return
if not message.guild:
return
config = self._get_wordle_config()
if not config.get("enabled", False):
return
# Match the Wordle bot by name
bot_name = config.get("bot_name", "Wordle")
if message.author.name != bot_name:
return
parsed = _parse_wordle_embeds(message)
if not parsed:
return
# Only comment on results, not "playing" notifications
if parsed["type"] == "playing":
reply_chance = config.get("playing_reply_chance", 0.0)
if reply_chance <= 0 or random.random() > reply_chance:
return
else:
reply_chance = config.get("reply_chance", 0.5)
if random.random() > reply_chance:
return
# Build context for the LLM
context_parts = [
f"[Wordle bot posted in #{message.channel.name}]",
"[Wordle scoring: players guess a 5-letter word in up to 6 tries. "
"LOWER is BETTER — 1/6 is a genius guess, 2/6 is incredible, 3/6 is great, "
"4/6 is mediocre, 5/6 is rough, 6/6 barely scraped by, X/6 means they failed]",
]
if parsed["type"] == "results":
context_parts.append("[This is a Wordle results summary]")
if parsed.get("streak"):
context_parts.append(f"[Group streak: {parsed['streak']} days]")
if parsed.get("wordle_number"):
context_parts.append(f"[Wordle #{parsed['wordle_number']}]")
for s in parsed.get("scores", []):
context_parts.append(f"[{s['player']} scored {s['score']}]")
# Identify the winner (lowest score = best)
scores = parsed.get("scores", [])
if scores:
best = min(scores, key=lambda s: int(s["score"][0]))
worst = max(scores, key=lambda s: int(s["score"][0]))
if best != worst:
context_parts.append(
f"[{best['player']} won with {best['score']}, "
f"{worst['player']} came last with {worst['score']}]"
)
elif parsed["type"] == "playing":
context_parts.append(f"[Someone is currently playing Wordle]")
context_parts.append(f"[{parsed['summary']}]")
prompt_context = "\n".join(context_parts)
user_msg = (
f"{prompt_context}\n"
f"React to this Wordle update with a short, fun comment. "
f"Keep it to 1-2 sentences."
)
ch_id = message.channel.id
if ch_id not in self._chat_history:
self._chat_history[ch_id] = deque(maxlen=6)
self._chat_history[ch_id].append({"role": "user", "content": user_msg})
active_prompt = self._get_active_prompt()
recent_bot_replies = [
m["content"][:150] for m in self._chat_history[ch_id]
if m["role"] == "assistant"
][-3:]
typing_ctx = None
async def start_typing():
nonlocal typing_ctx
typing_ctx = message.channel.typing()
await typing_ctx.__aenter__()
response = await self.bot.llm_chat.chat(
list(self._chat_history[ch_id]),
active_prompt,
on_first_token=start_typing,
recent_bot_replies=recent_bot_replies,
)
if typing_ctx:
await typing_ctx.__aexit__(None, None, None)
# Strip leaked metadata brackets (same as chat.py)
if response:
segments = re.split(r"^\s*\[[^\]]*\]\s*$", response, flags=re.MULTILINE)
segments = [s.strip() for s in segments if s.strip()]
response = segments[-1] if segments else ""
if not response:
logger.warning("LLM returned no response for Wordle comment in #%s", message.channel.name)
return
self._chat_history[ch_id].append({"role": "assistant", "content": response})
await message.reply(response, mention_author=False)
logger.info(
"Wordle %s reply in #%s: %s",
parsed["type"],
message.channel.name,
response[:100],
)
async def setup(bot: commands.Bot):
await bot.add_cog(WordleCog(bot))