Current section
Files
Jump to
Current section
Files
src/telega.erl
-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, handle_update/2, 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, start_polling_default/1, is_draining/1, get_supervisor_pid/1]).
-export_type([telega/3, telega_builder/3]).
-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(ALLL, ALLM, ALLN) :: {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, ALLL, ALLM, ALLN}.
-opaque telega_builder(ALLO, ALLP, ALLQ) :: {telega_builder,
telega@internal@config:config(),
gleam@option:option(telega@router:router(ALLO, ALLP, ALLQ)),
gleam@option:option(telega@bot:session_settings(ALLO, ALLP)),
gleam@option:option(fun((telega@bot:context(ALLO, ALLP, ALLQ), ALLP) -> {ok,
nil} |
{error, ALLP})),
list(fun((telega@bot:pre_context(ALLQ)) -> telega@bot:pre_router_result())),
ALLQ,
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(ALLO, ALLP, ALLQ)) -> {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()}.
-file("src/telega.gleam", 99).
?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", 107).
?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", 116).
?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", 126).
?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", 134).
?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", 141).
?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(), ALMY) -> telega_builder(any(), any(), ALMY).
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", 177).
-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", 191).
-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", 205).
?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", 229).
?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()),
ALNM
) -> telega_builder(any(), any(), ALNM).
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", 248).
?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", 264).
?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(), ALNX) -> telega_builder(any(), any(), ALNX).
new_for_polling_with_dependencies(Api_client, Dependencies) ->
default_builder(polling_config(Api_client), Dependencies).
-file("src/telega.gleam", 274).
?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(ALOD, ALOE, ALOF),
telega@router:router(ALOD, ALOE, ALOF)
) -> telega_builder(ALOD, ALOE, ALOF).
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", 282).
?DOC(" Set session settings for the bot.\n").
-spec with_session_settings(
telega_builder(ALOP, ALOQ, ALOR),
telega@bot:session_settings(ALOP, ALOQ)
) -> telega_builder(ALOP, ALOQ, ALOR).
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", 319).
?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(ALPA, ALPB, any()), ALPG) -> telega_builder(ALPA, ALPB, ALPG).
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", 359).
?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(ALPK, ALPL, ALPM),
fun((telega@bot:context(ALPK, ALPL, ALPM), ALPL) -> {ok, nil} |
{error, ALPL})
) -> telega_builder(ALPK, ALPL, ALPM).
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", 391).
?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(ALPW, ALPX, ALPY),
fun((telega@bot:pre_context(ALPY)) -> telega@bot:pre_router_result())
) -> telega_builder(ALPW, ALPX, ALPY).
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", 402).
?DOC(" Set whether to drop pending updates.\n").
-spec set_drop_pending_updates(telega_builder(ALQG, ALQH, ALQI), boolean()) -> telega_builder(ALQG, ALQH, ALQI).
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", 410).
?DOC(" Set max connections for webhook.\n").
-spec set_max_connections(telega_builder(ALQP, ALQQ, ALQR), integer()) -> telega_builder(ALQP, ALQQ, ALQR).
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", 418).
?DOC(" Set IP address for webhook.\n").
-spec set_ip_address(telega_builder(ALQY, ALQZ, ALRA), binary()) -> telega_builder(ALQY, ALQZ, ALRA).
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", 426).
?DOC(" Set allowed updates for webhook.\n").
-spec set_allowed_updates(telega_builder(ALRH, ALRI, ALRJ), list(binary())) -> telega_builder(ALRH, ALRI, ALRJ).
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", 434).
?DOC(" Set certificate for webhook.\n").
-spec set_certificate(
telega_builder(ALRR, ALRS, ALRT),
telega@model@types:file()
) -> telega_builder(ALRR, ALRS, ALRT).
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", 442).
?DOC(" Set a custom API client.\n").
-spec set_api_client(
telega_builder(ALSA, ALSB, ALSC),
telega@client:telegram_client()
) -> telega_builder(ALSA, ALSB, ALSC).
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", 450).
?DOC(" Set polling configuration for the supervised polling worker.\n").
-spec with_polling_config(
telega_builder(ALSJ, ALSK, ALSL),
integer(),
integer(),
integer()
) -> telega_builder(ALSJ, ALSK, ALSL).
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", 465).
?DOC(" Set a callback for when polling stops due to errors.\n").
-spec with_polling_on_stop(
telega_builder(ALSS, ALST, ALSU),
fun((telega@error:telega_error()) -> nil)
) -> telega_builder(ALSS, ALST, ALSU).
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", 477).
?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(ALTB, ALTC, ALTD),
integer(),
integer(),
integer()
) -> telega_builder(ALTB, ALTC, ALTD).
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", 506).
?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(ALTK, ALTL, ALTM),
fun((telega(ALTK, ALTL, ALTM)) -> {ok, nil} |
{error, telega@error:telega_error()})
) -> telega_builder(ALTK, ALTL, ALTM).
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", 517).
?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(ALTY, ALTZ, ALUA), fun(() -> nil)) -> telega_builder(ALTY, ALTZ, ALUA).
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", 528).
?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(ALUH, ALUI, ALUJ), integer()) -> telega_builder(ALUH, ALUI, ALUJ).
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", 545).
?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(ALUQ, ALUR, ALUS)) -> telega_builder(ALUQ, ALUR, ALUS).
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", 567).
?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(ALUZ, ALVA, ALVB)) -> telega_builder(ALUZ, ALVA, ALVB).
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", 592).
?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(ALVI, ALVJ, ALVK),
list(binary()),
fun((binary(), binary()) -> gleam@option:option(binary()))
) -> telega_builder(ALVI, ALVJ, ALVK).
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", 612).
?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(ALVT, ALVU, ALVV)) -> telega_builder(ALVT, ALVU, ALVV).
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", 619).
?DOC(" Set nil session for the bot.\n").
-spec with_nil_session(telega_builder(nil, ALWC, ALWD)) -> telega_builder(nil, ALWC, ALWD).
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", 1385).
?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", 1553).
-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", 1503).
?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)}
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}
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", 1435).
?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(AMOY, AMOZ, AMPA),
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(AMOY, AMOZ, AMPA)} | {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", 1483).
?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", 1573).
-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(APTP, APTQ, APTR), gleam@erlang@process:subject(telega@bot:chat_instance_message(APTP, APTQ, APTR)))),
gleam@erlang@process:name(gleam@otp@factory_supervisor:message(telega@bot:chat_instance_args(APTP, APTQ, APTR), gleam@erlang@process:subject(telega@bot:chat_instance_message(APTP, APTQ, APTR))))}.
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", 1603).
-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", 735).
?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(ALXF, ALXG, ALXH)) -> {ok,
telega(ALXF, ALXG, ALXH)} |
{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", 633).
?DOC(" Initialize the bot for long polling with nil session.\n").
-spec init_for_polling_nil_session(telega_builder(nil, ALWK, ALWL)) -> {ok,
telega(nil, ALWK, ALWL)} |
{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", 648).
?DOC(" Initialize the bot for webhook mode with a supervision tree.\n").
-spec init(telega_builder(ALWU, ALWV, ALWW)) -> {ok, telega(ALWU, ALWV, ALWW)} |
{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", 846).
?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", 855).
?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", 860).
?DOC(" Get session for the current context.\n").
-spec get_session(telega@bot:context(ALYC, any(), any())) -> ALYC.
get_session(Ctx) ->
erlang:element(5, Ctx).
-file("src/telega.gleam", 868).
?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(), ALYK)) -> ALYK.
get_dependencies(Ctx) ->
erlang:element(6, Ctx).
-file("src/telega.gleam", 875).
?DOC(" Add logging context to the current context.\n").
-spec log_context(
telega@bot:context(ALYO, ALYP, ALYQ),
binary(),
fun((telega@bot:context(ALYO, ALYP, ALYQ)) -> {ok,
telega@bot:context(ALYO, ALYP, ALYQ)} |
{error, ALYP})
) -> {ok, telega@bot:context(ALYO, ALYP, ALYQ)} | {error, ALYP}.
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", 886).
?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", 893).
-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", 904).
?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(ALZV, ALZW, ALZX),
gleam@option:option(telega@bot:handler(ALZV, ALZW, ALZX)),
gleam@option:option(integer()),
fun((telega@bot:context(ALZV, ALZW, ALZX), telega@update:update()) -> {ok,
telega@bot:context(ALZV, ALZW, ALZX)} |
{error, ALZW})
) -> {ok, telega@bot:context(ALZV, ALZW, ALZX)} | {error, ALZW}.
wait_any(Ctx, Handle_else, Timeout, Handler) ->
telega@bot:wait_handler(Ctx, {handle_all, Handler}, Handle_else, Timeout).
-file("src/telega.gleam", 923).
?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(AMAT, AMAU, AMAV),
binary(),
gleam@option:option(telega@bot:handler(AMAT, AMAU, AMAV)),
gleam@option:option(integer()),
fun((telega@bot:context(AMAT, AMAU, AMAV), telega@update:command()) -> {ok,
telega@bot:context(AMAT, AMAU, AMAV)} |
{error, AMAU})
) -> {ok, telega@bot:context(AMAT, AMAU, AMAV)} | {error, AMAU}.
wait_command(Ctx, Command, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(
Ctx,
{handle_command, Command, Continue},
Handle_else,
Timeout
).
-file("src/telega.gleam", 943).
?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(AMBR, AMBS, AMBT),
list(binary()),
gleam@option:option(telega@bot:handler(AMBR, AMBS, AMBT)),
gleam@option:option(integer()),
fun((telega@bot:context(AMBR, AMBS, AMBT), telega@update:command()) -> {ok,
telega@bot:context(AMBR, AMBS, AMBT)} |
{error, AMBS})
) -> {ok, telega@bot:context(AMBR, AMBS, AMBT)} | {error, AMBS}.
wait_commands(Ctx, Commands, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(
Ctx,
{handle_commands, Commands, Continue},
Handle_else,
Timeout
).
-file("src/telega.gleam", 963).
?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(AMCQ, AMCR, AMCS),
gleam@option:option(telega@bot:handler(AMCQ, AMCR, AMCS)),
gleam@option:option(integer()),
fun((telega@bot:context(AMCQ, AMCR, AMCS), binary()) -> {ok,
telega@bot:context(AMCQ, AMCR, AMCS)} |
{error, AMCR})
) -> {ok, telega@bot:context(AMCQ, AMCR, AMCS)} | {error, AMCR}.
wait_text(Ctx, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(Ctx, {handle_text, Continue}, Handle_else, Timeout).
-file("src/telega.gleam", 982).
?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(AMDO, AMDP, AMDQ),
telega@bot:hears(),
gleam@option:option(telega@bot:handler(AMDO, AMDP, AMDQ)),
gleam@option:option(integer()),
fun((telega@bot:context(AMDO, AMDP, AMDQ), binary()) -> {ok,
telega@bot:context(AMDO, AMDP, AMDQ)} |
{error, AMDP})
) -> {ok, telega@bot:context(AMDO, AMDP, AMDQ)} | {error, AMDP}.
wait_hears(Ctx, Hears, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(
Ctx,
{handle_hears, Hears, Continue},
Handle_else,
Timeout
).
-file("src/telega.gleam", 1002).
?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(AMEM, AMEN, AMEO),
gleam@option:option(telega@bot:handler(AMEM, AMEN, AMEO)),
gleam@option:option(integer()),
fun((telega@bot:context(AMEM, AMEN, AMEO), telega@model@types:message()) -> {ok,
telega@bot:context(AMEM, AMEN, AMEO)} |
{error, AMEN})
) -> {ok, telega@bot:context(AMEM, AMEN, AMEO)} | {error, AMEN}.
wait_message(Ctx, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(
Ctx,
{handle_message, Continue},
Handle_else,
Timeout
).
-file("src/telega.gleam", 1021).
?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(AMFK, AMFL, AMFM),
gleam@option:option(telega@bot:callback_query_filter()),
gleam@option:option(telega@bot:handler(AMFK, AMFL, AMFM)),
gleam@option:option(integer()),
fun((telega@bot:context(AMFK, AMFL, AMFM), binary(), binary()) -> {ok,
telega@bot:context(AMFK, AMFL, AMFM)} |
{error, AMFL})
) -> {ok, telega@bot:context(AMFK, AMFL, AMFM)} | {error, AMFL}.
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 => <<?FILEPATH/utf8>>,
module => <<"telega"/utf8>>,
function => <<"wait_callback_query"/utf8>>,
line => 1029,
value => _assert_fail,
start => 36582,
'end' => 36634,
pattern_start => 36593,
pattern_end => 36607})
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", 1044).
?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(AMGJ, AMGK, AMGL),
gleam@option:option(telega@bot:handler(AMGJ, AMGK, AMGL)),
gleam@option:option(integer()),
fun((telega@bot:context(AMGJ, AMGK, AMGL), telega@model@types:audio()) -> {ok,
telega@bot:context(AMGJ, AMGK, AMGL)} |
{error, AMGK})
) -> {ok, telega@bot:context(AMGJ, AMGK, AMGL)} | {error, AMGK}.
wait_audio(Ctx, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(Ctx, {handle_audio, Continue}, Handle_else, Timeout).
-file("src/telega.gleam", 1063).
?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(AMHH, AMHI, AMHJ),
gleam@option:option(telega@bot:handler(AMHH, AMHI, AMHJ)),
gleam@option:option(integer()),
fun((telega@bot:context(AMHH, AMHI, AMHJ), telega@model@types:video()) -> {ok,
telega@bot:context(AMHH, AMHI, AMHJ)} |
{error, AMHI})
) -> {ok, telega@bot:context(AMHH, AMHI, AMHJ)} | {error, AMHI}.
wait_video(Ctx, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(Ctx, {handle_video, Continue}, Handle_else, Timeout).
-file("src/telega.gleam", 1082).
?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(AMIF, AMIG, AMIH),
gleam@option:option(telega@bot:handler(AMIF, AMIG, AMIH)),
gleam@option:option(integer()),
fun((telega@bot:context(AMIF, AMIG, AMIH), telega@model@types:voice()) -> {ok,
telega@bot:context(AMIF, AMIG, AMIH)} |
{error, AMIG})
) -> {ok, telega@bot:context(AMIF, AMIG, AMIH)} | {error, AMIG}.
wait_voice(Ctx, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(Ctx, {handle_voice, Continue}, Handle_else, Timeout).
-file("src/telega.gleam", 1101).
?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(AMJD, AMJE, AMJF),
gleam@option:option(telega@bot:handler(AMJD, AMJE, AMJF)),
gleam@option:option(integer()),
fun((telega@bot:context(AMJD, AMJE, AMJF), list(telega@model@types:photo_size())) -> {ok,
telega@bot:context(AMJD, AMJE, AMJF)} |
{error, AMJE})
) -> {ok, telega@bot:context(AMJD, AMJE, AMJF)} | {error, AMJE}.
wait_photos(Ctx, Handle_else, Timeout, Continue) ->
telega@bot:wait_handler(
Ctx,
{handle_photos, Continue},
Handle_else,
Timeout
).
-file("src/telega.gleam", 1141).
?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(AMKC, AMKD, AMKE),
gleam@option:option(integer()),
gleam@option:option(integer()),
gleam@option:option(telega@bot:handler(AMKC, AMKD, AMKE)),
gleam@option:option(integer()),
fun((telega@bot:context(AMKC, AMKD, AMKE), integer()) -> {ok,
telega@bot:context(AMKC, AMKD, AMKE)} |
{error, AMKD})
) -> {ok, telega@bot:context(AMKC, AMKD, AMKE)} | {error, AMKD}.
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", 1200).
?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(AMLC, AMLD, AMLE),
gleam@option:option(telega@bot:handler(AMLC, AMLD, AMLE)),
gleam@option:option(integer()),
fun((telega@bot:context(AMLC, AMLD, AMLE), binary()) -> {ok,
telega@bot:context(AMLC, AMLD, AMLE)} |
{error, AMLD})
) -> {ok, telega@bot:context(AMLC, AMLD, AMLE)} | {error, AMLD}.
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", 1610).
-spec list_at(list(AMQO), integer()) -> {ok, AMQO} | {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", 1245).
?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(AMMA, AMMB, AMMC),
list({binary(), AMMG}),
gleam@option:option(telega@bot:handler(AMMA, AMMB, AMMC)),
gleam@option:option(integer()),
fun((telega@bot:context(AMMA, AMMB, AMMC), AMMG) -> {ok,
telega@bot:context(AMMA, AMMB, AMMC)} |
{error, AMMB})
) -> {ok, telega@bot:context(AMMA, AMMB, AMMC)} | {error, AMMB}.
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", 1343).
?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(AMNA, AMNB, AMNC),
fun((telega@update:update()) -> boolean()),
gleam@option:option(telega@bot:handler(AMNA, AMNB, AMNC)),
gleam@option:option(integer()),
fun((telega@bot:context(AMNA, AMNB, AMNC), telega@update:update()) -> {ok,
telega@bot:context(AMNA, AMNB, AMNC)} |
{error, AMNB})
) -> {ok, telega@bot:context(AMNA, AMNB, AMNC)} | {error, AMNB}.
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", 1364).
?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", 1420).
?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", 1425).
?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).