laniakea

package module
v2.0.0-rc.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: GPL-3.0 Imports: 34 Imported by: 0

README

Laniakea

Laniakea

Go Version Gitea Release

A lightweight, easy-to-use, and performant Telegram Bot API wrapper for Go. It simplifies bot development with a clean plugin system, middleware support, automatic command generation, and built-in rate limiting.

На русском

Wiki


✨ Features

  • Simple & Intuitive API: Designed for ease of use, based on practical examples.
  • Plugin System: Organize your bot's functionality into independent, reusable plugins.
  • Command Handling: Easily register commands and extract arguments.
  • Middleware Support: Run code before or after commands (e.g., logging, access control).
  • Automatic Command Generation: Generate help and command lists automatically.
  • Built-in Rate Limiting: Protect your bot from hitting Telegram API limits (supports retry_after handling).
  • Context-Aware: Pass custom application data or state contexts to your handlers.
  • Configurable API: Mix Set... and Add... helpers to configure bots clearly (for example, bot.SetErrorTemplate(...).AddPlugins(...)).
  • Polling and Webhook Runtime: Run bots through long polling with Run() / RunWithContext(...) or through a bot-owned webhook server with RunWebhookWithContext(...).

📦 Installation

go get git.scuroneko.dev/scuroneko/laniakea/v2@v2.0.0-rc.1

🚀 Quick Start (with step-by-step explanation)

Here is a minimal echo/ping bot example with detailed comments.

package main

import (
	"log"

	"git.scuroneko.dev/scuroneko/laniakea/v2" // Import the Laniakea library
)

// echo is a command handler function.
// It receives two parameters:
//   - ctx: the message context (contains info about the message, sender, chat, etc.)
//   - data: your shared application data (here we use NoData, a placeholder for no shared data)
func echo(ctx *laniakea.MessageContext, data laniakea.NoData) error {
	// Answer the user with the text they sent, without any command prefix.
	// ctx.Text contains the user's message with the command part stripped off.
	ctx.Answer(ctx.Text) // User input WITHOUT command
	return nil
}

func main() {
	// 1. Create bot options. Replace "TOKEN" with your actual bot token from @BotFather.
	opts := &laniakea.BotOpts{Token: "TOKEN"}

	// 2. Initialize a new bot instance.
	//    We use laniakea.NoData as the application data type (no shared data needed for this example).
	bot, err := laniakea.NewBot[laniakea.NoData](opts)
	if err != nil {
		log.Fatal(err)
	}
	// Ensure bot resources are cleaned up on exit.
	defer bot.Close()

	// 3. Create a new plugin named "ping".
	//    Plugins help group related commands and middlewares.
	p := laniakea.NewPlugin[laniakea.NoData]("ping")

	// 4. Add a command to the plugin.
	//    p.Command("echo", echo) creates a command that triggers the 'echo' function on the "/echo" command.
	p.Command("echo", echo)

	// 5. Add another command using an anonymous function (closure).
	//    This command simply replies "Pong" when the user sends "/ping".
	p.Command("ping", func(ctx *laniakea.MessageContext, data laniakea.NoData) error {
		ctx.Answer("Pong")
		return nil
	})

	// 6. Configure the bot with a custom error template and add the plugin.
	//    SetErrorTemplate sets a format string for errors (where %s will be replaced by the actual error).
	//    AddPlugins(p) registers our "ping" plugin with the bot.
	bot = bot.SetErrorTemplate("Error\n\n%s").AddPlugins(p)

	// 7. Automatically generate commands like /start, /help, and a list of all registered commands.
	//    This is optional but very useful for most bots.
	if err := bot.AutoGenerateCommands(); err != nil {
		log.Println(err)
	}

	// 8. Start the bot, listening for updates (long polling).
	if err := bot.Run(); err != nil {
		log.Fatal(err)
	}
}
How It Works
  1. BotOpts: Holds configuration like the API token.
  2. NewBot[T]: Creates a bot instance. The type parameter T allows you to pass custom shared application data (for example, *sql.DB or a service container) that will be available in all handlers. Use laniakea.NoData if you don't need it.
  3. NewPlugin: Creates a logical group for commands and middlewares.
  4. Command: Creates and registers a command. The first argument is the command name without the slash, the second is the handler function (func(*MessageContext, T) error).
  5. Handler Functions: Receive *MessageContext (message details, methods like Answer) and your custom application data T, and return an error for centralized error handling.
  6. SetErrorTemplate: Sets a template for error messages. The %s placeholder is replaced by the actual error.
  7. AutoGenerateCommands: Registers plugin-defined commands with Telegram across the supported scopes.
  8. Run(): Starts the bot's update polling loop and returns an error if startup or polling fails.
  9. RunWebhookWithContext(...): Starts the bot-owned webhook runtime when Telegram should deliver updates over HTTP instead of long polling.
  10. A Bot instance is single-use. After Run(), RunWithContext(), or RunWebhookWithContext() returns, create a new bot instance for the next session.

For tests or custom transports, use NewBotWithAPI[T](opts, api) with a preconfigured *tgapi.API. The bot takes ownership of that client and closes it from Bot.Close; API transport, retry, and rate-limit fields in BotOpts do not override the supplied client.

File-Based Config

BotOpts can also be loaded from or saved to config files through the file codec API.

Built in:

  • BotOptsFileJSONCodec for JSON files.

Example:

codec := laniakea.BotOptsFileJSONCodec{}
opts, err := laniakea.LoadBotOptsFile(codec, "config.json")
if err != nil {
	log.Fatal(err)
}

bot, err := laniakea.NewBot[laniakea.NoData](opts)
if err != nil {
	log.Fatal(err)
}

Placeholders like {{ TG_TOKEN }} inside the file are expanded from environment variables before decoding.

You can also implement your own codec for other formats by satisfying BotOptsFileCodec. Only JSON is supported out of the box right now. If you want another format such as TOML, use BotOptsFileJSONCodec as the reference implementation for your own codec.

See the full guide in the wiki: Bot Options and Configuration

Webhook Runtime

Laniakea also supports a bot-owned webhook runtime through RunWebhookWithContext(...) and RunWebhook(...).

Use it when:

  • Telegram should push updates to your HTTP endpoint instead of your bot polling for them.
  • You want webhook-delivered updates to reuse the same internal queue, worker pool, runners, and single-use lifecycle as polling.
  • You want Laniakea to register the webhook and own the local HTTP server.

Production notes:

  • Set BotWebhookOpts.SecretToken for request authentication.
  • BotWebhookOpts.SecretToken is required when BotWebhookOpts.UseStatusPath is enabled.
  • Keep BotWebhookOpts.Path specific instead of serving webhook traffic on /.
  • If you switch an existing deployment from webhook mode to long polling, delete the webhook first with CloseWebhook() or tgapi.DeleteWebhook(...). Telegram keeps webhook delivery active until it is removed.
  • Use RunWebhookWithContext(...) with a cancelable context, then call Close() after runtime shutdown.

See the full guide in the wiki: Webhook Runtime

📖 Core Concepts

Plugins

Plugins are the main way to organize code. A plugin can have multiple commands and middlewares.

plugin := laniakea.NewPlugin[*MyDB]("admin")
plugin.Command("ban", banUser)
bot.AddPlugins(plugin)
Commands

A command is a function that handles a specific bot command (e.g., /start).

func myHandler(ctx *laniakea.MessageContext, db *MyDB) error {
    // Access command arguments via ctx.Args ([]string)
    // Reply to the user: ctx.Answer("some text")
    return nil
}
MessageContext

Provides access to the incoming message and useful reply methods:

  • Answer(text string) *AnswerMessage: Sends a message with parse_mode none.
  • AnswerLong(text string) []*AnswerMessage: Splits long plain text into multiple messages.
  • AnswerMarkdown(text string) *AnswerMessage: Sends a message formatted with MarkdownV2 (you handle escaping).
  • Keyboard(text string, keyboard *InlineKeyboard) *AnswerMessage: Sends a message with parse_mode none and inline keyboard.
  • KeyboardLong(text string, keyboard *InlineKeyboard) []*AnswerMessage: Splits long plain text into multiple messages and attaches the keyboard to the final chunk.
  • KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage: Sends a message formatted with MarkdownV2 (you handle escaping) and inline keyboard.
  • AnswerPhoto(photoID, text string) *AnswerMessage: Sends a message with photo with parse_mode none.
  • AnswerPhotoMarkdown(photoID, text string) *AnswerMessage: Sends a photo with MarkdownV2 caption (you handle escaping).
  • EditCallback(text string, keyboard *InlineKeyboard) *AnswerMessage: Edits message with parse_mode none after clicking inline button.
  • EditCallbackMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage: Edits a message formatted with MarkdownV2 (you handle escaping) after clicking inline button.
  • SendAction(action tgapi.ChatActionType): Sends a “typing”, “uploading photo”, etc., action.
  • Fields: Text, Args, From, FromID, Msg, InlineMsgID, CallbackQueryID, etc.
  • And more methods and fields!
tgapi: API and Uploader

tgapi provides two clients:

  • API for JSON requests (e.g., SendMessage, EditMessageText, methods using file_id/URL).
  • Uploader for multipart uploads (e.g., SendPhoto, SendDocument, SendVideo with binary files).

This split keeps method intent explicit: JSON-only calls go through API, file uploads go through Uploader.

For advanced cases, tgapi.NewRequest(...) and tgapi.NewUploaderRequest(...) remain public as low-level escape hatches. They are intentionally less safe than method-specific helpers: callers must supply the correct Telegram method name and compatible request/response types themselves.

Automatic retries after Telegram 429 responses are bounded to three by default; configure the cap with NewAPIOpts(...).SetMaxRetries(...). Multipart uploads stream the encoded request instead of duplicating the complete body in memory. For downloads with an unknown size, use OpenFileByLinkWithContext or set an explicit bound with GetFileByLinkLimitWithContext.

App Data

The T in NewBot[T] is a powerful feature. You can pass any type, but shared dependencies such as database pools, service containers, or API clients should usually use a pointer type.

type MyDB struct { /* ... */ }
db := &MyDB{...}
bot, err := laniakea.NewBot[*MyDB](opts)
if err != nil {
    log.Fatal(err)
}
bot.SetAppData(db)
Scenes and Sessions

Scenes model multi-step conversations inside a plugin. Each active scene is stored in a session keyed by scope, so you can isolate flows per user, per chat, or per user-chat pair.

plugin := laniakea.NewPlugin[MyDB]("signup")

plugin.Scene("signup").
    SetScope(laniakea.SceneScopeUserChat).
    SetEntry("ask_name").
    OnStep("ask_name", func(ctx *laniakea.SceneContext, db MyDB) (laniakea.SceneResult, error) {
        if ctx.Text == "" {
            ctx.Answer("What is your name?")
            return ctx.Stay(), nil
        }

        if err := ctx.SaveData(struct {
            Name string `json:"name"`
        }{Name: ctx.Text}); err != nil {
            return laniakea.SceneResult{}, err
        }

        ctx.Answer("Nice to meet you.")
        return ctx.Next("done"), nil
    }).
    OnStep("done", func(ctx *laniakea.SceneContext, db MyDB) (laniakea.SceneResult, error) {
        return ctx.Exit(), nil
    })
  • Use ctx.EnterScene("signup") to enter the configured entry step.
  • Use ctx.EnterSceneStep("signup", "done") when you need an explicit starting step.
  • Return ctx.Stay(), ctx.Next(step), ctx.Exit(), or ctx.Pass() from scene handlers to control flow.
  • SceneActionPass keeps the current session unchanged and continues normal bot routing.
  • Use SceneContext.SaveData(...) and SceneContext.BindData(...) for JSON session state.
  • Use SceneScopeUser, SceneScopeChat, or SceneScopeUserChat depending on how widely a conversation should be shared.

⏱️ Runners

Runners are background tasks that execute alongside the bot runtime. They are registered before the bot starts and launched automatically when the bot starts.

import (
    "context"
    "time"
)

// One-shot runner — fires once in a goroutine when the bot starts (default).
bot.AddRunner(
	laniakea.NewRunner("seed-cache", func(ctx context.Context, b *laniakea.Bot[*MyDB]) error {
		return b.GetAppData().SeedCache(ctx)
	}),
)

// Periodic runner — fires every 10 minutes in a goroutine.
bot.AddRunner(
	laniakea.NewRunner("refresh-stats", func(ctx context.Context, b *laniakea.Bot[*MyDB]) error {
		return b.GetAppData().RefreshStats(ctx)
	}).Every(10 * time.Minute),
)

// Synchronous one-shot — blocks runtime startup until it completes.
bot.AddRunner(
	laniakea.NewRunner("migrate", func(ctx context.Context, b *laniakea.Bot[*MyDB]) error {
		return b.GetAppData().Migrate(ctx)
	}).Async(false),
)

Builder methods:

  • Async(bool) Runner[T] — if true (default), runs in a goroutine; if false, blocks runtime startup.
  • Every(time.Duration) Runner[T] — sets the repeat interval. Zero (default) means run once; positive value repeats. Periodic runners require Async(true).

Every runner receives a context that is canceled when polling or webhook execution stops. I/O and blocking work should pass it to downstream calls.

🧩 Middleware

Middleware are functions that run before a command handler. They are perfect for cross-cutting concerns like logging, access control, rate limiting, or modifying the context.

Signature

A middleware function has the same signature as a command handler, but it must return a bool:

func(ctx *MessageContext, db T) bool
  • If it returns true, the next middleware (or the command) will be executed.
  • If it returns false, the execution chain stops immediately (the command will not run).
Adding Middleware

Use AddMiddleware on a plugin to add one or more shared middleware functions. They are executed in the order they are added.

plugin := laniakea.NewPlugin[*MyDB]("admin")
plugin.AddMiddleware(laniakea.NewMiddleware("logging", loggingMiddleware))
plugin.AddMiddleware(laniakea.NewMiddleware("admin-only", adminOnlyMiddleware))
plugin.Command("ban", banUser)
Example Middlewares
  1. Logging Middleware – logs every command execution.
func loggingMiddleware(ctx *laniakea.MessageContext, db *MyDB) bool {
    log.Printf("User %d executed command: %s", ctx.FromID, ctx.Msg.Text)
    return true // continue to next middleware/command
}
  1. Admin-Only Middleware – restricts access to users with a specific role.
func adminOnlyMiddleware(ctx *laniakea.MessageContext, db *MyDB) bool {
    if !db.IsAdmin(ctx.FromID) { // assume db has IsAdmin method
        ctx.Answer("⛔ Access denied. Admins only.")
        return false // stop execution
    }
    return true
}
Important Notes
  • Middleware can modify the MessageContext (e.g., add custom fields) before the command runs.

⚙️ Advanced Configuration

  • Inline Keyboards: Build keyboards using laniakea.NewInlineKeyboardJSON, laniakea.NewInlineKeyboardBase64, or laniakea.NewInlineKeyboard. Bot.SetPayloadType(...) defines the default payload format, and InlineKeyboard.SetPayloadType(...) overrides it for one keyboard.
  • Keyboard Validation: InlineKeyboard.Get() validates every button and row before returning markup; Telegram limits callback_data to 1–64 bytes. Use SetUnlimitedRows() when automatic wrapping must be disabled explicitly.
  • Rate Limiting: Pass a configured utils.RateLimiter via BotOpts to handle Telegram's rate limits gracefully.
  • Observers: Runtime observer callbacks are dispatched asynchronously in order through a bounded queue. Slow observers do not block handlers; overload drops events with sampled warnings, and shutdown drains queued events.
  • Localization: L10n is safe for concurrent use once attached to the bot.
  • Custom Update Handlers: Use plugin.AddUpdateHandler(...) for Telegram update types that are not part of the command/payload flow.
  • Lifecycle: RunWithContext(...) and RunWebhookWithContext(...) do not call Close() for you. Shut the bot down explicitly, and create a fresh Bot for the next run.

