REPOSITORY / ScuroNeko/Laniakea

Wiki

KNOWLEDGE REPOSITORY
5
Scenes RU
ScuroNeko edited this page 2026-05-20 13:19:27 +03:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, эти данные не сохраняются.

Порядок маршрутизации

Пока сессия сцены активна, маршрутизация работает так:

  1. Сначала выполняются middleware бота.
  2. Бот ищет активную сессию сцены по настроенному приоритету областей действия.
  3. Затем выполняются middleware плагина-владельца сцены.
  4. Сначала проверяются локальные команды сцены.
  5. Если локальная команда не совпала, вызывается обработчик текущего шага.
  6. Если обработчик шага не найден, вызывается резервный обработчик OnMessage(...).
  7. Если сцена вернула 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 для просмотра состояния.
  • Набор вспомогательных методов для состояния сцены намеренно остаётся небольшим.

Связанные страницы: