Documentation
¶
Overview ¶
Package botapi_fsm provides a finite-state machine layer for github.com/gotd/botapi.
It is storage-agnostic: bring your own Store (memory, Redis, Postgres, …) or use NewMemory for a process-local machine. State and payload types are generic so each bot defines its own states and data shape.
Quick start:
type state string
const (
idle state = ""
awaitingName state = "name"
)
m := fsm.NewMemory[state, struct{}](idle)
m.Register(awaitingName, func(c *botapi.Context, sess *fsm.Session[state, struct{}]) error {
// sess.Data, c.Message().Text, …
return m.Clear(c)
})
pm := bot.Group(botapi.ChatTypeIs(botapi.ChatTypePrivate))
pm.OnCommand("profile", "Start profile wizard", func(c *botapi.Context) error {
if err := m.Enter(c, awaitingName, struct{}{}); err != nil {
return err
}
_, err := c.Reply("What is your name? /cancel to abort.")
return err
})
m.MountGroup(pm)
Mount registers message and callback handlers guarded by Machine.Active so only users with a non-idle session are routed into the FSM. Register FSM handlers before generic fallbacks — botapi dispatches the first match.
Index ¶
- Variables
- func ChatSenderKey(c *botapi.Context) (int64, bool)
- func ChatSenderUpdateKey(u *botapi.Update) (int64, bool)
- func MustPingRedis(ctx context.Context, client redis.Cmdable) error
- func PrivateChatKey(c *botapi.Context) (int64, bool)
- func SenderKey(c *botapi.Context) (int64, bool)
- func SenderUpdateKey(u *botapi.Update) (int64, bool)
- func WithSession[S comparable, D any](ctx context.Context, sess Session[S, D]) context.Context
- type BytesStore
- type CancelHandler
- type Handler
- type KeyFunc
- type Machine
- func (m *Machine[S, D]) Active() botapi.Predicate
- func (m *Machine[S, D]) Clear(c *botapi.Context) error
- func (m *Machine[S, D]) Enter(c *botapi.Context, state S, data D) error
- func (m *Machine[S, D]) Get(c *botapi.Context) (Session[S, D], bool, error)
- func (m *Machine[S, D]) Handler() botapi.Handler
- func (m *Machine[S, D]) InState(states ...S) botapi.Predicate
- func (m *Machine[S, D]) Middleware() botapi.Middleware
- func (m *Machine[S, D]) Mount(bot *botapi.Bot, predicates ...botapi.Predicate)
- func (m *Machine[S, D]) MountGroup(g *botapi.Group, predicates ...botapi.Predicate)
- func (m *Machine[S, D]) Register(state S, h Handler[S, D])
- func (m *Machine[S, D]) Set(c *botapi.Context, sess Session[S, D]) error
- func (m *Machine[S, D]) Warm(ctx context.Context, keys []int64) error
- type MemoryStore
- type Option
- func WithCancelCommand[S comparable, D any](cmd string) Option[S, D]
- func WithKeyFunc[S comparable, D any](f KeyFunc) Option[S, D]
- func WithOnCancel[S comparable, D any](h CancelHandler[S, D]) Option[S, D]
- func WithOnUnknown[S comparable, D any](h UnknownHandler[S, D]) Option[S, D]
- func WithUpdateKeyFunc[S comparable, D any](f UpdateKeyFunc) Option[S, D]
- type RedisBytesStore
- type Session
- type Store
- type UnknownHandler
- type UpdateKeyFunc
Constants ¶
This section is empty.
Variables ¶
var ErrNoSessionKey = errors.New("fsm: no session key in context")
ErrNoSessionKey is returned when a context carries no session key.
Functions ¶
func ChatSenderKey ¶
ChatSenderKey scopes sessions to a chat and user pair (for group FSMs).
func ChatSenderUpdateKey ¶
ChatSenderUpdateKey is the update-level counterpart of ChatSenderKey.
func MustPingRedis ¶
MustPingRedis verifies connectivity and returns detailed error.
func PrivateChatKey ¶
PrivateChatKey scopes sessions to the private-chat user id. Prefer SenderKey; use this explicitly when you only ever run in PM.
func SenderKey ¶
SenderKey uses the update sender's user id (messages, callbacks, inline). In private chats it falls back to chat id when From is absent — that can happen with some MTProto updates even though chat id equals the user id.
func SenderUpdateKey ¶
SenderUpdateKey is the update-level counterpart of SenderKey.
func WithSession ¶
WithSession attaches a session to ctx for downstream handlers and middleware.
Types ¶
type BytesStore ¶
type BytesStore interface {
Get(ctx context.Context, key int64) ([]byte, bool, error)
Set(ctx context.Context, key int64, value []byte) error
Clear(ctx context.Context, key int64) error
}
BytesStore is a minimal key-value backend for encoded sessions.
type CancelHandler ¶
CancelHandler runs after a session is cleared via the cancel command.
type Machine ¶
type Machine[S comparable, D any] struct { // contains filtered or unexported fields }
Machine routes botapi updates to per-state handlers.
func New ¶
func New[S comparable, D any](store Store[S, D], idle S, opts ...Option[S, D]) *Machine[S, D]
New builds a machine backed by store. idle is the zero/active-outside-FSM state.
func NewMemory ¶
func NewMemory[S comparable, D any](idle S, opts ...Option[S, D]) *Machine[S, D]
NewMemory is shorthand for an in-memory machine.
func (*Machine[S, D]) Active ¶
Active is a predicate that matches updates for users currently inside the FSM.
func (*Machine[S, D]) Handler ¶
Handler returns a botapi handler that dispatches by current session state. Pair it with Machine.Active (as Machine.Mount does) so idle users are not consumed.
func (*Machine[S, D]) Middleware ¶
func (m *Machine[S, D]) Middleware() botapi.Middleware
Middleware loads the current session into the context without handling the update.
func (*Machine[S, D]) Mount ¶
Mount registers FSM handlers on bot (messages + callback queries). Extra predicates narrow the scope (e.g. private chats only).
func (*Machine[S, D]) MountGroup ¶
MountGroup registers FSM handlers on a handler group.
type MemoryStore ¶
type MemoryStore[S comparable, D any] struct { // contains filtered or unexported fields }
MemoryStore keeps sessions in process memory.
func NewMemoryStore ¶
func NewMemoryStore[S comparable, D any]() *MemoryStore[S, D]
NewMemoryStore returns an empty in-memory store.
func (*MemoryStore[S, D]) Clear ¶
func (s *MemoryStore[S, D]) Clear(_ context.Context, key int64) error
type Option ¶
type Option[S comparable, D any] func(*Machine[S, D])
Option configures a Machine.
func WithCancelCommand ¶
func WithCancelCommand[S comparable, D any](cmd string) Option[S, D]
WithCancelCommand registers a command that clears the active session. Pass "" to disable. Default is "cancel".
func WithKeyFunc ¶
func WithKeyFunc[S comparable, D any](f KeyFunc) Option[S, D]
WithKeyFunc sets how session keys are derived from handler contexts.
func WithOnCancel ¶
func WithOnCancel[S comparable, D any](h CancelHandler[S, D]) Option[S, D]
WithOnCancel is called after /cancel clears a non-idle session.
func WithOnUnknown ¶
func WithOnUnknown[S comparable, D any](h UnknownHandler[S, D]) Option[S, D]
WithOnUnknown is called when a session references an unregistered state.
func WithUpdateKeyFunc ¶
func WithUpdateKeyFunc[S comparable, D any](f UpdateKeyFunc) Option[S, D]
WithUpdateKeyFunc sets how session keys are derived from updates (for predicates). Must agree with WithKeyFunc on the same updates.
type RedisBytesStore ¶
type RedisBytesStore struct {
// contains filtered or unexported fields
}
RedisBytesStore persists FSM payloads in Redis. Keys are stored as "<prefix><session-key>" and values are raw bytes.
func NewRedisBytesStore ¶
NewRedisBytesStore builds a Redis-backed BytesStore. If ttl is zero, keys do not expire.
func (*RedisBytesStore) Clear ¶
func (s *RedisBytesStore) Clear(ctx context.Context, key int64) error
type Session ¶
type Session[S comparable, D any] struct { State S Data D }
Session is a user's place in the state machine.
func SessionFromContext ¶
SessionFromContext returns a session previously stored with WithSession.
type Store ¶
type Store[S comparable, D any] interface { Get(ctx context.Context, key int64) (Session[S, D], bool, error) Set(ctx context.Context, key int64, sess Session[S, D]) error Clear(ctx context.Context, key int64) error }
Store persists FSM sessions. Implement it for Redis, SQL, or any backend.
func JSONStore ¶
func JSONStore[S comparable, D any](inner BytesStore) Store[S, D]
JSONStore adapts BytesStore to Store using JSON encoding.
func NewRedisJSONStore ¶
func NewRedisJSONStore[S comparable, D any]( client redis.Cmdable, prefix string, ttl time.Duration, ) Store[S, D]
NewRedisJSONStore is a convenience constructor for JSON-encoded sessions.
type UnknownHandler ¶
UnknownHandler runs when stored state has no registered handler (session is cleared first).