telega/reply

reply provides a convenient way to send messages to the active chat. It uses the Context object to access the chat ID and other necessary information.

Ephemeral messages (Bot API 10.3)

In a group chat a message can be addressed to a single user — nobody else sees it. with_ephemeral_text and with_ephemeral_markup aim at the user of the current update, wiring callback_query_id in when that update is a callback query, so the message appears under the pressed button:

let assert Ok(_) = reply.with_ephemeral_text(ctx, "Booked — only you see this")

with_ephemeral takes explicit parameters instead, built with ephemeral_parameters or types.new_ephemeral_message_parameters:

reply.with_ephemeral(
  ctx:,
  text: "Shown in place of the original message",
  parameters: types.EphemeralMessageParameters(
    ..reply.ephemeral_parameters(ctx),
    replace_callback_query_message: Some(True),
  ),
)

Every send* parameter record carries the same optional ephemeral_message_parameters field, so photos, videos, stickers and the rest can be ephemeral too:

api.send_photo(
  client,
  parameters: types.SendPhotoParameters(
    ..parameters,
    ephemeral_message_parameters: Some(reply.ephemeral_parameters(ctx)),
  ),
)

The sent Message carries an ephemeral_message_id; pass it to api.edit_ephemeral_message_* / api.delete_ephemeral_message to change or remove the message later. Under handle_bot_with_reply the first eligible send of an update is answered through the webhook response itself and comes back as a stub Message without that id — wrap such a call in webhook_reply.without_claim when you need the real one.

Values

