wip: scene sessions
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
package laniakea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type SceneHandler[T any] func(ctx *SceneContext, db T) (SceneResult, error)
|
||||
type Scene[T any] struct {
|
||||
Name string
|
||||
Scope SceneScope
|
||||
Entry string // starting step
|
||||
PluginName string
|
||||
|
||||
steps map[string]SceneHandler[T]
|
||||
commands map[string]SceneHandler[T]
|
||||
message SceneHandler[T]
|
||||
}
|
||||
|
||||
func NewScene[T any](name string) *Scene[T] {
|
||||
return &Scene[T]{
|
||||
Name: name,
|
||||
Scope: SceneScopeUserChat,
|
||||
Entry: "",
|
||||
steps: make(map[string]SceneHandler[T]),
|
||||
commands: make(map[string]SceneHandler[T]),
|
||||
message: nil,
|
||||
}
|
||||
}
|
||||
func (s *Scene[T]) SetScope(scope SceneScope) *Scene[T] {
|
||||
s.Scope = scope
|
||||
return s
|
||||
}
|
||||
func (s *Scene[T]) SetEntry(step string) *Scene[T] {
|
||||
s.Entry = step
|
||||
return s
|
||||
}
|
||||
func (s *Scene[T]) setPluginName(name string) *Scene[T] {
|
||||
s.PluginName = name
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Scene[T]) OnStep(step string, handler SceneHandler[T]) *Scene[T] {
|
||||
s.steps[step] = handler
|
||||
return s
|
||||
}
|
||||
func (s *Scene[T]) OnCommand(cmd string, handler SceneHandler[T]) *Scene[T] {
|
||||
s.commands[cmd] = handler
|
||||
return s
|
||||
}
|
||||
func (s *Scene[T]) OnMessage(handler SceneHandler[T]) *Scene[T] {
|
||||
s.message = handler
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Scene[T]) executeCommand(cmd string, ctx *SceneContext, db T) (SceneResult, bool, error) {
|
||||
handler, ok := s.commands[cmd]
|
||||
if !ok {
|
||||
return SceneResult{}, false, nil
|
||||
}
|
||||
result, err := handler(ctx, db)
|
||||
return result, true, err
|
||||
}
|
||||
func (s *Scene[T]) executeStep(step string, ctx *SceneContext, db T) (SceneResult, bool, error) {
|
||||
handler, ok := s.steps[step]
|
||||
if !ok {
|
||||
return SceneResult{}, false, nil
|
||||
}
|
||||
result, err := handler(ctx, db)
|
||||
return result, true, err
|
||||
}
|
||||
func (s *Scene[T]) executeMessage(ctx *SceneContext, db T) (SceneResult, bool, error) {
|
||||
if s.message == nil {
|
||||
return SceneResult{}, false, nil
|
||||
}
|
||||
result, err := s.message(ctx, db)
|
||||
return result, true, err
|
||||
}
|
||||
|
||||
type SceneSession struct {
|
||||
Scene string
|
||||
Step string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// SetData sets the session data. It is thread-safe and can be used to store any arbitrary data as a byte slice.
|
||||
func (s *SceneSession) SetData(data []byte) {
|
||||
s.Data = data
|
||||
}
|
||||
|
||||
// GetData retrieves the session data. It is thread-safe and returns the data as a byte slice.
|
||||
func (s *SceneSession) GetData() []byte {
|
||||
return s.Data
|
||||
}
|
||||
func (s *SceneSession) HasData() bool {
|
||||
return len(s.Data) > 0
|
||||
}
|
||||
|
||||
// ClearData clears the session data. It is thread-safe and sets the data to nil.
|
||||
func (s *SceneSession) ClearData() {
|
||||
s.Data = nil
|
||||
}
|
||||
|
||||
// BindData binds the session data to the provided struct.
|
||||
func (s *SceneSession) BindData(v any) error {
|
||||
if len(s.Data) == 0 {
|
||||
return nil // No data to bind, return nil error
|
||||
}
|
||||
return json.Unmarshal(s.Data, v)
|
||||
}
|
||||
|
||||
// SaveData saves the provided struct as JSON in the session data.
|
||||
func (s *SceneSession) SaveData(v any) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Data = data
|
||||
return nil
|
||||
}
|
||||
|
||||
type SessionStore interface {
|
||||
Get(key string) (SceneSession, error)
|
||||
Set(key string, session SceneSession) error
|
||||
Delete(key string) error
|
||||
}
|
||||
|
||||
type MemorySessionStore struct {
|
||||
store map[string]SceneSession
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMemorySessionStore() *MemorySessionStore {
|
||||
return &MemorySessionStore{
|
||||
store: make(map[string]SceneSession),
|
||||
}
|
||||
}
|
||||
func (s *MemorySessionStore) Get(key string) (SceneSession, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if session, ok := s.store[key]; ok {
|
||||
return session, nil
|
||||
}
|
||||
return SceneSession{}, nil
|
||||
}
|
||||
func (s *MemorySessionStore) Set(key string, session SceneSession) error {
|
||||
s.mu.Lock()
|
||||
s.store[key] = session
|
||||
s.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
func (s *MemorySessionStore) Delete(key string) error {
|
||||
s.mu.Lock()
|
||||
delete(s.store, key)
|
||||
s.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
type SceneResult struct {
|
||||
Action SceneAction
|
||||
Next string
|
||||
}
|
||||
|
||||
type SceneAction int
|
||||
|
||||
const (
|
||||
SceneActionStay SceneAction = iota
|
||||
SceneActionNext
|
||||
SceneActionExit
|
||||
SceneActionPass
|
||||
)
|
||||
|
||||
type SceneScope int
|
||||
|
||||
const (
|
||||
SceneScopeUser SceneScope = iota
|
||||
SceneScopeChat
|
||||
SceneScopeUserChat
|
||||
)
|
||||
|
||||
type sceneRuntime interface {
|
||||
FindScene(name string) (*sceneMeta, bool)
|
||||
GetSession(key string) (SceneSession, error)
|
||||
SetSession(key string, session SceneSession) error
|
||||
DeleteSession(key string) error
|
||||
BuildSceneKey(scope SceneScope, ctx *MsgContext) (string, bool)
|
||||
FindSceneSession(ctx *MsgContext) (string, SceneSession, error)
|
||||
}
|
||||
|
||||
type sceneMeta struct {
|
||||
Name string
|
||||
Scope SceneScope
|
||||
Entry string
|
||||
Steps map[string]struct{}
|
||||
}
|
||||
Reference in New Issue
Block a user