Hotline.Flow behaviour (Hotline v0.2.1)

Copy Markdown View Source

DSL and behaviour for building multi-step conversation flows.

A flow is a module that declares a sequence of steps. Each step has a prompt (sent to the user) and the module implements handle_input/3 to process responses and control transitions.

Example

defmodule MyBot.Flows.Registration do
  use Hotline.Flow

  step :name, prompt: "What's your name?"
  step :email, prompt: fn ctx -> "Thanks #{ctx.data.name}! What's your email?" end
  step :confirm,
    prompt: fn ctx ->
      "Confirm?\nName: #{ctx.data.name}\nEmail: #{ctx.data[:email] || "skipped"}"
    end,
    keyboard: [[%{text: "Yes", callback_data: "yes"}, %{text: "No", callback_data: "no"}]]

  @impl true
  def handle_input(:name, %{message: %{text: name}}, _ctx) when byte_size(name) >= 2 do
    {:next, store: %{name: name}}
  end
  def handle_input(:name, _, _ctx), do: {:retry, "Name must be at least 2 characters."}

  def handle_input(:email, %{message: %{text: "/skip"}}, _ctx), do: {:next, store: %{email: nil}}
  def handle_input(:email, %{message: %{text: email}}, _ctx) do
    if String.contains?(email, "@"),
      do: {:next, store: %{email: email}},
      else: {:retry, "Invalid email. Try again or /skip."}
  end

  def handle_input(:confirm, %{callback_query: %{data: "yes"}}, _ctx), do: :done
  def handle_input(:confirm, %{callback_query: %{data: "no"}}, _ctx), do: {:goto, :name, reset: true}
  def handle_input(:confirm, _, _ctx), do: {:retry, "Use the buttons above."}
end

Return Values

handle_input/3 must return one of:

  • :next — advance to the next step in sequence
  • {:next, store: %{key: value}} — merge data, then advance
  • {:goto, step} — jump to a named step
  • {:goto, step, reset: true} — jump and clear accumulated data
  • {:retry, message} — stay on the current step, send error message
  • :done — complete the flow
  • {:done, result} — complete with a result value
  • :cancel — cancel the flow

Optional Callbacks

  • on_done/1 — called when the flow completes (receives the final context)
  • on_cancel/1 — called when the flow is cancelled

Running Flows

Use Hotline.Flow.Engine to manage active flows. See Hotline.Flow.Runner for the pure execution logic.

Summary

Functions

Declare a step in the flow.

Types

step_result()

@type step_result() ::
  :next
  | {:next, [{:store, map()}]}
  | {:goto, atom()}
  | {:goto, atom(), keyword()}
  | {:retry, String.t()}
  | :done
  | {:done, term()}
  | :cancel

Callbacks

handle_input(step, update, ctx)

@callback handle_input(step :: atom(), update :: map(), ctx :: Hotline.Flow.Context.t()) ::
  step_result()

on_cancel(ctx)

(optional)
@callback on_cancel(ctx :: Hotline.Flow.Context.t()) :: any()

on_done(ctx)

(optional)
@callback on_done(ctx :: Hotline.Flow.Context.t()) :: any()

Functions

step(name, opts \\ [])

(macro)

Declare a step in the flow.

Options

  • :prompt — a string or fn ctx -> string end sent to the user when entering the step
  • :keyboard — inline keyboard markup (list of button rows) sent with the prompt