pub fn answer_alert(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(Bool, error.TelegaError)

Answer the callback query of this update with a modal alert the user has to dismiss. Use it for refusals and mistakes; answer_toast for everything else.

pub fn answer_callback_query(
  ctx ctx: bot.Context(session, error, dependencies),
  parameters parameters: types.AnswerCallbackQueryParameters,
) -> Result(Bool, error.TelegaError)

Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned.

Official reference: https://core.telegram.org/bots/api#answercallbackquery

pub fn answer_quietly(
  ctx ctx: bot.Context(session, error, dependencies),
) -> Result(Bool, error.TelegaError)

Answer the callback query of this update without showing anything — just stop the button’s spinner.

pub fn answer_toast(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(Bool, error.TelegaError)

Answer the callback query of this update with a toast — the notification strip at the top of the chat.

Every callback query must be answered, or the client keeps showing a spinner on the button for a minute. Errors when the update is not a callback query.

pub fn edit_callback_markup(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
  markup markup: types.InlineKeyboardMarkup,
) -> Result(types.Message, error.TelegaError)

edit_callback_message that also replaces the inline keyboard — the usual way to advance a menu in place.

pub fn edit_callback_message(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(types.Message, error.TelegaError)

Replace the text of the message the pressed button belongs to.

Works for an inline-mode message too (inline_message_id), which is why it returns whatever editMessageText returned rather than assuming a chat message. Errors when the update is not a callback query.

pub fn edit_text(
  ctx ctx: bot.Context(session, error, dependencies),
  parameters parameters: types.EditMessageTextParameters,
) -> Result(types.Message, error.TelegaError)

Use this method to edit text and game messages. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.

If parameters.parse_mode is None, the client’s default parse mode (set via client.set_default_parse_mode) is used.

Official reference: https://core.telegram.org/bots/api#editmessagetext

pub fn edit_text_formatted(
  ctx ctx: bot.Context(session, error, dependencies),
  message_id message_id: Int,
  formatted formatted: format.FormattedText,
) -> Result(types.Message, error.TelegaError)

Use this method to edit formatted text messages.

Official reference: https://core.telegram.org/bots/api#editmessagetext

pub fn ephemeral_parameters(
  ctx ctx: bot.Context(session, error, dependencies),
) -> types.EphemeralMessageParameters

Ephemeral parameters aimed at the user of the current update (Bot API 10.3).

When the update is a callback query, its id is wired in, so Telegram can attach the ephemeral message to the pressed button. Adjust the result with a record update to show the message in place of the original one:

types.EphemeralMessageParameters(
  ..reply.ephemeral_parameters(ctx),
  replace_callback_query_message: Some(True),
)
pub fn forward(
  ctx ctx: bot.Context(session, error, dependencies),
  parameters parameters: types.ForwardMessageParameters,
) -> Result(types.Message, error.TelegaError)

Use this method to forward messages of any kind. Service messages and messages with protected content can’t be forwarded. On success, the sent Message is returned.

Official reference: https://core.telegram.org/bots/api#forwardmessage

pub fn quote(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(types.Message, error.TelegaError)

Reply to the message that triggered this update, quoting it in the client.

An update that is not about a message (a callback query, an inline query) has nothing to quote, so the text is sent as a plain message.

pub fn remove_keyboard(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(types.Message, error.TelegaError)

Send text and take the custom (reply) keyboard off the user’s screen.

pub fn stream_into(
  ctx ctx: bot.Context(session, error, dependencies),
  message_id message_id: Int,
  chunks chunks: yielder.Yielder(String),
  every_ms every_ms: Int,
) -> Result(types.Message, error.TelegaError)

stream_text into a message that already exists — the “Thinking…” placeholder you sent before calling the model, so the user sees something immediately.

The message must be one the bot can edit in this chat. Under handle_bot_with_reply, send that placeholder inside webhook_reply.without_claim — a claimed send returns a stub with message_id: -1, and there is nothing to write into.

pub fn stream_text(
  ctx ctx: bot.Context(session, error, dependencies),
  chunks chunks: yielder.Yielder(String),
  every_ms every_ms: Int,
) -> Result(types.Message, error.TelegaError)

Stream text into a single message that is edited as it grows.

chunks is pulled to exhaustion — token by token from an LLM, line by line from a long job. The first non-empty chunk sends a message; after that the message is edited at most once per every_ms, and a final edit drops the cursor and shows the complete text. A stream of 400 tokens costs a handful of API calls, not 400.

use ctx <- telega.log_context(ctx, "answer")
let assert Ok(_) = reply.stream_text(ctx, llm_tokens(prompt), every_ms: 700)

Pick every_ms above Telegram’s per-chat pacing — 700 ms is a good default, and below ~500 ms in a private chat the edits queue up behind the rate limiter and the animation stutters.

Edits that fail while the stream runs are swallowed: a flood wait halfway through must not cost the user the answer, and the next flush carries the text the failed one would have. The first send and the final edit are not — their failure is returned. An empty stream sends nothing and is an error, since Telegram has no empty message to return.

pub fn text(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(
  bot.Context(session, error, dependencies),
  error.TelegaError,
)

Send text to the active chat and hand the context back unchanged.

The shape a handler wants: reply.text is the handler body, with no let assert and no use _ <- result.try around a Message nobody reads.

fn handle_text(ctx, text) {
  reply.text(ctx, "You said: " <> text)
}

The failure is a TelegaError, so a router using this shortcut has TelegaError as its error type. A bot with an error type of its own keeps using with_text and maps the error itself.

pub fn with_dice(
  ctx ctx: bot.Context(session, error, dependencies),
  parameters parameters: option.Option(types.SendDiceParameters),
) -> Result(types.Message, error.TelegaError)

Use this method to send an animated emoji that will display a random value.

Official reference: https://core.telegram.org/bots/api#senddice

pub fn with_entities(
  ctx ctx: bot.Context(session, error, dependencies),
  formatted formatted: format.FormattedText,
) -> Result(types.Message, error.TelegaError)

Send text with formatting described positionally, as MessageEntitys, instead of through a parse mode.

Nothing is escaped, because nothing is special: a user-supplied string carrying *, _ or <b> arrives exactly as typed. See format.entities.

format.build()
|> format.text("Result: ")
|> format.bold_text(whatever_the_user_typed)
|> format.to_formatted
|> reply.with_entities(ctx, _)
pub fn with_ephemeral(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
  parameters ephemeral_message_parameters: types.EphemeralMessageParameters,
) -> Result(types.Message, error.TelegaError)

Use this method to send an ephemeral text message with explicit EphemeralMessageParameters (Bot API 10.3).

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_ephemeral_markup(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
  markup reply_markup: types.SendMessageReplyMarkupParameters,
) -> Result(types.Message, error.TelegaError)

Use this method to send an ephemeral text message with keyboard markup (Bot API 10.3).

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_ephemeral_text(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(types.Message, error.TelegaError)

Use this method to send a text message that only one user of the chat sees (Bot API 10.3). Ephemeral messages work in group chats only; in private chats use with_text.

The message is aimed at the user of the current update — see ephemeral_parameters for finer control, and pass the result to with_ephemeral.

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_file_link(
  ctx ctx: bot.Context(session, error, dependencies),
  file_id file_id: String,
) -> Result(String, error.TelegaError)

Get download link for the file.

The link embeds the bot token: it grants access to every file of the bot, so keep it server-side instead of sending it to a user.

pub fn with_formatted(
  ctx ctx: bot.Context(session, error, dependencies),
  formatted formatted: format.FormattedText,
) -> Result(types.Message, error.TelegaError)

Use this method to send formatted text messages.

Example

let formatted = format.build()
  |> format.bold_text("Important!")
  |> format.to_formatted()
reply.with_formatted(ctx, formatted)

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_formatted_markup(
  ctx ctx: bot.Context(session, error, dependencies),
  formatted formatted: format.FormattedText,
  markup reply_markup: types.SendMessageReplyMarkupParameters,
) -> Result(types.Message, error.TelegaError)

Use this method to send formatted text messages with keyboard markup.

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_html(
  ctx ctx: bot.Context(session, error, dependencies),
  html html: String,
) -> Result(types.Message, error.TelegaError)

Use this method to send HTML formatted text messages.

Example

let html = format.bold("Hello") <> " " <> format.italic("World")
reply.with_html(ctx, html)

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_invoice(
  ctx ctx: bot.Context(session, error, dependencies),
  title title: String,
  description description: String,
  payload payload: String,
  currency currency: String,
  prices prices: List(#(String, Int)),
) -> Result(types.Message, error.TelegaError)

Use this method to send an invoice.

Official reference: https://core.telegram.org/bots/api#sendinvoice

pub fn with_markdown(
  ctx ctx: bot.Context(session, error, dependencies),
  markdown markdown: String,
) -> Result(types.Message, error.TelegaError)

Use this method to send Markdown formatted text messages.

Example

reply.with_markdown(ctx, "*Bold* _Italic_")

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_markdown_v2(
  ctx ctx: bot.Context(session, error, dependencies),
  markdown markdown: String,
) -> Result(types.Message, error.TelegaError)

Use this method to send MarkdownV2 formatted text messages.

Example

reply.with_markdown_v2(ctx, "*Bold* _Italic_ __Underline__")

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_markup(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
  markup reply_markup: types.SendMessageReplyMarkupParameters,
) -> Result(types.Message, error.TelegaError)

Use this method to send text messages with keyboard markup.

Uses the client’s default parse mode if one is configured via client.set_default_parse_mode.

Official reference: https://core.telegram.org/bots/api#sendmessage

pub fn with_media_group(
  ctx ctx: bot.Context(session, error, dependencies),
  media media: List(types.InputMedia),
) -> Result(List(types.Message), error.TelegaError)

Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. Returns a list of messages that were sent.

Example

let media_group = media_group.new()
  |> media_group.add_photo("https://example.com/photo1.jpg", None)
  |> media_group.add_photo("https://example.com/photo2.jpg", Some(
    media_group.PhotoOptions(
      caption: Some("Second photo"),
      parse_mode: Some("Markdown"),
      ..media_group.default_photo_options()
    )
  ))
  |> media_group.build()

reply.with_media_group(ctx, media_group)

Official reference: https://core.telegram.org/bots/api#sendmediagroup

pub fn with_paid_media(
  ctx ctx: bot.Context(session, error, dependencies),
  star_count star_count: Int,
  media media: List(types.InputPaidMedia),
) -> Result(types.Message, error.TelegaError)

Use this method to send paid media — photos and videos that the user must pay Telegram Stars to unlock.

Uses the client’s default parse mode if one is configured via client.set_default_parse_mode.

Example

reply.with_paid_media(ctx, star_count: 10, media: [
  types.InputPaidMediaPhotoInputPaidMedia(types.InputPaidMediaPhoto(
    type_: "photo",
    media: "https://example.com/photo.jpg",
  )),
])

Official reference: https://core.telegram.org/bots/api#sendpaidmedia

pub fn with_photo(
  ctx ctx: bot.Context(session, error, dependencies),
  photo photo: types.FileOrString,
  caption caption: option.Option(String),
) -> Result(types.Message, error.TelegaError)

Use this method to send a photo — by file_id, URL, or upload.

The caption uses the client’s default parse mode if one is configured via client.set_default_parse_mode.

Example

reply.with_photo(ctx, types.StringV("https://example.com/cat.jpg"), Some("A cat"))

Official reference: https://core.telegram.org/bots/api#sendphoto

pub fn with_photo_bytes(
  ctx ctx: bot.Context(session, error, dependencies),
  bytes bytes: BitArray,
  filename filename: String,
  content_type content_type: String,
  caption caption: option.Option(String),
) -> Result(types.Message, error.TelegaError)

Send a photo to the active chat by uploading raw bytes (multipart/form-data) — for art the bot holds in memory / object storage but has never sent before, so there is no file_id yet. The returned Message’s largest PhotoSize carries the new file_id; cache it and reuse with_photo (a plain JSON send) next time.

If caption is Some, the client’s default parse mode (if configured) is applied, matching with_photo.

pub fn with_poll(
  ctx ctx: bot.Context(session, error, dependencies),
  question question: String,
  options options: List(String),
) -> Result(types.Message, error.TelegaError)

Use this method to send a native poll.

Official reference: https://core.telegram.org/bots/api#sendpoll

pub fn with_sticker(
  ctx ctx: bot.Context(session, error, dependencies),
  sticker sticker: types.FileOrString,
) -> Result(types.Message, error.TelegaError)

Use this method to send a sticker.

Official reference: https://core.telegram.org/bots/api#sendsticker

pub fn with_text(
  ctx ctx: bot.Context(session, error, dependencies),
  text text: String,
) -> Result(types.Message, error.TelegaError)

Use this method to send text messages.

Uses the client’s default parse mode if one is configured via client.set_default_parse_mode.

Official reference: https://core.telegram.org/bots/api#sendmessage

Search Document