88dabd49a3
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.
66 lines
3.2 KiB
Kotlin
66 lines
3.2 KiB
Kotlin
// 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)
|
||
} |