ShowBot is a snarky, achievement-wielding podcast search bot that knows everything the hosts have ever said across 252+ episodes. Here's how to use it.
Just type naturally in the #showbot channel. No commands needed.
You: What is Tim's favorite database?
ShowBot: PostgreSQL, and if you didn't already know that, have you even
been listening? [snarky answer with episode links]
ShowBot remembers what you were talking about, so follow-ups work:
You: tell me more
ShowBot: [continues the conversation with context]
| Command | What it does |
|---|---|
/showbot |
Quick start guide and documentation link |
/ask question: |
Search transcripts — raw results, no LLM summary |
/summarize question: |
Search + AI summary with evidence cards |
/hosts |
Show host stats and attendance |
/attendance |
Host attendance report with visual bars |
/attendance host: Carol |
Detailed view — which episodes a host missed |
/episodes |
Browse all episodes (paginated, sent to DMs) |
/episode 251 |
AI-generated summary of a specific episode (sent to DMs) |
/profile |
Your ShowBot stats, level, XP, and achievements |
/badges |
View the punk zine badge art for achievements you've earned (DMs) |
/temperature set value: |
Set your personal LLM temperature (0.0–2.0) |
/temperature show |
See your current temperature setting |
/temperature reset |
Go back to the default (0.7) |
/serious |
Toggle serious mode on/off — no snark, just facts (per-user) |
/scrape |
Export text channel history to JSONL for RAG (admin only) |
So you want ShowBot to learn from the channel chatter? Bold move, crawler. Here's how it works.
Who can use it: Server managers only. If you don't have the Manage Server permission, ShowBot will laugh at you.
What it does: Grabs every human message from eligible text channels and dumps them into discord_history.jsonl — one JSON record per message with author, content, timestamp, and channel info.
What it skips:
#secret,#patrons-only,#showbot— off limits, don't even try- Voice channels — ShowBot can't hear you
- Bot messages — nobody wants to train on bot spam
- Empty messages — nothing to see here
- Channels ShowBot can't access — no permissions, no data
Incremental updates: ShowBot remembers the last message it scraped per channel. Run /scrape again and it only grabs what's new. No duplicate messages, no wasted API calls. It's smarter than it looks.
The output: discord_history.jsonl in the bot directory. Each line is a JSON object:
{"id": "123", "channel": "general", "author": "Tim", "content": "PostgreSQL forever", "created_at": "2025-01-15T10:30:00Z", "reply_to": null}Once you've scraped the channels, you need to feed that data into ShowBot's brain. Two commands, zero thinking required.
Step 1: Scrape (from Discord slash command or CLI)
python scrape_discord.py # CLI version — connects, scrapes, disconnectsStep 2: Ingest into RAG
python ingest_discord.py # embed and merge into rag_index.json
python ingest_discord.py --dry-run # preview without modifying anythingWhat happens behind the curtain:
- Messages are grouped into conversation chunks (15+ minute gap = new conversation)
- Tiny conversations (< 50 chars) get thrown in the trash where they belong
- Each chunk gets embedded with the same model as podcast transcripts (all-MiniLM-L6-v2)
- Discord segments are merged into the existing RAG index alongside the 54,800+ podcast segments
- Search results from Discord show as
Discord [epdiscord_YYYY-MM-DD] #channel-name
Re-running is safe. The ingest strips out old Discord segments before merging new ones. It's idempotent, crawler. Run it as many times as you want.
The numbers: 100,000+ Discord messages become ~27,000 conversation segments after chunking. Combined with podcast transcripts, ShowBot now searches across 82,000+ segments. It knows what you said. All of it.
ShowBot knows the difference between what the hosts said on the podcast and what you nerds typed in Discord. Ask about "discord" or "the channel" and it filters to Discord-only segments. Ask a normal question and it sticks to podcast transcripts — because let's be honest, the hosts' opinions carry more weight than your hot takes in #general. If the transcripts come up empty, Discord results sneak in as a fallback, like that friend who shows up uninvited but at least brings snacks.
Keywords that trigger Discord-only search: discord, channel, said in chat, in the channel, in discord, on discord, chat history, general channel
Works great:
- "What does Ben think about ColdFusion?"
- "Has Carol talked about testing?"
- "What topics come up the most?"
- "Tell me about Tim and PostgreSQL"
Works with counting:
- "How many times has Ben mentioned ColdFusion?"
- "How many episodes has Carol missed?"
- "How often does Tim talk about databases?"
Follow-ups work (sort of):
- Ask a question, then say "tell me more" or "what about Tim?"
- ShowBot only uses your prior questions as a fallback when the current query finds nothing on its own
- Memory is RAM-only — bot restart wipes the slate clean
Host filtering:
- Mention a host name and ShowBot prioritizes their own words
- "What does Ben think about..." searches Ben's segments first
ShowBot tracks your activity and awards achievements — Dungeon Crawler Carl style. Achievements are sent as private DMs with punk zine badge art.
How to earn them:
- Ask your first question (First Blood)
- Ask about PostgreSQL (Tim's Favorite)
- Ask about ColdFusion (Ben's Ride or Die)
- Ask about testing (Carol Approves)
- Ask about architecture (Adam's Spirit Animal)
- Mention all four hosts (Full Party Wipe)
- Ask a counting question (Bean Counter)
- Ask a follow-up (Tell Me More)
- Stump the bot (Stumped The Bot)
- Ask after midnight (Night Owl)
- Ask 3 questions in 2 minutes (Machine Gun Questions)
- Ask the same question twice (Groundhog Day)
- Trigger the fallacy detector (Logic Police Bait)
- Attack a host personally (Chose Violence)
- Hit 10/25/50/100 questions (Regular/Obsessed/Touched Grass?/Final Boss)
- Trigger specific fallacies (unique DCC-style achievement per fallacy type)
Use /profile to see your stats and /badges to view your earned badge art.
ShowBot detects 15 types of logical fallacies and calls them out conversationally:
Ad Hominem, Straw Man, False Dilemma, Hasty Generalization, Slippery Slope, Appeal to Authority, Appeal to Popularity, Appeal to Ignorance, Circular Reasoning, False Cause, Red Herring, Tu Quoque, False Analogy, No True Scotsman, Loaded Question
Each fallacy type has its own DCC-style achievement with rambling, ominous 80s/90s nostalgia text.
Warning: Personal attacks on hosts (Ad Hominem) trigger ShowBot's nuclear defense mode. You've been warned.
ShowBot checks your spelling:
- 1-2 typos: quiet correction, still answers
- 3+ typos: refuses to answer until you fix your spelling
Tech terms (postgresql, coldfusion, kubernetes, etc.) and contractions (don't, isn't, etc.) are whitelisted.
Look, not everyone wants to be roasted by a podcast bot. Some people just want the damn answer. Use the /serious slash command and ShowBot drops the snark, the sass, and the personality for your entire session. You get clean, factual, cited answers — like talking to a bot that hasn't been raised by four opinionated developers.
The command works in any channel — just type /serious and Discord will show it in the autocomplete. ShowBot confirms the toggle privately (only you see it).
/serious → "Serious mode on. Straight answers, no snark."
You: What does Ben think about TypeScript?
ShowBot: Ben has discussed TypeScript across several episodes. In episode 142,
he acknowledged it caught a real bug in his code. He sees value in it
for larger projects but considers the overhead unnecessary for smaller
ones.
/serious → "Serious mode off. The snark is back, baby."
It's a per-user toggle — your serious mode doesn't affect anyone else's experience. Works across chat and /summarize. Use /serious again to bring back the chaos.
You know how some days you want the bot to give you a straight, predictable answer and other days you want it to go full chaos goblin? Temperature controls that. It's your setting — it doesn't affect anyone else's queries, so go wild.
/temperature set value:0.0 → Deterministic robot mode. Same question = same answer. Boring but reliable.
/temperature set value:0.7 → The default. Balanced like a well-specced character build.
/temperature set value:1.5 → Creative mode. ShowBot starts improvising. Results may vary. A lot.
/temperature show → Check what you're running at.
/temperature reset → Back to 0.7. The coward's choice. (Just kidding. Mostly.)
Range: 0.0 to 2.0. Anything outside that range gets rejected because ShowBot has some self-preservation instincts.
What it affects: All LLM-powered responses to your questions — chat in #showbot and /summarize. It does NOT affect episode summaries (those stay at 0.3 because facts shouldn't be creative).
Persistence: Your setting sticks around as long as ShowBot is running. Bot restart = back to defaults. Think of it like a temporary enchantment, not a permanent stat boost.
| Trigger | What happens |
|---|---|
pew pew |
ASCII fireworks display |
i'm struggling |
Genuine heartfelt affirmation (snark off) |
heart check |
Same — ShowBot drops everything to be kind |
Every response includes clickable episode links that scroll directly to the quoted text on workingcode.dev using text fragments. Click a source link to see exactly what the hosts said.
- Be specific — "What does Ben think about TypeScript?" works better than "TypeScript"
- Use host names — ShowBot prioritizes that host's own words when you mention them
- Follow up — short messages like "tell me more" or "what about Carol?" use conversation context
- Counting questions — start with "how many" or "how often" for real data, not LLM guesses
- Attendance — ask "how many episodes has X missed?" for accurate speaker-based stats
- Challenge it — try logical fallacies and bad takes, ShowBot will call you out
- Collect achievements — check
/profileto see what you've unlocked - After midnight — you'll get a special Night Owl achievement
- Rapid fire — ask 3 questions in 2 minutes for the Machine Gun achievement
- Be nice — or don't. ShowBot can take it. But the hosts are off limits.
Because of course you want to know how it works. You're a Working Code listener — you can't just use something without peeking under the hood.
You type a question
↓
Two search engines fight over who finds the best transcript matches
↓
A re-ranker picks the winner
↓
An LLM reads the evidence and writes a snarky summary
↓
You get an answer with links to the actual transcript
┌─────────────────────────────────────────────────────────┐
│ Discord (you, asking about Ben's ColdFusion obsession) │
└──────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Discord Bot (disnake) │
│ - Listens in #showbot channel │
│ - Slash commands in all channels │
│ - Tracks conversation history (last 10 messages) │
│ - Detects which host you're asking about │
│ - Spellcheck, fallacy detection, achievements │
└──────────────────────────┬──────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────┐ ┌──────────────────┐
│ Semantic Search │ │ BM25 │ │ Host Detection │
│ (Embeddings) │ │ (Keywords)│ │ "about Ben?" → │
│ │ │ │ │ search Ben first │
│ all-MiniLM-L6-v2 │ │ rank-bm25│ │ │
│ 384-dim vectors │ │ │ │ │
└────────┬─────────┘ └────┬─────┘ └──────────────────┘
│ │
└───────┬────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Cross-Encoder Re-Ranker │
│ ms-marco-MiniLM-L-6-v2 │
│ │
│ Takes ~40 candidates from both searches │
│ Scores each one against your actual question │
│ Keeps the top 8 │
└──────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ LLM (Mistral Nemo 12B via Ollama) │
│ │
│ Gets: system prompt (personality) + conversation │
│ history + 8 evidence segments + your question │
│ │
│ Returns: a snarky, cited summary │
└──────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Response Builder │
│ - Adds clickable episode links (workingcode.dev) │
│ - Text fragment URLs scroll to the exact quote │
│ - Checks for achievements, fallacies, spelling │
│ - DMs achievements with punk zine badge art │
└─────────────────────────────────────────────────────────┘
Before the bot can answer anything, we had to feed it the entire podcast:
252 episodes of audio
↓
Whisper transcription → JSON transcripts with speaker labels
↓
54,800+ segments (speaker + text chunks)
↓
Sentence-transformers encodes each segment → 384-dim embeddings
↓
rag_index.json (15 MB) + rag_embeddings.npy (40 MB)
↓
BM25 index built at startup from segment text
↓
Host attendance computed from speaker metadata
New episodes are added weekly with one command:
python update_index.pyThis checks the RSS feed, scrapes any new transcripts from workingcode.dev, and rebuilds the full index in ~4 minutes.
Because neither one is good enough alone. Seriously.
| Search Type | Good At | Bad At |
|---|---|---|
| Semantic (embeddings) | "What does Tim think about databases?" → finds segments about database opinions even if they don't say "database" | Finding specific names like "PostgreSQL" when the query says "favorite database" |
| BM25 (keywords) | "PostgreSQL" → finds every mention of that exact word | Understanding that "favorite database" and "I love Postgres" are related |
We run both, merge the results, and let the cross-encoder sort out who actually won.
The embedding search is fast but dumb — it encodes the question and segments separately and compares vectors. The cross-encoder is slow but smart — it reads the question and each candidate together and decides "does this actually answer the question?"
Think of it like this:
- Embedding search: speed dating. Quick vibes check on 54,000 segments.
- Cross-encoder: the actual date. Deep conversation with the top 40 candidates.
We use Mistral Nemo 12B running locally via Ollama. It's connected through a Cloudflare Tunnel so the Discord bot (which could run on a cheap cloud server) can reach Ollama on a home machine.
Why not GPT-4 or Claude? Because:
- Free is better than not free
- The bot runs 24/7 — API costs add up
- 12B parameters is actually plenty when you're just summarizing evidence, not reasoning about quantum physics
- We like running things locally. We're that kind of podcast.
ShowBot keeps a buffer of the last 10 messages per user — tracked by your Discord user ID, not by channel. But here's the thing: it doesn't always use them.
ShowBot's memory is a fallback system, not a conversation engine. Here's what actually happens when you ask a question:
- ShowBot searches the transcripts using your question as-is
- Only if that search comes back empty does it pull in your previous question, glue it to your current one, and retry the search
- If that enriched search finds results, it passes your conversation history to the LLM so it can write a contextual answer
- If your first search already found good results? Your history sits there unused. ShowBot doesn't even tell the LLM about your prior conversation.
So "tell me more" works — but only because "tell me more" by itself returns zero search results, which triggers the fallback that prepends your last real question. It's not intelligence. It's a safety net that happens to look like memory.
The limitations — and they're real, crawler:
- It's a fallback, not a feature. If your follow-up question happens to match transcript segments on its own, ShowBot answers without any conversation context. It doesn't know or care what you asked before.
- 10 messages. That's it. Message eleven pushes message one into the void. Like a temp buff that expired while you were still reading the tooltip.
- Memory is RAM-only. When ShowBot restarts, your entire conversation history vanishes. No database. No save file. No continue screen. You're starting a fresh run every time the bot reboots.
- It's per-user, not per-channel. If you ask something in #showbot and then use
/summarizein #general, it's all the same thread to ShowBot. - Follow-ups only work if there's context. If ShowBot just restarted and you waltz in with "tell me more" — more about what? ShowBot doesn't know you. You're a stranger now. Start over.
It's not a chat agent — it's more like a party member with amnesia who occasionally gets a flashback when nothing else works, then blacks out again when you return to town.
When ShowBot detects a "how many" question, it bypasses the LLM entirely and does real counting:
- Keyword counting: scans all 54,800+ segments for exact matches, per-host breakdown, top episodes
- Attendance tracking: pre-computed at startup from transcript speaker metadata — who was in which episode, who missed what
This means "how many episodes has Carol missed?" gives you an exact number (73), not an LLM guess.
Achievements persist in SQLite (achievements.db) across restarts. The system tracks:
- Earned achievements per user (never repeat)
- Question counts, typo counts, fallacy counts
- Host mention tracking
- Question history for repeat detection
Each achievement has 20+ random DCC-style text variants (Dungeon Crawler Carl narrator voice — rambling, ominous, full of 80s/90s nostalgia). Badge art is generated in WP Punks punk zine style and attached to DMs.
Every response includes clickable episode links that use text fragments to scroll directly to the quoted text on workingcode.dev. Chrome, Edge, and Firefox all support this.
A link looks like:
https://workingcode.dev/episodes/108-2022-year-in-review/#:~:text=other%20than%20that%2C%20it%27s%20a%20great%20database
Click it and your browser highlights the exact words the bot is referencing. Trust but verify.
| Component | Technology | Why |
|---|---|---|
| Bot framework | disnake 2.x | discord.py fork with better slash command support |
| Embeddings | sentence-transformers (all-MiniLM-L6-v2) | Fast, small (80MB), good enough |
| Keyword search | rank-bm25 | Classic IR algorithm, zero dependencies |
| Re-ranker | cross-encoder (ms-marco-MiniLM-L-6-v2) | Best accuracy-per-MB in the business |
| LLM | Mistral Nemo 12B (Q4 quantized) | Smart enough to be snarky, small enough to run at home |
| LLM server | Ollama | One command to run any model |
| Tunnel | Cloudflare Tunnel | Free, connects cloud bot to home LLM |
| Transcript data | NumPy + JSON | 55 MB total. Fits in RAM. No database needed |
| Achievements | SQLite | Zero-config persistence for user data |
| Spellcheck | pyspellchecker | Lightweight, customizable dictionary |
| Badge art | DALL-E 3 generated | WP Punks punk zine sticker aesthetic |
Total RAM usage: ~1.5 GB for the bot + models (no LLM). The LLM runs separately on whatever machine has the GPU (or CPU patience).
- It can't make stuff up. Every answer comes from actual transcript evidence. If the hosts never talked about it, ShowBot says so.
- It sometimes gets sentiment wrong. Sarcasm in transcripts is hard. When Adam says "oh GREAT, another framework" — is that genuine or sarcastic? The LLM guesses. Sometimes it guesses wrong.
- Episode summaries are AI-generated. They're based on transcript excerpts, not human curation. Take them as a starting point, not gospel.
The whole thing is open source. You need:
- Python 3.9+
- A Discord bot token
- The transcript data (rag_index.json + rag_embeddings.npy)
- Ollama with Mistral Nemo 12B (for summaries) — or it works without for search-only mode
- About 30 minutes and a tolerance for reading startup logs
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your Discord token
python discord_bot.pypython update_index.py # Check RSS, scrape new transcripts, rebuild index
python update_index.py --dry-run # Preview what would be updatedThat's it. No Kubernetes. No microservices. Ben would be proud.