Documentation
¶
Index ¶
- Variables
- func AccessDeniedMessage(t *i18n.Localizer, role string) string
- func CmdRef(cmd string) string
- func CommandText(cmd, botUsername string) string
- func DismissCallback() string
- func EncodeConfirmNewCallback(mode string) string
- func EncodeListCallback(resource, action string, args []string, page int) string
- func EncodeModelProviderCallback(providerIndex, page int) string
- func EncodeModelSelectCallback(modelDBID string) string
- func EncodeRangeCallback(resource, action, rangeKey string) string
- func EncodeSkillActivateCallback(name string) string
- func ExtractCommandText(text string) string
- func FallbackTrailer(iv *Interactive, t *i18n.Localizer) string
- func IsInteractiveCallback(data string) bool
- func MdBold(s string) string
- func MdCode(s string) string
- func NoopCallback() string
- func TelegramGroupCommandTip(t *i18n.Localizer, botUsername string) string
- func UnknownCommandMessage(t *i18n.Localizer, text string) string
- type AccessEvaluator
- type BotMemberRoleAdapter
- type ChannelManageResolver
- type ChoicesView
- type CommandContext
- type CommandGroup
- type CommandQueries
- type ContainerFS
- type CurrentContext
- type ExecuteInput
- type FSEntry
- type Handler
- func (h *Handler) CommandAccess(ctx context.Context, input ExecuteInput) (bool, error)
- func (h *Handler) CurrentContext(ctx context.Context, botID string) (CurrentContext, error)
- func (h *Handler) Execute(ctx context.Context, botID, channelIdentityID, text string) (string, error)
- func (h *Handler) ExecuteResult(ctx context.Context, input ExecuteInput) (res *Result, err error)
- func (h *Handler) ExecuteWithInput(ctx context.Context, input ExecuteInput) (string, error)
- func (h *Handler) HasCommandResource(resource string) bool
- func (h *Handler) IsCommand(text string) bool
- func (*Handler) IsCommandShaped(text string) bool
- func (h *Handler) MemberRole(ctx context.Context, botID, channelIdentityID string) (string, error)
- func (h *Handler) ResolveLocale(ctx context.Context, botID string) string
- func (h *Handler) SetCompactionService(s *compaction.Service, q dbstore.Queries)
- func (h *Handler) SetLinkConsumer(c LinkConsumer)
- type HintVerb
- type Interactive
- type InteractiveKind
- type Invocation
- type InvocationInput
- type ItemAction
- type LinkConsumer
- type ListItem
- type ListView
- type MemberRoleResolver
- type MenuCommand
- type ModelPickerView
- type ParsedCallback
- type ParsedCommand
- type PickerLevel
- type PickerModel
- type PickerProvider
- type RangeView
- type Registry
- func (r *Registry) ActionHelp(groupName, action string, localizers ...*i18n.Localizer) string
- func (r *Registry) GlobalHelp(t *i18n.Localizer, commandTarget string) string
- func (r *Registry) GroupHelp(name string, localizers ...*i18n.Localizer) string
- func (r *Registry) GroupHelpResult(name string, localizers ...*i18n.Localizer) *Result
- func (r *Registry) RegisterGroup(group *CommandGroup)
- type Result
- type RuntimeSkillLister
- type Skill
- type SkillLoader
- type SubCommand
Constants ¶
This section is empty.
Variables ¶
var ( ErrNotCommand = commandsyntax.ErrNotCommand ErrCommandForOtherBot = commandsyntax.ErrCommandForOtherBot )
Functions ¶
func AccessDeniedMessage ¶
AccessDeniedMessage is the reply sent when the chat ACL denies a sender. It doubles as the binding hint so a forgetful owner learns how to link in (/link) while a genuine outsider learns they aren't permitted. A manager gets a narrower message because Manage access does not imply Chat access.
func CmdRef ¶
CmdRef renders a slash-command reference as a tap-to-copy code span: on Telegram a code span is tap-to-copy monospace, and on text-only channels the backticks strip cleanly. Accepts "schedule list" or "/schedule list".
func CommandText ¶
CommandText renders a slash command and optionally addresses its first token to a bot username. Keeping the suffix next to the resource is required for Telegram's /command@bot grammar; any arguments remain after that token.
func DismissCallback ¶
func DismissCallback() string
DismissCallback returns the callback_data that closes an interactive message.
func EncodeConfirmNewCallback ¶
EncodeConfirmNewCallback builds the callback_data for confirming a /new reset. Layout: "m~cn~{mode}" where mode is chat|discuss. Tapping re-dispatches "/new {mode} --confirm", which performs the actual session reset.
func EncodeListCallback ¶
EncodeListCallback builds the callback_data for a list-pagination button. Layout: "m~lp~{resource}~{action}~{page}~{argsToken}". When the encoded args would push the string past Telegram's 64-byte limit, the args are stashed in a bounded process-local table and referenced by a short token ("#<hash>").
func EncodeModelProviderCallback ¶
EncodeModelProviderCallback builds the callback_data for drilling into a provider's paginated model list. Layout: "m~mpl~{providerIndex}~{page}".
func EncodeModelSelectCallback ¶
EncodeModelSelectCallback builds the callback_data for selecting a model by its stable DB id. Layout: "m~ms~{modelDBID}". Using the stable id (not a positional/flat index) means a model-list change between render and tap can't silently resolve the tap to a different model. The id is a UUID (~36 bytes), so "m~ms~"+id stays well within Telegram's 64-byte callback_data limit.
func EncodeRangeCallback ¶
EncodeRangeCallback builds the callback_data for a time-window preset button. Layout: "m~rg~{resource}~{action}~{rangeKey}".
func EncodeSkillActivateCallback ¶
EncodeSkillActivateCallback builds the callback_data for a tap-to-activate skill row on /skill list. Layout: "m~sk~{name}". Tapping re-dispatches the canonical "/{name}" skill slash, so the activation runs through the exact same classify→resolve→activate pipeline as a typed slash — permissions and runtime-usability are judged at tap time, never baked into the keyboard. Names too long for Telegram's 64-byte limit are stashed like list args ("#<hash>"); a stash miss after restart/rollover simply invalidates the stale button (the user re-runs /skill list).
func ExtractCommandText ¶
func FallbackTrailer ¶
func FallbackTrailer(iv *Interactive, t *i18n.Localizer) string
FallbackTrailer derives a human-readable list of typeable commands from an Interactive payload, intended for appending to Result.Text on channels that cannot render buttons. Returns "" when the payload offers no typeable affordance worth surfacing (display-only list with no extras, suppressed choices, nil view).
The returned string carries Markdown markup (backticks via MdCode); the renderer's applyMessageFormat strips it for plain-text channels.
func IsInteractiveCallback ¶
IsInteractiveCallback reports whether data is one of our interactive callbacks (as opposed to a tool-approval callback or unrelated data).
func MdBold ¶
MdBold / MdCode author Markdown for capable channels. Telegram bold is **double-star** (single * is italic); backtick code spans are the safe path for dynamic values (escaped verbatim, never mis-parsed).
func NoopCallback ¶
func NoopCallback() string
NoopCallback returns the callback_data for inert buttons (e.g. the page indicator) that should be acknowledged but otherwise ignored.
func TelegramGroupCommandTip ¶
TelegramGroupCommandTip explains the one transport-specific rule users need when typing commands manually. Command lists stay visually clean; the bot username appears once in this complete, copyable example.
Types ¶
type AccessEvaluator ¶
type AccessEvaluator interface {
Evaluate(ctx context.Context, req acl.EvaluateRequest) (bool, error)
}
AccessEvaluator checks whether the current channel context may trigger chat.
type BotMemberRoleAdapter ¶
type BotMemberRoleAdapter struct {
BotService *bots.Service
ManageResolver ChannelManageResolver
}
BotMemberRoleAdapter adapts bots.Service to MemberRoleResolver.
func (*BotMemberRoleAdapter) GetMemberRole ¶
type ChannelManageResolver ¶
type ChannelManageResolver interface {
HasManageGrant(ctx context.Context, botID, channelIdentityID string) (bool, error)
}
ChannelManageResolver reports whether a channel identity has been granted the manage capability on a bot (Channel Access "Manage"). It lets an IM identity run owner-only slash commands without being the web owner.
type ChoicesView ¶
type ChoicesView struct {
Title string
Choices []ListItem
Columns int // optional keyboard columns; 0 lets renderers pick
BodyEnumeratesChoices bool
}
ChoicesView is a flat set of selectable choices (no pagination).
BodyEnumeratesChoices asserts that the body Text already enumerates every typeable affordance (e.g. /help <group>'s Usage block lists every sub-command verbatim), so the no-button fallback trailer should skip this surface to avoid duplicating what the body already says.
This is a bool (not a HintVerb sentinel like ListView's HintVerb) because the question being answered is different: ChoicesView picks WHETHER to emit a trailer at all, ListView picks WHICH verb to use. See ListView.HintVerb.
type CommandContext ¶
type CommandContext struct {
Ctx context.Context
BotID string
Role string // "owner", "admin", "member", or "" (guest)
WriteAccess bool
Args []string
ChannelIdentityID string
UserID string
ChannelType string
ConversationType string
ConversationID string
ThreadID string
RouteID string
SessionID string
Page int // zero-based page offset for paginated list commands
Prov int // provider index for the model picker (-1 if absent)
SelectID string // stable model id for picker selection ("" if absent)
Range string // time-window key for time-series commands ("" = default)
Locale string // resolved command-UI locale ("en", "zh", …)
L *i18n.Localizer
}
CommandContext carries execution context for a sub-command.
type CommandGroup ¶
type CommandGroup struct {
Name string
Description string
DefaultAction string
// contains filtered or unexported fields
}
CommandGroup groups sub-commands under a resource name.
func (*CommandGroup) ActionHelp ¶
func (g *CommandGroup) ActionHelp(action string, localizers ...*i18n.Localizer) string
func (*CommandGroup) Register ¶
func (g *CommandGroup) Register(sub SubCommand)
type CommandQueries ¶
type CommandQueries interface {
GetLatestSessionIDByBot(ctx context.Context, botID pgtype.UUID) (pgtype.UUID, error)
CountMessagesBySession(ctx context.Context, sessionID pgtype.UUID) (int64, error)
GetLatestAssistantUsage(ctx context.Context, sessionID pgtype.UUID) (int64, error)
GetSessionCacheStats(ctx context.Context, sessionID pgtype.UUID) (dbsqlc.GetSessionCacheStatsRow, error)
GetSessionUsedSkills(ctx context.Context, sessionID pgtype.UUID) ([]string, error)
GetTokenUsageByDayAndType(ctx context.Context, arg dbsqlc.GetTokenUsageByDayAndTypeParams) ([]dbsqlc.GetTokenUsageByDayAndTypeRow, error)
GetTokenUsageByModel(ctx context.Context, arg dbsqlc.GetTokenUsageByModelParams) ([]dbsqlc.GetTokenUsageByModelRow, error)
// UpdateSessionModelPreference is the /model and /reasoning clear path
// (issue #879, spec P11′): called with NULL fields to drop a web-pinned
// pair so the session returns to the bot-default chain.
UpdateSessionModelPreference(ctx context.Context, arg dbsqlc.UpdateSessionModelPreferenceParams) error
}
CommandQueries captures the sqlc methods used by slash commands. dbstore.Queries satisfies this interface directly.
type ContainerFS ¶
type ContainerFS interface {
ListDir(ctx context.Context, botID, path string) ([]FSEntry, error)
ReadFile(ctx context.Context, botID, path string) (string, error)
}
ContainerFS provides read-only access to a bot's container filesystem.
type CurrentContext ¶
type CurrentContext struct {
ChatModel string
// ReasoningEffort carries models.ReasoningEffortDisable when reasoning is off.
ReasoningEffort string
ContextWindow string // resolved chat-model context window (e.g. "128.0K"), "" if unknown
}
CurrentContext is the resolved current-state summary used to enrich /new and bare /model output. All fields are display-ready strings.
type ExecuteInput ¶
type ExecuteInput struct {
BotID string
ChannelIdentityID string
UserID string
Text string
// Invocation is the canonical channel parse. When present, command handling
// must use it instead of interpreting Text again.
Invocation *Invocation
ChannelType string
ConversationType string
ConversationID string
ThreadID string
RouteID string
SessionID string
// CommandTarget optionally identifies the bot username for transport-specific
// command guidance. Channel callers set it only for Telegram group messages.
CommandTarget string
// Locale optionally pins the command-UI locale. When empty, ExecuteResult
// resolves it from the bot's command_ui_language setting (auto → en).
Locale string
}
ExecuteInput carries the caller identity and channel context for command execution.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler processes slash commands intercepted before they reach the LLM.
func NewHandler ¶
func NewHandler( log *slog.Logger, roleResolver MemberRoleResolver, scheduleService *schedule.Service, settingsService *settings.Service, mcpConnService *mcp.ConnectionService, modelsService *models.Service, providersService *providers.Service, memProvService *memprovider.Service, searchProvService *searchproviders.Service, emailService *emailpkg.Service, emailOutboxService *emailpkg.OutboxService, queries CommandQueries, aclEvaluator AccessEvaluator, skillLoader SkillLoader, containerFS ContainerFS, ) *Handler
NewHandler creates a Handler with all required services.
func (*Handler) CommandAccess ¶
CommandAccess reports whether the caller may operate a command on the bot, applying the same policy as Execute's gate: /link is always allowed; owners and managers are always allowed; everyone else must pass the chat ACL. The channel processor uses it to gate the route-aware mode commands (/new, /stop, /status) that do not flow through Execute.
func (*Handler) CurrentContext ¶
CurrentContext resolves the bot's current model/reasoning state for enriching command output (e.g. the /new confirmation). It is a read-only view over existing bot settings and makes no changes.
func (*Handler) Execute ¶
func (h *Handler) Execute(ctx context.Context, botID, channelIdentityID, text string) (string, error)
Execute parses and runs a slash command, returning the text reply.
func (*Handler) ExecuteResult ¶
ExecuteResult parses and runs a slash command, returning a neutral Result. The Result always carries complete Text; Interactive is set only by commands that opt into rich rendering via SubCommand.ResultHandler.
func (*Handler) ExecuteWithInput ¶
ExecuteWithInput parses and runs a slash command with channel/session context, returning the plain-text reply. It delegates to ExecuteResult and flattens the structured result to its text form.
func (*Handler) HasCommandResource ¶
HasCommandResource checks registry membership for an already parsed command. Channel classification uses this form so it never reparses a synthetic string.
func (*Handler) IsCommand ¶
IsCommand reports whether the text contains a slash command. Handles both direct commands ("/help") and mention-prefixed commands ("@bot /help").
func (*Handler) IsCommandShaped ¶
IsCommandShaped reports whether text looks like a slash command (a leading slash followed by a command-name token), whether or not it is registered. It lets the channel layer reply with a helpful "unknown command" hint instead of forwarding a mistyped command to the model. Paths/URLs (e.g. "/a/b") are rejected so they are not mistaken for commands.
func (*Handler) MemberRole ¶
MemberRole resolves the sender's bot role for presentation decisions outside command execution. Callers must not use it as an authorization shortcut.
func (*Handler) ResolveLocale ¶
ResolveLocale resolves the command-UI locale for a bot from its command_ui_language setting (auto/unknown → server default). Any settings I/O error falls back to the default locale, so command rendering never blocks on it. Exported so the channel layer can localize its own renderer chrome and operational-failure messages with the same locale.
Uses the scoped GetCommandUILanguage (single DB query) rather than GetBot (which also fetches the ACL default-effect) — the locale is resolved per command and per interactive callback tap, so single-query matters for paginated lists where users tap Prev/Next repeatedly.
func (*Handler) SetCompactionService ¶
func (h *Handler) SetCompactionService(s *compaction.Service, q dbstore.Queries)
SetCompactionService configures the compaction service for the /compact command.
func (*Handler) SetLinkConsumer ¶
func (h *Handler) SetLinkConsumer(c LinkConsumer)
SetLinkConsumer wires the account-link consumer used by the /link command.
type HintVerb ¶
type HintVerb string
HintVerb names the shape of a typeable affordance when rendering the no-button fallback trailer. Setting ListView.HintVerb to one of these overrides the automatic inference in FallbackTrailer.
The values double as the suffix of the i18n key (cmd.fallback.<verb>) so adding a verb is a two-step change: register the constant here and add the matching localized template in locales/*.json.
type Interactive ¶
type Interactive struct {
Kind InteractiveKind
List *ListView
Picker *ModelPickerView
Choices *ChoicesView
Range *RangeView
}
Interactive carries optional structured data for rich rendering. Exactly one of the typed views is set, selected by Kind.
type InteractiveKind ¶
type InteractiveKind string
InteractiveKind discriminates the structured payload carried by a Result.
const ( // InteractiveList is a generic, paginated list of display rows. InteractiveList InteractiveKind = "list" // InteractiveModelPicker is the two-level provider→model drill-down picker. InteractiveModelPicker InteractiveKind = "model_picker" // InteractiveChoices is a flat one-shot set of selectable choices. InteractiveChoices InteractiveKind = "choices" // InteractiveRange is a time-window selector for time-series commands. InteractiveRange InteractiveKind = "range" )
type Invocation ¶
type Invocation = commandsyntax.Invocation
ParsedCommand remains an alias so command handlers and callers can migrate to the shared syntax package without duplicating the parser implementation.
func ParseInvocation ¶
func ParseInvocation(input InvocationInput) (Invocation, error)
type InvocationInput ¶
type InvocationInput = commandsyntax.InvocationInput
ParsedCommand remains an alias so command handlers and callers can migrate to the shared syntax package without duplicating the parser implementation.
type ItemAction ¶
ItemAction triggers a command when a row is tapped.
func (*ItemAction) Typeable ¶
func (a *ItemAction) Typeable() string
Typeable renders an ItemAction as the slash command a user would type to invoke it (e.g. "/memory set Alice"). Nil-safe.
Unlike ParsedCallback.SyntheticCommand, this is designed for display in hint text — it does not append --page artifacts and does not round-trip through the callback encoder.
Args containing whitespace are double-quoted so a copy-pasted hint round-trips through Parse()/tokenize() back to the same intent. Without quoting, `/memory set my provider` would tokenize as ["my", "provider"] and the handler would read only "my" — silently picking the wrong target.
type LinkConsumer ¶
type LinkConsumer interface {
ConsumeLinkCode(ctx context.Context, token, channelIdentityID string) (channelaccess.Binding, error)
}
LinkConsumer binds the calling channel identity to the web user that owns a one-time link code. It is satisfied by channelaccess.Service.
type ListItem ¶
type ListItem struct {
Label string
Detail string
Selected bool
Action *ItemAction
Callback string
}
ListItem is one row in a ListView. Action is nil for display-only rows.
Callback optionally carries pre-encoded callback_data for button-capable renderers; when set it takes precedence over Action as the button value. Rows with only Callback stay out of the no-button fallback trailer (which derives typeable hints from Action), so such callers must carry the typeable form in the body text instead.
type ListView ¶
type ListView struct {
Title string
ButtonText string // optional compact text for button-capable channels
Resource string // command resource (e.g. "mcp"), round-trips in callback data
Action string // command action (e.g. "list")
Args []string // narrowing args (e.g. a provider filter) that round-trip
Items []ListItem
Total int // total items across all pages
Page int // zero-based page index of this view
PageSize int // items per page
ExtraActions []ListItem // contextual action buttons below the list rows (e.g. "All commands")
HintVerb HintVerb // optional fallback-trailer verb override (HintVerb*)
}
ListView is a generic paginated list. It is re-derivable by re-running the originating command with a page offset, so Resource/Action/Args round-trip through the callback data of pagination buttons.
HintVerb is an optional explicit verb (one of HintVerb*) for trailer derivation on no-button channels — used when rows are display-only but the list has a paired typeable affordance (e.g. /mcp list rows are display-only but /mcp get <name> is the typeable next step). Empty = infer from structure.
Note the asymmetry with ChoicesView.BodyEnumeratesChoices: ListView needs to pick WHICH verb to emit, ChoicesView only needs to choose WHETHER to emit. Different questions, different mechanisms — kept that way rather than forcing them into one shape that fits neither cleanly.
type MemberRoleResolver ¶
type MemberRoleResolver interface {
GetMemberRole(ctx context.Context, botID, channelIdentityID string) (string, error)
}
MemberRoleResolver resolves a user's role within a bot.
type MenuCommand ¶
type MenuCommand struct {
Command string // command name without the leading slash, lowercase
Description string // short label shown beside the command in the menu
}
MenuCommand is one entry for a channel's native slash-command menu (e.g. Telegram's setMyCommands), so users discover and tap commands without typing.
func MenuCommands ¶
func MenuCommands(t *i18n.Localizer) []MenuCommand
MenuCommands returns the curated slash-command list to advertise in a channel's native command menu, with descriptions localized via t. It is the single source for that menu; order roughly follows everyday usefulness. Only single-token commands belong here — the native menu cannot express sub-actions like "schedule list" (those are discovered via /help or in-message buttons).
A nil Localizer renders English (the safe default), which is what transport adapters that register the menu without per-bot locale context currently pass.
type ModelPickerView ¶
type ModelPickerView struct {
Level PickerLevel
Providers []PickerProvider // populated at the provider level
Models []PickerModel // populated at the model level
ProviderIndex int // which provider we drilled into (model level)
ProviderName string // name of that provider (model level), for the header
Page int
PageSize int
Total int
CurrentModelDBID string // settings.ChatModelID, for ●/✓ marking
CurrentDisplay string // resolved current chat model "Name (Provider)", for the header
Reasoning string // current reasoning effort label, for the header
}
ModelPickerView is the two-level model picker (populated in the model-picker phase). Level selects whether Providers or Models is rendered.
type ParsedCallback ¶
type ParsedCallback struct {
Kind string
Resource string
Action string
Args []string
Page int
ProviderIndex int
SelectID string
Range string
}
ParsedCallback is the decoded form of an interactive callback_data string.
func DecodeCallback ¶
func DecodeCallback(data string) (ParsedCallback, bool)
DecodeCallback parses an interactive callback_data string. The bool is false for data that is not one of our interactive callbacks.
func (ParsedCallback) IsDismiss ¶
func (p ParsedCallback) IsDismiss() bool
IsDismiss reports whether the callback closes the interactive message.
func (ParsedCallback) IsNoop ¶
func (p ParsedCallback) IsNoop() bool
IsNoop reports whether the callback is inert (e.g. the page indicator).
func (ParsedCallback) IsSkillActivation ¶
func (p ParsedCallback) IsSkillActivation() bool
IsSkillActivation reports whether the callback activates a skill. Unlike pagination/selection callbacks, activation starts a fresh chat turn: adapters must dispatch it as a new directed message instead of editing the tapped card in place.
func (ParsedCallback) SyntheticCommand ¶
func (p ParsedCallback) SyntheticCommand() string
SyntheticCommand returns the slash command text to re-dispatch for a parsed callback, or "" when the callback has no command (dismiss/noop).
type ParsedCommand ¶
type ParsedCommand = commandsyntax.ParsedCommand
ParsedCommand remains an alias so command handlers and callers can migrate to the shared syntax package without duplicating the parser implementation.
func Parse ¶
func Parse(text string) (ParsedCommand, error)
type PickerLevel ¶
type PickerLevel string
PickerLevel is the drill-down level of a ModelPickerView.
const ( // LevelProviders renders the provider grid. LevelProviders PickerLevel = "providers" // LevelModels renders the paginated model list for one provider. LevelModels PickerLevel = "models" )
type PickerModel ¶
PickerModel is one model button in the picker. Selected marks the active model (rendered with ✓). DBID is the model's stable id, carried in the selection callback so a list change between render and tap can't resolve the tap to a different model.
type PickerProvider ¶
PickerProvider is one provider button in the picker. HasCurrent marks the provider that holds the currently-selected model (rendered with ●). Count is the number of chat models the provider offers.
type RangeView ¶
type RangeView struct {
Resource string
Action string
Current string // the active preset key (normalized), for the ● marker
Presets []string // ordered preset keys, e.g. ["24h","7d","30d","all"]
}
RangeView is a time-window selector for a time-series command. Selecting a preset re-runs "/{Resource} {Action} --range <preset>" in place.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds all registered command groups.
func (*Registry) ActionHelp ¶
func (*Registry) GlobalHelp ¶
GlobalHelp returns the top-level help text listing all commands. Commands stay short, plain, and tap-to-send; Telegram's manual /command@bot rule appears once below the list instead of repeating the username on every row.
func (*Registry) GroupHelpResult ¶
GroupHelpResult returns an interactive version of GroupHelp: each sub-action is a tappable button that dispatches "/{group} {action}" in place, plus a "◀ Back" button that returns to the group's default content view. Text-only channels fall back to the textual Usage listing.
func (*Registry) RegisterGroup ¶
func (r *Registry) RegisterGroup(group *CommandGroup)
type Result ¶
type Result struct {
Text string
Interactive *Interactive
Locale string
FeedbackError *agentfeedback.Error
}
Result is the neutral, platform-independent output of a command.
Text is always a complete rendering usable by channels without rich UI. Interactive is optional structured data that capable renderers (e.g. the Telegram inline keyboard) may upgrade into buttons and pagination.
Locale is the resolved command-UI locale ("en", "zh", …) the Text/Interactive labels were rendered in. Channels use it to localize their own renderer chrome (Close/Prev/Next/…) so the whole reply stays in one language. Empty means the server default (English).
func WithButtons ¶
WithButtons attaches tappable action buttons to any Result (including plain text / empty states). Button channels render a ChoicesView; text-only channels see only the text. Use this for empty-state guidance buttons ("All commands ▸") where there is no list to attach ExtraActions to.
func WithExtraActions ¶
WithExtraActions attaches contextual entry buttons below the list rows of a Result (e.g. "All commands", "Create new"). Only meaningful when the Result carries an InteractiveList. Nil/non-list Results pass through unchanged.
type RuntimeSkillLister ¶
type RuntimeSkillLister interface {
ListRuntimeSkills(ctx context.Context, botID string) ([]Skill, error)
}
RuntimeSkillLister lists the bot's runtime-usable skills — the same safe catalog the Web slash picker shows (unique, enabled, loadable as model context). Optional capability of SkillLoader implementations: when present, /skill list renders these entries with tap-to-activate buttons instead of the raw full listing, so IM and Web expose one identical skill surface.
type SkillLoader ¶
SkillLoader loads skills for a bot.
type SubCommand ¶
type SubCommand struct {
Name string
Usage string
IsWrite bool
Handler func(cc CommandContext) (string, error)
ResultHandler func(cc CommandContext) (*Result, error)
}
SubCommand describes a single sub-command within a resource group.
A sub-command provides either Handler (plain text) or ResultHandler (structured Result for rich rendering). When both are set, ResultHandler takes precedence.