feat(standalone): :integrations-telegram — мост Telegram-чат ↔ agent
Новый модуль :integrations-telegram, опциональный «плагин» для standalone:
* TelegramBridgeComponent — Component-имплементация, устанавливается
в agent через agent.install(...) только при заданном
AGENTIK_TELEGRAM_TOKEN. Нет токена — нет polling'а, classpath не
содержит pw.binom.telegram.* (factory возвращает AutoCloseable,
а Component-объект создаётся внутри factory и утекает в
standalone только через agent-api).
* Long-polling getUpdates?timeout=N — не webhook, не нужен публичный URL.
* chatId ↔ ConversationId маппинг в общей SQLite-БД standalone'а
(таблица tg_chat_map); persistent=true переиспользует conv_id,
persistent=false — каждый Telegram-message = свежий temp-диалог.
* Group-чаты и /new стартуют временный диалог, не пишут в мапу.
* Tool-call показывает «⚙️ обрабатываю…» через sendChatAction(typing)
по onlineOutbox.onlineEvents.
* Outbound streaming через editMessageText с guard'ом «message is not
modified»; End с пустым телом → «(empty response)».
* Ошибки conv.send — одной строкой в чат, polling не падает.
* uninstall отменяет scope, polling и stream-джобы корректно
останавливаются.
Конфиг — через env (AppConfig.fromEnv):
* AGENTIK_TELEGRAM_TOKEN (default пусто → модуль не активен)
* AGENTIK_TELEGRAM_POLLING_TIMEOUT (default 30s, ок 25–35)
* AGENTIK_TELEGRAM_PERSISTENT (default true)
Зависимость: наша in-house либа caffeine-mgn/telegramClient
(pw.binom.telegram:telegramClient:1.0.0-SNAPSHOT из mavenLocal).
standalone/Main.kt:
* integrationsScope (SupervisorJob + Dispatchers.Default) создаётся
до server'а; tgClient закрывается shutdown-hook'ом ДО agent.close()
и integrationsScope.cancel() — порядок важен, иначе polling
long-poll не прерывается и JVM висит на выходе.
Тесты (8/8 ✅):
* первый входящий текст → persistent conv + conv.send
* повтор из того же chat → тот же conv
* non-persistent → каждый message = новый temp-conv
* typing-индикатор на каждом сообщении
* outbound streaming → 3 editMessage подряд
* End с пустым телом → draft заменяется на «(empty response)»
* ошибка conv.send → одна строка в чат, polling не падает
* uninstall → polling остановлен, новые push'и не обрабатываются
standalone/README.md — добавлен раздел «Telegram-интеграция» с env-таблицей,
описанием поведения и инструкцией по включению.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
// 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)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(libs.kotlin.test)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testImplementation(libs.kotlinx.coroutines.core)
|
||||
testImplementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
Reference in New Issue
Block a user