-module(telega). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/telega.gleam"). -export([get_bot_subject_internal/1, get_client_internal/1, is_webhook_path/2, is_secret_token_valid/2, get_api_config/1, new/4, new_with_dependencies/5, new_for_polling/1, new_for_polling_with_dependencies/2, with_router/2, with_session_settings/2, with_dependencies/2, with_catch_handler/2, use_pre_handler/2, set_drop_pending_updates/2, set_max_connections/2, set_ip_address/2, set_allowed_updates/2, set_certificate/2, set_api_client/2, with_polling_config/4, with_polling_on_stop/2, with_chat_config/4, with_on_start/2, with_on_shutdown/2, with_drain_timeout/2, with_signal_handlers/1, with_auto_commands/1, with_command_translations/3, with_auto_allowed_updates/1, with_nil_session/1, shutdown/1, init_for_polling/1, init_for_polling_nil_session/1, init/1, supervised/1, supervised_for_polling/1, handle_update/2, handle_update_webhook/3, get_me/1, get_session/1, get_dependencies/1, log_context/3, log_info/2, log_error/2, wait_any/4, wait_command/5, wait_commands/5, wait_text/4, wait_hears/5, wait_message/4, wait_callback_query/5, wait_audio/4, wait_video/4, wait_voice/4, wait_photos/4, wait_number/6, wait_email/4, wait_choice/5, wait_for/5, wait_filtered/5, start_polling_default/1, is_draining/1, get_supervisor_pid/1]). -export_type([telega/3, telega_builder/3, webhook_response/0, webhook_event/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. -opaque telega(AOTW, AOTX, AOTY) :: {telega, telega@internal@config:config(), telega@model@types:user(), gleam@erlang@process:subject(telega@bot:bot_message()), gleam@erlang@process:pid_(), gleam@option:option(gleam@erlang@process:subject(telega@polling:polling_message())), integer(), gleam@option:option(fun(() -> nil))} | {gleam_phantom, AOTW, AOTX, AOTY}. -opaque telega_builder(AOTZ, AOUA, AOUB) :: {telega_builder, telega@internal@config:config(), gleam@option:option(telega@router:router(AOTZ, AOUA, AOUB)), gleam@option:option(telega@bot:session_settings(AOTZ, AOUA)), gleam@option:option(fun((telega@bot:context(AOTZ, AOUA, AOUB), AOUA) -> {ok, nil} | {error, AOUA})), list(fun((telega@bot:pre_context(AOUB)) -> telega@bot:pre_router_result())), AOUB, gleam@option:option(gleam@erlang@process:subject(telega@bot:bot_message())), gleam@option:option(telega@client:telegram_client()), gleam@option:option(boolean()), gleam@option:option(integer()), gleam@option:option(binary()), gleam@option:option(list(binary())), gleam@option:option(telega@model@types:file()), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(fun((telega@error:telega_error()) -> nil)), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(fun((telega(AOTZ, AOUA, AOUB)) -> {ok, nil} | {error, telega@error:telega_error()})), gleam@option:option(fun(() -> nil)), gleam@option:option(integer()), boolean(), boolean(), list(binary()), gleam@option:option(fun((binary(), binary()) -> gleam@option:option(binary()))), boolean()}. -type webhook_response() :: empty_response | {json_response, binary()}. -type webhook_event() :: {reply_claimed, telega@webhook_reply:webhook_reply(), gleam@erlang@process:subject(boolean())} | update_handled. -file("src/telega.gleam", 101). ?DOC(false). -spec get_bot_subject_internal(telega(any(), any(), any())) -> gleam@erlang@process:subject(telega@bot:bot_message()). get_bot_subject_internal(Telega) -> erlang:element(4, Telega). -file("src/telega.gleam", 109). ?DOC(false). -spec get_client_internal(telega(any(), any(), any())) -> telega@client:telegram_client(). get_client_internal(Telega) -> erlang:element(5, erlang:element(2, Telega)). -file("src/telega.gleam", 118). ?DOC( " Check if a path is the webhook path for the bot.\n" "\n" " Useful if you plan to implement own adapter.\n" ). -spec is_webhook_path(telega(any(), any(), any()), binary()) -> boolean(). is_webhook_path(Telega, Path) -> erlang:element(3, erlang:element(2, Telega)) =:= Path. -file("src/telega.gleam", 128). ?DOC( " Check if a secret token is valid.\n" "\n" " Useful if you plan to implement own adapter.\n" ). -spec is_secret_token_valid(telega(any(), any(), any()), binary()) -> boolean(). is_secret_token_valid(Telega, Token) -> erlang:element(4, erlang:element(2, Telega)) =:= Token. -file("src/telega.gleam", 136). ?DOC(" Helper to get the config for API requests.\n"). -spec get_api_config(telega(any(), any(), any())) -> telega@client:telegram_client(). get_api_config(Telega) -> erlang:element(5, erlang:element(2, Telega)). -file("src/telega.gleam", 143). ?DOC( " Build a builder with every field at its default, given a config and the\n" " injected `dependencies`. Shared by all four constructors so the long default list\n" " lives in exactly one place.\n" ). -spec default_builder(telega@internal@config:config(), AOVJ) -> telega_builder(any(), any(), AOVJ). default_builder(Config, Dependencies) -> {telega_builder, Config, none, none, none, [], Dependencies, none, none, none, none, none, none, none, none, none, none, none, none, none, none, none, none, none, false, false, [], none, false}. -file("src/telega.gleam", 179). -spec webhook_config( telega@client:telegram_client(), binary(), binary(), gleam@option:option(binary()) ) -> telega@internal@config:config(). webhook_config(Api_client, Server_url, Webhook_path, Secret_token) -> telega@internal@config:new( Api_client, telega@internal@utils:normalize_url(Server_url), telega@internal@utils:normalize_webhook_path(Webhook_path), Secret_token ). -file("src/telega.gleam", 193). -spec polling_config(telega@client:telegram_client()) -> telega@internal@config:config(). polling_config(Api_client) -> telega@internal@config:new( Api_client, <<"https://api.telegram.org"/utf8>>, <<"/webhook"/utf8>>, none ). -file("src/telega.gleam", 207). ?DOC( " Create a new Telega instance with no injected dependencies (`dependencies` is `Nil`).\n" "\n" " Requires an `api_client` created by an adapter package like `telega_httpc` or `telega_hackney`.\n" " To inject services, prefer `new_with_dependencies` — it fixes the `dependencies` type up front\n" " and avoids the field-reset footgun of `with_dependencies` (see `with_dependencies`).\n" ). -spec new( telega@client:telegram_client(), binary(), binary(), gleam@option:option(binary()) ) -> telega_builder(any(), any(), nil). new(Api_client, Server_url, Webhook_path, Secret_token) -> default_builder( webhook_config(Api_client, Server_url, Webhook_path, Secret_token), nil ). -file("src/telega.gleam", 231). ?DOC( " Like `new`, but injects `dependencies` (services) at construction.\n" "\n" " This is the safest way to use dependency injection: the builder's `dependencies`\n" " type is fixed from the start, so `with_router`/`with_catch_handler`/`on_start`\n" " can be called in any order without being reset. See `with_dependencies` for the\n" " `session` vs `dependencies` distinction.\n" "\n" " ```gleam\n" " telega.new_with_dependencies(api_client:, url:, webhook_path:, secret_token:, dependencies: Dependencies(db:, catalog:))\n" " |> telega.with_router(router)\n" " |> telega.init()\n" " ```\n" ). -spec new_with_dependencies( telega@client:telegram_client(), binary(), binary(), gleam@option:option(binary()), AOVX ) -> telega_builder(any(), any(), AOVX). new_with_dependencies( Api_client, Server_url, Webhook_path, Secret_token, Dependencies ) -> default_builder( webhook_config(Api_client, Server_url, Webhook_path, Secret_token), Dependencies ). -file("src/telega.gleam", 250). ?DOC( " Create a new Telega instance optimized for long polling, with no injected\n" " dependencies (`dependencies` is `Nil`).\n" "\n" " Requires an `api_client` created by an adapter package like `telega_httpc` or `telega_hackney`.\n" " This is a convenience function for polling bots that don't need webhook configuration.\n" " To inject services, prefer `new_for_polling_with_dependencies`.\n" ). -spec new_for_polling(telega@client:telegram_client()) -> telega_builder(any(), any(), nil). new_for_polling(Api_client) -> default_builder(polling_config(Api_client), nil). -file("src/telega.gleam", 266). ?DOC( " Like `new_for_polling`, but injects `dependencies` (services) at construction.\n" "\n" " Preferred over `new_for_polling` + `with_dependencies`: the `dependencies` type is fixed up\n" " front, so the builder steps that follow are never reset (see `with_dependencies`).\n" "\n" " ```gleam\n" " telega.new_for_polling_with_dependencies(api_client:, dependencies: Dependencies(db:, catalog:))\n" " |> telega.with_router(router)\n" " |> telega.init_for_polling()\n" " ```\n" ). -spec new_for_polling_with_dependencies(telega@client:telegram_client(), AOWI) -> telega_builder(any(), any(), AOWI). new_for_polling_with_dependencies(Api_client, Dependencies) -> default_builder(polling_config(Api_client), Dependencies). -file("src/telega.gleam", 276). ?DOC( " Set the router for handling updates.\n" " This is the primary way to handle updates - use router.new() to create a router\n" " and configure it with command handlers, text handlers, middleware, etc.\n" ). -spec with_router( telega_builder(AOWO, AOWP, AOWQ), telega@router:router(AOWO, AOWP, AOWQ) ) -> telega_builder(AOWO, AOWP, AOWQ). with_router(Builder, Router) -> {telega_builder, erlang:element(2, Builder), {some, Router}, 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), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 284). ?DOC(" Set session settings for the bot.\n"). -spec with_session_settings( telega_builder(AOXA, AOXB, AOXC), telega@bot:session_settings(AOXA, AOXB) ) -> telega_builder(AOXA, AOXB, AOXC). with_session_settings(Builder, Session_settings) -> {telega_builder, erlang:element(2, Builder), erlang:element(3, Builder), {some, Session_settings}, 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), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 321). ?DOC( " Inject typed, non-persisted dependencies (services) available in every\n" " handler via `ctx.dependencies` (or `get_dependencies`).\n" "\n" " Use this for things that are not user state and must not be persisted —\n" " a database pool, an http client, an i18n catalog, configuration, etc. The\n" " rule of thumb: `session` is the user's state (persisted), `dependencies` is the\n" " bot's services (set once at init, never persisted).\n" "\n" " > ⚠️ **Footgun — silently resets fields.** `with_dependencies` changes the builder's\n" " > `dependencies` type, so it cannot keep the previously-set `router`, `catch_handler`,\n" " > or `on_start` (they are typed against the old `dependencies`). It **resets them to\n" " > their defaults**. If you call it *after* `with_router`, your router is\n" " > silently dropped and the bot runs with no routes — and there is no compile\n" " > error, only a dead bot at runtime. Either call `with_dependencies` *first*, or —\n" " > better — skip it entirely and inject at construction with\n" " > `new_for_polling_with_dependencies` / `new_with_dependencies`, which fix the `dependencies` type up\n" " > front and have no reset behaviour.\n" "\n" " ```gleam\n" " // Preferred — no reset, any order:\n" " telega.new_for_polling_with_dependencies(api_client:, dependencies: Dependencies(db:, catalog:))\n" " |> telega.with_router(router)\n" " |> telega.init_for_polling()\n" "\n" " // With `with_dependencies` — MUST come before with_router/with_catch_handler/on_start:\n" " telega.new_for_polling(api_client:)\n" " |> telega.with_dependencies(Dependencies(db:, catalog:))\n" " |> telega.with_router(router)\n" " |> telega.init_for_polling()\n" " ```\n" ). -spec with_dependencies(telega_builder(AOXL, AOXM, any()), AOXR) -> telega_builder(AOXL, AOXM, AOXR). with_dependencies(Builder, Dependencies) -> {telega_builder, erlang:element(2, Builder), none, erlang:element(4, Builder), none, [], Dependencies, 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), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), none, erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 361). ?DOC( " Set catch handler for system errors (like session persistence failures) and [conversation](/docs/conversation) errors.\n" " This is different from router's catch handler which handles route errors.\n" ). -spec with_catch_handler( telega_builder(AOXV, AOXW, AOXX), fun((telega@bot:context(AOXV, AOXW, AOXX), AOXW) -> {ok, nil} | {error, AOXW}) ) -> telega_builder(AOXV, AOXW, AOXX). with_catch_handler(Builder, Catch_handler) -> {telega_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), {some, Catch_handler}, 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), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 393). ?DOC( " Register a global pre-router middleware ([`bot.PreHandler`](./telega/bot.html#PreHandler)).\n" "\n" " Pre-router middleware runs once per update inside the bot actor, *before*\n" " routing and before any chat instance is spawned or session loaded. Use it\n" " for cross-cutting concerns that apply to every update: anti-spam, analytics,\n" " and update deduplication. Returning `bot.Stop` drops the update before\n" " routing; `bot.Continue` lets it through to the next pre-handler and the\n" " router. Handlers run in the order they are registered, and the first `Stop`\n" " short-circuits the rest. Because they all run sequentially in the single bot\n" " actor, read-then-write logic (like dedup) is race-free across updates.\n" "\n" " ```gleam\n" " // Drop updates from a banned chat before they reach any handler.\n" " telega.new_for_polling(api_client:)\n" " |> telega.use_pre_handler(fn(pre) {\n" " case pre.update.chat_id == banned_chat {\n" " True -> bot.Stop\n" " False -> bot.Continue\n" " }\n" " })\n" " |> telega.with_router(router)\n" "\n" " // Webhook idempotency: drop updates Telegram re-delivers on retry.\n" " |> telega.use_pre_handler(idempotency.deduplicate(storage:, ttl_ms: 3600_000))\n" " ```\n" ). -spec use_pre_handler( telega_builder(AOYH, AOYI, AOYJ), fun((telega@bot:pre_context(AOYJ)) -> telega@bot:pre_router_result()) ) -> telega_builder(AOYH, AOYI, AOYJ). use_pre_handler(Builder, Pre_handler) -> {telega_builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), lists:append(erlang:element(6, Builder), [Pre_handler]), 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), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 404). ?DOC(" Set whether to drop pending updates.\n"). -spec set_drop_pending_updates(telega_builder(AOYR, AOYS, AOYT), boolean()) -> telega_builder(AOYR, AOYS, AOYT). set_drop_pending_updates(Builder, Drop) -> {telega_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), {some, Drop}, erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 412). ?DOC(" Set max connections for webhook.\n"). -spec set_max_connections(telega_builder(AOZA, AOZB, AOZC), integer()) -> telega_builder(AOZA, AOZB, AOZC). set_max_connections(Builder, Max) -> {telega_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), {some, Max}, erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 420). ?DOC(" Set IP address for webhook.\n"). -spec set_ip_address(telega_builder(AOZJ, AOZK, AOZL), binary()) -> telega_builder(AOZJ, AOZK, AOZL). set_ip_address(Builder, Ip) -> {telega_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), {some, Ip}, erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 428). ?DOC(" Set allowed updates for webhook.\n"). -spec set_allowed_updates(telega_builder(AOZS, AOZT, AOZU), list(binary())) -> telega_builder(AOZS, AOZT, AOZU). set_allowed_updates(Builder, Updates) -> {telega_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, Updates}, erlang:element(14, Builder), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 436). ?DOC(" Set certificate for webhook.\n"). -spec set_certificate( telega_builder(APAC, APAD, APAE), telega@model@types:file() ) -> telega_builder(APAC, APAD, APAE). set_certificate(Builder, Cert) -> {telega_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, Cert}, erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 444). ?DOC(" Set a custom API client.\n"). -spec set_api_client( telega_builder(APAL, APAM, APAN), telega@client:telegram_client() ) -> telega_builder(APAL, APAM, APAN). set_api_client(Builder, Client) -> {telega_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), {some, Client}, erlang:element(10, Builder), erlang:element(11, Builder), erlang:element(12, Builder), erlang:element(13, Builder), erlang:element(14, Builder), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 452). ?DOC(" Set polling configuration for the supervised polling worker.\n"). -spec with_polling_config( telega_builder(APAU, APAV, APAW), integer(), integer(), integer() ) -> telega_builder(APAU, APAV, APAW). with_polling_config(Builder, Timeout, Limit, Poll_interval) -> {telega_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, Timeout}, {some, Limit}, {some, Poll_interval}, erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 467). ?DOC(" Set a callback for when polling stops due to errors.\n"). -spec with_polling_on_stop( telega_builder(APBD, APBE, APBF), fun((telega@error:telega_error()) -> nil) ) -> telega_builder(APBD, APBE, APBF). with_polling_on_stop(Builder, On_stop) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), {some, On_stop}, erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 479). ?DOC( " Configure the chat instance factory supervisor.\n" "\n" " - `restart_tolerance_intensity` — max restarts within the period (default: 5)\n" " - `restart_tolerance_period` — period in seconds (default: 10)\n" " - `init_timeout` — chat instance init timeout in ms (default: 10 000)\n" ). -spec with_chat_config( telega_builder(APBM, APBN, APBO), integer(), integer(), integer() ) -> telega_builder(APBM, APBN, APBO). with_chat_config(Builder, Intensity, Period, Timeout) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), {some, Intensity}, {some, Period}, {some, Timeout}, erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 508). ?DOC( " Set a hook to run once the bot has fully started.\n" "\n" " Runs after the supervision tree is up and the `Telega` instance is built, so\n" " you can use it for warming caches, registering commands via the API, etc.\n" " Returning `Error` aborts startup and tears the supervision tree back down.\n" "\n" " ```gleam\n" " telega.new_for_polling(api_client:)\n" " |> telega.with_router(router)\n" " |> telega.with_on_start(fn(bot) {\n" " // register commands, warm caches...\n" " Ok(Nil)\n" " })\n" " |> telega.init_for_polling()\n" " ```\n" ). -spec with_on_start( telega_builder(APBV, APBW, APBX), fun((telega(APBV, APBW, APBX)) -> {ok, nil} | {error, telega@error:telega_error()}) ) -> telega_builder(APBV, APBW, APBX). with_on_start(Builder, On_start) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), {some, On_start}, erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 519). ?DOC( " Set a hook to run during `shutdown`, after in-flight updates have drained\n" " and before the supervision tree is stopped. Use it to release resources\n" " (close pools, flush buffers, deregister from a service discovery, …).\n" ). -spec with_on_shutdown(telega_builder(APCJ, APCK, APCL), fun(() -> nil)) -> telega_builder(APCJ, APCK, APCL). with_on_shutdown(Builder, On_shutdown) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), {some, On_shutdown}, erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 530). ?DOC( " Set the maximum time (in milliseconds) `shutdown` waits for in-flight\n" " updates to finish before forcibly stopping the supervision tree.\n" "\n" " Defaults to 5000ms.\n" ). -spec with_drain_timeout(telega_builder(APCS, APCT, APCU), integer()) -> telega_builder(APCS, APCT, APCU). with_drain_timeout(Builder, Timeout) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), {some, Timeout}, erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 547). ?DOC( " Install an OS signal handler (SIGTERM) that runs a graceful `shutdown` and\n" " then halts the VM.\n" "\n" " This makes the bot survive rolling deploys on platforms like fly.io or\n" " Kubernetes: on SIGTERM the bot stops accepting new updates, drains in-flight\n" " work (bounded by `with_drain_timeout`), runs the `on_shutdown` hook, and\n" " stops cleanly. The handler replaces the runtime's default signal behavior.\n" "\n" " Only SIGTERM is handled — BEAM reserves SIGINT for its interactive break\n" " handler, so it cannot be intercepted this way.\n" ). -spec with_signal_handlers(telega_builder(APDB, APDC, APDD)) -> telega_builder(APDB, APDC, APDD). with_signal_handlers(Builder) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), true, erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 569). ?DOC( " Publish the router's commands to Telegram on start.\n" "\n" " Every command registered with `router.on_command_with_description` is sent\n" " via `setMyCommands` once the bot is up, so the Telegram client shows them in\n" " the command menu without a manual call. Commands added with plain\n" " `router.on_command` (no description) are not published.\n" "\n" " For localized descriptions use `with_command_translations` instead — it\n" " turns this on as well.\n" "\n" " ```gleam\n" " telega.new_for_polling(api_client:)\n" " |> telega.with_router(router)\n" " |> telega.with_auto_commands()\n" " |> telega.init_for_polling()\n" " ```\n" ). -spec with_auto_commands(telega_builder(APDK, APDL, APDM)) -> telega_builder(APDK, APDL, APDM). with_auto_commands(Builder) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), true, erlang:element(27, Builder), erlang:element(28, Builder), erlang:element(29, Builder)}. -file("src/telega.gleam", 594). ?DOC( " Publish localized command descriptions on start.\n" "\n" " Implies `with_auto_commands`: the default-language commands are published\n" " first, then for every locale in `locales` a `setMyCommands(language_code:)`\n" " call is made. `translate(command, locale)` supplies the per-language text;\n" " returning `None` keeps the router's default description for that command.\n" "\n" " `telega_i18n` provides a convenience wrapper that builds `translate` from a\n" " translation catalog, so you usually call this through it.\n" "\n" " ```gleam\n" " telega.new_for_polling(api_client:)\n" " |> telega.with_router(router)\n" " |> telega.with_command_translations(\n" " locales: [\"en\", \"ru\"],\n" " translate: fn(command, locale) { lookup_description(command, locale) },\n" " )\n" " |> telega.init_for_polling()\n" " ```\n" ). -spec with_command_translations( telega_builder(APDT, APDU, APDV), list(binary()), fun((binary(), binary()) -> gleam@option:option(binary())) ) -> telega_builder(APDT, APDU, APDV). with_command_translations(Builder, Locales, Translate) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), true, Locales, {some, Translate}, erlang:element(29, Builder)}. -file("src/telega.gleam", 614). ?DOC( " Derive `allowed_updates` from the router's registered routes.\n" "\n" " Telegram then sends only the update types the bot actually handles, cutting\n" " out traffic for routes you never registered. A manual `set_allowed_updates`\n" " always wins (the escape hatch). If the router has a fallback, custom, or\n" " filtered route — which can match anything — derivation can't narrow safely\n" " and falls back to Telegram's default update set.\n" ). -spec with_auto_allowed_updates(telega_builder(APEE, APEF, APEG)) -> telega_builder(APEE, APEF, APEG). with_auto_allowed_updates(Builder) -> {telega_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), erlang:element(15, Builder), erlang:element(16, Builder), erlang:element(17, Builder), erlang:element(18, Builder), erlang:element(19, Builder), erlang:element(20, Builder), erlang:element(21, Builder), erlang:element(22, Builder), erlang:element(23, Builder), erlang:element(24, Builder), erlang:element(25, Builder), erlang:element(26, Builder), erlang:element(27, Builder), erlang:element(28, Builder), true}. -file("src/telega.gleam", 621). ?DOC(" Set nil session for the bot.\n"). -spec with_nil_session(telega_builder(nil, APEN, APEO)) -> telega_builder(nil, APEN, APEO). with_nil_session(Builder) -> _pipe = Builder, with_session_settings( _pipe, {session_settings, fun(_, _) -> {ok, nil} end, fun(_) -> {ok, none} end, fun() -> nil end} ). -file("src/telega.gleam", 1564). ?DOC( " Graceful shutdown with in-flight draining.\n" "\n" " 1. Emits `[telega, shutdown, start]`.\n" " 2. Stops intake — for polling, tells the worker to stop fetching updates\n" " (Telegram re-delivers unconfirmed updates on the next start); for webhook,\n" " the bot starts rejecting updates and `is_draining` reports `True` so\n" " adapters can answer `503`.\n" " 3. Waits up to `drain_timeout` for in-flight updates to finish.\n" " 4. Runs the `on_shutdown` hook.\n" " 5. Emits `[telega, shutdown, stop]` with the number of drained updates.\n" " 6. Stops the supervisor, cascading to all children (polling → bot →\n" " chat_factory).\n" ). -spec shutdown(telega(any(), any(), any())) -> nil. shutdown(Telega) -> Started_at = erlang:monotonic_time(), telega@telemetry:execute( [<<"telega"/utf8>>, <<"shutdown"/utf8>>, <<"start"/utf8>>], [{<<"system_time"/utf8>>, erlang:system_time()}], [] ), case erlang:element(6, Telega) of {some, Poller} -> telega@polling:stop_worker(Poller); none -> nil end, Drained = telega@bot:drain( erlang:element(4, Telega), erlang:element(7, Telega) ), case erlang:element(8, Telega) of {some, On_shutdown} -> On_shutdown(); none -> nil end, Duration = erlang:monotonic_time() - Started_at, telega@telemetry:execute( [<<"telega"/utf8>>, <<"shutdown"/utf8>>, <<"stop"/utf8>>], [{<<"duration"/utf8>>, Duration}, {<<"drained"/utf8>>, gleam@int:max(0, Drained)}], [{<<"timed_out"/utf8>>, {bool_value, Drained < 0}}] ), gleam@erlang@process:send_abnormal_exit( erlang:element(5, Telega), erlang:binary_to_atom(<<"shutdown"/utf8>>) ). -file("src/telega.gleam", 1740). -spec install_signal_handlers(telega(any(), any(), any())) -> nil. install_signal_handlers(Telega) -> telega@internal@signal:install( fun(_) -> shutdown(Telega), erlang:halt(0) end ). -file("src/telega.gleam", 1682). ?DOC( " Publish the router's described commands via `setMyCommands` when\n" " `auto_commands` is enabled: a default-language call first, then one\n" " `setMyCommands(language_code:)` per configured locale.\n" ). -spec maybe_sync_commands( telega_builder(any(), any(), any()), telega@internal@config:config() ) -> {ok, nil} | {error, telega@error:telega_error()}. maybe_sync_commands(Builder, Config) -> gleam@bool:guard( not erlang:element(26, Builder), {ok, nil}, fun() -> case erlang:element(3, Builder) of none -> {ok, nil}; {some, Router} -> Described = telega@router:registered_commands(Router), gleam@bool:guard( Described =:= [], {ok, nil}, fun() -> Client = erlang:element(5, Config), Base_commands = gleam@list:map( Described, fun(Pair) -> {bot_command, erlang:element(1, Pair), erlang:element(2, Pair), none} end ), gleam@result:'try'( telega@api:set_my_commands( Client, Base_commands, none ), fun(_) -> case erlang:element(28, Builder) of none -> {ok, nil}; {some, Translate} -> gleam@list:try_each( erlang:element(27, Builder), fun(Locale) -> Localized = gleam@list:map( Described, fun(Pair@1) -> Description = begin _pipe = Translate( erlang:element( 1, Pair@1 ), Locale ), gleam@option:unwrap( _pipe, erlang:element( 2, Pair@1 ) ) end, {bot_command, erlang:element( 1, Pair@1 ), Description, none} end ), _pipe@1 = telega@api:set_my_commands( Client, Localized, {some, {bot_command_parameters, none, {some, Locale}}} ), gleam@result:map( _pipe@1, fun(_) -> nil end ) end ) end end ) end ) end end ). -file("src/telega.gleam", 1614). ?DOC( " Build the `Telega` value, run the `on_start` hook, and install signal\n" " handlers if requested. Shared by `init` and `init_for_polling`.\n" ). -spec finalize( telega_builder(APZV, APZW, APZX), telega@internal@config:config(), telega@model@types:user(), gleam@erlang@process:subject(telega@bot:bot_message()), gleam@erlang@process:pid_(), gleam@option:option(gleam@erlang@process:subject(telega@polling:polling_message())) ) -> {ok, telega(APZV, APZW, APZX)} | {error, telega@error:telega_error()}. finalize(Builder, Config, Bot_info, Bot_subject, Supervisor_pid, Poller) -> Telega = {telega, Config, Bot_info, Bot_subject, Supervisor_pid, Poller, gleam@option:unwrap(erlang:element(24, Builder), 5000), erlang:element(23, Builder)}, Run_startup = fun() -> gleam@result:'try'( maybe_sync_commands(Builder, Config), fun(_) -> case erlang:element(22, Builder) of {some, On_start} -> On_start(Telega); none -> {ok, nil} end end ) end, gleam@result:'try'(case Run_startup() of {ok, _} -> {ok, nil}; {error, E} -> gleam@erlang@process:send_abnormal_exit( Supervisor_pid, erlang:binary_to_atom(<<"shutdown"/utf8>>) ), {error, E} end, fun(_) -> case erlang:element(25, Builder) of true -> install_signal_handlers(Telega); false -> nil end, {ok, Telega} end). -file("src/telega.gleam", 1662). ?DOC( " Resolve the effective `allowed_updates`. A manual value (via\n" " `set_allowed_updates`) always wins; otherwise, when `with_auto_allowed_updates`\n" " is enabled, derive the set from the router. `None` means \"do not restrict\".\n" ). -spec resolve_allowed_updates(telega_builder(any(), any(), any())) -> gleam@option:option(list(binary())). resolve_allowed_updates(Builder) -> case erlang:element(13, Builder) of {some, _} = Manual -> Manual; none -> case {erlang:element(29, Builder), erlang:element(3, Builder)} of {true, {some, Router}} -> case telega@router:allowed_updates(Router) of [] -> none; Updates -> {some, Updates} end; {_, _} -> none end end. -file("src/telega.gleam", 1760). -spec build_chat_factory_spec(telega_builder(any(), any(), any())) -> {gleam@otp@supervision:child_specification(gleam@otp@factory_supervisor:supervisor(telega@bot:chat_instance_args(ATEP, ATEQ, ATER), gleam@erlang@process:subject(telega@bot:chat_instance_message(ATEP, ATEQ, ATER)))), gleam@erlang@process:name(gleam@otp@factory_supervisor:message(telega@bot:chat_instance_args(ATEP, ATEQ, ATER), gleam@erlang@process:subject(telega@bot:chat_instance_message(ATEP, ATEQ, ATER))))}. build_chat_factory_spec(Builder) -> Intensity = gleam@option:unwrap(erlang:element(19, Builder), 5), Period = gleam@option:unwrap(erlang:element(20, Builder), 10), Timeout = gleam@option:unwrap(erlang:element(21, Builder), 10000), Name = gleam_erlang_ffi:new_name(<<"telega_chat_factory"/utf8>>), Spec = begin _pipe = gleam@otp@factory_supervisor:worker_child( fun telega@bot:start_chat_instance/1 ), _pipe@1 = gleam@otp@factory_supervisor:named(_pipe, Name), _pipe@2 = gleam@otp@factory_supervisor:restart_strategy( _pipe@1, transient ), _pipe@3 = gleam@otp@factory_supervisor:restart_tolerance( _pipe@2, Intensity, Period ), _pipe@4 = gleam@otp@factory_supervisor:timeout(_pipe@3, Timeout), gleam@otp@factory_supervisor:supervised(_pipe@4) end, {Spec, Name}. -file("src/telega.gleam", 1790). -spec generate_registry_name(binary()) -> binary(). generate_registry_name(Token) -> _pipe = Token, _pipe@1 = gleam@string:slice(_pipe, 0, 8), gleam@string:append( _pipe@1, <<"_"/utf8, (erlang:integer_to_binary(gleam@int:random(1000000)))/binary>> ). -file("src/telega.gleam", 737). ?DOC( " Initialize the bot for long polling with a supervision tree.\n" " Includes a supervised polling worker that auto-starts.\n" ). -spec init_for_polling(telega_builder(APFQ, APFR, APFS)) -> {ok, telega(APFQ, APFR, APFS)} | {error, telega@error:telega_error()}. init_for_polling(Builder) -> Api_client = gleam@option:unwrap( erlang:element(9, Builder), erlang:element(5, erlang:element(2, Builder)) ), gleam@result:'try'( begin _pipe = telega@api:get_me(Api_client), gleam@result:map_error(_pipe, fun(Err) -> case Err of {telegram_api_error, 404, _} -> {telegram_api_error, 404, <<"Bot not found. Please check that your BOT_TOKEN is valid and the bot exists. Get a valid token from @BotFather on Telegram."/utf8>>}; {telegram_api_error, 401, _} -> {telegram_api_error, 401, <<"Unauthorized. Your bot token is invalid. Please get a valid token from @BotFather on Telegram."/utf8>>}; _ -> Err end end) end, fun(Bot_info) -> Session_settings = gleam@option:to_result( erlang:element(4, Builder), no_session_settings_error ), gleam@result:'try'( Session_settings, fun(Session_settings@1) -> Router = gleam@option:to_result( erlang:element(3, Builder), {actor_error, <<"Router is required. Use with_router() to set a router."/utf8>>} ), gleam@result:'try'( Router, fun(Router@1) -> Router_handler = fun(Ctx, Upd) -> telega@router:handle(Router@1, Ctx, Upd) end, Catch_handler = gleam@option:unwrap( erlang:element(5, Builder), fun(_, Err@1) -> {error, Err@1} end ), Config = begin _record = erlang:element(2, Builder), {config, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), Api_client} end, Registry_name = generate_registry_name( telega@client:get_token( erlang:element(5, Config) ) ), gleam@result:'try'( telega@internal@registry:start(Registry_name), fun(Registry) -> {Chat_factory_spec, Chat_factory_name} = build_chat_factory_spec( Builder ), Bot_name = gleam_erlang_ffi:new_name( <<"telega_bot"/utf8>> ), Bot_subject = gleam@erlang@process:named_subject( Bot_name ), Chat_factory_ref = gleam@otp@factory_supervisor:get_by_name( Chat_factory_name ), Bot_spec = begin _pipe@1 = gleam@otp@supervision:worker( fun() -> telega@bot:start( Registry, Config, Bot_info, Router_handler, erlang:element(6, Builder), Session_settings@1, Catch_handler, erlang:element(7, Builder), Chat_factory_ref, {some, Bot_name} ) end ), gleam@otp@supervision:restart( _pipe@1, permanent ) end, Polling_timeout = gleam@option:unwrap( erlang:element(15, Builder), 30 ), Polling_limit = gleam@option:unwrap( erlang:element(16, Builder), 100 ), Polling_interval = gleam@option:unwrap( erlang:element(17, Builder), 1000 ), Allowed_updates = gleam@option:unwrap( resolve_allowed_updates(Builder), [] ), Poller_name = gleam_erlang_ffi:new_name( <<"telega_poller"/utf8>> ), Poller = gleam@erlang@process:named_subject( Poller_name ), Polling_spec = telega@polling:supervised( Api_client, Bot_subject, Polling_timeout, Polling_limit, Allowed_updates, Polling_interval, erlang:element(18, Builder), Poller_name ), gleam@result:'try'( begin _pipe@2 = gleam@otp@static_supervisor:new( one_for_one ), _pipe@3 = gleam@otp@static_supervisor:add( _pipe@2, Chat_factory_spec ), _pipe@4 = gleam@otp@static_supervisor:add( _pipe@3, Bot_spec ), _pipe@5 = gleam@otp@static_supervisor:add( _pipe@4, Polling_spec ), _pipe@6 = gleam@otp@static_supervisor:start( _pipe@5 ), gleam@result:map_error( _pipe@6, fun(Field@0) -> {supervisor_start_error, Field@0} end ) end, fun(Sup_started) -> finalize( Builder, Config, Bot_info, Bot_subject, erlang:element(2, Sup_started), {some, Poller} ) end ) end ) end ) end ) end ). -file("src/telega.gleam", 635). ?DOC(" Initialize the bot for long polling with nil session.\n"). -spec init_for_polling_nil_session(telega_builder(nil, APEV, APEW)) -> {ok, telega(nil, APEV, APEW)} | {error, telega@error:telega_error()}. init_for_polling_nil_session(Builder) -> _pipe = Builder, _pipe@1 = with_session_settings( _pipe, {session_settings, fun(_, _) -> {ok, nil} end, fun(_) -> {ok, none} end, fun() -> nil end} ), init_for_polling(_pipe@1). -file("src/telega.gleam", 650). ?DOC(" Initialize the bot for webhook mode with a supervision tree.\n"). -spec init(telega_builder(APFF, APFG, APFH)) -> {ok, telega(APFF, APFG, APFH)} | {error, telega@error:telega_error()}. init(Builder) -> Api_client = gleam@option:unwrap( erlang:element(9, Builder), erlang:element(5, erlang:element(2, Builder)) ), gleam@result:'try'( telega@api:set_webhook( Api_client, {set_webhook_parameters, <<<<(erlang:element(2, erlang:element(2, Builder)))/binary, "/"/utf8>>/binary, (erlang:element(3, erlang:element(2, Builder)))/binary>>, erlang:element(14, Builder), erlang:element(11, Builder), erlang:element(12, Builder), resolve_allowed_updates(Builder), erlang:element(10, Builder), {some, erlang:element(4, erlang:element(2, Builder))}} ), fun(Is_ok) -> gleam@bool:guard( not Is_ok, {error, set_webhook_error}, fun() -> gleam@result:'try'( telega@api:get_me(Api_client), fun(Bot_info) -> Session_settings = gleam@option:to_result( erlang:element(4, Builder), no_session_settings_error ), gleam@result:'try'( Session_settings, fun(Session_settings@1) -> Router = gleam@option:to_result( erlang:element(3, Builder), {router_error, <<"Router is required. Use with_router() to set a router."/utf8>>} ), gleam@result:'try'( Router, fun(Router@1) -> Router_handler = fun(Ctx, Upd) -> telega@router:handle( Router@1, Ctx, Upd ) end, Catch_handler = gleam@option:unwrap( erlang:element(5, Builder), fun(_, Err) -> {error, Err} end ), Config = begin _record = erlang:element( 2, Builder ), {config, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), Api_client} end, Registry_name = generate_registry_name( telega@client:get_token( erlang:element(5, Config) ) ), gleam@result:'try'( telega@internal@registry:start( Registry_name ), fun(Registry) -> {Chat_factory_spec, Chat_factory_name} = build_chat_factory_spec( Builder ), Bot_name = gleam_erlang_ffi:new_name( <<"telega_bot"/utf8>> ), Bot_subject = gleam@erlang@process:named_subject( Bot_name ), Chat_factory_ref = gleam@otp@factory_supervisor:get_by_name( Chat_factory_name ), Bot_spec = begin _pipe = gleam@otp@supervision:worker( fun() -> telega@bot:start( Registry, Config, Bot_info, Router_handler, erlang:element( 6, Builder ), Session_settings@1, Catch_handler, erlang:element( 7, Builder ), Chat_factory_ref, {some, Bot_name} ) end ), gleam@otp@supervision:restart( _pipe, permanent ) end, gleam@result:'try'( begin _pipe@1 = gleam@otp@static_supervisor:new( one_for_one ), _pipe@2 = gleam@otp@static_supervisor:add( _pipe@1, Chat_factory_spec ), _pipe@3 = gleam@otp@static_supervisor:add( _pipe@2, Bot_spec ), _pipe@4 = gleam@otp@static_supervisor:start( _pipe@3 ), gleam@result:map_error( _pipe@4, fun(Field@0) -> {supervisor_start_error, Field@0} end ) end, fun(Sup_started) -> finalize( Builder, Config, Bot_info, Bot_subject, erlang:element( 2, Sup_started ), none ) end ) end ) end ) end ) end ) end ) end ). -file("src/telega.gleam", 897). -spec child_started( {ok, telega(APGV, APGW, APGX)} | {error, telega@error:telega_error()} ) -> {ok, gleam@otp@actor:started(telega(APGV, APGW, APGX))} | {error, gleam@otp@actor:start_error()}. child_started(Started) -> case Started of {ok, Telega} -> {ok, {started, erlang:element(5, Telega), Telega}}; {error, E} -> {error, {init_failed, telega@error:to_string(E)}} end. -file("src/telega.gleam", 878). ?DOC( " Run the bot as a child of your own supervision tree (webhook mode).\n" "\n" " Wraps `init` into a `ChildSpecification`: telega still builds and owns its\n" " internal `chat factory → bot` tree, but that tree's root becomes a child\n" " of YOUR supervisor. If the bot tree dies, your supervisor restarts it by\n" " re-running `init` — `setWebhook`, `getMe` and a fresh internal tree.\n" "\n" " Add it after the resources the bot depends on (database pool, caches) —\n" " with a `RestForOne` strategy a dead dependency restarts the bot too:\n" "\n" " ```gleam\n" " let bot_ready = process.new_subject()\n" " let bot_child =\n" " telega.new(api_client:, url:, webhook_path:, secret_token:)\n" " |> telega.with_router(router)\n" " |> telega.with_on_start(fn(bot) { Ok(process.send(bot_ready, bot)) })\n" " |> telega.supervised()\n" "\n" " let assert Ok(_) =\n" " static_supervisor.new(static_supervisor.RestForOne)\n" " |> static_supervisor.add(db_pool_child)\n" " |> static_supervisor.add(bot_child)\n" " |> static_supervisor.start\n" "\n" " // Webhook adapters need the instance — receive it from the hook.\n" " let assert Ok(bot) = process.receive(bot_ready, within: 10_000)\n" " ```\n" "\n" " The started `Telega` instance is the child's data; supervisors don't hand\n" " child data back, so capture it in `with_on_start` as above when you need\n" " it outside the tree (webhook adapters, manual `shutdown`). Stopping your\n" " tree stops the bot the standard OTP way (no drain); for a drained stop use\n" " `shutdown` or `with_signal_handlers`.\n" ). -spec supervised(telega_builder(APGB, APGC, APGD)) -> gleam@otp@supervision:child_specification(telega(APGB, APGC, APGD)). supervised(Builder) -> gleam@otp@supervision:supervisor(fun() -> child_started(init(Builder)) end). -file("src/telega.gleam", 891). ?DOC( " Run the bot as a child of your own supervision tree (long-polling mode).\n" "\n" " The polling counterpart of `supervised` — wraps `init_for_polling`, so a\n" " restart re-runs `getMe` and starts a fresh `chat factory → bot → polling`\n" " tree. See `supervised` for the wiring pattern; in polling mode you rarely\n" " need the `Telega` instance afterwards, so the `with_on_start` capture is\n" " optional.\n" ). -spec supervised_for_polling(telega_builder(APGL, APGM, APGN)) -> gleam@otp@supervision:child_specification(telega(APGL, APGM, APGN)). supervised_for_polling(Builder) -> gleam@otp@supervision:supervisor( fun() -> child_started(init_for_polling(Builder)) end ). -file("src/telega.gleam", 912). ?DOC( " Handle an update.\n" "\n" " This function is useful when you want to handle updates in your own way.\n" ). -spec handle_update(telega(any(), any(), any()), telega@model@types:update()) -> boolean(). handle_update(Telega, Raw_update) -> Update = telega@update:raw_to_update(Raw_update), telega@bot:handle_update(erlang:element(4, Telega), Update). -file("src/telega.gleam", 956). ?DOC( " Handle an update, allowing the handler to answer it directly in the\n" " webhook HTTP response body ([webhook reply](https://core.telegram.org/bots/api#making-requests-when-getting-updates)).\n" "\n" " Waits up to `timeout` ms for the handler to either claim an eligible API\n" " call (see `telega/webhook_reply`) or finish. On a claim it returns\n" " `JsonResponse` for the adapter to send back; otherwise `EmptyResponse`.\n" " After the timeout the handler keeps running in the background and all its\n" " API calls go over regular HTTP.\n" "\n" " Pick a `timeout` safely below Telegram's webhook timeout — e.g. 5000 ms.\n" " Adapters expose this as `handle_bot_with_reply`; use this function directly\n" " only when implementing your own adapter.\n" "\n" " > ⚠️ A claimed call resolves to a synthetic stub in the handler: `True` for\n" " > boolean methods, a fake `Message` (`message_id: -1`, `date: 0`) for\n" " > `sendMessage`. Full guide in the `telega/webhook_reply` module docs.\n" ). -spec handle_update_webhook( telega(any(), any(), any()), telega@model@types:update(), integer() ) -> webhook_response(). handle_update_webhook(Telega, Raw_update, Timeout) -> Update = telega@update:raw_to_update(Raw_update), Envelope = gleam@erlang@process:new_subject(), Done = gleam@erlang@process:new_subject(), telega@bot:dispatch_update_with_envelope( erlang:element(4, Telega), Update, Done, Envelope ), Selector = begin _pipe = gleam_erlang_ffi:new_selector(), _pipe@1 = gleam@erlang@process:select_map( _pipe, Envelope, fun(Message) -> {claim, Reply, Granted} = Message, {reply_claimed, Reply, Granted} end ), gleam@erlang@process:select_map( _pipe@1, Done, fun(_) -> update_handled end ) end, case gleam_erlang_ffi:select(Selector, Timeout) of {ok, {reply_claimed, Reply@1, Granted@1}} -> gleam@erlang@process:send(Granted@1, true), {json_response, telega@webhook_reply:to_response_body(Reply@1)}; {ok, update_handled} -> empty_response; {error, nil} -> empty_response end. -file("src/telega.gleam", 993). ?DOC(" Get the bot's information.\n"). -spec get_me(telega(any(), any(), any())) -> telega@model@types:user(). get_me(Telega) -> erlang:element(3, Telega). -file("src/telega.gleam", 998). ?DOC(" Get session for the current context.\n"). -spec get_session(telega@bot:context(APIB, any(), any())) -> APIB. get_session(Ctx) -> erlang:element(5, Ctx). -file("src/telega.gleam", 1006). ?DOC( " Get the injected dependencies (services) for the current context.\n" "\n" " `dependencies` is set once at bot init via `with_dependencies` and is never persisted.\n" " See the `session` vs `dependencies` distinction in `with_dependencies`.\n" ). -spec get_dependencies(telega@bot:context(any(), any(), APIJ)) -> APIJ. get_dependencies(Ctx) -> erlang:element(6, Ctx). -file("src/telega.gleam", 1013). ?DOC(" Add logging context to the current context.\n"). -spec log_context( telega@bot:context(APIN, APIO, APIP), binary(), fun((telega@bot:context(APIN, APIO, APIP)) -> {ok, telega@bot:context(APIN, APIO, APIP)} | {error, APIO}) ) -> {ok, telega@bot:context(APIN, APIO, APIP)} | {error, APIO}. log_context(Ctx, Prefix, Fun) -> Ctx_with_log = {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), {some, Prefix}, erlang:element(10, Ctx)}, Fun(Ctx_with_log). -file("src/telega.gleam", 1024). ?DOC(" Context helpers for logging\n"). -spec log_info(telega@bot:context(any(), any(), any()), binary()) -> nil. log_info(Ctx, Message) -> case erlang:element(9, Ctx) of {some, Prefix} -> telega@internal@log:info_d(Prefix, Message); none -> telega@internal@log:info(Message) end. -file("src/telega.gleam", 1031). -spec log_error(telega@bot:context(any(), any(), any()), binary()) -> nil. log_error(Ctx, Message) -> case erlang:element(9, Ctx) of {some, Prefix} -> telega@internal@log:error_d(Prefix, Message); none -> telega@internal@log:error(Message) end. -file("src/telega.gleam", 1042). ?DOC( " Pauses the current chat actor's handler and waits for any update.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_any( telega@bot:context(APJU, APJV, APJW), gleam@option:option(telega@bot:handler(APJU, APJV, APJW)), gleam@option:option(integer()), fun((telega@bot:context(APJU, APJV, APJW), telega@update:update()) -> {ok, telega@bot:context(APJU, APJV, APJW)} | {error, APJV}) ) -> {ok, telega@bot:context(APJU, APJV, APJW)} | {error, APJV}. wait_any(Ctx, Handle_else, Timeout, Handler) -> telega@bot:wait_handler(Ctx, {handle_all, Handler}, Handle_else, Timeout). -file("src/telega.gleam", 1061). ?DOC( " Pauses the current chat actor's handler and waits for a specific command.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_command( telega@bot:context(APKS, APKT, APKU), binary(), gleam@option:option(telega@bot:handler(APKS, APKT, APKU)), gleam@option:option(integer()), fun((telega@bot:context(APKS, APKT, APKU), telega@update:command()) -> {ok, telega@bot:context(APKS, APKT, APKU)} | {error, APKT}) ) -> {ok, telega@bot:context(APKS, APKT, APKU)} | {error, APKT}. wait_command(Ctx, Command, Handle_else, Timeout, Continue) -> telega@bot:wait_handler( Ctx, {handle_command, Command, Continue}, Handle_else, Timeout ). -file("src/telega.gleam", 1081). ?DOC( " Pauses the current chat actor's handler and waits for one of the specified commands.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_commands( telega@bot:context(APLQ, APLR, APLS), list(binary()), gleam@option:option(telega@bot:handler(APLQ, APLR, APLS)), gleam@option:option(integer()), fun((telega@bot:context(APLQ, APLR, APLS), telega@update:command()) -> {ok, telega@bot:context(APLQ, APLR, APLS)} | {error, APLR}) ) -> {ok, telega@bot:context(APLQ, APLR, APLS)} | {error, APLR}. wait_commands(Ctx, Commands, Handle_else, Timeout, Continue) -> telega@bot:wait_handler( Ctx, {handle_commands, Commands, Continue}, Handle_else, Timeout ). -file("src/telega.gleam", 1101). ?DOC( " Pauses the current chat actor's handler and waits for a text message.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_text( telega@bot:context(APMP, APMQ, APMR), gleam@option:option(telega@bot:handler(APMP, APMQ, APMR)), gleam@option:option(integer()), fun((telega@bot:context(APMP, APMQ, APMR), binary()) -> {ok, telega@bot:context(APMP, APMQ, APMR)} | {error, APMQ}) ) -> {ok, telega@bot:context(APMP, APMQ, APMR)} | {error, APMQ}. wait_text(Ctx, Handle_else, Timeout, Continue) -> telega@bot:wait_handler(Ctx, {handle_text, Continue}, Handle_else, Timeout). -file("src/telega.gleam", 1120). ?DOC( " Pauses the current chat actor's handler and waits for a message that matches the given `Hears`.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_hears( telega@bot:context(APNN, APNO, APNP), telega@bot:hears(), gleam@option:option(telega@bot:handler(APNN, APNO, APNP)), gleam@option:option(integer()), fun((telega@bot:context(APNN, APNO, APNP), binary()) -> {ok, telega@bot:context(APNN, APNO, APNP)} | {error, APNO}) ) -> {ok, telega@bot:context(APNN, APNO, APNP)} | {error, APNO}. wait_hears(Ctx, Hears, Handle_else, Timeout, Continue) -> telega@bot:wait_handler( Ctx, {handle_hears, Hears, Continue}, Handle_else, Timeout ). -file("src/telega.gleam", 1140). ?DOC( " Pauses the current chat actor's handler and waits for any message.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_message( telega@bot:context(APOL, APOM, APON), gleam@option:option(telega@bot:handler(APOL, APOM, APON)), gleam@option:option(integer()), fun((telega@bot:context(APOL, APOM, APON), telega@model@types:message()) -> {ok, telega@bot:context(APOL, APOM, APON)} | {error, APOM}) ) -> {ok, telega@bot:context(APOL, APOM, APON)} | {error, APOM}. wait_message(Ctx, Handle_else, Timeout, Continue) -> telega@bot:wait_handler( Ctx, {handle_message, Continue}, Handle_else, Timeout ). -file("src/telega.gleam", 1159). ?DOC( " Pauses the current chat actor's handler and waits for a callback query.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_callback_query( telega@bot:context(APPJ, APPK, APPL), gleam@option:option(telega@bot:callback_query_filter()), gleam@option:option(telega@bot:handler(APPJ, APPK, APPL)), gleam@option:option(integer()), fun((telega@bot:context(APPJ, APPK, APPL), binary(), binary()) -> {ok, telega@bot:context(APPJ, APPK, APPL)} | {error, APPK}) ) -> {ok, telega@bot:context(APPJ, APPK, APPL)} | {error, APPK}. wait_callback_query(Ctx, Filter, Handle_else, Timeout, Continue) -> Default_re@1 = case gleam@regexp:from_string(<<".*"/utf8>>) of {ok, Default_re} -> Default_re; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"telega"/utf8>>, function => <<"wait_callback_query"/utf8>>, line => 1167, value => _assert_fail, start => 42130, 'end' => 42182, pattern_start => 42141, pattern_end => 42155}) end, Filter_value = gleam@option:unwrap( Filter, {callback_query_filter, Default_re@1} ), telega@bot:wait_handler( Ctx, {handle_callback_query, Filter_value, Continue}, Handle_else, Timeout ). -file("src/telega.gleam", 1182). ?DOC( " Pauses the current chat actor's handler and waits for an audio message.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_audio( telega@bot:context(APQI, APQJ, APQK), gleam@option:option(telega@bot:handler(APQI, APQJ, APQK)), gleam@option:option(integer()), fun((telega@bot:context(APQI, APQJ, APQK), telega@model@types:audio()) -> {ok, telega@bot:context(APQI, APQJ, APQK)} | {error, APQJ}) ) -> {ok, telega@bot:context(APQI, APQJ, APQK)} | {error, APQJ}. wait_audio(Ctx, Handle_else, Timeout, Continue) -> telega@bot:wait_handler(Ctx, {handle_audio, Continue}, Handle_else, Timeout). -file("src/telega.gleam", 1201). ?DOC( " Pauses the current chat actor's handler and waits for a video message.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_video( telega@bot:context(APRG, APRH, APRI), gleam@option:option(telega@bot:handler(APRG, APRH, APRI)), gleam@option:option(integer()), fun((telega@bot:context(APRG, APRH, APRI), telega@model@types:video()) -> {ok, telega@bot:context(APRG, APRH, APRI)} | {error, APRH}) ) -> {ok, telega@bot:context(APRG, APRH, APRI)} | {error, APRH}. wait_video(Ctx, Handle_else, Timeout, Continue) -> telega@bot:wait_handler(Ctx, {handle_video, Continue}, Handle_else, Timeout). -file("src/telega.gleam", 1220). ?DOC( " Pauses the current chat actor's handler and waits for a voice message.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_voice( telega@bot:context(APSE, APSF, APSG), gleam@option:option(telega@bot:handler(APSE, APSF, APSG)), gleam@option:option(integer()), fun((telega@bot:context(APSE, APSF, APSG), telega@model@types:voice()) -> {ok, telega@bot:context(APSE, APSF, APSG)} | {error, APSF}) ) -> {ok, telega@bot:context(APSE, APSF, APSG)} | {error, APSF}. wait_voice(Ctx, Handle_else, Timeout, Continue) -> telega@bot:wait_handler(Ctx, {handle_voice, Continue}, Handle_else, Timeout). -file("src/telega.gleam", 1239). ?DOC( " Pauses the current chat actor's handler and waits for photos.\n" " Other chats and users continue to be handled concurrently.\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_photos( telega@bot:context(APTC, APTD, APTE), gleam@option:option(telega@bot:handler(APTC, APTD, APTE)), gleam@option:option(integer()), fun((telega@bot:context(APTC, APTD, APTE), list(telega@model@types:photo_size())) -> {ok, telega@bot:context(APTC, APTD, APTE)} | {error, APTD}) ) -> {ok, telega@bot:context(APTC, APTD, APTE)} | {error, APTD}. wait_photos(Ctx, Handle_else, Timeout, Continue) -> telega@bot:wait_handler( Ctx, {handle_photos, Continue}, Handle_else, Timeout ). -file("src/telega.gleam", 1279). ?DOC( " Wait for a number with validation.\n" "\n" " This function waits for user to send text that can be parsed as an integer,\n" " with optional min/max validation.\n" "\n" " If validation fails and `or` handler is provided, it will be called.\n" " Otherwise, the function will keep waiting for valid input.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " use ctx, age <- wait_number(\n" " ctx,\n" " min: Some(0),\n" " max: Some(120),\n" " or: Some(bot.HandleText(fn(ctx, invalid) {\n" " reply.with_text(ctx, \"Please enter age between 0 and 120\")\n" " })),\n" " timeout: None,\n" " )\n" " ```\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_number( telega@bot:context(APUB, APUC, APUD), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(telega@bot:handler(APUB, APUC, APUD)), gleam@option:option(integer()), fun((telega@bot:context(APUB, APUC, APUD), integer()) -> {ok, telega@bot:context(APUB, APUC, APUD)} | {error, APUC}) ) -> {ok, telega@bot:context(APUB, APUC, APUD)} | {error, APUC}. wait_number(Ctx, Min, Max, Handle_else, Timeout, Continue) -> Validation_handler = {handle_text, fun(Ctx@1, Text) -> case gleam_stdlib:parse_int(Text) of {ok, Number} -> Min_ok = case Min of {some, Min_val} -> Number >= Min_val; none -> true end, Max_ok = case Max of {some, Max_val} -> Number =< Max_val; none -> true end, case Min_ok andalso Max_ok of true -> Continue(Ctx@1, Number); false -> wait_number( Ctx@1, Min, Max, Handle_else, Timeout, Continue ) end; {error, _} -> wait_number(Ctx@1, Min, Max, Handle_else, Timeout, Continue) end end}, telega@bot:wait_handler(Ctx, Validation_handler, Handle_else, Timeout). -file("src/telega.gleam", 1338). ?DOC( " Wait for email with validation.\n" "\n" " This function waits for user to send text that matches email pattern.\n" "\n" " If validation fails and `or` handler is provided, it will be called.\n" " Otherwise, the function will keep waiting for valid input.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " use ctx, email <- wait_email(\n" " ctx,\n" " or: Some(bot.HandleText(fn(ctx, invalid) {\n" " reply.with_text(ctx, \"Invalid email format. Try again.\")\n" " })),\n" " timeout: None,\n" " )\n" " ```\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_email( telega@bot:context(APVB, APVC, APVD), gleam@option:option(telega@bot:handler(APVB, APVC, APVD)), gleam@option:option(integer()), fun((telega@bot:context(APVB, APVC, APVD), binary()) -> {ok, telega@bot:context(APVB, APVC, APVD)} | {error, APVC}) ) -> {ok, telega@bot:context(APVB, APVC, APVD)} | {error, APVC}. wait_email(Ctx, Handle_else, Timeout, Continue) -> Email_pattern = <<"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"/utf8>>, Validation_handler = {handle_text, fun(Ctx@1, Text) -> case gleam@regexp:from_string(Email_pattern) of {ok, Re} -> case gleam@regexp:check(Re, Text) of true -> Continue(Ctx@1, Text); false -> wait_email(Ctx@1, Handle_else, Timeout, Continue) end; {error, _} -> Continue(Ctx@1, Text) end end}, telega@bot:wait_handler(Ctx, Validation_handler, Handle_else, Timeout). -file("src/telega.gleam", 1797). -spec list_at(list(AQBL), integer()) -> {ok, AQBL} | {error, nil}. list_at(List, Index) -> case {List, Index} of {[], _} -> {error, nil}; {[Head | _], 0} -> {ok, Head}; {[_ | Tail], N} -> list_at(Tail, N - 1) end. -file("src/telega.gleam", 1383). ?DOC( " Wait for user choice from inline keyboard.\n" "\n" " This function creates an inline keyboard with provided options\n" " and waits for user to select one.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " use ctx, color <- wait_choice(\n" " ctx,\n" " [\n" " #(\"🔴 Red\", Red),\n" " #(\"🔵 Blue\", Blue),\n" " #(\"🟢 Green\", Green),\n" " ],\n" " or: None,\n" " timeout: None,\n" " )\n" " ```\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_choice( telega@bot:context(APVZ, APWA, APWB), list({binary(), APWF}), gleam@option:option(telega@bot:handler(APVZ, APWA, APWB)), gleam@option:option(integer()), fun((telega@bot:context(APVZ, APWA, APWB), APWF) -> {ok, telega@bot:context(APVZ, APWA, APWB)} | {error, APWA}) ) -> {ok, telega@bot:context(APVZ, APWA, APWB)} | {error, APWA}. wait_choice(Ctx, Options, Handle_else, Timeout, Continue) -> Buttons = begin _pipe = Options, gleam@list:index_map( _pipe, fun(Opt, Idx) -> {Label, _} = Opt, Callback_data = erlang:integer_to_binary(Idx), {inline_keyboard_button, Label, none, none, none, {some, Callback_data}, none, none, none, none, none, none, none, none} end ) end, Keyboard = {inline_keyboard_markup, [Buttons]}, Send_params = {send_message_parameters, none, {int, erlang:element(3, erlang:element(3, Ctx))}, none, <<""/utf8>>, none, none, none, none, none, none, none, none, {some, {send_message_reply_inline_keyboard_markup_parameters, Keyboard}}}, _ = telega@api:send_message( erlang:element(5, erlang:element(4, Ctx)), Send_params ), wait_callback_query( Ctx, none, Handle_else, Timeout, fun(Ctx@1, Data, _) -> case gleam_stdlib:parse_int(Data) of {ok, Idx@1} -> case list_at(Options, Idx@1) of {ok, {_, Value}} -> Continue(Ctx@1, Value); {error, _} -> wait_choice( Ctx@1, Options, Handle_else, Timeout, Continue ) end; {error, _} -> wait_choice(Ctx@1, Options, Handle_else, Timeout, Continue) end end ). -file("src/telega.gleam", 1481). ?DOC( " Wait for update matching custom filter.\n" "\n" " This function waits for any update that passes the provided filter function.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " use ctx, photo_update <- wait_for(\n" " ctx,\n" " filter: fn(upd) {\n" " case upd {\n" " update.PhotoUpdate(..) -> True\n" " _ -> False\n" " }\n" " },\n" " or: Some(bot.HandleAll(fn(ctx, wrong_update) {\n" " reply.with_text(ctx, \"Please send a photo\")\n" " })),\n" " timeout: Some(60_000),\n" " )\n" " ```\n" "\n" " See [conversation](/docs/conversation)\n" ). -spec wait_for( telega@bot:context(APWZ, APXA, APXB), fun((telega@update:update()) -> boolean()), gleam@option:option(telega@bot:handler(APWZ, APXA, APXB)), gleam@option:option(integer()), fun((telega@bot:context(APWZ, APXA, APXB), telega@update:update()) -> {ok, telega@bot:context(APWZ, APXA, APXB)} | {error, APXA}) ) -> {ok, telega@bot:context(APWZ, APXA, APXB)} | {error, APXA}. wait_for(Ctx, Filter, Handle_else, Timeout, Continue) -> Filter_handler = {handle_all, fun(Ctx@1, Upd) -> case Filter(Upd) of true -> Continue(Ctx@1, Upd); false -> wait_for(Ctx@1, Filter, Handle_else, Timeout, Continue) end end}, telega@bot:wait_handler(Ctx, Filter_handler, Handle_else, Timeout). -file("src/telega.gleam", 1524). ?DOC( " Pauses the current chat actor's handler and waits for an update that matches\n" " a composable `router.Filter`.\n" "\n" " Unlike the typed waiters (`wait_text`, `wait_photos`, …) which each listen\n" " for a single update type, this accepts the router's filter combinators, so a\n" " single continuation can wait for several types at once. Combine with\n" " `router.or`/`router.or2` (any), `router.and`/`router.and2` (all) and\n" " `router.not`:\n" "\n" " ```gleam\n" " use ctx, upd <- wait_filtered(\n" " ctx,\n" " filter: router.or2(router.is_text(), router.has_photo()),\n" " or: None,\n" " timeout: None,\n" " )\n" " case upd {\n" " update.TextUpdate(text:, ..) -> // ...\n" " update.PhotoUpdate(photos:, ..) -> // ...\n" " _ -> // ...\n" " }\n" " ```\n" "\n" " `wait_for` remains the escape hatch for a raw `fn(Update) -> Bool` predicate.\n" ). -spec wait_filtered( telega@bot:context(APXX, APXY, APXZ), telega@router:filter(), gleam@option:option(telega@bot:handler(APXX, APXY, APXZ)), gleam@option:option(integer()), fun((telega@bot:context(APXX, APXY, APXZ), telega@update:update()) -> {ok, telega@bot:context(APXX, APXY, APXZ)} | {error, APXY}) ) -> {ok, telega@bot:context(APXX, APXY, APXZ)} | {error, APXY}. wait_filtered(Ctx, Filter, Handle_else, Timeout, Continue) -> wait_for( Ctx, fun(_capture) -> telega@router:matches(Filter, _capture) end, Handle_else, Timeout, Continue ). -file("src/telega.gleam", 1543). ?DOC( " Start polling with default configuration for a Telega instance.\n" " This is useful when you want to manually start polling outside the supervision tree.\n" ). -spec start_polling_default(telega(any(), any(), any())) -> {ok, telega@polling:poller()} | {error, telega@error:telega_error()}. start_polling_default(Telega) -> telega@polling:start_polling_default( erlang:element(5, erlang:element(2, Telega)), erlang:element(4, Telega) ). -file("src/telega.gleam", 1599). ?DOC( " Whether the bot is currently draining and no longer accepting updates.\n" "\n" " Webhook adapters should answer `503` when this is `True` so Telegram retries\n" " the update after the deploy instead of it being dropped.\n" ). -spec is_draining(telega(any(), any(), any())) -> boolean(). is_draining(Telega) -> telega@bot:is_draining(erlang:element(4, Telega)). -file("src/telega.gleam", 1604). ?DOC(" Get the supervisor PID for the running bot instance.\n"). -spec get_supervisor_pid(telega(any(), any(), any())) -> gleam@erlang@process:pid_(). get_supervisor_pid(Telega) -> erlang:element(5, Telega).