-module(telega@bot). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([set_webhook/1, wait_handler/2, start_registry/5]). -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(UEO) :: {registry, gleam@dict:dict(binary(), gleam@erlang@process:subject(bot_instanse_message(UEO))), telega@internal@config:config(), telega@model:user(), session_settings(UEO), list(handler(UEO)), gleam@erlang@process:subject(registry_message()), gleam@erlang@process:subject(gleam@erlang@process:subject(bot_instanse_message(UEO)))}. -type registry_message() :: {handle_bot_registry_message, telega@update:update()}. -type session_settings(UEP) :: {session_settings, fun((binary(), UEP) -> {ok, UEP} | {error, binary()}), fun((binary()) -> {ok, UEP} | {error, binary()})}. -type context(UEQ) :: {context, binary(), telega@update:update(), telega@model:user(), telega@internal@config:config(), UEQ, gleam@erlang@process:subject(bot_instanse_message(UEQ))}. -type bot_instanse_message(UER) :: bot_instanse_message_ok | {bot_instanse_message_new, gleam@erlang@process:subject(bot_instanse_message(UER)), telega@update:update()} | {bot_instanse_message_wait_handler, handler(UER)}. -type bot_instanse(UES) :: {bot_instanse, binary(), UES, telega@internal@config:config(), list(handler(UES)), telega@model:user(), session_settings(UES), gleam@option:option(handler(UES)), gleam@erlang@process:subject(bot_instanse_message(UES))}. -type hears() :: {hear_text, binary()} | {hear_texts, list(binary())} | {hear_regex, gleam@regex:regex()} | {hear_regexes, list(gleam@regex:regex())}. -type handler(UET) :: {handle_all, fun((context(UET)) -> {ok, UET} | {error, binary()})} | {handle_command, binary(), fun((context(UET), telega@update:command()) -> {ok, UET} | {error, binary()})} | {handle_commands, list(binary()), fun((context(UET), telega@update:command()) -> {ok, UET} | {error, binary()})} | {handle_text, fun((context(UET), binary()) -> {ok, UET} | {error, binary()})} | {handle_hears, hears(), fun((context(UET), binary()) -> {ok, UET} | {error, binary()})} | {handle_callback_query, callback_query_filter(), fun((context(UET), binary(), binary()) -> {ok, UET} | {error, binary()})}. -type callback_query_filter() :: {callback_query_filter, gleam@regex:regex()}. -spec try_send_update( gleam@erlang@process:subject(bot_instanse_message(UFE)), telega@update:update() ) -> {ok, bot_instanse_message(UFE)} | {error, gleam@erlang@process:call_error(bot_instanse_message(UFE))}. 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(UFW), handler(UFW)) -> {ok, UFW} | {error, binary()}. wait_handler(Ctx, Handler) -> gleam@erlang@process:send( erlang:element(7, Ctx), {bot_instanse_message_wait_handler, Handler} ), {ok, erlang:element(6, Ctx)}. -spec new_context(bot_instanse(UGB), telega@update:update()) -> context(UGB). new_context(Bot, Update) -> {context, erlang:element(2, Bot), Update, erlang:element(6, Bot), erlang:element(4, Bot), erlang:element(3, Bot), erlang:element(9, 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(UGG), telega@update:update()) -> {ok, UGG} | {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(UGU), telega@update:update(), handler(UGU)) -> gleam@option:option({ok, UGU} | {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(UHA), telega@update:update(), list(handler(UHA)) ) -> {ok, UHA} | {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(7, Bot)))( erlang:element(2, Bot), erlang:element(3, Bot) ) end. -spec handle_bot_instanse_message(bot_instanse_message(UGQ), bot_instanse(UGQ)) -> gleam@otp@actor:next(any(), bot_instanse(UGQ)). handle_bot_instanse_message(Message, Bot) -> case Message of {bot_instanse_message_new, Client, Message@1} -> case erlang:element(8, 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( 8, 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(8, Bot, {some, Handler@1}) ); bot_instanse_message_ok -> gleam@otp@actor:continue(Bot) end. -spec start_bot_instanse( registry(UGK), telega@update:update(), binary(), gleam@erlang@process:subject(gleam@erlang@process:subject(bot_instanse_message(UGK))) ) -> {ok, gleam@erlang@process:subject(bot_instanse_message(UGK))} | {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(5, Registry), Update) of {ok, Session} -> _pipe@1 = {bot_instanse, Session_key, Session, erlang:element(3, Registry), erlang:element(6, Registry), erlang:element(4, Registry), erlang:element(5, 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(UFK), binary(), telega@update:update()) -> gleam@otp@actor:next(any(), registry(UFK)). add_bot_instance(Registry, Session_key, Update) -> Registry_actor = gleam@otp@supervisor:supervisor( fun(_) -> start_bot_instanse( Registry, Update, Session_key, erlang:element(8, 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 => 97}) end, Bot_subject_result = begin _pipe = gleam@erlang@process:'receive'( erlang:element(8, 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(UFH)) -> gleam@otp@actor:next(any(), registry(UFH)). 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(UFP)), session_settings(UFP), gleam@erlang@process:subject(gleam@erlang@process:subject(registry_message())), telega@model:user() ) -> {ok, gleam@erlang@process:subject(registry_message())} | {error, gleam@otp@actor:start_error()}. start_registry(Config, Handlers, Session_settings, Root_subject, Bot_info) -> 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, Bot_info, Session_settings, Handlers, Registry_subject, Bot_instances_subject}, {ready, _pipe@1, Selector} end, 10000, fun handle_registry_message/2} ).