Telegram Update Handling

  • Commands and payloads are handled through plugins.
  • Non-command updates can be routed with plugin.AddUpdateHandler(updateType, handler).
  • message, channel_post, and callback_query stay on the command/payload flow.
  • tgapi.Update exposes a derived Type field after JSON unmarshalling so handlers can inspect the effective update kind directly.

📝 License

This project is licensed under the GNU General Public License v3.0 — see the LICENSE file for details.

📚 Learn More

GoDoc

Wiki

Telegram Bot API

✅ Built with ❤️ by scuroneko

Documentation

Overview

Package laniakea provides a modular, extensible framework for building scalable Telegram bots.

Core concepts:

  • Bot manages Telegram API access, update processing, logging, rate limiting, and dependency injection.
  • Plugins group commands, payloads, and non-command update handlers behind shared middleware.
  • MessageContext provides access to the current update and reply/edit/delete helpers.
  • InlineKeyboard builds callback-driven keyboards and structured payloads.
  • DraftProvider accumulates multi-step replies before sending them.
  • L10n stores key-based translations with fallback behavior.
  • Runners execute startup or background tasks alongside the polling loop.

Example usage:

bot, err := laniakea.NewBot[*mydb.AppData](laniakea.LoadOptsFromEnv())
if err != nil {
    return err
}
bot.SetAppData(myDB).
    AddUpdateType(tgapi.UpdateTypeMessage).
    AddPrefixes("/", "!").
    AddPlugins(&startPlugin, &helpPlugin).
    AddMiddleware(authMiddleware, logMiddleware).
    AddRunner(cleanupRunner).
    SetL10n(l10n.New())

return bot.Run()

Configure bots, plugins, and localization before starting Run, RunWithContext, or RunWebhookWithContext. Runtime accessors are safe for concurrent use unless stated otherwise.

Index

Constants

View Source
const (
	// ButtonStyleDanger marks a destructive inline keyboard action.
	ButtonStyleDanger tgapi.KeyboardButtonStyle = "danger"
	// ButtonStyleSuccess marks a confirmatory inline keyboard action.
	ButtonStyleSuccess tgapi.KeyboardButtonStyle = "success"
	// ButtonStylePrimary marks a primary inline keyboard action.
	ButtonStylePrimary tgapi.KeyboardButtonStyle = "primary"
)
View Source
const (
	// VersionString re-exports the module version string.
	VersionString = utils.VersionString
	// VersionMajor re-exports the module major version.
	VersionMajor = utils.VersionMajor
	// VersionMinor re-exports the module minor version.
	VersionMinor = utils.VersionMinor
	// VersionPatch re-exports the module patch version.
	VersionPatch = utils.VersionPatch
	// VersionBeta re-exports the module prerelease counter.
	VersionBeta = utils.VersionBeta
)
View Source
const ConfigVersion = 1

ConfigVersion is the current version of the built-in JSON BotOpts file format.

Variables

View Source
var (
	// ErrNoPrefixes reports that the bot was started without any command prefixes.
	ErrNoPrefixes = errors.New("no prefixes defined")
	// ErrNoPlugins reports that the bot was started without any registered plugins.
	ErrNoPlugins = errors.New("no plugins defined")
	// ErrBotAlreadyRun reports that Run, RunWithContext, or RunWebhookWithContext was called more than once.
	ErrBotAlreadyRun = errors.New("bot can only be run once")

	// ErrTokenRequired reports that BotOpts.Token was empty.
	ErrTokenRequired = errors.New("token required")
	// ErrOptsIsNil reports that NewBot was called with a nil BotOpts pointer.
	ErrOptsIsNil = errors.New("opts is nil")
)
View Source
var (
	// ErrInvalidBotCommand reports a command name that Telegram would reject.
	ErrInvalidBotCommand = errors.New("invalid bot command")
	// ErrInvalidBotCommandDescription reports an empty or overlong generated description.
	ErrInvalidBotCommandDescription = errors.New("invalid bot command description")
	// ErrDuplicateBotCommand reports the same command generated by multiple plugins.
	ErrDuplicateBotCommand = errors.New("duplicate bot command")
	// ErrPartialCommandScopeUpdate reports that an earlier command scope was
	// updated before a later scope failed.
	ErrPartialCommandScopeUpdate = errors.New("partial command scope update")
)
View Source
var (
	// CommandRegexInt matches one or more digits.
	CommandRegexInt = regexp.MustCompile(`^\d+$`)
	// CommandRegexString matches any non-empty string.
	CommandRegexString = regexp.MustCompile(`^.+$`)
	// CommandRegexBool matches true or false.
	CommandRegexBool = regexp.MustCompile(`^(true|false)$`)
)
View Source
var (
	// ErrEmptyMessage reports that a required message text is empty.
	ErrEmptyMessage = errors.New("empty message")
	// ErrMessageTooLong reports that a message exceeds Telegram's text limit.
	ErrMessageTooLong = errors.New("message too long")
	// ErrCaptionTooLong reports that a caption exceeds Telegram's caption limit.
	ErrCaptionTooLong = errors.New("caption too long")
	// ErrMessageSplitImpossible reports that automatic message splitting cannot preserve semantics.
	ErrMessageSplitImpossible = errors.New("message split is impossible")
	// ErrPayloadTypeMismatch reports that callback payload encoding does not match bot policy.
	ErrPayloadTypeMismatch = errors.New("payload type mismatch")
	// ErrDraftChatIDZero reports that a draft has no target chat ID.
	ErrDraftChatIDZero = errors.New("zero draft chat ID")
	// ErrMessageNil reports that a required message value is nil.
	ErrMessageNil = errors.New("message is nil")
	// ErrMessageContextNil reports that an operation requires ctx.Msg but none is set.
	ErrMessageContextNil = errors.New("message context is nil")
	// ErrEditTargetMissing reports that an edit operation has no message target.
	ErrEditTargetMissing = errors.New("edit target is missing")
	// ErrCallbackMessageMissing reports that a callback operation has no callback message target.
	ErrCallbackMessageMissing = errors.New("callback message is missing")
	// ErrDraftProviderNil reports that draft creation was requested without a draft provider.
	ErrDraftProviderNil = errors.New("draft provider is nil")
	// ErrAPIIsNil reports that an operation requires an API client but none is set.
	ErrAPIIsNil = errors.New("api is nil")
	// ErrMessageIDZero reports that an operation requires a non-zero message ID.
	ErrMessageIDZero = errors.New("message ID is zero")
	// ErrCodecIsNil reports that a config operation received a nil codec.
	ErrCodecIsNil = errors.New("codec is nil")
)
View Source
var (
	// ErrBindArgsTargetNotPointer reports that BindArgs received a nil or non-pointer destination.
	ErrBindArgsTargetNotPointer = errors.New("bind args: dst must be a non-nil pointer")
	// ErrBindArgsTargetNotStruct reports that BindArgs received a pointer to a non-struct value.
	ErrBindArgsTargetNotStruct = errors.New("bind args: dst must point to a struct")
	// ErrBindArgsUnsupportedFieldType reports that BindArgs encountered an unsupported field kind.
	ErrBindArgsUnsupportedFieldType = errors.New("bind args: unsupported field type")
	// ErrBindArgsConversion reports that BindArgs could not convert a string argument into a field type.
	ErrBindArgsConversion = errors.New("bind args: conversion failed")
	// ErrCantFindSession reports that no scene session matches the current context.
	ErrCantFindSession = errors.New("can't find session for this context")
	// ErrSceneNotFound reports that the requested scene is not registered.
	ErrSceneNotFound = errors.New("scene not found")
	// ErrSceneStepNotFound reports that the requested scene step is not registered.
	ErrSceneStepNotFound = errors.New("scene step not found")
	// ErrNotInScene reports that the current context has no active scene session.
	ErrNotInScene = errors.New("not in scene")
	// ErrSceneEntryNotSet reports that a scene has no configured entry step.
	ErrSceneEntryNotSet = errors.New("scene entry step not set")
	// ErrSceneRuntimeNil reports that scene APIs were used without an attached runtime.
	ErrSceneRuntimeNil = errors.New("scene runtime is nil")
	// ErrInvalidSceneAction reports a SceneResult with an unknown action.
	ErrInvalidSceneAction = errors.New("invalid scene action")
	// ErrHandlerExecutorNil reports an attempted registration or execution of a nil handler.
	ErrHandlerExecutorNil = errors.New("handler executor is nil")
	// ErrHandlerPanic reports a panic recovered from a user handler.
	ErrHandlerPanic = errors.New("handler panicked")
	// ErrObserverShutdownTimeout reports that observer callbacks did not stop before shutdown timed out.
	ErrObserverShutdownTimeout = errors.New("observer shutdown timed out")
	// ErrInlineKeyboardButtonAction reports a button without exactly one action.
	ErrInlineKeyboardButtonAction = errors.New("inline keyboard button must have exactly one action")
	// ErrCallbackDataLength reports callback data outside Telegram's 1-64 byte range.
	ErrCallbackDataLength = errors.New("callback data must be between 1 and 64 bytes")
	// ErrInlineKeyboardRowTooLong reports a row exceeding the configured maximum.
	ErrInlineKeyboardRowTooLong = errors.New("inline keyboard row exceeds maximum size")
	// ErrInlineKeyboardMaxRow reports a non-positive row limit without explicit unlimited mode.
	ErrInlineKeyboardMaxRow = errors.New("inline keyboard maximum row size must be positive")
)
View Source
var (
	// ErrNilBotWebhookOpts reports that a nil BotWebhookOpts was passed.
	ErrNilBotWebhookOpts = errors.New("nil BotWebhookOpts")
	// ErrNoBotWebhookOptsURL reports that BotWebhookOpts.URL is empty.
	ErrNoBotWebhookOptsURL = errors.New("empty BotWebhookOpts.URL")
	// ErrBotWebhookOptsMaxConnectionsRange reports that BotWebhookOpts.MaxConnections is out of range.
	ErrBotWebhookOptsMaxConnectionsRange = errors.New("BotWebhookOpts.MaxConnections must be between 1 and 100")
	// ErrBotWebhookOptsSecretTokenInvalid reports that SecretToken violates Telegram's format.
	ErrBotWebhookOptsSecretTokenInvalid = errors.New("BotWebhookOpts.SecretToken must be 1-256 characters from A-Z, a-z, 0-9, _ and -")
	// ErrBotUploaderWhenCertificate reports that a certificate was set without an uploader.
	ErrBotUploaderWhenCertificate = errors.New("bot uploader nil, but certificate set")
	// ErrStatusPathSecretRequired reports that UseStatusPath requires SecretToken to be set.
	ErrStatusPathSecretRequired = errors.New("SecretToken required when UseStatusPath is enabled")
	// ErrSetWebhookFailed reports that Telegram rejected the setWebhook request.
	ErrSetWebhookFailed = errors.New("failed to set webhook")
	// ErrBotAPINil reports that an operation requires an API client but none is set.
	ErrBotAPINil = errors.New("bot api is nil")
	// ErrBotWebhookOptsEmptyPath reports that BotWebhookOpts.Path is empty.
	ErrBotWebhookOptsEmptyPath = errors.New("empty BotWebhookOpts.Path")
	// ErrBotWebhookOptsPathNoSlash reports that BotWebhookOpts.Path does not start with '/'.
	ErrBotWebhookOptsPathNoSlash = errors.New("BotWebhookOpts.Path must start with '/'")
	// ErrBotWebhookOptsPathHasQueryOrFragment reports that BotWebhookOpts.Path contains a query or fragment.
	ErrBotWebhookOptsPathHasQueryOrFragment = errors.New("BotWebhookOpts.Path must not contain query or fragment")
	// ErrBotWebhookOptsPathCollidesStatus reports that BotWebhookOpts.Path collides with the reserved /status endpoint.
	ErrBotWebhookOptsPathCollidesStatus = errors.New("BotWebhookOpts.Path must not be '/status' when status path is enabled")
	// ErrBotWebhookTLSFilesIncomplete reports that only one of the two TLS files was provided.
	ErrBotWebhookTLSFilesIncomplete = errors.New("you must specify both private and public keys")
	// ErrBotWebhookTLSFilesTooMany reports that more than two TLS files were provided.
	ErrBotWebhookTLSFilesTooMany = errors.New("too many files; you must specify only private and public keys")
)
View Source
var (
	// ErrProxySchemeEmpty reports that a configured proxy has no URL scheme.
	ErrProxySchemeEmpty = errors.New("proxy scheme is empty")
	// ErrProxySchemeUnsupported reports that a configured proxy uses an unsupported URL scheme.
	ErrProxySchemeUnsupported = errors.New("proxy scheme is unsupported")
	// ErrProxyHostEmpty reports that a configured proxy has no host.
	ErrProxyHostEmpty = errors.New("proxy host is empty")
	// ErrProxyPortOutOfRange reports that a proxy port is outside 1..65535.
	ErrProxyPortOutOfRange = errors.New("proxy port must be in range 1-65535")
)
View Source
var ErrCmdArgCountMismatch = errors.New("command arg count mismatch")

ErrCmdArgCountMismatch is returned when the number of provided arguments is less than the number of required arguments.

View Source
var ErrCmdArgRegexpMismatch = errors.New("command arg regexp mismatch")

ErrCmdArgRegexpMismatch is returned when an argument fails regex validation.

View Source
var ErrConfigVersionMismatch = fmt.Errorf("config version mismatch: expected %d", ConfigVersion)

ErrConfigVersionMismatch reports that a config file declares a newer version than this library knows how to decode.

View Source
var ErrInvalidPayload = errors.New("invalid payload")

ErrInvalidPayload reports that a callback payload could not be decoded under the expected encoding (e.g. the compact format separator is missing).

View Source
var ErrInvalidPayloadType = errors.New("invalid payload type")

ErrInvalidPayloadType is returned when callback payload encoding type is unknown.

View Source
var ErrMiddlewareExecutorNil = errors.New("middleware executor is nil")

ErrMiddlewareExecutorNil reports an attempt to execute middleware without a callback.

View Source
var ErrTooManyCommands = errors.New("too many commands. max 100")

ErrTooManyCommands is returned when the total number of registered commands exceeds Telegram's limit of 100 bot commands per bot.

Telegram Bot API enforces this limit strictly. If exceeded, SetMyCommands will fail with a 400 error. This error helps catch the issue early during bot initialization.

Functions

func AsInternalError

func AsInternalError(err error) error

AsInternalError marks err as internal-only so it will be logged but not sent to the user through the centralized handler error flow.

func AsUserError

func AsUserError(err error) error

AsUserError marks err as safe to show to the user through the centralized handler error flow.

func IsInternalError

func IsInternalError(err error) bool

IsInternalError reports whether err was explicitly marked as internal-only.

func IsUserError

func IsUserError(err error) bool

IsUserError reports whether err was explicitly marked as user-visible.

func LoadPrefixesFromEnv

func LoadPrefixesFromEnv() []string

LoadPrefixesFromEnv returns the PREFIXES environment variable split by semicolon. Defaults to ["/"] if not set.

func Ptr

func Ptr[T any](v T) *T

Ptr returns a pointer to v.

func SaveBotOptsFile

func SaveBotOptsFile(codec BotOptsFileCodec, filename string, opts *BotOpts) error

SaveBotOptsFile encodes BotOpts with codec and writes the result to filename.

func SplitMessageText

func SplitMessageText(text string) []string

SplitMessageText splits plain text into Telegram-safe message chunks.

The function preserves the original text exactly: concatenating all returned chunks reconstructs text byte-for-byte. It prefers splitting at newlines or spaces within the Telegram message limit and falls back to hard rune-based splits when no separator is available.

