37 lines
No EOL
5.1 KiB
Markdown
37 lines
No EOL
5.1 KiB
Markdown
# ADR-073: telegram-send plugin-tool — Bot API messaging via CLI
|
||
|
||
## Статус
|
||
|
||
Accepted (PR #174)
|
||
|
||
## Контекст
|
||
|
||
Оркестратору нужен быстрый способ показывать пользователю результаты работы — сгенерированный `draw-image` PNG, .md-файл с архитектурой, текстовый вывод. Существующий способ показать файл — tunnel skill (запуск процесса + проброс порта через Cloudflare), что тяжело для разовой отправки и требует живого процесса.
|
||
|
||
В репо уже есть паттерн plugin-tool + CLI-проекта на примере `draw-image`: тонкая обёртка `.opencode/tools/draw-image.ts` дёргает CLI из `.opencode/draw-image/cli.ts` через `spawnSync("node", ["--experimental-strip-types", cliPath, ...args])`. Plugin-tools авто-дискаверятся opencode из папки `.opencode/tools/` — без записи в `opencode.json`, без MCP-сервера.
|
||
|
||
## Решение
|
||
|
||
Создан `telegram-send` plugin-tool + CLI-проект `.opencode/telegram/` по паттерну `draw-image`:
|
||
|
||
1. **Plugin-tool, не MCP-сервер** — `.opencode/tools/telegram-send.ts` авто-дискаверится opencode; `spawnSync` запускает CLI как дочерний процесс; результат парсится из stdout; ошибка → `⚠️ telegram-send failed (exit N): <stderr>`. Не требует записи в `opencode.json`, не требует long-running процесса.
|
||
|
||
2. **Без runtime-зависимостей** — `package.json` имеет только devDeps (`@types/node`, `typescript`, `vitest`). Рантайм использует встроенный `fetch`/`FormData`/`Blob`/`File` Node 22+. Это упрощает установку (только `npm install` для devDeps) и аудит.
|
||
|
||
3. **Один tool с параметром `action`** — `action: "text" | "document" | "photo"` вместо трёх отдельных tools. Причины: единый контрак (`{ ok, message_id, chat_id }`), один набор env-настроек, меньше шума в списке tools оркестратора. Параметры `path`/`caption`/`text` условно-обязательные (зависят от `action`).
|
||
|
||
4. **CLI как тонкий слой над api.ts** — `cli.ts` парсит argv, применяет `parse_mode` по умолчанию (`"MarkdownV2"`), диспетчеризует в `sendMessage`/`sendDocument`/`sendPhoto`. `api.ts` — низкоуровневый, не применяет дефолты (добавляет `parse_mode` в body только при явной передаче). Разделение: api.ts тестируется изолированно, cli.ts тестирует дефолты.
|
||
|
||
5. **`lib: ["ES2022", "DOM"]` в tsconfig** — DOM-типы нужны только для `fetch`/`BodyInit`/`Blob`/`FormData`/`File` (все используются в `src/api.ts`). Рантайм-значения берутся из Node 22+ глобалов, не из браузера. `allowImportingTsExtensions: true` — сознательный паттерн (как `draw-image`) для `node --experimental-strip-types`.
|
||
|
||
## Альтернативы
|
||
|
||
1. **MCP-сервер для Telegram** — отвергнуто: требует long-running процесса, записи в `opencode.json`, сложнее отладка. Plugin-tool проще и следует существующему паттерну `draw-image`.
|
||
|
||
2. **Прямой `fetch` из plugin-tool (без CLI)** — отвергнуто: plugin-tool выполняется в Bun plugin-host (`import.meta.dir` доступен), но `fetch` там может вести себя иначе; CLI-процесс на Node 22+ даёт предсказуемый рантайм с встроенным `fetch`. Плюс CLI тестируется изолированно (`npm test`).
|
||
|
||
3. **Внешние зависимости (`node-telegram-bot-api`, `telegraf`)** — отвергнуто: добавляют audit-поверхность, усложняют установку, избыточны для 3 endpoint'ов (`sendMessage`, `sendDocument`, `sendPhoto`). Встроенный `fetch` + `FormData` достаточны.
|
||
|
||
4. **Три отдельных tools (`telegram-send-text`, `telegram-send-document`, `telegram-send-photo`)** — отвергнуто: засоряет список tools, дублирует env-логику и контрак. Один tool с `action`-enum чище.
|
||
|
||
5. **Markdown → MarkdownV2 авто-конвертер** — отвергнуто (out-of-scope): пользователь сам экранирует через `escapeMarkdownV2` (экспортируется из `src/markdown.ts`) или использует `parse_mode: "plain"`/`"HTML"`. Конвертер — отдельная нетривиальная задача. |