95 lines
3.4 KiB
Markdown
95 lines
3.4 KiB
Markdown
# Build Bodies
|
|
|
|
## Purpose
|
|
|
|
Pure, side-effect-free helpers that translate the camelCase public message /
|
|
reply-markup shape used by callers into the snake_case wire shape that the
|
|
Telegram Bot API expects. Splitting these helpers out of the client keeps
|
|
the wire-format translation independently testable, makes it cheap to add
|
|
new optional fields, and means every option permutation can be exercised
|
|
without an HTTP round trip.
|
|
|
|
Telegram's Bot API mixes top-level fields (`chat_id`, `parse_mode`) with
|
|
nested objects (`reply_markup.inline_keyboard`). Hand-rolling each
|
|
conversion in the client would scatter snake-case knowledge across the
|
|
codebase; centralising it here means the wire-format contract is one file
|
|
to audit when Telegram releases a new option.
|
|
|
|
## Features
|
|
|
|
- `buildSendMessageBody(message)` — converts `TelegramMessage` → wire body
|
|
- `buildReplyMarkup(markup)` — converts `TelegramReplyMarkup` → wire markup
|
|
- `MEDIA_METHOD_MAP` — frozen record of `TelegramMediaType` → Bot API method
|
|
- Drops optional fields when falsy/absent — minimises wire payload
|
|
- Preserves caller-provided `chatId` type (string or number) — Telegram accepts both
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { buildSendMessageBody } from "@suaveplan/telegram-client";
|
|
|
|
const body = buildSendMessageBody({
|
|
chatId: 100,
|
|
text: "Hello",
|
|
parseMode: "HTML",
|
|
});
|
|
// body === { chat_id: 100, text: "Hello", parse_mode: "HTML" }
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
```ts
|
|
import {
|
|
buildReplyMarkup,
|
|
buildSendMessageBody,
|
|
MEDIA_METHOD_MAP,
|
|
} from "@suaveplan/telegram-client";
|
|
|
|
// Compose a fully-featured message body with inline + custom keyboards.
|
|
const body = buildSendMessageBody({
|
|
chatId: "@my_channel",
|
|
text: "*Pick one*",
|
|
parseMode: "MarkdownV2",
|
|
disableWebPagePreview: true,
|
|
replyToMessageId: 42,
|
|
replyMarkup: {
|
|
inlineKeyboard: [
|
|
[
|
|
{ text: "Docs", url: "https://core.telegram.org/bots/api" },
|
|
{ text: "Yes", callbackData: "yes" },
|
|
],
|
|
],
|
|
oneTimeKeyboard: true,
|
|
},
|
|
});
|
|
|
|
// Pick the Bot API method for a media upload.
|
|
const method = MEDIA_METHOD_MAP.photo; // "sendPhoto"
|
|
```
|
|
|
|
## API Reference
|
|
|
|
- `buildSendMessageBody(message: TelegramMessage): Record<string, unknown>`
|
|
- Always emits `chat_id` and `text`.
|
|
- Emits `parse_mode`, `disable_web_page_preview`, `disable_notification`,
|
|
`reply_to_message_id`, `reply_markup` only when the corresponding camelCase
|
|
field is truthy.
|
|
- `buildReplyMarkup(markup: TelegramReplyMarkup): Record<string, unknown>`
|
|
- Emits `inline_keyboard` when `inlineKeyboard` is set; each button is
|
|
`{ text, url?, callback_data? }`.
|
|
- Emits `keyboard` when `keyboard` is set; each button is
|
|
`{ text, request_contact?, request_location? }`.
|
|
- Emits `remove_keyboard: true` / `one_time_keyboard: true` when requested.
|
|
- `MEDIA_METHOD_MAP: Record<TelegramMediaType, "send<Type>">` — frozen.
|
|
|
|
## Implementation Notes
|
|
|
|
The helpers are pure: identical input → identical output, no global state,
|
|
no `Date.now()`/`Math.random()`. That makes the test suite a flat property
|
|
table — one expectation per option combination — and keeps coverage trivial.
|
|
The "emit-only-when-truthy" rule is important: Telegram interprets
|
|
`disable_notification: false` as the silent-mode opt-out, but a payload that
|
|
never sent the field at all reverts to the chat's default. To match upstream
|
|
behaviour exactly we drop the field when callers leave it `undefined` /
|
|
`false` and only include it when the caller explicitly opts in.
|