-module(telega@bot). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/telega/bot.gleam"). -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(ALGV, ALGW) :: {bot, gleam@erlang@process:subject(bot_message()), telega@internal@config:config(), telega@model@types:user(), fun((context(ALGV, ALGW), ALGW) -> {ok, nil} | {error, ALGW}), session_settings(ALGV, ALGW), list(handler(ALGV, ALGW)), telega@internal@registry:registry(chat_instance_message(ALGV, ALGW))}. -opaque bot_message() :: {cancel_conversation_bot_message, binary()} | {handle_update_bot_message, telega@update:update(), gleam@erlang@process:subject(boolean())}. -opaque chat_instance_message(ALGX, ALGY) :: {handle_new_chat_instance_message, telega@update:update(), list(handler(ALGX, ALGY)), gleam@erlang@process:subject(boolean())} | {wait_handler_chat_instance_message, handler(ALGX, ALGY), gleam@option:option(handler(ALGX, ALGY)), gleam@option:option(integer())}. -type continuation(ALGZ, ALHA) :: {continuation, handler(ALGZ, ALHA), gleam@option:option(handler(ALGZ, ALHA)), gleam@option:option(gleam@time@timestamp:timestamp())}. -type chat_instance(ALHB, ALHC) :: {chat_instance, binary(), ALHB, telega@internal@config:config(), fun((context(ALHB, ALHC), ALHC) -> {ok, nil} | {error, ALHC}), session_settings(ALHB, ALHC), gleam@erlang@process:subject(chat_instance_message(ALHB, ALHC)), gleam@option:option(continuation(ALHB, ALHC))}. -type context(ALHD, ALHE) :: {context, binary(), telega@update:update(), telega@internal@config:config(), ALHD, gleam@erlang@process:subject(chat_instance_message(ALHD, ALHE)), gleam@option:option(gleam@time@timestamp:timestamp()), gleam@option:option(binary())}. -type session_settings(ALHF, ALHG) :: {session_settings, fun((binary(), ALHF) -> {ok, ALHF} | {error, ALHG}), fun((binary()) -> {ok, gleam@option:option(ALHF)} | {error, ALHG}), fun(() -> ALHF)}. -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(ALHH, ALHI) :: {handle_all, fun((context(ALHH, ALHI), telega@update:update()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_command, binary(), fun((context(ALHH, ALHI), telega@update:command()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_commands, list(binary()), fun((context(ALHH, ALHI), telega@update:command()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_text, fun((context(ALHH, ALHI), binary()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_hears, hears(), fun((context(ALHH, ALHI), binary()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_message, fun((context(ALHH, ALHI), telega@model@types:message()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_voice, fun((context(ALHH, ALHI), telega@model@types:voice()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_audio, fun((context(ALHH, ALHI), telega@model@types:audio()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_video, fun((context(ALHH, ALHI), telega@model@types:video()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_photos, fun((context(ALHH, ALHI), list(telega@model@types:photo_size())) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_web_app_data, fun((context(ALHH, ALHI), telega@model@types:web_app_data()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_callback_query, callback_query_filter(), fun((context(ALHH, ALHI), binary(), binary()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})} | {handle_chat_member, fun((context(ALHH, ALHI), telega@model@types:chat_member_updated()) -> {ok, context(ALHH, ALHI)} | {error, ALHI})}. -file("src/telega/bot.gleam", 85). ?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", 427). -spec new_context(chat_instance(ALKF, ALKG), telega@update:update()) -> context(ALKF, ALKG). new_context(Chat, Update) -> {context, erlang:element(2, Chat), Update, erlang:element(4, Chat), erlang:element(3, Chat), erlang:element(7, Chat), none, none}. -file("src/telega/bot.gleam", 457). -spec next_session(context(ALKM, ALKN), ALKM) -> {ok, context(ALKM, ALKN)} | {error, ALKN}. next_session(Ctx, Session) -> {ok, {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), Session, erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx)}}. -file("src/telega/bot.gleam", 464). -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", 468). -spec get_session(session_settings(ALKV, ALKW), telega@update:update()) -> {ok, gleam@option:option(ALKV)} | {error, ALKW}. 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", 479). ?DOC(false). -spec handle_update( gleam@erlang@process:subject(bot_message()), telega@update:update() ) -> boolean(). handle_update(Bot_subject, Update) -> gleam@erlang@process:call_forever( Bot_subject, fun(_capture) -> {handle_update_bot_message, Update, _capture} end ). -file("src/telega/bot.gleam", 496). -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", 579). -spec extract_update_handlers(list(handler(ALTL, ALTM)), telega@update:update()) -> list(handler(ALTL, ALTM)). 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", 602). ?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(ALLK, ALLL), handler(ALLK, ALLL), gleam@option:option(handler(ALLK, ALLL)), gleam@option:option(integer()) ) -> {ok, context(ALLK, ALLL)} | {error, ALLL}. wait_handler(Ctx, Handler, Handle_else, Timeout) -> gleam@otp@actor:send( erlang:element(6, Ctx), {wait_handler_chat_instance_message, Handler, Handle_else, Timeout} ), {ok, Ctx}. -file("src/telega/bot.gleam", 615). -spec do_handle( context(ALVR, ALVS), telega@update:update(), handler(ALVR, ALVS) ) -> gleam@option:option({ok, context(ALVR, ALVS)} | {error, ALVS}). 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", 307). -spec do_handle_continuation( context(ALJV, ALJW), continuation(ALJV, ALJW), telega@update:update(), gleam@erlang@process:subject(boolean()), chat_instance(ALJV, ALJW) ) -> gleam@otp@actor:next(chat_instance(ALJV, ALJW), any()). do_handle_continuation(Context, Continuation, Update, Reply_with, Chat) -> case do_handle(Context, Update, erlang:element(2, Continuation)) of {some, {ok, {context, _, _, _, New_session, _, _, _}}} -> case (erlang:element(2, erlang:element(6, Chat)))( erlang:element(2, Chat), New_session ) of {ok, Persisted_session} -> gleam@otp@actor:send(Reply_with, true), gleam@otp@actor:continue( {chat_instance, erlang:element(2, Chat), Persisted_session, erlang:element(4, Chat), erlang:element(5, Chat), erlang:element(6, Chat), erlang:element(7, Chat), none} ); {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 session persistence after continuation: "/utf8>>, E@1 ), gleam@otp@actor:stop() end 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 handler: "/utf8>>, E@3 ), gleam@otp@actor:stop() end; none -> case erlang:element(3, Continuation) of {some, Handler} -> case do_handle(Context, Update, Handler) of {some, {ok, {context, _, _, _, New_session@1, _, _, _}}} -> case (erlang:element(2, erlang:element(6, Chat)))( erlang:element(2, Chat), New_session@1 ) of {ok, Persisted_session@1} -> gleam@otp@actor:send(Reply_with, true), gleam@otp@actor:continue( {chat_instance, erlang:element(2, Chat), Persisted_session@1, erlang:element(4, Chat), erlang:element(5, Chat), erlang:element(6, Chat), erlang:element(7, Chat), erlang:element(8, Chat)} ); {error, E@4} -> case (erlang:element(5, Chat))(Context, E@4) of {ok, _} -> gleam@otp@actor:send( Reply_with, false ), gleam@otp@actor:continue(Chat); {error, E@5} -> telega@internal@log:error_d( <<"Error in session persistence after handle_else: "/utf8>>, E@5 ), gleam@otp@actor:stop() end end; {some, {error, E@6}} -> case (erlang:element(5, Chat))(Context, E@6) of {ok, _} -> gleam@otp@actor:send(Reply_with, false), gleam@otp@actor:continue(Chat); {error, E@7} -> telega@internal@log:error_d( <<"Error in catch else handler: "/utf8>>, E@7 ), gleam@otp@actor:stop() 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", 647). -spec loop_handlers( chat_instance(ALMC, ALMD), context(ALMC, ALMD), any(), telega@update:update(), list(handler(ALMC, ALMD)) ) -> {ok, ALMC} | {error, ALMD}. 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, {context, erlang:element(2, Context), erlang:element(3, Context), erlang:element(4, Context), New_session, erlang:element(6, Context), erlang:element(7, Context), erlang:element(8, Context)}, 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(5, Context) ) end. -file("src/telega/bot.gleam", 242). -spec do_handle_new_chat_instance_message( context(ALJL, ALJM), chat_instance(ALJL, ALJM), telega@update:update(), list(handler(ALJL, ALJM)), gleam@erlang@process:subject(boolean()) ) -> gleam@otp@actor:next(chat_instance(ALJL, ALJM), any()). 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, {chat_instance, erlang:element(2, Chat), erlang:element(3, Chat), erlang:element(4, Chat), erlang:element(5, Chat), erlang:element(6, Chat), erlang:element(7, Chat), none}, 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( {chat_instance, erlang:element(2, Chat), New_session, erlang:element(4, Chat), erlang:element(5, Chat), erlang:element(6, Chat), erlang:element(7, Chat), erlang:element(8, Chat)} ); {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 ), gleam@otp@actor:stop() end end end. -file("src/telega/bot.gleam", 218). -spec loop_chat_instance( chat_instance(ALJF, ALJG), chat_instance_message(ALJF, ALJG) ) -> gleam@otp@actor:next(chat_instance(ALJF, ALJG), any()). loop_chat_instance(Chat, Message) -> 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} -> _pipe@2 = {chat_instance, erlang:element(2, Chat), erlang:element(3, Chat), erlang:element(4, Chat), erlang:element(5, Chat), erlang:element(6, Chat), erlang:element(7, Chat), begin _pipe@1 = {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}, {some, _pipe@1} end}, gleam@otp@actor:continue(_pipe@2) end. -file("src/telega/bot.gleam", 181). -spec start_chat_instance( binary(), telega@internal@config:config(), session_settings(ALIY, ALIZ), fun((context(ALIY, ALIZ), ALIZ) -> {ok, nil} | {error, ALIZ}) ) -> {ok, gleam@erlang@process:subject(chat_instance_message(ALIY, ALIZ))} | {error, telega@error:telega_error()}. start_chat_instance(Key, Config, Session_settings, Catch_handler) -> 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>>), file => <>, module => <<"telega/bot"/utf8>>, function => <<"start_chat_instance"/utf8>>, line => 191}) end, gleam@result:'try'( begin _pipe@2 = gleam@otp@actor:new_with_initialiser( 10, fun(Subject) -> Chat_instance = {chat_instance, Key, Session@1, Config, Catch_handler, Session_settings, Subject, none}, _pipe = gleam@otp@actor:initialised(Chat_instance), _pipe@1 = gleam@otp@actor:returning(_pipe, Subject), {ok, _pipe@1} end ), _pipe@3 = gleam@otp@actor:on_message( _pipe@2, fun loop_chat_instance/2 ), _pipe@4 = gleam@otp@actor:start(_pipe@3), gleam@result:map_error( _pipe@4, fun(Field@0) -> {chat_instance_start_error, Field@0} end ) end, fun(Started) -> {ok, erlang:element(3, Started)} end ). -file("src/telega/bot.gleam", 107). -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), Handlers = extract_update_handlers(erlang:element(7, Bot), Update), case telega@internal@registry:get(erlang:element(8, Bot), Key) of {some, Chat_subject} -> _pipe = gleam@otp@actor:send( Chat_subject, {handle_new_chat_instance_message, Update, Handlers, Reply_with} ), {ok, _pipe}; none -> gleam@result:'try'( start_chat_instance( Key, erlang:element(3, Bot), erlang:element(6, Bot), erlang:element(5, Bot) ), fun(Subject) -> telega@internal@registry:register( erlang:element(8, Bot), Key, Subject ), _pipe@1 = gleam@otp@actor:send( Subject, {handle_new_chat_instance_message, Update, Handlers, Reply_with} ), {ok, _pipe@1} end ) end. -file("src/telega/bot.gleam", 89). -spec bot_loop(bot(AMKW, AMKX), bot_message()) -> gleam@otp@actor:next(bot(AMKW, AMKX), any()). bot_loop(Bot, Message) -> 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 ), gleam@otp@actor:stop() 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( telega@internal@registry:registry(chat_instance_message(ALHV, ALHW)), telega@internal@config:config(), telega@model@types:user(), list(handler(ALHV, ALHW)), session_settings(ALHV, ALHW), fun((context(ALHV, ALHW), ALHW) -> {ok, nil} | {error, ALHW}) ) -> {ok, gleam@erlang@process:subject(bot_message())} | {error, telega@error:telega_error()}. start(Registry, Config, Bot_info, Handlers, Session_settings, Catch_handler) -> Self = gleam@erlang@process:new_subject(), Bot = {bot, Self, Config, Bot_info, Catch_handler, Session_settings, Handlers, Registry}, _pipe = gleam@otp@actor:new(Bot), _pipe@1 = gleam@otp@actor:on_message(_pipe, fun bot_loop/2), _pipe@2 = gleam@otp@actor:start(_pipe@1), _pipe@3 = gleam@result:map( _pipe@2, fun(Started) -> erlang:element(3, Started) end ), gleam@result:map_error( _pipe@3, fun(Field@0) -> {bot_start_error, Field@0} end ).