Current section
Files
Jump to
Current section
Files
src/telega@chat_action.erl
-module(telega@chat_action).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/telega/chat_action.gleam").
-export([with_action_every/4, with_action/3]).
-export_type([action/0, worker_message/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Keeps a chat action indicator (\"typing…\", \"sending photo…\") alive while\n"
" a long-running handler executes.\n"
"\n"
" Telegram clears a chat action after ~5 seconds, so a single\n"
" `api.send_chat_action` call is not enough for slow handlers. `with_action`\n"
" re-sends the action every ~4 seconds until the wrapped function returns:\n"
"\n"
" ```gleam\n"
" import telega/chat_action\n"
"\n"
" fn handler(ctx, _) {\n"
" use <- chat_action.with_action(ctx, chat_action.Typing)\n"
" // ... long-running work: LLM call, file processing, etc.\n"
" reply.with_text(ctx, \"Done!\")\n"
" }\n"
" ```\n"
"\n"
" The repeating sender runs in an unlinked worker process that monitors the\n"
" caller: it stops when the wrapped function returns *or* when the calling\n"
" process dies, so no processes are leaked even if the handler crashes.\n"
).
-type action() :: typing |
upload_photo |
record_video |
upload_video |
record_voice |
upload_voice |
upload_document |
choose_sticker |
find_location |
record_video_note |
upload_video_note.
-type worker_message() :: stop_worker.
-file("src/telega/chat_action.gleam", 108).
-spec worker_loop(
telega@client:telegram_client(),
telega@model@types:send_chat_action_parameters(),
integer(),
gleam@erlang@process:selector(worker_message())
) -> nil.
worker_loop(Client, Parameters, Interval, Selector) ->
case gleam_erlang_ffi:select(Selector, Interval) of
{ok, stop_worker} ->
nil;
{error, nil} ->
_ = telega@api:send_chat_action(Client, Parameters),
worker_loop(Client, Parameters, Interval, Selector)
end.
-file("src/telega/chat_action.gleam", 123).
-spec to_model_action(action()) -> telega@model@types:chat_action().
to_model_action(Action) ->
case Action of
typing ->
typing;
upload_photo ->
upload_photo;
record_video ->
record_video;
upload_video ->
upload_video;
record_voice ->
record_voice;
upload_voice ->
upload_voice;
upload_document ->
upload_document;
choose_sticker ->
choose_sticker;
find_location ->
find_location;
record_video_note ->
record_video_note;
upload_video_note ->
upload_video_note
end.
-file("src/telega/chat_action.gleam", 68).
?DOC(
" Same as `with_action` but with a custom re-send interval in milliseconds.\n"
" Useful for tests and for long uploads where a different cadence is needed.\n"
).
-spec with_action_every(
telega@bot:context(any(), any(), any()),
action(),
integer(),
fun(() -> AUVH)
) -> AUVH.
with_action_every(Ctx, Action, Interval, Run) ->
Client = erlang:element(5, erlang:element(4, Ctx)),
Parameters = {send_chat_action_parameters,
{str, erlang:element(2, Ctx)},
none,
none,
to_model_action(Action)},
_ = telega@api:send_chat_action(Client, Parameters),
Interval@1 = gleam@int:max(Interval, 1),
Caller = erlang:self(),
Handshake = gleam@erlang@process:new_subject(),
proc_lib:spawn(
fun() ->
Stop = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Handshake, Stop),
Monitor = gleam@erlang@process:monitor(Caller),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
_pipe@1 = gleam@erlang@process:select(_pipe, Stop),
gleam@erlang@process:select_specific_monitor(
_pipe@1,
Monitor,
fun(_) -> stop_worker end
)
end,
worker_loop(Client, Parameters, Interval@1, Selector)
end
),
Stop@1 = gleam@erlang@process:'receive'(Handshake, 1000),
Result = Run(),
case Stop@1 of
{ok, Stop@2} ->
gleam@erlang@process:send(Stop@2, stop_worker);
{error, nil} ->
nil
end,
Result.
-file("src/telega/chat_action.gleam", 58).
?DOC(
" Sends `action` immediately and then re-sends it every ~4 seconds while\n"
" `run` executes. Returns the result of `run`.\n"
"\n"
" ```gleam\n"
" use <- chat_action.with_action(ctx, chat_action.Typing)\n"
" slow_work(ctx)\n"
" ```\n"
).
-spec with_action(
telega@bot:context(any(), any(), any()),
action(),
fun(() -> AUVA)
) -> AUVA.
with_action(Ctx, Action, Run) ->
with_action_every(Ctx, Action, 4000, Run).