telega/dialog/types

Shared type definitions for the declarative dialog system.

A dialog is a set of windows; a window is a pure render function plus event handlers. See telega/dialog for the builder API and the full guide.

Types

A parsed button press delivered to on_action.

pub type ActionEvent {
  ActionEvent(action_id: String, arg: option.Option(String))
}

Constructors

What a window handler decides to do next. state is always explicit: handlers are pure functions from the old state to an action carrying the new state.

pub type DialogAction(state) {
  Stay(state)
  Goto(window_id: String, state: state)
  Back(state)
  Done(state)
  StartSub(
    sub_id: String,
    args: dict.Dict(String, String),
    state: state,
  )
}

Constructors

  • Stay(state)

    Re-render the current window with the new state.

  • Goto(window_id: String, state: state)

    Go to another window (pushed to history — Back returns here).

  • Back(state)

    One step back in the navigation stack.

  • Done(state)

    Finish the dialog (runs on_done, removes the keyboard).

  • StartSub(
      sub_id: String,
      args: dict.Dict(String, String),
      state: state,
    )

    Start a sub-dialog attached with dialog.subdialog. The sub takes over the live message; its Done returns control to the current window’s on_sub_result (see telega/dialog § sub-dialogs). args are handed to the sub-dialog’s init. One level of nesting only: StartSub from inside a sub-dialog is rejected at runtime (logged, window re-rendered).

Validation errors returned by dialog.build().

pub type DialogBuildError {
  DuplicateWindowId(id: String)
  UnknownWindowReference(from: String, to: String)
  CallbackDataTooLong(window: String, action: String, bytes: Int)
  NoWindows
  UnknownInitialWindow(id: String)
  DuplicateWidgetId(window: String, id: String)
  ReservedIdCharacter(kind: String, id: String)
  DuplicateSubDialogId(id: String)
  NestedSubDialog(id: String)
}

Constructors

  • DuplicateWindowId(id: String)
  • UnknownWindowReference(from: String, to: String)

    A Goto/widget done/on_sub_result target references a window that doesn’t exist.

  • CallbackDataTooLong(window: String, action: String, bytes: Int)

    The static part of a button’s callback data exceeds Telegram’s 64-byte limit. Use shorter dialog/window/action ids. For sub-dialog windows the budget includes the <sub_id>. step prefix.

  • NoWindows
  • UnknownInitialWindow(id: String)
  • DuplicateWidgetId(window: String, id: String)
  • ReservedIdCharacter(kind: String, id: String)

    : is the callback-data separator and is forbidden in dialog, window, action, and widget ids; . namespaces sub-dialog windows and is additionally forbidden in dialog and window ids.

  • DuplicateSubDialogId(id: String)
  • NestedSubDialog(id: String)

    The attached sub-dialog has sub-dialogs of its own — nesting is one level deep.

pub type DialogButton {
  ActionButton(text: String, action_id: String)
  ActionArgButton(text: String, action_id: String, arg: String)
  UrlButton(text: String, url: String)
  WebAppButton(text: String, url: String)
  NoopButton(text: String)
}

Constructors

  • ActionButton(text: String, action_id: String)

    Action button: the engine builds callback data (dlg:<dialog>:<window>:<action>).

  • ActionArgButton(text: String, action_id: String, arg: String)

    Action with an argument (e.g. the id of a list item): dlg:<dialog>:<window>:<action>:<arg>.

  • UrlButton(text: String, url: String)
  • WebAppButton(text: String, url: String)
  • NoopButton(text: String)

    Non-clickable button (header/counter): rendered as a copy-text button that copies its own label.

Media attached to a window. media is a file_id or URL — uploading local files via attach:// is not supported in dialogs yet (the string will widen to FileOrString without breakage when it is).

pub type DialogMedia {
  PhotoMedia(media: String, has_spoiler: Bool)
  VideoMedia(media: String, has_spoiler: Bool)
  AnimationMedia(media: String)
  DocumentMedia(media: String)
}

Constructors

  • PhotoMedia(media: String, has_spoiler: Bool)
  • VideoMedia(media: String, has_spoiler: Bool)
  • AnimationMedia(media: String)
  • DocumentMedia(media: String)

A managed keyboard widget: renders extra button rows for a window and handles their events itself. Widget buttons use action ids of the form w:<widget_id>:<cmd> (the w: action-id namespace is reserved for widgets), so the engine can route presses to on_event without touching the window’s on_action.

Built-in widgets live in telega/dialog/widget (pager, select, radio, multiselect, paged_select). Custom widgets construct this record directly:

  • goto_targets — window ids the widget may Emit(Goto(...)) to; validated by dialog.build().
  • static_actions — the widget’s full static action ids (w:<widget_id>:<cmd>) for the build-time callback-data byte budget; argument-carrying buttons are additionally validated on every render.
