mirror of
https://github.com/tdlight-team/tdlight-telegram-bot-api.git
synced 2024-11-23 12:36:51 +01:00
parent
14bf658b86
commit
100a7cc846
76
README.md
76
README.md
@ -15,6 +15,7 @@ Please note that only TDLight-specific issues are suitable for this repository.
|
||||
- [TDLight features](#tdlight-features)
|
||||
- [Added features](#added-features)
|
||||
- [Modified features](#modified-features)
|
||||
- [User Mode](#user-mode)
|
||||
- [Installation](#installation)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Usage](#usage)
|
||||
@ -126,6 +127,81 @@ In addition, the member list now shows the full bot list (previously only the bo
|
||||
|
||||
The bot will now receive Updates for all received media, even if a destruction timer is set.
|
||||
|
||||
<a name="user-mode"></a>
|
||||
### User Mode
|
||||
|
||||
You can allow user accounts to access the bot api with the command-line option `--allow-users` or set the env variable
|
||||
`TELEGRAM_ALLOW_USERS` to `1` when using docker. User Mode is disabled by default, so only bots can access the api.
|
||||
|
||||
You can now log into the bot api with user accounts to create userbots running on your account.
|
||||
|
||||
Note: Never send your 2fa password over a plain http connection. Make sure https is enabled or use this api locally.
|
||||
|
||||
#### User Authorization Process
|
||||
1. Send a request to `{api_url}/userlogin`
|
||||
|
||||
Parameters:
|
||||
- `phone_number`: `string`. The phone number of your Telegram Account.
|
||||
|
||||
Returns your `user_token` as `string`. You can use this just like a normal bot token on the `/user` endpoint
|
||||
|
||||
2. Send the received code to `{api_url}/user{user_token}/authcode`
|
||||
|
||||
Parameters:
|
||||
- `code`: `int`. The code send to you by Telegram In-App or by SMS
|
||||
|
||||
Will send `{"ok": true, "result": true}` on success.
|
||||
|
||||
3. Optional: Send your 2fa password to `{api_url}/user{user_token}/2fapassword`
|
||||
|
||||
Parameters:
|
||||
- `password`: `string`. Password for 2fa authentication
|
||||
|
||||
Will send `{"ok": true, "result": true}` on success.
|
||||
|
||||
4. Optional: Register the user by calling `{api_url}/user{user_token}/registerUser`.
|
||||
|
||||
User registration is disabled by default. You can enable it with the `--allow-users-registration` command line
|
||||
option or the env variable `TELEGRAM_ALLOW_USERS_REGISTRATION` set to `1` when using docker.
|
||||
|
||||
Parameters:
|
||||
- `first_name`: `string`. First name for the new account.
|
||||
- `last_name`: `string`, optional. Last name for the new account.
|
||||
|
||||
Will send `{"ok": true, "result": true}` on success.
|
||||
|
||||
You are now logged in and can use all methods like in the bot api, just replace the
|
||||
`/bot{bot_token}/` in your urls with `/user{token}/`.
|
||||
|
||||
You only need to authenticate once, the account will stay logged in. You can use the `logOut` method to log out
|
||||
or simply close the session in your account settings.
|
||||
|
||||
Some methods are (obviously) not available as a user. This includes:
|
||||
- `answerCallbackQuery`
|
||||
- `setMyCommands`
|
||||
- `editMessageReplyMarkup`
|
||||
- `uploadStickerFile`
|
||||
- `createNewStickerSet`
|
||||
- `addStickerToSet`
|
||||
- `setStickerPositionInSet`
|
||||
- `deleteStickerFromSet`
|
||||
- `setStickerSetThumb`
|
||||
- `sendInvoice`
|
||||
- `answerShippingQuery`
|
||||
- `answerPreCheckoutQuery`
|
||||
- `setPassportDataErrors`
|
||||
- `sendGame`
|
||||
- `setGameScore`
|
||||
- `getGameHighscores`
|
||||
|
||||
It is also not possible to attach a `reply_markup` to any message.
|
||||
|
||||
Your api wrapper may behave different in
|
||||
some cases, for examples command message-entities are not created in chats that don't contain any
|
||||
bots, so your Command Handler may not detect it.
|
||||
|
||||
It is possible to have multiple user-tokens to multiple client instances on the same bot api server.
|
||||
|
||||
<a name="installation"></a>
|
||||
## Installation
|
||||
|
||||
|
@ -40,6 +40,12 @@ fi
|
||||
if [ -n "$TELEGRAM_NO_FILE_LIMIT" ]; then
|
||||
CUSTOM_ARGS="${CUSTOM_ARGS} --no-file-limit"
|
||||
fi
|
||||
if [ -n "$TELEGRAM_ALLOW_USERS" ]; then
|
||||
CUSTOM_ARGS="${CUSTOM_ARGS} --allow-users"
|
||||
fi
|
||||
if [ -n "$TELEGRAM_ALLOW_USERS_REGISTRATION" ]; then
|
||||
CUSTOM_ARGS="${CUSTOM_ARGS} --allow-users-registration"
|
||||
fi
|
||||
if [ -n "$TELEGRAM_INSECURE" ]; then
|
||||
CUSTOM_ARGS="${CUSTOM_ARGS} --insecure"
|
||||
fi
|
||||
|
2
td
2
td
@ -1 +1 @@
|
||||
Subproject commit b47fab11cd7dddbb2829ef1830007632332b717b
|
||||
Subproject commit 2b92c16998d39e7bfee580defcb0109151e4fb81
|
@ -34,6 +34,21 @@
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#define CHECK_IS_BOT() \
|
||||
if (is_user_) { \
|
||||
return Status::Error(BOT_ONLY_ERROR_CODE, BOT_ONLY_ERROR_DESCRIPTION); \
|
||||
}
|
||||
|
||||
#define CHECK_IS_USER() \
|
||||
if (!is_user_) { \
|
||||
return Status::Error(USER_ONLY_ERROR_CODE, USER_ONLY_ERROR_DESCRIPTION); \
|
||||
}
|
||||
|
||||
#define CHECK_USER_REPLY_MARKUP() \
|
||||
if (reply_markup != nullptr && is_user_) { \
|
||||
return Status::Error(BOT_ONLY_ERROR_CODE, BOT_ONLY_ERROR_DESCRIPTION); \
|
||||
}
|
||||
|
||||
namespace telegram_bot_api {
|
||||
|
||||
using td::Jsonable;
|
||||
@ -124,6 +139,9 @@ void Client::fail_query_with_error(PromisedQueryPtr query, int32 error_code, Sli
|
||||
case 403:
|
||||
prefix = Slice("Forbidden");
|
||||
break;
|
||||
case 405:
|
||||
prefix = Slice("Method Not Allowed");
|
||||
break;
|
||||
case 500:
|
||||
prefix = Slice("Internal Server Error");
|
||||
break;
|
||||
@ -156,11 +174,31 @@ void Client::fail_query_with_error(PromisedQueryPtr &&query, object_ptr<td_api::
|
||||
fail_query_with_error(std::move(query), error->code_, error->message_, default_message);
|
||||
}
|
||||
|
||||
Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_test_dc, int64 tqueue_id,
|
||||
Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_user, bool is_test_dc, int64 tqueue_id,
|
||||
std::shared_ptr<const ClientParameters> parameters, td::ActorId<BotStatActor> stat_actor)
|
||||
: parent_(std::move(parent))
|
||||
, bot_token_(bot_token)
|
||||
, bot_token_id_("<unknown>")
|
||||
, is_user_(is_user)
|
||||
, is_test_dc_(is_test_dc)
|
||||
, tqueue_id_(tqueue_id)
|
||||
, parameters_(std::move(parameters))
|
||||
, stat_actor_(std::move(stat_actor)) {
|
||||
messages_lru_root_.lru_next = &messages_lru_root_;
|
||||
messages_lru_root_.lru_prev = &messages_lru_root_;
|
||||
|
||||
static auto is_inited = init_methods();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
Client::Client(td::ActorShared<> parent, const td::string &bot_token, const td::string &phone_number, bool is_user,
|
||||
bool is_test_dc, int64 tqueue_id, std::shared_ptr<const ClientParameters> parameters,
|
||||
td::ActorId<BotStatActor> stat_actor)
|
||||
: parent_(std::move(parent))
|
||||
, bot_token_(bot_token)
|
||||
, bot_token_id_("<unknown>")
|
||||
, phone_number_(phone_number)
|
||||
, is_user_(is_user)
|
||||
, is_test_dc_(is_test_dc)
|
||||
, tqueue_id_(tqueue_id)
|
||||
, parameters_(std::move(parameters))
|
||||
@ -2295,7 +2333,10 @@ class Client::TdOnAuthorizationCallback : public TdQueryCallback {
|
||||
}
|
||||
|
||||
void on_result(object_ptr<td_api::Object> result) override {
|
||||
bool was_ready = client_->authorization_state_->get_id() != td_api::authorizationStateWaitPhoneNumber::ID;
|
||||
bool was_ready = client_->authorization_state_->get_id() != td_api::authorizationStateWaitPhoneNumber::ID &&
|
||||
client_->authorization_state_->get_id() != td_api::authorizationStateWaitCode::ID &&
|
||||
client_->authorization_state_->get_id() != td_api::authorizationStateWaitPassword::ID &&
|
||||
client_->authorization_state_->get_id() != td_api::authorizationStateWaitRegistration::ID;
|
||||
if (result->get_id() == td_api::error::ID) {
|
||||
auto error = move_object_as<td_api::error>(result);
|
||||
if (error->code_ == 429 || error->code_ >= 500 || (error->code_ != 401 && was_ready)) {
|
||||
@ -2314,6 +2355,38 @@ class Client::TdOnAuthorizationCallback : public TdQueryCallback {
|
||||
Client *client_;
|
||||
};
|
||||
|
||||
class Client::TdOnAuthorizationQueryCallback : public TdQueryCallback {
|
||||
public:
|
||||
TdOnAuthorizationQueryCallback(Client *client, PromisedQueryPtr query) :
|
||||
client_(client), query_(std::move(query)) {
|
||||
}
|
||||
|
||||
void on_result(object_ptr<td_api::Object> result) override {
|
||||
bool was_ready = client_->authorization_state_->get_id() != td_api::authorizationStateWaitPhoneNumber::ID &&
|
||||
client_->authorization_state_->get_id() != td_api::authorizationStateWaitCode::ID &&
|
||||
client_->authorization_state_->get_id() != td_api::authorizationStateWaitPassword::ID &&
|
||||
client_->authorization_state_->get_id() != td_api::authorizationStateWaitRegistration::ID;
|
||||
if (result->get_id() == td_api::error::ID) {
|
||||
auto error = move_object_as<td_api::error>(result);
|
||||
if (error->code_ == 429 || error->code_ >= 500 || (error->code_ != 401 && was_ready)) {
|
||||
// try again
|
||||
return client_->on_update_authorization_state();
|
||||
}
|
||||
fail_query(401, "Unauthorized: Log in failed, logging out due to " + td::oneline(to_string(error)),
|
||||
std::move(query_));
|
||||
LOG(WARNING) << "Logging out due to " << td::oneline(to_string(error));
|
||||
client_->log_out();
|
||||
} else {
|
||||
answer_query(td::JsonTrue(), std::move(query_));
|
||||
client_->on_update_authorization_state();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Client *client_;
|
||||
PromisedQueryPtr query_;
|
||||
};
|
||||
|
||||
class Client::TdOnInitCallback : public TdQueryCallback {
|
||||
public:
|
||||
explicit TdOnInitCallback(Client *client) : client_(client) {
|
||||
@ -3237,7 +3310,7 @@ class Client::TdOnSendCustomRequestCallback : public TdQueryCallback {
|
||||
|
||||
class Client::TdOnPingCallback : public TdQueryCallback {
|
||||
public:
|
||||
TdOnPingCallback(PromisedQueryPtr query)
|
||||
explicit TdOnPingCallback(PromisedQueryPtr query)
|
||||
: query_(std::move(query)) {
|
||||
}
|
||||
|
||||
@ -3245,7 +3318,7 @@ class Client::TdOnPingCallback : public TdQueryCallback {
|
||||
if (result->get_id() == td_api::error::ID) {
|
||||
return fail_query_with_error(std::move(query_), move_object_as<td_api::error>(result), "Server not available");
|
||||
}
|
||||
CHECK(result->get_id() == 959899022); // id for return type `seconds`
|
||||
CHECK(result->get_id() == td_api::seconds::ID);
|
||||
|
||||
auto seconds_ = move_object_as<td_api::seconds>(result);
|
||||
answer_query(td::VirtuallyJsonableString(std::to_string(seconds_->seconds_)), std::move(query_));
|
||||
@ -3386,7 +3459,7 @@ void Client::raw_event(const td::Event::Raw &event) {
|
||||
}
|
||||
|
||||
void Client::loop() {
|
||||
if (logging_out_ || closing_ || was_authorized_) {
|
||||
if (logging_out_ || closing_ || was_authorized_ || waiting_for_auth_input_) {
|
||||
while (!cmd_queue_.empty()) {
|
||||
auto query = std::move(cmd_queue_.front());
|
||||
cmd_queue_.pop();
|
||||
@ -3947,9 +4020,20 @@ void Client::on_update_authorization_state() {
|
||||
case td_api::authorizationStateWaitEncryptionKey::ID:
|
||||
return send_request(make_object<td_api::checkDatabaseEncryptionKey>(), std::make_unique<TdOnInitCallback>(this));
|
||||
case td_api::authorizationStateWaitPhoneNumber::ID:
|
||||
return send_request(make_object<td_api::checkAuthenticationBotToken>(bot_token_),
|
||||
std::make_unique<TdOnAuthorizationCallback>(this));
|
||||
if (is_user_) {
|
||||
return send_request(make_object<td_api::setAuthenticationPhoneNumber>(phone_number_, nullptr),
|
||||
std::make_unique<TdOnAuthorizationCallback>(this));
|
||||
} else {
|
||||
return send_request(make_object<td_api::checkAuthenticationBotToken>(bot_token_),
|
||||
std::make_unique<TdOnAuthorizationCallback>(this));
|
||||
}
|
||||
case td_api::authorizationStateWaitCode::ID:
|
||||
case td_api::authorizationStateWaitPassword::ID:
|
||||
case td_api::authorizationStateWaitRegistration::ID:
|
||||
waiting_for_auth_input_ = true;
|
||||
return loop();
|
||||
case td_api::authorizationStateReady::ID: {
|
||||
waiting_for_auth_input_ = false;
|
||||
auto user_info = get_user_info(my_id_);
|
||||
if (my_id_ <= 0 || user_info == nullptr) {
|
||||
return send_request(make_object<td_api::getMe>(), std::make_unique<TdOnAuthorizationCallback>(this));
|
||||
@ -3970,12 +4054,14 @@ void Client::on_update_authorization_state() {
|
||||
return loop();
|
||||
}
|
||||
case td_api::authorizationStateLoggingOut::ID:
|
||||
waiting_for_auth_input_ = false;
|
||||
if (!logging_out_) {
|
||||
LOG(WARNING) << "Logging out";
|
||||
logging_out_ = true;
|
||||
}
|
||||
break;
|
||||
case td_api::authorizationStateClosing::ID:
|
||||
waiting_for_auth_input_ = false;
|
||||
if (!closing_) {
|
||||
LOG(WARNING) << "Closing";
|
||||
closing_ = true;
|
||||
@ -4334,6 +4420,7 @@ void Client::on_closed() {
|
||||
|
||||
if (logging_out_) {
|
||||
parameters_->shared_data_->webhook_db_->erase(bot_token_with_dc_);
|
||||
parameters_->shared_data_->user_db_->erase(bot_token_with_dc_);
|
||||
|
||||
class RmWorker : public td::Actor {
|
||||
public:
|
||||
@ -6029,6 +6116,17 @@ void Client::on_cmd(PromisedQueryPtr query) {
|
||||
return do_send_request(make_object<td_api::logOut>(), std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
||||
}
|
||||
}
|
||||
if (waiting_for_auth_input_) {
|
||||
if (query->method() == "authcode") {
|
||||
return process_authcode_query(query);
|
||||
} else if (query->method() == "2fapassword") {
|
||||
return process_2fapassword_query(query);
|
||||
} else if (query->method() == "registeruser" && parameters_->allow_users_registration_) {
|
||||
return process_register_user_query(query);
|
||||
} else {
|
||||
return fail_query(404, "Not Found: method not found", std::move(query));
|
||||
}
|
||||
}
|
||||
|
||||
if (logging_out_) {
|
||||
return fail_query(LOGGING_OUT_ERROR_CODE, LOGGING_OUT_ERROR_DESCRIPTION, std::move(query));
|
||||
@ -6063,6 +6161,7 @@ td::Status Client::process_get_my_commands_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_set_my_commands_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(bot_commands, get_bot_commands(query.get()));
|
||||
send_request(make_object<td_api::setCommands>(std::move(bot_commands)),
|
||||
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
||||
@ -6208,12 +6307,14 @@ td::Status Client::process_send_voice_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_send_game_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(game_short_name, get_required_string_arg(query.get(), "game_short_name"));
|
||||
do_send_message(make_object<td_api::inputMessageGame>(my_id_, game_short_name.str()), std::move(query));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
td::Status Client::process_send_invoice_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(title, get_required_string_arg(query.get(), "title"));
|
||||
TRY_RESULT(description, get_required_string_arg(query.get(), "description"));
|
||||
TRY_RESULT(payload, get_required_string_arg(query.get(), "payload"));
|
||||
@ -6351,6 +6452,7 @@ td::Status Client::process_stop_poll_query(PromisedQueryPtr &query) {
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(reply_markup, get_reply_markup(query.get()));
|
||||
CHECK_USER_REPLY_MARKUP();
|
||||
|
||||
resolve_reply_markup_bot_usernames(
|
||||
std::move(reply_markup), std::move(query),
|
||||
@ -6446,6 +6548,7 @@ td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) {
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(reply_markup, get_reply_markup(query.get()));
|
||||
CHECK_USER_REPLY_MARKUP();
|
||||
|
||||
if (chat_id.empty() && message_id == 0) {
|
||||
TRY_RESULT(inline_message_id, get_inline_message_id(query.get()));
|
||||
@ -6485,6 +6588,7 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(reply_markup, get_reply_markup(query.get()));
|
||||
CHECK_USER_REPLY_MARKUP();
|
||||
|
||||
if (chat_id.empty() && message_id == 0) {
|
||||
TRY_RESULT(inline_message_id, get_inline_message_id(query.get()));
|
||||
@ -6520,6 +6624,7 @@ td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) {
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(reply_markup, get_reply_markup(query.get()));
|
||||
CHECK_USER_REPLY_MARKUP();
|
||||
TRY_RESULT(input_media, get_input_media(query.get(), "media", false));
|
||||
|
||||
if (chat_id.empty() && message_id == 0) {
|
||||
@ -6554,6 +6659,7 @@ td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) {
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(reply_markup, get_reply_markup(query.get()));
|
||||
CHECK_USER_REPLY_MARKUP();
|
||||
TRY_RESULT(caption, get_caption(query.get()));
|
||||
|
||||
if (chat_id.empty() && message_id == 0) {
|
||||
@ -6584,9 +6690,11 @@ td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_edit_message_reply_markup_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(reply_markup, get_reply_markup(query.get()));
|
||||
CHECK_USER_REPLY_MARKUP();
|
||||
|
||||
if (chat_id.empty() && message_id == 0) {
|
||||
TRY_RESULT(inline_message_id, get_inline_message_id(query.get()));
|
||||
@ -6636,6 +6744,7 @@ td::Status Client::process_delete_message_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_set_game_score_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
@ -6673,6 +6782,7 @@ td::Status Client::process_set_game_score_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_get_game_high_scores_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto chat_id = query->arg("chat_id");
|
||||
auto message_id = get_message_id(query.get());
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
@ -6698,6 +6808,7 @@ td::Status Client::process_get_game_high_scores_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_answer_inline_query_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto inline_query_id = td::to_integer<int64>(query->arg("inline_query_id"));
|
||||
auto is_personal = to_bool(query->arg("is_personal"));
|
||||
int32 cache_time = get_integer_arg(query.get(), "cache_time", 300, 0, 24 * 60 * 60);
|
||||
@ -6721,6 +6832,7 @@ td::Status Client::process_answer_inline_query_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_answer_callback_query_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto callback_query_id = td::to_integer<int64>(query->arg("callback_query_id"));
|
||||
td::string text = query->arg("text").str();
|
||||
bool show_alert = to_bool(query->arg("show_alert"));
|
||||
@ -6733,6 +6845,7 @@ td::Status Client::process_answer_callback_query_query(PromisedQueryPtr &query)
|
||||
}
|
||||
|
||||
td::Status Client::process_answer_shipping_query_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto shipping_query_id = td::to_integer<int64>(query->arg("shipping_query_id"));
|
||||
auto ok = to_bool(query->arg("ok"));
|
||||
td::vector<object_ptr<td_api::shippingOption>> shipping_options;
|
||||
@ -6749,6 +6862,7 @@ td::Status Client::process_answer_shipping_query_query(PromisedQueryPtr &query)
|
||||
}
|
||||
|
||||
td::Status Client::process_answer_pre_checkout_query_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto pre_checkout_query_id = td::to_integer<int64>(query->arg("pre_checkout_query_id"));
|
||||
auto ok = to_bool(query->arg("ok"));
|
||||
td::MutableSlice error_message;
|
||||
@ -7226,6 +7340,7 @@ td::Status Client::process_get_sticker_set_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_upload_sticker_file_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
auto png_sticker = get_input_file(query.get(), "png_sticker");
|
||||
|
||||
@ -7238,6 +7353,7 @@ td::Status Client::process_upload_sticker_file_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
auto name = query->arg("name");
|
||||
auto title = query->arg("title");
|
||||
@ -7254,6 +7370,7 @@ td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query)
|
||||
}
|
||||
|
||||
td::Status Client::process_add_sticker_to_set_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
auto name = query->arg("name");
|
||||
TRY_RESULT(stickers, get_input_stickers(query.get()));
|
||||
@ -7268,6 +7385,7 @@ td::Status Client::process_add_sticker_to_set_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
td::Status Client::process_set_sticker_set_thumb_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
auto name = query->arg("name");
|
||||
auto thumbnail = get_input_file(query.get(), "thumb");
|
||||
@ -7280,6 +7398,7 @@ td::Status Client::process_set_sticker_set_thumb_query(PromisedQueryPtr &query)
|
||||
}
|
||||
|
||||
td::Status Client::process_set_sticker_position_in_set_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto file_id = trim(query->arg("sticker"));
|
||||
if (file_id.empty()) {
|
||||
return Status::Error(400, "Sticker is not specified");
|
||||
@ -7293,6 +7412,7 @@ td::Status Client::process_set_sticker_position_in_set_query(PromisedQueryPtr &q
|
||||
}
|
||||
|
||||
td::Status Client::process_delete_sticker_from_set_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
auto file_id = trim(query->arg("sticker"));
|
||||
if (file_id.empty()) {
|
||||
return Status::Error(400, "Sticker is not specified");
|
||||
@ -7304,6 +7424,7 @@ td::Status Client::process_delete_sticker_from_set_query(PromisedQueryPtr &query
|
||||
}
|
||||
|
||||
td::Status Client::process_set_passport_data_errors_query(PromisedQueryPtr &query) {
|
||||
CHECK_IS_BOT();
|
||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||
TRY_RESULT(passport_element_errors, get_passport_element_errors(query.get()));
|
||||
|
||||
@ -7557,6 +7678,45 @@ td::Status Client::process_ping_query(PromisedQueryPtr &query) {
|
||||
}
|
||||
|
||||
//end custom methods impl
|
||||
//start costom auth methods impl
|
||||
|
||||
void Client::process_authcode_query(PromisedQueryPtr &query) {
|
||||
auto code = query->arg("code");
|
||||
if (code.empty()) {
|
||||
return fail_query(400, "Bad Request: code not found", std::move(query));
|
||||
}
|
||||
if (authorization_state_->get_id() != td_api::authorizationStateWaitCode::ID) {
|
||||
return fail_query(400, "Bad Request: currently not waiting for a code", std::move(query));
|
||||
}
|
||||
send_request(make_object<td_api::checkAuthenticationCode>(code.str()),
|
||||
std::make_unique<TdOnAuthorizationQueryCallback>(this, std::move(query)));
|
||||
}
|
||||
|
||||
void Client::process_2fapassword_query(PromisedQueryPtr &query) {
|
||||
auto password = query->arg("password");
|
||||
if (password.empty()) {
|
||||
return fail_query(400, "Bad Request: password not found", std::move(query));
|
||||
}
|
||||
if (authorization_state_->get_id() != td_api::authorizationStateWaitPassword::ID) {
|
||||
return fail_query(400, "Bad Request: currently not waiting for a password", std::move(query));
|
||||
}
|
||||
send_request(make_object<td_api::checkAuthenticationPassword>(password.str()),
|
||||
std::make_unique<TdOnAuthorizationQueryCallback>(this, std::move(query)));
|
||||
}
|
||||
|
||||
void Client::process_register_user_query(PromisedQueryPtr &query) {
|
||||
auto first_name = query->arg("first_name");
|
||||
if (first_name.empty()) {
|
||||
return fail_query(400, "Bad Request: first_name not found", std::move(query));
|
||||
}
|
||||
auto last_name = query->arg("last_name");
|
||||
if (authorization_state_->get_id() != td_api::authorizationStateWaitRegistration::ID) {
|
||||
return fail_query(400, "Bad Request: currently not waiting for registration", std::move(query));
|
||||
}
|
||||
send_request(make_object<td_api::registerUser>(first_name.str(), last_name.str()),
|
||||
std::make_unique<TdOnAuthorizationQueryCallback>(this, std::move(query)));
|
||||
}
|
||||
//end custom auth methods impl
|
||||
|
||||
void Client::do_get_file(object_ptr<td_api::file> file, PromisedQueryPtr query) {
|
||||
if ((!parameters_->local_mode_ || !parameters_->no_file_limit_) &&
|
||||
@ -7776,6 +7936,9 @@ void Client::do_send_message(object_ptr<td_api::InputMessageContent> input_messa
|
||||
return fail_query_with_error(std::move(query), 400, r_reply_markup.error().message());
|
||||
}
|
||||
auto reply_markup = r_reply_markup.move_as_ok();
|
||||
if (reply_markup != nullptr && is_user_) {
|
||||
return fail_query_with_error(std::move(query), 405, "Method Not Allowed: reply markup not available as user.");
|
||||
}
|
||||
|
||||
resolve_reply_markup_bot_usernames(
|
||||
std::move(reply_markup), std::move(query),
|
||||
@ -9335,6 +9498,12 @@ constexpr Client::Slice Client::LOGGING_OUT_ERROR_DESCRIPTION;
|
||||
constexpr int Client::CLOSING_ERROR_CODE;
|
||||
constexpr Client::Slice Client::CLOSING_ERROR_DESCRIPTION;
|
||||
|
||||
constexpr int Client::BOT_ONLY_ERROR_CODE;
|
||||
constexpr Client::Slice Client::BOT_ONLY_ERROR_DESCRIPTION;
|
||||
|
||||
constexpr int Client::USER_ONLY_ERROR_CODE;
|
||||
constexpr Client::Slice Client::USER_ONLY_ERROR_DESCRIPTION;
|
||||
|
||||
std::unordered_map<td::string, td::Status (Client::*)(PromisedQueryPtr &query)> Client::methods_;
|
||||
|
||||
} // namespace telegram_bot_api
|
||||
|
@ -38,9 +38,13 @@ namespace td_api = td::td_api;
|
||||
|
||||
class Client : public WebhookActor::Callback {
|
||||
public:
|
||||
Client(td::ActorShared<> parent, const td::string &bot_token, bool is_test_dc, td::int64 tqueue_id,
|
||||
Client(td::ActorShared<> parent, const td::string &bot_token, bool is_user, bool is_test_dc, td::int64 tqueue_id,
|
||||
std::shared_ptr<const ClientParameters> parameters, td::ActorId<BotStatActor> stat_actor);
|
||||
|
||||
Client(td::ActorShared<> parent, const td::string &bot_token, const td::string &phone_number, bool is_user,
|
||||
bool is_test_dc, td::int64 tqueue_id, std::shared_ptr<const ClientParameters> parameters,
|
||||
td::ActorId<BotStatActor> stat_actor);
|
||||
|
||||
void send(PromisedQueryPtr query) override;
|
||||
|
||||
void close();
|
||||
@ -82,6 +86,12 @@ class Client : public WebhookActor::Callback {
|
||||
static constexpr int CLOSING_ERROR_CODE = 500;
|
||||
static constexpr Slice CLOSING_ERROR_DESCRIPTION = "Internal Server Error: restart";
|
||||
|
||||
static constexpr int BOT_ONLY_ERROR_CODE = 405;
|
||||
static constexpr Slice BOT_ONLY_ERROR_DESCRIPTION = "Method Not Allowed: You can only use this method as a bot";
|
||||
|
||||
static constexpr int USER_ONLY_ERROR_CODE = 405;
|
||||
static constexpr Slice USER_ONLY_ERROR_DESCRIPTION = "Method Not Allowed: You can only use this method as a user";
|
||||
|
||||
class JsonFile;
|
||||
class JsonDatedFile;
|
||||
class JsonDatedFiles;
|
||||
@ -149,6 +159,7 @@ class Client : public WebhookActor::Callback {
|
||||
|
||||
class TdOnOkCallback;
|
||||
class TdOnAuthorizationCallback;
|
||||
class TdOnAuthorizationQueryCallback;
|
||||
class TdOnInitCallback;
|
||||
class TdOnGetUserProfilePhotosCallback;
|
||||
class TdOnSendMessageCallback;
|
||||
@ -498,6 +509,11 @@ class Client : public WebhookActor::Callback {
|
||||
Status process_toggle_group_invites_query(PromisedQueryPtr &query);
|
||||
Status process_ping_query(PromisedQueryPtr &query);
|
||||
|
||||
//custom auth methods
|
||||
void process_authcode_query(PromisedQueryPtr &query);
|
||||
void process_2fapassword_query(PromisedQueryPtr &query);
|
||||
void process_register_user_query(PromisedQueryPtr &query);
|
||||
|
||||
|
||||
void webhook_verified(td::string cached_ip_address) override;
|
||||
void webhook_success() override;
|
||||
@ -823,11 +839,14 @@ class Client : public WebhookActor::Callback {
|
||||
bool logging_out_ = false;
|
||||
bool need_close_ = false;
|
||||
bool clear_tqueue_ = false;
|
||||
bool waiting_for_auth_input_ = false;
|
||||
|
||||
td::ActorShared<> parent_;
|
||||
td::string bot_token_;
|
||||
td::string bot_token_with_dc_;
|
||||
td::string bot_token_id_;
|
||||
td::string phone_number_;
|
||||
bool is_user_;
|
||||
bool is_test_dc_;
|
||||
int64 tqueue_id_;
|
||||
double start_time_ = 0;
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/Time.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/base64.h"
|
||||
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
@ -62,7 +64,9 @@ void ClientManager::send(PromisedQueryPtr query) {
|
||||
// automatically send 429
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parameters_->allow_users_ && query->is_user()) {
|
||||
return fail_query(405, "Method Not Allowed: Users are not allowed to use the api", std::move(query));
|
||||
}
|
||||
td::string token = query->token().str();
|
||||
if (token[0] == '0' || token.size() > 80u || token.find('/') != td::string::npos ||
|
||||
token.find(':') == td::string::npos) {
|
||||
@ -77,55 +81,29 @@ void ClientManager::send(PromisedQueryPtr query) {
|
||||
token += "/test";
|
||||
}
|
||||
|
||||
auto bot_token_with_dc = PSTRING() << query->token() << (query->is_test_dc() ? ":T" : "");
|
||||
if (parameters_->shared_data_->user_db_->isset(bot_token_with_dc) != query->is_user()) {
|
||||
return fail_query(400, "Bad Request: Please use the correct api endpoint for bots or users", std::move(query));
|
||||
}
|
||||
|
||||
auto id_it = token_to_id_.find(token);
|
||||
if (id_it == token_to_id_.end()) {
|
||||
std::string ip_address;
|
||||
if (query->peer_address().is_valid() && !query->peer_address().is_reserved()) { // external connection
|
||||
ip_address = query->peer_address().get_ip_str().str();
|
||||
} else {
|
||||
// invalid peer address or connection from the local network
|
||||
ip_address = query->get_header("x-real-ip").str();
|
||||
if (!check_flood_limits(query)) {
|
||||
return;
|
||||
}
|
||||
if (!ip_address.empty()) {
|
||||
td::IPAddress tmp;
|
||||
tmp.init_host_port(ip_address, 0).ignore();
|
||||
tmp.clear_ipv6_interface();
|
||||
if (tmp.is_valid()) {
|
||||
ip_address = tmp.get_ip_str().str();
|
||||
}
|
||||
}
|
||||
LOG(DEBUG) << "Receive incoming query for new bot " << token << " from " << query->peer_address();
|
||||
if (!ip_address.empty()) {
|
||||
LOG(DEBUG) << "Check Client creation flood control for IP address " << ip_address;
|
||||
auto res = flood_controls_.emplace(std::move(ip_address), td::FloodControlFast());
|
||||
auto &flood_control = res.first->second;
|
||||
if (res.second) {
|
||||
flood_control.add_limit(60, 20); // 20 in a minute
|
||||
flood_control.add_limit(60 * 60, 600); // 600 in an hour
|
||||
}
|
||||
td::uint32 now = static_cast<td::uint32>(td::Time::now());
|
||||
td::uint32 wakeup_at = flood_control.get_wakeup_at();
|
||||
if (wakeup_at > now) {
|
||||
LOG(INFO) << "Failed to create Client from IP address " << ip_address;
|
||||
return query->set_retry_after_error(static_cast<int>(wakeup_at - now) + 1);
|
||||
}
|
||||
flood_control.add_event(static_cast<td::int32>(now));
|
||||
}
|
||||
|
||||
auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), token, td::ActorOwn<Client>()});
|
||||
auto *client_info = clients_.get(id);
|
||||
auto stat_actor = client_info->stat_.actor_id(&client_info->stat_);
|
||||
auto client_id = td::create_actor<Client>(
|
||||
PSLICE() << "Client/" << token, actor_shared(this, id), query->token().str(), query->is_test_dc(),
|
||||
get_tqueue_id(r_user_id.ok(), query->is_test_dc()), parameters_, std::move(stat_actor));
|
||||
PSLICE() << "Client/" << token, actor_shared(this, id), query->token().str(), query->is_user(),
|
||||
query->is_test_dc(), get_tqueue_id(r_user_id.ok(), query->is_test_dc()), parameters_, std::move(stat_actor));
|
||||
|
||||
auto method = query->method();
|
||||
if (method != "deletewebhook" && method != "setwebhook") {
|
||||
auto bot_token_with_dc = PSTRING() << query->token() << (query->is_test_dc() ? ":T" : "");
|
||||
auto webhook_info = parameters_->shared_data_->webhook_db_->get(bot_token_with_dc);
|
||||
if (!webhook_info.empty()) {
|
||||
send_closure(client_id, &Client::send,
|
||||
get_webhook_restore_query(bot_token_with_dc, webhook_info, parameters_->shared_data_));
|
||||
get_webhook_restore_query(bot_token_with_dc, query->is_user(), webhook_info, parameters_->shared_data_));
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +118,89 @@ void ClientManager::send(PromisedQueryPtr query) {
|
||||
send_closure(client_info->client_, &Client::send, std::move(query)); // will send 429 if the client is already closed
|
||||
}
|
||||
|
||||
void ClientManager::user_login(PromisedQueryPtr query) {
|
||||
if (!check_flood_limits(query, true)) {
|
||||
return;
|
||||
}
|
||||
if (!parameters_->allow_users_) {
|
||||
return fail_query(405, "Method Not Allowed: Users are not allowed to use the api", std::move(query));
|
||||
}
|
||||
td::MutableSlice r_phone_number = query->arg("phone_number");
|
||||
if (r_phone_number.size() < 5 || r_phone_number.size() > 15) {
|
||||
return fail_query(401, "Unauthorized: invalid phone number specified", std::move(query));
|
||||
}
|
||||
td::int64 phone_number = 0;
|
||||
for (char const &c: r_phone_number) {
|
||||
if (isdigit(c)) {
|
||||
phone_number = phone_number * 10 + (c - 48);
|
||||
}
|
||||
}
|
||||
td::UInt256 token_data;
|
||||
td::Random::secure_bytes(token_data.raw, sizeof(token_data));
|
||||
td::string user_token = td::to_string(phone_number) + ":" + td::base64url_encode(token_data.as_slice());
|
||||
auto user_token_with_dc = PSTRING() << user_token << (query->is_test_dc() ? ":T" : "");
|
||||
|
||||
long token_hash = std::hash<td::string>{}(user_token);
|
||||
|
||||
auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), user_token, td::ActorOwn<Client>()});
|
||||
auto *client_info = clients_.get(id);
|
||||
auto stat_actor = client_info->stat_.actor_id(&client_info->stat_);
|
||||
auto client_id = td::create_actor<Client>(
|
||||
PSLICE() << "Client/" << user_token, actor_shared(this, id), user_token, td::to_string(phone_number),
|
||||
true, query->is_test_dc(), get_tqueue_id(token_hash, query->is_test_dc()), parameters_, std::move(stat_actor));
|
||||
|
||||
clients_.get(id)->client_ = std::move(client_id);
|
||||
auto id_it = token_to_id_.end();
|
||||
std::tie(id_it, std::ignore) = token_to_id_.emplace(user_token, id);
|
||||
parameters_->shared_data_->user_db_->set(user_token_with_dc, "1");
|
||||
answer_query(td::VirtuallyJsonableString(user_token), std::move(query));
|
||||
}
|
||||
|
||||
bool ClientManager::check_flood_limits(PromisedQueryPtr &query, bool is_user_login) {
|
||||
td::string ip_address;
|
||||
if (query->peer_address().is_valid() && !query->peer_address().is_reserved()) { // external connection
|
||||
ip_address = query->peer_address().get_ip_str().str();
|
||||
} else {
|
||||
// invalid peer address or connection from the local network
|
||||
ip_address = query->get_header("x-real-ip").str();
|
||||
}
|
||||
if (!ip_address.empty()) {
|
||||
td::IPAddress tmp;
|
||||
tmp.init_host_port(ip_address, 0).ignore();
|
||||
tmp.clear_ipv6_interface();
|
||||
if (tmp.is_valid()) {
|
||||
ip_address = tmp.get_ip_str().str();
|
||||
}
|
||||
}
|
||||
LOG(DEBUG) << "Receive incoming query for new bot " << query->token() << " from " << query->peer_address();
|
||||
if (!ip_address.empty()) {
|
||||
LOG(DEBUG) << "Check Client creation flood control for IP address " << ip_address;
|
||||
if (is_user_login) {
|
||||
ip_address += "/user";
|
||||
}
|
||||
auto res = flood_controls_.emplace(std::move(ip_address), td::FloodControlFast());
|
||||
auto &flood_control = res.first->second;
|
||||
if (res.second) {
|
||||
if (is_user_login) {
|
||||
flood_control.add_limit(60, 5); // 5 in a minute
|
||||
flood_control.add_limit(60 * 60, 15); // 15 in an hour
|
||||
} else {
|
||||
flood_control.add_limit(60, 20); // 20 in a minute
|
||||
flood_control.add_limit(60 * 60, 600); // 600 in an hour
|
||||
}
|
||||
}
|
||||
auto now = static_cast<td::uint32>(td::Time::now());
|
||||
td::uint32 wakeup_at = flood_control.get_wakeup_at();
|
||||
if (wakeup_at > now) {
|
||||
LOG(INFO) << "Failed to create Client from IP address " << ip_address;
|
||||
query->set_retry_after_error(static_cast<int>(wakeup_at - now) + 1);
|
||||
return false;
|
||||
}
|
||||
flood_control.add_event(static_cast<td::int32>(now));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientManager::get_stats(td::PromiseActor<td::BufferSlice> promise,
|
||||
td::vector<std::pair<td::string, td::string>> args) {
|
||||
if (close_flag_) {
|
||||
@ -297,13 +358,19 @@ void ClientManager::start_up() {
|
||||
parameters_->shared_data_->tqueue_ = std::move(tqueue);
|
||||
}
|
||||
|
||||
// init webhook_db
|
||||
// init webhook_db and user_db
|
||||
auto concurrent_webhook_db = td::make_unique<td::BinlogKeyValue<td::ConcurrentBinlog>>();
|
||||
auto status = concurrent_webhook_db->init("webhooks_db.binlog", td::DbKey::empty(), scheduler_id);
|
||||
LOG_IF(FATAL, status.is_error()) << "Can't open webhooks_db.binlog " << status.error();
|
||||
parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db);
|
||||
|
||||
auto concurrent_user_db = td::make_unique<td::BinlogKeyValue<td::ConcurrentBinlog>>();
|
||||
status = concurrent_user_db->init("user_db.binlog", td::DbKey::empty(), scheduler_id);
|
||||
LOG_IF(FATAL, status.is_error()) << "Can't open user_db.binlog " << status.error();
|
||||
parameters_->shared_data_->user_db_ = std::move(concurrent_user_db);
|
||||
|
||||
auto &webhook_db = *parameters_->shared_data_->webhook_db_;
|
||||
auto &user_db = *parameters_->shared_data_->user_db_;
|
||||
for (auto key_value : webhook_db.get_all()) {
|
||||
if (!token_range_(td::to_integer<td::uint64>(key_value.first))) {
|
||||
LOG(WARNING) << "DROP WEBHOOK: " << key_value.first << " ---> " << key_value.second;
|
||||
@ -311,12 +378,13 @@ void ClientManager::start_up() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto query = get_webhook_restore_query(key_value.first, key_value.second, parameters_->shared_data_);
|
||||
auto query = get_webhook_restore_query(key_value.first, user_db.isset(key_value.first), key_value.second, parameters_->shared_data_);
|
||||
send_closure_later(actor_id(this), &ClientManager::send, std::move(query));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, td::Slice webhook_info,
|
||||
PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool is_user, td::Slice webhook_info,
|
||||
std::shared_ptr<SharedData> shared_data) {
|
||||
// create Query with empty promise
|
||||
td::vector<td::BufferSlice> containers;
|
||||
@ -364,7 +432,7 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, td::S
|
||||
args.emplace_back(add_string("url"), add_string(parser.read_all()));
|
||||
|
||||
const auto method = add_string("setwebhook");
|
||||
auto query = std::make_unique<Query>(std::move(containers), token, is_test_dc, method, std::move(args),
|
||||
auto query = std::make_unique<Query>(std::move(containers), token, is_user, is_test_dc, method, std::move(args),
|
||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>>(),
|
||||
td::vector<td::HttpFile>(), std::move(shared_data), td::IPAddress());
|
||||
query->set_internal(true);
|
||||
@ -392,6 +460,7 @@ void ClientManager::close_db() {
|
||||
|
||||
parameters_->shared_data_->tqueue_->close(mpromise.get_promise());
|
||||
parameters_->shared_data_->webhook_db_->close(mpromise.get_promise());
|
||||
parameters_->shared_data_->user_db_->close(mpromise.get_promise());
|
||||
}
|
||||
|
||||
void ClientManager::finish_close() {
|
||||
|
@ -42,6 +42,9 @@ class ClientManager final : public td::Actor {
|
||||
}
|
||||
|
||||
void send(PromisedQueryPtr query);
|
||||
void user_login(PromisedQueryPtr query);
|
||||
|
||||
bool check_flood_limits(PromisedQueryPtr &query, bool is_user_login=false);
|
||||
|
||||
void get_stats(td::PromiseActor<td::BufferSlice> promise, td::vector<std::pair<td::string, td::string>> args);
|
||||
|
||||
@ -68,7 +71,7 @@ class ClientManager final : public td::Actor {
|
||||
|
||||
static td::int64 get_tqueue_id(td::int64 user_id, bool is_test_dc);
|
||||
|
||||
static PromisedQueryPtr get_webhook_restore_query(td::Slice token, td::Slice webhook_info,
|
||||
static PromisedQueryPtr get_webhook_restore_query(td::Slice token, bool is_user, td::Slice webhook_info,
|
||||
std::shared_ptr<SharedData> shared_data);
|
||||
|
||||
void start_up() override;
|
||||
|
@ -34,6 +34,7 @@ struct SharedData {
|
||||
// not thread-safe
|
||||
td::ListNode query_list_;
|
||||
td::unique_ptr<td::KeyValueSyncInterface> webhook_db_;
|
||||
td::unique_ptr<td::KeyValueSyncInterface> user_db_;
|
||||
td::unique_ptr<td::TQueue> tqueue_;
|
||||
|
||||
double unix_time_difference_{-1e100};
|
||||
@ -58,6 +59,8 @@ struct ClientParameters {
|
||||
bool allow_http_ = false;
|
||||
bool use_relative_path_ = false;
|
||||
bool no_file_limit_ = true;
|
||||
bool allow_users_ = false;
|
||||
bool allow_users_registration_ = false;
|
||||
|
||||
td::int32 api_id_ = 0;
|
||||
td::string api_hash_;
|
||||
|
@ -28,22 +28,37 @@ void HttpConnection::handle(td::unique_ptr<td::HttpQuery> http_query,
|
||||
return send_http_error(404, "Not Found: absolute URI is specified in the Request-Line");
|
||||
}
|
||||
|
||||
if (!url_path_parser.try_skip("/bot")) {
|
||||
bool is_login = false;
|
||||
bool is_user = false;
|
||||
if (url_path_parser.try_skip("/bot")) {
|
||||
} else if (url_path_parser.try_skip("/userlogin")) {
|
||||
is_user = true;
|
||||
is_login = true;
|
||||
} else if (url_path_parser.try_skip("/user")) {
|
||||
is_user = true;
|
||||
} else {
|
||||
return send_http_error(404, "Not Found");
|
||||
}
|
||||
|
||||
auto token = url_path_parser.read_till('/');
|
||||
td::MutableSlice token;
|
||||
bool is_test_dc = false;
|
||||
if (url_path_parser.try_skip("/test")) {
|
||||
is_test_dc = true;
|
||||
}
|
||||
url_path_parser.skip('/');
|
||||
if (url_path_parser.status().is_error()) {
|
||||
return send_http_error(404, "Not Found");
|
||||
td::MutableSlice method;
|
||||
if (!is_login) {
|
||||
token = url_path_parser.read_till('/');
|
||||
is_test_dc = false;
|
||||
if (url_path_parser.try_skip("/test")) {
|
||||
is_test_dc = true;
|
||||
}
|
||||
url_path_parser.skip('/');
|
||||
if (url_path_parser.status().is_error()) {
|
||||
return send_http_error(404, "Not Found");
|
||||
}
|
||||
|
||||
method = url_path_parser.data();
|
||||
}
|
||||
|
||||
auto method = url_path_parser.data();
|
||||
auto query = std::make_unique<Query>(std::move(http_query->container_), token, is_test_dc, method,
|
||||
|
||||
auto query = std::make_unique<Query>(std::move(http_query->container_), token, is_user, is_test_dc, method,
|
||||
std::move(http_query->args_), std::move(http_query->headers_),
|
||||
std::move(http_query->files_), shared_data_, http_query->peer_address_);
|
||||
|
||||
@ -52,7 +67,11 @@ void HttpConnection::handle(td::unique_ptr<td::HttpQuery> http_query,
|
||||
td::init_promise_future(&promise, &future);
|
||||
future.set_event(td::EventCreator::yield(actor_id()));
|
||||
auto promised_query = PromisedQueryPtr(query.release(), PromiseDeleter(std::move(promise)));
|
||||
send_closure(client_manager_, &ClientManager::send, std::move(promised_query));
|
||||
if (is_login) {
|
||||
send_closure(client_manager_, &ClientManager::user_login, std::move(promised_query));
|
||||
} else {
|
||||
send_closure(client_manager_, &ClientManager::send, std::move(promised_query));
|
||||
}
|
||||
result_ = std::move(future);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace telegram_bot_api {
|
||||
|
||||
std::unordered_map<td::string, std::unique_ptr<td::VirtuallyJsonable>> empty_parameters;
|
||||
|
||||
Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_test_dc, td::MutableSlice method,
|
||||
Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
||||
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address)
|
||||
@ -31,6 +31,7 @@ Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_t
|
||||
, peer_address_(peer_address)
|
||||
, container_(std::move(container))
|
||||
, token_(token)
|
||||
, is_user_(is_user)
|
||||
, is_test_dc_(is_test_dc)
|
||||
, method_(method)
|
||||
, args_(std::move(args))
|
||||
|
@ -37,6 +37,9 @@ class Query : public td::ListNode {
|
||||
td::Slice token() const {
|
||||
return token_;
|
||||
}
|
||||
bool is_user() const {
|
||||
return is_user_;
|
||||
}
|
||||
bool is_test_dc() const {
|
||||
return is_test_dc_;
|
||||
}
|
||||
@ -122,7 +125,7 @@ class Query : public td::ListNode {
|
||||
is_internal_ = is_internal;
|
||||
}
|
||||
|
||||
Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_test_dc, td::MutableSlice method,
|
||||
Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
||||
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address);
|
||||
@ -155,6 +158,7 @@ class Query : public td::ListNode {
|
||||
// request
|
||||
td::vector<td::BufferSlice> container_;
|
||||
td::Slice token_;
|
||||
bool is_user_;
|
||||
bool is_test_dc_;
|
||||
td::MutableSlice method_;
|
||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> args_;
|
||||
|
@ -586,7 +586,7 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
|
||||
method != "logout" && !td::begins_with(method, "get")) {
|
||||
VLOG(webhook) << "Receive request " << method << " in response to webhook";
|
||||
auto query =
|
||||
std::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, td::MutableSlice(),
|
||||
std::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, false, td::MutableSlice(),
|
||||
std::move(response->args_), std::move(response->headers_),
|
||||
std::move(response->files_), parameters_->shared_data_, response->peer_address_);
|
||||
auto promised_query =
|
||||
|
@ -176,6 +176,9 @@ int main(int argc, char *argv[]) {
|
||||
[&] { parameters->no_file_limit_ = true; });
|
||||
options.add_option('\0', "insecure", "allow the Bot API to send request via insecure HTTP", [&] { parameters->allow_http_ = true; });
|
||||
options.add_option('\0', "relative", "use relative file path in local mode", [&] { parameters->use_relative_path_ = true; });
|
||||
options.add_option('\0', "allow-users", "allow user accounts to use the API", [&] { parameters->allow_users_ = true; });
|
||||
options.add_option('\0', "allow-users-registration", "allow user accounts to be registered on the API",
|
||||
[&] { parameters->allow_users_registration_ = true; });
|
||||
|
||||
options.add_checked_option(
|
||||
'\0', "api-id",
|
||||
|
Loading…
Reference in New Issue
Block a user