Current section
Files
Jump to
Current section
Files
src/telega@bot.erl
-module(telega@bot).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([cancel_conversation/2, next_session/2, get_session/2, handle_update/2, wait_handler/4, start/6]).
-export_type([bot/2, bot_message/0, chat_instance_message/2, continuation/2, chat_instance/2, context/2, session_settings/2, callback_query_filter/0, hears/0, handler/2]).
-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 bot(AMIV, AMIW) :: {bot,
gleam@erlang@process:subject(bot_message()),
telega@internal@config:config(),
telega@model:user(),
fun((context(AMIV, AMIW), AMIW) -> {ok, nil} | {error, AMIW}),
session_settings(AMIV, AMIW),
list(handler(AMIV, AMIW)),
gleam@erlang@process:subject(telega@internal@registry:registry_message(chat_instance_message(AMIV, AMIW)))}.
-opaque bot_message() :: {cancel_conversation_bot_message, binary()} |
{handle_update_bot_message,
telega@update:update(),
gleam@erlang@process:subject(boolean())}.
-opaque chat_instance_message(AMIX, AMIY) :: {handle_new_chat_instance_message,
telega@update:update(),
list(handler(AMIX, AMIY)),
gleam@erlang@process:subject(boolean())} |
{wait_handler_chat_instance_message,
handler(AMIX, AMIY),
gleam@option:option(handler(AMIX, AMIY)),
gleam@option:option(integer())}.
-type continuation(AMIZ, AMJA) :: {continuation,
handler(AMIZ, AMJA),
gleam@option:option(handler(AMIZ, AMJA)),
gleam@option:option(gleam@time@timestamp:timestamp())}.
-type chat_instance(AMJB, AMJC) :: {chat_instance,
binary(),
AMJB,
telega@internal@config:config(),
fun((context(AMJB, AMJC), AMJC) -> {ok, nil} | {error, AMJC}),
session_settings(AMJB, AMJC),
gleam@erlang@process:subject(chat_instance_message(AMJB, AMJC)),
gleam@option:option(continuation(AMJB, AMJC))}.
-type context(AMJD, AMJE) :: {context,
binary(),
telega@update:update(),
telega@internal@config:config(),
AMJD,
gleam@erlang@process:subject(chat_instance_message(AMJD, AMJE))}.
-type session_settings(AMJF, AMJG) :: {session_settings,
fun((binary(), AMJF) -> {ok, AMJF} | {error, AMJG}),
fun((binary()) -> {ok, gleam@option:option(AMJF)} | {error, AMJG}),
fun(() -> AMJF)}.
-type callback_query_filter() :: {callback_query_filter, gleam@regexp:regexp()}.
-type hears() :: {hear_text, binary()} |
{hear_texts, list(binary())} |
{hear_regex, gleam@regexp:regexp()} |
{hear_regexes, list(gleam@regexp:regexp())}.
-type handler(AMJH, AMJI) :: {handle_all,
fun((context(AMJH, AMJI), telega@update:update()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_command,
binary(),
fun((context(AMJH, AMJI), telega@update:command()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_commands,
list(binary()),
fun((context(AMJH, AMJI), telega@update:command()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_text,
fun((context(AMJH, AMJI), binary()) -> {ok, context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_hears,
hears(),
fun((context(AMJH, AMJI), binary()) -> {ok, context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_message,
fun((context(AMJH, AMJI), telega@model:message()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_voice,
fun((context(AMJH, AMJI), telega@model:voice()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_audio,
fun((context(AMJH, AMJI), telega@model:audio()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_video,
fun((context(AMJH, AMJI), telega@model:video()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_photos,
fun((context(AMJH, AMJI), list(telega@model:photo_size())) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_web_app_data,
fun((context(AMJH, AMJI), telega@model:web_app_data()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_callback_query,
callback_query_filter(),
fun((context(AMJH, AMJI), binary(), binary()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})} |
{handle_chat_member,
fun((context(AMJH, AMJI), telega@model:chat_member_updated()) -> {ok,
context(AMJH, AMJI)} |
{error, AMJI})}.
-file("src/telega/bot.gleam", 90).
?DOC(" Stops waiting for any handler for specific key (chat_id)\n").
-spec cancel_conversation(bot(any(), any()), binary()) -> nil.
cancel_conversation(Bot, Key) ->
gleam@otp@actor:send(
erlang:element(2, Bot),
{cancel_conversation_bot_message, Key}
).
-file("src/telega/bot.gleam", 229).
-spec handle_update_chat_instance(
gleam@erlang@process:subject(chat_instance_message(AMLB, AMLC)),
telega@update:update(),
list(handler(AMLB, AMLC)),
gleam@erlang@process:subject(boolean())
) -> {ok, nil} | {error, any()}.
handle_update_chat_instance(Chat_subject, Update, Handlers, Reply_with) ->
_pipe = gleam@otp@actor:send(
Chat_subject,
{handle_new_chat_instance_message, Update, Handlers, Reply_with}
),
{ok, _pipe}.
-file("src/telega/bot.gleam", 406).
-spec new_context(chat_instance(AMMH, AMMI), telega@update:update()) -> context(AMMH, AMMI).
new_context(Chat, Update) ->
{context,
erlang:element(2, Chat),
Update,
erlang:element(4, Chat),
erlang:element(3, Chat),
erlang:element(7, Chat)}.
-file("src/telega/bot.gleam", 434).
-spec next_session(context(AMTU, AMTV), AMTU) -> {ok, context(AMTU, AMTV)} |
{error, any()}.
next_session(Ctx, Session) ->
{ok,
begin
_record = Ctx,
{context,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
Session,
erlang:element(6, _record)}
end}.
-file("src/telega/bot.gleam", 438).
-spec build_session_key(telega@update:update()) -> binary().
build_session_key(Update) ->
<<<<(erlang:integer_to_binary(erlang:element(3, Update)))/binary, ":"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(2, Update)))/binary>>.
-file("src/telega/bot.gleam", 442).
-spec get_session(session_settings(AMMS, AMMT), telega@update:update()) -> {ok,
gleam@option:option(AMMS)} |
{error, AMMT}.
get_session(Session_settings, Update) ->
_pipe = Update,
_pipe@1 = build_session_key(_pipe),
(erlang:element(3, Session_settings))(_pipe@1).
-file("src/telega/bot.gleam", 453).
?DOC(false).
-spec handle_update(
gleam@erlang@process:subject(bot_message()),
telega@update:update()
) -> {ok, boolean()} | {error, telega@error:telega_error()}.
handle_update(Bot_subject, Update) ->
_pipe = gleam@erlang@process:try_call_forever(
Bot_subject,
fun(_capture) -> {handle_update_bot_message, Update, _capture} end
),
gleam@result:map_error(
_pipe,
fun(E) -> {bot_handle_update_error, gleam@string:inspect(E)} end
).
-file("src/telega/bot.gleam", 471).
-spec check_hears(binary(), hears()) -> boolean().
check_hears(Text, Hear) ->
case Hear of
{hear_text, Str} ->
Text =:= Str;
{hear_texts, Strings} ->
gleam@list:contains(Strings, Text);
{hear_regex, Re} ->
gleam@regexp:check(Re, Text);
{hear_regexes, Regexes} ->
gleam@list:any(
Regexes,
fun(_capture) -> gleam@regexp:check(_capture, Text) end
)
end.
-file("src/telega/bot.gleam", 554).
-spec extract_update_handlers(list(handler(AMVG, AMVH)), telega@update:update()) -> list(handler(AMVG, AMVH)).
extract_update_handlers(Handlers, Update) ->
gleam@list:filter(Handlers, fun(Handler) -> case {Handler, Update} of
{{handle_all, _}, _} ->
true;
{{handle_text, _}, {text_update, _, _, _, _, _}} ->
true;
{{handle_hears, Hears, _}, {text_update, _, _, Text, _, _}} ->
check_hears(Text, Hears);
{{handle_command, Command, _},
{command_update, _, _, Update_command, _, _}} ->
erlang:element(3, Update_command) =:= Command;
{{handle_callback_query, Filter, _},
{callback_query_update, _, _, Query, _}} ->
case erlang:element(7, Query) of
{some, Data} ->
gleam@regexp:check(erlang:element(2, Filter), Data);
none ->
false
end;
{_, _} ->
false
end end).
-file("src/telega/bot.gleam", 584).
?DOC(
" Pass any handler to start waiting\n"
"\n"
" `or` - calls if there are any other updates\n"
" `timeout` - the conversation will be canceled after this timeout\n"
).
-spec wait_handler(
context(AMNH, AMNI),
handler(AMNH, AMNI),
gleam@option:option(handler(AMNH, AMNI)),
gleam@option:option(integer())
) -> {ok, context(AMNH, AMNI)} | {error, any()}.
wait_handler(Ctx, Handler, Handle_else, Timeout) ->
gleam@erlang@process:send(
erlang:element(6, Ctx),
{wait_handler_chat_instance_message, Handler, Handle_else, Timeout}
),
{ok, Ctx}.
-file("src/telega/bot.gleam", 597).
-spec do_handle(
context(AMXM, AMXN),
telega@update:update(),
handler(AMXM, AMXN)
) -> gleam@option:option({ok, context(AMXM, AMXN)} | {error, AMXN}).
do_handle(Context, Update, Handler) ->
case {Handler, Update} of
{{handle_all, Handler@1}, _} ->
_pipe = Context,
_pipe@1 = Handler@1(_pipe, Update),
{some, _pipe@1};
{{handle_text, Handler@2}, {text_update, _, _, Text, _, _}} ->
_pipe@2 = Context,
_pipe@3 = Handler@2(_pipe@2, Text),
{some, _pipe@3};
{{handle_hears, _, Handler@3}, {text_update, _, _, Text@1, _, _}} ->
_pipe@4 = Context,
_pipe@5 = Handler@3(_pipe@4, Text@1),
{some, _pipe@5};
{{handle_command, _, Handler@4},
{command_update, _, _, Update_command, _, _}} ->
_pipe@6 = Context,
_pipe@7 = Handler@4(_pipe@6, Update_command),
{some, _pipe@7};
{{handle_commands, _, Handler@5},
{command_update, _, _, Update_command@1, _, _}} ->
_pipe@8 = Context,
_pipe@9 = Handler@5(_pipe@8, Update_command@1),
{some, _pipe@9};
{{handle_callback_query, _, Handler@6},
{callback_query_update, _, _, Query, _}} ->
gleam@option:map(
erlang:element(7, Query),
fun(Data) ->
Handler@6(Context, Data, erlang:element(2, Query))
end
);
{{handle_message, Handler@7}, {message_update, _, _, Message, _}} ->
_pipe@10 = Context,
_pipe@11 = Handler@7(_pipe@10, Message),
{some, _pipe@11};
{{handle_chat_member, Handler@8},
{chat_member_update, _, _, Chat_member_updated, _}} ->
_pipe@12 = Context,
_pipe@13 = Handler@8(_pipe@12, Chat_member_updated),
{some, _pipe@13};
{{handle_voice, Handler@9}, {voice_update, _, _, Voice, _, _}} ->
_pipe@14 = Context,
_pipe@15 = Handler@9(_pipe@14, Voice),
{some, _pipe@15};
{{handle_audio, Handler@10}, {audio_update, _, _, Audio, _, _}} ->
_pipe@16 = Context,
_pipe@17 = Handler@10(_pipe@16, Audio),
{some, _pipe@17};
{{handle_video, Handler@11}, {video_update, _, _, Video, _, _}} ->
_pipe@18 = Context,
_pipe@19 = Handler@11(_pipe@18, Video),
{some, _pipe@19};
{{handle_web_app_data, Handler@12},
{web_app_update, _, _, Web_app_data, _, _}} ->
_pipe@20 = Context,
_pipe@21 = Handler@12(_pipe@20, Web_app_data),
{some, _pipe@21};
{_, _} ->
none
end.
-file("src/telega/bot.gleam", 333).
-spec do_handle_continuation(
context(AMLZ, AMMA),
continuation(AMLZ, AMMA),
telega@update:update(),
gleam@erlang@process:subject(boolean()),
chat_instance(AMLZ, AMMA)
) -> gleam@otp@actor:next(any(), chat_instance(AMLZ, AMMA)).
do_handle_continuation(Context, Continuation, Update, Reply_with, Chat) ->
case do_handle(Context, Update, erlang:element(2, Continuation)) of
{some, {ok, {context, _, _, _, New_session, _}}} ->
gleam@otp@actor:send(Reply_with, true),
gleam@otp@actor:continue(
begin
_record = Chat,
{chat_instance,
erlang:element(2, _record),
New_session,
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
none}
end
);
{some, {error, E}} ->
case (erlang:element(5, Chat))(Context, E) of
{ok, _} ->
gleam@otp@actor:send(Reply_with, false),
gleam@otp@actor:continue(Chat);
{error, E@1} ->
telega@internal@log:error_d(
<<"Error in catch handler: "/utf8>>,
E@1
),
{stop, normal}
end;
none ->
case erlang:element(3, Continuation) of
{some, Handler} ->
case do_handle(Context, Update, Handler) of
{some, {ok, {context, _, _, _, New_session@1, _}}} ->
gleam@otp@actor:send(Reply_with, true),
gleam@otp@actor:continue(
begin
_record@1 = Chat,
{chat_instance,
erlang:element(2, _record@1),
New_session@1,
erlang:element(4, _record@1),
erlang:element(5, _record@1),
erlang:element(6, _record@1),
erlang:element(7, _record@1),
erlang:element(8, _record@1)}
end
);
{some, {error, E@2}} ->
case (erlang:element(5, Chat))(Context, E@2) of
{ok, _} ->
gleam@otp@actor:send(Reply_with, false),
gleam@otp@actor:continue(Chat);
{error, E@3} ->
telega@internal@log:error_d(
<<"Error in catch else handler: "/utf8>>,
E@3
),
{stop, normal}
end;
none ->
gleam@otp@actor:send(Reply_with, false),
gleam@otp@actor:continue(Chat)
end;
none ->
gleam@otp@actor:send(Reply_with, false),
gleam@otp@actor:continue(Chat)
end
end.
-file("src/telega/bot.gleam", 629).
-spec loop_handlers(
chat_instance(AMNT, AMNU),
context(ANBV, AMNU),
any(),
telega@update:update(),
list(handler(ANBV, AMNU))
) -> {ok, AMNT} | {error, AMNU}.
loop_handlers(Chat, Context, Config, Update, Handlers) ->
case Handlers of
[Handler | Rest] ->
case do_handle(Context, Update, Handler) of
{some, {ok, {context, _, _, _, New_session, _}}} ->
loop_handlers(
Chat,
begin
_record = Context,
{context,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
New_session,
erlang:element(6, _record)}
end,
Config,
Update,
Rest
);
{some, {error, E}} ->
{error, E};
none ->
loop_handlers(Chat, Context, Config, Update, Rest)
end;
[] ->
(erlang:element(2, erlang:element(6, Chat)))(
erlang:element(2, Chat),
erlang:element(3, Chat)
)
end.
-file("src/telega/bot.gleam", 268).
-spec do_handle_new_chat_instance_message(
context(AMLO, AMLP),
chat_instance(AMLO, AMLP),
telega@update:update(),
list(handler(AMLO, AMLP)),
gleam@erlang@process:subject(boolean())
) -> gleam@otp@actor:next(any(), chat_instance(AMLO, AMLP)).
do_handle_new_chat_instance_message(Context, Chat, Update, Handlers, Reply_with) ->
case erlang:element(8, Chat) of
{some, Continuation} ->
case erlang:element(4, Continuation) of
{some, Ttl} ->
case gleam@time@timestamp:compare(
Ttl,
gleam@time@timestamp:system_time()
) of
lt ->
do_handle_new_chat_instance_message(
Context,
begin
_record = Chat,
{chat_instance,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
none}
end,
Update,
Handlers,
Reply_with
);
_ ->
do_handle_continuation(
Context,
Continuation,
Update,
Reply_with,
Chat
)
end;
none ->
do_handle_continuation(
Context,
Continuation,
Update,
Reply_with,
Chat
)
end;
none ->
case loop_handlers(
Chat,
Context,
erlang:element(4, Chat),
Update,
Handlers
) of
{ok, New_session} ->
gleam@otp@actor:send(Reply_with, true),
gleam@otp@actor:continue(
begin
_record@1 = Chat,
{chat_instance,
erlang:element(2, _record@1),
New_session,
erlang:element(4, _record@1),
erlang:element(5, _record@1),
erlang:element(6, _record@1),
erlang:element(7, _record@1),
erlang:element(8, _record@1)}
end
);
{error, E} ->
case (erlang:element(5, Chat))(Context, E) of
{ok, _} ->
gleam@otp@actor:send(Reply_with, false),
gleam@otp@actor:continue(Chat);
{error, E@1} ->
telega@internal@log:error_d(
<<"Error in catch handler: "/utf8>>,
E@1
),
{stop, normal}
end
end
end.
-file("src/telega/bot.gleam", 242).
-spec loop_chat_instance(
chat_instance_message(AMLJ, AMLK),
chat_instance(AMLJ, AMLK)
) -> gleam@otp@actor:next(any(), chat_instance(AMLJ, AMLK)).
loop_chat_instance(Message, Chat) ->
case Message of
{handle_new_chat_instance_message, Update, Handlers, Reply_with} ->
do_handle_new_chat_instance_message(
new_context(Chat, Update),
Chat,
Update,
Handlers,
Reply_with
);
{wait_handler_chat_instance_message, Handler, Handle_else, Timeout} ->
gleam@otp@actor:continue(
begin
_record = Chat,
{chat_instance,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
{some,
{continuation,
Handler,
Handle_else,
begin
gleam@option:map(
Timeout,
fun(Timeout@1) ->
_pipe = gleam@time@timestamp:system_time(
),
gleam@time@timestamp:add(
_pipe,
gleam@time@duration:seconds(
Timeout@1
)
)
end
)
end}}}
end
)
end.
-file("src/telega/bot.gleam", 189).
-spec start_chat_instance(
binary(),
telega@internal@config:config(),
session_settings(AMKS, AMKT),
fun((context(AMKS, AMKT), AMKT) -> {ok, nil} | {error, AMKT})
) -> {ok, gleam@erlang@process:subject(chat_instance_message(AMKS, AMKT))} |
{error, telega@error:telega_error()}.
start_chat_instance(Key, Config, Session_settings, Catch_handler) ->
_pipe@1 = gleam@otp@actor:start_spec(
{spec,
fun() ->
Self = gleam@erlang@process:new_subject(),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Self,
fun gleam@function:identity/1
)
end,
Session@1 = case (erlang:element(3, Session_settings))(Key) of
{ok, {some, Session}} ->
Session;
{ok, none} ->
(erlang:element(4, Session_settings))();
{error, Error} ->
erlang:error(#{gleam_error => panic,
message => (<<"Failed to get session: "/utf8,
(gleam@string:inspect(Error))/binary>>),
module => <<"telega/bot"/utf8>>,
function => <<"start_chat_instance"/utf8>>,
line => 205})
end,
Chat_instance = {chat_instance,
Key,
Session@1,
Config,
Catch_handler,
Session_settings,
Self,
none},
{ready, Chat_instance, Selector}
end,
250,
fun loop_chat_instance/2}
),
gleam@result:map_error(
_pipe@1,
fun(Error@1) ->
{chat_instance_start_error, gleam@string:inspect(Error@1)}
end
).
-file("src/telega/bot.gleam", 112).
-spec handle_update_bot_message(
bot(any(), any()),
telega@update:update(),
gleam@erlang@process:subject(boolean())
) -> {ok, nil} | {error, telega@error:telega_error()}.
handle_update_bot_message(Bot, Update, Reply_with) ->
Key = build_session_key(Update),
case telega@internal@registry:get(erlang:element(8, Bot), Key) of
{some, Chat_subject} ->
Handlers = extract_update_handlers(erlang:element(7, Bot), Update),
handle_update_chat_instance(
Chat_subject,
Update,
Handlers,
Reply_with
);
none ->
gleam@result:'try'(
start_chat_instance(
Key,
erlang:element(3, Bot),
erlang:element(6, Bot),
erlang:element(5, Bot)
),
fun(Chat_subject@1) ->
telega@internal@registry:register(
erlang:element(8, Bot),
Key,
Chat_subject@1
),
Handlers@1 = extract_update_handlers(
erlang:element(7, Bot),
Update
),
handle_update_chat_instance(
Chat_subject@1,
Update,
Handlers@1,
Reply_with
)
end
)
end.
-file("src/telega/bot.gleam", 94).
-spec bot_loop(bot_message(), bot(ANKA, ANKB)) -> gleam@otp@actor:next(any(), bot(ANKA, ANKB)).
bot_loop(Message, Bot) ->
case Message of
{handle_update_bot_message, Update, Reply_with} ->
case handle_update_bot_message(Bot, Update, Reply_with) of
{ok, _} ->
gleam@otp@actor:continue(Bot);
{error, Error} ->
telega@internal@log:error_d(
<<"Error in handler: "/utf8>>,
Error
),
{stop, normal}
end;
{cancel_conversation_bot_message, Key} ->
telega@internal@registry:unregister(erlang:element(8, Bot), Key),
gleam@otp@actor:continue(Bot)
end.
-file("src/telega/bot.gleam", 57).
-spec start(
gleam@erlang@process:subject(telega@internal@registry:registry_message(chat_instance_message(ANLD, ANLE))),
telega@internal@config:config(),
telega@model:user(),
list(handler(ANLD, ANLE)),
session_settings(ANLD, ANLE),
fun((context(ANLD, ANLE), ANLE) -> {ok, nil} | {error, ANLE})
) -> {ok, gleam@erlang@process:subject(bot_message())} |
{error, telega@error:telega_error()}.
start(
Registry_subject,
Config,
Bot_info,
Handlers,
Session_settings,
Catch_handler
) ->
_pipe@1 = gleam@otp@actor:start_spec(
{spec,
fun() ->
Self = gleam@erlang@process:new_subject(),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Self,
fun gleam@function:identity/1
)
end,
Bot = {bot,
Self,
Config,
Bot_info,
Catch_handler,
Session_settings,
Handlers,
Registry_subject},
{ready, Bot, Selector}
end,
500,
fun bot_loop/2}
),
gleam@result:map_error(
_pipe@1,
fun(Err) -> {bot_start_error, gleam@string:inspect(Err)} end
).