GetChatAdministrators returns []ChatMember, where ChatMember is a sealed-interface union. The codegen template emitted the generic client.Call[..., []ChatMember] for it — encoding/json cannot unmarshal a slice of an interface (no discriminator-aware path), so every real response from Telegram failed at the parse step: telegram: parse: json: cannot unmarshal api.ChatMember into Go struct field Result[[]ChatMember].Result of type api.ChatMember Fix is in cmd/genapi/methods.tmpl: add a third branch alongside the existing single-union branch. When a method returns []<union>, emit CallRaw + json.Unmarshal into []json.RawMessage + per-element Unmarshal<Union>(e). Mirrors what GetChatMember (single-element) already does, applied uniformly so any future slice-of-union method Telegram introduces inherits the right shape. Survey of v1.1.1 across all 23 sealed-interface unions confirms GetChatAdministrators was the only broken site; the fix regenerates just that one method body. New regression tests in api/getchatadministrators_test.go cover the typical admin+owner response and the empty-array case.
8.4 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
- func End() error
- func Next(s State) error
- type Conversation
- type Handler
- type KeyStrategy
- type MemoryStorage
- type State
- type Step
- type Storage
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