/** * One renderer per event type, all built on one skeleton — approved by * the owner on 20.08.2026 after ~15 live rounds in the test forum: * * #type #instance * icon Type: action * * Label: value *
quoted text from someone else — commit body, task body* * Group * #N (overdue): title * * Label: value ← actions/directions * * Three levels of styling, never mixed: a field is a bold, capitalized * label plus a plain value; a group is italic+underline, no bold and no * colon; line 2 (the type) follows the same field rule. A blank line * separates BLOCKS BY MEANING (header / body / actions), not mechanically * after every line. */ import { ICON, type NotifyEvent } from './events.ts'; /** Escapes EVERYTHING that comes from outside — only the template adds tags. */ export declare const esc: (v: unknown) => string; /** * Telegram cuts a message at 4096 characters — we cut it ourselves first, * on a line boundary where possible. * * Two traps, both caused a SILENT loss of the message: * 1. Cutting strictly at the last `\n` does not work: if a long chunk runs * as one line (a stack trace, command output — the most common `detail` * on an incident), the last line break sits BEFORE it, and the whole * content got dropped — only the heading arrived, with not a single * fact about what broke. * 2. Cutting in the middle of an HTML tag or entity does not work either: * Telegram replies `400 can't parse entities`, and we treat a 4xx as a * permanent error and do not retry — the message disappeared for good. */ export declare const clampMessage: (text: string, limit?: number, marker?: string) => string; /** * GitHub Markdown, read in Telegram. A PR or issue body arrives as the author * wrote it for GitHub, and Telegram knows none of it: `## Как проверял` stood * as two hash signs, a screenshot as `` in full, the * PR template's HTML comment as a paragraph, a table as a fence of bars. On * the 49 PR and issue cards of the week of 25.08.2026 that was the "wall of * text" the owner read. So the body is translated, not pasted (03.09.2026): * * - a comment `` is the template talking to the author — dropped; * - `## Heading` → bold line; `**x**` → bold; `` `x` `` → code; a fenced * block → `
`; * - an image `` → a link named by its alt (or `image`); * a link `[text](https://…)` → a link; a link to a repo path (no scheme) * keeps only its text — the address would not open from a phone anyway; * - a table: the `|---|` rule is dropped, a row's cells are joined by ` · `; * - `Closes #N` / `Fixes #N` lines are GitHub's own bookkeeping — dropped; * - runs of blank lines collapse to one. * * Everything is escaped FIRST, then the markup is added on the escaped text: * the patterns contain no `<>&"`, so nothing the author typed can become a tag. */ export declare const markdownToTelegram: (text: string) => string; /** * A session has no name of its own, so the incident's title is built out of * what happened to it: `burning the limit` → `Claude session is burning the * limit`. That is the whole of what the separate `session` card ever said * differently — every other row it had is an incident row now. * * The 36-character id is still not printed: he cannot type it, cannot search * it and cannot act on it. It reaches the card only inside the `rm` command at * the bottom, which is the one place it is of any use. */ export declare const sessionTitle: (action: string | undefined) => string; export declare const slug: (raw: string) => string; /** * The instance tag: exactly which concrete event this is (branch, * environment, task, number) — the parser uses it to match a 🔴 against a * later green card of the SAME instance. An explicit `key` always wins; * without one, it is derived from the type's most stable fields (branch/ * environment outrank the title, because a recurring task's title does not * change, while for a report the title is exactly the one stable field it * has). */ export declare const eventKey: (e: NotifyEvent) => string; /** * The third tag is the OUTCOME, and it is always there. The owner: "I'm * missing a fail tag or something like it, so failures can be grouped and * ok can be grouped." One tap in Telegram collects every failure of a * project at once, no matter what type it arrived as — a deploy, a check, * a scheduled task, an incident. * * The value comes from the ICON, never from the status word. The icon is * already the single source of truth for the sound, and a second list of "what * counts as broken" would drift from the first — it already did once, when a * red card arrived silent. * * One icon meaning, one tag. A watchdog that SWITCHED SOMETHING OFF is not a * failure and must not be filed under the same word as one: the owner read * `#fail` under a 🚫 and said so. Nor is a task that has simply gone quiet — * nobody knows yet whether it broke, and `#unknown` is the honest word for it. */ /** Every icon the package can print — the key space of the outcome table. */ type Icon = (typeof ICON)[keyof typeof ICON]; /** The five words the third tag is allowed to be, and there is no sixth. */ type OutcomeTag = 'ok' | 'fail' | 'off' | 'unknown' | 'info'; /** * The type is `Record` over EVERY icon, not over `string`. A new icon added to * `ICON` without a word here now fails to compile. Under the old loose type it * fell through a `?? 'info'` default instead: a card whose outcome nobody had * decided was indistinguishable from a card that is genuinely just news, and * nothing anywhere went red. `info` is therefore written out for each icon * that means it, never left to a fallback. */ export declare const OUTCOME_TAG: Readonly>; export declare const outcomeTag: (e: NotifyEvent) => OutcomeTag; /** * Renders an event into finished HTML text, cut to Telegram's limit. * * Assembly is TAIL-FIRST (v2.1): the parts that must survive any cut — the * tag line (the human filter and the parser's machine key), the pointer * block (`Log`/`Check`/`Source`) and the cut marker — are measured before * the body is clamped, and the body gets what is left. Under the old order * the pointer was part of the body, so the longest cards lost exactly the * line saying where to look. */ export declare const render: (e: NotifyEvent) => string; export {};