feat: send multi-line announcements as IRCv3 draft/multiline batches #22

Merged
alex merged 1 commit from pi/issue21-1786803087726 into master 2026-08-15 14:13:24 +00:00
Contributor

Fixes #21

What

Implements the client side of the IRCv3 draft/multiline spec, and uses it for multi-line announcements — most visibly Forgejo release notes, which previously arrived as one separate IRC message per note line.

How

@irc/client 0.23.x (the latest) has no multiline plugin, so the extension is implemented in-tree following the repo's pure-logic/wiring split:

  • src/multiline.ts (new, pure): parses the max-bytes/max-lines values from the draft/multiline CAP advertisement, plans how a message is delivered (batch vs plain per-line fallback), splits over-long lines at word boundaries into draft/multiline-concat continuations (multi-byte safe, trailing space kept per the spec's recommendation so halves re-join cleanly), enforces the server's limits with truncation + a marker, and builds the wire commands.
  • src/irc/multiline.ts (new, wiring): requests the cap during registration (the library's batch plugin already negotiates the required batch dependency), captures the limits from the raw CAP LS/NEW exchange (incl. multi-line LS 302 continuation lines), and posts either one well-formed batch (BATCH +ref draft/multiline <target> / tagged PRIVMSGs / BATCH -ref, sends chained so lines are never interleaved) or one plain PRIVMSG per non-blank line as fallback. State is keyed per client instance, so reconnects reset cleanly.
  • src/irc/client.ts: attaches the multiline session on every (re)connection attempt.
  • src/poller/forgejo-poller.ts / feed-poller.ts: post through the multiline-aware poster; on servers without the extension behavior is unchanged (one message per line).
  • src/forgejo-activity.ts: postMessageLines is gone — the whole multi-line notes text is now handed to the injected post in one piece, letting the production poster deliver it as a single batch.
  • Docs: README (feature bullet + release-notes paragraph) and AGENTS.md (structure entries + a "note on IRCv3 multiline").

Per-line byte budgets use the spec's worst-case relay formula (512 − 14 − 10 − nick/user/host), so servers can relay each line unchanged; limits are honored to avoid FAIL BATCH rejections.

Known limitation (documented in AGENTS.md): blank lines inside a batch are dropped, because @irc/client's send() cannot emit an empty trailing PRIVMSG parameter.

Verification

  • New unit tests for both modules (src/multiline_test.ts, src/irc/multiline_test.ts): cap parsing, planning, concat splits, byte-safety, truncation, wire shape, negotiation capture, fallbacks.

  • End-to-end smoke test against a scripted IRC server through the real @irc/client (not committed) confirmed the actual bytes on the wire: CAP REQ :batch draft/multiline, then

    BATCH +m1 draft/multiline #chan
    @batch=m1 PRIVMSG #chan :first line
    @batch=m1 PRIVMSG #chan :second paragraph
    BATCH -m1
    

    plus the draft/multiline-concat form for over-long lines, all within the 512-byte frame limit.

  • deno fmt --check, deno lint, deno check, deno test (305 passed), and deno task fallow all green.

Fixes #21 ## What Implements the client side of the [IRCv3 `draft/multiline` spec](https://ircv3.net/specs/extensions/multiline), and uses it for multi-line announcements — most visibly Forgejo release notes, which previously arrived as one separate IRC message per note line. ## How `@irc/client` 0.23.x (the latest) has no multiline plugin, so the extension is implemented in-tree following the repo's pure-logic/wiring split: - **`src/multiline.ts`** (new, pure): parses the `max-bytes`/`max-lines` values from the `draft/multiline` CAP advertisement, plans how a message is delivered (batch vs plain per-line fallback), splits over-long lines at word boundaries into `draft/multiline-concat` continuations (multi-byte safe, trailing space kept per the spec's recommendation so halves re-join cleanly), enforces the server's limits with truncation + a `…` marker, and builds the wire commands. - **`src/irc/multiline.ts`** (new, wiring): requests the cap during registration (the library's `batch` plugin already negotiates the required `batch` dependency), captures the limits from the raw `CAP LS`/`NEW` exchange (incl. multi-line LS 302 continuation lines), and posts either one well-formed batch (`BATCH +ref draft/multiline <target>` / tagged `PRIVMSG`s / `BATCH -ref`, sends chained so lines are never interleaved) or one plain `PRIVMSG` per non-blank line as fallback. State is keyed per client instance, so reconnects reset cleanly. - **`src/irc/client.ts`**: attaches the multiline session on every (re)connection attempt. - **`src/poller/forgejo-poller.ts` / `feed-poller.ts`**: post through the multiline-aware poster; on servers without the extension behavior is unchanged (one message per line). - **`src/forgejo-activity.ts`**: `postMessageLines` is gone — the whole multi-line notes text is now handed to the injected `post` in one piece, letting the production poster deliver it as a single batch. - Docs: README (feature bullet + release-notes paragraph) and AGENTS.md (structure entries + a "note on IRCv3 multiline"). Per-line byte budgets use the spec's worst-case relay formula (`512 − 14 − 10 − nick/user/host`), so servers can relay each line unchanged; limits are honored to avoid `FAIL BATCH` rejections. Known limitation (documented in AGENTS.md): blank lines inside a batch are dropped, because `@irc/client`'s `send()` cannot emit an empty trailing `PRIVMSG` parameter. ## Verification - New unit tests for both modules (`src/multiline_test.ts`, `src/irc/multiline_test.ts`): cap parsing, planning, concat splits, byte-safety, truncation, wire shape, negotiation capture, fallbacks. - End-to-end smoke test against a scripted IRC server through the **real** `@irc/client` (not committed) confirmed the actual bytes on the wire: `CAP REQ :batch draft/multiline`, then ``` BATCH +m1 draft/multiline #chan @batch=m1 PRIVMSG #chan :first line @batch=m1 PRIVMSG #chan :second paragraph BATCH -m1 ``` plus the `draft/multiline-concat` form for over-long lines, all within the 512-byte frame limit. - `deno fmt --check`, `deno lint`, `deno check`, `deno test` (305 passed), and `deno task fallow` all green.
feat: send multi-line announcements as IRCv3 draft/multiline batches
All checks were successful
PR / deno (pull_request) Successful in 45s
PR / fallow (pull_request) Successful in 28s
Build and Push Container Image / build-and-push-1 (push) Successful in 1m28s
Build and Push Container Image / build-and-push (push) Successful in 0s
Build and Push Container Image / deploy-1 (push) Successful in 4s
Build and Push Container Image / deploy (push) Successful in 0s
7e99d66677
Co-authored-by: alex <alex@forge.l3x.in>
alex approved these changes 2026-08-15 14:13:12 +00:00
alex merged commit 7e99d66677 into master 2026-08-15 14:13:24 +00:00
alex deleted branch pi/issue21-1786803087726 2026-08-15 14:13:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
dependencies
No milestone
No assignees
2 participants
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
alex/irc-greeter-bot!22
No description provided.