Skip to content

Releases

v0.9.1 — Whisper service now opt-in

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 16 Mar 00:17

Fixes a startup error that occurred when the Whisper transcription container image was unavailable. The whisper service is now opt-in via a Docker Compose profile and is no longer pulled on a standard docker compose up -d.

Changed

  • docker-compose.yml — whisper service moved to profiles: [whisper]; removed depends_on: - whisper from the bot service so the bot starts cleanly without it
  • README.md — Quick Start adds the optional --profile whisper command; Recording section documents both transcription backend options; added Read Message History to the required bot permissions list; added git clone step
  • .env.example — added comment explaining that WHISPER_MODE=local requires --profile whisper

Upgrading

If you were previously running the bundled Whisper container, start it explicitly going forward:

docker compose --profile whisper up -d

v0.9.0 — Session commands & campaign Q&A

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:45

Session commands and campaign Q&A. All four slash commands are implemented bot-side; /next, /recap, and /note require the Quest Board v0.9.0 backend endpoints; /ask requires the v0.10.0 endpoint. Until those are deployed the commands return a clear error rather than crashing.

Added

  • bot/cogs/sessions.py — new cog with four slash commands:

    • /next — fetches the next confirmed session for the guild via GET /api/bot/guilds/{guild_id}/next-session and posts a green embed with <t:{ts}:F> / <t:{ts}:R> countdown; graceful 404 handling
    • /recap <session_id> — fetches summary and GM notes via GET /api/bot/sessions/{session_id}/summary; shows summary as embed description, GM notes as a field, or a prompt to use /record start if neither is present yet
    • /note <text> [session_id] — verifies the user is linked, auto-resolves to the next upcoming session if no session_id is given, calls POST /api/bot/sessions/{session_id}/notes; all responses ephemeral (notes are private)
    • /ask <question> — fetches up to 10 completed sessions with summaries, passes them to services/qa.answer_question, and posts the answer as an embed; footer shows how many sessions were searched
  • bot/services/qa.py — campaign Q&A service:

    • answer_question(question, sessions, campaign_name, game_system, settings)
    • Reuses the same Ollama / Anthropic / OpenAI backends and mode selection as summarisation.py; no new config required
    • System prompt instructs the LLM to answer only from provided summaries and say clearly when it lacks information
    • Per-session summaries truncated to 800 chars to stay within model limits

Changed

  • bot/api_client.py — four new response models and async methods:

    • NextSessionResponse + get_next_session(guild_id)
    • SessionSummaryResponse + get_session_summary(session_id)
    • post_session_note(session_id, discord_user_id, note)
    • SessionHistoryItem + get_session_history(guild_id, limit=10)
  • bot/main.py — added "bot.cogs.sessions" to _COGS

Notes

/next, /recap, and /note require Quest Board backend endpoints added in v0.9.0 of the Quest Board app. /ask additionally requires the v0.10.0 history endpoint. See docs/questboard-improvements.md for implementation details.

v0.8.0 — Attendance RSVP

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:45

Attendance RSVP. Session-confirmed embeds now include ✅ / ❌ reactions so players can indicate attendance directly from Discord. Reactions write to Quest Board's existing attendance table — no new backend endpoints required.

Changed

  • bot/cogs/notifications.py

    • _handle_confirmed — adds an "Attendance" field to the confirmed embed, seeds ✅ then ❌ reactions (0.5 s apart), and stores the message→session mapping with type: "attendance" so the voting cog routes reactions correctly
    • _store_message_map — accepts a new map_type parameter (default "voting"); stores "type" key in the mapping payload; backwards-compatible with existing Redis entries (missing key defaults to "voting")
    • Added _ATTENDANCE_EMOJIS = ["✅", "❌"] constant
  • bot/cogs/voting.py

    • _handle_reaction now reads mapping["type"] and branches:
      • "attendance" → delegates to new _handle_attendance_reaction: ✅ calls put_attendance(attended=True), ❌ calls put_attendance(attended=False); other emojis ignored
      • "voting" (default) → delegates to new _handle_vote_reaction (existing logic, now extracted to its own method)
    • Added _ATTEND_YES / _ATTEND_NO constants
  • docs/questboard-improvements.md — added four new entries for the Quest Board backend endpoints required by bot v0.9.0 and v0.10.0

