-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()).