Wiki
猫Scenes
Scenes — это слой маршрутизации Laniakea с сохранением состояния для многошаговых и модальных диалогов. Сцена регистрируется внутри плагина, запускается через MessageContext, хранится через SessionStore и получает обновления раньше обычной маршрутизации команд, пока её сессия активна.
Что дают сцены
- Регистрацию через
Plugin.NewScene(...)иPlugin.AddScene(...). - Явный вход и выход через
MessageContext.EnterScene(...),EnterSceneStep(...)иExitScene(). - Области действия сессии на пользователя, чат или пару пользователь-чат.
- Обработчики шагов, локальные команды сцены и резервный обработчик сообщений на уровне сцены.
- JSON-состояние сцены через
SceneContext.BindData(...)иSaveData(...). - Встроенное in-memory-хранилище и интерфейс
SessionStoreдля собственного постоянного хранения.
Основной API
type SceneScope int
const (
SceneScopeUser SceneScope = iota
SceneScopeChat
SceneScopeUserChat
)
type SceneSession struct {
Scene string
Step string
Data []byte
}
type SessionStore interface {
Get(key string) (SceneSession, error)
Set(key string, session SceneSession) error
Delete(key string) error
}
MemorySessionStore используется по умолчанию. Чтобы заменить его, вызовите Bot.SetSessionStore(...).
Область действия сессии
SceneScopeUser: одна сессия сцены на пользователя во всех чатах.SceneScopeChat: одна сессия сцены на чат для всех пользователей.SceneScopeUserChat: отдельная сессия на пару(user, chat).
Рекомендуемое поведение по умолчанию:
- Для большинства интерактивных сценариев используйте
SceneScopeUserChat. SceneScopeUserнужен только там, где один и тот же сценарий должен продолжаться между чатами.SceneScopeChatподходит для общих сценариев на уровень чата.
Регистрация
Сцены регистрируются внутри плагина в том же стиле, что и команды с данными callback.
plugin.NewScene("signup").
SetScope(laniakea.SceneScopeUserChat).
SetEntry("ask_name").
OnStep("ask_name", askName).
OnStep("confirm", confirmSignup).
OnCommand("cancel", cancelSignup).
OnMessage(fallbackMessage)
SetEntry(...) обязателен для ctx.EnterScene(...). Если нужен явный старт с другого шага, используйте ctx.EnterSceneStep(...).
Модель обработчиков
Обычные команды используют *MessageContext. Обработчики сцен используют *SceneContext.
type SceneHandler[T any] func(ctx *SceneContext, db T) (SceneResult, error)
SceneContext встраивает *MessageContext и добавляет вспомогательные методы для сцен:
ctx.Stay()ctx.Next(step)ctx.Exit()ctx.Pass()ctx.BindData(&dst)ctx.SaveData(src)
Обработчик сцены возвращает SceneResult, который управляет переходом состояния:
Stay: оставить ту же сцену и тот же шаг.Next(step): перейти на другой зарегистрированный шаг.Exit: удалить текущую сессию.Pass: не менять текущую сессию и продолжить обычную маршрутизацию.
SceneActionPass намеренно ничего не делает с состоянием сцены. Если обработчик вызвал SaveData(...), а потом вернул Pass, эти данные не сохраняются.
Порядок маршрутизации
Пока сессия сцены активна, маршрутизация работает так:
- Сначала выполняются middleware бота.
- Бот ищет активную сессию сцены по настроенному приоритету областей действия.
- Затем выполняются middleware плагина-владельца сцены.
- Сначала проверяются локальные команды сцены.
- Если локальная команда не совпала, вызывается обработчик текущего шага.
- Если обработчик шага не найден, вызывается резервный обработчик
OnMessage(...). - Если сцена вернула
Pass, продолжается обычная маршрутизация команд и обновлений.
Текст сцены берётся из текста сообщения или подписи. Поэтому текущая модель сцен ориентирована именно на сценарии, основанные на сообщениях.
Состояние между шагами
Используйте SceneSession.Data через SceneContext.BindData(...) и SaveData(...), если только вы не пишете собственную логику хранения.
type ProfileDraft struct {
Name string
Age int
}
func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
var draft ProfileDraft
if err := ctx.BindData(&draft); err != nil {
return laniakea.SceneResult{}, err
}
draft.Name = ctx.Text
if err := ctx.SaveData(draft); err != nil {
return laniakea.SceneResult{}, err
}
return ctx.Next("age"), nil
}
Контракт хранилища остаётся маленьким, потому что Data []byte не привязан к конкретному формату хранения.
Пример сценария
Точка входа из команды:
func startSignup(ctx *laniakea.MessageContext, db *App) error {
return ctx.EnterScene("signup")
}
Обработчик шага:
func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
if ctx.Text == "" {
ctx.Answer("Как тебя зовут?")
return ctx.Stay(), nil
}
ctx.Answer("Спасибо.")
return ctx.Next("confirm"), nil
}
Локальная команда сцены:
func cancelSignup(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
ctx.Answer("Регистрация отменена")
return ctx.Exit(), nil
}
Осознанные ограничения текущей модели
- Локальная маршрутизация данных callback внутри сцены пока не реализована.
- Внутренний механизм выполнения сцен не экспортируется как публичный API для просмотра состояния.
- Набор вспомогательных методов для состояния сцены намеренно остаётся небольшим.
Связанные страницы:
Navigation
Start here
Runtime and Architecture
- Bot-Lifecycle
- Webhook-Runtime
- Middleware
- Runners
- Error-Handling
- Logging
- Update-Routing-Model
- Policies
- Scenes
Interaction and Telegram API
- Inline-Keyboards-and-Payloads
- Auto-Generated-Commands
- Drafts
- Rich-Messages
- Localization
- Rate-Limiting
- tgapi-Overview