func Val

func Val[T any](p *T, def T) T

Val returns dereferenced pointer value or def when p is nil.

Types

type AnswerMessage

type AnswerMessage struct {
	// MessageID identifies the sent Telegram message.
	MessageID int
	// Text contains the text or caption sent with the message.
	// For rich messages, it contains rendered HTML for v1 compatibility.
	Text string
	// RichHTML contains the rendered HTML of a rich message.
	RichHTML string // Since: Bot API 10.2
	// IsMedia reports whether the answer contains media.
	IsMedia bool
	// contains filtered or unexported fields
}

AnswerMessage represents a message sent or edited via MessageContext. It holds metadata to allow further editing or deletion.

func (*AnswerMessage) Delete

func (m *AnswerMessage) Delete()

Delete removes the message associated with this AnswerMessage.

func (*AnswerMessage) Edit

func (m *AnswerMessage) Edit(text string) *AnswerMessage

Edit replaces the text of the message without changing the keyboard or parse mode. Uses ParseNone (plain text).

func (*AnswerMessage) EditCaption

func (m *AnswerMessage) EditCaption(text string) *AnswerMessage

EditCaption edits the caption of a media message using plain text.

func (*AnswerMessage) EditCaptionKeyboard

func (m *AnswerMessage) EditCaptionKeyboard(text string, kb *InlineKeyboard) *AnswerMessage

EditCaptionKeyboard edits the caption of a media message with a new inline keyboard (plain text).

func (*AnswerMessage) EditCaptionKeyboardMarkdown

func (m *AnswerMessage) EditCaptionKeyboardMarkdown(text string, kb *InlineKeyboard) *AnswerMessage

EditCaptionKeyboardMarkdown edits the caption of a media message with a new inline keyboard using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*AnswerMessage) EditCaptionMarkdown

func (m *AnswerMessage) EditCaptionMarkdown(text string) *AnswerMessage

EditCaptionMarkdown edits the caption of a media message using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*AnswerMessage) EditMarkdown

func (m *AnswerMessage) EditMarkdown(text string) *AnswerMessage

EditMarkdown replaces the text of the message using MarkdownV2 formatting.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here. Unescaped input may cause Telegram API errors or broken formatting.

func (*AnswerMessage) EditRich

func (m *AnswerMessage) EditRich(blocks ...tgapi.InputRichBlock) *AnswerMessage

EditRich builds rich blocks and replaces the message content without changing its inline keyboard. It doesn't upload local files referenced with attach://.

Since: Bot API 10.2

func (*AnswerMessage) EditRichKeyboard

func (m *AnswerMessage) EditRichKeyboard(keyboard *InlineKeyboard, blocks ...tgapi.InputRichBlock) *AnswerMessage

EditRichKeyboard builds rich blocks and replaces the message content and inline keyboard. It doesn't upload local files referenced with attach://.

Since: Bot API 10.2

type AppData

type AppData any

AppData is the generic shared application data type injected into bots, plugins, and handlers.

Use it for long-lived shared dependencies such as database handles, service containers, API clients, or immutable configuration snapshots.

Example:

type MyDB struct { ... }
myDB := &MyDB{}
bot, err := NewBot[*MyDB](opts)
if err != nil {
	return err
}
bot.SetAppData(myDB)

Use NoData if no shared application data is needed.

type AppDataLogger

type AppDataLogger[T AppData] func(data T) sneklog.LoggerWriter

AppDataLogger builds a sneklog.LoggerWriter from injected application data.

Use it when shared application data exposes a log sink or adapter that should receive framework logs.

type Bot

type Bot[T AppData] struct {
	// contains filtered or unexported fields
}

Bot is the core Telegram bot instance.

Manages:

  • API communication via tgapi
  • Update processing pipeline (middleware → plugins)
  • Background runners
  • Logging and rate limiting
  • Localization and draft message support

Runtime accessors are safe for concurrent use. Configure the bot before Run, RunWithContext, or RunWebhookWithContext. A Bot is single-use: after Run, RunWithContext, or RunWebhookWithContext returns, create a new Bot for the next session.

func NewBot

func NewBot[T any](opts *BotOpts) (*Bot[T], error)

NewBot creates and initializes a new Bot instance using the provided BotOpts.

Automatically:

  • Creates API and Uploader clients
  • Initializes structured logging (JSON stdout + optional file)
  • Fetches bot username via GetMe()
  • Sets up DraftProvider with random IDs
  • Adds API and Uploader loggers to extraLoggers

func NewBotWithAPI

func NewBotWithAPI[T any](opts *BotOpts, api *tgapi.API) (*Bot[T], error)

NewBotWithAPI creates a Bot using a preconfigured API client. The Bot takes ownership of api and closes it from Bot.Close. API transport, retry, and rate-limit fields in opts do not reconfigure the supplied client.

func (*Bot[T]) AddAppDataLoggerWriter

func (bot *Bot[T]) AddAppDataLoggerWriter(writer AppDataLogger[T]) *Bot[T]

AddAppDataLoggerWriter adds an app-data-backed logger writer to all loggers.

The writer will receive logs from:

  • Main bot logger
  • Request logger (if enabled)
  • API and Uploader loggers
  • Already registered plugin loggers

Call this after AddPlugins if plugin loggers should also receive the writer. Plugins registered later do not automatically inherit previously added writers; call AddAppDataLoggerWriter again after adding them.

Example:

bot.AddAppDataLoggerWriter(func(data *MyAppData) sneklog.LoggerWriter {
    return data.QueryLogger()
})

func (*Bot[T]) AddMiddleware

func (bot *Bot[T]) AddMiddleware(middleware ...Middleware[T]) *Bot[T]

AddMiddleware registers one or more middleware handlers.

Middleware are executed in order of increasing .order value before plugins. If two middleware have the same order, they are sorted lexicographically by name.

Middleware can:

  • Modify or reject updates before they reach plugins
  • Inject context (e.g., user auth state, rate limit status)
  • Log, validate, or transform incoming data

Example:

bot.AddMiddleware(authMiddleware, rateLimitMiddleware)

Middleware with an empty name are skipped with a warning.

func (*Bot[T]) AddPlugins

func (bot *Bot[T]) AddPlugins(plugin ...*Plugin[T]) *Bot[T]

AddPlugins registers one or more plugins. Plugins are executed in registration order unless filtered by middleware.

Registration is a commit point for plugin configuration. The Bot stores plugin metadata internally, so plugins must be fully configured before they are passed here. Post-registration mutation through the original *Plugin is not a supported API, even if some changes appear to work due to shared maps.

func (*Bot[T]) AddPrefixes

func (bot *Bot[T]) AddPrefixes(prefixes ...string) *Bot[T]

AddPrefixes adds one or more command prefixes (e.g., "/", "!"). The bot must have at least one prefix before any runtime entry point starts.

func (*Bot[T]) AddRunner

func (bot *Bot[T]) AddRunner(runner Runner[T]) *Bot[T]

AddRunner registers a background runner to execute concurrently with the bot.

Runners are goroutines that run independently of update processing. Common use cases:

  • Periodic cleanup (e.g., expiring drafts, clearing temp files)
  • Metrics collection or health checks
  • Scheduled tasks (e.g., daily announcements)

Runners start from the bot runtime entry points, immediately after RunWithContext or RunWebhookWithContext begins.

Example:

bot.AddRunner(cleanupRunner)

Runners with an empty name are skipped with a warning.

func (*Bot[T]) AddUpdateType

func (bot *Bot[T]) AddUpdateType(t ...tgapi.UpdateType) *Bot[T]

AddUpdateType adds one or more update types to the list. Does not overwrite existing types.

func (*Bot[T]) AutoGenerateCommands

func (bot *Bot[T]) AutoGenerateCommands() error

AutoGenerateCommands replaces plugin-defined commands in the private-chat, group-chat, and all-chat-administrators scopes.

Returns ErrTooManyCommands if the total number of commands exceeds 100. Returns any API error from Telegram (e.g., network issues, invalid scope).

Important: This method assumes the bot has been properly initialized and the API client is authenticated and ready.

Usage:

err := bot.AutoGenerateCommands()
if err != nil {
    log.Fatal(err)
}

func (*Bot[T]) AutoGenerateCommandsForScope

func (bot *Bot[T]) AutoGenerateCommandsForScope(scope *tgapi.BotCommandScope) error

AutoGenerateCommandsForScope registers all plugin-defined commands with Telegram's Bot API for the specified command scope. A nil scope selects Telegram's default scope.

The scope parameter defines where the commands should be available (e.g., private chats, group chats, chat administrators). See tgapi.BotCommandScope and its predefined types.

Returns ErrTooManyCommands if the total number of commands exceeds 100. Returns any API error from Telegram (e.g., network issues, invalid scope).

Usage:

privateScope := &tgapi.BotCommandScope{Type: tgapi.BotCommandScopePrivateType}
if err := bot.AutoGenerateCommandsForScope(privateScope); err != nil {
    log.Fatal(err)
}

func (*Bot[T]) AutoGenerateCommandsForScopeWithContext

func (bot *Bot[T]) AutoGenerateCommandsForScopeWithContext(ctx context.Context, scope *tgapi.BotCommandScope) error

AutoGenerateCommandsForScopeWithContext is the context-aware variant of AutoGenerateCommandsForScope.

func (*Bot[T]) AutoGenerateCommandsWithContext

func (bot *Bot[T]) AutoGenerateCommandsWithContext(ctx context.Context) error

AutoGenerateCommandsWithContext is the context-aware variant of AutoGenerateCommands.

func (*Bot[T]) Close

func (bot *Bot[T]) Close() error

Close gracefully shuts down bot-owned resources.

Close shuts down, in order:

  • The asynchronous observer dispatcher, after draining queued events
  • Registered plugins via Plugin.Close
  • Webhook logger (if initialized)
  • Uploader logger resources
  • API client internals, after pending API and upload requests complete
  • RequestLogger (if enabled)
  • Main logger

RunWithContext and RunWebhookWithContext do not call Close automatically. The caller is responsible for invoking Close after runtime returns to release these resources.

Close returns a joined error containing all shutdown failures, if any.

func (*Bot[T]) CloseRemote

func (bot *Bot[T]) CloseRemote(ctx context.Context) error

CloseRemote sends Telegram Bot API "close" request for the current bot instance using ctx for cancellation and deadlines.

This is separate from Bot.Close(), which only releases local resources.

func (*Bot[T]) CloseWebhook

func (bot *Bot[T]) CloseWebhook() error

CloseWebhook removes the current Telegram webhook registration.

It is separate from Close, which only releases local resources. Call it before switching a deployment from webhook delivery to polling.

func (*Bot[T]) ExecRunners

func (bot *Bot[T]) ExecRunners(ctx context.Context)

ExecRunners executes all runners registered on the Bot with context-based lifecycle management.

Execution semantics by configuration:

  • every=0, async=true: Runs once in a goroutine; runtime shutdown waits for it.
  • every=0, async=false: Runs once synchronously; warns if slower than 2 seconds.
  • every>0, async=true: Runs in a loop with the configured interval until ctx.Done().
  • every>0, async=false: Skipped with a warning (invalid configuration).

Background runners listen for ctx.Done() and gracefully shut down when the context is canceled.

This method is typically called once during bot startup from RunWithContext or RunWebhookWithContext.

func (*Bot[T]) GetAPI

func (bot *Bot[T]) GetAPI() *tgapi.API

GetAPI returns the underlying Telegram Bot API client.

func (*Bot[T]) GetAppData

func (bot *Bot[T]) GetAppData() T

GetAppData returns the injected application data. If SetAppData was not called, it returns the zero value of T.

func (*Bot[T]) GetDraftProvider

func (bot *Bot[T]) GetDraftProvider() *DraftProvider

GetDraftProvider returns the draft provider currently used by the bot.

func (*Bot[T]) GetLogger

func (bot *Bot[T]) GetLogger() *sneklog.Logger

GetLogger returns the main bot logger.

func (*Bot[T]) GetLoggerLevel

func (bot *Bot[T]) GetLoggerLevel() sneklog.LogLevel

GetLoggerLevel returns the effective log level derived from the bot's debug flag.

func (*Bot[T]) GetObserver

func (bot *Bot[T]) GetObserver() Observer

GetObserver returns the bot's event observer, or nil if no observer is set.

func (*Bot[T]) GetPayloadType

func (bot *Bot[T]) GetPayloadType() BotPayloadType

GetPayloadType returns the bot's default callback payload encoding type.

func (*Bot[T]) GetRequestLogger

func (bot *Bot[T]) GetRequestLogger() *sneklog.Logger

GetRequestLogger returns the request-level logger, if configured.

func (*Bot[T]) GetSessionStore

func (bot *Bot[T]) GetSessionStore() SessionStore

GetSessionStore returns the session store used for scene management.

func (*Bot[T]) GetUpdateOffset

func (bot *Bot[T]) GetUpdateOffset() int

GetUpdateOffset returns the current update offset (thread-safe).

func (*Bot[T]) GetUpdateTypes

func (bot *Bot[T]) GetUpdateTypes() []tgapi.UpdateType

GetUpdateTypes returns the list of update types the bot is configured to receive.

func (*Bot[T]) GetUploader

func (bot *Bot[T]) GetUploader() *tgapi.Uploader

GetUploader returns the underlying file uploader client.

func (*Bot[T]) GetWebhookLogger

func (bot *Bot[T]) GetWebhookLogger() *sneklog.Logger

GetWebhookLogger returns the webhook logger, if configured.

func (*Bot[T]) L10n

func (bot *Bot[T]) L10n(lang, key string) string

L10n translates a key in the given language. Returns key if translation not found.

func (*Bot[T]) Run

func (bot *Bot[T]) Run() error

Run starts the bot using a background context.

Equivalent to RunWithContext(context.Background()). Use this for simple bots where graceful shutdown is not required.

For production use, prefer RunWithContext to handle SIGINT/SIGTERM gracefully.

func (*Bot[T]) RunWebhook

func (bot *Bot[T]) RunWebhook(opts *BotWebhookOpts, tlsFiles ...string) error

RunWebhook starts the webhook runtime with a background context.

It is shorthand for RunWebhookWithContext(context.Background(), opts, tlsFiles...).

func (*Bot[T]) RunWebhookWithContext

func (bot *Bot[T]) RunWebhookWithContext(ctx context.Context, opts *BotWebhookOpts, tlsFiles ...string) error

RunWebhookWithContext registers the webhook and serves incoming updates until ctx is canceled.

The bot uses the same update queue, worker pool, runner startup, and single-use lifecycle guarantees as RunWithContext. When opts.AllowedUpdates is empty, the bot-level update types configured through SetUpdateTypes/AddUpdateType are used. When UseStatusPath is enabled, SecretToken must be non-empty so the operational endpoint is not left public.

When two TLS files are provided, the method serves HTTPS using the existing key-then-cert argument order.

func (*Bot[T]) RunWithContext

func (bot *Bot[T]) RunWithContext(ctx context.Context) error

RunWithContext starts the bot with a given context for graceful shutdown.

This is the main entry point for bot execution. It:

  • Validates required configuration (prefixes, plugins)
  • Starts all registered runners as background goroutines
  • Begins polling for updates via Telegram's GetUpdates API
  • Processes updates concurrently using a worker pool with size configurable via BotOpts.MaxWorkers

The context controls graceful shutdown. When canceled, the bot:

  • Stops polling for new updates
  • Finishes processing currently queued updates
  • Waits for registered runners to exit

If you are switching an existing deployment from webhook delivery to polling, delete the current webhook first with CloseWebhook or tgapi.DeleteWebhook. Telegram keeps webhook delivery active until the webhook is removed.

RunWithContext does not close API, uploader, or logger resources on return. The caller must invoke Close after RunWithContext finishes.

