Files
go-telegram/docs/reference/dispatch/conversation.md
T
lukaszraczylo 1088b7f4d7 docs: auto-generate markdown reference + soften README
- Add gomarkdoc-driven reference docs in docs/reference/, regenerated
  automatically by 'make regen' alongside the api/ codegen
- New 'make docs' target installs gomarkdoc on first run; 'make
  docs-check' is a CI gate
- Fold doc-clean assertion into existing codegen-clean job (single
  diff check covers spec + api + reference)
- Rewrite README header: logo via <picture>, friendlier tagline,
  emoji-led 'Why you'll like it' bullets instead of Why-table
- Drop duplicate echo snippet, soften 'Codegen pipeline' section into
  'Keeping up with Telegram'
- Link reference from README, Pages nav, and a new Markdown reference
  card on index.html (target = GitHub source view, renders .md natively)
2026-05-09 14:14:37 +01:00

7.1 KiB

conversation

import "github.com/lukaszraczylo/go-telegram/dispatch/conversation"

Package conversation implements a stateful conversation handler for the go-telegram dispatch router. It provides a state-machine abstraction over multi-step Telegram bot interactions, with pluggable storage and flexible key strategies.

Index

Variables

ErrKeyNotFound is returned by Storage.Get when no conversation is active for the given key.

var ErrKeyNotFound = errors.New("conversation: key not found")

func End

func End() error

End signals the conversation has finished and state should be cleared. Conversation handlers return End() to terminate.

func Next

func Next(s State) error

Next signals the conversation should advance to the given state. Conversation handlers return Next("state_name") to transition.

type Conversation

Conversation is a stateful handler with entry, per-state, exit and fallback steps. A conversation is keyed by KeyStrategy (default KeyByUserAndChat) and persisted by Storage (default in-memory).

type Conversation struct {
    // EntryPoints starts a new conversation when a matching filter fires
    // and no conversation is already active for the key.
    EntryPoints []Step

    // States maps each state to the steps that handle it.
    States map[State][]Step

    // Exits, if any match, end the active conversation early. Useful for
    // /cancel-style commands.
    Exits []Step

    // Fallbacks run when no state step matches the current update.
    Fallbacks []Step

    // Storage persists conversation state. Defaults to NewMemoryStorage.
    Storage Storage

    // KeyStrategy derives the persistence key. Defaults to KeyByUserAndChat.
    KeyStrategy KeyStrategy

    // AllowReEntry, when true, lets entry-point steps fire even while a
    // conversation is already active for the key (effectively restarting it).
    AllowReEntry bool
}

func (*Conversation) Dispatch

func (c *Conversation) Dispatch(next dispatch.Handler[*api.Update]) dispatch.Handler[*api.Update]

Dispatch is a global middleware-shaped Handler that consumes updates and routes them through the conversation graph. Register via router.Use(conv.Dispatch).

If the conversation claims an update, downstream handlers are skipped. If the conversation does not claim it, downstream handlers run as normal.

type Handler

Handler defines a step in the conversation. Receives the dispatch context and the raw update. Returns:

  • nil to stay in the current state
  • Next("state") to transition to a different state
  • End() to end the conversation
  • any other non-nil error to surface to the dispatcher (state unchanged)
type Handler func(ctx *dispatch.Context, u *api.Update) error

type KeyStrategy

KeyStrategy derives a persistence key from an update. Strategies determine how conversation scope works — per-user, per-chat, or per-user-and-chat. Implementations must return a stable string for the same logical scope across updates.

Returns the empty string if the update doesn't have enough context to derive a key (in which case the conversation handler skips it).

type KeyStrategy func(u *api.Update) string

KeyByChat derives a key from the chat ID. Useful for group flows where any user in the chat can drive the conversation.

var KeyByChat KeyStrategy = func(u *api.Update) string {
    if cid := chatID(u); cid != 0 {
        return fmt.Sprintf("c:%d", cid)
    }
    return ""
}

KeyByUser derives a key from the sending user's ID. Useful for DM conversations and any flow that should follow the user across chats.

var KeyByUser KeyStrategy = func(u *api.Update) string {
    if uid := userID(u); uid != 0 {
        return fmt.Sprintf("u:%d", uid)
    }
    return ""
}

KeyByUserAndChat derives a key from both user and chat IDs. The most common strategy: each user has their own conversation per chat.

var KeyByUserAndChat KeyStrategy = func(u *api.Update) string {
    uid := userID(u)
    cid := chatID(u)
    if uid == 0 || cid == 0 {
        return ""
    }
    return fmt.Sprintf("uc:%d:%d", cid, uid)
}

type MemoryStorage

MemoryStorage is the default in-process Storage. It is safe for concurrent use. Conversation state is lost on process restart; use a custom Storage backed by a database for persistent flows.

type MemoryStorage struct {
    // contains filtered or unexported fields
}

func NewMemoryStorage

func NewMemoryStorage() *MemoryStorage

NewMemoryStorage constructs an empty in-memory storage.

func (*MemoryStorage) Delete

func (s *MemoryStorage) Delete(_ context.Context, key string) error

func (*MemoryStorage) Get

func (s *MemoryStorage) Get(_ context.Context, key string) (State, error)

func (*MemoryStorage) Set

func (s *MemoryStorage) Set(_ context.Context, key string, state State) error

type State

State is a label identifying a node in the conversation graph. The empty string is the implicit "no active conversation" state.

type State string

type Step

Step pairs a filter with a handler for one conversation step.

type Step struct {
    Filter  dispatch.Filter[*api.Update]
    Handler Handler
}

type Storage

Storage persists per-user (or per-chat, per-message — depending on the KeyStrategy in use) conversation state across update deliveries.

Implementations must be safe for concurrent use.

type Storage interface {
    Get(ctx context.Context, key string) (State, error)
    Set(ctx context.Context, key string, state State) error
    Delete(ctx context.Context, key string) error
}

Generated by gomarkdoc