pub type KeyboardWidget(state, session, error, dependencies) {
  KeyboardWidget(
    id: String,
    render: fn(WidgetCtx(state, session, error, dependencies)) -> List(
      List(DialogButton),
    ),
    on_event: fn(
      WidgetCtx(state, session, error, dependencies),
      String,
      option.Option(String),
    ) -> Result(WidgetResult(state), error),
    goto_targets: List(String),
    static_actions: List(String),
  )
}

Constructors

  • KeyboardWidget(
      id: String,
      render: fn(WidgetCtx(state, session, error, dependencies)) -> List(
        List(DialogButton),
      ),
      on_event: fn(
        WidgetCtx(state, session, error, dependencies),
        String,
        option.Option(String),
      ) -> Result(WidgetResult(state), error),
      goto_targets: List(String),
      static_actions: List(String),
    )

User-facing labels for engine-generated UI. All strings are resolved via a fn(Context) -> Labels factory so telega_i18n works out of the box. Defaults are wordless unicode symbols to stay locale-neutral.

pub type Labels {
  Labels(
    prev: String,
    next: String,
    page_info: String,
    checked: String,
    unchecked: String,
    checkbox_on: String,
    checkbox_off: String,
    done: String,
    stale: String,
  )
}

Constructors

  • Labels(
      prev: String,
      next: String,
      page_info: String,
      checked: String,
      unchecked: String,
      checkbox_on: String,
      checkbox_off: String,
      done: String,
      stale: String,
    )

    Arguments

    stale

    answer_callback_query text shown when the user presses a button on an outdated dialog message.

The visible result of rendering a window: message text, an inline keyboard, and an optional media attachment. The engine turns it into the right send*/edit* calls (see telega/dialog/render) and builds callback data for the buttons itself. For media windows the text becomes the caption.

pub type RenderedWindow {
  RenderedWindow(
    text: format.FormattedText,
    buttons: List(List(DialogButton)),
    media: option.Option(DialogMedia),
  )
}

Constructors

Everything a widget sees when rendering or handling an event: the window’s user state, the widget’s own persistent store, the dialog labels, and the update context.

pub type WidgetCtx(state, session, error, dependencies) {
  WidgetCtx(
    state: state,
    store: WidgetStore,
    labels: Labels,
    ctx: bot.Context(session, error, dependencies),
  )
}

Constructors

What a widget decides after handling one of its events.

pub type WidgetResult(state) {
  StoreUpdated(WidgetStore)
  Emit(DialogAction(state))
}

Constructors

  • StoreUpdated(WidgetStore)

    Update the widget store; the window is re-rendered in place.

  • Emit(DialogAction(state))

    Delegate a dialog action outward — e.g. Goto on a “done” press. To update the store and the state together, emit Stay(new_state) after a StoreUpdated round-trip or fold the value into the state itself.

A widget’s private key-value store. Persisted with the dialog instance under __dialog_widget:<window_id>:<widget_id>, so widget state survives restarts together with the rest of the dialog.

pub opaque type WidgetStore

A single dialog window: a pure render function plus event handlers.

  • render must be pure — its only “effect” is reading ctx (e.g. for i18n). This is what makes windows snapshot-testable without a network.
  • on_action receives an already-parsed ActionEvent, not a raw string.
  • on_text: None means text sent to this window is politely ignored: the engine just re-renders the window.
  • widgets are managed keyboard widgets (see telega/dialog/widget): the engine appends their button rows after render’s own buttons and routes their events to KeyboardWidget.on_event, bypassing on_action.
  • on_sub_result runs when a sub-dialog started from this window (via StartSub) finishes: it receives the window’s state and the result dict exported by the subdialog attachment, and decides where to go next (Stay re-renders this window). None just re-renders.
pub type Window(state, session, error, dependencies) {
  Window(
    id: String,
    render: fn(state, bot.Context(session, error, dependencies)) -> RenderedWindow,
    on_action: fn(
      state,
      ActionEvent,
      bot.Context(session, error, dependencies),
    ) -> Result(DialogAction(state), error),
    on_text: option.Option(
      fn(state, String, bot.Context(session, error, dependencies)) -> Result(
        DialogAction(state),
        error,
      ),
    ),
    widgets: List(
      KeyboardWidget(state, session, error, dependencies),
    ),
    on_sub_result: option.Option(
      fn(
        state,
        dict.Dict(String, String),
        bot.Context(session, error, dependencies),
      ) -> Result(DialogAction(state), error),
    ),
  )
}

Constructors

Values

pub fn default_labels() -> Labels
pub fn new_store() -> WidgetStore
pub fn store_get(
  store store: WidgetStore,
  key key: String,
) -> option.Option(String)
pub fn store_set(
  store store: WidgetStore,
  key key: String,
  value value: String,
) -> WidgetStore
Search Document