mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-06-06 22:49:32 +00:00
f899cc2663
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.
244 lines
8.4 KiB
Markdown
244 lines
8.4 KiB
Markdown
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
|
|
|
|
# conversation
|
|
|
|
```go
|
|
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](<#variables>)
|
|
- [func End\(\) error](<#End>)
|
|
- [func Next\(s State\) error](<#Next>)
|
|
- [type Conversation](<#Conversation>)
|
|
- [func \(c \*Conversation\) Dispatch\(next dispatch.Handler\[\*api.Update\]\) dispatch.Handler\[\*api.Update\]](<#Conversation.Dispatch>)
|
|
- [type Handler](<#Handler>)
|
|
- [type KeyStrategy](<#KeyStrategy>)
|
|
- [type MemoryStorage](<#MemoryStorage>)
|
|
- [func NewMemoryStorage\(\) \*MemoryStorage](<#NewMemoryStorage>)
|
|
- [func \(s \*MemoryStorage\) Delete\(\_ context.Context, key string\) error](<#MemoryStorage.Delete>)
|
|
- [func \(s \*MemoryStorage\) Get\(\_ context.Context, key string\) \(State, error\)](<#MemoryStorage.Get>)
|
|
- [func \(s \*MemoryStorage\) Set\(\_ context.Context, key string, state State\) error](<#MemoryStorage.Set>)
|
|
- [type State](<#State>)
|
|
- [type Step](<#Step>)
|
|
- [type Storage](<#Storage>)
|
|
|
|
|
|
## Variables
|
|
|
|
<a name="ErrKeyNotFound"></a>ErrKeyNotFound is returned by Storage.Get when no conversation is active for the given key.
|
|
|
|
```go
|
|
var ErrKeyNotFound = errors.New("conversation: key not found")
|
|
```
|
|
|
|
<a name="End"></a>
|
|
## func [End](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/handler.go#L34>)
|
|
|
|
```go
|
|
func End() error
|
|
```
|
|
|
|
End signals the conversation has finished and state should be cleared. Conversation handlers return End\(\) to terminate.
|
|
|
|
<a name="Next"></a>
|
|
## func [Next](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/handler.go#L28>)
|
|
|
|
```go
|
|
func Next(s State) error
|
|
```
|
|
|
|
Next signals the conversation should advance to the given state. Conversation handlers return Next\("state\_name"\) to transition.
|
|
|
|
<a name="Conversation"></a>
|
|
## type [Conversation](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/handler.go#L55-L79>)
|
|
|
|
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\).
|
|
|
|
```go
|
|
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
|
|
}
|
|
```
|
|
|
|
<a name="Conversation.Dispatch"></a>
|
|
### func \(\*Conversation\) [Dispatch](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/handler.go#L87>)
|
|
|
|
```go
|
|
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.
|
|
|
|
<a name="Handler"></a>
|
|
## type [Handler](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/handler.go#L44>)
|
|
|
|
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\)
|
|
|
|
```go
|
|
type Handler func(ctx *dispatch.Context, u *api.Update) error
|
|
```
|
|
|
|
<a name="KeyStrategy"></a>
|
|
## type [KeyStrategy](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/key.go#L16>)
|
|
|
|
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\).
|
|
|
|
```go
|
|
type KeyStrategy func(u *api.Update) string
|
|
```
|
|
|
|
<a name="KeyByChat"></a>KeyByChat derives a key from the chat ID. Useful for group flows where any user in the chat can drive the conversation.
|
|
|
|
```go
|
|
var KeyByChat KeyStrategy = func(u *api.Update) string {
|
|
if cid := chatID(u); cid != 0 {
|
|
return fmt.Sprintf("c:%d", cid)
|
|
}
|
|
return ""
|
|
}
|
|
```
|
|
|
|
<a name="KeyByUser"></a>KeyByUser derives a key from the sending user's ID. Useful for DM conversations and any flow that should follow the user across chats.
|
|
|
|
```go
|
|
var KeyByUser KeyStrategy = func(u *api.Update) string {
|
|
if uid := userID(u); uid != 0 {
|
|
return fmt.Sprintf("u:%d", uid)
|
|
}
|
|
return ""
|
|
}
|
|
```
|
|
|
|
<a name="KeyByUserAndChat"></a>KeyByUserAndChat derives a key from both user and chat IDs. The most common strategy: each user has their own conversation per chat.
|
|
|
|
```go
|
|
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)
|
|
}
|
|
```
|
|
|
|
<a name="MemoryStorage"></a>
|
|
## type [MemoryStorage](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/in_memory.go#L11-L14>)
|
|
|
|
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.
|
|
|
|
```go
|
|
type MemoryStorage struct {
|
|
// contains filtered or unexported fields
|
|
}
|
|
```
|
|
|
|
<a name="NewMemoryStorage"></a>
|
|
### func [NewMemoryStorage](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/in_memory.go#L17>)
|
|
|
|
```go
|
|
func NewMemoryStorage() *MemoryStorage
|
|
```
|
|
|
|
NewMemoryStorage constructs an empty in\-memory storage.
|
|
|
|
<a name="MemoryStorage.Delete"></a>
|
|
### func \(\*MemoryStorage\) [Delete](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/in_memory.go#L38>)
|
|
|
|
```go
|
|
func (s *MemoryStorage) Delete(_ context.Context, key string) error
|
|
```
|
|
|
|
|
|
|
|
<a name="MemoryStorage.Get"></a>
|
|
### func \(\*MemoryStorage\) [Get](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/in_memory.go#L21>)
|
|
|
|
```go
|
|
func (s *MemoryStorage) Get(_ context.Context, key string) (State, error)
|
|
```
|
|
|
|
|
|
|
|
<a name="MemoryStorage.Set"></a>
|
|
### func \(\*MemoryStorage\) [Set](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/in_memory.go#L31>)
|
|
|
|
```go
|
|
func (s *MemoryStorage) Set(_ context.Context, key string, state State) error
|
|
```
|
|
|
|
|
|
|
|
<a name="State"></a>
|
|
## type [State](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/state.go#L9>)
|
|
|
|
State is a label identifying a node in the conversation graph. The empty string is the implicit "no active conversation" state.
|
|
|
|
```go
|
|
type State string
|
|
```
|
|
|
|
<a name="Step"></a>
|
|
## type [Step](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/handler.go#L47-L50>)
|
|
|
|
Step pairs a filter with a handler for one conversation step.
|
|
|
|
```go
|
|
type Step struct {
|
|
Filter dispatch.Filter[*api.Update]
|
|
Handler Handler
|
|
}
|
|
```
|
|
|
|
<a name="Storage"></a>
|
|
## type [Storage](<https://github.com/lukaszraczylo/go-telegram/blob/main/dispatch/conversation/storage.go#L16-L20>)
|
|
|
|
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.
|
|
|
|
```go
|
|
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](<https://github.com/princjef/gomarkdoc>)
|