feat: state is now scoped by both user id and chat id
This commit is contained in:
1 parent
4b94fcb920
commit
ec12f29fdb
3 files changed
+31
-23
No files matched your search
@@ -27,9 +27,9 @@ type EventIsolation interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FSMStorage interface {
|
type FSMStorage interface {
|
||||||
Get(ctx context.Context, userID int) (state.Snapshot, error)
|
Get(ctx context.Context, scope isolation.Scope) (state.Snapshot, error)
|
||||||
Put(ctx context.Context, state state.Snapshot) error
|
Put(ctx context.Context, state state.Snapshot) error
|
||||||
Delete(ctx context.Context, userID int) error
|
Delete(ctx context.Context, scope isolation.Scope) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateHandler interface {
|
type UpdateHandler interface {
|
||||||
@@ -157,13 +157,13 @@ func (d *Dispatcher) handleUpdates(ch <-chan model.Update) {
|
|||||||
func (d *Dispatcher) handleUpdate(ctx context.Context, upd model.Update) {
|
func (d *Dispatcher) handleUpdate(ctx context.Context, upd model.Update) {
|
||||||
var st *state.Context
|
var st *state.Context
|
||||||
|
|
||||||
snapshot, err := d.fsmStorage.Get(ctx, int(upd.UserID))
|
snapshot, err := d.fsmStorage.Get(ctx, isolation.Scope{UserID: upd.UserID, ChatID: upd.ChatID})
|
||||||
if err != nil && !errors.Is(err, state.ErrStateNotFound) {
|
if err != nil && !errors.Is(err, state.ErrStateNotFound) {
|
||||||
log.Error().Err(err).Send()
|
log.Error().Err(err).Send()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errors.Is(err, state.ErrStateNotFound) {
|
if errors.Is(err, state.ErrStateNotFound) {
|
||||||
st = state.New(int(upd.UserID))
|
st = state.New(isolation.Scope{UserID: upd.UserID, ChatID: upd.ChatID})
|
||||||
} else {
|
} else {
|
||||||
st = state.Restore(snapshot)
|
st = state.Restore(snapshot)
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ func (d *Dispatcher) handleUpdate(ctx context.Context, upd model.Update) {
|
|||||||
|
|
||||||
st = c.State()
|
st = c.State()
|
||||||
if st.State() == state.ClearState {
|
if st.State() == state.ClearState {
|
||||||
if err := d.fsmStorage.Delete(ctx, int(upd.UserID)); err != nil {
|
if err := d.fsmStorage.Delete(ctx, isolation.Scope{UserID: upd.UserID, ChatID: upd.ChatID}); err != nil {
|
||||||
log.Error().Err(err).Send()
|
log.Error().Err(err).Send()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ type EventIsolation struct {
|
|||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fsmKey struct {
|
||||||
|
userID int64
|
||||||
|
chatID int64
|
||||||
|
}
|
||||||
|
|
||||||
type eventIsolationKey struct {
|
type eventIsolationKey struct {
|
||||||
userID int
|
userID int
|
||||||
chatID int
|
chatID int
|
||||||
@@ -34,8 +39,11 @@ func NewFSMStorage() *FSMStorage {
|
|||||||
return &FSMStorage{}
|
return &FSMStorage{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FSMStorage) Get(_ context.Context, userID int) (state.Snapshot, error) {
|
func (f *FSMStorage) Get(_ context.Context, scope isolation.Scope) (state.Snapshot, error) {
|
||||||
st, ok := f.m.Load(userID)
|
st, ok := f.m.Load(fsmKey{
|
||||||
|
userID: scope.UserID,
|
||||||
|
chatID: scope.ChatID,
|
||||||
|
})
|
||||||
if !ok {
|
if !ok {
|
||||||
return state.Snapshot{}, state.ErrStateNotFound
|
return state.Snapshot{}, state.ErrStateNotFound
|
||||||
}
|
}
|
||||||
@@ -44,12 +52,18 @@ func (f *FSMStorage) Get(_ context.Context, userID int) (state.Snapshot, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FSMStorage) Put(_ context.Context, snapshot state.Snapshot) error {
|
func (f *FSMStorage) Put(_ context.Context, snapshot state.Snapshot) error {
|
||||||
f.m.Store(snapshot.ID, snapshot)
|
f.m.Store(fsmKey{
|
||||||
|
userID: snapshot.Scope.UserID,
|
||||||
|
chatID: snapshot.Scope.ChatID,
|
||||||
|
}, snapshot)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FSMStorage) Delete(_ context.Context, userID int) error {
|
func (f *FSMStorage) Delete(_ context.Context, scope isolation.Scope) error {
|
||||||
f.m.Delete(userID)
|
f.m.Delete(fsmKey{
|
||||||
|
userID: scope.UserID,
|
||||||
|
chatID: scope.ChatID,
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-13
@@ -3,8 +3,7 @@ package state
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/abelith/gomax/v1/isolation"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,13 +31,13 @@ func (JSONCodec) Unmarshal(d []byte, v any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Snapshot struct {
|
type Snapshot struct {
|
||||||
ID int `json:"id"`
|
Scope isolation.Scope `json:"id"`
|
||||||
State State `json:"state"`
|
State State `json:"state"`
|
||||||
Data map[string][]byte `json:"data"`
|
Data map[string][]byte `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
id int
|
scope isolation.Scope
|
||||||
state State
|
state State
|
||||||
data map[string][]byte
|
data map[string][]byte
|
||||||
codec Codec
|
codec Codec
|
||||||
@@ -48,9 +47,9 @@ type Option func(*Context)
|
|||||||
|
|
||||||
func WithCodec(c Codec) Option { return func(sc *Context) { sc.codec = c } }
|
func WithCodec(c Codec) Option { return func(sc *Context) { sc.codec = c } }
|
||||||
|
|
||||||
func New(id int, opts ...Option) *Context {
|
func New(scope isolation.Scope, opts ...Option) *Context {
|
||||||
sc := &Context{
|
sc := &Context{
|
||||||
id: id,
|
scope: scope,
|
||||||
data: make(map[string][]byte),
|
data: make(map[string][]byte),
|
||||||
codec: JSONCodec{},
|
codec: JSONCodec{},
|
||||||
}
|
}
|
||||||
@@ -61,7 +60,7 @@ func New(id int, opts ...Option) *Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Restore(s Snapshot, opts ...Option) *Context {
|
func Restore(s Snapshot, opts ...Option) *Context {
|
||||||
sc := New(s.ID, opts...)
|
sc := New(s.Scope, opts...)
|
||||||
sc.state = s.State
|
sc.state = s.State
|
||||||
for k, v := range s.Data {
|
for k, v := range s.Data {
|
||||||
sc.data[k] = v
|
sc.data[k] = v
|
||||||
@@ -74,15 +73,10 @@ func (sc *Context) Snapshot() Snapshot {
|
|||||||
for k, v := range sc.data {
|
for k, v := range sc.data {
|
||||||
data[k] = v
|
data[k] = v
|
||||||
}
|
}
|
||||||
return Snapshot{ID: sc.id, State: sc.state, Data: data}
|
return Snapshot{Scope: sc.scope, State: sc.state, Data: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *Context) Set(state State) {
|
func (sc *Context) Set(state State) {
|
||||||
log.Debug().
|
|
||||||
Int("user_id", sc.id).
|
|
||||||
Str("from", string(sc.state)).
|
|
||||||
Str("to", string(state)).
|
|
||||||
Msg("fsm state transition")
|
|
||||||
sc.state = state
|
sc.state = state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user