Skip to main content

src/telega_mist.erl

-module(telega_mist).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/telega_mist.gleam").
-export([handle_bot_with_limit/4, handle_bot/3]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

-file("src/telega_mist.gleam", 105).
-spec empty_response(integer()) -> gleam@http@response:response(mist:response_data()).
empty_response(Status) ->
    _pipe = gleam@http@response:new(Status),
    gleam@http@response:set_body(_pipe, {bytes, gleam@bytes_tree:new()}).

-file("src/telega_mist.gleam", 110).
-spec is_secret_token_valid(
    telega:telega(any(), any(), any()),
    gleam@http@request:request(any())
) -> boolean().
is_secret_token_valid(Telega, Req) ->
    Secret_header_value = begin
        _pipe = gleam@http@request:get_header(
            Req,
            <<"x-telegram-bot-api-secret-token"/utf8>>
        ),
        gleam@result:unwrap(_pipe, <<""/utf8>>)
    end,
    telega:is_secret_token_valid(Telega, Secret_header_value).

-file("src/telega_mist.gleam", 121).
-spec is_bot_request(
    telega:telega(any(), any(), any()),
    gleam@http@request:request(mist@internal@http:connection())
) -> boolean().
is_bot_request(Telega, Req) ->
    Path = begin
        _pipe = gleam@http@request:path_segments(Req),
        gleam@string:join(_pipe, <<"/"/utf8>>)
    end,
    telega:is_webhook_path(Telega, Path).

-file("src/telega_mist.gleam", 65).
?DOC(" Same as `handle_bot`, but lets you set the maximum request body size in bytes.\n").
-spec handle_bot_with_limit(
    telega:telega(any(), any(), any()),
    gleam@http@request:request(mist@internal@http:connection()),
    integer(),
    fun(() -> gleam@http@response:response(mist:response_data()))
) -> gleam@http@response:response(mist:response_data()).
handle_bot_with_limit(Telega, Req, Max_body_limit, Handler) ->
    gleam@bool:lazy_guard(
        not is_bot_request(Telega, Req),
        Handler,
        fun() ->
            gleam@bool:lazy_guard(
                not is_secret_token_valid(Telega, Req),
                fun() -> empty_response(401) end,
                fun() ->
                    gleam@bool:lazy_guard(
                        telega:is_draining(Telega),
                        fun() -> empty_response(503) end,
                        fun() -> case mist:read_body(Req, Max_body_limit) of
                                {ok, Req@1} ->
                                    case gleam@json:parse_bits(
                                        erlang:element(4, Req@1),
                                        {decoder,
                                            fun gleam@dynamic@decode:decode_dynamic/1}
                                    ) of
                                        {ok, Json} ->
                                            proc_lib:spawn_link(
                                                fun() ->
                                                    case telega@update:decode_raw(
                                                        Json
                                                    ) of
                                                        {ok, Message} ->
                                                            telega:handle_update(
                                                                Telega,
                                                                Message
                                                            ),
                                                            nil;

                                                        {error, E} ->
                                                            erlang:error(
                                                                #{gleam_error => panic,
                                                                    message => (<<"Failed to decode update"/utf8,
                                                                        (telega@error:to_string(
                                                                            E
                                                                        ))/binary>>),
                                                                    file => <<?FILEPATH/utf8>>,
                                                                    module => <<"telega_mist"/utf8>>,
                                                                    function => <<"handle_bot_with_limit"/utf8>>,
                                                                    line => 94}
                                                            )
                                                    end
                                                end
                                            ),
                                            empty_response(200);

                                        {error, _} ->
                                            empty_response(400)
                                    end;

                                {error, _} ->
                                    empty_response(400)
                            end end
                    )
                end
            )
        end
    ).

-file("src/telega_mist.gleam", 51).
?DOC(
    " A handler to process incoming requests from the Telegram API directly on top\n"
    " of [mist](https://hexdocs.pm/mist/), without wisp — for minimalistic\n"
    " deployments.\n"
    "\n"
    " It checks the webhook path, validates the secret token, decodes the incoming\n"
    " update, and dispatches it to the bot in a separate process so the `200 OK`\n"
    " response is returned immediately (Telegram waits for the response before\n"
    " sending the next update).\n"
    "\n"
    " ```gleam\n"
    " import gleam/http/request.{type Request}\n"
    " import gleam/http/response.{type Response}\n"
    " import mist.{type Connection, type ResponseData}\n"
    " import telega.{type Telega}\n"
    " import telega_mist\n"
    "\n"
    " fn handle_request(\n"
    "   req: Request(Connection),\n"
    "   bot: Telega(session, error, dependencies),\n"
    " ) -> Response(ResponseData) {\n"
    "   use <- telega_mist.handle_bot(telega: bot, req:)\n"
    "\n"
    "   // Your other routes here...\n"
    "   response.new(404) |> response.set_body(mist.Bytes(bytes_tree.new()))\n"
    " }\n"
    " ```\n"
).
-spec handle_bot(
    telega:telega(any(), any(), any()),
    gleam@http@request:request(mist@internal@http:connection()),
    fun(() -> gleam@http@response:response(mist:response_data()))
) -> gleam@http@response:response(mist:response_data()).
handle_bot(Telega, Req, Handler) ->
    handle_bot_with_limit(Telega, Req, 4000000, Handler).