Files
2026-05-27 23:29:53 +01:00

6.2 KiB

Add @suaveplan/telegram-client

Package: @suaveplan/telegram-client Status: PROPOSED Type: new-package

Why

The genesis monorepo lacks a Telegram Bot API client. The upstream @suaveplan/telegram-client at ../suaveplan/packages/universal/telegram-client/ provides a small, runtime-agnostic client used for transactional bot messaging in production but has zero deterministic-clock / no-mock test discipline, no injectable fetch, no OTel surface, and no Zod boundary. This change ports the upstream functional surface 1:1, then rewrites it to the genesis quality bar (injectable Clock + fetch, Zod-validated config, real-fetch test discipline, SuaveplanError extension, OTel counters/histograms, 100% istanbul coverage) so downstream @suaveplan/notifications-* and ops alerting packages have a production-grade Telegram channel.

What changes

Additive — new package at packages/messaging/telegram-client/.

  • ADDED: TelegramBotClient class — Bot API client wrapping sendMessage, editMessage, deleteMessage, sendMedia, getChat, getMe over native fetch with AbortController timeouts.
  • ADDED: createTelegramBotClient factory — preferred over new, accepts injectable fetch and clock.
  • ADDED: TelegramClientError extends SuaveplanError<TelegramClientCode>.
  • ADDED: TELEGRAM_CLIENT_CODES registry — API_ERROR, AUTH_ERROR, RATE_LIMITED, CHAT_NOT_FOUND, BOT_BLOCKED, CONFIG_MISSING, TIMEOUT, FORBIDDEN.
  • ADDED: TelegramBotConfigSchema Zod schema for { token, baseUrl?, timeoutMs? }.
  • ADDED: types TelegramBotConfig, TelegramMessage, TelegramReplyMarkup, TelegramInlineButton, TelegramKeyboardButton, TelegramResponse<T>, TelegramChat, TelegramUser, TelegramSentMessage, TelegramDeliveryReport, TelegramDeliveryStatus, TelegramMediaType.
  • ADDED: buildSendMessageBody, buildReplyMarkup, parseRetryAfterMs, classifyStatus — pure helpers (exported for testability).
  • ADDED: OTel counter telegram_client.request.total and histogram telegram_client.request.duration_ms.
  • ADDED: subpath exports ./errors, ./types.

Out of scope

  • Long-polling getUpdates / webhook update receivers — upstream does not implement them.
  • Bot-on-bot orchestration, conversational state — not in upstream.
  • File upload via multipart (only URL-based media) — upstream is URL-only.

Source mapping

This change derives from ../suaveplan/enterprise/telegram-client.md AND the upstream functional surface at ../suaveplan/packages/universal/telegram-client/src/.

Requirement Source lines / files Notes
Send text messages with full options src/core/telegram-bot-client/telegram-bot-client.ts:50-53,287-339 verbatim API surface (sendMessage, buildSendMessageBody, buildReplyMarkup)
Edit / delete messages src/core/telegram-bot-client/telegram-bot-client.ts:56-80 verbatim
Send media (photo/document/video/audio/voice/animation) src/core/telegram-bot-client/telegram-bot-client.ts:83-101 verbatim method map
Get chat info and bot info src/core/telegram-bot-client/telegram-bot-client.ts:104-111 verbatim
Authenticated POST request envelope with AbortController timeout src/core/telegram-bot-client/telegram-bot-client.ts:113-163 verbatim
Retry-after parsing (body / headers fallback) src/core/telegram-bot-client/telegram-bot-client.ts:179-215 verbatim
Typed error classification by HTTP status + Telegram description src/core/telegram-bot-client/telegram-bot-client.ts:218-284 verbatim
Stable error code registry with metadata src/error-codes.ts:18-117 reworded — genesis uses defineCodes("TELEGRAM_CLIENT", […]) + SuaveplanError extension, but the same 8 codes
Config token required (throw on missing) src/core/telegram-bot-client/telegram-bot-client.ts:36-47 verbatim
Default base URL https://api.telegram.org and 10s timeout src/core/telegram-bot-client/telegram-bot-client.ts:21-22 verbatim constants
Zero browser / React deps enterprise/telegram-client.md L37 cross-cutting bar
Zod schemas on public types enterprise/telegram-client.md L38 cross-cutting bar
Deterministic — injectable clock enterprise/telegram-client.md L40 cross-cutting bar
OTel instrumentation enterprise/telegram-client.md L41 cross-cutting bar
Structured errors extending shared base enterprise/telegram-client.md L42 cross-cutting bar
AbortSignal everywhere async enterprise/telegram-client.md L43 upstream uses internal AbortController for timeout; genesis preserves this
No mocks — injectable fetch enterprise/telegram-client.md L53 + CLAUDE.md rule 2 cross-cutting bar
sideEffects: false, workspace:^ deps, Bun catalog externals enterprise/telegram-client.md L55-L57 cross-cutting bar

Requirements with no source line

Requirement Justification
100 % istanbul coverage on touched modules Genesis-wide policy (CLAUDE.md §Quality).
Test files import only from @suaveplan/testing/runner Genesis-wide policy (CLAUDE.md rule 11).
Sibling .md doc per impl file ≥200 words Genesis-wide policy (CLAUDE.md §Documentation).

Verification approach

  • src/core/telegram-bot-client/telegram-bot-client.test.ts — every Bot API method, every error classification branch, retry-after parsing, custom base URL, config validation, OTel emission.
  • src/utils/build-bodies.test.ts — pure body / reply-markup builders across every option permutation.
  • src/utils/classify-error.test.ts — pure status classifier and retry-after parser, including header fallbacks.
  • src/errors/telegram-client-error.test.ts — error class shape, code registry, throwability.
  • src/types/telegram-bot-config.test.ts — Zod schema accept / reject.
  • tests/dep-graph.test.ts — runtime deps locked.

All tests use an injectable fetch that returns canned Response objects (Response.json(...)) — no network, no vi.mock, no mocks of any kind. The OTel test asserts against a real in-memory MeterProvider.

Migration impact

None — additive only. No existing consumer in genesis imports @suaveplan/telegram-client.