A Bot is single-use. After RunWithContext returns, later calls return ErrBotAlreadyRun.

func (*Bot[T]) SetAppData

func (bot *Bot[T]) SetAppData(ctx T) *Bot[T]

SetAppData injects shared application data into the bot.

The data is accessible to commands, payload handlers, middleware, scenes, and runners through the generic type parameter T.

For shared dependencies such as *sql.DB, prefer using a pointer type as T. Value-typed application data is supported, but the bot warns once because handlers receive T by value.

func (*Bot[T]) SetDebug

func (bot *Bot[T]) SetDebug(debug bool) *Bot[T]

SetDebug enables or disables debug logging.

func (*Bot[T]) SetDraftProvider

func (bot *Bot[T]) SetDraftProvider(p *DraftProvider) *Bot[T]

SetDraftProvider replaces the default DraftProvider with a custom one. Useful for using LinearDraftIDGenerator to persist draft IDs across restarts.

func (*Bot[T]) SetErrorTemplate

func (bot *Bot[T]) SetErrorTemplate(s string) *Bot[T]

SetErrorTemplate sets the format string for error messages sent to users. Use "%s" to insert the error message. Example: "❌ Error: %s" → "❌ Error: Command not found".

func (*Bot[T]) SetL10n

func (bot *Bot[T]) SetL10n(l *L10n) *Bot[T]

SetL10n sets the localization (i18n) provider for the bot.

The L10n instance must be pre-populated with translations. Translations are accessed via Bot.L10n(lang, key).

Replaces any previously set L10n instance.

func (*Bot[T]) SetLogger

func (bot *Bot[T]) SetLogger(l *sneklog.Logger) *Bot[T]

SetLogger replaces the main bot logger before runtime starts.

func (*Bot[T]) SetObserver

func (bot *Bot[T]) SetObserver(observer Observer) *Bot[T]

SetObserver sets an event observer for instrumentation.

func (*Bot[T]) SetPayloadType

func (bot *Bot[T]) SetPayloadType(t BotPayloadType) *Bot[T]

SetPayloadType sets the default payload encoding type used for callback data. JSON stores payload as a string: `{"cmd":"command","args":[...]}`. Base64 stores the same JSON encoded as a Base64URL string. InlineKeyboard.SetPayloadType may override this value for an individual keyboard.

func (*Bot[T]) SetRequestLogger

func (bot *Bot[T]) SetRequestLogger(l *sneklog.Logger) *Bot[T]

SetRequestLogger replaces the request-level logger before runtime starts.

func (*Bot[T]) SetSceneScopePriority

func (bot *Bot[T]) SetSceneScopePriority(priority []SceneScope) *Bot[T]

SetSceneScopePriority sets the lookup order for resolving active scene sessions.

func (*Bot[T]) SetSessionStore

func (bot *Bot[T]) SetSessionStore(store SessionStore) *Bot[T]

SetSessionStore replaces the session store used for scene management.

func (*Bot[T]) SetStrictPayloadType

func (bot *Bot[T]) SetStrictPayloadType(strict bool) *Bot[T]

SetStrictPayloadType enables or disables strict callback payload decoding. When enabled, callback payloads must match the bot's default payload type.

func (*Bot[T]) SetUpdateOffset

func (bot *Bot[T]) SetUpdateOffset(offset int)

SetUpdateOffset sets the update offset for next GetUpdates call (thread-safe).

func (*Bot[T]) SetUpdateTypes

func (bot *Bot[T]) SetUpdateTypes(t ...tgapi.UpdateType) *Bot[T]

SetUpdateTypes sets the list of update types the bot will request from Telegram. Overwrites any previously set types.

func (*Bot[T]) SetWebhookLogger

func (bot *Bot[T]) SetWebhookLogger(l *sneklog.Logger) *Bot[T]

SetWebhookLogger replaces the webhook logger before runtime starts.

func (*Bot[T]) Updates

func (bot *Bot[T]) Updates(ctx context.Context) ([]tgapi.Update, error)

Updates fetches new updates from Telegram API using long polling. It respects the bot's current update offset and automatically advances it after successful retrieval. The method supports selective update types through AllowedUpdates and includes optional request logging.

Parameters:

  • ctx: request context used to cancel the in-flight long polling request

Returns:

  • []tgapi.Update: slice of received updates (empty if none available)
  • error: any error encountered during the API call

Behavior:

  1. Uses the bot's current update offset (via GetUpdateOffset)
  2. Requests updates with the timeout configured via PollTimeout
  3. Filters updates by types specified in bot.GetUpdateTypes()
  4. Logs raw update JSON if RequestLogger is configured
  5. Automatically updates the offset to the last received update ID + 1
  6. Returns all received updates (empty slice if none)

Note: This is a blocking call that waits up to the configured PollTimeout for new updates, unless ctx is canceled earlier. For non-blocking behavior, consider using webhooks instead.

Example:

updates, err := bot.Updates(ctx)
if err != nil {
    log.Fatal(err)
}
for _, update := range updates {
    // process update
}

func (*Bot[T]) UpdatesIter

func (bot *Bot[T]) UpdatesIter(ctx context.Context) iter.Seq2[tgapi.Update, error]

UpdatesIter fetches updates once and yields each update in order.

If fetching updates fails, the iterator yields the error once with a zero update and then stops.

func (*Bot[T]) UsePolicy

func (bot *Bot[T]) UsePolicy(name string, policy Policy[T]) *Bot[T]

UsePolicy registers a Policy as a bot-level middleware.

type BotOpts

type BotOpts struct {
	// Token is the Telegram bot token (required).
	Token string

	// UpdateTypes is a list of update types to listen for.
	// Example: "["message", "edited_message", "callback_query"]"
	// Defaults to empty (Telegram will return all types).
	UpdateTypes []tgapi.UpdateType

	// Debug enables debug-level logging.
	Debug bool

	// ErrorTemplate is the format string used to wrap error messages sent to users.
	// Use "%s" to insert the actual error. Example: "❌ Error: %s"
	ErrorTemplate string

	// Prefixes is a list of command prefixes (e.g., ["/", "!"]).
	// Defaults to ["/"] if not set via environment.
	Prefixes []string

	// LoggerBasePath is the directory where log files are written.
	// Defaults to "./".
	LoggerBasePath string

	// UseRequestLogger enables detailed logging of all Telegram API requests.
	UseRequestLogger bool

	// WriteToFile enables writing logs to files (main.log and requests.log).
	WriteToFile bool

	// UseTestServer uses Telegram's test server (https://api.test.telegram.org).
	UseTestServer bool

	// APIURL overrides the default Telegram API endpoint (useful for proxies or self-hosted).
	APIURL string

	// RateLimit is the maximum number of API requests per second.
	// Telegram allows up to 30 req/s for most bots. Defaults to 30.
	RateLimit int

	// DropRateLimitOverflow rejects outgoing Telegram API requests immediately when
	// rate-limit capacity is unavailable instead of waiting for capacity.
	DropRateLimitOverflow bool

	// StrictPayloadType disables callback payload fallback decoding.
	// When enabled, the bot accepts only the configured default payload type.
	StrictPayloadType bool

	// MaxWorkers is the maximum number of update handlers that may run concurrently.
	MaxWorkers int

	// PollTimeout is the long-polling timeout in seconds for getUpdates.
	// Defaults to 30. Telegram allows 0..50; values outside that range are accepted
	// by the bot but rejected by Telegram at runtime.
	PollTimeout int

	// FileConfigVersion stores the version declared by the config file used to
	// load these options.
	//
	// It is zero when the options were not loaded from a versioned file.
	FileConfigVersion int

	// LogFormat selects text or JSON output for bot-managed loggers.
	LogFormat utils.LogFormat
	// LogFormatter customizes bot-managed logger writers when supported.
	LogFormatter *sneklog.Formatter

	// ProxyURL routes Telegram API requests through an HTTP, HTTPS, SOCKS5, or SOCKS5H proxy.
	// When nil, the HTTP client uses proxy settings from the environment.
	ProxyURL *url.URL
}

BotOpts holds configuration options for initializing a Bot.

Values are loaded from environment variables via LoadOptsFromEnv(). Use &BotOpts{} to create a value and set fields manually.

func LoadBotOptsFile

func LoadBotOptsFile(codec BotOptsFileCodec, filename string) (*BotOpts, error)

LoadBotOptsFile reads a config file, expands env placeholders, and decodes BotOpts.

func LoadOptsFromEnv

func LoadOptsFromEnv() *BotOpts

LoadOptsFromEnv loads BotOpts from environment variables.

Environment variables:

  • TG_TOKEN: Bot token (required)
  • UPDATE_TYPES: semicolon-separated update types (e.g., "message;callback_query")
  • DEBUG: "true" to enable debug logging
  • ERROR_TEMPLATE: format string for error messages (e.g., "❌ %s")
  • PREFIXES: semicolon-separated prefixes (e.g., "/;!bot")
  • LOGGER_BASE_PATH: directory for log files (default: "./")
  • USE_REQ_LOG: "true" to enable request logging
  • WRITE_TO_FILE: "true" to write logs to files
  • USE_TEST_SERVER: "true" to use Telegram test server
  • API_URL: custom API endpoint
  • RATE_LIMIT: max requests per second (default: 30)
  • DROP_RL_OVERFLOW: "true" to reject rate-limited API requests instead of waiting
  • STRICT_PAYLOAD_TYPE: "true" to reject callback payloads encoded in a different format
  • MAX_WORKERS: maximum number of concurrent update handlers (default: 32)
  • POLL_TIMEOUT: long-polling timeout in seconds for getUpdates (default: 30)
  • LOG_FORMAT: logger output format, "text" or "json" (default: "text")

Returns a populated BotOpts. NewBot validates required fields and returns ErrTokenRequired when TG_TOKEN is missing.

func (*BotOpts) SetAPIURL

func (opts *BotOpts) SetAPIURL(url string) *BotOpts

SetAPIURL overrides the default Telegram API endpoint (useful for proxies or self-hosted). If not set, defaults to "https://api.telegram.org".

func (*BotOpts) SetDebug

func (opts *BotOpts) SetDebug(debug bool) *BotOpts

SetDebug enables or disables debug-level logging. Default is false.

func (*BotOpts) SetDropRateLimitOverflow

func (opts *BotOpts) SetDropRateLimitOverflow(drop bool) *BotOpts

SetDropRateLimitOverflow configures outgoing Telegram API requests to fail immediately when rate-limit capacity is unavailable. Default is false.

func (*BotOpts) SetErrorTemplate

func (opts *BotOpts) SetErrorTemplate(tpl string) *BotOpts

SetErrorTemplate sets the format string for error messages sent to users. Use "%s" to insert the actual error. Example: "❌ Error: %s" If not set, defaults to "%s".

func (*BotOpts) SetLogFormat

func (opts *BotOpts) SetLogFormat(format utils.LogFormat) *BotOpts

SetLogFormat sets the output format used by bot-managed loggers.

func (*BotOpts) SetLogFormatter

func (opts *BotOpts) SetLogFormatter(formatter *sneklog.Formatter) *BotOpts

SetLogFormatter sets the formatter used by bot-managed logger writers.

func (*BotOpts) SetLoggerBasePath

func (opts *BotOpts) SetLoggerBasePath(path string) *BotOpts

SetLoggerBasePath sets the directory where log files are written. Defaults to "./".

func (*BotOpts) SetMaxWorkers

func (opts *BotOpts) SetMaxWorkers(workers int) *BotOpts

SetMaxWorkers sets the maximum number of concurrent update handlers. Must be called before NewBot, as the value is captured during bot creation.

The optimal value depends on your bot's workload:

  • For I/O-bound handlers (e.g., database queries, external API calls), you may need more workers, but be mindful of downstream service limits.
  • For CPU-bound handlers, keep workers close to the number of CPU cores.

Recommended starting points (adjust based on profiling and monitoring):

  • Small to medium bots with fast handlers: 16–32
  • Medium to large bots with fast handlers: 32–64
  • Large bots with heavy I/O: 64–128 (ensure your infrastructure can handle it)

The default is 32. Monitor queue length and processing latency to fine-tune.

func (*BotOpts) SetPollTimeout

func (opts *BotOpts) SetPollTimeout(seconds int) *BotOpts

SetPollTimeout sets the long-polling timeout in seconds for getUpdates. Defaults to 30. Telegram accepts 0..50.

func (*BotOpts) SetPrefixes

func (opts *BotOpts) SetPrefixes(prefixes ...string) *BotOpts

SetPrefixes sets the command prefixes (e.g., "/", "!"). If not set via environment, defaults to ["/"].

func (*BotOpts) SetProxyURL

func (opts *BotOpts) SetProxyURL(u *url.URL) *BotOpts

SetProxyURL sets the HTTP, HTTPS, SOCKS5, or SOCKS5H proxy used for Telegram API requests. A nil URL restores proxy discovery from the environment.

func (*BotOpts) SetRateLimit

func (opts *BotOpts) SetRateLimit(limit int) *BotOpts

SetRateLimit sets the maximum number of API requests per second. Telegram allows up to 30 req/s for most bots. Defaults to 30.

func (*BotOpts) SetStrictPayloadType

func (opts *BotOpts) SetStrictPayloadType(strict bool) *BotOpts

SetStrictPayloadType enables or disables strict callback payload decoding. When enabled, the bot accepts only the configured default payload type.

func (*BotOpts) SetToken

func (opts *BotOpts) SetToken(token string) *BotOpts

SetToken sets the Telegram bot token (required).

func (*BotOpts) SetUpdateTypes

func (opts *BotOpts) SetUpdateTypes(types ...tgapi.UpdateType) *BotOpts

SetUpdateTypes sets the list of update types to listen for. If empty (default), Telegram will return all update types. Example: opts.SetUpdateTypes("message", "callback_query").

func (*BotOpts) SetUseRequestLogger

func (opts *BotOpts) SetUseRequestLogger(use bool) *BotOpts

SetUseRequestLogger enables detailed logging of all Telegram API requests. Default is false.

func (*BotOpts) SetUseTestServer

func (opts *BotOpts) SetUseTestServer(use bool) *BotOpts

SetUseTestServer enables using Telegram's test server (https://api.telegram.org/bot<token>/test). Default is false.

func (*BotOpts) SetWriteToFile

func (opts *BotOpts) SetWriteToFile(write bool) *BotOpts

SetWriteToFile enables writing logs to files (main.log and requests.log). Default is false.

type BotOptsFileCodec

type BotOptsFileCodec interface {
	FromBytes([]byte) (*BotOpts, error)
	ToBytes(*BotOpts) ([]byte, error)
	Load(filename string) (*BotOpts, error)
	Save(filename string, opts *BotOpts) error
}

BotOptsFileCodec decodes and encodes BotOpts file formats.

type BotOptsFileJSON

type BotOptsFileJSON struct {
	// Version identifies the JSON configuration format version.
	Version int `json:"version"`
	// Token is the Telegram bot token.
	Token string `json:"token"`
	// UpdateTypes limits the update kinds requested from Telegram.
	UpdateTypes []tgapi.UpdateType `json:"update_types"`
	// Debug enables debug logging.
	Debug bool `json:"debug"`
	// ErrorTemplate formats user-facing handler errors.
	ErrorTemplate string `json:"error_template"`
	// Prefixes contains accepted command prefixes.
	Prefixes []string `json:"prefixes"`
	// Logger contains file logging options.
	Logger botOptsFileJSONLogger `json:"logger"`
	// API contains Telegram client and rate-limit options.
	API botOptsFileJSONAPI `json:"api"`
	// StrictPayloadType requires callback payloads to use the configured encoding.
	StrictPayloadType bool `json:"strict_payload_type"`
	// MaxWorkers limits concurrent update handlers.
	MaxWorkers int `json:"max_workers"`
	// Proxy configures the Telegram API proxy.
	Proxy BotOptsFileJSONProxy `json:"proxy"`
}

