Wiki
猫Table of Contents
- MessageContext
- The fields you will use first
- The helpers you will use most often
- Rich messages
- Markdown helpers
- Editing and deleting
- Callback-specific helpers
- Photos and captions
- Drafts
- Localization
- NewInlineKeyboard
- Sending chat actions
- Error handling inside handlers
- Common pitfalls
- ctx.Msg can be nil
- Answer(...) and Edit(...) are not interchangeable
- Long replies are explicit
- Markdown helpers require escaping
- Callback helpers only make sense in callback flow
- A practical example
- Where to go next
MessageContext
Russian version: MessageContext-RU
MessageContext is the runtime object passed into command handlers, payload handlers, middleware, and update handlers.
It gives you access to:
- the incoming update
- the current message and sender
- parsed command or payload arguments
- reply, edit, delete, callback, draft, and localization helpers
If you write handlers, MessageContext is the API surface you will use most often.
For the full routing and field-guarantee matrix by update kind, see Update-Routing-Model.
The fields you will use first
Text
ctx.Text is the parsed text payload after the command name.
Example:
- incoming message:
/echo hello world - command:
echo ctx.Text == "hello world"
This is usually the easiest field to use for simple commands.
Args
ctx.Args is the tokenized version of ctx.Text.
Example:
ctx.Text == "hello world"ctx.Args == []string{"hello", "world"}
Use this when you want simple positional arguments.
Msg
ctx.Msg points to the current Telegram message when the current update has one.
You will often use it for:
- chat ID
- thread ID
- original message metadata
Not every update has a message. For some non-message update types, ctx.Msg is nil.
From and FromID
ctx.From is the sender user when one exists.
ctx.FromID is the same sender’s numeric ID, extracted for convenience.
Use FromID when you only need the identifier and do not want to keep checking for nil.
The helpers you will use most often
Answer
Use Answer(...) for the normal “reply with text” case.
func start(ctx *laniakea.MessageContext, db laniakea.NoData) error {
ctx.Answer("Welcome")
return nil
}
This is the default high-level reply helper for plain text.
AnswerLong
Use AnswerLong(...) when plain text may exceed Telegram’s message limit.
func help(ctx *laniakea.MessageContext, db laniakea.NoData) error {
ctx.AnswerLong(buildLargeHelpText())
return nil
}
Important:
- this is explicit on purpose
- normal
Answer(...)keeps single-message semantics AnswerLong(...)may send multiple messages
Keyboard
Use Keyboard(...) when you want to send a message with an inline keyboard.
func menu(ctx *laniakea.MessageContext, db laniakea.NoData) error {
kb := ctx.NewInlineKeyboard(2).
AddCallbackButton("Profile", "profile.open").
AddCallbackButton("Settings", "settings.open")
ctx.Keyboard("Choose an action", kb)
return nil
}
KeyboardLong
Use KeyboardLong(...) when plain text may be too long and the keyboard should stay attached to the final chunk.
This is useful for:
- long help text
- generated summaries
- reports with an action button at the end
Rich messages
Use RichAnswer(...) and RichAnswerKeyboard(...) to validate and send Bot API 10.2 input rich blocks built with tgrich:
ctx.RichAnswer(
tgrich.H1(tgrich.Text("Report")),
tgrich.P(tgrich.Bold(tgrich.Text("all systems go"))),
)
See Rich-Messages for all constructors, media uploads, validation, and the receive side.
Markdown helpers
Use:
AnswerMarkdown(...)KeyboardMarkdown(...)EditCallbackMarkdown(...)- other
...Markdownvariants
Important rule:
- user input must be escaped before passing it into MarkdownV2 helpers
Use laniakea.EscapeMarkdownV2(...) for this.
Example:
func whoami(ctx *laniakea.MessageContext, db laniakea.NoData) error {
name := laniakea.EscapeMarkdownV2(ctx.From.FirstName)
ctx.AnswerMarkdown("*User:* " + name)
return nil
}
Editing and deleting
Once you already have an AnswerMessage, you can edit or delete it.
Example:
func slowTask(ctx *laniakea.MessageContext, db laniakea.NoData) error {
msg := ctx.Answer("Working...")
if msg == nil {
return nil
}
// do work
msg.Edit("Done")
return nil
}
Available patterns include:
Edit(...)EditMarkdown(...)EditCaption(...)Delete()
These methods assume a single concrete message target.
That is why multi-message helpers like AnswerLong(...) are separate APIs.
Callback-specific helpers
When handling inline button callbacks, these helpers are especially useful.
EditCallback
Edits the callback-linked message.
func approve(ctx *laniakea.MessageContext, db laniakea.NoData) error {
ctx.EditCallback("Approved", nil)
return nil
}
AnswerCbQuery
Acknowledges the callback query itself.
Use:
AnswerCbQuery()for empty acknowledgementAnswerCbQueryText(...)for a short noticeAnswerCbQueryAlert(...)for a visible alertAnswerCbQueryUrl(...)for redirect behavior
Example:
func approve(ctx *laniakea.MessageContext, db laniakea.NoData) error {
ctx.AnswerCbQueryText("Saved")
ctx.EditCallback("Saved", nil)
return nil
}
CallbackDelete
Deletes the message that triggered the callback.
Use this only when that behavior is really clear to the user.
Photos and captions
Use:
AnswerPhoto(...)AnswerPhotoKeyboard(...)AnswerPhotoMarkdown(...)
These helpers are for sending a photo with an optional caption.
Caption rules differ from normal message text:
- captions have a smaller Telegram limit
- caption editing uses the caption-specific edit helpers
Drafts
MessageContext also exposes draft creation helpers:
NewDraft()NewDraftMarkdown()
Drafts are useful when:
- a response is built incrementally
- you want to stage text before flushing
- the workflow benefits from draft IDs or batching behavior
For ordinary one-shot replies, Answer(...) is simpler.
See Drafts for the full model.
Localization
Use:
ctx.Translate("some.key")
This looks up text using the current user’s language when available and falls back to the configured default language.
Example:
func ping(ctx *laniakea.MessageContext, db laniakea.NoData) error {
ctx.Answer(ctx.Translate("ping.answer"))
return nil
}
See Localization for setup and dictionary structure.
NewInlineKeyboard
The recommended way to build keyboards inside handlers is:
kb := ctx.NewInlineKeyboard(2)
This is important because it inherits the bot’s default payload configuration automatically.
A keyboard can still override its own payload type locally if needed.
See Inline-Keyboards-and-Payloads.
Sending chat actions
Use SendAction(...) to show activity like typing or uploading.
Example:
func report(ctx *laniakea.MessageContext, db *App) error {
ctx.SendAction(tgapi.ChatActionTyping)
text, err := db.BuildReport(ctx.FromID)
if err != nil {
return err
}
ctx.AnswerLong(text)
return nil
}
This is especially useful for slower handlers.
Error handling inside handlers
A common pattern is:
func profile(ctx *laniakea.MessageContext, db *App) error {
user, err := db.LoadUser(ctx.FromID)
if err != nil {
return err
}
ctx.Answer(user.Name)
return nil
}
You usually do not call ctx.Error(...) directly in normal handlers unless you intentionally want immediate explicit error messaging there.
The more idiomatic pattern is:
- return
error - let the bot’s centralized error flow handle it
Common pitfalls
ctx.Msg can be nil
Do not assume every update has a message object.
This especially matters in custom update handlers.
Answer(...) and Edit(...) are not interchangeable
Answer(...) creates a new message.
Edit(...) changes an existing one and requires a valid target.
Long replies are explicit
If the text may exceed Telegram’s normal message limit, use AnswerLong(...) or KeyboardLong(...).
Markdown helpers require escaping
Do not pass raw user input to MarkdownV2 methods without escaping.
Callback helpers only make sense in callback flow
Methods like EditCallback(...) and AnswerCbQueryText(...) depend on callback-specific context.
A practical example
func settings(ctx *laniakea.MessageContext, db *App) error {
kb := ctx.NewInlineKeyboard(1).
AddCallbackButton("Enable notifications", "settings.notifications.enable").
AddCallbackButton("Disable notifications", "settings.notifications.disable")
ctx.Keyboard("Notification settings", kb)
return nil
}
This example uses:
- a handler
ctx.NewInlineKeyboard(...)- callback payload routing
ctx.Keyboard(...)
That is a typical Laniakea interaction pattern.
Where to go next
- Read Inline-Keyboards-and-Payloads if you are building button-driven flows.
- Read Commands-and-Plugins if you want the full handler registration model.
- Read Drafts if you need staged or multi-step message assembly.
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