internal/chat: мультиканальный Router (UserID-сессии, routing, pending Ask/ответ) + Channel интерфейс
All checks were successful
build-test / build (push) Successful in 41s

This commit is contained in:
Hermes
2026-08-14 20:33:04 +05:00
parent 4ebafc48d0
commit c9d69594cc
6 changed files with 452 additions and 0 deletions

70
internal/chat/types.go Normal file
View File

@@ -0,0 +1,70 @@
// Package chat — мультиканальное общение с пользователем.
//
// Единый Router поверх любых Channel (Telegram / TUI / GUI...). Сессии
// ключуются по UserID («кто»), доставка — по Address («где»), а pull/push
// разница (polling/scan/подписка) скрыта внутри каждого Channel.
//
// Примитивы обмена: Send (уведомить), Ask (спросить, асинхронно),
// + входящие события через OnMessage. Ask привязан к адресу вопроса.
package chat
import "context"
// Address — «где» пользователь: конкретный канал+адрес (tg://123, tui://local).
type Address string
// UserID — «кто» владелец сессии (один на человека, канал неважен).
type UserID string
// Option — выбор (кнопка в TG, пронумерованный пункт в TUI).
type Option struct {
ID string
Label string
}
// Message — один исходящий/входящий контент.
type Message struct {
Text string
Options []Option // Confirm = Ask с да/нет; выбор = список
QuestionID string // блок 4: идентификатор вопроса в каждом Message
}
// Incoming — входящее событие, канал сам заполняет UserID/Address.
type Incoming struct {
UserID UserID
Address Address
Msg Message
Channel Channel
}
// Handler — единый вход всех каналов в Router.
type Handler func(Incoming)
// Channel — любой канал связи. pull/push разница скрыта внутри Run.
type Channel interface {
// Run запускает цикл канала (polling/scan/подписка). Может вернуть
// M5 ChannelFatal — тогда Router перезапустит канал.
Run(ctx context.Context) error
// OnMessage регистрирует единый обработчик входящих (Router).
OnMessage(h Handler)
// Send — fire-and-forget уведомление. M1 если нет адреса, M2 если не доставлено.
Send(ctx context.Context, to Address, m Message) error
// Ask — неблокирующий вопрос: регистрирует pending и возвращается сразу.
// M3 если pending уже открыт. Ответ придёт через OnMessage с тем же QuestionID.
Ask(ctx context.Context, to Address, prompt Message) error
// Close закрывает канал.
Close() error
}
// PendingQ — незакрытый вопрос (блок 4: один на сессию).
type PendingQ struct {
UserID UserID
AnswerAddr Address
Msg Message
}
// Route — куда доставить следующий ответ пользователю.
type Route struct {
Address Address
Channel Channel
}