BotOptsFileJSON is the JSON file representation of BotOpts.

type BotOptsFileJSONCodec

type BotOptsFileJSONCodec struct{}

BotOptsFileJSONCodec encodes and decodes BotOpts using BotOptsFileJSON.

func (BotOptsFileJSONCodec) EscapeEnv

func (codec BotOptsFileJSONCodec) EscapeEnv(s string) string

EscapeEnv escapes an environment value for use inside a JSON string.

func (BotOptsFileJSONCodec) FromBytes

func (codec BotOptsFileJSONCodec) FromBytes(data []byte) (*BotOpts, error)

FromBytes decodes BotOpts from JSON file bytes.

func (BotOptsFileJSONCodec) Load

func (codec BotOptsFileJSONCodec) Load(filename string) (*BotOpts, error)

Load reads BotOpts from a JSON config file.

func (BotOptsFileJSONCodec) Save

func (codec BotOptsFileJSONCodec) Save(filename string, opts *BotOpts) error

Save writes BotOpts to a JSON config file.

func (BotOptsFileJSONCodec) ToBytes

func (codec BotOptsFileJSONCodec) ToBytes(opts *BotOpts) ([]byte, error)

ToBytes encodes BotOpts into JSON file bytes.

type BotOptsFileJSONProxy

type BotOptsFileJSONProxy struct {
	// Scheme is one of http, https, socks5, or socks5h.
	Scheme string `json:"scheme"`
	// Host is the proxy hostname or IP address without a port.
	Host string `json:"host"`
	// Port is the proxy TCP port in the range 1..65535.
	Port int `json:"port"`
	// User is the optional proxy authentication username.
	User string `json:"user"`
	// Password is the optional proxy authentication password.
	Password string `json:"password"`
}

BotOptsFileJSONProxy is the JSON representation of a Telegram API proxy.

type BotPayloadType

type BotPayloadType string

BotPayloadType defines the serialization format for callback data payloads.

const (
	// BotPayloadBase64 encodes callback data as a Base64 string.
	BotPayloadBase64 BotPayloadType = "base64"
	// BotPayloadJSON encodes callback data as a JSON string.
	BotPayloadJSON BotPayloadType = "json"
	// BotPayloadCompact encodes callback data as a compact delimited string.
	BotPayloadCompact BotPayloadType = "compact"
	// BotPayloadCompactBase64 encodes compact callback data as a Base64 string.
	BotPayloadCompactBase64 BotPayloadType = "compact-base64"
)

type BotWebhookOpts

type BotWebhookOpts struct {
	// Path is the local HTTP route that receives Telegram updates.
	Path string
	// LocalPort is the TCP port used by the webhook server.
	LocalPort int
	// UseStatusPath enables the authenticated /status endpoint.
	UseStatusPath bool

	// URL is the public base URL Telegram uses for delivery.
	URL string
	// Certificate contains a self-signed public certificate to upload.
	Certificate []byte
	// IPAddress fixes the destination IP used by Telegram.
	IPAddress string
	// MaxConnections limits simultaneous Telegram webhook connections to 1–100.
	MaxConnections int8
	// AllowedUpdates limits the update kinds delivered to the webhook.
	AllowedUpdates []tgapi.UpdateType
	// DropPendingUpdates requests deletion of queued updates during registration.
	DropPendingUpdates bool
	// SecretToken authenticates Telegram requests and the optional status endpoint.
	SecretToken string
}

BotWebhookOpts configures Telegram webhook registration and the local HTTP server.

func NewBotWebhookOpts

func NewBotWebhookOpts() *BotWebhookOpts

NewBotWebhookOpts returns webhook options with the default path, local port, and max connections.

func (*BotWebhookOpts) MustLoadCertificate

func (opts *BotWebhookOpts) MustLoadCertificate(filename string) *BotWebhookOpts

MustLoadCertificate loads a webhook certificate from disk and panics on failure.

func (*BotWebhookOpts) SetAllowedUpdates

func (opts *BotWebhookOpts) SetAllowedUpdates(updates ...tgapi.UpdateType) *BotWebhookOpts

SetAllowedUpdates sets the Telegram update types that should be delivered to the webhook.

func (*BotWebhookOpts) SetCertificate

func (opts *BotWebhookOpts) SetCertificate(certificate []byte) *BotWebhookOpts

SetCertificate sets the self-signed webhook certificate bytes to upload.

func (*BotWebhookOpts) SetDropPendingUpdates

func (opts *BotWebhookOpts) SetDropPendingUpdates(drop bool) *BotWebhookOpts

SetDropPendingUpdates configures whether Telegram should drop pending updates while setting the webhook.

func (*BotWebhookOpts) SetIPAddress

func (opts *BotWebhookOpts) SetIPAddress(ip string) *BotWebhookOpts

SetIPAddress sets the fixed IP address Telegram should use for webhook delivery.

func (*BotWebhookOpts) SetLocalPort

func (opts *BotWebhookOpts) SetLocalPort(port int) *BotWebhookOpts

SetLocalPort sets the local HTTP port used by the webhook server.

func (*BotWebhookOpts) SetMaxConnections

func (opts *BotWebhookOpts) SetMaxConnections(max int8) *BotWebhookOpts

SetMaxConnections sets Telegram's maximum number of simultaneous webhook connections.

func (*BotWebhookOpts) SetPath

func (opts *BotWebhookOpts) SetPath(path string) *BotWebhookOpts

SetPath sets the local HTTP path that receives Telegram webhook requests.

func (*BotWebhookOpts) SetSecretToken

func (opts *BotWebhookOpts) SetSecretToken(secretToken string) *BotWebhookOpts

SetSecretToken sets the secret token expected in Telegram webhook requests. The same token is also required to access /status when that endpoint is enabled.

func (*BotWebhookOpts) SetURL

func (opts *BotWebhookOpts) SetURL(url string) *BotWebhookOpts

SetURL sets the public base URL Telegram should call for incoming updates.

func (*BotWebhookOpts) SetUseStatusPath

func (opts *BotWebhookOpts) SetUseStatusPath(use bool) *BotWebhookOpts

SetUseStatusPath enables or disables the optional /status endpoint. A non-empty SecretToken is required when this endpoint is enabled.

type CallbackData

type CallbackData struct {
	// Command is the command name used for payload routing.
	Command string `json:"cmd"`
	// Args contains the string arguments passed to the payload handler.
	Args []string `json:"args"`
}

CallbackData represents the structured payload sent when an inline button with callback data is pressed.

This structure is serialized to JSON and sent to the bot as a string. The bot should parse this back to determine the command and arguments.

Example:

{"cmd":"delete_user","args":["123","confirm"]}

func NewCallbackData

func NewCallbackData(command string, args ...any) CallbackData

NewCallbackData creates a new CallbackData instance with the given command and args.

All args are converted to strings using fmt.Sprint. This is safe for primitives (int, string, bool, float64) but may not serialize complex structs meaningfully.

Use this to build callback payloads for bot command routing.

func (CallbackData) Encode

func (d CallbackData) Encode(t BotPayloadType) (string, error)

Encode serializes the CallbackData according to the specified payload type. Supported types: BotPayloadJSON, BotPayloadBase64, BotPayloadCompact, and BotPayloadCompactBase64. For unknown types, returns an empty string.

func (CallbackData) ToBase64

func (d CallbackData) ToBase64() string

ToBase64 serializes the CallbackData to a JSON string and then encodes it as Base64. Returns an empty string if serialization or encoding fails.

func (CallbackData) ToCompact

func (d CallbackData) ToCompact() string

ToCompact serializes the CallbackData to a compact delimited string. Returns an empty string if serialization fails.

The compact format coalesces "no args" with "single empty arg" — both produce "cmd|" and decode back to nil args. Use ToJSON or ToBase64 when that distinction must be preserved.

func (CallbackData) ToCompactBase64

func (d CallbackData) ToCompactBase64() string

ToCompactBase64 serializes the CallbackData to compact text and then encodes it as Base64. Returns an empty string if serialization or encoding fails.

func (CallbackData) ToJSON

func (d CallbackData) ToJSON() string

ToJSON serializes the CallbackData to a JSON string. Returns an empty string if serialization fails.

type Command

type Command[T AppData] struct {
	// contains filtered or unexported fields
}

Command represents a bot command with arguments, description, and executor. Can be registered in a Plugin and optionally skipped from auto-generation.

func NewCommand

func NewCommand[T any](command string, exec CommandExecutor[T], args ...CommandArg) *Command[T]

NewCommand creates a new Command with the given identifier, executor, and arguments.

The identifier is used as the routing key for both /-prefixed commands and callback payloads — the difference is registration: pass the result to Plugin.AddCommand/Plugin.Command for message routing, or to Plugin.AddPayload/Plugin.Payload for callback_data routing.

For /-commands the identifier must not include the leading slash (e.g. "start", not "/start") and should match [_a-z0-9]{1,32} to satisfy Telegram's BotCommand validation. Payload identifiers may use any bytes that fit Telegram's callback_data limit, though the configured payload encoding may impose its own restrictions.

func (*Command[T]) SetDescription

func (c *Command[T]) SetDescription(desc string) *Command[T]

SetDescription sets the human-readable description of the command.

func (*Command[T]) SetEphemeral

func (c *Command[T]) SetEphemeral(b bool) *Command[T]

SetEphemeral controls whether Telegram treats the command as ephemeral.

Since: Bot API 10.2

func (*Command[T]) SkipCommandAutoGen

func (c *Command[T]) SkipCommandAutoGen() *Command[T]

SkipCommandAutoGen marks this command to be excluded from auto-generated help menus.

func (*Command[T]) Use

func (c *Command[T]) Use(m Middleware[T]) *Command[T]

Use adds a middleware to the command's execution chain. Middlewares are executed in the order they are added.

type CommandArg

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

CommandArg defines a single argument for a command, including type, regex, and whether it is required.

func NewCommandArg

func NewCommandArg(text string) CommandArg

NewCommandArg creates an optional argument without value validation.

func (CommandArg) SetRequired

func (c CommandArg) SetRequired() CommandArg

SetRequired marks this argument as required. Returns the receiver for method chaining.

func (CommandArg) SetValueType

func (c CommandArg) SetValueType(t CommandValueType) CommandArg

SetValueType sets expected value type and switches built-in validation regexp.

type CommandExecutor

type CommandExecutor[T AppData] func(ctx *MessageContext, dbContext T) error

CommandExecutor is the function type that executes a command. It receives the message context and injected application data. Returning a non-nil error routes it through the bot's error handler.

type CommandGroup

type CommandGroup[T any] struct {
	// contains filtered or unexported fields
}

CommandGroup builds a set of commands with a shared name prefix and middleware.

func NewCommandGroup

func NewCommandGroup[T any](prefix string) *CommandGroup[T]

NewCommandGroup creates a command group that prefixes every added command.

func (*CommandGroup[T]) AddCommand

func (g *CommandGroup[T]) AddCommand(cmd *Command[T]) *CommandGroup[T]

AddCommand adds a prefixed copy of cmd to the group.

func (*CommandGroup[T]) Build

func (g *CommandGroup[T]) Build() []*Command[T]

Build returns command copies with group middleware prepended.

func (*CommandGroup[T]) Use

func (g *CommandGroup[T]) Use(m Middleware[T]) *CommandGroup[T]

Use adds middleware that runs before each command's own middleware.

type CommandScopeUpdateError

type CommandScopeUpdateError struct {
	// UpdatedScopes contains scopes changed before the failure.
	UpdatedScopes []tgapi.BotCommandScopeType
	// FailedScope identifies the scope whose update failed.
	FailedScope tgapi.BotCommandScopeType
	// Err is the Telegram API error for FailedScope.
	Err error
}

CommandScopeUpdateError describes a failed multi-scope command update. UpdatedScopes lists scopes successfully changed before FailedScope failed.

func (*CommandScopeUpdateError) Error

func (e *CommandScopeUpdateError) Error() string

Error returns a human-readable partial-update description.

func (*CommandScopeUpdateError) Unwrap

func (e *CommandScopeUpdateError) Unwrap() []error

Unwrap exposes both the partial-update sentinel and the underlying API error.

type CommandValueType

type CommandValueType string

CommandValueType defines the expected type of command argument.

const (
	// CommandValueString expects any non-empty string.
	CommandValueString CommandValueType = "string"
	// CommandValueInt expects a decimal integer (digits only).
	CommandValueInt CommandValueType = "int"
	// CommandValueBool expects an exact "true" or "false".
	CommandValueBool CommandValueType = "bool"
	// CommandValueAny accepts any input without validation.
	CommandValueAny CommandValueType = "any"
)

type DictEntry

type DictEntry map[string]string

DictEntry maps language codes to translated strings.

type Draft

type Draft struct {

	// ID uniquely identifies the draft within its provider.
	ID uint64
	// Message contains the current draft text.
	Message string
	// contains filtered or unexported fields
}

Draft represents a single message draft that can be edited and flushed.

Drafts are safe to use from a single goroutine. Multiple goroutines must synchronize access manually.

Drafts are automatically removed from the provider's map when Flush() succeeds.

func (*Draft) Clear

func (d *Draft) Clear()

Clear resets the draft's message content to empty string.

Does not affect server-side draft — use Flush() for that.

func (*Draft) Delete

func (d *Draft) Delete()

Delete removes the draft from its provider and clears its content.

You may call it manually if you want to cancel a draft without sending it.

func (*Draft) Flush

func (d *Draft) Flush() error

Flush sends the draft as a final message and clears it locally.

If successful:

  • The message is sent to Telegram.
  • The draft's content is cleared.
  • The draft is removed from the provider's map.

If an error occurs:

  • The message is NOT sent.
  • The draft remains in the provider and retains its content.
  • You can call Flush() again to retry.

If the draft is empty, Flush() returns nil without calling the API.

func (*Draft) GetMessage

func (d *Draft) GetMessage() string

GetMessage returns the current content of the draft.

Useful for inspection, logging, or validation before flushing.

func (*Draft) Push

func (d *Draft) Push(text string) error

Push appends text to the draft and attempts to update the server-side draft.

Returns an error if the Telegram API rejects the update (e.g., due to network issues). The draft's Message field is always updated, even if the API call fails.

Use this method to build the message incrementally.

func (*Draft) SetChat

func (d *Draft) SetChat(chatID int64, messageThreadID int) *Draft

SetChat overrides the draft's target chat and message thread.

This is useful for sending a draft to a different chat than the provider's default.

func (*Draft) SetEntities

func (d *Draft) SetEntities(entities []tgapi.MessageEntity) *Draft

SetEntities replaces the draft's message entities.

The entities slice is copied.

type DraftProvider

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

DraftProvider manages a collection of Drafts and a shared draft ID generator.

DraftProvider is safe for concurrent use.

func NewLinearDraftProvider

func NewLinearDraftProvider(api *tgapi.API, startValue uint64) *DraftProvider

NewLinearDraftProvider creates a new DraftProvider using linear (incrementing) draft IDs.

startValue is the initial value for the counter. Use 0 for fresh start, or a known value to resume from persisted state.

This is useful when you need to store draft IDs externally (e.g., in a database) and want to reconstruct drafts after restart.

func NewRandomDraftProvider

func NewRandomDraftProvider(api *tgapi.API) *DraftProvider

NewRandomDraftProvider creates a DraftProvider using non-cryptographic random IDs. Zero values and collisions with active drafts are retried.

func (*DraftProvider) FlushAll

func (p *DraftProvider) FlushAll() error

FlushAll sends all pending drafts as final messages and clears them.

