-module(telega). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([is_webhook_path/2, is_secret_token_valid/2, new/4, handle_all/2, handle_command/3, handle_commands/3, handle_text/2, log_context/3, with_session_settings/4, handle_update/2, init/1, init_nil_session/1]). -export_type([telega/1, telega_builder/1, session_settings/1, command/0, handler/1, registry_item/0, registry/1, registry_message/0, bot_instanse_message/0, bot_instanse/1]). -opaque telega(PPY) :: {telega, telega@bot:bot(), list(handler(PPY)), gleam@option:option(session_settings(PPY)), gleam@option:option(gleam@erlang@process:subject(registry_message()))}. -opaque telega_builder(PPZ) :: {telega_builder, telega(PPZ)}. -opaque session_settings(PQA) :: {session_settings, fun((binary(), PQA) -> {ok, PQA} | {error, binary()}), fun((binary()) -> {ok, PQA} | {error, binary()}), fun((telega@message:message()) -> binary())}. -type command() :: {command, binary(), binary(), gleam@option:option(binary())}. -opaque handler(PQB) :: {handle_all, fun((telega@bot:context(PQB)) -> {ok, PQB} | {error, binary()})} | {handle_command, binary(), fun((telega@bot:context(PQB), command()) -> {ok, PQB} | {error, binary()})} | {handle_commands, list(binary()), fun((telega@bot:context(PQB), command()) -> {ok, PQB} | {error, binary()})} | {handle_text, fun((telega@bot:context(PQB), binary()) -> {ok, PQB} | {error, binary()})}. -type registry_item() :: {registry_item, gleam@erlang@process:subject(bot_instanse_message()), gleam@erlang@process:subject(gleam@erlang@process:subject(bot_instanse_message()))}. -type registry(PQC) :: {registry, gleam@dict:dict(binary(), registry_item()), telega@bot:bot(), session_settings(PQC), list(handler(PQC))}. -type registry_message() :: {handle_bot_registry_message, telega@message:message()}. -type bot_instanse_message() :: {handle_bot_instanse_message, telega@message:message()}. -type bot_instanse(PQD) :: {bot_instanse, binary(), PQD, telega@bot:bot(), list(handler(PQD)), session_settings(PQD)}. -spec is_webhook_path(telega(any()), binary()) -> boolean(). is_webhook_path(Telega, Path) -> telega@bot:get_webhook_path(erlang:element(2, Telega)) =:= Path. -spec is_secret_token_valid(telega(any()), binary()) -> boolean(). is_secret_token_valid(Telega, Token) -> telega@bot:get_secret_token(erlang:element(2, Telega)) =:= Token. -spec new(binary(), binary(), binary(), gleam@option:option(binary())) -> telega_builder(any()). new(Token, Server_url, Webhook_path, Secret_token) -> {telega_builder, {telega, telega@bot:new(Token, Server_url, Webhook_path, Secret_token), [], none, none}}. -spec handle_all( telega_builder(PQL), fun((telega@bot:context(PQL)) -> {ok, PQL} | {error, binary()}) ) -> telega_builder(PQL). handle_all(Builder, Handler) -> {telega_builder, erlang:setelement( 3, erlang:element(2, Builder), [{handle_all, Handler} | erlang:element(3, erlang:element(2, Builder))] )}. -spec handle_command( telega_builder(PQR), binary(), fun((telega@bot:context(PQR), command()) -> {ok, PQR} | {error, binary()}) ) -> telega_builder(PQR). handle_command(Builder, Command, Handler) -> {telega_builder, erlang:setelement( 3, erlang:element(2, Builder), [{handle_command, Command, Handler} | erlang:element(3, erlang:element(2, Builder))] )}. -spec handle_commands( telega_builder(PQX), list(binary()), fun((telega@bot:context(PQX), command()) -> {ok, PQX} | {error, binary()}) ) -> telega_builder(PQX). handle_commands(Builder, Commands, Handler) -> {telega_builder, erlang:setelement( 3, erlang:element(2, Builder), [{handle_commands, Commands, Handler} | erlang:element(3, erlang:element(2, Builder))] )}. -spec handle_text( telega_builder(PRE), fun((telega@bot:context(PRE), binary()) -> {ok, PRE} | {error, binary()}) ) -> telega_builder(PRE). handle_text(Builder, Handler) -> {telega_builder, erlang:setelement( 3, erlang:element(2, Builder), [{handle_text, Handler} | erlang:element(3, erlang:element(2, Builder))] )}. -spec log_context( telega@bot:context(PRK), binary(), fun(() -> {ok, PRK} | {error, binary()}) ) -> {ok, PRK} | {error, binary()}. log_context(Ctx, Prefix, Handler) -> Prefix@1 = <<<<"["/utf8, Prefix/binary>>/binary, "] "/utf8>>, telega@log:info( <<<>/binary, (gleam@string:inspect(erlang:element(3, erlang:element(2, Ctx))))/binary>> ), _pipe = Handler(), gleam@result:map_error( _pipe, fun(E) -> telega@log:error( <<<>/binary, (gleam@string:inspect(E))/binary>> ), E end ). -spec with_session_settings( telega_builder(PRQ), fun((binary(), PRQ) -> {ok, PRQ} | {error, binary()}), fun((binary()) -> {ok, PRQ} | {error, binary()}), fun((telega@message:message()) -> binary()) ) -> telega_builder(PRQ). with_session_settings(Builder, Persist_session, Get_session, Get_session_key) -> {telega_builder, erlang:setelement( 4, erlang:element(2, Builder), {some, {session_settings, Persist_session, Get_session, Get_session_key}} )}. -spec nil_session_settings(telega_builder(nil)) -> telega_builder(nil). nil_session_settings(Builder) -> {telega_builder, erlang:setelement( 4, erlang:element(2, Builder), {some, {session_settings, fun(_, _) -> {ok, nil} end, fun(_) -> {ok, nil} end, fun(_) -> <<""/utf8>> end}} )}. -spec handle_update(telega(any()), telega@message:message()) -> {ok, nil} | {error, binary()}. handle_update(Telega, Message) -> Registry_subject = gleam@option:to_result( erlang:element(5, Telega), <<"Registry not initialized"/utf8>> ), gleam@result:'try'( Registry_subject, fun(Registry_subject@1) -> {ok, gleam@otp@actor:send( Registry_subject@1, {handle_bot_registry_message, Message} )} end ). -spec try_send_message(registry_item(), telega@message:message()) -> {ok, PZH} | {error, gleam@erlang@process:call_error(PZH)}. try_send_message(Registry_item, Message) -> gleam@erlang@process:try_call( erlang:element(2, Registry_item), fun(_) -> {handle_bot_instanse_message, Message} end, 1000 ). -spec get_session(session_settings(PTB), telega@message:message()) -> {ok, PTB} | {error, binary()}. get_session(Session_settings, Message) -> _pipe = (erlang:element(4, Session_settings))(Message), _pipe@1 = (erlang:element(3, Session_settings))(_pipe), gleam@result:map_error( _pipe@1, fun(E) -> <<"Failed to get session:\n "/utf8, (gleam@string:inspect(E))/binary>> end ). -spec extract_command(telega@message:message()) -> command(). extract_command(Message) -> case erlang:element(17, erlang:element(3, Message)) of none -> {command, <<""/utf8>>, <<""/utf8>>, none}; {some, Text} -> case gleam@string:split(Text, <<" "/utf8>>) of [Command | Payload] -> {command, Text, gleam@string:drop_left(Command, 1), case Payload of [] -> none; [Payload@1 | _] -> {some, Payload@1} end}; [] -> {command, Text, <<""/utf8>>, none} end end. -spec do_bot_handle_update( bot_instanse(PTP), telega@message:message(), list(handler(PTP)) ) -> {ok, PTP} | {error, binary()}. do_bot_handle_update(Bot, Message, Handlers) -> case Handlers of [Handler | Rest] -> Handle_result = case {Handler, erlang:element(2, Message)} of {{handle_all, Handle}, _} -> Handle( {context, Message, erlang:element(4, Bot), erlang:element(3, Bot)} ); {{handle_text, Handle@1}, text_message} -> Handle@1( {context, Message, erlang:element(4, Bot), erlang:element(3, Bot)}, gleam@option:unwrap( erlang:element(17, erlang:element(3, Message)), <<""/utf8>> ) ); {{handle_command, Command, Handle@2}, command_message} -> Message_command = extract_command(Message), case erlang:element(3, Message_command) =:= Command of true -> Handle@2( {context, Message, erlang:element(4, Bot), erlang:element(3, Bot)}, Message_command ); false -> {ok, erlang:element(3, Bot)} end; {{handle_commands, Commands, Handle@3}, command_message} -> Message_command@1 = extract_command(Message), case gleam@list:contains( Commands, erlang:element(3, Message_command@1) ) of true -> Handle@3( {context, Message, erlang:element(4, Bot), erlang:element(3, Bot)}, Message_command@1 ); false -> {ok, erlang:element(3, Bot)} end; {_, _} -> {ok, erlang:element(3, Bot)} end, case Handle_result of {ok, New_session} -> do_bot_handle_update( erlang:setelement(3, Bot, New_session), Message, Rest ); {error, E} -> {error, <<<<<<"Failed to handle message: \n"/utf8, (gleam@string:inspect(Message))/binary>>/binary, "\n"/utf8>>/binary, E/binary>>} 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(), bot_instanse(PTM)) -> gleam@otp@actor:next(any(), bot_instanse(PTM)). handle_bot_instanse_message(Message, Bot) -> case Message of {handle_bot_instanse_message, Message@1} -> case do_bot_handle_update(Bot, Message@1, erlang:element(5, Bot)) of {ok, New_session} -> gleam@otp@actor:continue( erlang:setelement(3, Bot, New_session) ); {error, E} -> telega@log:error( <<"Failed to handle update:\n"/utf8, E/binary>> ), {stop, normal} end end. -spec start_bot_instanse( registry(any()), telega@message:message(), binary(), gleam@erlang@process:subject(gleam@erlang@process:subject(bot_instanse_message())) ) -> {ok, gleam@erlang@process:subject(bot_instanse_message())} | {error, gleam@otp@actor:start_error()}. start_bot_instanse(Registry, Message, Session_key, Parent_subject) -> gleam@otp@actor:start_spec( {spec, fun() -> Registry_subject = gleam@erlang@process:new_subject(), gleam@erlang@process:send(Parent_subject, Registry_subject), Selector = begin _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting( _pipe, Registry_subject, fun gleam@function:identity/1 ) end, case get_session(erlang:element(4, Registry), Message) of {ok, Session} -> _pipe@1 = {bot_instanse, Session_key, Session, erlang:element(3, Registry), erlang:element(5, Registry), erlang:element(4, Registry)}, {ready, _pipe@1, Selector}; {error, E} -> {failed, <<"Failed to init session:\n"/utf8, E/binary>>} end end, 10000, fun handle_bot_instanse_message/2} ). -spec add_bot_instance(registry(PSY), binary(), telega@message:message()) -> gleam@otp@actor:next(any(), registry(PSY)). add_bot_instance(Registry, Session_key, Message) -> Parent_subject = gleam@erlang@process:new_subject(), Registry_actor = gleam@otp@supervisor:supervisor( fun(_) -> start_bot_instanse(Registry, Message, Session_key, Parent_subject) 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"/utf8>>, function => <<"add_bot_instance"/utf8>>, line => 381}) end, Bot_subject_result = begin _pipe = gleam@erlang@process:'receive'(Parent_subject, 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} -> Registry_item = {registry_item, Bot_subject, Parent_subject}, case try_send_message(Registry_item, Message) of {ok, _} -> gleam@otp@actor:continue( erlang:setelement( 2, Registry, gleam@dict:insert( erlang:element(2, Registry), Session_key, Registry_item ) ) ); {error, _} -> gleam@otp@actor:continue(Registry) end; {error, E@1} -> telega@log:error(E@1), gleam@otp@actor:continue(Registry) end. -spec handle_registry_message(registry_message(), registry(PSV)) -> gleam@otp@actor:next(any(), registry(PSV)). handle_registry_message(Message, Registry) -> case Message of {handle_bot_registry_message, Message@1} -> Session_key = (erlang:element(4, erlang:element(4, Registry)))( Message@1 ), case gleam@dict:get(erlang:element(2, Registry), Session_key) of {ok, Registry_item} -> case try_send_message(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 end. -spec start_registry( telega(PSM), session_settings(PSM), 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(Telega, Session_settings, Parent_subject) -> gleam@otp@actor:start_spec( {spec, fun() -> Registry_subject = gleam@erlang@process:new_subject(), gleam@erlang@process:send(Parent_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(), erlang:element(2, Telega), Session_settings, erlang:element(3, Telega)}, {ready, _pipe@1, Selector} end, 10000, fun handle_registry_message/2} ). -spec init(telega_builder(PSD)) -> {ok, telega(PSD)} | {error, binary()}. init(Builder) -> {telega_builder, Telega} = Builder, gleam@result:'try'( telega@api:set_webhook(erlang:element(2, Telega)), fun(Is_ok) -> gleam@bool:guard( not Is_ok, {error, <<"Failed to set webhook"/utf8>>}, fun() -> Session_settings = gleam@option:to_result( erlang:element(4, Telega), <<"Session settings not initialized"/utf8>> ), gleam@result:'try'( Session_settings, fun(Session_settings@1) -> Telega_subject = gleam@erlang@process:new_subject(), Registry_actor = gleam@otp@supervisor:supervisor( fun(_) -> start_registry( Telega, Session_settings@1, Telega_subject ) end ), gleam@result:'try'( begin _pipe = gleam@otp@supervisor:start( fun(_capture) -> gleam@otp@supervisor:add( _capture, Registry_actor ) end ), gleam@result:map_error( _pipe, fun(E) -> <<"Failed to start telega:\n"/utf8, (gleam@string:inspect(E))/binary>> end ) end, fun(_) -> gleam@result:'try'( begin _pipe@1 = gleam@erlang@process:'receive'( Telega_subject, 1000 ), gleam@result:map_error( _pipe@1, fun(E@1) -> <<"Failed to start registry:\n"/utf8, (gleam@string:inspect( E@1 ))/binary>> end ) end, fun(Registry_subject) -> {ok, erlang:setelement( 5, Telega, {some, Registry_subject} )} end ) end ) end ) end ) end ). -spec init_nil_session(telega_builder(nil)) -> {ok, telega(nil)} | {error, binary()}. init_nil_session(Builder) -> _pipe = Builder, _pipe@1 = nil_session_settings(_pipe), init(_pipe@1).