telega/dialog/render

The edit-or-send engine behind dialogs.

Keeps one live dialog message per chat: sends it the first time, edits it on every subsequent render. Media windows are supported by the strategy matrix below — the Bot API cannot turn a text message into a media one (or back), so a kind change recreates the message:

was \ becomestextmedia
— (no message)sendMessagesendPhoto/sendVideo/…
texteditMessageTextdelete (best effort) + send
media (any kind)delete (best effort) + sendeditMessageMedia

deleteMessage is best effort (messages older than 48 hours cannot be deleted): its failure is swallowed and the fresh message is sent anyway.

Also hosts the alert/toast helpers and the Bot API error classifiers (is_not_modified & co) — the latter are useful outside dialogs too.

Types

The kind of the live dialog message. The Bot API cannot edit a text message into a media one or back, so the engine tracks the kind to pick the right strategy from the matrix in the module doc. All media messages share one kind: editMessageMedia changes the file, the media type, and the caption in a single call.

pub type MessageKind {
  TextMessage
  MediaMessage
}

Constructors

  • TextMessage
  • MediaMessage

Why a window could not be rendered.

pub type RenderError {
  ApiError(error.TelegaError)
  InvalidButton(action_id: String, reason: String)
}

Constructors

  • ApiError(error.TelegaError)

    The Telegram API call failed (after the edit-fallback matrix).

  • InvalidButton(action_id: String, reason: String)

    A button is misconfigured: callback data over 64 bytes or a reserved : in an action id. This is a developer error and must be visible, not silently degraded.

Values

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

Show a modal alert to the user who pressed the button. Call from on_action before returning; the engine then skips its automatic answer_callback_query for this event.

pub fn build_markup(
  dialog_id dialog_id: String,
  window_id window_id: String,
  buttons buttons: List(List(types.DialogButton)),
) -> Result(
  option.Option(types.InlineKeyboardMarkup),
  RenderError,
)

Build the inline keyboard for a window, generating callback data by the dlg:<dialog>:<window>:<action>[:<arg>] scheme and validating the 64-byte limit and reserved characters per button.

pub fn callback_data(
  dialog_id dialog_id: String,
  window_id window_id: String,
  action_id action_id: String,
  arg arg: option.Option(String),
) -> String

The exact callback-data string for an action — the single place the dlg: scheme is constructed. Build-time validation measures budgets through this same function, so packing and validation can never diverge.

pub fn is_cant_be_edited(error error: error.TelegaError) -> Bool

The message exists but cannot be edited (e.g. not sent by the bot).

pub fn is_message_not_found(
  error error: error.TelegaError,
) -> Bool

The message to edit no longer exists (deleted by the user or too old).

pub fn is_not_modified(error error: error.TelegaError) -> Bool

The edit was a no-op: new content equals the current one. Safe to treat as success.

pub const max_callback_data_bytes: Int

Telegram’s callback-data byte limit; validation and packing share it.

pub fn pack_callback_data(
  dialog_id dialog_id: String,
  window_id window_id: String,
  action_id action_id: String,
  arg arg: option.Option(String),
) -> Result(String, RenderError)

Build callback data for an action, checking the reserved : in the action id and Telegram’s 64-byte limit. The w: action-id prefix is the widget namespace (w:<widget_id>:<cmd>) and is the only place : is allowed — the engine routes such presses to the widget’s on_event. The bare id "w" is rejected too: with a :-carrying arg it would be indistinguishable from a widget event on parse.

pub fn remove_keyboard(
  ctx ctx: bot.Context(session, error, dependencies),
  chat_id chat_id: Int,
  message_id message_id: Int,
) -> Result(Nil, error.TelegaError)

Remove the inline keyboard from the live dialog message (used on Done). Best effort: failures are reported but callers usually ignore them.

pub fn render_window(
  ctx ctx: bot.Context(session, error, dependencies),
  chat_id chat_id: Int,
  message message: option.Option(#(Int, MessageKind)),
  dialog_id dialog_id: String,
  window_id window_id: String,
  window window: types.RenderedWindow,
) -> Result(#(Int, MessageKind), RenderError)

Render a window into the live dialog message following the edit-or-send strategy matrix (see the module doc): send when there is no message yet, edit when the kind is unchanged, delete-and-resend when it changed. Edits fall back to a fresh send when the old message is gone. Returns the id and kind of the live message.

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

Show a toast notification at the top of the chat. Same contract as alert, but without the modal dialog.

Search Document