If one or more drafts fail to send, FlushAll still attempts all drafts and returns the first encountered error.

After successful flush, each draft is removed from the provider and cleared.

func (*DraftProvider) GetDraft

func (p *DraftProvider) GetDraft(id uint64) (*Draft, bool)

GetDraft retrieves a draft by its ID.

Returns the draft and true if found, or nil and false if not found.

func (*DraftProvider) NewDraft

func (p *DraftProvider) NewDraft(parseMode tgapi.ParseMode) *Draft

NewDraft creates a new draft with the provided parse mode.

The caller must set a chat with SetChat before Push or Flush.

type ErrorEvent

type ErrorEvent struct {
	// UpdateID identifies the Telegram update when available.
	UpdateID int
	// UpdateType identifies the normalized update kind when available.
	UpdateType tgapi.UpdateType
	// Plugin names the component that reported the error.
	Plugin string
	// HandlerKind classifies the failing handler or runtime component.
	HandlerKind HandlerEventKind
	// HandlerName identifies the failing handler within its kind.
	HandlerName string
	// FromID identifies the originating user when available.
	FromID int64
	// ChatID identifies the originating chat when available.
	ChatID int64
	// Err is the reported error.
	Err error
	// UserFacing reports whether Err is safe to show to the user.
	UserFacing bool
}

ErrorEvent describes an error routed through framework error handling.

type Event

type Event interface {
	// contains filtered or unexported methods
}

Event is the marker interface implemented by all observer runtime events.

type HandlerEventKind

type HandlerEventKind string

HandlerEventKind identifies the kind of handler observed by runtime events.

const (
	// HandlerCommandKind identifies a command handler.
	HandlerCommandKind HandlerEventKind = "command"
	// HandlerMessageKind identifies a message fallback handler.
	HandlerMessageKind HandlerEventKind = "message"
	// HandlerMiddlewareKind identifies middleware execution.
	HandlerMiddlewareKind HandlerEventKind = "middleware"
	// HandlerPayloadKind identifies a callback payload handler.
	HandlerPayloadKind HandlerEventKind = "payload"
	// HandlerUpdateKind identifies a generic update handler.
	HandlerUpdateKind HandlerEventKind = "update"
	// HandlerRunnerKind identifies a background runner execution.
	HandlerRunnerKind HandlerEventKind = "runner"
	// HandlerPollingKind identifies polling and getUpdates runtime work.
	HandlerPollingKind HandlerEventKind = "polling"
	// HandlerSceneKind identifies a scene runtime handler wrapper.
	HandlerSceneKind HandlerEventKind = "scene"
	// HandlerSceneStepKind identifies a scene step handler.
	HandlerSceneStepKind HandlerEventKind = "scene_step"
	// HandlerSceneCommandKind identifies a scene-local command handler.
	HandlerSceneCommandKind HandlerEventKind = "scene_command"
	// HandlerScenePayloadKind identifies a scene-local callback payload handler.
	HandlerScenePayloadKind HandlerEventKind = "scene_payload"
	// HandlerSceneMessageKind identifies a scene message fallback handler.
	HandlerSceneMessageKind HandlerEventKind = "scene_message"
)

type HandlerFinishedEvent

type HandlerFinishedEvent struct {
	// UpdateID identifies the Telegram update.
	UpdateID int
	// UpdateType identifies the normalized update kind.
	UpdateType tgapi.UpdateType
	// Plugin names the plugin that owns the handler.
	Plugin string
	// HandlerKind classifies the handler.
	HandlerKind HandlerEventKind
	// HandlerName identifies the handler within its plugin and kind.
	HandlerName string
	// FromID identifies the originating user when available.
	FromID int64
	// ChatID identifies the originating chat when available.
	ChatID int64
	// Duration is the handler execution time.
	Duration time.Duration
	// Err is the error returned or recovered from the handler.
	Err error
	// UserFacing reports whether Err is safe to show to the user.
	UserFacing bool
}

HandlerFinishedEvent describes a handler that has completed.

type HandlerStartedEvent

type HandlerStartedEvent struct {
	// UpdateID identifies the Telegram update.
	UpdateID int
	// UpdateType identifies the normalized update kind.
	UpdateType tgapi.UpdateType
	// Plugin names the plugin that owns the handler.
	Plugin string
	// HandlerKind classifies the handler.
	HandlerKind HandlerEventKind
	// HandlerName identifies the handler within its plugin and kind.
	HandlerName string
	// FromID identifies the originating user when available.
	FromID int64
	// ChatID identifies the originating chat when available.
	ChatID int64
}

HandlerStartedEvent describes a handler about to execute.

type InlineKeyboard

type InlineKeyboard struct {
	// CurrentLine is the row currently being built.
	CurrentLine extypes.Slice[tgapi.InlineKeyboardButton]
	// Lines contains completed keyboard rows.
	Lines [][]tgapi.InlineKeyboardButton
	// contains filtered or unexported fields
}

InlineKeyboard is a stateful builder for constructing Telegram inline keyboard layouts.

Buttons are added row-by-row. When a row reaches maxRow, it is automatically flushed. Call AddLine() to manually end a row, or Get() to finalize and retrieve the markup.

The keyboard is not thread-safe. Build it in a single goroutine.

func NewInlineKeyboard

func NewInlineKeyboard(payloadType BotPayloadType, maxRow int) *InlineKeyboard

NewInlineKeyboard creates a keyboard builder with the specified payload encoding and a positive maximum number of buttons per row.

Use NewInlineKeyboardJSON or NewInlineKeyboardBase64 for the common cases.

func NewInlineKeyboardBase64

func NewInlineKeyboardBase64(maxRow int) *InlineKeyboard

NewInlineKeyboardBase64 creates a keyboard builder with a positive maximum number of buttons per row, using Base64 encoding for button payloads.

Example: NewInlineKeyboardBase64(3) creates a keyboard with at most 3 buttons per line.

func NewInlineKeyboardCompact

func NewInlineKeyboardCompact(maxRow int) *InlineKeyboard

NewInlineKeyboardCompact creates a keyboard builder using compact callback payloads.

func NewInlineKeyboardCompactBase64

func NewInlineKeyboardCompactBase64(maxRow int) *InlineKeyboard

NewInlineKeyboardCompactBase64 creates a keyboard builder using Base64-encoded compact payloads.

func NewInlineKeyboardJSON

func NewInlineKeyboardJSON(maxRow int) *InlineKeyboard

NewInlineKeyboardJSON creates a keyboard builder with a positive maximum number of buttons per row.

Example: NewInlineKeyboardJSON(3) creates a keyboard with at most 3 buttons per line.

func (*InlineKeyboard) AddButton

AddButton adds a button pre-configured via InlineKeyboardButtonBuilder. This is the most flexible way to create buttons with custom emoji, style, URL, and callback.

func (*InlineKeyboard) AddCallbackButton

func (in *InlineKeyboard) AddCallbackButton(text, cmd string, args ...any) *InlineKeyboard

AddCallbackButton adds a button that sends a structured callback payload to the bot. The command and args are serialized according to the current payloadType.

func (*InlineKeyboard) AddCallbackButtonStyle

func (in *InlineKeyboard) AddCallbackButtonStyle(text string, style tgapi.KeyboardButtonStyle, cmd string, args ...any) *InlineKeyboard

AddCallbackButtonStyle adds a styled callback button. Style affects visual appearance; callback data is sent to bot on press.

func (*InlineKeyboard) AddLine

func (in *InlineKeyboard) AddLine() *InlineKeyboard

AddLine manually ends the current row and starts a new one. If the current row is empty, nothing happens.

func (*InlineKeyboard) AddURLButton

func (in *InlineKeyboard) AddURLButton(text, url string) *InlineKeyboard

AddURLButton adds a button that opens a URL when pressed. No callback data is attached.

func (*InlineKeyboard) AddURLButtonStyle

func (in *InlineKeyboard) AddURLButtonStyle(text string, style tgapi.KeyboardButtonStyle, url string) *InlineKeyboard

AddURLButtonStyle adds a button with a visual style that opens a URL. Style must be one of: ButtonStyleDanger, ButtonStyleSuccess, ButtonStylePrimary.

func (*InlineKeyboard) Get

func (in *InlineKeyboard) Get() (*tgapi.ReplyMarkup, error)

Get finalizes the keyboard and returns a tgapi.ReplyMarkup. Automatically flushes the current line if not empty.

Returns a pointer to a ReplyMarkup suitable for use with tgapi.SendMessage.

func (*InlineKeyboard) GetMaxRow

func (in *InlineKeyboard) GetMaxRow() int

GetMaxRow returns the maximum number of buttons per row, or zero in explicit unlimited mode.

func (*InlineKeyboard) GetPayloadType

func (in *InlineKeyboard) GetPayloadType() BotPayloadType

GetPayloadType returns the keyboard-local callback payload encoding type.

func (*InlineKeyboard) SetMaxRow

func (in *InlineKeyboard) SetMaxRow(maxRow int) *InlineKeyboard

SetMaxRow sets the positive number of buttons appended to a row before the keyboard automatically starts a new line. Get returns ErrInlineKeyboardMaxRow when maxRow is not positive.

func (*InlineKeyboard) SetPayloadType

func (in *InlineKeyboard) SetPayloadType(t BotPayloadType) *InlineKeyboard

SetPayloadType sets the keyboard-local serialization format for callback data added via AddCallbackButton and AddCallbackButtonStyle methods. It overrides the bot's default payload type for this keyboard only.

func (*InlineKeyboard) SetUnlimitedRows

func (in *InlineKeyboard) SetUnlimitedRows() *InlineKeyboard

SetUnlimitedRows disables automatic row wrapping explicitly.

type InlineKeyboardButtonBuilder

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

InlineKeyboardButtonBuilder is a fluent builder for creating a single inline keyboard button.

Use NewInlineKeyboardButton() to start, then chain methods to configure:

  • SetIconCustomEmojiID() — adds a custom emoji icon
  • SetStyle() — sets visual style (danger/success/primary)
  • SetURL() — makes button open a URL
  • SetCallbackDataJSON() — attaches structured command + args for bot handling

Call Build to validate and produce the final tgapi.InlineKeyboardButton. Builder methods are immutable — each returns a copy.

func NewInlineKeyboardButton

func NewInlineKeyboardButton(text string) InlineKeyboardButtonBuilder

NewInlineKeyboardButton creates a new button builder with the given display text. The button will have no URL, no style, and no callback data by default.

func (InlineKeyboardButtonBuilder) Build

Build validates and returns the configured inline keyboard button.

func (InlineKeyboardButtonBuilder) SetCallbackData

func (b InlineKeyboardButtonBuilder) SetCallbackData(cmd string, args ...any) InlineKeyboardButtonBuilder

SetCallbackData sets a structured callback payload using the configured payload type. The default payload type is JSON.

func (InlineKeyboardButtonBuilder) SetCallbackDataBase64

func (b InlineKeyboardButtonBuilder) SetCallbackDataBase64(cmd string, args ...any) InlineKeyboardButtonBuilder

SetCallbackDataBase64 sets a Base64-encoded structured callback payload. Base64 does not bypass Telegram's 64-byte callback-data limit.

func (InlineKeyboardButtonBuilder) SetCallbackDataCompact

func (b InlineKeyboardButtonBuilder) SetCallbackDataCompact(cmd string, args ...any) InlineKeyboardButtonBuilder

SetCallbackDataCompact sets a structured callback payload encoded as compact text.

func (InlineKeyboardButtonBuilder) SetCallbackDataCompactBase64

func (b InlineKeyboardButtonBuilder) SetCallbackDataCompactBase64(cmd string, args ...any) InlineKeyboardButtonBuilder

SetCallbackDataCompactBase64 sets a compact callback payload encoded as Base64.

func (InlineKeyboardButtonBuilder) SetCallbackDataJSON

func (b InlineKeyboardButtonBuilder) SetCallbackDataJSON(cmd string, args ...any) InlineKeyboardButtonBuilder

SetCallbackDataJSON sets a structured callback payload that will be sent to the bot when the button is pressed. The command and arguments are serialized as JSON.

Args are converted to strings using fmt.Sprint. Non-string types (e.g., int, bool) are safely serialized, but complex structs may not serialize usefully.

Example: SetCallbackDataJSON("delete_user", 123, "confirm") → {"cmd":"delete_user","args":["123","confirm"]}.

func (InlineKeyboardButtonBuilder) SetDisabled

SetDisabled makes the button inert and clears URL and callback actions.

Since: Bot API 10.3

func (InlineKeyboardButtonBuilder) SetIconCustomEmojiID

SetIconCustomEmojiID sets a custom emoji ID to display as the button's icon. This is a Telegram Bot API feature for custom emoji icons.

func (InlineKeyboardButtonBuilder) SetPayloadType

SetPayloadType sets the encoding used by SetCallbackData.

func (InlineKeyboardButtonBuilder) SetStyle

SetStyle sets the visual style of the button. Valid values: ButtonStyleDanger, ButtonStyleSuccess, ButtonStylePrimary. If not set, the button uses the default style.

func (InlineKeyboardButtonBuilder) SetURL

SetURL sets a URL that will be opened when the button is pressed. It clears callback data because Telegram requires exactly one button action.

func (InlineKeyboardButtonBuilder) Validate

func (b InlineKeyboardButtonBuilder) Validate() error

Validate checks that the button has exactly one action and valid callback data.

type L10n

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

L10n stores translations with a configurable fallback language and is safe for concurrent use.

func NewL10n

func NewL10n(fallbackLanguage string) *L10n

NewL10n creates a localization store with the given fallback language.

func (*L10n) AddDictEntry

func (l *L10n) AddDictEntry(key string, value DictEntry) *L10n

AddDictEntry stores translations for key.

func (*L10n) GetFallbackLanguage

func (l *L10n) GetFallbackLanguage() string

GetFallbackLanguage returns the currently configured fallback language code.

func (*L10n) Translate

func (l *L10n) Translate(lang, key string) string

Translate returns the translation for key in lang, falling back to the configured language or the key itself.

type LinearDraftIDGenerator

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

LinearDraftIDGenerator generates draft IDs using a monotonically increasing counter. Useful for debugging, persistence, or when drafts must be ordered.

func (*LinearDraftIDGenerator) Next

func (g *LinearDraftIDGenerator) Next() uint64

Next returns the next linear ID, atomically incremented.

type MemorySessionStore

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

MemorySessionStore stores scene sessions in memory.

func NewMemorySessionStore

func NewMemorySessionStore() *MemorySessionStore

NewMemorySessionStore creates an empty in-memory session store.

func (*MemorySessionStore) Delete

func (s *MemorySessionStore) Delete(key string) error

Delete removes the session stored under key.

func (*MemorySessionStore) Get

Get returns the session stored under key, or the zero session when absent.

func (*MemorySessionStore) Set

func (s *MemorySessionStore) Set(key string, session SceneSession) error

Set stores session under key.

type MessageContext

