-module(telega@flow). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/telega/flow.gleam"). -export([new/4, add_step/3, add_step_with_middleware/4, add_step_with_hooks/5, add_step_with_hooks_and_middleware/6, add_global_middleware/2, add_conditional/5, add_multi_conditional/4, add_parallel_steps/4, parallel/4, add_subflow/6, inline_next/3, on_complete/2, on_error/2, set_on_flow_enter/2, set_on_flow_leave/2, set_on_flow_exit/2, build/2, next/3, unsafe_next/3, goto/3, back/2, complete/2, cancel/2, wait/3, wait_callback/3, exit/3, enter_subflow/4, return_from_subflow/3, get_current_step/2, store_step_data/3, get_step_data/2, clear_step_data/1, clear_step_data_key/2, is_callback_passed/3, store_data/3, get_data/2, next_with_data/5, create_memory_storage/0, new_with_default_converters/3, text_step/3, message_step/2, with_inline_subflow/6, with_inline_subflow_mapped/8, new_registry/0, register_with_data/4, register/3, register_callable/2, validation_middleware/1, create_text_handler/1, call_flow/4, to_handler/1, create_resume_handler/1, create_resume_handler_with_keyboard/2, apply_to_router/2, compose_conditional/4, compose_parallel/4, compose_sequential/3]). -export_type([flow_state/0, flow_stack_frame/0, parallel_state/0, flow_instance/0, flow_storage/1, flow_builder/3, flow/3, flow_registry/2, step_config/3, conditional_transition/1, parallel_config/1, subflow_config/3, flow_action/1, inline_step/0, flow_trigger/0, composed_step/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Type-safe, persistent flow system for building multi-step conversational interactions.\n" "\n" " ## Core Concepts\n" "\n" " - **Flow** - State machine for multi-step conversations with compile-time validated transitions\n" " - **FlowRegistry** - Central container managing all flows, supporting triggers and manual calls\n" " - **Storage** - Pluggable persistence (database, memory, Redis) with automatic state recovery\n" "\n" " ## Quick Start\n" "\n" " ```gleam\n" " // 1. Define flow steps\n" " pub type OnboardingStep {\n" " Welcome\n" " CollectName\n" " CollectEmail\n" " Complete\n" " }\n" "\n" " // 2. Build flow with handlers\n" " let onboarding_flow =\n" " flow.new(\"onboarding\", storage, step_to_string, string_to_step)\n" " |> flow.add_step(Welcome, welcome_handler)\n" " |> flow.add_step(CollectName, name_handler)\n" " |> flow.add_step(CollectEmail, email_handler)\n" " |> flow.add_step(Complete, complete_handler)\n" " |> flow.build(initial: Welcome)\n" "\n" " // 3. Register and apply to router\n" " let flow_registry =\n" " flow.new_registry()\n" " |> flow.register(flow.OnCommand(\"/start\"), onboarding_flow)\n" "\n" " router |> flow.apply_to_router(flow_registry)\n" " ```\n" "\n" " ## Advanced Usage\n" "\n" " ```gleam\n" " // Call flow from any handler\n" " fn my_handler(ctx, _data) {\n" " let initial_data = dict.from_list([#(\"product_id\", \"123\")])\n" " flow.call_flow(ctx, flow_registry, \"checkout\", initial_data)\n" " }\n" "\n" " // Conditional transitions\n" " |> flow.add_conditional(\n" " from: CollectAge,\n" " condition: fn(instance) {\n" " case flow.get_data(instance, \"age\") {\n" " Some(age) -> int.parse(age) |> result.unwrap(0) >= 18\n" " None -> False\n" " }\n" " },\n" " true: AdultFlow,\n" " false: MinorFlow\n" " )\n" "\n" " // Parallel step execution\n" " |> flow.add_parallel_steps(\n" " trigger_step: StartVerification,\n" " parallel_steps: [EmailVerify, PhoneVerify, DocumentVerify],\n" " join_at: VerificationComplete\n" " )\n" " ```\n" ). -type flow_state() :: {flow_state, binary(), gleam@dict:dict(binary(), binary()), list(binary()), list(flow_stack_frame()), gleam@option:option(parallel_state())}. -type flow_stack_frame() :: {flow_stack_frame, binary(), binary(), gleam@dict:dict(binary(), binary())}. -type parallel_state() :: {parallel_state, list(binary()), list(binary()), gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary())), binary()}. -type flow_instance() :: {flow_instance, binary(), binary(), integer(), integer(), flow_state(), gleam@dict:dict(binary(), binary()), gleam@option:option(binary()), integer(), integer()}. -type flow_storage(ATKW) :: {flow_storage, fun((flow_instance()) -> {ok, nil} | {error, ATKW}), fun((binary()) -> {ok, gleam@option:option(flow_instance())} | {error, ATKW}), fun((binary()) -> {ok, nil} | {error, ATKW}), fun((integer(), integer()) -> {ok, list(flow_instance())} | {error, ATKW})}. -opaque flow_builder(ATKX, ATKY, ATKZ) :: {flow_builder, binary(), gleam@dict:dict(binary(), step_config(ATKX, ATKY, ATKZ)), fun((ATKX) -> binary()), fun((binary()) -> {ok, ATKX} | {error, nil}), flow_storage(ATKZ), gleam@option:option(fun((telega@bot:context(ATKY, ATKZ), flow_instance()) -> {ok, telega@bot:context(ATKY, ATKZ)} | {error, ATKZ})), gleam@option:option(fun((telega@bot:context(ATKY, ATKZ), flow_instance(), gleam@option:option(ATKZ)) -> {ok, telega@bot:context(ATKY, ATKZ)} | {error, ATKZ})), list(fun((telega@bot:context(ATKY, ATKZ), flow_instance(), fun(() -> {ok, {telega@bot:context(ATKY, ATKZ), flow_action(ATKX), flow_instance()}} | {error, ATKZ})) -> {ok, {telega@bot:context(ATKY, ATKZ), flow_action(ATKX), flow_instance()}} | {error, ATKZ})), list(conditional_transition(ATKX)), list(parallel_config(ATKX)), list(subflow_config(ATKX, ATKY, ATKZ)), gleam@option:option(fun((telega@bot:context(ATKY, ATKZ), flow_instance()) -> {ok, {telega@bot:context(ATKY, ATKZ), flow_instance()}} | {error, ATKZ})), gleam@option:option(fun((telega@bot:context(ATKY, ATKZ), flow_instance()) -> {ok, {telega@bot:context(ATKY, ATKZ), flow_instance()}} | {error, ATKZ})), gleam@option:option(fun((telega@bot:context(ATKY, ATKZ), flow_instance()) -> {ok, telega@bot:context(ATKY, ATKZ)} | {error, ATKZ}))}. -opaque flow(ATLA, ATLB, ATLC) :: {flow, binary(), gleam@dict:dict(binary(), step_config(ATLA, ATLB, ATLC)), ATLA, fun((ATLA) -> binary()), fun((binary()) -> {ok, ATLA} | {error, nil}), flow_storage(ATLC), gleam@option:option(fun((telega@bot:context(ATLB, ATLC), flow_instance()) -> {ok, telega@bot:context(ATLB, ATLC)} | {error, ATLC})), gleam@option:option(fun((telega@bot:context(ATLB, ATLC), flow_instance(), gleam@option:option(ATLC)) -> {ok, telega@bot:context(ATLB, ATLC)} | {error, ATLC})), list(fun((telega@bot:context(ATLB, ATLC), flow_instance(), fun(() -> {ok, {telega@bot:context(ATLB, ATLC), flow_action(ATLA), flow_instance()}} | {error, ATLC})) -> {ok, {telega@bot:context(ATLB, ATLC), flow_action(ATLA), flow_instance()}} | {error, ATLC})), list(conditional_transition(ATLA)), list(parallel_config(ATLA)), list(subflow_config(ATLA, ATLB, ATLC)), gleam@option:option(fun((telega@bot:context(ATLB, ATLC), flow_instance()) -> {ok, {telega@bot:context(ATLB, ATLC), flow_instance()}} | {error, ATLC})), gleam@option:option(fun((telega@bot:context(ATLB, ATLC), flow_instance()) -> {ok, {telega@bot:context(ATLB, ATLC), flow_instance()}} | {error, ATLC})), gleam@option:option(fun((telega@bot:context(ATLB, ATLC), flow_instance()) -> {ok, telega@bot:context(ATLB, ATLC)} | {error, ATLC}))}. -opaque flow_registry(ATLD, ATLE) :: {flow_registry, list({flow_trigger(), flow(gleam@dynamic:dynamic_(), ATLD, ATLE), gleam@dict:dict(binary(), binary())}), gleam@dict:dict(binary(), flow(gleam@dynamic:dynamic_(), ATLD, ATLE))}. -type step_config(ATLF, ATLG, ATLH) :: {step_config, fun((telega@bot:context(ATLG, ATLH), flow_instance()) -> {ok, {telega@bot:context(ATLG, ATLH), flow_action(ATLF), flow_instance()}} | {error, ATLH}), list(fun((telega@bot:context(ATLG, ATLH), flow_instance(), fun(() -> {ok, {telega@bot:context(ATLG, ATLH), flow_action(ATLF), flow_instance()}} | {error, ATLH})) -> {ok, {telega@bot:context(ATLG, ATLH), flow_action(ATLF), flow_instance()}} | {error, ATLH})), gleam@option:option(fun((telega@bot:context(ATLG, ATLH), flow_instance()) -> {ok, {telega@bot:context(ATLG, ATLH), flow_instance()}} | {error, ATLH})), gleam@option:option(fun((telega@bot:context(ATLG, ATLH), flow_instance()) -> {ok, {telega@bot:context(ATLG, ATLH), flow_instance()}} | {error, ATLH}))}. -type conditional_transition(ATLI) :: {conditional_transition, binary(), list({fun((flow_instance()) -> boolean()), ATLI}), ATLI}. -type parallel_config(ATLJ) :: {parallel_config, binary(), list(ATLJ), ATLJ}. -type subflow_config(ATLK, ATLL, ATLM) :: {subflow_config, binary(), flow(gleam@dynamic:dynamic_(), ATLL, ATLM), ATLK, fun((flow_instance()) -> gleam@dict:dict(binary(), binary())), fun((gleam@dict:dict(binary(), binary()), flow_instance()) -> flow_instance())}. -type flow_action(ATLN) :: {next, ATLN} | {next_string, binary()} | back | {complete, gleam@dict:dict(binary(), binary())} | cancel | {wait, binary()} | {wait_callback, binary()} | {go_to, ATLN} | {exit, gleam@option:option(gleam@dict:dict(binary(), binary()))} | {return_from_subflow, gleam@dict:dict(binary(), binary())} | {start_parallel, list(ATLN), ATLN} | {complete_parallel_step, ATLN, gleam@dict:dict(binary(), binary())} | {enter_subflow, binary(), gleam@dict:dict(binary(), binary())}. -type inline_step() :: {inline_step, binary()}. -type flow_trigger() :: {on_command, binary()} | {on_text, telega@router:pattern()} | {on_callback, telega@router:pattern()} | {on_filtered, telega@router:filter()} | on_photo | on_video | on_audio | on_voice | on_any_text. -type composed_step() :: {composed_flow_step, integer()} | composed_select_flow | composed_start_parallel | {composed_parallel_flow, integer()} | composed_merge_results. -file("src/telega/flow.gleam", 316). ?DOC(" Create a new flow builder\n"). -spec new( binary(), flow_storage(ATOD), fun((ATOF) -> binary()), fun((binary()) -> {ok, ATOF} | {error, nil}) ) -> flow_builder(ATOF, any(), ATOD). new(Flow_name, Storage, Step_to_string, String_to_step) -> {flow_builder, Flow_name, maps:new(), Step_to_string, String_to_step, Storage, none, none, [], [], [], [], none, none, none}. -file("src/telega/flow.gleam", 365). ?DOC(" Add a step to the flow\n"). -spec add_step( flow_builder(ATOU, ATOV, ATOW), ATOU, fun((telega@bot:context(ATOV, ATOW), flow_instance()) -> {ok, {telega@bot:context(ATOV, ATOW), flow_action(ATOU), flow_instance()}} | {error, ATOW}) ) -> flow_builder(ATOU, ATOV, ATOW). add_step(Builder, Step, Handler) -> Step_name = (erlang:element(4, Builder))(Step), Config = {step_config, Handler, [], none, none}, {flow_builder, erlang:element(2, Builder), gleam@dict:insert(erlang:element(3, Builder), Step_name, Config), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 377). ?DOC(" Add a step with middleware\n"). -spec add_step_with_middleware( flow_builder(ATPG, ATPH, ATPI), ATPG, list(fun((telega@bot:context(ATPH, ATPI), flow_instance(), fun(() -> {ok, {telega@bot:context(ATPH, ATPI), flow_action(ATPG), flow_instance()}} | {error, ATPI})) -> {ok, {telega@bot:context(ATPH, ATPI), flow_action(ATPG), flow_instance()}} | {error, ATPI})), fun((telega@bot:context(ATPH, ATPI), flow_instance()) -> {ok, {telega@bot:context(ATPH, ATPI), flow_action(ATPG), flow_instance()}} | {error, ATPI}) ) -> flow_builder(ATPG, ATPH, ATPI). add_step_with_middleware(Builder, Step, Middlewares, Handler) -> Step_name = (erlang:element(4, Builder))(Step), Config = {step_config, Handler, Middlewares, none, none}, {flow_builder, erlang:element(2, Builder), gleam@dict:insert(erlang:element(3, Builder), Step_name, Config), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 409). ?DOC( " Add a step with lifecycle hooks\n" "\n" " ## Example\n" "\n" " ```gleam\n" " flow.new(\"checkout\", storage, step_to_string, string_to_step)\n" " |> flow.add_step_with_hooks(\n" " Payment,\n" " handler: payment_handler,\n" " on_enter: Some(fn(ctx, instance) {\n" " // Log payment step entry\n" " use _ <- result.try(reply.with_text(ctx, \"Entering payment...\"))\n" " Ok(#(ctx, instance))\n" " }),\n" " on_leave: Some(fn(ctx, instance) {\n" " // Cleanup or log\n" " Ok(#(ctx, instance))\n" " }),\n" " )\n" " ```\n" ). -spec add_step_with_hooks( flow_builder(ATPW, ATPX, ATPY), ATPW, fun((telega@bot:context(ATPX, ATPY), flow_instance()) -> {ok, {telega@bot:context(ATPX, ATPY), flow_action(ATPW), flow_instance()}} | {error, ATPY}), gleam@option:option(fun((telega@bot:context(ATPX, ATPY), flow_instance()) -> {ok, {telega@bot:context(ATPX, ATPY), flow_instance()}} | {error, ATPY})), gleam@option:option(fun((telega@bot:context(ATPX, ATPY), flow_instance()) -> {ok, {telega@bot:context(ATPX, ATPY), flow_instance()}} | {error, ATPY})) ) -> flow_builder(ATPW, ATPX, ATPY). add_step_with_hooks(Builder, Step, Handler, On_enter, On_leave) -> Step_name = (erlang:element(4, Builder))(Step), Config = {step_config, Handler, [], On_enter, On_leave}, {flow_builder, erlang:element(2, Builder), gleam@dict:insert(erlang:element(3, Builder), Step_name, Config), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 422). ?DOC(" Add a step with hooks and middleware\n"). -spec add_step_with_hooks_and_middleware( flow_builder(ATQO, ATQP, ATQQ), ATQO, fun((telega@bot:context(ATQP, ATQQ), flow_instance()) -> {ok, {telega@bot:context(ATQP, ATQQ), flow_action(ATQO), flow_instance()}} | {error, ATQQ}), list(fun((telega@bot:context(ATQP, ATQQ), flow_instance(), fun(() -> {ok, {telega@bot:context(ATQP, ATQQ), flow_action(ATQO), flow_instance()}} | {error, ATQQ})) -> {ok, {telega@bot:context(ATQP, ATQQ), flow_action(ATQO), flow_instance()}} | {error, ATQQ})), gleam@option:option(fun((telega@bot:context(ATQP, ATQQ), flow_instance()) -> {ok, {telega@bot:context(ATQP, ATQQ), flow_instance()}} | {error, ATQQ})), gleam@option:option(fun((telega@bot:context(ATQP, ATQQ), flow_instance()) -> {ok, {telega@bot:context(ATQP, ATQQ), flow_instance()}} | {error, ATQQ})) ) -> flow_builder(ATQO, ATQP, ATQQ). add_step_with_hooks_and_middleware( Builder, Step, Handler, Middlewares, On_enter, On_leave ) -> Step_name = (erlang:element(4, Builder))(Step), Config = {step_config, Handler, Middlewares, On_enter, On_leave}, {flow_builder, erlang:element(2, Builder), gleam@dict:insert(erlang:element(3, Builder), Step_name, Config), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 436). ?DOC(" Add global middleware that applies to all steps\n"). -spec add_global_middleware( flow_builder(ATRK, ATRL, ATRM), fun((telega@bot:context(ATRL, ATRM), flow_instance(), fun(() -> {ok, {telega@bot:context(ATRL, ATRM), flow_action(ATRK), flow_instance()}} | {error, ATRM})) -> {ok, {telega@bot:context(ATRL, ATRM), flow_action(ATRK), flow_instance()}} | {error, ATRM}) ) -> flow_builder(ATRK, ATRL, ATRM). add_global_middleware(Builder, Middleware) -> {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), lists:append(erlang:element(9, Builder), [Middleware]), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 447). ?DOC(" Add conditional transition\n"). -spec add_conditional( flow_builder(ATRW, ATRX, ATRY), ATRW, fun((flow_instance()) -> boolean()), ATRW, ATRW ) -> flow_builder(ATRW, ATRX, ATRY). add_conditional(Builder, From, Condition, On_true, On_false) -> From_str = (erlang:element(4, Builder))(From), Conditional = {conditional_transition, From_str, [{Condition, On_true}], On_false}, {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), lists:append(erlang:element(10, Builder), [Conditional]), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 468). ?DOC(" Add multi-way conditional\n"). -spec add_multi_conditional( flow_builder(ATSF, ATSG, ATSH), ATSF, list({fun((flow_instance()) -> boolean()), ATSF}), ATSF ) -> flow_builder(ATSF, ATSG, ATSH). add_multi_conditional(Builder, From, Conditions, Default) -> From_str = (erlang:element(4, Builder))(From), Conditional = {conditional_transition, From_str, Conditions, Default}, {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), lists:append(erlang:element(10, Builder), [Conditional]), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 516). ?DOC( " Add parallel step execution.\n" "\n" " @deprecated Use `parallel()` instead for cleaner API.\n" ). -spec add_parallel_steps(flow_builder(ATSZ, ATTA, ATTB), ATSZ, list(ATSZ), ATSZ) -> flow_builder(ATSZ, ATTA, ATTB). add_parallel_steps(Builder, Trigger_step, Parallel_steps, Join_at) -> Trigger_str = (erlang:element(4, Builder))(Trigger_step), Config = {parallel_config, Trigger_str, Parallel_steps, Join_at}, {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), lists:append(erlang:element(11, Builder), [Config]), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 504). ?DOC( " Add parallel step execution (simplified API).\n" "\n" " This is the recommended way to add parallel steps to a flow.\n" " When the flow reaches `from` step, it will execute all `steps` in parallel,\n" " and automatically transition to `join` step when all parallel steps complete.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " flow.new(\"kyc_verification\", storage, to_string, from_string)\n" " |> flow.add_step(Start, start_handler)\n" " |> flow.add_step(EmailVerify, email_handler)\n" " |> flow.add_step(PhoneVerify, phone_handler)\n" " |> flow.add_step(DocumentVerify, document_handler)\n" " |> flow.parallel(\n" " from: Start,\n" " steps: [EmailVerify, PhoneVerify, DocumentVerify],\n" " join: AllComplete,\n" " )\n" " |> flow.add_step(AllComplete, complete_handler)\n" " |> flow.build(initial: Start)\n" " ```\n" ). -spec parallel(flow_builder(ATSP, ATSQ, ATSR), ATSP, list(ATSP), ATSP) -> flow_builder(ATSP, ATSQ, ATSR). parallel(Builder, From, Steps, Join) -> add_parallel_steps(Builder, From, Steps, Join). -file("src/telega/flow.gleam", 562). ?DOC( " Add sub-flow\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let address_flow =\n" " flow.new(\"address\", storage, step_to_string, string_to_step)\n" " |> flow.add_step(Street, street_handler)\n" " |> flow.build(initial: Street)\n" "\n" " let checkout_flow =\n" " flow.new(\"checkout\", storage, step_to_string, string_to_step)\n" " |> flow.add_step(Cart, cart_handler)\n" " |> flow.add_subflow(\n" " trigger_step: CollectAddress,\n" " subflow: address_flow,\n" " return_to: Payment,\n" " map_args: fn(instance) { instance.state.data },\n" " map_result: fn(result, instance) {\n" " FlowInstance(..instance, state: FlowState(\n" " ..instance.state,\n" " data: dict.merge(instance.state.data, result)\n" " ))\n" " },\n" " )\n" " |> flow.build(initial: Cart)\n" " ```\n" ). -spec add_subflow( flow_builder(ATTJ, ATTK, ATTL), ATTJ, flow(gleam@dynamic:dynamic_(), ATTK, ATTL), ATTJ, fun((flow_instance()) -> gleam@dict:dict(binary(), binary())), fun((gleam@dict:dict(binary(), binary()), flow_instance()) -> flow_instance()) ) -> flow_builder(ATTJ, ATTK, ATTL). add_subflow(Builder, Trigger_step, Subflow, Return_to, Map_args, Map_result) -> Trigger_str = (erlang:element(4, Builder))(Trigger_step), Config = {subflow_config, Trigger_str, Subflow, Return_to, Map_args, Map_result}, {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), lists:append(erlang:element(12, Builder), [Config]), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 722). -spec inline_step_to_string(inline_step()) -> binary(). inline_step_to_string(Step) -> erlang:element(2, Step). -file("src/telega/flow.gleam", 726). -spec string_to_inline_step(binary()) -> {ok, inline_step()} | {error, nil}. string_to_inline_step(S) -> {ok, {inline_step, S}}. -file("src/telega/flow.gleam", 731). ?DOC(" Navigate to next inline step by name\n"). -spec inline_next(telega@bot:context(ATVJ, ATVK), flow_instance(), binary()) -> {ok, {telega@bot:context(ATVJ, ATVK), flow_action(inline_step()), flow_instance()}} | {error, ATVK}. inline_next(Ctx, Instance, Step_name) -> {ok, {Ctx, {next, {inline_step, Step_name}}, Instance}}. -file("src/telega/flow.gleam", 740). ?DOC(" Set completion handler\n"). -spec on_complete( flow_builder(ATVQ, ATVR, ATVS), fun((telega@bot:context(ATVR, ATVS), flow_instance()) -> {ok, telega@bot:context(ATVR, ATVS)} | {error, ATVS}) ) -> flow_builder(ATVQ, ATVR, ATVS). on_complete(Builder, Handler) -> {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), {some, Handler}, erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 749). ?DOC(" Set error handler\n"). -spec on_error( flow_builder(ATWF, ATWG, ATWH), fun((telega@bot:context(ATWG, ATWH), flow_instance(), gleam@option:option(ATWH)) -> {ok, telega@bot:context(ATWG, ATWH)} | {error, ATWH}) ) -> flow_builder(ATWF, ATWG, ATWH). on_error(Builder, Handler) -> {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), {some, Handler}, erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 773). ?DOC( " Set flow enter hook\n" "\n" " Called when the flow is first started or resumed from a parent flow.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " flow.new(\"checkout\", storage, step_to_string, string_to_step)\n" " |> flow.set_on_flow_enter(fn(ctx, instance) {\n" " use ctx <- result.try(\n" " reply.with_text(ctx, \"Welcome to checkout!\")\n" " |> result.map_error(fn(e) { e })\n" " )\n" " Ok(#(ctx, instance))\n" " })\n" " ```\n" ). -spec set_on_flow_enter( flow_builder(ATWV, ATWW, ATWX), fun((telega@bot:context(ATWW, ATWX), flow_instance()) -> {ok, {telega@bot:context(ATWW, ATWX), flow_instance()}} | {error, ATWX}) ) -> flow_builder(ATWV, ATWW, ATWX). set_on_flow_enter(Builder, Hook) -> {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), {some, Hook}, erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 793). ?DOC( " Set flow leave hook\n" "\n" " Called when transitioning to a subflow.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " flow.new(\"checkout\", storage, step_to_string, string_to_step)\n" " |> flow.set_on_flow_leave(fn(ctx, instance) {\n" " // Save state before entering subflow\n" " Ok(#(ctx, instance))\n" " })\n" " ```\n" ). -spec set_on_flow_leave( flow_builder(ATXG, ATXH, ATXI), fun((telega@bot:context(ATXH, ATXI), flow_instance()) -> {ok, {telega@bot:context(ATXH, ATXI), flow_instance()}} | {error, ATXI}) ) -> flow_builder(ATXG, ATXH, ATXI). set_on_flow_leave(Builder, Hook) -> {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), {some, Hook}, erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 816). ?DOC( " Set flow exit hook\n" "\n" " Called when the flow completes or is cancelled.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " flow.new(\"checkout\", storage, step_to_string, string_to_step)\n" " |> flow.set_on_flow_exit(fn(ctx, instance) {\n" " use _ <- result.try(\n" " reply.with_text(ctx, \"Checkout complete!\")\n" " |> result.map_error(fn(e) { e })\n" " )\n" " Ok(ctx)\n" " })\n" " ```\n" ). -spec set_on_flow_exit( flow_builder(ATXR, ATXS, ATXT), fun((telega@bot:context(ATXS, ATXT), flow_instance()) -> {ok, telega@bot:context(ATXS, ATXT)} | {error, ATXT}) ) -> flow_builder(ATXR, ATXS, ATXT). set_on_flow_exit(Builder, Hook) -> {flow_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), {some, Hook}}. -file("src/telega/flow.gleam", 824). ?DOC(" Build the flow\n"). -spec build(flow_builder(ATYC, ATYD, ATYE), ATYC) -> flow(ATYC, ATYD, ATYE). build(Builder, Initial_step) -> {flow, erlang:element(2, Builder), erlang:element(3, Builder), Initial_step, erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder), erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder)}. -file("src/telega/flow.gleam", 848). ?DOC(" Next navigation\n"). -spec next(telega@bot:context(ATYL, ATYM), flow_instance(), ATYP) -> {ok, {telega@bot:context(ATYL, ATYM), flow_action(ATYP), flow_instance()}} | {error, ATYM}. next(Ctx, Instance, Step) -> {ok, {Ctx, {next, Step}, Instance}}. -file("src/telega/flow.gleam", 857). ?DOC(" Next navigation with string step (for dynamic navigation)\n"). -spec unsafe_next(telega@bot:context(ATYT, ATYU), flow_instance(), binary()) -> {ok, {telega@bot:context(ATYT, ATYU), flow_action(any()), flow_instance()}} | {error, ATYU}. unsafe_next(Ctx, Instance, Step) -> {ok, {Ctx, {next_string, Step}, Instance}}. -file("src/telega/flow.gleam", 866). ?DOC(" Type-safe goto navigation (clears step data)\n"). -spec goto(telega@bot:context(ATZB, ATZC), flow_instance(), ATZF) -> {ok, {telega@bot:context(ATZB, ATZC), flow_action(ATZF), flow_instance()}} | {error, ATZC}. goto(Ctx, Instance, Step) -> {ok, {Ctx, {go_to, Step}, Instance}}. -file("src/telega/flow.gleam", 875). ?DOC(" Go back to previous step\n"). -spec back(telega@bot:context(ATZJ, ATZK), flow_instance()) -> {ok, {telega@bot:context(ATZJ, ATZK), flow_action(any()), flow_instance()}} | {error, ATZK}. back(Ctx, Instance) -> {ok, {Ctx, back, Instance}}. -file("src/telega/flow.gleam", 883). ?DOC(" Complete the flow\n"). -spec complete(telega@bot:context(ATZR, ATZS), flow_instance()) -> {ok, {telega@bot:context(ATZR, ATZS), flow_action(any()), flow_instance()}} | {error, ATZS}. complete(Ctx, Instance) -> {ok, {Ctx, {complete, erlang:element(3, erlang:element(6, Instance))}, Instance}}. -file("src/telega/flow.gleam", 891). ?DOC(" Cancel the flow\n"). -spec cancel(telega@bot:context(ATZZ, AUAA), flow_instance()) -> {ok, {telega@bot:context(ATZZ, AUAA), flow_action(any()), flow_instance()}} | {error, AUAA}. cancel(Ctx, Instance) -> {ok, {Ctx, cancel, Instance}}. -file("src/telega/flow.gleam", 899). ?DOC(" Wait for user input\n"). -spec wait(telega@bot:context(AUAH, AUAI), flow_instance(), binary()) -> {ok, {telega@bot:context(AUAH, AUAI), flow_action(any()), flow_instance()}} | {error, AUAI}. wait(Ctx, Instance, Token) -> {ok, {Ctx, {wait, Token}, Instance}}. -file("src/telega/flow.gleam", 908). ?DOC(" Wait for callback\n"). -spec wait_callback(telega@bot:context(AUAP, AUAQ), flow_instance(), binary()) -> {ok, {telega@bot:context(AUAP, AUAQ), flow_action(any()), flow_instance()}} | {error, AUAQ}. wait_callback(Ctx, Instance, Token) -> {ok, {Ctx, {wait_callback, <<<>/binary, (erlang:element(2, Instance))/binary>>}, Instance}}. -file("src/telega/flow.gleam", 917). ?DOC(" Exit with result\n"). -spec exit( telega@bot:context(AUAX, AUAY), flow_instance(), gleam@option:option(gleam@dict:dict(binary(), binary())) ) -> {ok, {telega@bot:context(AUAX, AUAY), flow_action(any()), flow_instance()}} | {error, AUAY}. exit(Ctx, Instance, Result) -> {ok, {Ctx, {exit, Result}, Instance}}. -file("src/telega/flow.gleam", 938). ?DOC( " Enter a subflow by name\n" "\n" " This pushes the current flow state onto the stack and starts the subflow.\n" " When the subflow completes with `return_from_subflow`, execution returns\n" " to the parent flow at the step specified in `add_subflow`.\n" "\n" " ## Example\n" " ```gleam\n" " fn my_handler(ctx, instance) {\n" " // Enter address collection subflow\n" " flow.enter_subflow(ctx, instance, \"address_collection\", dict.new())\n" " }\n" " ```\n" ). -spec enter_subflow( telega@bot:context(AUBI, AUBJ), flow_instance(), binary(), gleam@dict:dict(binary(), binary()) ) -> {ok, {telega@bot:context(AUBI, AUBJ), flow_action(any()), flow_instance()}} | {error, AUBJ}. enter_subflow(Ctx, Instance, Subflow_name, Data) -> {ok, {Ctx, {enter_subflow, Subflow_name, Data}, Instance}}. -file("src/telega/flow.gleam", 962). ?DOC( " Return from a subflow with result data\n" "\n" " This pops the parent flow from the stack and merges the result data\n" " into the parent's state, then continues at the return step.\n" "\n" " ## Example\n" " ```gleam\n" " fn final_step_handler(ctx, instance) {\n" " let result = dict.from_list([\n" " #(\"street\", flow.get_data(instance, \"street\") |> option.unwrap(\"\")),\n" " #(\"city\", flow.get_data(instance, \"city\") |> option.unwrap(\"\")),\n" " ])\n" " flow.return_from_subflow(ctx, instance, result)\n" " }\n" " ```\n" ). -spec return_from_subflow( telega@bot:context(AUBS, AUBT), flow_instance(), gleam@dict:dict(binary(), binary()) ) -> {ok, {telega@bot:context(AUBS, AUBT), flow_action(any()), flow_instance()}} | {error, AUBT}. return_from_subflow(Ctx, Instance, Result) -> {ok, {Ctx, {return_from_subflow, Result}, Instance}}. -file("src/telega/flow.gleam", 1062). ?DOC(" Get current step\n"). -spec get_current_step(flow(AUDF, any(), any()), flow_instance()) -> {ok, AUDF} | {error, nil}. get_current_step(Flow, Instance) -> (erlang:element(6, Flow))(erlang:element(2, erlang:element(6, Instance))). -file("src/telega/flow.gleam", 1070). ?DOC(" Store step data\n"). -spec store_step_data(flow_instance(), binary(), binary()) -> flow_instance(). store_step_data(Instance, Key, Value) -> {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), gleam@dict:insert(erlang:element(7, Instance), Key, Value), erlang:element(8, Instance), erlang:element(9, Instance), erlang:element(10, Instance)}. -file("src/telega/flow.gleam", 1082). ?DOC(" Get step data\n"). -spec get_step_data(flow_instance(), binary()) -> gleam@option:option(binary()). get_step_data(Instance, Key) -> _pipe = gleam_stdlib:map_get(erlang:element(7, Instance), Key), gleam@option:from_result(_pipe). -file("src/telega/flow.gleam", 1088). ?DOC(" Clear all step data\n"). -spec clear_step_data(flow_instance()) -> flow_instance(). clear_step_data(Instance) -> {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), maps:new(), erlang:element(8, Instance), erlang:element(9, Instance), erlang:element(10, Instance)}. -file("src/telega/flow.gleam", 1093). ?DOC(" Clear specific step data key\n"). -spec clear_step_data_key(flow_instance(), binary()) -> flow_instance(). clear_step_data_key(Instance, Key) -> {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), gleam@dict:delete(erlang:element(7, Instance), Key), erlang:element(8, Instance), erlang:element(9, Instance), erlang:element(10, Instance)}. -file("src/telega/flow.gleam", 1100). -spec is_callback_passed(flow_instance(), binary(), binary()) -> gleam@option:option(boolean()). is_callback_passed(Instance, Key, Callback_id) -> case get_step_data(Instance, Key) of {some, Data} -> Callback_data = telega@keyboard:bool_callback_data(Callback_id), case telega@keyboard:unpack_callback(Data, Callback_data) of {ok, Callback} -> {some, erlang:element(2, Callback)}; {error, _} -> none end; none -> none end. -file("src/telega/flow.gleam", 1118). ?DOC(" Store data in the flow instance\n"). -spec store_data(flow_instance(), binary(), binary()) -> flow_instance(). store_data(Instance, Key, Value) -> {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record = erlang:element(6, Instance), {flow_state, erlang:element(2, _record), gleam@dict:insert( erlang:element(3, erlang:element(6, Instance)), Key, Value ), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), erlang:element(10, Instance)}. -file("src/telega/flow.gleam", 1133). ?DOC(" Get data from the flow instance\n"). -spec get_data(flow_instance(), binary()) -> gleam@option:option(binary()). get_data(Instance, Key) -> _pipe = gleam_stdlib:map_get( erlang:element(3, erlang:element(6, Instance)), Key ), _pipe@1 = gleam@result:map(_pipe, fun(Field@0) -> {some, Field@0} end), gleam@result:unwrap(_pipe@1, none). -file("src/telega/flow.gleam", 1140). ?DOC(" Update instance data and continue to next step\n"). -spec next_with_data( telega@bot:context(AUDQ, AUDR), flow_instance(), AUDU, binary(), binary() ) -> {ok, {telega@bot:context(AUDQ, AUDR), flow_action(AUDU), flow_instance()}} | {error, AUDR}. next_with_data(Ctx, Instance, Step, Key, Value) -> Updated_instance = store_data(Instance, Key, Value), next(Ctx, Updated_instance, Step). -file("src/telega/flow.gleam", 1151). -spec find_instance_by_token(flow_storage(AUDY), binary()) -> {ok, gleam@option:option(flow_instance())} | {error, AUDY}. find_instance_by_token(Storage, Token) -> case gleam@string:split(Token, <<":"/utf8>>) of [Instance_id | _] -> (erlang:element(3, Storage))(Instance_id); _ -> {ok, none} end. -file("src/telega/flow.gleam", 1239). ?DOC(" Run the enter hook if present\n"). -spec run_enter_hook( gleam@option:option(fun((telega@bot:context(AUEP, AUEQ), flow_instance()) -> {ok, {telega@bot:context(AUEP, AUEQ), flow_instance()}} | {error, AUEQ})), telega@bot:context(AUEP, AUEQ), flow_instance() ) -> {ok, {telega@bot:context(AUEP, AUEQ), flow_instance()}} | {error, AUEQ}. run_enter_hook(Hook, Ctx, Instance) -> case Hook of {some, Enter_fn} -> Enter_fn(Ctx, Instance); none -> {ok, {Ctx, Instance}} end. -file("src/telega/flow.gleam", 1251). ?DOC(" Run the leave hook if present\n"). -spec run_leave_hook( gleam@option:option(fun((telega@bot:context(AUFA, AUFB), flow_instance()) -> {ok, {telega@bot:context(AUFA, AUFB), flow_instance()}} | {error, AUFB})), telega@bot:context(AUFA, AUFB), flow_instance() ) -> {ok, {telega@bot:context(AUFA, AUFB), flow_instance()}} | {error, AUFB}. run_leave_hook(Hook, Ctx, Instance) -> case Hook of {some, Leave_fn} -> Leave_fn(Ctx, Instance); none -> {ok, {Ctx, Instance}} end. -file("src/telega/flow.gleam", 1263). ?DOC(" Run the flow enter hook if present\n"). -spec run_flow_enter_hook( gleam@option:option(fun((telega@bot:context(AUFL, AUFM), flow_instance()) -> {ok, {telega@bot:context(AUFL, AUFM), flow_instance()}} | {error, AUFM})), telega@bot:context(AUFL, AUFM), flow_instance() ) -> {ok, {telega@bot:context(AUFL, AUFM), flow_instance()}} | {error, AUFM}. run_flow_enter_hook(Hook, Ctx, Instance) -> case Hook of {some, Enter_fn} -> Enter_fn(Ctx, Instance); none -> {ok, {Ctx, Instance}} end. -file("src/telega/flow.gleam", 1275). ?DOC(" Run the flow leave hook if present\n"). -spec run_flow_leave_hook( gleam@option:option(fun((telega@bot:context(AUFW, AUFX), flow_instance()) -> {ok, {telega@bot:context(AUFW, AUFX), flow_instance()}} | {error, AUFX})), telega@bot:context(AUFW, AUFX), flow_instance() ) -> {ok, {telega@bot:context(AUFW, AUFX), flow_instance()}} | {error, AUFX}. run_flow_leave_hook(Hook, Ctx, Instance) -> case Hook of {some, Leave_fn} -> Leave_fn(Ctx, Instance); none -> {ok, {Ctx, Instance}} end. -file("src/telega/flow.gleam", 1287). ?DOC(" Run the flow exit hook if present\n"). -spec run_flow_exit_hook( gleam@option:option(fun((telega@bot:context(AUGH, AUGI), flow_instance()) -> {ok, telega@bot:context(AUGH, AUGI)} | {error, AUGI})), telega@bot:context(AUGH, AUGI), flow_instance() ) -> {ok, telega@bot:context(AUGH, AUGI)} | {error, AUGI}. run_flow_exit_hook(Hook, Ctx, Instance) -> case Hook of {some, Exit_fn} -> Exit_fn(Ctx, Instance); none -> {ok, Ctx} end. -file("src/telega/flow.gleam", 1668). -spec handle_error( flow(any(), AUHW, AUHX), telega@bot:context(AUHW, AUHX), flow_instance(), gleam@option:option(AUHX) ) -> {ok, telega@bot:context(AUHW, AUHX)} | {error, AUHX}. handle_error(Flow, Ctx, Instance, Error) -> case erlang:element(9, Flow) of {some, Handler} -> case Handler(Ctx, Instance, Error) of {ok, New_ctx} -> {ok, New_ctx}; {error, _} -> {ok, Ctx} end; none -> {ok, Ctx} end. -file("src/telega/flow.gleam", 1685). -spec generate_flow_id(integer(), integer(), binary()) -> binary(). generate_flow_id(User_id, Chat_id, Flow_name) -> <<<<<<<>/binary, (erlang:integer_to_binary(Chat_id))/binary>>/binary, "_"/utf8>>/binary, (erlang:integer_to_binary(User_id))/binary>>. -file("src/telega/flow.gleam", 1690). ?DOC(" Create in-memory storage (for testing)\n"). -spec create_memory_storage() -> flow_storage(any()). create_memory_storage() -> {flow_storage, fun(_) -> {ok, nil} end, fun(_) -> {ok, none} end, fun(_) -> {ok, nil} end, fun(_, _) -> {ok, []} end}. -file("src/telega/flow.gleam", 1700). ?DOC(" Helper to create a string_to_step function for a list of steps\n"). -spec create_string_to_step(list({binary(), AUIK})) -> fun((binary()) -> {ok, AUIK} | {error, nil}). create_string_to_step(Steps) -> Step_dict = maps:from_list(Steps), fun(Name) -> gleam_stdlib:map_get(Step_dict, Name) end. -file("src/telega/flow.gleam", 341). ?DOC(" Create a flow builder with default string conversion (uses string.inspect)\n"). -spec new_with_default_converters( binary(), flow_storage(ATOM), list({binary(), ATOO}) ) -> flow_builder(ATOO, any(), ATOM). new_with_default_converters(Flow_name, Storage, Steps) -> {flow_builder, Flow_name, maps:new(), fun gleam@string:inspect/1, create_string_to_step(Steps), Storage, none, none, [], [], [], [], none, none, none}. -file("src/telega/flow.gleam", 1708). ?DOC(" Generate a unique wait token\n"). -spec generate_wait_token(flow_instance()) -> binary(). generate_wait_token(Instance) -> <<<<(erlang:element(2, Instance))/binary, "_"/utf8>>/binary, (erlang:integer_to_binary(telega@internal@utils:current_time_ms()))/binary>>. -file("src/telega/flow.gleam", 1713). ?DOC(" Create a text input step\n"). -spec text_step(binary(), binary(), AUIO) -> fun((telega@bot:context(AUIP, AUIQ), flow_instance()) -> {ok, {telega@bot:context(AUIP, AUIQ), flow_action(AUIO), flow_instance()}} | {error, AUIQ}). text_step(Prompt, Data_key, Next_step) -> fun(Ctx, Instance) -> case get_step_data(Instance, <<"user_input"/utf8>>) of {some, Value} -> Instance@1 = begin _pipe = Instance, _pipe@1 = store_data(_pipe, Data_key, Value), clear_step_data_key(_pipe@1, <<"user_input"/utf8>>) end, {ok, {Ctx, {next, Next_step}, Instance@1}}; none -> case telega@reply:with_text(Ctx, Prompt) of {ok, _} -> Token = generate_wait_token(Instance), {ok, {Ctx, {wait, Token}, Instance}}; {error, _} -> {ok, {Ctx, cancel, Instance}} end end end. -file("src/telega/flow.gleam", 1741). ?DOC(" Create a message display step\n"). -spec message_step( fun((flow_instance()) -> binary()), gleam@option:option(AUIU) ) -> fun((telega@bot:context(AUIW, AUIX), flow_instance()) -> {ok, {telega@bot:context(AUIW, AUIX), flow_action(AUIU), flow_instance()}} | {error, AUIX}). message_step(Message_fn, Next_step) -> fun(Ctx, Instance) -> Message = Message_fn(Instance), case telega@reply:with_text(Ctx, Message) of {ok, _} -> case Next_step of {some, Step} -> {ok, {Ctx, {next, Step}, Instance}}; none -> {ok, {Ctx, {complete, erlang:element( 3, erlang:element(6, Instance) )}, Instance}} end; {error, _} -> {ok, {Ctx, cancel, Instance}} end end. -file("src/telega/flow.gleam", 1847). ?DOC(" Extract user and chat IDs from context\n"). -spec extract_ids_from_context(telega@bot:context(any(), any())) -> {integer(), integer()}. extract_ids_from_context(Ctx) -> {erlang:element(2, erlang:element(3, Ctx)), erlang:element(3, erlang:element(3, Ctx))}. -file("src/telega/flow.gleam", 1934). -spec unsafe_coerce(any()) -> any(). unsafe_coerce(Value) -> _pipe = Value, _pipe@1 = erlang:term_to_binary(_pipe), erlang:binary_to_term(_pipe@1). -file("src/telega/flow.gleam", 620). ?DOC( " Add an inline subflow defined within the parent flow\n" "\n" " This creates a subflow inline without needing to define a separate flow.\n" " Steps are identified by string names.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let checkout_flow =\n" " flow.new(\"checkout\", storage, step_to_string, string_to_step)\n" " |> flow.add_step(Cart, cart_handler)\n" " |> flow.with_inline_subflow(\n" " name: \"address_collection\",\n" " trigger: CollectAddress,\n" " return_to: Payment,\n" " initial: \"street\",\n" " steps: [\n" " #(\"street\", fn(ctx, instance) {\n" " // Ask for street\n" " flow.wait(ctx, instance, \"street_input\")\n" " }),\n" " #(\"city\", fn(ctx, instance) {\n" " // Ask for city\n" " flow.wait(ctx, instance, \"city_input\")\n" " }),\n" " #(\"done\", fn(ctx, instance) {\n" " flow.return_from_subflow(ctx, instance, instance.state.data)\n" " }),\n" " ],\n" " )\n" " |> flow.add_step(Payment, payment_handler)\n" " |> flow.build(initial: Cart)\n" " ```\n" ). -spec with_inline_subflow( flow_builder(ATTZ, ATUA, ATUB), binary(), ATTZ, ATTZ, binary(), list({binary(), fun((telega@bot:context(ATUA, ATUB), flow_instance()) -> {ok, {telega@bot:context(ATUA, ATUB), flow_action(inline_step()), flow_instance()}} | {error, ATUB})}) ) -> flow_builder(ATTZ, ATUA, ATUB). with_inline_subflow(Builder, Name, Trigger, Return_to, Initial, Steps) -> Inline_flow_name = <<<<(erlang:element(2, Builder))/binary, "::"/utf8>>/binary, Name/binary>>, Inline_storage = erlang:element(6, Builder), Inline_builder = new( Inline_flow_name, Inline_storage, fun inline_step_to_string/1, fun string_to_inline_step/1 ), Inline_builder@1 = gleam@list:fold( Steps, Inline_builder, fun(B, Step_tuple) -> {Step_name, Handler} = Step_tuple, add_step(B, {inline_step, Step_name}, Handler) end ), Inline_flow = build(Inline_builder@1, {inline_step, Initial}), Coerced_flow = unsafe_coerce(Inline_flow), add_subflow( Builder, Trigger, Coerced_flow, Return_to, fun(Instance) -> erlang:element(3, erlang:element(6, Instance)) end, fun(Result, Instance@1) -> {flow_instance, erlang:element(2, Instance@1), erlang:element(3, Instance@1), erlang:element(4, Instance@1), erlang:element(5, Instance@1), begin _record = erlang:element(6, Instance@1), {flow_state, erlang:element(2, _record), maps:merge( erlang:element(3, erlang:element(6, Instance@1)), Result ), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance@1), erlang:element(8, Instance@1), erlang:element(9, Instance@1), erlang:element(10, Instance@1)} end ). -file("src/telega/flow.gleam", 678). ?DOC(" Add an inline subflow with custom argument and result mapping\n"). -spec with_inline_subflow_mapped( flow_builder(ATUO, ATUP, ATUQ), binary(), ATUO, ATUO, binary(), list({binary(), fun((telega@bot:context(ATUP, ATUQ), flow_instance()) -> {ok, {telega@bot:context(ATUP, ATUQ), flow_action(inline_step()), flow_instance()}} | {error, ATUQ})}), fun((flow_instance()) -> gleam@dict:dict(binary(), binary())), fun((gleam@dict:dict(binary(), binary()), flow_instance()) -> flow_instance()) ) -> flow_builder(ATUO, ATUP, ATUQ). with_inline_subflow_mapped( Builder, Name, Trigger, Return_to, Initial, Steps, Map_args, Map_result ) -> Inline_flow_name = <<<<(erlang:element(2, Builder))/binary, "::"/utf8>>/binary, Name/binary>>, Inline_storage = erlang:element(6, Builder), Inline_builder = new( Inline_flow_name, Inline_storage, fun inline_step_to_string/1, fun string_to_inline_step/1 ), Inline_builder@1 = gleam@list:fold( Steps, Inline_builder, fun(B, Step_tuple) -> {Step_name, Handler} = Step_tuple, add_step(B, {inline_step, Step_name}, Handler) end ), Inline_flow = build(Inline_builder@1, {inline_step, Initial}), Coerced_flow = unsafe_coerce(Inline_flow), add_subflow(Builder, Trigger, Coerced_flow, Return_to, Map_args, Map_result). -file("src/telega/flow.gleam", 1939). ?DOC(" Create a new empty flow registry\n"). -spec new_registry() -> flow_registry(any(), any()). new_registry() -> {flow_registry, [], maps:new()}. -file("src/telega/flow.gleam", 1953). ?DOC(" Add a flow to the registry with a trigger and initial data\n"). -spec register_with_data( flow_registry(AUOH, AUOI), flow_trigger(), flow(any(), AUOH, AUOI), gleam@dict:dict(binary(), binary()) ) -> flow_registry(AUOH, AUOI). register_with_data(Registry, Trigger, Flow, Initial_data) -> Coerced_flow = unsafe_coerce(Flow), {flow_registry, lists:append( erlang:element(2, Registry), [{Trigger, Coerced_flow, Initial_data}] ), gleam@dict:insert( erlang:element(3, Registry), erlang:element(2, Flow), Coerced_flow )}. -file("src/telega/flow.gleam", 1944). ?DOC(" Add a flow to the registry with a trigger\n"). -spec register( flow_registry(AUNX, AUNY), flow_trigger(), flow(any(), AUNX, AUNY) ) -> flow_registry(AUNX, AUNY). register(Registry, Trigger, Flow) -> register_with_data(Registry, Trigger, Flow, maps:new()). -file("src/telega/flow.gleam", 1967). ?DOC(" Register a flow without a trigger (for calling from handlers)\n"). -spec register_callable(flow_registry(AUOT, AUOU), flow(any(), AUOT, AUOU)) -> flow_registry(AUOT, AUOU). register_callable(Registry, Flow) -> Coerced_flow = unsafe_coerce(Flow), {flow_registry, erlang:element(2, Registry), gleam@dict:insert( erlang:element(3, Registry), erlang:element(2, Flow), Coerced_flow )}. -file("src/telega/flow.gleam", 2126). ?DOC(" Apply middleware chain to handler\n"). -spec apply_middlewares( telega@bot:context(AUQQ, AUQR), flow_instance(), fun(() -> {ok, {telega@bot:context(AUQQ, AUQR), flow_action(AUQU), flow_instance()}} | {error, AUQR}), list(fun((telega@bot:context(AUQQ, AUQR), flow_instance(), fun(() -> {ok, {telega@bot:context(AUQQ, AUQR), flow_action(AUQU), flow_instance()}} | {error, AUQR})) -> {ok, {telega@bot:context(AUQQ, AUQR), flow_action(AUQU), flow_instance()}} | {error, AUQR})) ) -> {ok, {telega@bot:context(AUQQ, AUQR), flow_action(AUQU), flow_instance()}} | {error, AUQR}. apply_middlewares(Ctx, Instance, Handler, Middlewares) -> case Middlewares of [] -> Handler(); [Middleware | Rest] -> Middleware( Ctx, Instance, fun() -> apply_middlewares(Ctx, Instance, Handler, Rest) end ) end. -file("src/telega/flow.gleam", 2143). ?DOC(" Check for conditional transitions\n"). -spec check_conditionals(flow(any(), any(), any()), flow_instance()) -> gleam@option:option(binary()). check_conditionals(Flow, Instance) -> gleam@list:fold( erlang:element(11, Flow), none, fun(Acc, Conditional) -> case Acc of {some, _} -> Acc; none -> case erlang:element(2, Conditional) =:= erlang:element( 2, erlang:element(6, Instance) ) of true -> _pipe = gleam@list:fold( erlang:element(3, Conditional), none, fun(Inner_acc, Cond) -> case Inner_acc of {some, _} -> Inner_acc; none -> {Check_fn, Step} = Cond, case Check_fn(Instance) of true -> {some, (erlang:element(5, Flow))( Step )}; false -> none end end end ), gleam@option:'or'( _pipe, {some, (erlang:element(5, Flow))( erlang:element(4, Conditional) )} ); false -> none end end end ). -file("src/telega/flow.gleam", 2175). ?DOC(" Check if current step triggers parallel execution\n"). -spec check_parallel_trigger(flow(AURM, any(), any()), flow_instance()) -> gleam@option:option(parallel_config(AURM)). check_parallel_trigger(Flow, Instance) -> _pipe = gleam@list:find( erlang:element(12, Flow), fun(Config) -> erlang:element(2, Config) =:= erlang:element( 2, erlang:element(6, Instance) ) end ), gleam@option:from_result(_pipe). -file("src/telega/flow.gleam", 2186). ?DOC(" Check if current step triggers a subflow\n"). -spec check_subflow_trigger(flow(AURU, AURV, AURW), flow_instance()) -> gleam@option:option(subflow_config(AURU, AURV, AURW)). check_subflow_trigger(Flow, Instance) -> _pipe = gleam@list:find( erlang:element(13, Flow), fun(Config) -> erlang:element(2, Config) =:= erlang:element( 2, erlang:element(6, Instance) ) end ), gleam@option:from_result(_pipe). -file("src/telega/flow.gleam", 2426). ?DOC(" Return from subflow to parent flow\n"). -spec return_to_parent_flow( telega@bot:context(AUTY, AUTZ), flow_instance(), gleam@dict:dict(binary(), binary()), subflow_config(any(), AUTY, AUTZ) ) -> {ok, telega@bot:context(AUTY, AUTZ)} | {error, AUTZ}. return_to_parent_flow(Ctx, Instance, Result, Config) -> case erlang:element(5, erlang:element(6, Instance)) of [Frame | Rest_stack] -> Temp_instance = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record = erlang:element(6, Instance), {flow_state, erlang:element(2, _record), erlang:element(4, Frame), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), erlang:element(10, Instance)}, Mapped_instance = (erlang:element(6, Config))(Result, Temp_instance), Updated_instance = {flow_instance, erlang:element(2, Mapped_instance), erlang:element(2, Frame), erlang:element(4, Mapped_instance), erlang:element(5, Mapped_instance), begin _record@1 = erlang:element(6, Mapped_instance), {flow_state, erlang:element(3, Frame), erlang:element(3, _record@1), [erlang:element(3, Frame) | erlang:element( 4, erlang:element(6, Mapped_instance) )], Rest_stack, erlang:element(6, _record@1)} end, maps:new(), erlang:element(8, Mapped_instance), erlang:element(9, Mapped_instance), telega@internal@utils:current_time_ms()}, case (erlang:element( 2, erlang:element(7, erlang:element(3, Config)) ))(Updated_instance) of {ok, _} -> {ok, Ctx}; {error, _} -> {ok, Ctx} end; [] -> {ok, Ctx} end. -file("src/telega/flow.gleam", 2467). ?DOC(" Handle error within a subflow\n"). -spec handle_subflow_error( flow(gleam@dynamic:dynamic_(), AUUM, AUUN), telega@bot:context(AUUM, AUUN), flow_instance(), gleam@option:option(AUUN), subflow_config(any(), AUUM, AUUN) ) -> {ok, telega@bot:context(AUUM, AUUN)} | {error, AUUN}. handle_subflow_error(Flow, Ctx, Instance, Error, _) -> case erlang:element(9, Flow) of {some, Handler} -> case Handler(Ctx, Instance, Error) of {ok, New_ctx} -> {ok, New_ctx}; {error, _} -> {ok, Ctx} end; none -> {ok, Ctx} end. -file("src/telega/flow.gleam", 2527). ?DOC(" Merge results from parallel execution\n"). -spec merge_parallel_results( gleam@dict:dict(binary(), binary()), gleam@dict:dict(binary(), gleam@dict:dict(binary(), binary())) ) -> gleam@dict:dict(binary(), binary()). merge_parallel_results(Base_data, Parallel_results) -> gleam@dict:fold( Parallel_results, Base_data, fun(Acc, Step_name, Step_results) -> gleam@dict:fold( Step_results, Acc, fun(Inner_acc, Key, Value) -> gleam@dict:insert( Inner_acc, <<<>/binary, Key/binary>>, Value ) end ) end ). -file("src/telega/flow.gleam", 2654). -spec composed_step_to_string(composed_step()) -> binary(). composed_step_to_string(Step) -> case Step of {composed_flow_step, N} -> <<"flow_"/utf8, (erlang:integer_to_binary(N))/binary>>; composed_select_flow -> <<"select_flow"/utf8>>; composed_start_parallel -> <<"start_parallel"/utf8>>; {composed_parallel_flow, N@1} -> <<"parallel_"/utf8, (erlang:integer_to_binary(N@1))/binary>>; composed_merge_results -> <<"merge_results"/utf8>> end. -file("src/telega/flow.gleam", 2664). -spec string_to_composed_step(binary()) -> {ok, composed_step()} | {error, nil}. string_to_composed_step(String) -> case String of <<"select_flow"/utf8>> -> {ok, composed_select_flow}; <<"start_parallel"/utf8>> -> {ok, composed_start_parallel}; <<"merge_results"/utf8>> -> {ok, composed_merge_results}; _ -> case gleam_stdlib:string_starts_with(String, <<"flow_"/utf8>>) of true -> _pipe = gleam@string:drop_start(String, 5), _pipe@1 = gleam_stdlib:parse_int(_pipe), _pipe@2 = gleam@result:map( _pipe@1, fun(Field@0) -> {composed_flow_step, Field@0} end ), gleam@result:replace_error(_pipe@2, nil); false -> case gleam_stdlib:string_starts_with( String, <<"parallel_"/utf8>> ) of true -> _pipe@3 = gleam@string:drop_start(String, 9), _pipe@4 = gleam_stdlib:parse_int(_pipe@3), _pipe@5 = gleam@result:map( _pipe@4, fun(Field@0) -> {composed_parallel_flow, Field@0} end ), gleam@result:replace_error(_pipe@5, nil); false -> {error, nil} end end end. -file("src/telega/flow.gleam", 2710). -spec validation_middleware( fun((flow_instance()) -> {ok, nil} | {error, binary()}) ) -> fun((telega@bot:context(AUXY, AUXZ), flow_instance(), fun(() -> {ok, {telega@bot:context(AUXY, AUXZ), flow_action(AUXX), flow_instance()}} | {error, AUXZ})) -> {ok, {telega@bot:context(AUXY, AUXZ), flow_action(AUXX), flow_instance()}} | {error, AUXZ}). validation_middleware(Validator) -> fun(Ctx, Instance, Next) -> case Validator(Instance) of {ok, _} -> Next(); {error, Msg} -> case telega@reply:with_text( Ctx, <<"Validation failed: "/utf8, Msg/binary>> ) of {ok, _} -> {ok, {Ctx, back, Instance}}; {error, _} -> {ok, {Ctx, cancel, Instance}} end end end. -file("src/telega/flow.gleam", 1320). -spec process_action( flow(AUHI, AUHJ, AUHK), telega@bot:context(AUHJ, AUHK), flow_action(AUHI), flow_instance() ) -> {ok, telega@bot:context(AUHJ, AUHK)} | {error, AUHK}. process_action(Flow, Ctx, Action, Instance) -> case Action of {next, Step} -> Step_name = (erlang:element(5, Flow))(Step), Updated_instance = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record = erlang:element(6, Instance), {flow_state, Step_name, erlang:element(3, _record), [erlang:element(2, erlang:element(6, Instance)) | erlang:element(4, erlang:element(6, Instance))], erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))(Updated_instance) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance); {error, Err} -> handle_error(Flow, Ctx, Instance, {some, Err}) end; {next_string, Step_name@1} -> Updated_instance@1 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@1 = erlang:element(6, Instance), {flow_state, Step_name@1, erlang:element(3, _record@1), [erlang:element(2, erlang:element(6, Instance)) | erlang:element(4, erlang:element(6, Instance))], erlang:element(5, _record@1), erlang:element(6, _record@1)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@1 ) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance@1); {error, Err@1} -> handle_error(Flow, Ctx, Instance, {some, Err@1}) end; {return_from_subflow, Result} -> case erlang:element(5, erlang:element(6, Instance)) of [Frame | Rest_stack] -> Updated_instance@2 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@2 = erlang:element(6, Instance), {flow_state, erlang:element(3, Frame), maps:merge( erlang:element( 3, erlang:element(6, Instance) ), Result ), erlang:element(4, _record@2), Rest_stack, erlang:element(6, _record@2)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@2 ) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance@2); {error, Err@2} -> handle_error(Flow, Ctx, Instance, {some, Err@2}) end; [] -> {ok, Ctx} end; {start_parallel, Steps, Join_at} -> Step_names = gleam@list:map(Steps, erlang:element(5, Flow)), Join_step_name = (erlang:element(5, Flow))(Join_at), Parallel_state = {parallel_state, Step_names, [], maps:new(), Join_step_name}, Updated_instance@3 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@3 = erlang:element(6, Instance), {flow_state, erlang:element(2, _record@3), erlang:element(3, _record@3), erlang:element(4, _record@3), erlang:element(5, _record@3), {some, Parallel_state}} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@3 ) of {ok, _} -> case Step_names of [First | _] -> Step_instance = {flow_instance, erlang:element(2, Updated_instance@3), erlang:element(3, Updated_instance@3), erlang:element(4, Updated_instance@3), erlang:element(5, Updated_instance@3), begin _record@4 = erlang:element( 6, Updated_instance@3 ), {flow_state, First, erlang:element(3, _record@4), erlang:element(4, _record@4), erlang:element(5, _record@4), erlang:element(6, _record@4)} end, erlang:element(7, Updated_instance@3), erlang:element(8, Updated_instance@3), erlang:element(9, Updated_instance@3), erlang:element(10, Updated_instance@3)}, execute_step(Flow, Ctx, Step_instance); [] -> {ok, Ctx} end; {error, Err@3} -> handle_error(Flow, Ctx, Instance, {some, Err@3}) end; {complete_parallel_step, Step@1, Result@1} -> case erlang:element(6, erlang:element(6, Instance)) of {some, Parallel_state@1} -> Step_name@2 = (erlang:element(5, Flow))(Step@1), Updated_parallel = {parallel_state, gleam@list:filter( erlang:element(2, Parallel_state@1), fun(S) -> S /= Step_name@2 end ), [Step_name@2 | erlang:element(3, Parallel_state@1)], gleam@dict:insert( erlang:element(4, Parallel_state@1), Step_name@2, Result@1 ), erlang:element(5, Parallel_state@1)}, case erlang:element(2, Updated_parallel) of [] -> Updated_instance@4 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@5 = erlang:element(6, Instance), {flow_state, erlang:element(5, Updated_parallel), merge_parallel_results( erlang:element( 3, erlang:element(6, Instance) ), erlang:element(4, Updated_parallel) ), erlang:element(4, _record@5), erlang:element(5, _record@5), none} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@4 ) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance@4); {error, Err@4} -> handle_error( Flow, Ctx, Instance, {some, Err@4} ) end; [Next | _] -> Updated_instance@5 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@6 = erlang:element(6, Instance), {flow_state, Next, erlang:element(3, _record@6), erlang:element(4, _record@6), erlang:element(5, _record@6), {some, Updated_parallel}} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@5 ) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance@5); {error, Err@5} -> handle_error( Flow, Ctx, Instance, {some, Err@5} ) end end; none -> {ok, Ctx} end; {go_to, Step@2} -> Step_name@3 = (erlang:element(5, Flow))(Step@2), Updated_instance@6 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), {flow_state, Step_name@3, erlang:element(3, erlang:element(6, Instance)), [Step_name@3], [], none}, maps:new(), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@6 ) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance@6); {error, Err@6} -> handle_error(Flow, Ctx, Instance, {some, Err@6}) end; back -> case erlang:element(4, erlang:element(6, Instance)) of [Previous_step | Rest] -> Updated_instance@7 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@7 = erlang:element(6, Instance), {flow_state, Previous_step, erlang:element(3, _record@7), Rest, erlang:element(5, _record@7), erlang:element(6, _record@7)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@7 ) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance@7); {error, Err@7} -> handle_error(Flow, Ctx, Instance, {some, Err@7}) end; [] -> {ok, Ctx} end; {complete, Data} -> Completed_instance = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@8 = erlang:element(6, Instance), {flow_state, erlang:element(2, _record@8), Data, erlang:element(4, _record@8), erlang:element(5, _record@8), erlang:element(6, _record@8)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), erlang:element(10, Instance)}, case erlang:element(8, Flow) of {some, Handler} -> case Handler(Ctx, Completed_instance) of {ok, New_ctx} -> case run_flow_exit_hook( erlang:element(16, Flow), New_ctx, Completed_instance ) of {ok, Final_ctx} -> _ = (erlang:element( 4, erlang:element(7, Flow) ))(erlang:element(2, Instance)), {ok, Final_ctx}; {error, Err@8} -> handle_error( Flow, Ctx, Instance, {some, Err@8} ) end; {error, Err@9} -> handle_error(Flow, Ctx, Instance, {some, Err@9}) end; none -> case run_flow_exit_hook( erlang:element(16, Flow), Ctx, Completed_instance ) of {ok, Final_ctx@1} -> _ = (erlang:element(4, erlang:element(7, Flow)))( erlang:element(2, Instance) ), {ok, Final_ctx@1}; {error, Err@10} -> handle_error(Flow, Ctx, Instance, {some, Err@10}) end end; cancel -> case run_flow_exit_hook(erlang:element(16, Flow), Ctx, Instance) of {ok, Final_ctx@2} -> _ = (erlang:element(4, erlang:element(7, Flow)))( erlang:element(2, Instance) ), {ok, Final_ctx@2}; {error, Err@11} -> handle_error(Flow, Ctx, Instance, {some, Err@11}) end; {wait, Token} -> Updated_instance@8 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), erlang:element(7, Instance), {some, Token}, erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@8 ) of {ok, _} -> {ok, Ctx}; {error, Err@12} -> handle_error(Flow, Ctx, Instance, {some, Err@12}) end; {wait_callback, Token@1} -> Updated_instance@9 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), erlang:element(7, Instance), {some, Token@1}, erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@9 ) of {ok, _} -> {ok, Ctx}; {error, Err@13} -> handle_error(Flow, Ctx, Instance, {some, Err@13}) end; {exit, _} -> _ = (erlang:element(4, erlang:element(7, Flow)))( erlang:element(2, Instance) ), {ok, Ctx}; {enter_subflow, Subflow_name, Data@1} -> case gleam@list:find( erlang:element(13, Flow), fun(Config) -> erlang:element(2, erlang:element(3, Config)) =:= Subflow_name end ) of {ok, Subflow_config} -> Return_step = (erlang:element(5, Flow))( erlang:element(4, Subflow_config) ), Stack_frame = {flow_stack_frame, erlang:element(2, Flow), Return_step, erlang:element(3, erlang:element(6, Instance))}, Subflow_initial_step = (erlang:element( 5, erlang:element(3, Subflow_config) ))(erlang:element(4, erlang:element(3, Subflow_config))), Updated_instance@10 = {flow_instance, erlang:element(2, Instance), erlang:element(2, erlang:element(3, Subflow_config)), erlang:element(4, Instance), erlang:element(5, Instance), {flow_state, Subflow_initial_step, maps:merge( erlang:element(3, erlang:element(6, Instance)), Data@1 ), [Subflow_initial_step], [Stack_frame | erlang:element(5, erlang:element(6, Instance))], none}, maps:new(), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@10 ) of {ok, _} -> execute_subflow_step( erlang:element(3, Subflow_config), Ctx, Updated_instance@10, Subflow_config ); {error, Err@14} -> handle_error(Flow, Ctx, Instance, {some, Err@14}) end; {error, _} -> {ok, Ctx} end end. -file("src/telega/flow.gleam", 1161). -spec execute_step( flow(any(), AUEE, AUEF), telega@bot:context(AUEE, AUEF), flow_instance() ) -> {ok, telega@bot:context(AUEE, AUEF)} | {error, AUEF}. execute_step(Flow, Ctx, Instance) -> case check_conditionals(Flow, Instance) of {some, Next_step} -> Updated_instance = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record = erlang:element(6, Instance), {flow_state, Next_step, erlang:element(3, _record), [erlang:element(2, erlang:element(6, Instance)) | erlang:element(4, erlang:element(6, Instance))], erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))(Updated_instance) of {ok, _} -> execute_step(Flow, Ctx, Updated_instance); {error, Err} -> handle_error(Flow, Ctx, Instance, {some, Err}) end; none -> case check_parallel_trigger(Flow, Instance) of {some, Config} -> start_parallel_execution(Flow, Ctx, Instance, Config); none -> case check_subflow_trigger(Flow, Instance) of {some, Subflow_config} -> start_subflow_execution( Flow, Ctx, Instance, Subflow_config ); none -> case gleam_stdlib:map_get( erlang:element(3, Flow), erlang:element(2, erlang:element(6, Instance)) ) of {ok, Config@1} -> case run_enter_hook( erlang:element(4, Config@1), Ctx, Instance ) of {ok, {Ctx_after_enter, Instance_after_enter}} -> Handler_fn = fun() -> (erlang:element(2, Config@1))( Ctx_after_enter, Instance_after_enter ) end, Result = apply_middlewares( Ctx_after_enter, Instance_after_enter, Handler_fn, lists:append( erlang:element(10, Flow), erlang:element(3, Config@1) ) ), case Result of {ok, {New_ctx, Action, New_instance}} -> process_action_with_leave_hook( Flow, New_ctx, Action, New_instance, erlang:element( 5, Config@1 ) ); {error, Err@1} -> handle_error( Flow, Ctx, Instance, {some, Err@1} ) end; {error, Err@2} -> handle_error( Flow, Ctx, Instance, {some, Err@2} ) end; {error, _} -> handle_error(Flow, Ctx, Instance, none) end end end end. -file("src/telega/flow.gleam", 1299). ?DOC(" Process action with leave hook support\n"). -spec process_action_with_leave_hook( flow(AUGS, AUGT, AUGU), telega@bot:context(AUGT, AUGU), flow_action(AUGS), flow_instance(), gleam@option:option(fun((telega@bot:context(AUGT, AUGU), flow_instance()) -> {ok, {telega@bot:context(AUGT, AUGU), flow_instance()}} | {error, AUGU})) ) -> {ok, telega@bot:context(AUGT, AUGU)} | {error, AUGU}. process_action_with_leave_hook(Flow, Ctx, Action, Instance, Leave_hook) -> case Action of {wait, _} -> process_action(Flow, Ctx, Action, Instance); {wait_callback, _} -> process_action(Flow, Ctx, Action, Instance); _ -> case run_leave_hook(Leave_hook, Ctx, Instance) of {ok, {Ctx_after_leave, Instance_after_leave}} -> process_action( Flow, Ctx_after_leave, Action, Instance_after_leave ); {error, Err} -> handle_error(Flow, Ctx, Instance, {some, Err}) end end. -file("src/telega/flow.gleam", 970). -spec start_or_resume( flow(any(), AUCD, AUCE), telega@bot:context(AUCD, AUCE), integer(), integer(), gleam@dict:dict(binary(), binary()) ) -> {ok, telega@bot:context(AUCD, AUCE)} | {error, AUCE}. start_or_resume(Flow, Ctx, User_id, Chat_id, Initial_data) -> Flow_id = generate_flow_id(User_id, Chat_id, erlang:element(2, Flow)), case (erlang:element(3, erlang:element(7, Flow)))(Flow_id) of {ok, {some, Instance}} -> execute_step(Flow, Ctx, Instance); {ok, none} -> Initial_step_name = (erlang:element(5, Flow))( erlang:element(4, Flow) ), New_instance = {flow_instance, Flow_id, erlang:element(2, Flow), User_id, Chat_id, {flow_state, Initial_step_name, Initial_data, [Initial_step_name], [], none}, maps:new(), none, telega@internal@utils:current_time_ms(), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))(New_instance) of {ok, _} -> case run_flow_enter_hook( erlang:element(14, Flow), Ctx, New_instance ) of {ok, {Ctx_after_enter, Instance_after_enter}} -> execute_step( Flow, Ctx_after_enter, Instance_after_enter ); {error, Err} -> handle_error(Flow, Ctx, New_instance, {some, Err}) end; {error, Err@1} -> handle_error(Flow, Ctx, New_instance, {some, Err@1}) end; {error, Err@2} -> Dummy_instance = {flow_instance, Flow_id, erlang:element(2, Flow), User_id, Chat_id, {flow_state, <<""/utf8>>, maps:new(), [], [], none}, maps:new(), none, 0, 0}, handle_error(Flow, Ctx, Dummy_instance, {some, Err@2}) end. -file("src/telega/flow.gleam", 1038). -spec resume_with_token( flow(any(), AUCR, AUCS), telega@bot:context(AUCR, AUCS), binary(), gleam@option:option(gleam@dict:dict(binary(), binary())) ) -> {ok, telega@bot:context(AUCR, AUCS)} | {error, AUCS}. resume_with_token(Flow, Ctx, Token, Data) -> case find_instance_by_token(erlang:element(7, Flow), Token) of {ok, {some, Instance}} -> Updated_instance = case Data of {some, D} -> {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), maps:merge(erlang:element(7, Instance), D), none, erlang:element(9, Instance), erlang:element(10, Instance)}; none -> {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), erlang:element(7, Instance), none, erlang:element(9, Instance), erlang:element(10, Instance)} end, execute_step(Flow, Ctx, Updated_instance); _ -> {ok, Ctx} end. -file("src/telega/flow.gleam", 1777). ?DOC(" Create a text handler for resuming flows\n"). -spec create_text_handler(flow(any(), AUKB, AUKC)) -> fun((telega@bot:context(AUKB, AUKC), telega@update:update()) -> {ok, telega@bot:context(AUKB, AUKC)} | {error, AUKC}). create_text_handler(Flow) -> fun(Ctx, Update) -> case Update of {text_update, From_id, Chat_id, Text, _, _} -> case (erlang:element(5, erlang:element(7, Flow)))( From_id, Chat_id ) of {ok, [Instance | _]} when erlang:element(8, Instance) =/= none -> Data = maps:from_list([{<<"user_input"/utf8>>, Text}]), resume_with_token( Flow, Ctx, gleam@option:unwrap( erlang:element(8, Instance), <<""/utf8>> ), {some, Data} ); _ -> {ok, Ctx} end; _ -> {ok, Ctx} end end. -file("src/telega/flow.gleam", 1852). ?DOC(" Start a flow with optional initial data\n"). -spec start( flow(any(), AULD, AULE), gleam@dict:dict(binary(), binary()), telega@bot:context(AULD, AULE) ) -> {ok, telega@bot:context(AULD, AULE)} | {error, AULE}. start(Flow, Initial_data, Ctx) -> {From_id, Chat_id} = extract_ids_from_context(Ctx), start_or_resume(Flow, Ctx, From_id, Chat_id, Initial_data). -file("src/telega/flow.gleam", 1834). ?DOC( " Call a registered flow from any handler\n" "\n" " ## Parameters\n" " - `ctx`: Current context\n" " - `registry`: The flow registry containing the flow\n" " - `flow_name`: Name of the flow to call\n" " - `initial_data`: Initial data to pass to the flow\n" "\n" " ## Example\n" " ```gleam\n" " fn my_handler(ctx, registry, _data) {\n" " let initial_data = dict.from_list([\n" " #(\"user_name\", \"John\"),\n" " #(\"product_id\", \"123\")\n" " ])\n" " flow.call_flow(ctx, registry, \"checkout\", initial_data)\n" " }\n" " ```\n" ). -spec call_flow( telega@bot:context(AUKM, AUKN), flow_registry(AUKM, AUKN), binary(), gleam@dict:dict(binary(), binary()) ) -> {ok, telega@bot:context(AUKM, AUKN)} | {error, AUKN}. call_flow(Ctx, Registry, Flow_name, Initial_data) -> case gleam_stdlib:map_get(erlang:element(3, Registry), Flow_name) of {ok, Found_flow} -> start(Found_flow, Initial_data, Ctx); {error, _} -> {ok, Ctx} end. -file("src/telega/flow.gleam", 1862). ?DOC(" Create a router handler that starts a flow\n"). -spec to_handler(flow(any(), AULR, AULS)) -> fun((telega@bot:context(AULR, AULS), telega@update:command()) -> {ok, telega@bot:context(AULR, AULS)} | {error, AULS}). to_handler(Flow) -> fun(Ctx, _) -> start(Flow, maps:new(), Ctx) end. -file("src/telega/flow.gleam", 1870). ?DOC(" Create a router handler that starts a flow with initial data (internal)\n"). -spec to_handler_with_data( flow(any(), AUMD, AUME), gleam@dict:dict(binary(), binary()) ) -> fun((telega@bot:context(AUMD, AUME), telega@update:command()) -> {ok, telega@bot:context(AUMD, AUME)} | {error, AUME}). to_handler_with_data(Flow, Initial_data) -> fun(Ctx, _) -> start(Flow, Initial_data, Ctx) end. -file("src/telega/flow.gleam", 1879). ?DOC(" Create a router handler for resuming flows from callback queries (internal)\n"). -spec resume_handler(flow(any(), AUMR, AUMS)) -> fun((telega@bot:context(AUMR, AUMS), telega@update:update()) -> {ok, telega@bot:context(AUMR, AUMS)} | {error, AUMS}). resume_handler(Flow) -> fun(Ctx, Update) -> case Update of {callback_query_update, _, _, Query, _} -> Data = gleam@option:unwrap( erlang:element(7, Query), <<""/utf8>> ), Token@1 = case gleam@string:split(Data, <<":"/utf8>>) of [_, Token | _] -> Token; _ -> Data end, resume_with_token(Flow, Ctx, Token@1, none); _ -> {ok, Ctx} end end. -file("src/telega/flow.gleam", 1760). ?DOC(" Create a router handler for resuming flows from callback queries\n"). -spec create_resume_handler(flow(any(), AUJC, AUJD)) -> fun((telega@bot:context(AUJC, AUJD), telega@update:update()) -> {ok, telega@bot:context(AUJC, AUJD)} | {error, AUJD}). create_resume_handler(Flow) -> resume_handler(Flow). -file("src/telega/flow.gleam", 1899). ?DOC(" Create a router handler for resuming flows from callback queries with keyboard parsing (internal)\n"). -spec resume_handler_with_keyboard( flow(any(), AUND, AUNE), telega@keyboard:keyboard_callback_data(binary()) ) -> fun((telega@bot:context(AUND, AUNE), telega@update:update()) -> {ok, telega@bot:context(AUND, AUNE)} | {error, AUNE}). resume_handler_with_keyboard(Flow, Callback_data) -> fun(Ctx, Update) -> case Update of {callback_query_update, _, _, Query, _} -> Data = gleam@option:unwrap( erlang:element(7, Query), <<""/utf8>> ), case telega@keyboard:unpack_callback(Data, Callback_data) of {ok, Callback} -> resume_with_token( Flow, Ctx, erlang:element(2, Callback), none ); {error, _} -> Token@1 = case gleam@string:split(Data, <<":"/utf8>>) of [_, Token | _] -> Token; _ -> Data end, resume_with_token(Flow, Ctx, Token@1, none) end; _ -> {ok, Ctx} end end. -file("src/telega/flow.gleam", 1768). ?DOC(" Create a router handler for resuming flows from callback queries with keyboard parsing\n"). -spec create_resume_handler_with_keyboard( flow(any(), AUJO, AUJP), telega@keyboard:keyboard_callback_data(binary()) ) -> fun((telega@bot:context(AUJO, AUJP), telega@update:update()) -> {ok, telega@bot:context(AUJO, AUJP)} | {error, AUJP}). create_resume_handler_with_keyboard(Flow, Callback_data) -> resume_handler_with_keyboard(Flow, Callback_data). -file("src/telega/flow.gleam", 2003). ?DOC(" Add a flow route to router\n"). -spec add_flow_route( telega@router:router(AUPL, AUPM), flow_trigger(), flow(gleam@dynamic:dynamic_(), AUPL, AUPM), gleam@dict:dict(binary(), binary()) ) -> telega@router:router(AUPL, AUPM). add_flow_route(Router, Trigger, Flow, Initial_data) -> case Trigger of {on_command, Command} -> telega@router:on_command( Router, Command, to_handler_with_data(Flow, Initial_data) ); {on_text, Pattern} -> telega@router:on_text( Router, Pattern, fun(Ctx, _) -> start(Flow, Initial_data, Ctx) end ); {on_callback, Pattern@1} -> telega@router:on_callback( Router, Pattern@1, fun(Ctx@1, _, _) -> start(Flow, Initial_data, Ctx@1) end ); {on_filtered, Filter} -> telega@router:on_filtered( Router, Filter, fun(Ctx@2, _) -> start(Flow, Initial_data, Ctx@2) end ); on_photo -> telega@router:on_photo( Router, fun(Ctx@3, _) -> start(Flow, Initial_data, Ctx@3) end ); on_video -> telega@router:on_video( Router, fun(Ctx@4, _) -> start(Flow, Initial_data, Ctx@4) end ); on_audio -> telega@router:on_audio( Router, fun(Ctx@5, _) -> start(Flow, Initial_data, Ctx@5) end ); on_voice -> telega@router:on_voice( Router, fun(Ctx@6, _) -> start(Flow, Initial_data, Ctx@6) end ); on_any_text -> telega@router:on_any_text( Router, fun(Ctx@7, _) -> start(Flow, Initial_data, Ctx@7) end ) end. -file("src/telega/flow.gleam", 2046). ?DOC(" Auto-resume handler for text messages\n"). -spec auto_resume_handler(flow_registry(AUPW, AUPX)) -> fun((telega@bot:context(AUPW, AUPX), binary()) -> {ok, telega@bot:context(AUPW, AUPX)} | {error, AUPX}). auto_resume_handler(Registry) -> fun(Ctx, Text) -> {User_id, Chat_id} = extract_ids_from_context(Ctx), Flows = maps:values(erlang:element(3, Registry)), Result = gleam@list:fold(Flows, none, fun(Acc, Flow) -> case Acc of {some, _} -> Acc; none -> Flow_id = generate_flow_id( User_id, Chat_id, erlang:element(2, Flow) ), case (erlang:element(3, erlang:element(7, Flow)))( Flow_id ) of {ok, {some, Instance}} when erlang:element( 8, Instance ) =/= none -> Data = maps:from_list( [{<<"user_input"/utf8>>, Text}] ), _pipe = resume_with_token( Flow, Ctx, gleam@option:unwrap( erlang:element(8, Instance), <<""/utf8>> ), {some, Data} ), {some, _pipe}; _ -> none end end end), case Result of {some, Res} -> Res; none -> {ok, Ctx} end end. -file("src/telega/flow.gleam", 2087). ?DOC(" Auto-resume handler for callback queries (internal)\n"). -spec auto_resume_callback_handler(flow_registry(AUQG, AUQH)) -> fun((telega@bot:context(AUQG, AUQH), binary(), binary()) -> {ok, telega@bot:context(AUQG, AUQH)} | {error, AUQH}). auto_resume_callback_handler(Registry) -> fun(Ctx, _, Data) -> {User_id, Chat_id} = extract_ids_from_context(Ctx), Flows = maps:values(erlang:element(3, Registry)), Result = gleam@list:fold(Flows, none, fun(Acc, Flow) -> case Acc of {some, _} -> Acc; none -> Flow_id = generate_flow_id( User_id, Chat_id, erlang:element(2, Flow) ), case (erlang:element(3, erlang:element(7, Flow)))( Flow_id ) of {ok, {some, Instance}} when erlang:element( 8, Instance ) =/= none -> Callback_data = maps:from_list( [{<<"callback_data"/utf8>>, Data}] ), _pipe = resume_with_token( Flow, Ctx, gleam@option:unwrap( erlang:element(8, Instance), <<""/utf8>> ), {some, Callback_data} ), {some, _pipe}; _ -> none end end end), case Result of {some, Res} -> Res; none -> {ok, Ctx} end end. -file("src/telega/flow.gleam", 1979). ?DOC(" Apply all registered flows to a router\n"). -spec apply_to_router( telega@router:router(AUPD, AUPE), flow_registry(AUPD, AUPE) ) -> telega@router:router(AUPD, AUPE). apply_to_router(Router, Registry) -> Router_with_flows = gleam@list:fold( erlang:element(2, Registry), Router, fun(Router@1, Flow_entry) -> {Trigger, Flow, Initial_data} = Flow_entry, add_flow_route(Router@1, Trigger, Flow, Initial_data) end ), case maps:size(erlang:element(3, Registry)) of 0 -> Router_with_flows; _ -> _pipe = Router_with_flows, _pipe@1 = telega@router:on_any_text( _pipe, auto_resume_handler(Registry) ), telega@router:on_callback( _pipe@1, {prefix, <<""/utf8>>}, auto_resume_callback_handler(Registry) ) end. -file("src/telega/flow.gleam", 2486). ?DOC(" Start parallel execution\n"). -spec start_parallel_execution( flow(AUVC, AUVD, AUVE), telega@bot:context(AUVD, AUVE), flow_instance(), parallel_config(AUVC) ) -> {ok, telega@bot:context(AUVD, AUVE)} | {error, AUVE}. start_parallel_execution(Flow, Ctx, Instance, Config) -> Pending_steps = gleam@list:map( erlang:element(3, Config), erlang:element(5, Flow) ), Join_step = (erlang:element(5, Flow))(erlang:element(4, Config)), Parallel_state = {parallel_state, Pending_steps, [], maps:new(), Join_step}, Updated_instance = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record = erlang:element(6, Instance), {flow_state, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), {some, Parallel_state}} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))(Updated_instance) of {ok, _} -> case Pending_steps of [Current_step | _] -> Step_instance = {flow_instance, erlang:element(2, Updated_instance), erlang:element(3, Updated_instance), erlang:element(4, Updated_instance), erlang:element(5, Updated_instance), begin _record@1 = erlang:element(6, Updated_instance), {flow_state, Current_step, erlang:element(3, _record@1), erlang:element(4, _record@1), erlang:element(5, _record@1), erlang:element(6, _record@1)} end, erlang:element(7, Updated_instance), erlang:element(8, Updated_instance), erlang:element(9, Updated_instance), erlang:element(10, Updated_instance)}, execute_step(Flow, Ctx, Step_instance); [] -> {ok, Ctx} end; {error, Err} -> handle_error(Flow, Ctx, Instance, {some, Err}) end. -file("src/telega/flow.gleam", 2559). ?DOC(" Compose flows with conditional selection\n"). -spec compose_conditional( binary(), fun((flow_instance()) -> binary()), gleam@dict:dict(binary(), flow(gleam@dynamic:dynamic_(), AUWH, AUWI)), flow_storage(AUWI) ) -> flow(composed_step(), AUWH, AUWI). compose_conditional(Name, Condition, Flows, Storage) -> Builder = new( Name, Storage, fun composed_step_to_string/1, fun string_to_composed_step/1 ), Builder@1 = add_step( Builder, composed_select_flow, fun(Ctx, Instance) -> Flow_name = Condition(Instance), case gleam_stdlib:map_get(Flows, Flow_name) of {ok, Flow} -> {User_id, Chat_id} = extract_ids_from_context(Ctx), _pipe = start_or_resume( Flow, Ctx, User_id, Chat_id, erlang:element(3, erlang:element(6, Instance)) ), gleam@result:map( _pipe, fun(New_ctx) -> {New_ctx, {complete, erlang:element( 3, erlang:element(6, Instance) )}, Instance} end ); {error, _} -> {ok, {Ctx, cancel, Instance}} end end ), build(Builder@1, composed_select_flow). -file("src/telega/flow.gleam", 2589). ?DOC(" Compose flows for parallel execution\n"). -spec compose_parallel( binary(), list(flow(gleam@dynamic:dynamic_(), AUWS, AUWT)), fun((list(gleam@dict:dict(binary(), binary()))) -> gleam@dict:dict(binary(), binary())), flow_storage(AUWT) ) -> flow(composed_step(), AUWS, AUWT). compose_parallel(Name, Flows, Merge_results, Storage) -> Builder = new( Name, Storage, fun composed_step_to_string/1, fun string_to_composed_step/1 ), Parallel_steps = gleam@list:index_map( Flows, fun(_, Index) -> {composed_parallel_flow, Index} end ), Builder@1 = add_step( Builder, composed_start_parallel, fun(Ctx, Instance) -> {ok, {Ctx, {start_parallel, Parallel_steps, composed_merge_results}, Instance}} end ), Builder@3 = gleam@list:index_fold( Flows, Builder@1, fun(Builder@2, Flow, Index@1) -> Step = {composed_parallel_flow, Index@1}, add_step( Builder@2, Step, fun(Ctx@1, Instance@1) -> {User_id, Chat_id} = extract_ids_from_context(Ctx@1), _pipe = start_or_resume( Flow, Ctx@1, User_id, Chat_id, erlang:element(3, erlang:element(6, Instance@1)) ), gleam@result:map( _pipe, fun(New_ctx) -> {New_ctx, {complete_parallel_step, Step, erlang:element( 3, erlang:element(6, Instance@1) )}, Instance@1} end ) end ) end ), Builder@4 = add_step( Builder@3, composed_merge_results, fun(Ctx@2, Instance@2) -> case erlang:element(6, erlang:element(6, Instance@2)) of {some, State} -> Results = maps:values(erlang:element(4, State)), Merged = Merge_results(Results), Updated_instance = {flow_instance, erlang:element(2, Instance@2), erlang:element(3, Instance@2), erlang:element(4, Instance@2), erlang:element(5, Instance@2), begin _record = erlang:element(6, Instance@2), {flow_state, erlang:element(2, _record), Merged, erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance@2), erlang:element(8, Instance@2), erlang:element(9, Instance@2), erlang:element(10, Instance@2)}, {ok, {Ctx@2, {complete, Merged}, Updated_instance}}; none -> {ok, {Ctx@2, {complete, erlang:element(3, erlang:element(6, Instance@2))}, Instance@2}} end end ), Builder@5 = add_parallel_steps( Builder@4, composed_start_parallel, Parallel_steps, composed_merge_results ), build(Builder@5, composed_start_parallel). -file("src/telega/flow.gleam", 2693). -spec create_composed_handler( flow(gleam@dynamic:dynamic_(), AUXJ, AUXK), list(flow(gleam@dynamic:dynamic_(), AUXJ, AUXK)), integer() ) -> fun((telega@bot:context(AUXJ, AUXK), flow_instance()) -> {ok, {telega@bot:context(AUXJ, AUXK), flow_action(composed_step()), flow_instance()}} | {error, AUXK}). create_composed_handler(Flow, All_flows, Index) -> fun(Ctx, Instance) -> {User_id, Chat_id} = extract_ids_from_context(Ctx), _pipe = start_or_resume( Flow, Ctx, User_id, Chat_id, erlang:element(3, erlang:element(6, Instance)) ), gleam@result:map( _pipe, fun(New_ctx) -> case gleam@list:drop(All_flows, Index + 1) of [_ | _] -> {New_ctx, {next, {composed_flow_step, Index + 1}}, Instance}; [] -> {New_ctx, {complete, erlang:element(3, erlang:element(6, Instance))}, Instance} end end ) end. -file("src/telega/flow.gleam", 2539). ?DOC(" Compose flows sequentially\n"). -spec compose_sequential( binary(), list(flow(gleam@dynamic:dynamic_(), AUVX, AUVY)), flow_storage(AUVY) ) -> flow(composed_step(), AUVX, AUVY). compose_sequential(Name, Flows, Storage) -> Builder = new( Name, Storage, fun composed_step_to_string/1, fun string_to_composed_step/1 ), Builder@2 = gleam@list:index_fold( Flows, Builder, fun(Builder@1, Flow, Index) -> Step = {composed_flow_step, Index}, add_step( Builder@1, Step, create_composed_handler(Flow, Flows, Index) ) end ), build(Builder@2, {composed_flow_step, 0}). -file("src/telega/flow.gleam", 2282). ?DOC(" Process action within a subflow\n"). -spec process_subflow_action( flow(gleam@dynamic:dynamic_(), AUTI, AUTJ), telega@bot:context(AUTI, AUTJ), flow_action(gleam@dynamic:dynamic_()), flow_instance(), subflow_config(any(), AUTI, AUTJ) ) -> {ok, telega@bot:context(AUTI, AUTJ)} | {error, AUTJ}. process_subflow_action(Flow, Ctx, Action, Instance, Config) -> case Action of {complete, Data} -> return_to_parent_flow(Ctx, Instance, Data, Config); {exit, {some, Data}} -> return_to_parent_flow(Ctx, Instance, Data, Config); {exit, none} -> return_to_parent_flow( Ctx, Instance, erlang:element(3, erlang:element(6, Instance)), Config ); {return_from_subflow, Result} -> return_to_parent_flow(Ctx, Instance, Result, Config); cancel -> _ = (erlang:element(4, erlang:element(7, Flow)))( erlang:element(2, Instance) ), {ok, Ctx}; {next, Step} -> Step_name = (erlang:element(5, Flow))(Step), Updated_instance = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record = erlang:element(6, Instance), {flow_state, Step_name, erlang:element(3, _record), [erlang:element(2, erlang:element(6, Instance)) | erlang:element(4, erlang:element(6, Instance))], erlang:element(5, _record), erlang:element(6, _record)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))(Updated_instance) of {ok, _} -> execute_subflow_step(Flow, Ctx, Updated_instance, Config); {error, Err} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err}, Config ) end; {next_string, Step_name@1} -> Updated_instance@1 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@1 = erlang:element(6, Instance), {flow_state, Step_name@1, erlang:element(3, _record@1), [erlang:element(2, erlang:element(6, Instance)) | erlang:element(4, erlang:element(6, Instance))], erlang:element(5, _record@1), erlang:element(6, _record@1)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@1 ) of {ok, _} -> execute_subflow_step(Flow, Ctx, Updated_instance@1, Config); {error, Err@1} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err@1}, Config ) end; {go_to, Step@1} -> Step_name@2 = (erlang:element(5, Flow))(Step@1), Updated_instance@2 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), {flow_state, Step_name@2, erlang:element(3, erlang:element(6, Instance)), [Step_name@2], erlang:element(5, erlang:element(6, Instance)), none}, maps:new(), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@2 ) of {ok, _} -> execute_subflow_step(Flow, Ctx, Updated_instance@2, Config); {error, Err@2} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err@2}, Config ) end; back -> case erlang:element(4, erlang:element(6, Instance)) of [Previous_step | Rest] -> Updated_instance@3 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), begin _record@2 = erlang:element(6, Instance), {flow_state, Previous_step, erlang:element(3, _record@2), Rest, erlang:element(5, _record@2), erlang:element(6, _record@2)} end, erlang:element(7, Instance), erlang:element(8, Instance), erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@3 ) of {ok, _} -> execute_subflow_step( Flow, Ctx, Updated_instance@3, Config ); {error, Err@3} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err@3}, Config ) end; [] -> {ok, Ctx} end; {wait, Token} -> Updated_instance@4 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), erlang:element(7, Instance), {some, Token}, erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@4 ) of {ok, _} -> {ok, Ctx}; {error, Err@4} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err@4}, Config ) end; {wait_callback, Token@1} -> Updated_instance@5 = {flow_instance, erlang:element(2, Instance), erlang:element(3, Instance), erlang:element(4, Instance), erlang:element(5, Instance), erlang:element(6, Instance), erlang:element(7, Instance), {some, Token@1}, erlang:element(9, Instance), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Flow)))( Updated_instance@5 ) of {ok, _} -> {ok, Ctx}; {error, Err@5} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err@5}, Config ) end; {start_parallel, _, _} -> {ok, Ctx}; {complete_parallel_step, _, _} -> {ok, Ctx}; {enter_subflow, _, _} -> {ok, Ctx} end. -file("src/telega/flow.gleam", 2254). ?DOC(" Execute a step within a subflow context\n"). -spec execute_subflow_step( flow(gleam@dynamic:dynamic_(), AUST, AUSU), telega@bot:context(AUST, AUSU), flow_instance(), subflow_config(any(), AUST, AUSU) ) -> {ok, telega@bot:context(AUST, AUSU)} | {error, AUSU}. execute_subflow_step(Flow, Ctx, Instance, Config) -> case gleam_stdlib:map_get( erlang:element(3, Flow), erlang:element(2, erlang:element(6, Instance)) ) of {ok, Step_config} -> Handler_fn = fun() -> (erlang:element(2, Step_config))(Ctx, Instance) end, Result = apply_middlewares( Ctx, Instance, Handler_fn, lists:append( erlang:element(10, Flow), erlang:element(3, Step_config) ) ), case Result of {ok, {New_ctx, Action, New_instance}} -> process_subflow_action( Flow, New_ctx, Action, New_instance, Config ); {error, Err} -> handle_subflow_error( Flow, Ctx, Instance, {some, Err}, Config ) end; {error, _} -> handle_subflow_error(Flow, Ctx, Instance, none, Config) end. -file("src/telega/flow.gleam", 2197). ?DOC(" Start subflow execution\n"). -spec start_subflow_execution( flow(AUSE, AUSF, AUSG), telega@bot:context(AUSF, AUSG), flow_instance(), subflow_config(AUSE, AUSF, AUSG) ) -> {ok, telega@bot:context(AUSF, AUSG)} | {error, AUSG}. start_subflow_execution(Parent_flow, Ctx, Instance, Config) -> case run_flow_leave_hook(erlang:element(15, Parent_flow), Ctx, Instance) of {ok, {Ctx_after_leave, Instance_after_leave}} -> Return_step = (erlang:element(5, Parent_flow))( erlang:element(4, Config) ), Stack_frame = {flow_stack_frame, erlang:element(2, Parent_flow), Return_step, erlang:element(3, erlang:element(6, Instance_after_leave))}, Subflow_data = (erlang:element(5, Config))(Instance_after_leave), Subflow_initial_step = (erlang:element(5, erlang:element(3, Config)))( erlang:element(4, erlang:element(3, Config)) ), Updated_instance = {flow_instance, erlang:element(2, Instance_after_leave), erlang:element(2, erlang:element(3, Config)), erlang:element(4, Instance_after_leave), erlang:element(5, Instance_after_leave), {flow_state, Subflow_initial_step, Subflow_data, [Subflow_initial_step], [Stack_frame | erlang:element( 5, erlang:element(6, Instance_after_leave) )], none}, maps:new(), erlang:element(8, Instance_after_leave), erlang:element(9, Instance_after_leave), telega@internal@utils:current_time_ms()}, case (erlang:element(2, erlang:element(7, Parent_flow)))( Updated_instance ) of {ok, _} -> execute_subflow_step( erlang:element(3, Config), Ctx_after_leave, Updated_instance, Config ); {error, Err} -> handle_error(Parent_flow, Ctx, Instance, {some, Err}) end; {error, Err@1} -> handle_error(Parent_flow, Ctx, Instance, {some, Err@1}) end.