Current section

Files

Jump to
telega src telega@bot.erl
Raw

src/telega@bot.erl

-module(telega@bot).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([cancel_conversation/2, wait_handler/2, next_session/2, get_session/2, update_to_string/1, handle_update/2, start/6]).
-export_type([bot/2, bot_message/0, chat_instance_message/2, chat_instance/2, context/2, handler/2, callback_query_filter/0, hears/0, session_settings/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(ANYE, ANYF) :: {bot,
gleam@erlang@process:subject(bot_message()),
telega@internal@config:config(),
telega@model:user(),
fun((context(ANYE, ANYF), ANYF) -> {ok, nil} | {error, ANYF}),
session_settings(ANYE, ANYF),
list(handler(ANYE, ANYF)),
gleam@erlang@process:subject(telega@internal@registry:registry_message(chat_instance_message(ANYE, ANYF)))}.
-opaque bot_message() :: {cancel_conversation_bot_message, binary()} |
{handle_update_bot_message,
telega@update:update(),
gleam@erlang@process:subject(boolean())}.
-opaque chat_instance_message(ANYG, ANYH) :: {handle_new_chat_instance_message,
telega@update:update(),
list(handler(ANYG, ANYH)),
gleam@erlang@process:subject(boolean())} |
{wait_handler_chat_instance_message, handler(ANYG, ANYH)}.
-type chat_instance(ANYI, ANYJ) :: {chat_instance,
binary(),
ANYI,
telega@internal@config:config(),
fun((context(ANYI, ANYJ), ANYJ) -> {ok, nil} | {error, ANYJ}),
session_settings(ANYI, ANYJ),
gleam@erlang@process:subject(chat_instance_message(ANYI, ANYJ)),
gleam@option:option(handler(ANYI, ANYJ))}.
-type context(ANYK, ANYL) :: {context,
binary(),
telega@update:update(),
telega@internal@config:config(),
ANYK,
gleam@erlang@process:subject(chat_instance_message(ANYK, ANYL))}.
-type handler(ANYM, ANYN) :: {handle_all,
fun((context(ANYM, ANYN)) -> {ok, context(ANYM, ANYN)} | {error, ANYN})} |
{handle_command,
binary(),
fun((context(ANYM, ANYN), telega@update:command()) -> {ok,
context(ANYM, ANYN)} |
{error, ANYN})} |
{handle_commands,
list(binary()),
fun((context(ANYM, ANYN), telega@update:command()) -> {ok,
context(ANYM, ANYN)} |
{error, ANYN})} |
{handle_text,
fun((context(ANYM, ANYN), binary()) -> {ok, context(ANYM, ANYN)} |
{error, ANYN})} |
{handle_hears,
hears(),
fun((context(ANYM, ANYN), binary()) -> {ok, context(ANYM, ANYN)} |
{error, ANYN})} |
{handle_callback_query,
callback_query_filter(),
fun((context(ANYM, ANYN), binary(), binary()) -> {ok,
context(ANYM, ANYN)} |
{error, ANYN})}.
-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 session_settings(ANYO, ANYP) :: {session_settings,
fun((binary(), ANYO) -> {ok, ANYO} | {error, ANYP}),
fun((binary()) -> {ok, gleam@option:option(ANYO)} | {error, ANYP}),
fun(() -> ANYO)}.
-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", 212).
-spec handle_update_chat_instance(
gleam@erlang@process:subject(chat_instance_message(AOAI, AOAJ)),
telega@update:update(),
list(handler(AOAI, AOAJ)),
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", 304).
-spec new_context(chat_instance(AOAV, AOAW), telega@update:update()) -> context(AOAV, AOAW).
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", 382).
-spec wait_handler(context(AOBF, AOBG), handler(AOBF, AOBG)) -> {ok,
context(AOBF, AOBG)} |
{error, any()}.
wait_handler(Ctx, Handler) ->
gleam@erlang@process:send(
erlang:element(6, Ctx),
{wait_handler_chat_instance_message, Handler}
),
{ok, Ctx}.
-file("src/telega/bot.gleam", 387).
-spec do_handle(
context(AOKZ, AOLA),
telega@update:update(),
handler(AOKZ, AOLA)
) -> gleam@option:option({ok, context(AOKZ, AOLA)} | {error, AOLA}).
do_handle(Context, Update, Handler) ->
case {Handler, Update} of
{{handle_all, Handler@1}, _} ->
_pipe = Context,
_pipe@1 = Handler@1(_pipe),
{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
);
{_, _} ->
none
end.
-file("src/telega/bot.gleam", 407).
-spec loop_handlers(
chat_instance(AOBP, AOBQ),
context(AOLN, AOBQ),
any(),
telega@update:update(),
list(handler(AOLN, AOBQ))
) -> {ok, AOBP} | {error, AOBQ}.
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", 225).
-spec loop_chat_instance(
chat_instance_message(AOAQ, AOAR),
chat_instance(AOAQ, AOAR)
) -> gleam@otp@actor:next(any(), chat_instance(AOAQ, AOAR)).
loop_chat_instance(Message, Chat) ->
case Message of
{handle_new_chat_instance_message, Update, Handlers, Reply_with} ->
Context = new_context(Chat, Update),
case erlang:element(8, Chat) of
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 = 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),
erlang:element(8, _record)}
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;
{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),
none}
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
),
{stop, normal}
end;
none ->
gleam@otp@actor:send(Reply_with, true),
gleam@otp@actor:continue(Chat)
end
end;
{wait_handler_chat_instance_message, Handler@1} ->
gleam@otp@actor:continue(
begin
_record@2 = Chat,
{chat_instance,
erlang:element(2, _record@2),
erlang:element(3, _record@2),
erlang:element(4, _record@2),
erlang:element(5, _record@2),
erlang:element(6, _record@2),
erlang:element(7, _record@2),
{some, Handler@1}}
end
)
end.
-file("src/telega/bot.gleam", 445).
-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", 356).
-spec extract_update_handlers(list(handler(AORU, AORV)), telega@update:update()) -> list(handler(AORU, AORV)).
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", 469).
-spec next_session(context(AOSD, AOSE), AOSD) -> {ok, context(AOSD, AOSE)} |
{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", 473).
-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", 477).
-spec get_session(session_settings(AOCE, AOCF), telega@update:update()) -> {ok,
gleam@option:option(AOCE)} |
{error, AOCF}.
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", 488).
-spec update_to_string(telega@update:update()) -> binary().
update_to_string(Update) ->
case Update of
{command_update, _, Chat_id, Command, _, _} ->
<<<<<<"command \""/utf8, (erlang:element(3, Command))/binary>>/binary,
"\" from "/utf8>>/binary,
(erlang:integer_to_binary(Chat_id))/binary>>;
{text_update, _, Chat_id@1, Text, _, _} ->
<<<<<<"text \""/utf8, Text/binary>>/binary, "\" from "/utf8>>/binary,
(erlang:integer_to_binary(Chat_id@1))/binary>>;
{callback_query_update, From_id, _, Query, _} ->
<<<<<<"callback query "/utf8,
(gleam@option:unwrap(
erlang:element(7, Query),
<<"no data"/utf8>>
))/binary>>/binary,
" from "/utf8>>/binary,
(erlang:integer_to_binary(From_id))/binary>>
end.
-file("src/telega/bot.gleam", 504).
?DOC(false).
-spec handle_update(
gleam@erlang@process:subject(bot_message()),
telega@update:update()
) -> {ok, boolean()} | {error, gleam@erlang@process:call_error(any())}.
handle_update(Bot_subject, Update) ->
gleam@erlang@process:try_call_forever(
Bot_subject,
fun(_capture) -> {handle_update_bot_message, Update, _capture} end
).
-file("src/telega/bot.gleam", 172).
-spec start_chat_instance(
binary(),
telega@internal@config:config(),
session_settings(ANZZ, AOAA),
fun((context(ANZZ, AOAA), AOAA) -> {ok, nil} | {error, AOAA})
) -> {ok, gleam@erlang@process:subject(chat_instance_message(ANZZ, AOAA))} |
{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 => 188})
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", 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),
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", 89).
-spec bot_loop(bot_message(), bot(AOVT, AOVU)) -> gleam@otp@actor:next(any(), bot(AOVT, AOVU)).
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", 52).
-spec start(
gleam@erlang@process:subject(telega@internal@registry:registry_message(chat_instance_message(AOWW, AOWX))),
telega@internal@config:config(),
telega@model:user(),
list(handler(AOWW, AOWX)),
session_settings(AOWW, AOWX),
fun((context(AOWW, AOWX), AOWX) -> {ok, nil} | {error, AOWX})
) -> {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
).