type MessageContext struct {
	// API is the Telegram API client used by context helpers.
	API *tgapi.API
	// Update is the Telegram update being handled.
	Update tgapi.Update

	// Msg is the normalized Telegram message for message-backed update kinds.
	// It is nil for updates that do not include a message object.
	Msg *tgapi.Message
	// From is the normalized Telegram user for update kinds that expose one.
	// It stays nil for sender-chat-only updates and update kinds without a user.
	From *tgapi.User
	// Chat is the normalized Telegram chat for update kinds that expose one.
	// It is nil for updates that do not include a chat identity.
	Chat *tgapi.Chat

	// Logger is the logger assigned by the matched plugin for the current handler call.
	// It may fall back to the bot logger when the plugin has no dedicated logger.
	Logger *sneklog.Logger

	// InlineMsgID is the inline message identifier for callback queries that target
	// an inline message instead of a chat message.
	InlineMsgID string
	// CallbackMsgID is the message ID targeted by the current callback query when
	// the callback comes from a chat message.
	CallbackMsgID int
	// CallbackQueryID is the Telegram callback query ID for payload handlers and
	// callback-backed scene handlers.
	CallbackQueryID string
	// FromID is the normalized sender ID when the current update exposes a user.
	// It is zero when the update has no user identity.
	FromID int64
	// ChatID is the normalized chat ID when the current update exposes a chat.
	// It is zero when the update has no chat identity.
	ChatID int64
	// Prefix is the matched command prefix for command routing and scene-local
	// command routing. It is empty outside those flows.
	Prefix string
	// Text is the parsed command tail for command routing, the parsed scene-command
	// tail for scene-local command routing, or the trimmed message text seen by a
	// scene step/message handler. It is empty when the current routing path does
	// not derive text input.
	Text string
	// Args contains parsed command or payload arguments for the current routing
	// path. It is nil or empty when no argument vector is derived.
	Args []string
	// contains filtered or unexported fields
}

MessageContext holds the normalized per-update context passed to command, payload, scene, middleware, and generic update handlers.

MessageContext is populated from the current Telegram update before handler routing. Not every field is guaranteed for every update kind. In particular:

  • Update is always present.
  • Msg is populated only for update kinds that carry a Telegram message object.
  • From and FromID are populated only when the update exposes a user identity.
  • Chat and ChatID are populated only when the update exposes a chat identity.
  • Text, Args, and Prefix are populated only by command or scene command routing.
  • CallbackQueryID, CallbackMsgID, and InlineMsgID are populated only for callback query handling when the corresponding callback targets exist.

Helper methods on MessageContext may require a message-backed context. For example, reply helpers need Msg, while inline callback edit helpers can work through InlineMsgID when there is no chat message.

func (*MessageContext) Answer

func (ctx *MessageContext) Answer(text string) *AnswerMessage

Answer sends a plain text message (ParseNone).

func (*MessageContext) AnswerCallback

func (ctx *MessageContext) AnswerCallback()

AnswerCallback answers the callback query with no text or alert.

func (*MessageContext) AnswerCallbackAlert

func (ctx *MessageContext) AnswerCallbackAlert(text string)

AnswerCallbackAlert answers the callback query with a user-visible alert.

func (*MessageContext) AnswerCallbackText

func (ctx *MessageContext) AnswerCallbackText(text string)

AnswerCallbackText answers the callback query with a text notification.

func (*MessageContext) AnswerCallbackURL

func (ctx *MessageContext) AnswerCallbackURL(u string)

AnswerCallbackURL answers the callback query with a URL redirect.

func (*MessageContext) AnswerLong

func (ctx *MessageContext) AnswerLong(text string) []*AnswerMessage

AnswerLong sends one or more plain-text messages if text exceeds Telegram's limit.

The text is split into Telegram-safe chunks. Returned messages preserve send order. If a chunk fails to send, already-sent messages are returned.

func (*MessageContext) AnswerLongf

func (ctx *MessageContext) AnswerLongf(template string, args ...any) []*AnswerMessage

AnswerLongf formats a string using fmt.Sprintf and sends it as one or more plain-text messages.

func (*MessageContext) AnswerMarkdown

func (ctx *MessageContext) AnswerMarkdown(text string) *AnswerMessage

AnswerMarkdown sends a message using MarkdownV2 formatting.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) AnswerPhoto

func (ctx *MessageContext) AnswerPhoto(photoID, text string) *AnswerMessage

AnswerPhoto sends a photo with plain text caption.

func (*MessageContext) AnswerPhotoKeyboard

func (ctx *MessageContext) AnswerPhotoKeyboard(photoID, text string, kb *InlineKeyboard) *AnswerMessage

AnswerPhotoKeyboard sends a photo with caption and inline keyboard (plain text).

func (*MessageContext) AnswerPhotoKeyboardMarkdown

func (ctx *MessageContext) AnswerPhotoKeyboardMarkdown(photoID, text string, kb *InlineKeyboard) *AnswerMessage

AnswerPhotoKeyboardMarkdown sends a photo with caption and inline keyboard using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) AnswerPhotoMarkdown

func (ctx *MessageContext) AnswerPhotoMarkdown(photoID, text string) *AnswerMessage

AnswerPhotoMarkdown sends a photo with MarkdownV2 caption.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) AnswerPhotof

func (ctx *MessageContext) AnswerPhotof(photoID, template string, args ...any) *AnswerMessage

AnswerPhotof formats a string and sends it as a photo caption (plain text).

func (*MessageContext) AnswerPhotofMarkdown

func (ctx *MessageContext) AnswerPhotofMarkdown(photoID, template string, args ...any) *AnswerMessage

AnswerPhotofMarkdown formats a string and sends it as a photo caption using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) Answerf

func (ctx *MessageContext) Answerf(template string, args ...any) *AnswerMessage

Answerf formats a string using fmt.Sprintf and sends it as a plain text message.

func (*MessageContext) AnswerfMarkdown

func (ctx *MessageContext) AnswerfMarkdown(template string, args ...any) *AnswerMessage

AnswerfMarkdown formats a string using fmt.Sprintf and sends it using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) BindArgs

func (ctx *MessageContext) BindArgs(dst any) error

BindArgs binds positional command arguments from ctx.Args into dst.

Exported struct fields are filled in declaration order. When fewer arguments are provided than fields, the remaining fields keep their zero values. If the final bindable field is a string, it receives the remaining arguments joined with spaces.

func (*MessageContext) CallbackDelete

func (ctx *MessageContext) CallbackDelete()

CallbackDelete deletes the message that triggered the callback query.

func (*MessageContext) Context

func (ctx *MessageContext) Context() context.Context

Context returns the request-scoped context associated with the current update.

func (*MessageContext) EditCallback

func (ctx *MessageContext) EditCallback(text string, keyboard *InlineKeyboard) *AnswerMessage

EditCallback edits the callback message using plain text (ParseNone).

func (*MessageContext) EditCallbackMarkdown

func (ctx *MessageContext) EditCallbackMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage

EditCallbackMarkdown edits the callback message using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) EditCallbackRich

func (ctx *MessageContext) EditCallbackRich(keyboard *InlineKeyboard, blocks ...tgapi.InputRichBlock) *AnswerMessage

EditCallbackRich builds rich blocks and replaces the callback message content and inline keyboard. It doesn't upload local files referenced with attach://.

Since: Bot API 10.2

func (*MessageContext) EditCallbackf

func (ctx *MessageContext) EditCallbackf(format string, keyboard *InlineKeyboard, args ...any) *AnswerMessage

EditCallbackf formats a string using fmt.Sprintf and edits the callback message with plain text.

func (*MessageContext) EditCallbackfMarkdown

func (ctx *MessageContext) EditCallbackfMarkdown(format string, keyboard *InlineKeyboard, args ...any) *AnswerMessage

EditCallbackfMarkdown formats a string using fmt.Sprintf and edits the callback message with MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) EnterScene

func (ctx *MessageContext) EnterScene(name string) error

EnterScene enters the named scene at its configured entry step.

func (*MessageContext) EnterSceneStep

func (ctx *MessageContext) EnterSceneStep(name, step string) error

EnterSceneStep enters the named scene at a specific step.

func (*MessageContext) Error

func (ctx *MessageContext) Error(err error)

Error routes err through the centralized handler error path.

The error is logged via ctx.Logger. When IsUserError(err) is true, the formatted error template is delivered to the user — through an answer to the active callback query when one exists, otherwise as a chat reply. Internal errors are logged but not surfaced to the user.

func (*MessageContext) ExitScene

func (ctx *MessageContext) ExitScene() error

ExitScene leaves the currently active scene for this context.

func (*MessageContext) HasPhoto

func (ctx *MessageContext) HasPhoto() bool

HasPhoto reports whether the current message contains a photo payload.

func (*MessageContext) IsCallback

func (ctx *MessageContext) IsCallback() bool

IsCallback reports whether the context belongs to a callback query.

func (*MessageContext) Keyboard

func (ctx *MessageContext) Keyboard(text string, kb *InlineKeyboard) *AnswerMessage

Keyboard sends a message with an inline keyboard (plain text).

func (*MessageContext) KeyboardLong

func (ctx *MessageContext) KeyboardLong(text string, kb *InlineKeyboard) []*AnswerMessage

KeyboardLong sends long plain text split across multiple messages.

The inline keyboard is attached only to the final chunk.

func (*MessageContext) KeyboardMarkdown

func (ctx *MessageContext) KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage

KeyboardMarkdown sends a message with an inline keyboard using MarkdownV2.

⚠️ WARNING: User input must be escaped with tgfmt.EscapeMarkdownV2() before passing here.

func (*MessageContext) NewDraft

func (ctx *MessageContext) NewDraft() *Draft

NewDraft creates a new message draft associated with the current chat. Draft sends are rate-limited by the API client.

func (*MessageContext) NewDraftMarkdown

func (ctx *MessageContext) NewDraftMarkdown() *Draft

NewDraftMarkdown creates a new message draft associated with the current chat, with Markdown V2 parse mode enabled. Draft sends are rate-limited by the API client.

func (*MessageContext) NewInlineKeyboard

func (ctx *MessageContext) NewInlineKeyboard(maxRow int) *InlineKeyboard

NewInlineKeyboard creates a new keyboard builder with the context's payload encoding type and the specified maximum number of buttons per row.

func (*MessageContext) NewInlineKeyboardButton

func (ctx *MessageContext) NewInlineKeyboardButton(text string) InlineKeyboardButtonBuilder

NewInlineKeyboardButton creates a button builder using the context payload encoding.

func (*MessageContext) RichAnswer

func (ctx *MessageContext) RichAnswer(blocks ...tgapi.InputRichBlock) *AnswerMessage

RichAnswer builds and sends input rich-message blocks.

Since: Bot API 10.2

func (*MessageContext) RichAnswerKeyboard

func (ctx *MessageContext) RichAnswerKeyboard(keyboard *InlineKeyboard, blocks ...tgapi.InputRichBlock) *AnswerMessage

RichAnswerKeyboard builds and sends input rich-message blocks with an inline keyboard.

Since: Bot API 10.2

func (*MessageContext) SendAction

func (ctx *MessageContext) SendAction(action tgapi.ChatActionType)

SendAction sends a chat action (typing, uploading_photo, etc.) to indicate bot activity.

func (*MessageContext) Translate

func (ctx *MessageContext) Translate(key string) string

Translate looks up a key in the current user's language. Falls back to the bot's default language if user's language is unknown or unsupported.

func (*MessageContext) UpsertKeyboard

func (ctx *MessageContext) UpsertKeyboard(text string, keyboard *InlineKeyboard) *AnswerMessage

UpsertKeyboard edits a callback message or sends a new plain-text message with a keyboard.

func (*MessageContext) UpsertKeyboardMarkdown

func (ctx *MessageContext) UpsertKeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage

UpsertKeyboardMarkdown edits a callback message or sends a new MarkdownV2 message with a keyboard.

func (*MessageContext) UpsertKeyboardRich

func (ctx *MessageContext) UpsertKeyboardRich(keyboard *InlineKeyboard, blocks ...tgapi.InputRichBlock) *AnswerMessage

UpsertKeyboardRich builds rich blocks and either edits the callback message or sends a new message. Photo callback messages are replaced because their text content can't be edited directly. It doesn't upload local files referenced with attach://.

Since: Bot API 10.2

type Middleware

type Middleware[T AppData] struct {
	// contains filtered or unexported fields
}

Middleware represents a reusable execution interceptor. Can be synchronous (blocking) or asynchronous (non-blocking).

func NewMiddleware

func NewMiddleware[T AppData](name string, executor MiddlewareExecutor[T]) Middleware[T]

NewMiddleware creates a new synchronous middleware.

func RequirePolicy

func RequirePolicy[T AppData](name string, p Policy[T]) Middleware[T]

RequirePolicy adapts a Policy into a blocking middleware.

func (Middleware[T]) Execute

func (m Middleware[T]) Execute(ctx *MessageContext, db T) bool

Execute runs the middleware. If async, runs in a goroutine and returns true immediately. Otherwise, returns the result of the executor.

Async note: the goroutine receives a shallow copy of MessageContext, so scalar fields (FromID, ChatID, CallbackQueryID, ...) remain a stable snapshot. Pointer and slice fields (Msg, From, Chat, API, Logger, Args) continue to share storage with the synchronous flow. Async middleware must treat those fields as read-only — mutating them races the sync chain that mutates the same context concurrently. Bot runtimes wait for tracked asynchronous middleware before returning.

func (Middleware[T]) SetAsync

func (m Middleware[T]) SetAsync(async bool) Middleware[T]

SetAsync marks the middleware to run asynchronously. Execution continues regardless of its return value.

func (Middleware[T]) SetOrder

func (m Middleware[T]) SetOrder(order int) Middleware[T]

SetOrder sets the bot-level middleware execution order.

type MiddlewareExecutor

type MiddlewareExecutor[T AppData] func(ctx *MessageContext, db T) bool

MiddlewareExecutor is the function type for middleware logic. Returns true to continue execution, false to block it. If async, return value is ignored.

type MultiObserver

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

func (MultiObserver) OnError

func (m MultiObserver) OnError(ctx context.Context, event ErrorEvent)

func (MultiObserver) OnHandlerFinished

func (m MultiObserver) OnHandlerFinished(ctx context.Context, event HandlerFinishedEvent)

func (MultiObserver) OnHandlerStarted

func (m MultiObserver) OnHandlerStarted(ctx context.Context, event HandlerStartedEvent)

func (MultiObserver) OnPolicyChecked

func (m MultiObserver) OnPolicyChecked(ctx context.Context, event PolicyCheckedEvent)

func (MultiObserver) OnPollingRetry

func (m MultiObserver) OnPollingRetry(ctx context.Context, event PollingRetryEvent)

func (MultiObserver) OnRunnerFinished

func (m MultiObserver) OnRunnerFinished(ctx context.Context, event RunnerFinishedEvent)

func (MultiObserver) OnSceneTransition

func (m MultiObserver) OnSceneTransition(ctx context.Context, event SceneTransitionEvent)

func (MultiObserver) OnUpdateHandled

func (m MultiObserver) OnUpdateHandled(ctx context.Context, event UpdateHandledEvent)

func (MultiObserver) OnUpdateReceived

func (m MultiObserver) OnUpdateReceived(ctx context.Context, event UpdateReceivedEvent)

type NoData

type NoData struct{}

NoData is a placeholder type for bots that do not use shared application data.

Use Bot[NoData] to indicate no shared dependency injection is required.

type Observer

type Observer interface {
	OnUpdateReceived(ctx context.Context, event UpdateReceivedEvent)
	OnUpdateHandled(ctx context.Context, event UpdateHandledEvent)
	OnHandlerStarted(ctx context.Context, event HandlerStartedEvent)
	OnHandlerFinished(ctx context.Context, event HandlerFinishedEvent)
	OnSceneTransition(ctx context.Context, event SceneTransitionEvent)
	OnPolicyChecked(ctx context.Context, event PolicyCheckedEvent)
	OnRunnerFinished(ctx context.Context, event RunnerFinishedEvent)
	OnPollingRetry(ctx context.Context, event PollingRetryEvent)
	OnError(ctx context.Context, event ErrorEvent)
}

Observer receives best-effort runtime instrumentation events.

During RunWithContext and RunWebhookWithContext, callbacks execute on a dedicated dispatcher goroutine in enqueue order and never block update handlers. The queue is bounded; overload drops events and emits sampled warnings. Runtime shutdown cancels callback contexts and drains queued events; Bot.Close returns ErrObserverShutdownTimeout if a callback ignores cancellation.