v0.7.0 — Polish & hardening

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:44

Polish and hardening. Remote settings, pre-commit security hooks, and a full setup guide in the README.

Added

  • .pre-commit-config.yaml — pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-added-large-files (500 KB limit), check-merge-conflict, detect-private-key, and a local pip-audit hook that runs on every change to requirements.txt
  • README — full setup guide covering: Discord Developer Portal walkthrough, Quest Board admin configuration, account linking, reaction voting, recording pipeline, Whisper model trade-offs, Ollama model recommendations, and a troubleshooting section

Changed

  • bot/main.py — on_ready now calls _apply_remote_settings, which fetches GET /api/bot/settings from Quest Board and applies admin-configured Whisper/LLM endpoints, models, and API keys on top of env-var defaults; failure is logged as a warning and does not prevent the bot from starting; API keys are never logged (placeholder string used in log line)

v0.6.0 — Recording & transcription

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:43

Voice recording, transcription, and summarisation pipeline. GMs can record a voice session directly from Discord; the audio is automatically transcribed and summarised, and the results are uploaded to Quest Board.

Added

  • bot/cogs/recording.py — full implementation replacing the v0.1.0 stub:

    • /record start <session_id> — validates the UUID, requires the GM to be in a voice channel, fetches session metadata from the API for the announcement embed, connects to voice and starts a discord.sinks.WaveSink; schedules an auto-stop at MAX_RECORDING_HOURS
    • /record stop — stops the sink, disconnects from voice, hands off to the processing pipeline as a background task
    • Processing pipeline: collects per-user WAV files from the sink → mixes to mono MP3 (utils/audio) → deletes WAVs → transcribes (services/transcription) → summarises (services/summarisation) → uploads via POST /api/bot/sessions/{id}/transcript → posts a summary embed; cleans up the MP3 on completion or error
    • Auto-stop fires after MAX_RECORDING_HOURS with a channel notice
    • One active recording per guild enforced; clear errors if already recording or if the GM is not in a voice channel
    • Transcript content is never logged — only metadata (duration, word count, file sizes)
  • bot/utils/audio.py — mix_to_mp3(wav_paths, output_path) wraps an FFmpeg subprocess; single-input path skips amix filter; logs file size on completion; raises RuntimeError on non-zero FFmpeg exit

  • bot/services/transcription.py — transcribe(mp3_path, settings):

    • WHISPER_MODE=local — multipart POST to ahmetoner/whisper-asr-webservice at WHISPER_API_URL/asr
    • WHISPER_MODE=api — OpenAI POST /v1/audio/transcriptions (whisper-1)
    • 10-minute timeout; raises RuntimeError on failure
  • bot/services/summarisation.py — summarise(transcript, campaign_name, game_system, settings):

    • SUMMARISER_MODE=ollama — POST /api/generate to local Ollama
    • SUMMARISER_MODE=api — Anthropic claude-haiku-4-5-20251001 if ANTHROPIC_API_KEY set, otherwise OpenAI gpt-4o-mini
    • TTRPG-focused system prompt: key events, NPC introductions, player decisions, and a "What's next?" hook
    • 5-minute timeout; raises RuntimeError on failure

v0.5.0 — Reaction voting

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:43

Reaction voting. Emoji reactions on session_proposed messages are now translated into Quest Board availability votes in real time.

Changed

  • bot/cogs/voting.py — full implementation replacing the v0.1.0 stub:
    • on_raw_reaction_add — records availability="yes" when a user adds a slot emoji (🇦–🇪) to a session_proposed message
    • on_raw_reaction_remove — records availability="no" when a user removes a slot emoji
    • Looks up the message→session mapping via NotificationsCog.get_message_mapping (Redis-backed, 30-day TTL) to resolve emoji position to a slot_id without a Quest Board round-trip
    • Calls GET /api/bot/platform-links/discord/{user_id} before voting; on 404 sends the user a DM prompting them to run /link
    • Ignores the bot's own seed reactions, non-slot emojis, and messages with no stored mapping; logs and swallows API errors so transient failures don't surface as Discord errors

v0.4.0 — Account linking

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:43

Discord account linking. Players can connect their Discord identity to Quest Board so that emoji reactions on session messages are recorded as votes against their Quest Board account.

