Current section

Files

Jump to
telega src telega.erl
Raw

src/telega.erl

-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(
<<<<Prefix@1/binary, "Received message: "/utf8>>/binary,
(gleam@string:inspect(erlang:element(3, erlang:element(2, Ctx))))/binary>>
),
_pipe = Handler(),
gleam@result:map_error(
_pipe,
fun(E) ->
telega@log:error(
<<<<Prefix@1/binary, "Handler failed: "/utf8>>/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, Command, 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 => 364})
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).