func NewMultiObserver

func NewMultiObserver(isAsync bool, observers ...Observer) Observer

type Plugin

type Plugin[T AppData] struct {
	// contains filtered or unexported fields
}

Plugin represents a collection of commands and payloads (e.g., callback handlers), with shared middleware and configuration.

A Plugin is intended to be fully configured before it is passed to Bot.AddPlugins. After registration, treat the plugin as committed and do not mutate it further. Post-registration changes through the original *Plugin are not a supported API.

func NewPlugin

func NewPlugin[T AppData](name string) *Plugin[T]

NewPlugin creates a new Plugin with the given name.

func (*Plugin[T]) AddCommand

func (p *Plugin[T]) AddCommand(command *Command[T]) *Plugin[T]

AddCommand registers a command in the plugin.

func (*Plugin[T]) AddCommandGroup

func (p *Plugin[T]) AddCommandGroup(group *CommandGroup[T]) *Plugin[T]

AddCommandGroup registers every command built by group.

func (*Plugin[T]) AddMiddleware

func (p *Plugin[T]) AddMiddleware(middleware Middleware[T]) *Plugin[T]

AddMiddleware adds a middleware to the plugin's global middleware chain. Middlewares are executed before any command or payload.

func (*Plugin[T]) AddPayload

func (p *Plugin[T]) AddPayload(command *Command[T]) *Plugin[T]

AddPayload registers a payload (e.g., callback query data) in the plugin. Payloads are triggered by inline button callback_data, not by message text.

func (*Plugin[T]) AddScene

func (p *Plugin[T]) AddScene(scene *Scene[T]) *Plugin[T]

AddScene registers a multi-step scene in the plugin.

func (*Plugin[T]) AddUpdateHandler

func (p *Plugin[T]) AddUpdateHandler(t tgapi.UpdateType, handler CommandExecutor[T]) *Plugin[T]

AddUpdateHandler registers a handler for a non-command update type. Message, channel post, and callback query updates stay on the command/payload flow.

func (*Plugin[T]) Close

func (p *Plugin[T]) Close() error

Close releases plugin-owned resources such as its logger and optional OnClose callback.

Only loggers created by the bot during registration are closed. A logger supplied via SetLogger remains the caller's responsibility — the framework never closes a logger it does not own.

func (*Plugin[T]) Command

func (p *Plugin[T]) Command(command string, exec CommandExecutor[T], args ...CommandArg) *Command[T]

Command creates and immediately adds a new command to the plugin. Returns the created command for further configuration.

func (*Plugin[T]) CommandGroup

func (p *Plugin[T]) CommandGroup(prefix string, groupFunc func(group *CommandGroup[T])) *Plugin[T]

CommandGroup configures and registers a prefixed command group.

func (*Plugin[T]) Payload

func (p *Plugin[T]) Payload(command string, exec CommandExecutor[T], args ...CommandArg) *Command[T]

Payload creates and immediately adds a new payload command to the plugin. Returns the created payload command for further configuration.

func (*Plugin[T]) RemoveLogger

func (p *Plugin[T]) RemoveLogger() *Plugin[T]

RemoveLogger clears the custom logger for this plugin.

Call this before Bot.AddPlugins. If the plugin is already registered, changing the original *Plugin does not update the Bot's internal copy.

func (*Plugin[T]) Scene

func (p *Plugin[T]) Scene(name string) *Scene[T]

Scene creates, registers, and returns a new scene owned by the plugin.

func (*Plugin[T]) SetLogger

func (p *Plugin[T]) SetLogger(l *sneklog.Logger) *Plugin[T]

SetLogger sets the logger used for this plugin's handlers.

Call this before Bot.AddPlugins. If the plugin is already registered, changing the original *Plugin does not update the Bot's internal copy.

func (*Plugin[T]) SetMessageFallback

func (p *Plugin[T]) SetMessageFallback(handler CommandExecutor[T]) *Plugin[T]

SetMessageFallback registers a fallback handler for messages that do not match a command.

func (*Plugin[T]) SetOnClose

func (p *Plugin[T]) SetOnClose(f func() error) *Plugin[T]

SetOnClose registers a callback invoked from Plugin.Close after the plugin logger is closed.

Call this before Bot.AddPlugins. If the plugin is already registered, changing the original *Plugin does not update the Bot's internal copy.

func (*Plugin[T]) SkipCommandAutoGen

func (p *Plugin[T]) SkipCommandAutoGen() *Plugin[T]

SkipCommandAutoGen marks the entire plugin to be excluded from auto-generated help menus.

func (*Plugin[T]) UsePolicy

func (p *Plugin[T]) UsePolicy(name string, policy Policy[T]) *Plugin[T]

UsePolicy registers a Policy as plugin middleware for all plugin handlers.

type Policy

type Policy[T AppData] func(ctx *MessageContext, data T) error

Policy defines a reusable authorization rule for the current update context.

func AllPolicies

func AllPolicies[T AppData](policies ...Policy[T]) Policy[T]

AllPolicies composes policies that all must succeed.

func AnyPolicy

func AnyPolicy[T AppData](policies ...Policy[T]) Policy[T]

AnyPolicy composes policies where at least one must succeed.

func NotPolicy

func NotPolicy[T AppData](policy Policy[T]) Policy[T]

NotPolicy inverts a policy deny result while preserving internal failures.

func RequireBotAdmin

func RequireBotAdmin[T AppData]() Policy[T]

RequireBotAdmin allows execution only when the bot is an admin in the chat.

func RequireCallbackFromUser

func RequireCallbackFromUser[T AppData]() Policy[T]

RequireCallbackFromUser allows execution only for callback queries sent by non-bot users.

func RequireChatAdmin

func RequireChatAdmin[T AppData]() Policy[T]

RequireChatAdmin allows execution only for chat administrators or owners.

func RequireChatCreator

func RequireChatCreator[T AppData]() Policy[T]

RequireChatCreator allows execution only for the chat owner.

func RequireGroupChat

func RequireGroupChat[T AppData]() Policy[T]

RequireGroupChat allows execution only in group or supergroup chats.

func RequirePrivateChat

func RequirePrivateChat[T AppData]() Policy[T]

RequirePrivateChat allows execution only in private chats.

func RequireSupergroupChat

func RequireSupergroupChat[T AppData]() Policy[T]

RequireSupergroupChat allows execution only in supergroup chats.

type PolicyCheckedEvent

type PolicyCheckedEvent struct {
	// Name identifies the evaluated policy.
	Name string
	// Plugin names the plugin that requested the policy check.
	Plugin string
	// FromID identifies the evaluated user when available.
	FromID int64
	// ChatID identifies the evaluated chat when available.
	ChatID int64
	// Passed reports whether the policy accepted the context.
	Passed bool
	// Err is the policy evaluation error, if any.
	Err error
	// Internal reports whether evaluation failed internally rather than denying access.
	Internal bool
}

PolicyCheckedEvent describes the result of a policy evaluation.

type PollingRetryEvent

type PollingRetryEvent struct {
	// Attempt is the one-based retry number.
	Attempt int
	// Delay is the time before the next polling attempt.
	Delay time.Duration
	// Err is the polling error that triggered the retry.
	Err error
}

PollingRetryEvent describes a polling retry after a failed getUpdates call.

type RandomDraftIDGenerator

type RandomDraftIDGenerator struct{}

RandomDraftIDGenerator generates draft IDs using math/rand/v2.

Suitable for general use thanks to the wide 64-bit value space. Not suitable for security-sensitive purposes — use crypto/rand if unpredictability against an adversary matters.

func (*RandomDraftIDGenerator) Next

func (g *RandomDraftIDGenerator) Next() uint64

Next returns a random 64-bit unsigned integer.

type Runner

type Runner[T AppData] struct {
	// contains filtered or unexported fields
}

Runner represents a configurable background or one-time task to be executed by a Bot.

Runners are configured using builder methods Async and Every. Once the bot's runtime has started executing the runner, it should not be modified.

Execution semantics:

  • every=0, async=true: Run once in a goroutine (non-blocking, default).
  • every=0, async=false: Run once synchronously (blocks runtime startup).
  • every>0, async=true: Run repeatedly in a goroutine with the given interval.
  • every>0, async=false: Invalid configuration — skipped with a warning.

func NewRunner

func NewRunner[T AppData](name string, fn RunnerFn[T]) Runner[T]

NewRunner creates a new Runner with the given name and function.

The default configuration is async=true and every=0, i.e. a one-shot goroutine that fires once when the bot runtime starts. Use Async and Every to customize this. Do not call builder methods concurrently or after the bot runtime has begun executing runners.

func (Runner[T]) Async

func (r Runner[T]) Async(async bool) Runner[T]

Async sets whether the runner executes synchronously or asynchronously. If true, the runner runs in a goroutine (non-blocking). If false, the runner blocks the caller during execution.

Note: periodic runners (Every > 0) require async=true and are skipped with a warning when async=false.

func (Runner[T]) Every

func (r Runner[T]) Every(timeout time.Duration) Runner[T]

Every sets the interval between repeated executions of a periodic runner.

A zero value (the default) keeps the runner one-shot. A positive value schedules the runner to fire repeatedly with the given interval and requires async=true; periodic sync runners are skipped with a warning.

type RunnerFinishedEvent

type RunnerFinishedEvent struct {
	// Name identifies the runner.
	Name string
	// Duration is the callback execution time.
	Duration time.Duration
	// Err is the callback error or recovered panic.
	Err error
}

RunnerFinishedEvent describes a completed background runner execution.

type RunnerFn

type RunnerFn[T AppData] func(context.Context, *Bot[T]) error

RunnerFn is a cancelable task executed by a Bot runtime.

type Scene

type Scene[T any] struct {
	// contains filtered or unexported fields
}

Scene defines a multi-step conversational flow.

func NewScene

func NewScene[T any](name string) *Scene[T]

NewScene creates a new scene with user-chat scope by default.

func (*Scene[T]) OnCommand

func (s *Scene[T]) OnCommand(cmd string, handler SceneHandler[T]) *Scene[T]

OnCommand registers a command handler active while the scene is running.

func (*Scene[T]) OnMessage

func (s *Scene[T]) OnMessage(handler SceneHandler[T]) *Scene[T]

OnMessage registers a fallback handler used when no scene command or step matches.

func (*Scene[T]) OnPayload

func (s *Scene[T]) OnPayload(cmd string, handler SceneHandler[T]) *Scene[T]

OnPayload registers a callback payload handler active while the scene is running.

func (*Scene[T]) OnStep

func (s *Scene[T]) OnStep(step string, handler SceneHandler[T]) *Scene[T]

OnStep registers a handler for a named scene step.

func (*Scene[T]) SetEntry

func (s *Scene[T]) SetEntry(step string) *Scene[T]

SetEntry sets the initial step entered by MessageContext.EnterScene.

func (*Scene[T]) SetScope

func (s *Scene[T]) SetScope(scope SceneScope) *Scene[T]

SetScope changes how scene sessions are keyed and shared.

type SceneAction

type SceneAction int

SceneAction controls how the bot updates scene state after a handler returns.

const (
	// SceneActionStay keeps the current scene and step active.
	SceneActionStay SceneAction = iota
	// SceneActionNext moves the session to another named step.
	SceneActionNext
	// SceneActionExit removes the current scene session.
	SceneActionExit
	// SceneActionPass lets normal bot routing continue after the scene handler.
	SceneActionPass
)

type SceneContext

type SceneContext struct {
	*MessageContext
	// contains filtered or unexported fields
}

SceneContext wraps MessageContext with scene session state for scene handlers.

func (*SceneContext) BindData

func (ctx *SceneContext) BindData(v any) error

BindData unmarshals the current scene session payload into v.

func (*SceneContext) Exit

func (ctx *SceneContext) Exit() SceneResult

Exit leaves the current scene.

func (*SceneContext) Next

func (ctx *SceneContext) Next(step string) SceneResult

Next advances the current scene to step.

func (*SceneContext) Pass

func (ctx *SceneContext) Pass() SceneResult

Pass stops scene handling and lets normal routing continue.

func (*SceneContext) SaveData

func (ctx *SceneContext) SaveData(v any) error

SaveData marshals v and stores it in the current scene session payload.

func (*SceneContext) Stay

func (ctx *SceneContext) Stay() SceneResult

Stay keeps the current scene step active.

type SceneHandler

type SceneHandler[T any] func(ctx *SceneContext, db T) (SceneResult, error)

SceneHandler handles a scene step, scene command, or fallback message.

type SceneResult

type SceneResult struct {
	// Action controls the scene state transition.
	Action SceneAction
	// Next names the destination step for SceneActionNext.
	Next string
}

SceneResult describes how scene execution should proceed after a handler returns.

type SceneScope

type SceneScope int

SceneScope defines how scene sessions are keyed.

const (
	// SceneScopeUser shares a scene across all chats for one user.
	SceneScopeUser SceneScope = iota
	// SceneScopeChat shares a scene across all users in one chat.
	SceneScopeChat
	// SceneScopeUserChat isolates a scene per user-chat pair.
	SceneScopeUserChat
)

type SceneSession

type SceneSession struct {
	// Scene is the registered scene name for the active session.
	Scene string
	// Step is the current step name inside the active scene.
	Step string
	// contains filtered or unexported fields
}

SceneSession stores the active scene state for one session key.

func (*SceneSession) BindData

func (s *SceneSession) BindData(v any) error

BindData unmarshals the stored JSON payload into v.

func (*SceneSession) ClearData

func (s *SceneSession) ClearData()

ClearData removes any stored session data.

func (*SceneSession) GetData

func (s *SceneSession) GetData() []byte

GetData returns the raw session data payload.

func (*SceneSession) HasData

func (s *SceneSession) HasData() bool

HasData reports whether the session has a non-empty data payload.

func (*SceneSession) SaveData

func (s *SceneSession) SaveData(v any) error

SaveData marshals v as JSON and stores it in the session.

func (*SceneSession) SetData

func (s *SceneSession) SetData(data []byte)

SetData stores arbitrary opaque session data.

type SceneTransitionEvent

type SceneTransitionEvent struct {
	// Plugin names the plugin that owns the scene.
	Plugin string
	// Scene names the transitioning scene.
	Scene string
	// From is the previous scene step.
	From string
	// To is the resulting scene step.
	To string
	// Action identifies the requested state transition.
	Action SceneAction
	// FromID identifies the session user when available.
	FromID int64
	// ChatID identifies the session chat when available.
	ChatID int64
}

SceneTransitionEvent describes a scene state transition.

type SessionStore

type SessionStore interface {
	Get(key string) (SceneSession, error)
	Set(key string, session SceneSession) error
	Delete(key string) error
}

SessionStore persists scene sessions by key.

type UpdateHandledEvent

type UpdateHandledEvent struct {
	// UpdateID identifies the Telegram update.
	UpdateID int
	// UpdateType identifies the normalized update kind.
	UpdateType tgapi.UpdateType
	// FromID identifies the originating user when available.
	FromID int64
	// ChatID identifies the originating chat when available.
	ChatID int64
	// Duration is the total framework handling time.
	Duration time.Duration
	// Handled reports whether a registered path handled the update.
	Handled bool
}

UpdateHandledEvent describes a completed update execution path.

type UpdateReceivedEvent

type UpdateReceivedEvent struct {
	// UpdateID identifies the Telegram update.
	UpdateID int
	// UpdateType identifies the normalized update kind.
	UpdateType tgapi.UpdateType
	// FromID identifies the originating user when available.
	FromID int64
	// ChatID identifies the originating chat when available.
	ChatID int64
}

UpdateReceivedEvent describes an update entering the bot runtime.

Directories

Path Synopsis
Package tgfmt provides small helpers for Telegram text formatting.
Package tgfmt provides small helpers for Telegram text formatting.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL