package core import ( "context" "errors" mcontext "github.com/abelith/gomax/context" "github.com/abelith/gomax/state" maxbot "github.com/max-messenger/max-bot-api-client-go/v2" "github.com/max-messenger/max-bot-api-client-go/v2/model" "github.com/rs/zerolog/log" "sync" ) type DispatcherOption interface{} type EventIsolation interface{} type FSMStorage interface { Get(ctx context.Context, userID int) (state.Snapshot, error) Put(ctx context.Context, state state.Snapshot) error Delete(ctx context.Context, userID int) error } type UpdateHandler interface { HandleUpdate(c *mcontext.Context) error Use(mw ...Middleware) } type Poller interface { Start(ctx context.Context) (<-chan model.Update, error) } type Dispatcher struct { api *maxbot.Api poller Poller handler UpdateHandler fsmStorage FSMStorage eventIsolation EventIsolation wg sync.WaitGroup } func NewDispatcher(api *maxbot.Api, options ...DispatcherOption) *Dispatcher { d := &Dispatcher{api: api} // todo: apply options return d } func (d *Dispatcher) handleUpdates(ch <-chan model.Update) { for update := range ch { d.wg.Go(func() { d.handleUpdate(update) }) } } func (d *Dispatcher) Start(ctx context.Context) error { ch, err := d.poller.Start(ctx) if err != nil { return err } d.handleUpdates(ch) <-ctx.Done() d.wg.Wait() return nil } func (d *Dispatcher) handleUpdate(upd model.Update) { ctx := context.Background() var st *state.Context snapshot, err := d.fsmStorage.Get(ctx, int(upd.UserID)) if err != nil && !errors.Is(err, state.ErrStateNotFound) { log.Error().Err(err).Send() return } if errors.Is(err, state.ErrStateNotFound) { st = state.New(int(upd.UserID)) } else { st = state.Restore(snapshot) } c := mcontext.New(ctx, upd, st, d.api) if err := d.handler.HandleUpdate(c); err != nil { log.Error().Err(err).Send() return } st = c.State() if st.State() == state.ClearState { if err := d.fsmStorage.Delete(ctx, int(upd.UserID)); err != nil { log.Error().Err(err).Send() return } } if st.State() != "" { if err := d.fsmStorage.Put(ctx, st.Snapshot()); err != nil { log.Error().Err(err).Send() return } } }