[Hardening] Budget Discord embed total size (6000 chars) across all bot embeds #94

Closed
opened 2026-07-14 19:48:10 +00:00 by claude-bot · 2 comments
Contributor

Context

Discord enforces two separate embed limits: the description alone may be up to 4096 characters, but the whole embed (title + description + all field names/values + footer) may not exceed 6000 characters total. Exceeding the total raises discord.HTTPException (400) and the entire send fails.

The bot's embed builders only guard the description:

  • /recap (bot/questboard_bot/cogs/sessions.py:140-171): truncates the summary to 4000 (session.summary[:4000], line 151) but then adds a "GM Notes" field up to 1000 chars (lines 154-158), a session-date field (line 147), a party-feedback field (lines 160-168), title, and footer. Combined worst case comfortably exceeds 6000 — the whole /recap command fails with an HTTP error for long summaries + long notes.
  • Summary notification embeds do the same summary[:4000] truncation at bot/questboard_bot/cogs/notifications.py:485 and :545, each followed by additional add_field calls (e.g. :488, :588).

Current behavior

A session whose summary is near 4000 chars plus ~1000 chars of GM notes (plus title/fields/footer) produces an embed over 6000 total; the followup.send(embed=...) raises and the user sees a failed command instead of a truncated recap.

Fix / Spec

  1. Add a shared helper in bot/questboard_bot/utils/ (e.g. embed_budget.py or extend an existing utils module):
    • Signature idea: fit_embed(embed: discord.Embed, max_total: int = 6000) -> discord.Embed or a builder that accepts description + fields and assembles under budget.
    • Compute total length (title + description + every field name/value + footer text, matching Discord's counting), and if over budget, truncate the description last (fields are short and informative; the description is the compressible part), appending an ellipsis marker (e.g. ).
    • Keep individual limits too: description <= 4096, field value <= 1024.
  2. Use the helper in /recap (sessions.py) and both summary embeds (notifications.py:485, :545), replacing the bare [:4000] slices.
  3. Unit-test the helper directly (no Discord API needed): over-budget input yields total <= 6000 with ellipsis; under-budget input passes through unchanged.

Acceptance criteria

  • A recap whose combined content exceeds 6000 chars posts successfully with visible truncation (ellipsis) instead of raising HTTPException.
  • Helper unit tests cover: over-budget total, over-limit description, under-budget passthrough.
  • All three call sites (sessions.py recap, notifications.py:485, :545) use the shared helper.

References

  • bot/questboard_bot/cogs/sessions.py:140-171 (recap embed assembly; [:4000] at 151, notes field at 154-158)
  • bot/questboard_bot/cogs/notifications.py:485, :545 (summary[:4000] descriptions with subsequent fields)
  • Discord limits: description 4096, field value 1024, total 6000

Filed from the July 2026 full-project review.

## Context Discord enforces two separate embed limits: the description alone may be up to 4096 characters, but the **whole embed** (title + description + all field names/values + footer) may not exceed **6000** characters total. Exceeding the total raises `discord.HTTPException` (400) and the entire send fails. The bot's embed builders only guard the description: - `/recap` (`bot/questboard_bot/cogs/sessions.py:140-171`): truncates the summary to 4000 (`session.summary[:4000]`, line 151) but then adds a "GM Notes" field up to 1000 chars (lines 154-158), a session-date field (line 147), a party-feedback field (lines 160-168), title, and footer. Combined worst case comfortably exceeds 6000 — the whole `/recap` command fails with an HTTP error for long summaries + long notes. - Summary notification embeds do the same `summary[:4000]` truncation at `bot/questboard_bot/cogs/notifications.py:485` and `:545`, each followed by additional `add_field` calls (e.g. `:488`, `:588`). ## Current behavior A session whose summary is near 4000 chars plus ~1000 chars of GM notes (plus title/fields/footer) produces an embed over 6000 total; the `followup.send(embed=...)` raises and the user sees a failed command instead of a truncated recap. ## Fix / Spec 1. Add a shared helper in `bot/questboard_bot/utils/` (e.g. `embed_budget.py` or extend an existing utils module): - Signature idea: `fit_embed(embed: discord.Embed, max_total: int = 6000) -> discord.Embed` or a builder that accepts description + fields and assembles under budget. - Compute total length (title + description + every field name/value + footer text, matching Discord's counting), and if over budget, truncate the **description last** (fields are short and informative; the description is the compressible part), appending an ellipsis marker (e.g. `…`). - Keep individual limits too: description <= 4096, field value <= 1024. 2. Use the helper in `/recap` (`sessions.py`) and both summary embeds (`notifications.py:485`, `:545`), replacing the bare `[:4000]` slices. 3. Unit-test the helper directly (no Discord API needed): over-budget input yields total <= 6000 with ellipsis; under-budget input passes through unchanged. ## Acceptance criteria - A recap whose combined content exceeds 6000 chars posts successfully with visible truncation (ellipsis) instead of raising `HTTPException`. - Helper unit tests cover: over-budget total, over-limit description, under-budget passthrough. - All three call sites (`sessions.py` recap, `notifications.py:485`, `:545`) use the shared helper. ## References - `bot/questboard_bot/cogs/sessions.py:140-171` (recap embed assembly; `[:4000]` at 151, notes field at 154-158) - `bot/questboard_bot/cogs/notifications.py:485`, `:545` (`summary[:4000]` descriptions with subsequent fields) - Discord limits: description 4096, field value 1024, total 6000 _Filed from the July 2026 full-project review._
Author
Contributor

Picking this up as part of a v3.3.0 push. Landing on branch hardening/bot together with #83, #85, and #111 (grouped by component to keep the diffs reviewable).

Picking this up as part of a v3.3.0 push. Landing on branch `hardening/bot` together with #83, #85, and #111 (grouped by component to keep the diffs reviewable).
Author
Contributor

Fixed on main (commit d5866ce, merged via cd315d6). New bot/questboard_bot/utils/embed_budget.py with fit_embed(embed, max_total=6000, ...) that counts the whole embed the way Discord does (title + description + every field name/value + footer) and truncates the description last with an ellipsis, while also enforcing description ≤ 4096 and field value ≤ 1024. Applied at all three call sites: /recap (sessions.py) and both summary embeds in notifications.py. Helper unit-tested directly (over-budget total, over-limit description, over-limit field value, under-budget passthrough). Bot suite green (158 passed).

Fixed on `main` (commit `d5866ce`, merged via `cd315d6`). New `bot/questboard_bot/utils/embed_budget.py` with `fit_embed(embed, max_total=6000, ...)` that counts the whole embed the way Discord does (title + description + every field name/value + footer) and truncates the description last with an ellipsis, while also enforcing description ≤ 4096 and field value ≤ 1024. Applied at all three call sites: `/recap` (`sessions.py`) and both summary embeds in `notifications.py`. Helper unit-tested directly (over-budget total, over-limit description, over-limit field value, under-budget passthrough). Bot suite green (158 passed).
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#94
No description provided.