71 lines
3.0 KiB
Go
71 lines
3.0 KiB
Go
// 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
|
||
}
|