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([set_webhook/1, wait_handler/2, start_registry/4]).
-export_type([registry/1, registry_message/0, session_settings/1, context/1, bot_instanse_message/1, bot_instanse/1, hears/0, handler/1, callback_query_filter/0]).
-type registry(UCC) :: {registry,
gleam@dict:dict(binary(), gleam@erlang@process:subject(bot_instanse_message(UCC))),
telega@internal@config:config(),
session_settings(UCC),
list(handler(UCC)),
gleam@erlang@process:subject(registry_message()),
gleam@erlang@process:subject(gleam@erlang@process:subject(bot_instanse_message(UCC)))}.
-type registry_message() :: {handle_bot_registry_message,
telega@update:update()}.
-type session_settings(UCD) :: {session_settings,
fun((binary(), UCD) -> {ok, UCD} | {error, binary()}),
fun((binary()) -> {ok, UCD} | {error, binary()})}.
-type context(UCE) :: {context,
binary(),
telega@update:update(),
telega@internal@config:config(),
UCE,
gleam@erlang@process:subject(bot_instanse_message(UCE))}.
-type bot_instanse_message(UCF) :: bot_instanse_message_ok |
{bot_instanse_message_new,
gleam@erlang@process:subject(bot_instanse_message(UCF)),
telega@update:update()} |
{bot_instanse_message_wait_handler, handler(UCF)}.
-type bot_instanse(UCG) :: {bot_instanse,
binary(),
UCG,
telega@internal@config:config(),
list(handler(UCG)),
session_settings(UCG),
gleam@option:option(handler(UCG)),
gleam@erlang@process:subject(bot_instanse_message(UCG))}.
-type hears() :: {hear_text, binary()} |
{hear_texts, list(binary())} |
{hear_regex, gleam@regex:regex()} |
{hear_regexes, list(gleam@regex:regex())}.
-type handler(UCH) :: {handle_all,
fun((context(UCH)) -> {ok, UCH} | {error, binary()})} |
{handle_command,
binary(),
fun((context(UCH), telega@update:command()) -> {ok, UCH} |
{error, binary()})} |
{handle_commands,
list(binary()),
fun((context(UCH), telega@update:command()) -> {ok, UCH} |
{error, binary()})} |
{handle_text,
fun((context(UCH), binary()) -> {ok, UCH} | {error, binary()})} |
{handle_hears,
hears(),
fun((context(UCH), binary()) -> {ok, UCH} | {error, binary()})} |
{handle_callback_query,
callback_query_filter(),
fun((context(UCH), binary(), binary()) -> {ok, UCH} | {error, binary()})}.
-type callback_query_filter() :: {callback_query_filter, gleam@regex:regex()}.
-spec try_send_update(
gleam@erlang@process:subject(bot_instanse_message(UCS)),
telega@update:update()
) -> {ok, bot_instanse_message(UCS)} |
{error, gleam@erlang@process:call_error(bot_instanse_message(UCS))}.
try_send_update(Registry_item, Update) ->
gleam@erlang@process:try_call(
Registry_item,
fun(_capture) -> {bot_instanse_message_new, _capture, Update} end,
1000
).
-spec set_webhook(telega@internal@config:config()) -> {ok, boolean()} |
{error, binary()}.
set_webhook(Config) ->
telega@api:set_webhook(
erlang:element(5, Config),
{set_webhook_parameters,
<<<<(erlang:element(2, Config))/binary, "/"/utf8>>/binary,
(erlang:element(3, Config))/binary>>,
none,
none,
none,
none,
{some, erlang:element(4, Config)}}
).
-spec wait_handler(context(UDK), handler(UDK)) -> {ok, UDK} | {error, binary()}.
wait_handler(Ctx, Handler) ->
gleam@erlang@process:send(
erlang:element(6, Ctx),
{bot_instanse_message_wait_handler, Handler}
),
{ok, erlang:element(5, Ctx)}.
-spec new_context(bot_instanse(UDP), telega@update:update()) -> context(UDP).
new_context(Bot, Update) ->
{context,
erlang:element(2, Bot),
Update,
erlang:element(4, Bot),
erlang:element(3, Bot),
erlang:element(8, Bot)}.
-spec get_session_key(telega@update:update()) -> {ok, binary()} |
{error, binary()}.
get_session_key(Update) ->
case Update of
{command_update, Chat_id, _, _} ->
{ok, gleam@int:to_string(Chat_id)};
{text_update, Chat_id@1, _, _} ->
{ok, gleam@int:to_string(Chat_id@1)};
{callback_query_update, From_id, _} ->
{ok, gleam@int:to_string(From_id)};
{unknown_update, _} ->
{error,
<<"Unknown update type don't allow to get session key"/utf8>>}
end.
-spec get_session(session_settings(UDU), telega@update:update()) -> {ok, UDU} |
{error, binary()}.
get_session(Session_settings, Update) ->
gleam@result:'try'(
get_session_key(Update),
fun(Key) -> _pipe = (erlang:element(3, Session_settings))(Key),
gleam@result:map_error(
_pipe,
fun(E) ->
<<"Failed to get session:\n "/utf8,
(gleam@string:inspect(E))/binary>>
end
) end
).
-spec hears_check(binary(), hears()) -> boolean().
hears_check(Text, Hear) ->
case Hear of
{hear_text, Str} ->
Text =:= Str;
{hear_texts, Strs} ->
gleam@list:contains(Strs, Text);
{hear_regex, Re} ->
gleam@regex:check(Re, Text);
{hear_regexes, Regexes} ->
gleam@list:any(
Regexes,
fun(_capture) -> gleam@regex:check(_capture, Text) end
)
end.
-spec do_handle(bot_instanse(UEI), telega@update:update(), handler(UEI)) -> gleam@option:option({ok,
UEI} |
{error, binary()}).
do_handle(Bot, Update, Handler) ->
case {Handler, Update} of
{{handle_all, Handle}, _} ->
{some, Handle(new_context(Bot, Update))};
{{handle_text, Handle@1}, {text_update, _, Text, _}} ->
{some, Handle@1(new_context(Bot, Update), Text)};
{{handle_hears, Hear, Handle@2}, {text_update, _, Text@1, _}} ->
case hears_check(Text@1, Hear) of
true ->
{some, Handle@2(new_context(Bot, Update), Text@1)};
false ->
none
end;
{{handle_command, Command, Handle@3},
{command_update, _, Update_command, _}} ->
case erlang:element(3, Update_command) =:= Command of
true ->
{some, Handle@3(new_context(Bot, Update), Update_command)};
false ->
none
end;
{{handle_commands, Commands, Handle@4},
{command_update, _, Update_command@1, _}} ->
case gleam@list:contains(
Commands,
erlang:element(3, Update_command@1)
) of
true ->
{some, Handle@4(new_context(Bot, Update), Update_command@1)};
false ->
none
end;
{{handle_callback_query, Filter, Handle@5},
{callback_query_update, _, Raw}} ->
case erlang:element(7, Raw) of
{some, Data} ->
case gleam@regex:check(erlang:element(2, Filter), Data) of
true ->
{some,
Handle@5(
new_context(Bot, Update),
Data,
erlang:element(2, Raw)
)};
false ->
none
end;
none ->
none
end;
{_, _} ->
none
end.
-spec loop_handlers(
bot_instanse(UEO),
telega@update:update(),
list(handler(UEO))
) -> {ok, UEO} | {error, binary()}.
loop_handlers(Bot, Update, Handlers) ->
case Handlers of
[Handler | Rest] ->
case do_handle(Bot, Update, Handler) of
{some, {ok, New_session}} ->
loop_handlers(
erlang:setelement(3, Bot, New_session),
Update,
Rest
);
{some, {error, E}} ->
{error,
<<<<<<"Failed to handle message "/utf8,
(gleam@string:inspect(Update))/binary>>/binary,
":\n"/utf8>>/binary,
E/binary>>};
none ->
loop_handlers(Bot, Update, Rest)
end;
[] ->
(erlang:element(2, erlang:element(6, Bot)))(
erlang:element(2, Bot),
erlang:element(3, Bot)
)
end.
-spec handle_bot_instanse_message(bot_instanse_message(UEE), bot_instanse(UEE)) -> gleam@otp@actor:next(any(), bot_instanse(UEE)).
handle_bot_instanse_message(Message, Bot) ->
case Message of
{bot_instanse_message_new, Client, Message@1} ->
case erlang:element(7, Bot) of
{some, Handler} ->
case do_handle(Bot, Message@1, Handler) of
{some, {ok, New_session}} ->
gleam@otp@actor:send(
Client,
bot_instanse_message_ok
),
gleam@otp@actor:continue(
erlang:setelement(
7,
erlang:setelement(3, Bot, New_session),
none
)
);
{some, {error, E}} ->
telega@log:error(
<<"Failed to handle update:\n"/utf8, E/binary>>
),
{stop, normal};
none ->
gleam@otp@actor:send(
Client,
bot_instanse_message_ok
),
gleam@otp@actor:continue(Bot)
end;
none ->
case loop_handlers(Bot, Message@1, erlang:element(5, Bot)) of
{ok, New_session@1} ->
gleam@otp@actor:send(
Client,
bot_instanse_message_ok
),
gleam@otp@actor:continue(
erlang:setelement(3, Bot, New_session@1)
);
{error, E@1} ->
telega@log:error(
<<"Failed to handle update:\n"/utf8,
E@1/binary>>
),
{stop, normal}
end
end;
{bot_instanse_message_wait_handler, Handler@1} ->
gleam@otp@actor:continue(
erlang:setelement(7, Bot, {some, Handler@1})
);
bot_instanse_message_ok ->
gleam@otp@actor:continue(Bot)
end.
-spec start_bot_instanse(
registry(UDY),
telega@update:update(),
binary(),
gleam@erlang@process:subject(gleam@erlang@process:subject(bot_instanse_message(UDY)))
) -> {ok, gleam@erlang@process:subject(bot_instanse_message(UDY))} |
{error, gleam@otp@actor:start_error()}.
start_bot_instanse(Registry, Update, Session_key, Parent_subject) ->
gleam@otp@actor:start_spec(
{spec,
fun() ->
Actor_subj = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Parent_subject, Actor_subj),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Actor_subj,
fun gleam@function:identity/1
)
end,
case get_session(erlang:element(4, Registry), Update) of
{ok, Session} ->
_pipe@1 = {bot_instanse,
Session_key,
Session,
erlang:element(3, Registry),
erlang:element(5, Registry),
erlang:element(4, Registry),
none,
Actor_subj},
{ready, _pipe@1, Selector};
{error, E} ->
{failed,
<<"Failed to init bot instanse:\n"/utf8, E/binary>>}
end
end,
10000,
fun handle_bot_instanse_message/2}
).
-spec add_bot_instance(registry(UCY), binary(), telega@update:update()) -> gleam@otp@actor:next(any(), registry(UCY)).
add_bot_instance(Registry, Session_key, Update) ->
Registry_actor = gleam@otp@supervisor:supervisor(
fun(_) ->
start_bot_instanse(
Registry,
Update,
Session_key,
erlang:element(7, Registry)
)
end
),
_assert_subject = gleam@otp@supervisor:start(
fun(_capture) -> gleam@otp@supervisor:add(_capture, Registry_actor) end
),
{ok, _} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"telega/bot"/utf8>>,
function => <<"add_bot_instance"/utf8>>,
line => 96})
end,
Bot_subject_result = begin
_pipe = gleam@erlang@process:'receive'(
erlang:element(7, Registry),
1000
),
gleam@result:map_error(
_pipe,
fun(E) ->
<<"Failed to start bot instanse:\n"/utf8,
(gleam@string:inspect(E))/binary>>
end
)
end,
case Bot_subject_result of
{ok, Bot_subject} ->
case try_send_update(Bot_subject, Update) of
{ok, _} ->
gleam@otp@actor:continue(
erlang:setelement(
2,
Registry,
gleam@dict:insert(
erlang:element(2, Registry),
Session_key,
Bot_subject
)
)
);
{error, E@1} ->
telega@log:error(
<<"Failed to send message to bot instanse: "/utf8,
(gleam@string:inspect(E@1))/binary>>
),
gleam@otp@actor:continue(Registry)
end;
{error, E@2} ->
telega@log:error(E@2),
gleam@otp@actor:continue(Registry)
end.
-spec handle_registry_message(registry_message(), registry(UCV)) -> gleam@otp@actor:next(any(), registry(UCV)).
handle_registry_message(Message, Registry) ->
case Message of
{handle_bot_registry_message, Message@1} ->
case get_session_key(Message@1) of
{ok, Session_key} ->
case gleam@dict:get(
erlang:element(2, Registry),
Session_key
) of
{ok, Registry_item} ->
case try_send_update(Registry_item, Message@1) of
{ok, _} ->
gleam@otp@actor:continue(Registry);
{error, _} ->
add_bot_instance(
Registry,
Session_key,
Message@1
)
end;
{error, nil} ->
add_bot_instance(Registry, Session_key, Message@1)
end;
{error, E} ->
telega@log:error(
<<"Failed to get session key: "/utf8,
(gleam@string:inspect(E))/binary>>
),
gleam@otp@actor:continue(Registry)
end
end.
-spec start_registry(
telega@internal@config:config(),
list(handler(UDD)),
session_settings(UDD),
gleam@erlang@process:subject(gleam@erlang@process:subject(registry_message()))
) -> {ok, gleam@erlang@process:subject(registry_message())} |
{error, gleam@otp@actor:start_error()}.
start_registry(Config, Handlers, Session_settings, Root_subject) ->
gleam@otp@actor:start_spec(
{spec,
fun() ->
Registry_subject = gleam@erlang@process:new_subject(),
Bot_instances_subject = gleam@erlang@process:new_subject(),
gleam@erlang@process:send(Root_subject, Registry_subject),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Registry_subject,
fun gleam@function:identity/1
)
end,
_pipe@1 = {registry,
gleam@dict:new(),
Config,
Session_settings,
Handlers,
Registry_subject,
Bot_instances_subject},
{ready, _pipe@1, Selector}
end,
10000,
fun handle_registry_message/2}
).