Wiki
猫Recipes
Russian version: Recipes-RU
This page collects short, task-focused examples for common bot patterns. Each recipe is intentionally small and copy-friendly, and you can combine them with the deeper pages such as Commands-and-Plugins, Middleware, and Inline-Keyboards-and-Payloads.
Admin-only command
Use plugin middleware when several commands share the same access rule.
type App struct{}
func (a *App) IsAdmin(userID int64) bool { return userID == 42 }
admin := laniakea.NewPlugin[*App]("admin")
admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MessageContext, app *App) bool {
return ctx.From != nil && app.IsAdmin(ctx.From.ID)
}))
admin.NewCommand(func(ctx *laniakea.MessageContext, app *App) error {
ctx.Answer("Admin command executed")
return nil
}, "reload")
Callback button flow
Use a payload handler for inline keyboard callbacks.
menu := laniakea.NewPlugin[laniakea.NoData]("menu")
menu.NewCommand(func(ctx *laniakea.MessageContext, db laniakea.NoData) error {
kb := ctx.NewInlineKeyboard(1)
kb.AddCallbackButton("Open settings", "settings")
ctx.Keyboard("Choose an action", kb)
return nil
}, "menu")
menu.NewPayload(func(ctx *laniakea.MessageContext, db laniakea.NoData) error {
ctx.EditCallback("Settings screen", nil)
return nil
}, "settings")
Long plain-text reply
Use AnswerLong(...) when you want explicit splitting into multiple safe Telegram messages.
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
report := buildLargePlainTextReport()
ctx.AnswerLong(report)
return nil
}, "report")
If you need an inline keyboard on the final chunk, use KeyboardLong(...).
Localized command
Attach an L10n store to the bot and use ctx.Translate(...) inside handlers.
l10n := laniakea.NewL10n("en").
AddDictEntry("greeting", laniakea.DictEntry{
"en": "Hello",
"ru": "Privet",
})
bot.SetL10n(l10n)
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
ctx.Answer(ctx.Translate("greeting"))
return nil
}, "start")
Non-command update handler
Use AddUpdateHandler(...) for Telegram update types that are outside the command and payload flow.
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MessageContext, db *App) error {
if ctx.From != nil {
ctx.Logger.Infoln("inline query from", ctx.From.ID)
}
return nil
})
This is usually cleaner than forcing non-command traffic through a command parser.
Draft-based staged reply
Use drafts when you want to build a reply progressively and publish it once at the end.
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
draft := ctx.NewDraft()
if draft == nil {
return nil
}
if err := draft.Push("Collecting data...\n"); err != nil {
return err
}
if err := draft.Push("Formatting output...\n"); err != nil {
return err
}
return draft.Flush()
}, "build")
File upload with tgapi
Use the higher-level handler flow for routing, but drop down to tgapi uploader methods when you need multipart upload behavior.
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
uploader := tgapi.NewUploader(ctx.Api)
defer uploader.Close()
_, err := uploader.SendPhoto(tgapi.UploadPhoto{
ChatID: ctx.Msg.Chat.ID,
}, tgapi.NewUploaderFile("report.jpg", []byte("hello")))
return err
}, "upload")
If your exact uploader call differs, keep the general rule in mind: handler routing can stay high-level even when the send path needs tgapi.
Strict payload mode
Enable strict payload decoding if you want callback payload formats to be enforced consistently.
opts := (&laniakea.BotOpts{}).
SetToken("TOKEN").
SetStrictPayloadType(true)
Or after bot creation:
bot.SetStrictPayloadType(true)
This is most useful when you want payload-type mismatches to fail loudly instead of being decoded tolerantly.
Related pages
Navigation
Start here
Runtime and Architecture
- Bot-Lifecycle
- Webhook-Runtime
- Middleware
- Runners
- Error-Handling
- Logging
- Update-Routing-Model
- Policies
- Scenes
Interaction and Telegram API
- Inline-Keyboards-and-Payloads
- Auto-Generated-Commands
- Drafts
- Rich-Messages
- Localization
- Rate-Limiting
- tgapi-Overview