REPOSITORY / ScuroNeko/Laniakea

Wiki

KNOWLEDGE REPOSITORY

Refresh scenes draft wiki

Clarify scene routing, entry steps, and local commands

Document scene state storage and expose RU draft links in sidebar
2026-03-27 13:01:31 +03:00
parent 47c98ed7bf
commit 5408d818f7
3 changed files with 95 additions and 8 deletions
+45 -3
@@ -13,17 +13,19 @@ DRAFT: эта страница описывает предлагаемую мо
## Цели дизайна
- Сцены должны быть опциональными и additve.
- Сцены должны быть опциональными и additive.
- Plugins должны оставаться основной единицей регистрации.
- `MsgContext` должен оставаться базовым контекстом для обычных handlers.
- Нужно покрыть и пошаговые flow, и модальные chat loops.
- Нужно явно различать сессии в личке и в чате.
- Локальные stop/escape-команды сцены должны быть first-class механизмом.
- Нужен встроенный способ хранить состояние сцены между шагами.
## Ментальная модель
- Команды и payload маршрутизируются по trigger.
- Сцены маршрутизируются по активному состоянию.
- Сцены являются отдельным слоем маршрутизации поверх commands, payloads и обычных update handlers.
- Команда вроде `/rpstart` входит в сцену.
- Пока сцена активна, обычные сообщения сначала уходят в сцену.
- Локальные команды сцены вроде `/rpstop` парсятся внутри активной сцены.
@@ -59,6 +61,7 @@ const (
type SceneSession struct {
Scene string
Step string
Data []byte
}
type SessionStore interface {
@@ -80,6 +83,8 @@ plugin.NewScene("rp").
AddCommand("rpstop", stopRP)
```
`SetEntry(...)` задаёт начальный step или state сцены. Когда `ctx.EnterScene("rp")` создаёт новую сессию, фреймворк записывает это значение в `SceneSession.Step`.
Так plugin API остаётся визуально согласованным:
- `NewCommand(...)`
@@ -93,6 +98,7 @@ plugin.NewScene("rp").
- Пошаговые сцены: именованный step обрабатывает каждый update и выбирает следующий step.
- Модальные сцены: долгоживущий "режим" обрабатывает обычные сообщения, пока пользователь явно не выйдет.
- Обе формы должны уметь регистрировать локальные команды сцены.
Пример пошагового flow:
@@ -172,6 +178,41 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
}
```
## Состояние между шагами
Для реальных сценариев недостаточно хранить только `Scene` и `Step`. Обычно между update нужно накапливать данные: черновик профиля, выбранные параметры, временные ID и другие промежуточные значения.
Это не стоит полностью перекладывать на разработчика приложения. Фреймворк должен давать слот для хранения в `SceneSession` и удобные helper-методы на `SceneContext`.
Предлагаемый helper API:
- `ctx.BindState(&dst)`
- `ctx.SaveState(src)`
- `ctx.ClearState()`
Пример:
```go
type ProfileDraft struct {
Name string
Age int
}
func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
var draft ProfileDraft
_ = ctx.BindState(&draft)
draft.Name = ctx.Text
if err := ctx.SaveState(draft); err != nil {
return nil, err
}
return ctx.Next("age"), nil
}
```
Использование `Data []byte` в `SceneSession` делает контракт `SessionStore` маленьким и не завязанным на конкретный формат хранения. В первой реализации scene helpers могут работать через JSON.
## Алгоритм маршрутизации
Роутер должен вести себя так:
@@ -183,7 +224,7 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
5. Если сцена активна, сначала попробовать scene-local command или payload routing.
6. Если локальный route не совпал, вызвать message handler или step handler сцены.
7. Если сцена вернула `Stay`, сохранить текущую сессию без изменений.
8. Если сцена вернула `Next(step)`, сохранить новый step.
8. Если сцена вернула `Next(step)`, сохранить новый step и оставить `Data`.
9. Если сцена вернула `Exit`, удалить сессию.
10. Если сцена вернула `Pass`, продолжить обычную маршрутизацию.
@@ -194,11 +235,11 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
- Middleware должны продолжать работать на базовом `MsgContext`.
- Scene handlers должны так же пользоваться `ctx.BindArgs(...)` и `ctx.Context()`.
- Первая версия может поставляться с in-memory store плюс интерфейсом `SessionStore` для кастомного persistence.
- Helpers для сериализации состояния должны входить в первый scene API, а не оставаться приложенческим boilerplate.
## Открытые вопросы
- Нужно ли добавлять scene-local payloads уже в первой версии или отложить на вторую?
- Должен ли `SceneContext` напрямую предоставлять helpers для scene-session storage?
- Стоит ли поставлять `MemorySessionStore` по умолчанию, или сцены должны требовать явный store?
- Должны ли modal scenes иметь явный fallback mode вроде "pass to normal routing" против "consume silently"?
@@ -210,6 +251,7 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
- Добавить `MsgContext.EnterScene(...)`, `ExitScene(...)` и `CurrentScene(...)`.
- Маршрутизировать активные сцены раньше обычного command/payload flow.
- Поддержать scene-local commands и `OnMessage(...)`.
- Поддержать helpers для состояния сцены поверх `SceneSession.Data`.
Связанные страницы:
+44 -2
@@ -19,11 +19,13 @@ Scenes are the planned stateful routing layer for long-lived interactions in Lan
- Support both step-based flows and modal chat loops.
- Distinguish private-chat and group-chat sessions explicitly.
- Make scene-local stop/escape commands first-class.
- Provide a built-in way to persist scene state between steps.
## Mental model
- Commands and payloads route by trigger.
- Scenes route by active state.
- Scenes are a separate routing layer above commands, payloads, and generic update handlers.
- A command such as `/rpstart` enters a scene.
- Once the scene is active, regular messages are routed to the scene first.
- Scene-local commands such as `/rpstop` are parsed inside the active scene.
@@ -59,6 +61,7 @@ const (
type SceneSession struct {
Scene string
Step string
Data []byte
}
type SessionStore interface {
@@ -80,6 +83,8 @@ plugin.NewScene("rp").
AddCommand("rpstop", stopRP)
```
`SetEntry(...)` defines the initial step or state of the scene. When `ctx.EnterScene("rp")` creates a new session, the framework writes that entry step into `SceneSession.Step`.
This keeps the plugin API visually consistent:
- `NewCommand(...)`
@@ -93,6 +98,7 @@ The first version should support two common shapes.
- Step-based scenes: a named step handles each update and decides the next step.
- Modal scenes: a long-lived "mode" handles ordinary messages until an explicit exit command ends it.
- Both shapes should still be able to register scene-local commands.
Step-based flow example:
@@ -172,6 +178,41 @@ func stopRP(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
}
```
## State between steps
Scenes need more than just `Scene` and `Step`. Real flows also accumulate data between updates, such as a draft profile, selected options, or temporary IDs.
That state should not be pushed entirely onto the application author. The framework should provide a storage slot inside `SceneSession` plus ergonomic helpers on `SceneContext`.
Proposed helper API:
- `ctx.BindState(&dst)`
- `ctx.SaveState(src)`
- `ctx.ClearState()`
Example:
```go
type ProfileDraft struct {
Name string
Age int
}
func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error) {
var draft ProfileDraft
_ = ctx.BindState(&draft)
draft.Name = ctx.Text
if err := ctx.SaveState(draft); err != nil {
return nil, err
}
return ctx.Next("age"), nil
}
```
Using `Data []byte` in `SceneSession` keeps the `SessionStore` contract small and storage-agnostic. The scene helpers can use JSON for the first implementation.
## Routing algorithm
The router should behave like this:
@@ -183,7 +224,7 @@ The router should behave like this:
5. If a scene is active, try scene-local command or payload routing first.
6. If no scene-local route matches, try the scene message or step handler.
7. If the scene returns `Stay`, keep the current session.
8. If the scene returns `Next(step)`, persist the new step.
8. If the scene returns `Next(step)`, persist the new step and keep `Data`.
9. If the scene returns `Exit`, delete the session.
10. If the scene returns `Pass`, continue normal routing.
@@ -194,11 +235,11 @@ The router should behave like this:
- Middleware should still run on the base `MsgContext`.
- Scene handlers should continue to benefit from `ctx.BindArgs(...)` and `ctx.Context()`.
- The first version can ship with an in-memory store plus a `SessionStore` interface for custom persistence.
- State serialization helpers should be part of the first scene API, not left as app-specific boilerplate.
## Open questions
- Should scene-local payloads be added in the first version or the second?
- Should `SceneContext` expose scene-session storage helpers directly?
- Should the framework ship with a default `MemorySessionStore`, or should scenes require an explicit store?
- Should modal scenes have an explicit fallback mode such as "pass to normal routing" versus "consume silently"?
@@ -210,6 +251,7 @@ The router should behave like this:
- Add `MsgContext.EnterScene(...)`, `ExitScene(...)`, and `CurrentScene(...)`.
- Route active scenes before normal command and payload flow.
- Support scene-local commands and `OnMessage(...)`.
- Support scene-state helpers backed by `SceneSession.Data`.
Related pages:
+6 -3
@@ -12,6 +12,7 @@
- [[Bot-Lifecycle]]
- [[Middleware]]
- [[Scenes]]
- [[Scenes-RU|Scenes (RU)]]
## Changes and troubleshooting
- [[Migration]]
@@ -26,6 +27,8 @@
- [[Semver-and-Releases]]
- [[Page-Priority]]
## Drafts and RU companion pages
- [[Scenes-RU]]
- [[Framework-Backlog-RU]]
## Drafts
- [[Framework-Backlog]]
- [[Scenes]]
- [[Framework-Backlog-RU|Framework Backlog (RU)]]
- [[Scenes-RU|Scenes (RU)]]