Files
agentik/integrations-telegram/build.gradle.kts
T
subochev 88dabd49a3 feat(standalone): :integrations-telegram — bridge stability + markdown rendering
Outbound (DurableEvent-driven):
- typing дёргается ДО conv.send (а не только по online-событиям);
  рефреш по online — каждые ≥4 с, чтобы не получить 429-спам
  (Telegram rate-limit ~250 ms на sendChatAction).
- финальный текст — sendMessage(... ParseMode.HTML) на
  DurableEvent.AssistantMessage, без промежуточных editMessage.
- служебные строки ((empty response), ошибки) идут как plain text
  (без parseMode=HTML), чтобы 400 can't parse entities не выбил
  предупреждение в чат.

Inline markdown → Telegram HTML (MarkdownToTelegram):
- **bold** / __bold__ / *italic* / _italic_ / `code` → <b>/<i>/<code>;
  fenced ``` → <pre>; [text](url) → <a href>.
- markdown-таблицы → Unicode-рамка (┌─┬─┐││├─┼─┤└─┴─┘) внутри <pre>
  (Telegram Bot API <table> не поддерживает).
- escapeOutsideTags теперь ведёт стек открытых тегов: раньше брал
  первый попавшийся закрывающий тег и терял парность (агент вставил
  <i> внутри <code> → Telegram 400 can't find end tag для <code>).
- экранирует только вне наших тегов; внутри — уже подготовленный текст.
- сообщения > 4096 режутся по \n вне тегов.

Видимость ошибок:
- kotlin-logging в build.gradle.kts.
- runCatching'и заменены на try/catch + log.error/warn с контекстом
  (chatId, convId, htmlLen, превью первых ≤120 символов HTML).
  Раньше Telegram 400 на sendMessage с 'can't parse entities' уходил
  молча — теперь видно в логе с полным телом запроса.

Тесты: 23 + 12 + 28 = 63 в :integrations-telegram, 132 в :standalone.
:standalone:assemble собирает fatjar.
2026-10-06 00:51:28 +03:00

66 lines
3.2 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Telegram-интеграция для :standalone: переносит входящие текстовые
// сообщения Telegram-бота в диалоги [pw.binom.agentik.proto.Agent] и
// стримит ответы агента обратно в чат.
//
// Переиспользуемый модуль (не зависит от :standalone): компонент
// `TelegramBridgeComponent` реализует [pw.binom.agentik.agent.Component]
// и подключается к любому `MutableAgent`. `:standalone` инициализирует
// его при наличии `AGENTIK_TELEGRAM_TOKEN` (см. `AppConfig.IntegrationSection`).
//
// Зависимости:
// - :agent-api — Component / MutableAgent
// - :proto — Agent / Conversation
// - :outbox-api — OnlineEvent / OnlineOutbox (стриминг ответа)
// - :content-api — Content.Text (что шлём в conv.send)
// - :journal-api — DurableEvent.AssistantMessage (для fallback, см.
// TelegramBridgeComponent — когда подписчик на
// onlineOutbox пропустил ход, добираем полный текст
// из durable-события)
// - pw.binom.telegram:telegramClient — KMP-обёртка Bot HTTP API
// (`telegramClient-jvm` уже опубликован в mavenLocal как 1.0.0-SNAPSHOT).
// - ktor-client-cio — HTTP-движок для TelegramClient (KMP).
// - ksqlite — таблица `tg_chat_map` (chat_id ↔ conversation_id).
// - kotlinx-coroutines — ASync-механика поллера и подписчиков.
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
}
kotlin {
jvmToolchain(21)
}
dependencies {
api(project(":agent-api"))
api(project(":proto"))
api(project(":outbox-api"))
implementation(project(":content-api"))
implementation(project(":journal-api"))
// Telegram Bot HTTP API (опубликован локально через publishToMavenLocal
// в /home/subochev/common-projects/WORK/telegramClient — caffeineRepo
// пока не имеет релизов, mavenLocal идёт первым в settings.gradle.kts).
// `implementation`, а не `api`: типы `pw.binom.telegram.*` НЕ должны
// утекать в classpath standalone'а — standalone видит только фабрику
// `createTelegramBridgeComponent` (см. TelegramBridgeComponent.kt).
implementation("pw.binom.telegram:telegramClient:1.0.0-SNAPSHOT")
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.ksqlite)
implementation(libs.kotlin.logging)
implementation(libs.logback.classic)
}
dependencies {
testImplementation(libs.kotlin.test)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.kotlinx.coroutines.core)
testImplementation(libs.kotlinx.serialization.json)
}