Changed

  • bot/cogs/linking.py — full implementation replacing the v0.1.0 stub:
    • /link — generates a secrets.token_hex(32) token, registers it in Quest Board's Redis via POST /api/bot/linking-tokens (TTL 10 min), sends the user a DM with {questboard_public_url}/auth/link?token=<token>, then polls GET /api/bot/link-status/{token} every 30 s for up to 10 min; sends a confirmation DM on success or an expiry DM on timeout; handles discord.Forbidden (DMs disabled) and Quest Board API errors gracefully
    • /unlink — directs the user to their Quest Board profile page; a dedicated bot-facing unlink endpoint is tracked in docs/questboard-improvements.md (Priority 3)

v0.3.0 — Discord notifications

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:42

Bot-driven Discord notifications. Quest Board's Celery tasks now call the bot's HTTP server instead of posting plain webhook embeds on campaigns that have a guild_id configured.

Added

  • bot/cogs/notifications.py — full implementation replacing the v0.1.0 stub:
    • on_bot_notify listener routes incoming payloads to per-event handlers
    • _resolve_channel — fetches TextChannel by ID with clear logging on NotFound / Forbidden errors
    • session_proposed — rich embed with one field per time slot using Discord <t:timestamp:F> localised timestamps; slot details fetched from GET /api/bot/sessions/{id}/timeslots; seed reactions 🇦–🇪 added immediately after posting (0.5 s apart to respect rate limits)
    • session_confirmed — embed with confirmed time; campaign name fetched from API
    • session_reminder — embed with human-readable label (_reminder_label converts hours_until to "2 hours", "3 days", etc.) and confirmed time; campaign name fetched from API
    • session_cancelled — embed using title and campaign name from payload
    • vote_update — embed with per-slot yes / maybe / no counts fetched from API
    • get_message_mapping(message_id) — public method used by the voting cog (v0.5.0) to resolve a Discord message ID back to a session ID and ordered slot list
    • Redis-backed message→session store (qb_msg:{message_id}, 30-day TTL); falls back to an in-memory dict if REDIS_URL is not set or Redis is unreachable

v0.2.0 — API client alignment

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:42

Aligns the API client with the Quest Board v0.2.0 bot endpoints now live on the backend. No new bot behaviour in this release — all functional work was Quest Board-side (Campaign fields, five new bot endpoints, /auth/link, linking token Redis flow, and Celery notification routing).

Changed

  • bot/api_client.py — BotSettingsResponse updated to match the actual shape returned by GET /api/bot/settings: fields renamed from bot-centric names (whisper_mode, ollama_url, …) to the names Quest Board uses (whisper_endpoint_url, llm_endpoint_url, llm_model, …)

v0.1.0 — Initial scaffold

Choose a tag to compare

@10thTARDIS 10thTARDIS released this 15 Mar 23:42

Initial scaffold. Project structure, configuration, typed API client, and stub cogs. The bot connects to Discord and starts an HTTP server but does not yet send messages or record votes.

Added

  • Project structure — bot/ package with main.py, config.py, api_client.py; cogs/, services/, utils/ sub-packages
  • bot/config.py — Pydantic Settings reading from .env; all variables documented with defaults
  • bot/api_client.py — Async httpx wrapper for all Quest Board bot endpoints; sends X-Bot-Key header; typed Pydantic response models for every endpoint (existing and to-be-added in v0.2.0)
  • bot/main.py — QuestBoardBot (discord.py commands.Bot subclass) with required intents; aiohttp HTTP server at POST /notify and GET /health; startup audio-temp cleanup; graceful API client shutdown
  • Cog stubs — notifications, linking, voting, recording cogs all load cleanly; slash commands /link, /unlink, /record start, /record stop registered but return "coming soon" responses
  • Dockerfile — Python 3.12-slim, FFmpeg, non-root botuser
  • docker-compose.yml — bot + whisper services; audio temp volume; ollama service commented out (assumed running on home server)
  • requirements.txt — pinned direct and transitive dependencies
  • requirements.in — unpinned direct dependencies for pip-compile
  • requirements-dev.txt — pip-audit, pip-tools, pytest, pytest-asyncio, pre-commit
  • .env.example — all variables with inline documentation
  • .gitignore — .env, audio files, transcripts, Python artefacts