fix(bot): posts name the campaign's timezone and hold no unvetted person text (#1166, #1167) #1169

Merged
claude-bot merged 6 commits from fix/1166-1167-bot-posts into main 2026-10-13 07:36:37 +00:00
Collaborator

Closes #1166 and #1167.

Timezone (#1167). Every post that shows a session time now reads Tuesday 13 October at 8:00 pm CDT (<t:…:t> your time): the campaign's zone with its abbreviation, then each reader's own time. The backend adds the campaign's IANA zone to each event's extra (bot_post_facts.session_facts, and notification_dm); the bot resolves it with zoneinfo and never prints the name. No zone, or one that does not resolve, falls back to the old <t:…:F>. Applies to poll slots (wide fields, bracket on every line; a 10-slot poll is pinned by a test), the closed poll, time added, confirmed, reminder (keeps the relative time), cancelled, vote counts and the nudge.

Person-written text (#1166). No escaping for Discord's parser; structure instead.

  1. Plain lines that ping (vote reminder, nudge) carry no person-written text. The vote reminder names the session by number and counts the unlinked ("2 others have not linked Discord: …") instead of naming them.
  2. People's names in descriptions and field values go through plain_name, an allow-list (letters with their combining marks, digits, spaces, ' - .; 40 characters).
  3. GM-written prose (approved summary, recap excerpt, cancel reason, reveal text, a session title that falls back into a description, quote text) is trusted as the GM's own post and keeps its markdown. The boundary is stated at the top of faces.py and in docs/INTEGRATIONS.md.
  4. The link field is built only from a URL that passes safe_url (http/https, no whitespace or brackets, the app's host when QUESTBOARD_PUBLIC_URL is set to a real value).
  5. Embed titles and footers are plain text and unchanged.
  6. bot/tests/test_faces_text.py has a row for every public function in faces; one without a row fails.

Additive on the wire: no BOT_CONTRACT_VERSION bump. The backend still sends unlinked_names; the bot only counts them.

Verified

On docker-test (before merging main): bot suite (1181 passed, 31 skipped); backend bot-event modules and the wiki guard tests (187), wider event selection (45); ruff; version and changelog checks.

Not run: full backend and frontend suites, build, anything after merging main (clean merge). Not checkable on dev: the dev bot has no Discord token.

🤖 Generated with Claude Code

Closes #1166 and #1167. **Timezone (#1167).** Every post that shows a session time now reads `Tuesday 13 October at 8:00 pm CDT (<t:…:t> your time)`: the campaign's zone with its abbreviation, then each reader's own time. The backend adds the campaign's IANA zone to each event's `extra` (`bot_post_facts.session_facts`, and `notification_dm`); the bot resolves it with `zoneinfo` and never prints the name. No zone, or one that does not resolve, falls back to the old `<t:…:F>`. Applies to poll slots (wide fields, bracket on every line; a 10-slot poll is pinned by a test), the closed poll, time added, confirmed, reminder (keeps the relative time), cancelled, vote counts and the nudge. **Person-written text (#1166).** No escaping for Discord's parser; structure instead. 1. Plain lines that ping (vote reminder, nudge) carry no person-written text. The vote reminder names the session by number and counts the unlinked ("2 others have not linked Discord: …") instead of naming them. 2. People's names in descriptions and field values go through `plain_name`, an allow-list (letters with their combining marks, digits, spaces, `' - .`; 40 characters). 3. GM-written prose (approved summary, recap excerpt, cancel reason, reveal text, a session title that falls back into a description, quote text) is trusted as the GM's own post and keeps its markdown. The boundary is stated at the top of `faces.py` and in `docs/INTEGRATIONS.md`. 4. The link field is built only from a URL that passes `safe_url` (http/https, no whitespace or brackets, the app's host when `QUESTBOARD_PUBLIC_URL` is set to a real value). 5. Embed titles and footers are plain text and unchanged. 6. `bot/tests/test_faces_text.py` has a row for every public function in `faces`; one without a row fails. Additive on the wire: no `BOT_CONTRACT_VERSION` bump. The backend still sends `unlinked_names`; the bot only counts them. ## Verified On `docker-test` (before merging main): bot suite (1181 passed, 31 skipped); backend bot-event modules and the wiki guard tests (187), wider event selection (45); ruff; version and changelog checks. Not run: full backend and frontend suites, `build`, anything after merging main (clean merge). Not checkable on dev: the dev bot has no Discord token. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Every event with a session time carries extra.timezone; faces.when renders it
in that zone with the reader's own time beside it. Names of people go through
plain_name (an allow-list), the vote reminder names no one and uses the
session number, the link field passes one shared validator, and GM prose is
documented as trusted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into fix/1166-1167-bot-posts
Some checks failed
CI / Does this change touch the recording path? (pull_request) Successful in 17s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Backend lint (ruff) (pull_request) Successful in 30s
CI / Bot/backend version sync (pull_request) Successful in 32s
CI / Feedback site tests (pull_request) Successful in 53s
CI / Bot tests and audit (pull_request) Successful in 2m15s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m19s
CI / Docker image build (pull_request) Successful in 4m5s
CI / End-to-end journeys (phone) (pull_request) Failing after 12m44s
CI / End-to-end journeys (keyboard) (pull_request) Failing after 14m18s
CI / Frontend (shard 2/2) (pull_request) Successful in 19m2s
CI / End-to-end journeys (desktop) (pull_request) Has been cancelled
CI / Frontend (shard 1/2) (pull_request) Has been cancelled
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
70571fc41e
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-10-13 05:50:01 +00:00
test(e2e): the DM event carries the campaign's timezone (#1167)
Some checks failed
CI / Does this change touch the recording path? (pull_request) Successful in 28s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 47s
CI / Backend lint (ruff) (pull_request) Successful in 53s
CI / Feedback site tests (pull_request) Successful in 1m7s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m26s
CI / Bot tests and audit (pull_request) Successful in 2m39s
CI / Docker image build (pull_request) Failing after 3m1s
CI / End-to-end journeys (desktop) (pull_request) Has been skipped
CI / End-to-end journeys (keyboard) (pull_request) Has been skipped
CI / End-to-end journeys (phone) (pull_request) Has been skipped
CI / Frontend (shard 2/2) (pull_request) Successful in 8m34s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m24s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
f64651c653
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci: re-run after a runner build-cache failure
Some checks failed
CI / Does this change touch the recording path? (pull_request) Successful in 17s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 28s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m1s
CI / Backend lint (ruff) (pull_request) Successful in 1m7s
CI / Feedback site tests (pull_request) Successful in 1m22s
CI / Docker image build (pull_request) Failing after 2m34s
CI / End-to-end journeys (desktop) (pull_request) Has been skipped
CI / End-to-end journeys (keyboard) (pull_request) Has been skipped
CI / End-to-end journeys (phone) (pull_request) Has been skipped
CI / Bot tests and audit (pull_request) Successful in 2m53s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m26s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m17s
CI / Backend migration, tests, and audit (pull_request) Successful in 32m7s
b1a7688701
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci: re-run after a second runner build-cache failure
All checks were successful
CI / Docker image build (pull_request) Successful in 1m1s
CI / Bot/backend version sync (pull_request) Successful in 1m8s
CI / Backend lint (ruff) (pull_request) Successful in 1m16s
CI / Does this change touch the recording path? (pull_request) Successful in 24s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Feedback site tests (pull_request) Successful in 1m49s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m15s
CI / Bot tests and audit (pull_request) Successful in 3m36s
CI / Frontend (shard 2/2) (pull_request) Successful in 9m30s
CI / Frontend (shard 1/2) (pull_request) Successful in 11m18s
CI / End-to-end journeys (phone) (pull_request) Successful in 13m12s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 14m34s
CI / End-to-end journeys (desktop) (pull_request) Successful in 20m1s
CI / Backend migration, tests, and audit (pull_request) Successful in 33m11s
6f47458573
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot deleted branch fix/1166-1167-bot-posts 2026-10-13 07:36:37 +00:00
Sign in to join this conversation.
No description provided.