Current section
Files
Jump to
Current section
Files
src/telega@api.erl
-module(telega@api).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([new_api_config/1, set_webhook/2, get_webhook_info/1, delete_webhook/1, delete_webhook_and_drop_updates/1, log_out/1, close/1, send_message/2, set_my_commands/3, delete_my_commands/2, get_my_commands/2, send_dice/2, get_me/1, answer_callback_query/2, edit_message_text/2, forward_message/2, set_chat_menu_button/2, get_file/2]).
-export_type([telegram_api_config/0, telegram_api_request/0, api_response/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" This module provides an interface for interacting with the Telegram Bot API.\n"
" It will be useful if you want to interact with the Telegram Bot API directly, without running a bot.\n"
" But it will be more convenient to use the `reply` module in bot handlers.\n"
).
-type telegram_api_config() :: {telegram_api_config,
binary(),
integer(),
binary()}.
-type telegram_api_request() :: {telegram_api_post_request,
binary(),
binary(),
gleam@option:option(list({binary(), binary()}))} |
{telegram_api_get_request,
binary(),
gleam@option:option(list({binary(), binary()}))}.
-type api_response(AKOH) :: {api_success_response, boolean(), AKOH} |
{api_error_response, boolean(), integer(), binary()}.
-file("src/telega/api.gleam", 40).
-spec new_api_config(binary()) -> telegram_api_config().
new_api_config(Token) ->
{telegram_api_config, Token, 3, <<"https://api.telegram.org"/utf8>>}.
-file("src/telega/api.gleam", 341).
-spec build_url(telegram_api_config(), binary()) -> binary().
build_url(Config, Path) ->
<<<<<<(erlang:element(4, Config))/binary,
(erlang:element(2, Config))/binary>>/binary,
"/"/utf8>>/binary,
Path/binary>>.
-file("src/telega/api.gleam", 354).
-spec new_post_request(
telegram_api_config(),
binary(),
binary(),
gleam@option:option(list({binary(), binary()}))
) -> telegram_api_request().
new_post_request(Config, Path, Body, Query) ->
{telegram_api_post_request, build_url(Config, Path), Body, Query}.
-file("src/telega/api.gleam", 363).
-spec new_get_request(
telegram_api_config(),
binary(),
gleam@option:option(list({binary(), binary()}))
) -> telegram_api_request().
new_get_request(Config, Path, Query) ->
{telegram_api_get_request, build_url(Config, Path), Query}.
-file("src/telega/api.gleam", 371).
-spec set_query(
gleam@http@request:request(binary()),
gleam@option:option(list({binary(), binary()}))
) -> gleam@http@request:request(binary()).
set_query(Api_request, Query) ->
case Query of
none ->
Api_request;
{some, Query@1} ->
gleam@http@request:set_query(Api_request, Query@1)
end.
-file("src/telega/api.gleam", 383).
-spec api_to_request(telegram_api_request()) -> {ok,
gleam@http@request:request(binary())} |
{error, binary()}.
api_to_request(Api_request) ->
_pipe@6 = case Api_request of
{telegram_api_get_request, Url, Query} ->
_pipe = gleam@http@request:to(Url),
_pipe@1 = gleam@result:map(
_pipe,
fun(_capture) ->
gleam@http@request:set_method(_capture, get)
end
),
gleam@result:map(
_pipe@1,
fun(_capture@1) -> set_query(_capture@1, Query) end
);
{telegram_api_post_request, Url@1, Body, Query@1} ->
_pipe@2 = gleam@http@request:to(Url@1),
_pipe@3 = gleam@result:map(
_pipe@2,
fun(_capture@2) ->
gleam@http@request:set_body(_capture@2, Body)
end
),
_pipe@4 = gleam@result:map(
_pipe@3,
fun(_capture@3) ->
gleam@http@request:set_method(_capture@3, post)
end
),
_pipe@5 = gleam@result:map(
_pipe@4,
fun(_capture@4) ->
gleam@http@request:set_header(
_capture@4,
<<"Content-Type"/utf8>>,
<<"application/json"/utf8>>
)
end
),
gleam@result:map(
_pipe@5,
fun(_capture@5) -> set_query(_capture@5, Query@1) end
)
end,
gleam@result:map_error(
_pipe@6,
fun(Error) ->
<<"Failed to convert API request to HTTP request: "/utf8,
(gleam@string:inspect(Error))/binary>>
end
).
-file("src/telega/api.gleam", 453).
-spec response_decoder(gleam@dynamic@decode:decoder(AKTG)) -> gleam@dynamic@decode:decoder(api_response(AKTG)).
response_decoder(Result_decoder) ->
gleam@dynamic@decode:field(
<<"ok"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_bool/1},
fun(Ok) -> case Ok of
true ->
gleam@dynamic@decode:field(
<<"result"/utf8>>,
Result_decoder,
fun(Result) ->
gleam@dynamic@decode:success(
{api_success_response, Ok, Result}
)
end
);
false ->
gleam@dynamic@decode:field(
<<"error_code"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Error_code) ->
gleam@dynamic@decode:field(
<<"description"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Description) ->
gleam@dynamic@decode:success(
{api_error_response,
Ok,
Error_code,
Description}
)
end
)
end
)
end end
).
-file("src/telega/api.gleam", 440).
-spec parse_api_response(
gleam@dynamic:dynamic_(),
gleam@dynamic@decode:decoder(AKTI)
) -> {ok, api_response(AKTI)} | {error, list(gleam@dynamic:decode_error())}.
parse_api_response(Json, Result_decoder) ->
_pipe = gleam@dynamic@decode:run(Json, response_decoder(Result_decoder)),
gleam@result:map_error(
_pipe,
fun(Errors) ->
gleam@list:map(
Errors,
fun(Error) ->
{decode_error,
erlang:element(2, Error),
erlang:element(3, Error),
erlang:element(4, Error)}
end
)
end
).
-file("src/telega/api.gleam", 405).
-spec map_response(
{ok, gleam@http@response:response(binary())} | {error, binary()},
gleam@dynamic@decode:decoder(AKQC)
) -> {ok, AKQC} | {error, binary()}.
map_response(Response, Result_decoder) ->
_pipe = Response,
_pipe@3 = gleam@result:map(
_pipe,
fun(Response@1) ->
_pipe@1 = gleam@json:decode(
erlang:element(4, Response@1),
fun(_capture) ->
parse_api_response(_capture, Result_decoder)
end
),
_pipe@2 = gleam@result:map_error(
_pipe@1,
fun(Error) ->
<<<<<<"Failed to decode response: "/utf8,
(erlang:element(4, Response@1))/binary>>/binary,
" With error: "/utf8>>/binary,
(gleam@string:inspect(Error))/binary>>
end
),
gleam@result:then(_pipe@2, fun(Response@2) -> case Response@2 of
{api_success_response, _, Result} ->
{ok, Result};
{api_error_response, _, Error_code, Description} ->
{error,
<<<<<<"Request failed with code "/utf8,
(erlang:integer_to_binary(
Error_code
))/binary>>/binary,
" :\n"/utf8>>/binary,
Description/binary>>}
end end)
end
),
gleam@result:flatten(_pipe@3).
-file("src/telega/api.gleam", 485).
-spec send_with_retry(gleam@http@request:request(binary()), integer()) -> {ok,
gleam@http@response:response(binary())} |
{error, gleam@dynamic:dynamic_()}.
send_with_retry(Api_request, Retries) ->
Response = gleam@httpc:send(Api_request),
case Retries of
0 ->
gleam@result:map_error(Response, fun gleam_stdlib:identity/1);
_ ->
case Response of
{ok, Response@1} ->
case erlang:element(2, Response@1) of
429 ->
telega@log:warn(
<<"Telegram API throttling, HTTP 429 'Too Many Requests'"/utf8>>
),
gleam_erlang_ffi:sleep(1000),
send_with_retry(Api_request, Retries - 1);
_ ->
{ok, Response@1}
end;
{error, _} ->
gleam_erlang_ffi:sleep(1000),
send_with_retry(Api_request, Retries - 1)
end
end.
-file("src/telega/api.gleam", 470).
-spec fetch(telegram_api_request(), telegram_api_config()) -> {ok,
gleam@http@response:response(binary())} |
{error, binary()}.
fetch(Api_request, Config) ->
gleam@result:'try'(
api_to_request(Api_request),
fun(Api_request@1) ->
_pipe = send_with_retry(Api_request@1, erlang:element(3, Config)),
gleam@result:map_error(
_pipe,
fun(Error) ->
telega@log:info(
<<"Api request failed with error:"/utf8,
(gleam@string:inspect(Error))/binary>>
),
_pipe@1 = gleam@dynamic:string(Error),
gleam@result:unwrap(
_pipe@1,
<<"Failed to send request"/utf8>>
)
end
)
end
).
-file("src/telega/api.gleam", 65).
?DOC(
" Set the webhook URL using [setWebhook](https://core.telegram.org/bots/api#setwebhook) API.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#setwebhook\n"
).
-spec set_webhook(telegram_api_config(), telega@model:set_webhook_parameters()) -> {ok,
boolean()} |
{error, binary()}.
set_webhook(Config, Parameters) ->
Body = telega@model:encode_set_webhook_parameters(Parameters),
_pipe = new_post_request(
Config,
<<"setWebhook"/utf8>>,
gleam@json:to_string(Body),
none
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 84).
?DOC(
" Use this method to get current webhook status.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#getwebhookinfo\n"
).
-spec get_webhook_info(telegram_api_config()) -> {ok,
telega@model:webhook_info()} |
{error, binary()}.
get_webhook_info(Config) ->
_pipe = new_get_request(Config, <<"getWebhookInfo"/utf8>>, none),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:webhook_info_decoder()).
-file("src/telega/api.gleam", 93).
?DOC(
" Use this method to remove webhook integration if you decide to switch back to [getUpdates](https://core.telegram.org/bots/api#getupdates).\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#deletewebhook\n"
).
-spec delete_webhook(telegram_api_config()) -> {ok, boolean()} |
{error, binary()}.
delete_webhook(Config) ->
_pipe = new_get_request(Config, <<"deleteWebhook"/utf8>>, none),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 100).
?DOC(" The same as [delete_webhook](#delete_webhook) but also drops all pending updates.\n").
-spec delete_webhook_and_drop_updates(telegram_api_config()) -> {ok, boolean()} |
{error, binary()}.
delete_webhook_and_drop_updates(Config) ->
_pipe = new_get_request(
Config,
<<"deleteWebhook"/utf8>>,
{some, [{<<"drop_pending_updates"/utf8>>, <<"true"/utf8>>}]}
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 115).
?DOC(
" Use this method to log out from the cloud Bot API server before launching the bot locally.\n"
" You **must** log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates.\n"
" After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#logout\n"
).
-spec log_out(telegram_api_config()) -> {ok, boolean()} | {error, binary()}.
log_out(Config) ->
_pipe = new_get_request(Config, <<"logOut"/utf8>>, none),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 126).
?DOC(
" Use this method to close the bot instance before moving it from one local server to another.\n"
" You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart.\n"
" The method will return error 429 in the first 10 minutes after the bot is launched.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#close\n"
).
-spec close(telegram_api_config()) -> {ok, boolean()} | {error, binary()}.
close(Config) ->
_pipe = new_get_request(Config, <<"close"/utf8>>, none),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 135).
?DOC(
" Use this method to send text messages with additional parameters.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#sendmessage\n"
).
-spec send_message(
telegram_api_config(),
telega@model:send_message_parameters()
) -> {ok, telega@model:message()} | {error, binary()}.
send_message(Config, Parameters) ->
Body_json = telega@model:encode_send_message_parameters(Parameters),
_pipe = new_post_request(
Config,
<<"sendMessage"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:message_decoder()).
-file("src/telega/api.gleam", 154).
?DOC(
" Use this method to change the list of the bot's commands. See [commands documentation](https://core.telegram.org/bots/features#commands) for more details about bot commands.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#setmycommands\n"
).
-spec set_my_commands(
telegram_api_config(),
list(telega@model:bot_command()),
gleam@option:option(telega@model:bot_command_parameters())
) -> {ok, boolean()} | {error, binary()}.
set_my_commands(Config, Commands, Parameters) ->
Parameters@1 = begin
_pipe = gleam@option:unwrap(
Parameters,
telega@model:default_bot_command_parameters()
),
telega@model:encode_bot_command_parameters(_pipe)
end,
Body_json = gleam@json:object(
[{<<"commands"/utf8>>,
gleam@json:array(
Commands,
fun(Command) ->
gleam@json:object(
[{<<"command"/utf8>>,
gleam@json:string(
erlang:element(2, Command)
)},
{<<"description"/utf8>>,
gleam@json:string(
erlang:element(3, Command)
)} |
Parameters@1]
)
end
)}]
),
_pipe@1 = new_post_request(
Config,
<<"setMyCommands"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@2 = fetch(_pipe@1, Config),
map_response(_pipe@2, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 191).
?DOC(
" Use this method to delete the list of the bot's commands for the given scope and user language.\n"
" After deletion, [higher level commands](https://core.telegram.org/bots/api#determining-list-of-commands) will be shown to affected users.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#deletemycommands\n"
).
-spec delete_my_commands(
telegram_api_config(),
gleam@option:option(telega@model:bot_command_parameters())
) -> {ok, boolean()} | {error, binary()}.
delete_my_commands(Config, Parameters) ->
Parameters@1 = begin
_pipe = gleam@option:unwrap(
Parameters,
telega@model:default_bot_command_parameters()
),
telega@model:encode_bot_command_parameters(_pipe)
end,
Body_json = gleam@json:object(Parameters@1),
_pipe@1 = new_post_request(
Config,
<<"deleteMyCommands"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@2 = fetch(_pipe@1, Config),
map_response(_pipe@2, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 214).
?DOC(
" Use this method to get the current list of the bot's commands for the given scope and user language.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#getmycommands\n"
).
-spec get_my_commands(
telegram_api_config(),
gleam@option:option(telega@model:bot_command_parameters())
) -> {ok, telega@model:bot_command()} | {error, binary()}.
get_my_commands(Config, Parameters) ->
Parameters@1 = begin
_pipe = gleam@option:unwrap(
Parameters,
telega@model:default_bot_command_parameters()
),
telega@model:encode_bot_command_parameters(_pipe)
end,
Body_json = gleam@json:object(Parameters@1),
_pipe@1 = new_post_request(
Config,
<<"getMyCommands"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@2 = fetch(_pipe@1, Config),
map_response(_pipe@2, telega@model:bot_command_decoder()).
-file("src/telega/api.gleam", 237).
?DOC(
" Use this method to send an animated emoji that will display a random value.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#senddice\n"
).
-spec send_dice(telegram_api_config(), telega@model:send_dice_parameters()) -> {ok,
telega@model:message()} |
{error, binary()}.
send_dice(Config, Parameters) ->
Body_json = telega@model:encode_send_dice_parameters(Parameters),
_pipe = new_post_request(
Config,
<<"sendDice"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:message_decoder()).
-file("src/telega/api.gleam", 256).
?DOC(
" A simple method for testing your bot's authentication token.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#getme\n"
).
-spec get_me(telegram_api_config()) -> {ok, telega@model:user()} |
{error, binary()}.
get_me(Config) ->
_pipe = new_get_request(Config, <<"getMe"/utf8>>, none),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:user_decoder()).
-file("src/telega/api.gleam", 267).
?DOC(
" Use this method to send answers to callback queries sent from inline keyboards.\n"
" The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.\n"
" On success, _True_ is returned.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#answercallbackquery\n"
).
-spec answer_callback_query(
telegram_api_config(),
telega@model:answer_callback_query_parameters()
) -> {ok, boolean()} | {error, binary()}.
answer_callback_query(Config, Parameters) ->
Body_json = telega@model:encode_answer_callback_query_parameters(Parameters),
_pipe = new_post_request(
Config,
<<"answerCallbackQuery"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 287).
?DOC(
" Use this method to edit text and game messages.\n"
" On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#editmessagetext\n"
).
-spec edit_message_text(
telegram_api_config(),
telega@model:edit_message_text_parameters()
) -> {ok, telega@model:message()} | {error, binary()}.
edit_message_text(Config, Parameters) ->
Body_json = telega@model:encode_edit_message_text_parameters(Parameters),
_pipe = new_post_request(
Config,
<<"editMessageText"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:message_decoder()).
-file("src/telega/api.gleam", 307).
?DOC(
" Use this method to forward messages of any kind. Service messages and messages with protected content can't be forwarded.\n"
" On success, the sent Message is returned.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#forwardmessage\n"
).
-spec forward_message(
telegram_api_config(),
telega@model:forward_message_parameters()
) -> {ok, telega@model:message()} | {error, binary()}.
forward_message(Config, Parameters) ->
Body_json = telega@model:encode_forward_message_parameters(Parameters),
_pipe = new_post_request(
Config,
<<"forwardMessage"/utf8>>,
gleam@json:to_string(Body_json),
none
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:message_decoder()).
-file("src/telega/api.gleam", 326).
?DOC(
" Use this method to change the bot's menu button in a private chat, or the default menu button. Returns True on success.\n"
"\n"
" **Official reference:** https://core.telegram.org/bots/api#setchatmenubutton\n"
).
-spec set_chat_menu_button(
telegram_api_config(),
telega@model:set_chat_menu_button_parameters()
) -> {ok, boolean()} | {error, binary()}.
set_chat_menu_button(Config, Parameters) ->
_pipe@1 = new_post_request(
Config,
<<"setChatMenuButton"/utf8>>,
begin
_pipe = telega@model:encode_set_chat_menu_button_parameters(
Parameters
),
gleam@json:to_string(_pipe)
end,
none
),
_pipe@2 = fetch(_pipe@1, Config),
map_response(_pipe@2, {decoder, fun gleam@dynamic@decode:decode_bool/1}).
-file("src/telega/api.gleam", 345).
-spec get_file(telegram_api_config(), binary()) -> {ok, telega@model:file()} |
{error, binary()}.
get_file(Config, File_id) ->
_pipe = new_get_request(
Config,
<<"getFile"/utf8>>,
{some, [{<<"file_id"/utf8>>, File_id}]}
),
_pipe@1 = fetch(_pipe, Config),
map_response(_pipe@1, telega@model:file_decoder()).