From d9c00c452b70c5050bfe0253d201cf17188129f9 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 28 Jun 2023 20:55:38 +0300 Subject: [PATCH 01/45] Use bot identifier as token for webhook requests. --- telegram-bot-api/WebhookActor.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index 2033a68..4f79c27 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -623,10 +623,12 @@ void WebhookActor::handle(td::unique_ptr response) { if (!method.empty() && method != "deletewebhook" && method != "setwebhook" && method != "close" && method != "logout" && !td::begins_with(method, "get")) { VLOG(webhook) << "Receive request " << method << " in response to webhook"; - auto query = td::make_unique(std::move(response->container_), td::MutableSlice(), false, - td::MutableSlice(), std::move(response->args_), - std::move(response->headers_), std::move(response->files_), - parameters_->shared_data_, response->peer_address_, false); + response->container_.emplace_back(PSLICE() << (tqueue_id_ & ((static_cast(1) << 54) - 1))); + auto token = response->container_.back().as_slice(); + auto query = td::make_unique( + std::move(response->container_), token, tqueue_id_ >= (static_cast(1) << 54), + td::MutableSlice(), std::move(response->args_), std::move(response->headers_), + std::move(response->files_), parameters_->shared_data_, response->peer_address_, false); auto promised_query = PromisedQueryPtr(query.release(), PromiseDeleter(td::Promise>())); send_closure(callback_, &Callback::send, std::move(promised_query)); } From 84e512c2e4ea66340d99529b4fcb55249b4903ef Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 6 Jul 2023 14:01:14 +0300 Subject: [PATCH 02/45] Make Client::get_reply_markup static. --- telegram-bot-api/Client.cpp | 83 +++++++++++++++++++------------------ telegram-bot-api/Client.h | 35 ++++++++++------ 2 files changed, 66 insertions(+), 52 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index eb66463..bcb1bd3 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -4795,13 +4795,13 @@ void Client::fix_inline_query_results_bot_user_ids( } void Client::resolve_bot_usernames(PromisedQueryPtr query, td::Promise on_success) { - CHECK(!unresolved_bot_usernames_.empty()); + CHECK(!bot_user_ids_.unresolved_bot_usernames_.empty()); auto query_id = current_bot_resolve_query_id_++; auto &pending_query = pending_bot_resolve_queries_[query_id]; - pending_query.pending_resolve_count = unresolved_bot_usernames_.size(); + pending_query.pending_resolve_count = bot_user_ids_.unresolved_bot_usernames_.size(); pending_query.query = std::move(query); pending_query.on_success = std::move(on_success); - for (auto &username : unresolved_bot_usernames_) { + for (auto &username : bot_user_ids_.unresolved_bot_usernames_) { auto &query_ids = awaiting_bot_resolve_queries_[username]; query_ids.push_back(query_id); if (query_ids.size() == 1) { @@ -4809,13 +4809,13 @@ void Client::resolve_bot_usernames(PromisedQueryPtr query, td::Promise(this, username)); } } - unresolved_bot_usernames_.clear(); + bot_user_ids_.unresolved_bot_usernames_.clear(); } template void Client::resolve_reply_markup_bot_usernames(object_ptr reply_markup, PromisedQueryPtr query, OnSuccess on_success) { - if (!unresolved_bot_usernames_.empty()) { + if (!bot_user_ids_.unresolved_bot_usernames_.empty()) { CHECK(reply_markup != nullptr); CHECK(reply_markup->get_id() == td_api::replyMarkupInlineKeyboard::ID); return resolve_bot_usernames( @@ -4834,7 +4834,7 @@ void Client::resolve_reply_markup_bot_usernames(object_ptr template void Client::resolve_inline_query_results_bot_usernames(td::vector> results, PromisedQueryPtr query, OnSuccess on_success) { - if (!unresolved_bot_usernames_.empty()) { + if (!bot_user_ids_.unresolved_bot_usernames_.empty()) { return resolve_bot_usernames( std::move(query), td::PromiseCreator::lambda([this, results = std::move(results), @@ -4856,9 +4856,9 @@ void Client::on_resolve_bot_username(const td::string &username, int64 user_id) awaiting_bot_resolve_queries_.erase(query_ids_it); if (user_id == 0) { - bot_user_ids_.erase(username); + bot_user_ids_.bot_user_ids_.erase(username); } else { - auto &temp_bot_user_id = bot_user_ids_[username]; + auto &temp_bot_user_id = bot_user_ids_.bot_user_ids_[username]; temp_to_real_bot_user_id_[temp_bot_user_id] = user_id; temp_bot_user_id = user_id; } @@ -5271,6 +5271,7 @@ void Client::on_update(object_ptr result) { } else { CHECK(update->value_->get_id() == td_api::optionValueInteger::ID); my_id_ = move_object_as(update->value_)->value_; + bot_user_ids_.default_bot_user_id_ = my_id_; } } if (name == "group_anonymous_bot_user_id" && update->value_->get_id() == td_api::optionValueInteger::ID) { @@ -5587,7 +5588,8 @@ td::Result> Client::get_keyboard_butt return td::Status::Error(400, "KeyboardButton must be a String or an Object"); } -td::Result> Client::get_inline_keyboard_button(td::JsonValue &button) { +td::Result> Client::get_inline_keyboard_button( + td::JsonValue &button, BotUserIds &bot_user_ids) { if (button.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "InlineKeyboardButton must be an Object"); } @@ -5659,7 +5661,7 @@ td::Result> Client::get_inline_ int64 bot_user_id = 0; if (bot_username.empty()) { - bot_user_id = my_id_; + bot_user_id = bot_user_ids.default_bot_user_id_; } else { if (bot_username[0] == '@') { bot_username = bot_username.substr(1); @@ -5672,16 +5674,13 @@ td::Result> Client::get_inline_ return td::Status::Error(400, "LoginUrl bot username is invalid"); } } - if (cur_temp_bot_user_id_ >= 100000) { - return td::Status::Error(400, "Too many different LoginUrl bot usernames"); - } - auto &user_id = bot_user_ids_[bot_username]; + auto &user_id = bot_user_ids.bot_user_ids_[bot_username]; if (user_id == 0) { - user_id = cur_temp_bot_user_id_++; + user_id = bot_user_ids.cur_temp_bot_user_id_++; user_id *= 1000; } if (user_id % 1000 == 0) { - unresolved_bot_usernames_.insert(bot_username); + bot_user_ids.unresolved_bot_usernames_.insert(bot_username); } bot_user_id = user_id; } @@ -5702,7 +5701,8 @@ td::Result> Client::get_inline_ return td::Status::Error(400, "Text buttons are unallowed in the inline keyboard"); } -td::Result> Client::get_reply_markup(const Query *query) { +td::Result> Client::get_reply_markup(const Query *query, + BotUserIds &bot_user_ids) { auto reply_markup = query->arg("reply_markup"); if (reply_markup.empty()) { return nullptr; @@ -5715,10 +5715,11 @@ td::Result> Client::get_reply_markup(con return td::Status::Error(400, "Can't parse reply keyboard markup JSON object"); } - return get_reply_markup(r_value.move_as_ok()); + return get_reply_markup(r_value.move_as_ok(), bot_user_ids); } -td::Result> Client::get_reply_markup(td::JsonValue &&value) { +td::Result> Client::get_reply_markup(td::JsonValue &&value, + BotUserIds &bot_user_ids) { td::vector>> rows; td::vector>> inline_rows; td::Slice input_field_placeholder; @@ -5765,7 +5766,7 @@ td::Result> Client::get_reply_markup(td: "Field \"inline_keyboard\" of the InlineKeyboardMarkup must be an Array of Arrays"); } for (auto &button : inline_row.get_array()) { - auto r_button = get_inline_keyboard_button(button); + auto r_button = get_inline_keyboard_button(button, bot_user_ids); if (r_button.is_error()) { return td::Status::Error(400, PSLICE() << "Can't parse inline keyboard button: " << r_button.error().message()); @@ -5829,7 +5830,7 @@ td::Result> Client::get_reply_markup(td: result = make_object(is_personal, input_field_placeholder.str()); } if (result == nullptr || result->get_id() != td_api::replyMarkupInlineKeyboard::ID) { - unresolved_bot_usernames_.clear(); + bot_user_ids.unresolved_bot_usernames_.clear(); } return std::move(result); @@ -6231,7 +6232,7 @@ td::Result> Client::get_inl } td::Result>> Client::get_inline_query_results( - const Query *query) { + const Query *query, BotUserIds &bot_user_ids) { auto results_encoded = query->arg("results"); if (results_encoded.empty()) { return td::vector>(); @@ -6244,11 +6245,11 @@ td::Result>> Clien 400, PSLICE() << "Can't parse JSON encoded inline query results: " << r_values.error().message()); } - return get_inline_query_results(r_values.move_as_ok()); + return get_inline_query_results(r_values.move_as_ok(), bot_user_ids); } td::Result>> Client::get_inline_query_results( - td::JsonValue &&values) { + td::JsonValue &&values, BotUserIds &bot_user_ids) { if (values.type() == td::JsonValue::Type::Null) { return td::vector>(); } @@ -6258,7 +6259,7 @@ td::Result>> Clien td::vector> inline_query_results; for (auto &value : values.get_array()) { - auto r_inline_query_result = get_inline_query_result(std::move(value)); + auto r_inline_query_result = get_inline_query_result(std::move(value), bot_user_ids); if (r_inline_query_result.is_error()) { return td::Status::Error( 400, PSLICE() << "Can't parse inline query result: " << r_inline_query_result.error().message()); @@ -6269,7 +6270,8 @@ td::Result>> Clien return std::move(inline_query_results); } -td::Result> Client::get_inline_query_result(const Query *query) { +td::Result> Client::get_inline_query_result( + const Query *query, BotUserIds &bot_user_ids) { auto result_encoded = query->arg("result"); if (result_encoded.empty()) { return td::Status::Error(400, "Result isn't specified"); @@ -6282,10 +6284,11 @@ td::Result> Client::get_inlin 400, PSLICE() << "Can't parse JSON encoded web view query results " << r_value.error().message()); } - return get_inline_query_result(r_value.move_as_ok()); + return get_inline_query_result(r_value.move_as_ok(), bot_user_ids); } -td::Result> Client::get_inline_query_result(td::JsonValue &&value) { +td::Result> Client::get_inline_query_result( + td::JsonValue &&value, BotUserIds &bot_user_ids) { if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "Inline query result must be an object"); } @@ -6326,7 +6329,7 @@ td::Result> Client::get_inlin TRY_RESULT(reply_markup_object, get_json_object_field(object, "reply_markup", td::JsonValue::Type::Object)); object_ptr reply_markup; if (reply_markup_object.type() != td::JsonValue::Type::Null) { - TRY_RESULT_ASSIGN(reply_markup, get_reply_markup(std::move(reply_markup_object))); + TRY_RESULT_ASSIGN(reply_markup, get_reply_markup(std::move(reply_markup_object), bot_user_ids)); } auto thumbnail_url_field_name = td::Slice("thumbnail_url"); @@ -7859,7 +7862,7 @@ void Client::on_cmd(PromisedQueryPtr query) { } CHECK(was_authorized_); - unresolved_bot_usernames_.clear(); + bot_user_ids_.unresolved_bot_usernames_.clear(); auto method_it = methods_.find(query->method().str()); if (method_it == methods_.end()) { @@ -8263,7 +8266,7 @@ td::Status Client::process_send_poll_query(PromisedQueryPtr &query) { 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())); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); resolve_reply_markup_bot_usernames( std::move(reply_markup), std::move(query), @@ -8318,7 +8321,7 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { auto allow_sending_without_reply = to_bool(query->arg("allow_sending_without_reply")); auto disable_notification = to_bool(query->arg("disable_notification")); auto protect_content = to_bool(query->arg("protect_content")); - // TRY_RESULT(reply_markup, get_reply_markup(query.get())); + // TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); auto reply_markup = nullptr; TRY_RESULT(input_message_contents, get_input_message_contents(query.get(), "media")); @@ -8378,7 +8381,7 @@ td::Status Client::process_edit_message_text_query(PromisedQueryPtr &query) { TRY_RESULT(input_message_text, get_input_message_text(query.get())); auto chat_id = query->arg("chat_id"); auto message_id = get_message_id(query.get()); - TRY_RESULT(reply_markup, get_reply_markup(query.get())); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); if (chat_id.empty() && message_id == 0) { TRY_RESULT(inline_message_id, get_inline_message_id(query.get())); @@ -8417,7 +8420,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())); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); if (chat_id.empty() && message_id == 0) { TRY_RESULT(inline_message_id, get_inline_message_id(query.get())); @@ -8452,7 +8455,7 @@ td::Status Client::process_edit_message_live_location_query(PromisedQueryPtr &qu 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())); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); TRY_RESULT(input_media, get_input_media(query.get(), "media")); if (chat_id.empty() && message_id == 0) { @@ -8486,7 +8489,7 @@ td::Status Client::process_edit_message_media_query(PromisedQueryPtr &query) { 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())); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); TRY_RESULT(caption, get_caption(query.get())); if (chat_id.empty() && message_id == 0) { @@ -8519,7 +8522,7 @@ td::Status Client::process_edit_message_caption_query(PromisedQueryPtr &query) { td::Status Client::process_edit_message_reply_markup_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())); + TRY_RESULT(reply_markup, get_reply_markup(query.get(), bot_user_ids_)); if (chat_id.empty() && message_id == 0) { TRY_RESULT(inline_message_id, get_inline_message_id(query.get())); @@ -8639,7 +8642,7 @@ td::Status Client::process_get_game_high_scores_query(PromisedQueryPtr &query) { td::Status Client::process_answer_web_app_query_query(PromisedQueryPtr &query) { auto web_app_query_id = query->arg("web_app_query_id"); - TRY_RESULT(result, get_inline_query_result(query.get())); + TRY_RESULT(result, get_inline_query_result(query.get(), bot_user_ids_)); td::vector> results; results.push_back(std::move(result)); @@ -8668,7 +8671,7 @@ td::Status Client::process_answer_inline_query_query(PromisedQueryPtr &query) { make_object(query->arg("switch_pm_parameter").str())); } } - TRY_RESULT(results, get_inline_query_results(query.get())); + TRY_RESULT(results, get_inline_query_results(query.get(), bot_user_ids_)); resolve_inline_query_results_bot_usernames( std::move(results), std::move(query), @@ -10032,7 +10035,7 @@ void Client::do_send_message(object_ptr input_messa auto allow_sending_without_reply = to_bool(query->arg("allow_sending_without_reply")); auto disable_notification = to_bool(query->arg("disable_notification")); auto protect_content = to_bool(query->arg("protect_content")); - auto r_reply_markup = get_reply_markup(query.get()); + auto r_reply_markup = get_reply_markup(query.get(), bot_user_ids_); if (r_reply_markup.is_error()) { return fail_query_with_error(std::move(query), 400, r_reply_markup.error().message()); } diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index d0abc78..07e01c1 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -238,6 +238,7 @@ class Client final : public WebhookActor::Callback { struct UserInfo; struct ChatInfo; struct BotCommandScope; + struct BotUserIds; enum class AccessRights { Read, ReadMembers, Edit, Write }; @@ -332,11 +333,12 @@ class Client final : public WebhookActor::Callback { static td::Result> get_keyboard_button(td::JsonValue &button); - td::Result> get_inline_keyboard_button(td::JsonValue &button); + static td::Result> get_inline_keyboard_button(td::JsonValue &button, + BotUserIds &bot_user_ids); - td::Result> get_reply_markup(const Query *query); + static td::Result> get_reply_markup(const Query *query, BotUserIds &bot_user_ids); - td::Result> get_reply_markup(td::JsonValue &&value); + static td::Result> get_reply_markup(td::JsonValue &&value, BotUserIds &bot_user_ids); static td::Result> get_labeled_price_part(td::JsonValue &value); @@ -370,13 +372,17 @@ class Client final : public WebhookActor::Callback { static td::Result> get_inline_query_results_button( td::MutableSlice value); - td::Result> get_inline_query_result(const Query *query); + static td::Result> get_inline_query_result(const Query *query, + BotUserIds &bot_user_ids); - td::Result> get_inline_query_result(td::JsonValue &&value); + static td::Result> get_inline_query_result(td::JsonValue &&value, + BotUserIds &bot_user_ids); - td::Result>> get_inline_query_results(const Query *query); + static td::Result>> get_inline_query_results( + const Query *query, BotUserIds &bot_user_ids); - td::Result>> get_inline_query_results(td::JsonValue &&value); + static td::Result>> get_inline_query_results( + td::JsonValue &&value, BotUserIds &bot_user_ids); struct BotCommandScope { object_ptr scope_; @@ -1076,11 +1082,13 @@ class Client final : public WebhookActor::Callback { td::WaitFreeHashMap sticker_set_names_; - int64 cur_temp_bot_user_id_ = 1; - td::FlatHashMap bot_user_ids_; - td::FlatHashSet unresolved_bot_usernames_; - td::FlatHashMap temp_to_real_bot_user_id_; - td::FlatHashMap> awaiting_bot_resolve_queries_; + struct BotUserIds { + int64 default_bot_user_id_ = 0; + int64 cur_temp_bot_user_id_ = 1; + td::FlatHashMap bot_user_ids_; + td::FlatHashSet unresolved_bot_usernames_; + }; + BotUserIds bot_user_ids_; struct PendingBotResolveQuery { std::size_t pending_resolve_count = 0; @@ -1090,6 +1098,9 @@ class Client final : public WebhookActor::Callback { td::FlatHashMap pending_bot_resolve_queries_; int64 current_bot_resolve_query_id_ = 1; + td::FlatHashMap> awaiting_bot_resolve_queries_; + td::FlatHashMap temp_to_real_bot_user_id_; + td::string dir_; td::ActorOwn td_client_; td::ActorContext context_; From a9a0140476fae8edd4741f2a85dc3148b15ec679 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 6 Jul 2023 14:47:31 +0300 Subject: [PATCH 03/45] Keep last time when a file was uploaded. --- telegram-bot-api/Client.cpp | 59 ++++++++++++++++++++++++++++++++++++- telegram-bot-api/Client.h | 7 ++++- telegram-bot-api/Query.h | 12 ++++++-- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index bcb1bd3..ca01e24 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -10028,7 +10028,64 @@ void Client::finish_set_webhook(PromisedQueryPtr query) { webhook_set_query_ = std::move(query); } -void Client::do_send_message(object_ptr input_message_content, PromisedQueryPtr query) { +void Client::delete_last_send_message_time(td::int64 file_size, double max_delay) { + auto last_send_message_time = last_send_message_time_.get(file_size); + if (last_send_message_time == 0.0) { + return; + } + if (last_send_message_time < td::Time::now() - max_delay) { + LOG(DEBUG) << "Clear last send message time for size " << file_size; + last_send_message_time_.erase(file_size); + } +} + +void Client::do_send_message(object_ptr input_message_content, PromisedQueryPtr query, + bool force) { + if (!parameters_->local_mode_) { + if (!force) { + auto file_size = query->files_size(); + if (file_size > 100000) { + auto &last_send_message_time = last_send_message_time_[file_size]; + auto now = td::Time::now(); + auto min_delay = td::clamp(static_cast(file_size) * 1e-7, 0.1, 0.5); + auto max_bucket_volume = 1.0; + if (last_send_message_time > now + 5.0) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + + last_send_message_time = td::max(last_send_message_time + min_delay, now - max_bucket_volume); + LOG(DEBUG) << "Query with files of size " << file_size << " can be processed in " + << last_send_message_time - now << " seconds"; + + td::create_actor( + "DeleteLastSendMessageTimeSleepActor", last_send_message_time + min_delay - (now - max_bucket_volume), + td::PromiseCreator::lambda([actor_id = actor_id(this), file_size, + max_delay = max_bucket_volume + min_delay](td::Result) mutable { + send_closure(actor_id, &Client::delete_last_send_message_time, file_size, max_delay); + })) + .release(); + + if (last_send_message_time > now) { + td::create_actor( + "DoSendMessageSleepActor", last_send_message_time - now, + td::PromiseCreator::lambda([actor_id = actor_id(this), + input_message_content = std::move(input_message_content), + query = std::move(query)](td::Result) mutable { + send_closure(actor_id, &Client::do_send_message, std::move(input_message_content), std::move(query), + true); + })) + .release(); + return; + } + } + } else { + if (logging_out_ || closing_) { + return fail_query_closing(std::move(query)); + } + CHECK(was_authorized_); + } + } + auto chat_id = query->arg("chat_id"); auto message_thread_id = get_message_id(query.get(), "message_thread_id"); auto reply_to_message_id = get_message_id(query.get(), "reply_to_message_id"); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 07e01c1..e31c45a 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -639,7 +639,10 @@ class Client final : public WebhookActor::Callback { void on_webhook_closed(td::Status status); - void do_send_message(object_ptr input_message_content, PromisedQueryPtr query); + void delete_last_send_message_time(td::int64 file_size, double max_delay); + + void do_send_message(object_ptr input_message_content, PromisedQueryPtr query, + bool force = false); int64 get_send_message_query_id(PromisedQueryPtr query, bool is_multisend); @@ -1082,6 +1085,8 @@ class Client final : public WebhookActor::Callback { td::WaitFreeHashMap sticker_set_names_; + td::WaitFreeHashMap last_send_message_time_; + struct BotUserIds { int64 default_bot_user_id_ = 0; int64 cur_temp_bot_user_id_ = 1; diff --git a/telegram-bot-api/Query.h b/telegram-bot-api/Query.h index 0b7b51e..5044ce0 100644 --- a/telegram-bot-api/Query.h +++ b/telegram-bot-api/Query.h @@ -38,38 +38,48 @@ class Query final : public td::ListNode { td::Slice token() const { return token_; } + bool is_test_dc() const { return is_test_dc_; } + td::Slice method() const { return method_; } + bool has_arg(td::Slice key) const { auto it = std::find_if(args_.begin(), args_.end(), [&key](const std::pair &s) { return s.first == key; }); return it != args_.end(); } + td::MutableSlice arg(td::Slice key) const { auto it = std::find_if(args_.begin(), args_.end(), [&key](const std::pair &s) { return s.first == key; }); return it == args_.end() ? td::MutableSlice() : it->second; } + const td::vector> &args() const { return args_; } + td::Slice get_header(td::Slice key) const { auto it = std::find_if(headers_.begin(), headers_.end(), [&key](const std::pair &s) { return s.first == key; }); return it == headers_.end() ? td::Slice() : it->second; } + const td::HttpFile *file(td::Slice key) const { auto it = std::find_if(files_.begin(), files_.end(), [&key](const td::HttpFile &f) { return f.field_name == key; }); return it == files_.end() ? nullptr : &*it; } + const td::vector &files() const { return files_; } + td::int64 files_size() const; + td::string get_peer_ip_address() const; td::BufferSlice &answer() { @@ -152,8 +162,6 @@ class Query final : public td::ListNode { td::int64 query_size() const; - td::int64 files_size() const; - td::int64 files_max_size() const; void send_request_stat() const; From c8e50b8011b76d1f6478bb2441dd6bd696b191f9 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 19 Jul 2023 23:54:47 +0300 Subject: [PATCH 04/45] Improve replies handling. --- telegram-bot-api/Client.cpp | 36 +++++++++++++++++++++++------------- telegram-bot-api/Client.h | 6 +++++- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index ca01e24..f9fd04c 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -4384,12 +4384,12 @@ void Client::on_get_reply_message(int64 chat_id, object_ptr rep CHECK(!queue.queue_.empty()); object_ptr &message = queue.queue_.front().message; CHECK(chat_id == message->chat_id_); - int64 &reply_to_message_id = get_reply_to_message_id(message); + int64 reply_to_message_id = get_reply_to_message_id(message); CHECK(reply_to_message_id > 0); if (reply_to_message == nullptr) { LOG(INFO) << "Can't find message " << reply_to_message_id << " in chat " << chat_id << ". It is already deleted or inaccessible because of the chosen privacy mode"; - reply_to_message_id = 0; + drop_reply_to_message_id(message); } else { CHECK(chat_id == reply_to_message->chat_id_); CHECK(reply_to_message_id == reply_to_message->id_); @@ -11213,18 +11213,29 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr &message) { +td::int64 Client::get_reply_to_message_id(const object_ptr &message) { if (message->content_->get_id() == td_api::messagePinMessage::ID) { CHECK(message->reply_to_message_id_ == 0); - return static_cast(message->content_.get())->message_id_; + return static_cast(message->content_.get())->message_id_; } + return message->reply_to_message_id_; +} + +void Client::drop_reply_to_message_id(object_ptr &message) { + if (message->content_->get_id() == td_api::messagePinMessage::ID) { + static_cast(message->content_.get())->message_id_ = 0; + return; + } + message->reply_to_message_id_ = 0; +} + +void Client::drop_reply_to_message_in_another_chat(object_ptr &message) { if (message->reply_in_chat_id_ != message->chat_id_ && message->reply_to_message_id_ != 0) { - LOG(WARNING) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ - << " from another chat " << message->reply_in_chat_id_; + LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ << " from another chat " + << message->reply_in_chat_id_; message->reply_in_chat_id_ = 0; message->reply_to_message_id_ = 0; } - return message->reply_to_message_id_; } void Client::set_message_reply_to_message_id(MessageInfo *message_info, int64 reply_to_message_id) { @@ -11412,6 +11423,9 @@ void Client::process_new_message_queue(int64 chat_id, int state) { auto &message_ref = queue.queue_.front().message; CHECK(chat_id == message_ref->chat_id_); int64 message_id = message_ref->id_; + + drop_reply_to_message_in_another_chat(message_ref); + int64 reply_to_message_id = get_reply_to_message_id(message_ref); if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, state > 0) == nullptr) { queue.has_active_request_ = true; @@ -11664,12 +11678,8 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->is_topic_message = message->is_topic_message_; message_info->author_signature = std::move(message->author_signature_); - if (message->reply_in_chat_id_ != chat_id && message->reply_to_message_id_ != 0) { - LOG(WARNING) << "Drop reply to message " << message_id << " in chat " << chat_id << " from another chat " - << message->reply_in_chat_id_; - message->reply_in_chat_id_ = 0; - message->reply_to_message_id_ = 0; - } + drop_reply_to_message_in_another_chat(message); + set_message_reply_to_message_id(message_info.get(), message->reply_to_message_id_); if (message_info->content == nullptr || force_update_content) { message_info->content = std::move(message->content_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index e31c45a..0e18508 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -832,7 +832,11 @@ class Client final : public WebhookActor::Callback { mutable bool is_content_changed = false; }; - static int64 &get_reply_to_message_id(object_ptr &message); + static int64 get_reply_to_message_id(const object_ptr &message); + + static void drop_reply_to_message_id(object_ptr &message); + + static void drop_reply_to_message_in_another_chat(object_ptr &message); void set_message_reply_to_message_id(MessageInfo *message_info, int64 reply_to_message_id); From 68dc4f54a51790829196905c772fac851a0d8aae Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 20 Jul 2023 16:25:42 +0300 Subject: [PATCH 05/45] Don't track replies by yet unsent messages. --- telegram-bot-api/Client.cpp | 57 +++++++------------------------------ telegram-bot-api/Client.h | 7 +---- 2 files changed, 11 insertions(+), 53 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index f9fd04c..032c0b2 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -194,8 +194,7 @@ Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_te Client::~Client() { td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), messages_, users_, groups_, - supergroups_, chats_, reply_message_ids_, - yet_unsent_reply_message_ids_, sticker_set_names_); + supergroups_, chats_, reply_message_ids_, sticker_set_names_); } bool Client::init_methods() { @@ -1952,13 +1951,13 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } object("forward_date", message_->initial_send_date); } - if (message_->reply_to_message_id > 0 && need_reply_ && !message_->is_reply_to_message_deleted) { + if (message_->reply_to_message_id > 0 && need_reply_) { const MessageInfo *reply_to_message = client_->get_message(message_->chat_id, message_->reply_to_message_id, true); if (reply_to_message != nullptr) { object("reply_to_message", JsonMessage(reply_to_message, false, "reply in " + source_, client_)); } else { - LOG(WARNING) << "Replied to unknown or deleted message " << message_->reply_to_message_id << " in chat " - << message_->chat_id << " while storing " << source_ << " " << message_->id; + LOG(INFO) << "Replied to unknown or deleted message " << message_->reply_to_message_id << " in chat " + << message_->chat_id << " while storing " << source_ << ' ' << message_->id; } } if (message_->media_album_id != 0) { @@ -4432,7 +4431,7 @@ void Client::on_get_callback_query_message(object_ptr message, } LOG(INFO) << "Can't find callback query reply to message " << message_info->reply_to_message_id << " in chat " << chat_id << ". It may be already deleted"; - message_info->is_reply_to_message_deleted = true; + message_info->reply_to_message_id = 0; } } else { LOG(INFO) << "Receive callback query " << (state == 1 ? "reply to " : "") << "message " << message_id << " in chat " @@ -7741,31 +7740,16 @@ void Client::decrease_yet_unsent_message_count(int64 chat_id, int32 count) { } } -td::int64 Client::extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id, - bool *is_reply_to_message_deleted) { +td::int64 Client::extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id) { auto yet_unsent_message_it = yet_unsent_messages_.find({chat_id, message_id}); CHECK(yet_unsent_message_it != yet_unsent_messages_.end()); - auto reply_to_message_id = yet_unsent_message_it->second.reply_to_message_id; - if (is_reply_to_message_deleted != nullptr && yet_unsent_message_it->second.is_reply_to_message_deleted) { - *is_reply_to_message_deleted = true; - } auto query_id = yet_unsent_message_it->second.send_message_query_id; yet_unsent_messages_.erase(yet_unsent_message_it); decrease_yet_unsent_message_count(chat_id, 1); - if (reply_to_message_id > 0) { - auto it = yet_unsent_reply_message_ids_.find({chat_id, reply_to_message_id}); - CHECK(it != yet_unsent_reply_message_ids_.end()); - auto erased_count = it->second.erase(message_id); - CHECK(erased_count > 0); - if (it->second.empty()) { - yet_unsent_reply_message_ids_.erase(it); - } - } - return query_id; } @@ -7780,8 +7764,7 @@ void Client::on_message_send_succeeded(object_ptr &&message, in CHECK(message_info != nullptr); message_info->is_content_changed = false; - auto query_id = - extract_yet_unsent_message_query_id(chat_id, old_message_id, &message_info->is_reply_to_message_deleted); + auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id); auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { query.messages.push_back(td::json_encode(JsonMessage(message_info, true, "sent message", this))); @@ -7809,7 +7792,7 @@ void Client::on_message_send_succeeded(object_ptr &&message, in void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, td::Status result) { auto error = make_object(result.code(), result.message().str()); - auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id, nullptr); + auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id); auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { if (query.error == nullptr || query.error->message_ == "Group send failed") { @@ -10145,16 +10128,9 @@ void Client::on_sent_message(object_ptr &&message, int64 query_ CHECK(message != nullptr); int64 chat_id = message->chat_id_; int64 message_id = message->id_; - int64 reply_to_message_id = message->reply_to_message_id_; - if (reply_to_message_id > 0) { - CHECK(message->reply_in_chat_id_ == chat_id); - bool is_inserted = yet_unsent_reply_message_ids_[{chat_id, reply_to_message_id}].insert(message_id).second; - CHECK(is_inserted); - } FullMessageId yet_unsent_message_id{chat_id, message_id}; YetUnsentMessage yet_unsent_message; - yet_unsent_message.reply_to_message_id = reply_to_message_id; yet_unsent_message.send_message_query_id = query_id; auto emplace_result = yet_unsent_messages_.emplace(yet_unsent_message_id, yet_unsent_message); CHECK(emplace_result.second); @@ -10964,8 +10940,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { state = 1; } if (state == 1) { - auto reply_to_message_id = - message_info == nullptr || message_info->is_reply_to_message_deleted ? 0 : message_info->reply_to_message_id; + auto reply_to_message_id = message_info == nullptr ? 0 : message_info->reply_to_message_id; if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, false) == nullptr) { queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id), @@ -10980,8 +10955,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { return send_request(make_object(message_sticker_set_id), td::make_unique(this, message_sticker_set_id, user_id, 0)); } - auto reply_to_message_id = - message_info == nullptr || message_info->is_reply_to_message_deleted ? 0 : message_info->reply_to_message_id; + auto reply_to_message_id = message_info == nullptr ? 0 : message_info->reply_to_message_id; if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id, true); auto reply_sticker_set_id = @@ -11514,17 +11488,6 @@ void Client::process_new_message_queue(int64 chat_id, int state) { } void Client::remove_replies_to_message(int64 chat_id, int64 reply_to_message_id, bool only_from_cache) { - if (!only_from_cache) { - auto yet_unsent_it = yet_unsent_reply_message_ids_.find({chat_id, reply_to_message_id}); - if (yet_unsent_it != yet_unsent_reply_message_ids_.end()) { - for (auto message_id : yet_unsent_it->second) { - auto &message = yet_unsent_messages_[{chat_id, message_id}]; - CHECK(message.reply_to_message_id == reply_to_message_id); - message.is_reply_to_message_deleted = true; - } - } - } - auto it = reply_message_ids_.find({chat_id, reply_to_message_id}); if (it == reply_message_ids_.end()) { return; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 0e18508..682578d 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -500,7 +500,7 @@ class Client final : public WebhookActor::Callback { void decrease_yet_unsent_message_count(int64 chat_id, int32 count); - int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id, bool *is_reply_to_message_deleted); + int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id); void on_message_send_succeeded(object_ptr &&message, int64 old_message_id); void on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, td::Status result); @@ -828,7 +828,6 @@ class Client final : public WebhookActor::Callback { bool can_be_saved = false; bool is_automatic_forward = false; bool is_topic_message = false; - mutable bool is_reply_to_message_deleted = false; mutable bool is_content_changed = false; }; @@ -1040,15 +1039,11 @@ class Client final : public WebhookActor::Callback { td::FlatHashMap, FullMessageIdHash> reply_message_ids_; // message -> replies to it - td::FlatHashMap, FullMessageIdHash> - yet_unsent_reply_message_ids_; // message -> replies to it td::FlatHashMap> file_download_listeners_; td::FlatHashSet download_started_file_ids_; struct YetUnsentMessage { - int64 reply_to_message_id = 0; - bool is_reply_to_message_deleted = false; int64 send_message_query_id = 0; }; td::FlatHashMap yet_unsent_messages_; From 9ce2f7df4cb83e22f0e667426ac313c4a7058812 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 20 Jul 2023 16:30:46 +0300 Subject: [PATCH 06/45] Don't drop replies to deleted messages. --- telegram-bot-api/Client.cpp | 50 +++---------------------------------- telegram-bot-api/Client.h | 7 ------ 2 files changed, 3 insertions(+), 54 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 032c0b2..ac4e39b 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -194,7 +194,7 @@ Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_te Client::~Client() { td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), messages_, users_, groups_, - supergroups_, chats_, reply_message_ids_, sticker_set_names_); + supergroups_, chats_, sticker_set_names_); } bool Client::init_methods() { @@ -4431,7 +4431,6 @@ void Client::on_get_callback_query_message(object_ptr message, } LOG(INFO) << "Can't find callback query reply to message " << message_info->reply_to_message_id << " in chat " << chat_id << ". It may be already deleted"; - message_info->reply_to_message_id = 0; } } else { LOG(INFO) << "Receive callback query " << (state == 1 ? "reply to " : "") << "message " << message_id << " in chat " @@ -11212,30 +11211,6 @@ void Client::drop_reply_to_message_in_another_chat(object_ptr & } } -void Client::set_message_reply_to_message_id(MessageInfo *message_info, int64 reply_to_message_id) { - if (message_info->reply_to_message_id == reply_to_message_id) { - return; - } - - if (message_info->reply_to_message_id > 0) { - LOG_IF(ERROR, reply_to_message_id > 0) - << "Message " << message_info->id << " in chat " << message_info->chat_id - << " has changed reply_to_message from " << message_info->reply_to_message_id << " to " << reply_to_message_id; - auto it = reply_message_ids_.find({message_info->chat_id, message_info->reply_to_message_id}); - if (it != reply_message_ids_.end()) { - it->second.erase(message_info->id); - if (it->second.empty()) { - reply_message_ids_.erase(it); - } - } - } - if (reply_to_message_id > 0) { - reply_message_ids_[{message_info->chat_id, reply_to_message_id}].insert(message_info->id); - } - - message_info->reply_to_message_id = reply_to_message_id; -} - td::Slice Client::get_sticker_type(const object_ptr &type) { CHECK(type != nullptr); switch (type->get_id()) { @@ -11487,26 +11462,7 @@ void Client::process_new_message_queue(int64 chat_id, int state) { new_message_queues_.erase(chat_id); } -void Client::remove_replies_to_message(int64 chat_id, int64 reply_to_message_id, bool only_from_cache) { - auto it = reply_message_ids_.find({chat_id, reply_to_message_id}); - if (it == reply_message_ids_.end()) { - return; - } - - if (!only_from_cache) { - for (auto message_id : it->second) { - auto message_info = get_message_editable(chat_id, message_id); - CHECK(message_info != nullptr); - CHECK(message_info->reply_to_message_id == reply_to_message_id); - message_info->reply_to_message_id = 0; - } - } - reply_message_ids_.erase(it); -} - td::unique_ptr Client::delete_message(int64 chat_id, int64 message_id, bool only_from_cache) { - remove_replies_to_message(chat_id, message_id, only_from_cache); - auto message_info = std::move(messages_[{chat_id, message_id}]); if (message_info == nullptr) { if (yet_unsent_messages_.count({chat_id, message_id}) > 0) { @@ -11533,7 +11489,6 @@ td::unique_ptr Client::delete_message(int64 chat_id, int64 on_message_send_failed(chat_id, message_id, 0, std::move(error)); } } else { - set_message_reply_to_message_id(message_info.get(), 0); messages_.erase({chat_id, message_id}); } return message_info; @@ -11643,7 +11598,8 @@ Client::FullMessageId Client::add_message(object_ptr &&message, drop_reply_to_message_in_another_chat(message); - set_message_reply_to_message_id(message_info.get(), message->reply_to_message_id_); + message_info->reply_to_message_id = message->reply_to_message_id_; + if (message_info->content == nullptr || force_update_content) { message_info->content = std::move(message->content_); message_info->is_content_changed = true; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 682578d..3a2c278 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -837,8 +837,6 @@ class Client final : public WebhookActor::Callback { static void drop_reply_to_message_in_another_chat(object_ptr &message); - void set_message_reply_to_message_id(MessageInfo *message_info, int64 reply_to_message_id); - static td::Slice get_sticker_type(const object_ptr &type); static td::Result> get_sticker_type(td::Slice type); @@ -875,8 +873,6 @@ class Client final : public WebhookActor::Callback { static void json_store_permissions(td::JsonObjectScope &object, const td_api::chatPermissions *permissions); - void remove_replies_to_message(int64 chat_id, int64 reply_to_message_id, bool only_from_cache); - td::unique_ptr delete_message(int64 chat_id, int64 message_id, bool only_from_cache); void add_new_message(object_ptr &&message, bool is_edited); @@ -1037,9 +1033,6 @@ class Client final : public WebhookActor::Callback { td::WaitFreeHashMap> supergroups_; td::WaitFreeHashMap> chats_; - td::FlatHashMap, FullMessageIdHash> - reply_message_ids_; // message -> replies to it - td::FlatHashMap> file_download_listeners_; td::FlatHashSet download_started_file_ids_; From 1fa5c2c31af605ef1e00edf0471c182d385850a8 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 20 Jul 2023 16:58:10 +0300 Subject: [PATCH 07/45] Improve processing of new messages. --- telegram-bot-api/Client.cpp | 45 +++++++++++++++++-------------------- telegram-bot-api/Client.h | 2 -- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index ac4e39b..c34d11f 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -861,7 +861,7 @@ class Client::JsonChat final : public td::Jsonable { if (pinned_message != nullptr) { object("pinned_message", JsonMessage(pinned_message, false, "pin in JsonChat", client_)); } else { - LOG(ERROR) << "Pinned unknown, inaccessible or deleted message " << pinned_message_id_; + LOG(INFO) << "Pinned unknown, inaccessible or deleted message " << pinned_message_id_; } } if (chat_info->message_auto_delete_time != 0) { @@ -2174,8 +2174,8 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { const MessageInfo *pinned_message = client_->get_message(message_->chat_id, message_id, true); if (pinned_message != nullptr) { object("pinned_message", JsonMessage(pinned_message, false, "pin in " + source_, client_)); - } else { - LOG_IF(ERROR, need_reply_) << "Pinned unknown, inaccessible or deleted message " << message_id; + } else if (need_reply_) { + LOG(INFO) << "Pinned unknown, inaccessible or deleted message " << message_id; } } break; @@ -4388,7 +4388,6 @@ void Client::on_get_reply_message(int64 chat_id, object_ptr rep if (reply_to_message == nullptr) { LOG(INFO) << "Can't find message " << reply_to_message_id << " in chat " << chat_id << ". It is already deleted or inaccessible because of the chosen privacy mode"; - drop_reply_to_message_id(message); } else { CHECK(chat_id == reply_to_message->chat_id_); CHECK(reply_to_message_id == reply_to_message->id_); @@ -4422,7 +4421,7 @@ void Client::on_get_callback_query_message(object_ptr message, << ". It may be already deleted"; } else { CHECK(state == 1); - auto message_info = get_message_editable(chat_id, message_id); + auto message_info = get_message(chat_id, message_id, true); if (message_info == nullptr) { LOG(INFO) << "Can't find callback query message " << message_id << " in chat " << chat_id << ". It may be already deleted, while searcing for its reply to message"; @@ -10975,6 +10974,7 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { 150, user_id + (static_cast(3) << 33)); queue.queue_.pop(); + state = 0; } new_callback_query_queues_.erase(user_id); } @@ -11134,7 +11134,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr &mes return message->reply_to_message_id_; } -void Client::drop_reply_to_message_id(object_ptr &message) { - if (message->content_->get_id() == td_api::messagePinMessage::ID) { - static_cast(message->content_.get())->message_id_ = 0; - return; - } - message->reply_to_message_id_ = 0; -} - void Client::drop_reply_to_message_in_another_chat(object_ptr &message) { if (message->reply_in_chat_id_ != message->chat_id_ && message->reply_to_message_id_ != 0) { LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ << " from another chat " @@ -11376,10 +11368,13 @@ void Client::process_new_message_queue(int64 chat_id, int state) { drop_reply_to_message_in_another_chat(message_ref); int64 reply_to_message_id = get_reply_to_message_id(message_ref); - if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, state > 0) == nullptr) { - queue.has_active_request_ = true; - return send_request(make_object(chat_id, message_id), - td::make_unique(this, chat_id)); + if (state == 0) { + if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, false) == nullptr) { + queue.has_active_request_ = true; + return send_request(make_object(chat_id, message_id), + td::make_unique(this, chat_id)); + } + state = 1; } auto message_sticker_set_id = get_sticker_set_id(message_ref->content_); if (!have_sticker_set_name(message_sticker_set_id)) { @@ -11389,18 +11384,20 @@ void Client::process_new_message_queue(int64 chat_id, int state) { } if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id, true); - CHECK(reply_to_message_info != nullptr); - auto reply_sticker_set_id = get_sticker_set_id(reply_to_message_info->content); - if (!have_sticker_set_name(reply_sticker_set_id)) { - queue.has_active_request_ = true; - return send_request(make_object(reply_sticker_set_id), - td::make_unique(this, reply_sticker_set_id, 0, chat_id)); + if (reply_to_message_info != nullptr) { + auto reply_sticker_set_id = get_sticker_set_id(reply_to_message_info->content); + if (!have_sticker_set_name(reply_sticker_set_id)) { + queue.has_active_request_ = true; + return send_request(make_object(reply_sticker_set_id), + td::make_unique(this, reply_sticker_set_id, 0, chat_id)); + } } } auto message = std::move(message_ref); auto is_edited = queue.queue_.front().is_edited; queue.queue_.pop(); + state = 0; if (need_skip_update_message(chat_id, message, is_edited)) { add_message(std::move(message)); continue; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 3a2c278..e6fb8fb 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -833,8 +833,6 @@ class Client final : public WebhookActor::Callback { static int64 get_reply_to_message_id(const object_ptr &message); - static void drop_reply_to_message_id(object_ptr &message); - static void drop_reply_to_message_in_another_chat(object_ptr &message); static td::Slice get_sticker_type(const object_ptr &type); From 736411c1134bca6f61b5e456f7a429ff87c33afc Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 20 Jul 2023 17:06:32 +0300 Subject: [PATCH 08/45] Update TDLib to 1.8.15 and support votes by chats in polls. --- td | 2 +- telegram-bot-api/Client.cpp | 87 ++++++++++++++++++++++++------- telegram-bot-api/Client.h | 2 + telegram-bot-api/WebhookActor.cpp | 2 +- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/td b/td index 328b864..5388843 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 328b8649d859c5ed4088a875cbb059db6029dc0d +Subproject commit 53888437cf11aca258aae7e76552a38c1750d6e7 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index c34d11f..b7d8ea7 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1408,7 +1408,21 @@ class Client::JsonPollAnswer final : public td::Jsonable { void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); object("poll_id", td::to_string(poll_answer_->poll_id_)); - object("user", JsonUser(poll_answer_->user_id_, client_)); + switch (poll_answer_->voter_id_->get_id()) { + case td_api::messageSenderUser::ID: { + auto user_id = static_cast(poll_answer_->voter_id_.get())->user_id_; + object("user", JsonUser(user_id, client_)); + break; + } + case td_api::messageSenderChat::ID: { + auto voter_chat_id = static_cast(poll_answer_->voter_id_.get())->chat_id_; + object("user", JsonUser(client_->channel_bot_user_id_, client_)); + object("voter_chat", JsonChat(voter_chat_id, false, client_)); + break; + } + default: + UNREACHABLE(); + } object("option_ids", td::json_array(poll_answer_->option_ids_, [](int32 option_id) { return option_id; })); } @@ -2287,6 +2301,8 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } case td_api::messageChatSetBackground::ID: break; + case td_api::messageStory::ID: + break; default: UNREACHABLE(); } @@ -5494,6 +5510,13 @@ bool Client::to_bool(td::MutableSlice value) { return value == "true" || value == "yes" || value == "1"; } +td_api::object_ptr Client::get_message_reply_to(int64 reply_to_message_id) { + if (reply_to_message_id > 0) { + return make_object(0, reply_to_message_id); + } + return nullptr; +} + td::Result> Client::get_keyboard_button(td::JsonValue &button) { if (button.type() == td::JsonValue::Type::Object) { auto &object = button.get_object(); @@ -5730,7 +5753,7 @@ td::Result> Client::get_reply_markup(td: if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "Object expected as reply markup"); } - for (auto &field_value : value.get_object()) { + for (auto &field_value : value.get_object().field_values_) { if (field_value.first == "keyboard") { auto keyboard = std::move(field_value.second); if (keyboard.type() != td::JsonValue::Type::Array) { @@ -8327,11 +8350,11 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { auto message_count = input_message_contents.size(); count += static_cast(message_count); - send_request( - make_object(chat_id, message_thread_id, reply_to_message_id, - get_message_send_options(disable_notification, protect_content), - std::move(input_message_contents), false), - td::make_unique(this, chat_id, message_count, std::move(query))); + send_request(make_object( + chat_id, message_thread_id, get_message_reply_to(reply_to_message_id), + get_message_send_options(disable_notification, protect_content), + std::move(input_message_contents), false), + td::make_unique(this, chat_id, message_count, std::move(query))); }; check_message_thread(chat_id, message_thread_id, reply_to_message_id, std::move(query), std::move(on_message_thread_checked)); @@ -10098,11 +10121,11 @@ void Client::do_send_message(object_ptr input_messa } count++; - send_request( - make_object(chat_id, message_thread_id, reply_to_message_id, - get_message_send_options(disable_notification, protect_content), - std::move(reply_markup), std::move(input_message_content)), - td::make_unique(this, chat_id, std::move(query))); + send_request(make_object( + chat_id, message_thread_id, get_message_reply_to(reply_to_message_id), + get_message_send_options(disable_notification, protect_content), + std::move(reply_markup), std::move(input_message_content)), + td::make_unique(this, chat_id, std::move(query))); }; check_message_thread(chat_id, message_thread_id, reply_to_message_id, std::move(query), std::move(on_message_thread_checked)); @@ -11172,6 +11195,8 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr &message) { if (message->content_->get_id() == td_api::messagePinMessage::ID) { - CHECK(message->reply_to_message_id_ == 0); + CHECK(message->reply_to_ == nullptr); return static_cast(message->content_.get())->message_id_; } - return message->reply_to_message_id_; + if (message->reply_to_ != nullptr) { + switch (message->reply_to_->get_id()) { + case td_api::messageReplyToMessage::ID: { + auto reply_to = static_cast(message->reply_to_.get()); + CHECK(reply_to->message_id_ > 0); + CHECK(reply_to->chat_id_ == message->chat_id_); + return reply_to->message_id_; + } + case td_api::messageReplyToStory::ID: + break; + default: + UNREACHABLE(); + break; + } + } + return 0; } void Client::drop_reply_to_message_in_another_chat(object_ptr &message) { - if (message->reply_in_chat_id_ != message->chat_id_ && message->reply_to_message_id_ != 0) { - LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ << " from another chat " - << message->reply_in_chat_id_; - message->reply_in_chat_id_ = 0; - message->reply_to_message_id_ = 0; + if (message->reply_to_ != nullptr && message->reply_to_->get_id() == td_api::messageReplyToMessage::ID) { + auto reply_in_chat_id = static_cast(message->reply_to_.get())->chat_id_; + if (reply_in_chat_id != message->chat_id_) { + LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ + << " from another chat " << reply_in_chat_id; + message->reply_to_ = nullptr; + } } } @@ -11595,7 +11637,12 @@ Client::FullMessageId Client::add_message(object_ptr &&message, drop_reply_to_message_in_another_chat(message); - message_info->reply_to_message_id = message->reply_to_message_id_; + if (message->reply_to_ != nullptr && message->reply_to_->get_id() == td_api::messageReplyToMessage::ID) { + message_info->reply_to_message_id = + static_cast(message->reply_to_.get())->message_id_; + } else { + message_info->reply_to_message_id = 0; + } if (message_info->content == nullptr || force_update_content) { message_info->content = std::move(message->content_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index e6fb8fb..53eb449 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -331,6 +331,8 @@ class Client final : public WebhookActor::Callback { static bool to_bool(td::MutableSlice value); + static object_ptr get_message_reply_to(int64 reply_to_message_id); + static td::Result> get_keyboard_button(td::JsonValue &button); static td::Result> get_inline_keyboard_button(td::JsonValue &button, diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index 4f79c27..b07a867 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -73,7 +73,7 @@ WebhookActor::WebhookActor(td::ActorShared callback, td::int64 tqueue_ WebhookActor::~WebhookActor() { td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), update_map_, queue_updates_, - queues_); + queues_, ssl_ctx_); } void WebhookActor::relax_wakeup_at(double wakeup_at, const char *source) { From 51fba26f783160825231dbb286821887a0a80930 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 21 Jul 2023 13:33:00 +0300 Subject: [PATCH 09/45] Add Chat.emoji_status_expiration_date. --- telegram-bot-api/Client.cpp | 4 ++++ telegram-bot-api/Client.h | 1 + 2 files changed, 5 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index b7d8ea7..24345a6 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -713,6 +713,9 @@ class Client::JsonChat final : public td::Jsonable { } if (user_info->emoji_status_custom_emoji_id != 0) { object("emoji_status_custom_emoji_id", td::to_string(user_info->emoji_status_custom_emoji_id)); + if (user_info->emoji_status_expiration_date != 0) { + object("emoji_status_expiration_date", user_info->emoji_status_expiration_date); + } } if (!user_info->bio.empty()) { object("bio", user_info->bio); @@ -10380,6 +10383,7 @@ void Client::add_user(UserInfo *user_info, object_ptr &&user) { } user_info->language_code = std::move(user->language_code_); user_info->emoji_status_custom_emoji_id = user->emoji_status_ != nullptr ? user->emoji_status_->custom_emoji_id_ : 0; + user_info->emoji_status_expiration_date = user->emoji_status_ != nullptr ? user->emoji_status_->expiration_date_ : 0; user_info->have_access = user->have_access_; user_info->is_premium = user->is_premium_; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 53eb449..47d38e9 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -709,6 +709,7 @@ class Client final : public WebhookActor::Callback { td::string editable_username; td::string language_code; int64 emoji_status_custom_emoji_id; + int32 emoji_status_expiration_date; object_ptr photo; td::string bio; From afd30f2cfa2bee1cf61e7e2cea391b0e726cf81f Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 24 Jul 2023 16:14:03 +0300 Subject: [PATCH 10/45] Support messageStory as empty objects. --- telegram-bot-api/Client.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 24345a6..685b8ac 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -2305,6 +2305,7 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { case td_api::messageChatSetBackground::ID: break; case td_api::messageStory::ID: + object("story", JsonEmptyObject()); break; default: UNREACHABLE(); @@ -11199,8 +11200,6 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr Date: Mon, 24 Jul 2023 17:19:14 +0300 Subject: [PATCH 11/45] Add unpinAllGeneralForumTopicMessages. --- telegram-bot-api/Client.cpp | 11 +++++++++++ telegram-bot-api/Client.h | 1 + 2 files changed, 12 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 685b8ac..34623be 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -276,6 +276,7 @@ bool Client::init_methods() { methods_.emplace("reopengeneralforumtopic", &Client::process_reopen_general_forum_topic_query); methods_.emplace("hidegeneralforumtopic", &Client::process_hide_general_forum_topic_query); methods_.emplace("unhidegeneralforumtopic", &Client::process_unhide_general_forum_topic_query); + methods_.emplace("unpinallgeneralforumtopicmessages", &Client::process_unpin_all_general_forum_topic_messages_query); methods_.emplace("getchatmember", &Client::process_get_chat_member_query); methods_.emplace("getchatadministrators", &Client::process_get_chat_administrators_query); methods_.emplace("getchatmembercount", &Client::process_get_chat_member_count_query); @@ -9102,6 +9103,16 @@ td::Status Client::process_unhide_general_forum_topic_query(PromisedQueryPtr &qu return td::Status::OK(); } +td::Status Client::process_unpin_all_general_forum_topic_messages_query(PromisedQueryPtr &query) { + auto chat_id = query->arg("chat_id"); + + check_chat(chat_id, AccessRights::Write, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) { + send_request(make_object(chat_id, GENERAL_MESSAGE_THREAD_ID), + td::make_unique(std::move(query))); + }); + return td::Status::OK(); +} + td::Status Client::process_get_chat_member_query(PromisedQueryPtr &query) { auto chat_id = query->arg("chat_id"); TRY_RESULT(user_id, get_user_id(query.get())); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 47d38e9..467f6cb 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -590,6 +590,7 @@ class Client final : public WebhookActor::Callback { td::Status process_reopen_general_forum_topic_query(PromisedQueryPtr &query); td::Status process_hide_general_forum_topic_query(PromisedQueryPtr &query); td::Status process_unhide_general_forum_topic_query(PromisedQueryPtr &query); + td::Status process_unpin_all_general_forum_topic_messages_query(PromisedQueryPtr &query); td::Status process_get_chat_member_query(PromisedQueryPtr &query); td::Status process_get_chat_administrators_query(PromisedQueryPtr &query); td::Status process_get_chat_member_count_query(PromisedQueryPtr &query); From ec8e44de5a6b82f364cc508053965f48cf700171 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 24 Jul 2023 21:41:15 +0300 Subject: [PATCH 12/45] Improve warnings for old updates. --- telegram-bot-api/Client.cpp | 4 +++- telegram-bot-api/Client.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 34623be..7597b5e 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5015,6 +5015,7 @@ void Client::on_update_authorization_state() { td::reset_to_empty(pending_updates_); } last_update_creation_time_ = td::Time::now(); + log_in_date_ = get_unix_time(); } return loop(); } @@ -11488,7 +11489,8 @@ void Client::process_new_message_queue(int64 chat_id, int state) { auto now = get_unix_time(); auto update_delay_time = now - td::max(message_date, parameters_->shared_data_->get_unix_time(webhook_set_time_)); const auto UPDATE_DELAY_WARNING_TIME = 10 * 60; - if (update_delay_time > UPDATE_DELAY_WARNING_TIME && message_date > last_synchronization_error_date_ + 60) { + if (message_date > log_in_date_ && update_delay_time > UPDATE_DELAY_WARNING_TIME && + message_date > last_synchronization_error_date_ + 60) { if (delayed_update_count_ == 0) { delayed_update_type_ = update_type; delayed_chat_id_ = chat_id; diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 467f6cb..fa93505 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -1153,6 +1153,8 @@ class Client final : public WebhookActor::Callback { double previous_get_updates_finish_time_ = 0; double next_get_updates_conflict_time_ = 0; + int32 log_in_date_ = 0; + int32 flood_limited_query_count_ = 0; double next_flood_limit_warning_time_ = 0; From c927614964b56755b002d4a7323d87052a132a5d Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 25 Jul 2023 22:26:12 +0300 Subject: [PATCH 13/45] Improve threads usage. --- telegram-bot-api/ClientManager.cpp | 12 +++++------ telegram-bot-api/ClientParameters.h | 29 +++++++++++++++++++++++++++ telegram-bot-api/HttpServer.h | 9 +++------ telegram-bot-api/WebhookActor.cpp | 7 +++---- telegram-bot-api/WebhookActor.h | 1 - telegram-bot-api/telegram-bot-api.cpp | 25 +++++++++-------------- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 210d215..1bad19d 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -303,9 +303,6 @@ td::int64 ClientManager::get_tqueue_id(td::int64 user_id, bool is_test_dc) { } void ClientManager::start_up() { - //NB: the same scheduler as for database in Td - auto scheduler_id = 1; - // init tqueue { auto load_start_time = td::Time::now(); @@ -333,7 +330,8 @@ void ClientManager::start_up() { } } - auto concurrent_binlog = std::make_shared(std::move(binlog), scheduler_id); + auto concurrent_binlog = + std::make_shared(std::move(binlog), SharedData::get_database_scheduler_id()); auto concurrent_tqueue_binlog = td::make_unique>(); concurrent_tqueue_binlog->set_binlog(std::move(concurrent_binlog)); tqueue->set_callback(std::move(concurrent_tqueue_binlog)); @@ -348,7 +346,7 @@ void ClientManager::start_up() { // init webhook_db auto concurrent_webhook_db = td::make_unique>(); auto status = concurrent_webhook_db->init(parameters_->working_directory_ + "webhooks_db.binlog", td::DbKey::empty(), - scheduler_id); + SharedData::get_database_scheduler_id()); LOG_IF(FATAL, status.is_error()) << "Can't open webhooks_db.binlog " << status; parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db); @@ -365,8 +363,8 @@ void ClientManager::start_up() { } // launch watchdog - watchdog_id_ = td::create_actor_on_scheduler( - "ManagerWatchdog", td::Scheduler::instance()->sched_count() - 3, td::this_thread::get_id(), WATCHDOG_TIMEOUT); + watchdog_id_ = td::create_actor_on_scheduler("ManagerWatchdog", SharedData::get_watchdog_scheduler_id(), + td::this_thread::get_id(), WATCHDOG_TIMEOUT); set_timeout_in(600.0); } diff --git a/telegram-bot-api/ClientParameters.h b/telegram-bot-api/ClientParameters.h index 6956388..b233138 100644 --- a/telegram-bot-api/ClientParameters.h +++ b/telegram-bot-api/ClientParameters.h @@ -62,6 +62,35 @@ struct SharedData { // the same scheduler as for file GC in Td return 2; } + + static td::int32 get_client_scheduler_id() { + // the thread for ClientManager and all Clients + return 4; + } + + static td::int32 get_watchdog_scheduler_id() { + // the thread for watchdogs + return 5; + } + + static td::int32 get_slow_incoming_http_scheduler_id() { + // the thread for slow incoming HTTP connections + return 6; + } + + static td::int32 get_slow_outgoing_http_scheduler_id() { + // the thread for slow outgoing HTTP connections + return 7; + } + + static td::int32 get_dns_resolver_scheduler_id() { + // the thread for DNS resolving + return 8; + } + + static td::int32 get_thread_count() { + return 9; + } }; struct ClientParameters { diff --git a/telegram-bot-api/HttpServer.h b/telegram-bot-api/HttpServer.h index 8686a3f..c2398ee 100644 --- a/telegram-bot-api/HttpServer.h +++ b/telegram-bot-api/HttpServer.h @@ -6,6 +6,8 @@ // #pragma once +#include "telegram-bot-api/ClientParameters.h" + #include "td/net/HttpInboundConnection.h" #include "td/net/TcpListener.h" @@ -61,13 +63,8 @@ class HttpServer final : public td::TcpListener::Callback { } void accept(td::SocketFd fd) final { - auto scheduler_count = td::Scheduler::instance()->sched_count(); - auto scheduler_id = scheduler_count - 1; - if (scheduler_id > 0) { - scheduler_id--; - } td::create_actor("HttpInboundConnection", td::BufferedFd(std::move(fd)), 0, - 50, 500, creator_(), scheduler_id) + 50, 500, creator_(), SharedData::get_slow_incoming_http_scheduler_id()) .release(); } diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index b07a867..1cd3661 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -47,10 +47,8 @@ WebhookActor::WebhookActor(td::ActorShared callback, td::int64 tqueue_ , fix_ip_address_(fix_ip_address) , from_db_flag_(from_db_flag) , max_connections_(max_connections) - , secret_token_(std::move(secret_token)) - , slow_scheduler_id_(td::Scheduler::instance()->sched_count() - 2) { + , secret_token_(std::move(secret_token)) { CHECK(max_connections_ > 0); - CHECK(slow_scheduler_id_ > 0); if (!cached_ip_address.empty()) { auto r_ip_address = td::IPAddress::get_ip_address(cached_ip_address); @@ -230,7 +228,8 @@ td::Status WebhookActor::create_connection(td::BufferedFd fd) { auto *conn = connections_.get(id); conn->actor_id_ = td::create_actor( PSLICE() << "Connect:" << id, std::move(fd), std::move(ssl_stream), 0, 50, 60, - td::ActorShared(actor_id(this), id), slow_scheduler_id_); + td::ActorShared(actor_id(this), id), + SharedData::get_slow_outgoing_http_scheduler_id()); conn->ip_generation_ = ip_generation_; conn->event_id_ = {}; conn->id_ = id; diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h index 926a914..4c101a3 100644 --- a/telegram-bot-api/WebhookActor.h +++ b/telegram-bot-api/WebhookActor.h @@ -177,7 +177,6 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback { double last_success_time_ = 0; double wakeup_at_ = 0; bool last_update_was_successful_ = true; - td::int32 slow_scheduler_id_ = -1; void relax_wakeup_at(double wakeup_at, const char *source); diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 36bde52..11bae39 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -459,27 +459,22 @@ int main(int argc, char *argv[]) { // << (td::GitInfo::is_dirty() ? "(dirty)" : "") << " started"; LOG(WARNING) << "Bot API " << parameters->version_ << " server started"; - // +3 threads for Td - // one thread for ClientManager and all Clients - // one thread for watchdogs - // one thread for slow HTTP connections - // one thread for DNS resolving - const int thread_count = 7; - td::ConcurrentScheduler sched(thread_count, cpu_affinity); + td::ConcurrentScheduler sched(SharedData::get_thread_count() - 1, cpu_affinity); td::GetHostByNameActor::Options get_host_by_name_options; - get_host_by_name_options.scheduler_id = thread_count; + get_host_by_name_options.scheduler_id = SharedData::get_dns_resolver_scheduler_id(); parameters->get_host_by_name_actor_id_ = sched.create_actor_unsafe(0, "GetHostByName", std::move(get_host_by_name_options)) .release(); - auto client_manager = - sched.create_actor_unsafe(thread_count - 3, "ClientManager", std::move(parameters), token_range) - .release(); + auto client_manager = sched + .create_actor_unsafe(SharedData::get_client_scheduler_id(), "ClientManager", + std::move(parameters), token_range) + .release(); sched .create_actor_unsafe( - thread_count - 3, "HttpServer", http_ip_address, http_port, + SharedData::get_client_scheduler_id(), "HttpServer", http_ip_address, http_port, [client_manager, shared_data] { return td::ActorOwn( td::create_actor("HttpConnection", client_manager, shared_data)); @@ -489,7 +484,7 @@ int main(int argc, char *argv[]) { if (http_stat_port != 0) { sched .create_actor_unsafe( - thread_count - 3, "HttpStatsServer", http_stat_ip_address, http_stat_port, + SharedData::get_client_scheduler_id(), "HttpStatsServer", http_stat_ip_address, http_stat_port, [client_manager] { return td::ActorOwn( td::create_actor("HttpStatConnection", client_manager)); @@ -498,8 +493,8 @@ int main(int argc, char *argv[]) { } constexpr double WATCHDOG_TIMEOUT = 0.25; - auto watchdog_id = - sched.create_actor_unsafe(thread_count - 2, "Watchdog", td::this_thread::get_id(), WATCHDOG_TIMEOUT); + auto watchdog_id = sched.create_actor_unsafe(SharedData::get_watchdog_scheduler_id(), "Watchdog", + td::this_thread::get_id(), WATCHDOG_TIMEOUT); sched.start(); From 9f688af4fbc76e2da48985a7241325c1b5d8d369 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 25 Jul 2023 22:32:05 +0300 Subject: [PATCH 14/45] Add dedicated threads for TQueue and webhook databases and webhook certificate processing. --- telegram-bot-api/Client.cpp | 4 ++-- telegram-bot-api/ClientManager.cpp | 4 ++-- telegram-bot-api/ClientParameters.h | 17 +++++++++++------ telegram-bot-api/WebhookActor.cpp | 11 ++++++----- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 7597b5e..d136f64 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -9874,7 +9874,7 @@ void Client::webhook_error(td::Status status) { void Client::webhook_closed(td::Status status) { if (has_webhook_certificate_) { - td::Scheduler::instance()->run_on_scheduler(SharedData::get_database_scheduler_id(), + td::Scheduler::instance()->run_on_scheduler(SharedData::get_webhook_certificate_scheduler_id(), [actor_id = actor_id(this), path = get_webhook_certificate_path(), status = std::move(status)](td::Unit) mutable { LOG(INFO) << "Unlink certificate " << path; @@ -9987,7 +9987,7 @@ void Client::do_set_webhook(PromisedQueryPtr query, bool was_deleted) { CHECK(!webhook_set_query_); active_webhook_set_query_ = std::move(query); td::Scheduler::instance()->run_on_scheduler( - SharedData::get_database_scheduler_id(), + SharedData::get_webhook_certificate_scheduler_id(), [actor_id = actor_id(this), from_path = cert_file_ptr->temp_file_name, to_path = get_webhook_certificate_path(), size](td::Unit) mutable { LOG(INFO) << "Copy certificate to " << to_path; diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 1bad19d..b94ab75 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -331,7 +331,7 @@ void ClientManager::start_up() { } auto concurrent_binlog = - std::make_shared(std::move(binlog), SharedData::get_database_scheduler_id()); + std::make_shared(std::move(binlog), SharedData::get_binlog_scheduler_id()); auto concurrent_tqueue_binlog = td::make_unique>(); concurrent_tqueue_binlog->set_binlog(std::move(concurrent_binlog)); tqueue->set_callback(std::move(concurrent_tqueue_binlog)); @@ -346,7 +346,7 @@ void ClientManager::start_up() { // init webhook_db auto concurrent_webhook_db = td::make_unique>(); auto status = concurrent_webhook_db->init(parameters_->working_directory_ + "webhooks_db.binlog", td::DbKey::empty(), - SharedData::get_database_scheduler_id()); + SharedData::get_binlog_scheduler_id()); LOG_IF(FATAL, status.is_error()) << "Can't open webhooks_db.binlog " << status; parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db); diff --git a/telegram-bot-api/ClientParameters.h b/telegram-bot-api/ClientParameters.h index b233138..b22031d 100644 --- a/telegram-bot-api/ClientParameters.h +++ b/telegram-bot-api/ClientParameters.h @@ -53,11 +53,6 @@ struct SharedData { return static_cast(result); } - static td::int32 get_database_scheduler_id() { - // the same scheduler as for database in Td - return 1; - } - static td::int32 get_file_gc_scheduler_id() { // the same scheduler as for file GC in Td return 2; @@ -88,9 +83,19 @@ struct SharedData { return 8; } - static td::int32 get_thread_count() { + static td::int32 get_binlog_scheduler_id() { + // the thread for TQueue and webhook binlogs return 9; } + + static td::int32 get_webhook_certificate_scheduler_id() { + // the thread for webhook certificate processing + return 10; + } + + static td::int32 get_thread_count() { + return 11; + } }; struct ClientParameters { diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index 1cd3661..e2870b6 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -724,11 +724,12 @@ void WebhookActor::start_up() { if (url_.protocol_ != td::HttpUrl::Protocol::Http && !stop_flag_) { // asynchronously create SSL context - td::Scheduler::instance()->run_on_scheduler( - SharedData::get_database_scheduler_id(), [actor_id = actor_id(this), cert_path = cert_path_](td::Unit) mutable { - send_closure(actor_id, &WebhookActor::on_ssl_context_created, - td::SslCtx::create(cert_path, td::SslCtx::VerifyPeer::On)); - }); + td::Scheduler::instance()->run_on_scheduler(SharedData::get_webhook_certificate_scheduler_id(), + [actor_id = actor_id(this), cert_path = cert_path_](td::Unit) mutable { + send_closure( + actor_id, &WebhookActor::on_ssl_context_created, + td::SslCtx::create(cert_path, td::SslCtx::VerifyPeer::On)); + }); } yield(); From 2bbaf87feaf65cb7489dc1aafd5650df6474b437 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 31 Jul 2023 14:02:50 +0300 Subject: [PATCH 15/45] Use get_json_object_long_field to fetch "amount". --- telegram-bot-api/Client.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index d136f64..c9e3470 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5874,20 +5874,8 @@ td::Result> Client::get_labeled_pri return td::Status::Error(400, "LabeledPrice label must be non-empty"); } - TRY_RESULT(amount, get_json_object_field(object, "amount", td::JsonValue::Type::Null, false)); - td::Slice number; - if (amount.type() == td::JsonValue::Type::Number) { - number = amount.get_number(); - } else if (amount.type() == td::JsonValue::Type::String) { - number = amount.get_string(); - } else { - return td::Status::Error(400, "Field \"amount\" must be of type Number or String"); - } - auto parsed_amount = td::to_integer_safe(number); - if (parsed_amount.is_error()) { - return td::Status::Error(400, "Can't parse \"amount\" as Number"); - } - return make_object(label, parsed_amount.ok()); + TRY_RESULT(amount, get_json_object_long_field(object, "amount", false)); + return make_object(label, amount); } td::Result>> Client::get_labeled_price_parts( From a78edf070363e9dfa7393e0e34b511d6d8acc625 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 31 Jul 2023 17:53:56 +0300 Subject: [PATCH 16/45] Use JsonObject member functions to get field values. --- telegram-bot-api/Client.cpp | 578 ++++++++++++++++++------------------ 1 file changed, 291 insertions(+), 287 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index c9e3470..cabd34f 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5527,26 +5527,26 @@ td::Result> Client::get_keyboard_butt if (button.type() == td::JsonValue::Type::Object) { auto &object = button.get_object(); - TRY_RESULT(text, get_json_object_string_field(object, "text", false)); + TRY_RESULT(text, object.get_required_string_field("text")); - TRY_RESULT(request_phone_number, get_json_object_bool_field(object, "request_phone_number")); - TRY_RESULT(request_contact, get_json_object_bool_field(object, "request_contact")); + TRY_RESULT(request_phone_number, object.get_optional_bool_field("request_phone_number")); + TRY_RESULT(request_contact, object.get_optional_bool_field("request_contact")); if (request_phone_number || request_contact) { return make_object(text, make_object()); } - TRY_RESULT(request_location, get_json_object_bool_field(object, "request_location")); + TRY_RESULT(request_location, object.get_optional_bool_field("request_location")); if (request_location) { return make_object(text, make_object()); } - if (has_json_object_field(object, "request_poll")) { + if (object.has_field("request_poll")) { bool force_regular = false; bool force_quiz = false; - TRY_RESULT(request_poll, get_json_object_field(object, "request_poll", td::JsonValue::Type::Object, false)); + TRY_RESULT(request_poll, object.extract_required_field("request_poll", td::JsonValue::Type::Object)); auto &request_poll_object = request_poll.get_object(); - if (has_json_object_field(request_poll_object, "type")) { - TRY_RESULT(type, get_json_object_string_field(request_poll_object, "type")); + if (request_poll_object.has_field("type")) { + TRY_RESULT(type, request_poll_object.get_optional_string_field("type")); if (type == "quiz") { force_quiz = true; } else if (type == "regular") { @@ -5557,47 +5557,48 @@ td::Result> Client::get_keyboard_butt text, make_object(force_regular, force_quiz)); } - if (has_json_object_field(object, "web_app")) { - TRY_RESULT(web_app, get_json_object_field(object, "web_app", td::JsonValue::Type::Object, false)); + if (object.has_field("web_app")) { + TRY_RESULT(web_app, object.extract_required_field("web_app", td::JsonValue::Type::Object)); auto &web_app_object = web_app.get_object(); - TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + TRY_RESULT(url, web_app_object.get_required_string_field("url")); return make_object(text, make_object(url)); } - if (has_json_object_field(object, "request_user")) { - TRY_RESULT(request_user, get_json_object_field(object, "request_user", td::JsonValue::Type::Object, false)); + if (object.has_field("request_user")) { + TRY_RESULT(request_user, object.extract_required_field("request_user", td::JsonValue::Type::Object)); auto &request_user_object = request_user.get_object(); - TRY_RESULT(id, get_json_object_int_field(request_user_object, "request_id", false)); - auto restrict_user_is_bot = has_json_object_field(request_user_object, "user_is_bot"); - TRY_RESULT(user_is_bot, get_json_object_bool_field(request_user_object, "user_is_bot")); - auto restrict_user_is_premium = has_json_object_field(request_user_object, "user_is_premium"); - TRY_RESULT(user_is_premium, get_json_object_bool_field(request_user_object, "user_is_premium")); + TRY_RESULT(id, request_user_object.get_required_int_field("request_id")); + auto restrict_user_is_bot = request_user_object.has_field("user_is_bot"); + TRY_RESULT(user_is_bot, request_user_object.get_optional_bool_field("user_is_bot")); + auto restrict_user_is_premium = request_user_object.has_field("user_is_premium"); + TRY_RESULT(user_is_premium, request_user_object.get_optional_bool_field("user_is_premium")); return make_object( text, make_object(id, restrict_user_is_bot, user_is_bot, restrict_user_is_premium, user_is_premium)); } - if (has_json_object_field(object, "request_chat")) { - TRY_RESULT(request_chat, get_json_object_field(object, "request_chat", td::JsonValue::Type::Object, false)); + if (object.has_field("request_chat")) { + TRY_RESULT(request_chat, object.extract_required_field("request_chat", td::JsonValue::Type::Object)); auto &request_chat_object = request_chat.get_object(); - TRY_RESULT(id, get_json_object_int_field(request_chat_object, "request_id", false)); - TRY_RESULT(chat_is_channel, get_json_object_bool_field(request_chat_object, "chat_is_channel")); - auto restrict_chat_is_forum = has_json_object_field(request_chat_object, "chat_is_forum"); - TRY_RESULT(chat_is_forum, get_json_object_bool_field(request_chat_object, "chat_is_forum")); - auto restrict_chat_has_username = has_json_object_field(request_chat_object, "chat_has_username"); - TRY_RESULT(chat_has_username, get_json_object_bool_field(request_chat_object, "chat_has_username")); - TRY_RESULT(chat_is_created, get_json_object_bool_field(request_chat_object, "chat_is_created")); + TRY_RESULT(id, request_chat_object.get_required_int_field("request_id")); + TRY_RESULT(chat_is_channel, request_chat_object.get_optional_bool_field("chat_is_channel")); + auto restrict_chat_is_forum = request_chat_object.has_field("chat_is_forum"); + TRY_RESULT(chat_is_forum, request_chat_object.get_optional_bool_field("chat_is_forum")); + auto restrict_chat_has_username = request_chat_object.has_field("chat_has_username"); + TRY_RESULT(chat_has_username, request_chat_object.get_optional_bool_field("chat_has_username")); + TRY_RESULT(chat_is_created, request_chat_object.get_optional_bool_field("chat_is_created")); object_ptr user_administrator_rights; - if (has_json_object_field(request_chat_object, "user_administrator_rights")) { - TRY_RESULT_ASSIGN(user_administrator_rights, get_chat_administrator_rights(get_json_object_field_force( - request_chat_object, "user_administrator_rights"))); + if (request_chat_object.has_field("user_administrator_rights")) { + TRY_RESULT_ASSIGN( + user_administrator_rights, + get_chat_administrator_rights(request_chat_object.extract_field("user_administrator_rights"))); } object_ptr bot_administrator_rights; - if (has_json_object_field(request_chat_object, "bot_administrator_rights")) { - TRY_RESULT_ASSIGN(bot_administrator_rights, get_chat_administrator_rights(get_json_object_field_force( - request_chat_object, "bot_administrator_rights"))); + if (request_chat_object.has_field("bot_administrator_rights")) { + TRY_RESULT_ASSIGN(bot_administrator_rights, + get_chat_administrator_rights(request_chat_object.extract_field("bot_administrator_rights"))); } - TRY_RESULT(bot_is_member, get_json_object_bool_field(request_chat_object, "bot_is_member")); + TRY_RESULT(bot_is_member, request_chat_object.get_optional_bool_field("bot_is_member")); return make_object( text, make_object( id, chat_is_channel, restrict_chat_is_forum, chat_is_forum, restrict_chat_has_username, @@ -5622,68 +5623,68 @@ td::Result> Client::get_inline_ auto &object = button.get_object(); - TRY_RESULT(text, get_json_object_string_field(object, "text", false)); + TRY_RESULT(text, object.get_required_string_field("text")); { - TRY_RESULT(url, get_json_object_string_field(object, "url")); + TRY_RESULT(url, object.get_optional_string_field("url")); if (!url.empty()) { return make_object(text, make_object(url)); } } { - TRY_RESULT(callback_data, get_json_object_string_field(object, "callback_data")); + TRY_RESULT(callback_data, object.get_optional_string_field("callback_data")); if (!callback_data.empty()) { return make_object( text, make_object(callback_data)); } } - if (has_json_object_field(object, "callback_game")) { + if (object.has_field("callback_game")) { return make_object(text, make_object()); } - if (has_json_object_field(object, "pay")) { + if (object.has_field("pay")) { return make_object(text, make_object()); } - if (has_json_object_field(object, "switch_inline_query")) { - TRY_RESULT(switch_inline_query, get_json_object_string_field(object, "switch_inline_query", false)); + if (object.has_field("switch_inline_query")) { + TRY_RESULT(switch_inline_query, object.get_required_string_field("switch_inline_query")); return make_object( text, make_object( switch_inline_query, td_api::make_object(true, true, true, true))); } - if (has_json_object_field(object, "switch_inline_query_chosen_chat")) { + if (object.has_field("switch_inline_query_chosen_chat")) { TRY_RESULT(switch_inline_query, - get_json_object_field(object, "switch_inline_query_chosen_chat", td::JsonValue::Type::Object, false)); + object.extract_required_field("switch_inline_query_chosen_chat", td::JsonValue::Type::Object)); CHECK(switch_inline_query.type() == td::JsonValue::Type::Object); auto &switch_inline_query_object = switch_inline_query.get_object(); - TRY_RESULT(query, get_json_object_string_field(switch_inline_query_object, "query")); - TRY_RESULT(allow_user_chats, get_json_object_bool_field(switch_inline_query_object, "allow_user_chats")); - TRY_RESULT(allow_bot_chats, get_json_object_bool_field(switch_inline_query_object, "allow_bot_chats")); - TRY_RESULT(allow_group_chats, get_json_object_bool_field(switch_inline_query_object, "allow_group_chats")); - TRY_RESULT(allow_channel_chats, get_json_object_bool_field(switch_inline_query_object, "allow_channel_chats")); + TRY_RESULT(query, switch_inline_query_object.get_optional_string_field("query")); + TRY_RESULT(allow_user_chats, switch_inline_query_object.get_optional_bool_field("allow_user_chats")); + TRY_RESULT(allow_bot_chats, switch_inline_query_object.get_optional_bool_field("allow_bot_chats")); + TRY_RESULT(allow_group_chats, switch_inline_query_object.get_optional_bool_field("allow_group_chats")); + TRY_RESULT(allow_channel_chats, switch_inline_query_object.get_optional_bool_field("allow_channel_chats")); return make_object( text, make_object( query, td_api::make_object(allow_user_chats, allow_bot_chats, allow_group_chats, allow_channel_chats))); } - if (has_json_object_field(object, "switch_inline_query_current_chat")) { - TRY_RESULT(switch_inline_query, get_json_object_string_field(object, "switch_inline_query_current_chat", false)); + if (object.has_field("switch_inline_query_current_chat")) { + TRY_RESULT(switch_inline_query, object.get_required_string_field("switch_inline_query_current_chat")); return make_object( text, make_object( switch_inline_query, td_api::make_object())); } - if (has_json_object_field(object, "login_url")) { - TRY_RESULT(login_url, get_json_object_field(object, "login_url", td::JsonValue::Type::Object, false)); + if (object.has_field("login_url")) { + TRY_RESULT(login_url, object.extract_required_field("login_url", td::JsonValue::Type::Object)); CHECK(login_url.type() == td::JsonValue::Type::Object); auto &login_url_object = login_url.get_object(); - TRY_RESULT(url, get_json_object_string_field(login_url_object, "url", false)); - TRY_RESULT(bot_username, get_json_object_string_field(login_url_object, "bot_username")); - TRY_RESULT(request_write_access, get_json_object_bool_field(login_url_object, "request_write_access")); - TRY_RESULT(forward_text, get_json_object_string_field(login_url_object, "forward_text")); + TRY_RESULT(url, login_url_object.get_required_string_field("url")); + TRY_RESULT(bot_username, login_url_object.get_optional_string_field("bot_username")); + TRY_RESULT(request_write_access, login_url_object.get_optional_bool_field("request_write_access")); + TRY_RESULT(forward_text, login_url_object.get_optional_string_field("forward_text")); int64 bot_user_id = 0; if (bot_username.empty()) { @@ -5717,10 +5718,10 @@ td::Result> Client::get_inline_ text, make_object(url, bot_user_id, forward_text)); } - if (has_json_object_field(object, "web_app")) { - TRY_RESULT(web_app, get_json_object_field(object, "web_app", td::JsonValue::Type::Object, false)); + if (object.has_field("web_app")) { + TRY_RESULT(web_app, object.extract_required_field("web_app", td::JsonValue::Type::Object)); auto &web_app_object = web_app.get_object(); - TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + TRY_RESULT(url, web_app_object.get_required_string_field("url")); return make_object(text, make_object(url)); } @@ -5869,12 +5870,12 @@ td::Result> Client::get_labeled_pri auto &object = value.get_object(); - TRY_RESULT(label, get_json_object_string_field(object, "label", false)); + TRY_RESULT(label, object.get_required_string_field("label")); if (label.empty()) { return td::Status::Error(400, "LabeledPrice label must be non-empty"); } - TRY_RESULT(amount, get_json_object_long_field(object, "amount", false)); + TRY_RESULT(amount, object.get_required_long_field("amount")); return make_object(label, amount); } @@ -5930,17 +5931,17 @@ td::Result> Client::get_shipping_opti auto &object = option.get_object(); - TRY_RESULT(id, get_json_object_string_field(object, "id", false)); + TRY_RESULT(id, object.get_required_string_field("id")); if (id.empty()) { return td::Status::Error(400, "ShippingOption identifier must be non-empty"); } - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); + TRY_RESULT(title, object.get_required_string_field("title")); if (title.empty()) { return td::Status::Error(400, "ShippingOption title must be non-empty"); } - TRY_RESULT(prices_json, get_json_object_field(object, "prices", td::JsonValue::Type::Array, false)); + TRY_RESULT(prices_json, object.extract_required_field("prices", td::JsonValue::Type::Array)); auto r_prices = get_labeled_price_parts(prices_json); if (r_prices.is_error()) { @@ -6087,42 +6088,42 @@ td::Result> Client::get_input_me CHECK(input_message_content.type() == td::JsonValue::Type::Object); auto &object = input_message_content.get_object(); - TRY_RESULT(message_text, get_json_object_string_field(object, "message_text")); + TRY_RESULT(message_text, object.get_optional_string_field("message_text")); if (!message_text.empty()) { - TRY_RESULT(disable_web_page_preview, get_json_object_bool_field(object, "disable_web_page_preview")); - TRY_RESULT(parse_mode, get_json_object_string_field(object, "parse_mode")); - auto entities = get_json_object_field_force(object, "entities"); + TRY_RESULT(disable_web_page_preview, object.get_optional_bool_field("disable_web_page_preview")); + TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); + auto entities = object.extract_field("entities"); TRY_RESULT(input_message_text, get_input_message_text(std::move(message_text), disable_web_page_preview, std::move(parse_mode), std::move(entities))); return std::move(input_message_text); } - if (has_json_object_field(object, "latitude") && has_json_object_field(object, "longitude")) { - TRY_RESULT(latitude, get_json_object_double_field(object, "latitude", false)); - TRY_RESULT(longitude, get_json_object_double_field(object, "longitude", false)); - TRY_RESULT(horizontal_accuracy, get_json_object_double_field(object, "horizontal_accuracy")); - TRY_RESULT(live_period, get_json_object_int_field(object, "live_period")); - TRY_RESULT(heading, get_json_object_int_field(object, "heading")); - TRY_RESULT(proximity_alert_radius, get_json_object_int_field(object, "proximity_alert_radius")); + if (object.has_field("latitude") && object.has_field("longitude")) { + TRY_RESULT(latitude, object.get_required_double_field("latitude")); + TRY_RESULT(longitude, object.get_required_double_field("longitude")); + TRY_RESULT(horizontal_accuracy, object.get_optional_double_field("horizontal_accuracy")); + TRY_RESULT(live_period, object.get_optional_int_field("live_period")); + TRY_RESULT(heading, object.get_optional_int_field("heading")); + TRY_RESULT(proximity_alert_radius, object.get_optional_int_field("proximity_alert_radius")); auto location = make_object(latitude, longitude, horizontal_accuracy); - if (has_json_object_field(object, "title") && has_json_object_field(object, "address")) { - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(address, get_json_object_string_field(object, "address", false)); + if (object.has_field("title") && object.has_field("address")) { + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(address, object.get_required_string_field("address")); td::string provider; td::string venue_id; td::string venue_type; - TRY_RESULT(google_place_id, get_json_object_string_field(object, "google_place_id")); - TRY_RESULT(google_place_type, get_json_object_string_field(object, "google_place_type")); + TRY_RESULT(google_place_id, object.get_optional_string_field("google_place_id")); + TRY_RESULT(google_place_type, object.get_optional_string_field("google_place_type")); if (!google_place_id.empty() || !google_place_type.empty()) { provider = "gplaces"; venue_id = std::move(google_place_id); venue_type = std::move(google_place_type); } - TRY_RESULT(foursquare_id, get_json_object_string_field(object, "foursquare_id")); - TRY_RESULT(foursquare_type, get_json_object_string_field(object, "foursquare_type")); + TRY_RESULT(foursquare_id, object.get_optional_string_field("foursquare_id")); + TRY_RESULT(foursquare_type, object.get_optional_string_field("foursquare_type")); if (!foursquare_id.empty() || !foursquare_type.empty()) { provider = "foursquare"; venue_id = std::move(foursquare_id); @@ -6136,46 +6137,46 @@ td::Result> Client::get_input_me return make_object(std::move(location), live_period, heading, proximity_alert_radius); } - if (has_json_object_field(object, "phone_number")) { - TRY_RESULT(phone_number, get_json_object_string_field(object, "phone_number", false)); - TRY_RESULT(first_name, get_json_object_string_field(object, "first_name", false)); - TRY_RESULT(last_name, get_json_object_string_field(object, "last_name")); - TRY_RESULT(vcard, get_json_object_string_field(object, "vcard")); + if (object.has_field("phone_number")) { + TRY_RESULT(phone_number, object.get_required_string_field("phone_number")); + TRY_RESULT(first_name, object.get_required_string_field("first_name")); + TRY_RESULT(last_name, object.get_optional_string_field("last_name")); + TRY_RESULT(vcard, object.get_optional_string_field("vcard")); return make_object( make_object(phone_number, first_name, last_name, vcard, 0)); } - if (has_json_object_field(object, "payload")) { - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(description, get_json_object_string_field(object, "description", false)); - TRY_RESULT(payload, get_json_object_string_field(object, "payload", false)); + if (object.has_field("payload")) { + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(description, object.get_required_string_field("description")); + TRY_RESULT(payload, object.get_required_string_field("payload")); if (!td::check_utf8(payload)) { return td::Status::Error(400, "InputInvoiceMessageContent payload must be encoded in UTF-8"); } - TRY_RESULT(provider_token, get_json_object_string_field(object, "provider_token", false)); - TRY_RESULT(currency, get_json_object_string_field(object, "currency", false)); - TRY_RESULT(prices_object, get_json_object_field(object, "prices", td::JsonValue::Type::Array, false)); + TRY_RESULT(provider_token, object.get_required_string_field("provider_token")); + TRY_RESULT(currency, object.get_required_string_field("currency")); + TRY_RESULT(prices_object, object.extract_required_field("prices", td::JsonValue::Type::Array)); TRY_RESULT(prices, get_labeled_price_parts(prices_object)); - TRY_RESULT(provider_data, get_json_object_string_field(object, "provider_data")); - TRY_RESULT(max_tip_amount, get_json_object_long_field(object, "max_tip_amount")); + TRY_RESULT(provider_data, object.get_optional_string_field("provider_data")); + TRY_RESULT(max_tip_amount, object.get_optional_long_field("max_tip_amount")); td::vector suggested_tip_amounts; TRY_RESULT(suggested_tip_amounts_array, - get_json_object_field(object, "suggested_tip_amounts", td::JsonValue::Type::Array)); + object.extract_optional_field("suggested_tip_amounts", td::JsonValue::Type::Array)); if (suggested_tip_amounts_array.type() == td::JsonValue::Type::Array) { TRY_RESULT_ASSIGN(suggested_tip_amounts, get_suggested_tip_amounts(suggested_tip_amounts_array)); } - TRY_RESULT(photo_url, get_json_object_string_field(object, "photo_url")); - TRY_RESULT(photo_size, get_json_object_int_field(object, "photo_size")); - TRY_RESULT(photo_width, get_json_object_int_field(object, "photo_width")); - TRY_RESULT(photo_height, get_json_object_int_field(object, "photo_height")); - TRY_RESULT(need_name, get_json_object_bool_field(object, "need_name")); - TRY_RESULT(need_phone_number, get_json_object_bool_field(object, "need_phone_number")); - TRY_RESULT(need_email_address, get_json_object_bool_field(object, "need_email")); - TRY_RESULT(need_shipping_address, get_json_object_bool_field(object, "need_shipping_address")); - TRY_RESULT(send_phone_number_to_provider, get_json_object_bool_field(object, "send_phone_number_to_provider")); - TRY_RESULT(send_email_address_to_provider, get_json_object_bool_field(object, "send_email_to_provider")); - TRY_RESULT(is_flexible, get_json_object_bool_field(object, "is_flexible")); + TRY_RESULT(photo_url, object.get_optional_string_field("photo_url")); + TRY_RESULT(photo_size, object.get_optional_int_field("photo_size")); + TRY_RESULT(photo_width, object.get_optional_int_field("photo_width")); + TRY_RESULT(photo_height, object.get_optional_int_field("photo_height")); + TRY_RESULT(need_name, object.get_optional_bool_field("need_name")); + TRY_RESULT(need_phone_number, object.get_optional_bool_field("need_phone_number")); + TRY_RESULT(need_email_address, object.get_optional_bool_field("need_email")); + TRY_RESULT(need_shipping_address, object.get_optional_bool_field("need_shipping_address")); + TRY_RESULT(send_phone_number_to_provider, object.get_optional_bool_field("send_phone_number_to_provider")); + TRY_RESULT(send_email_address_to_provider, object.get_optional_bool_field("send_email_to_provider")); + TRY_RESULT(is_flexible, object.get_optional_bool_field("is_flexible")); return make_object( make_object(currency, std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), @@ -6205,18 +6206,18 @@ td::Result> Client::get_inl auto &object = value.get_object(); - TRY_RESULT(text, get_json_object_string_field(object, "text", false)); + TRY_RESULT(text, object.get_required_string_field("text")); - if (has_json_object_field(object, "start_parameter")) { - TRY_RESULT(start_parameter, get_json_object_string_field(object, "start_parameter", false)); + if (object.has_field("start_parameter")) { + TRY_RESULT(start_parameter, object.get_required_string_field("start_parameter")); return make_object( text, make_object(start_parameter)); } - if (has_json_object_field(object, "web_app")) { - TRY_RESULT(web_app, get_json_object_field(object, "web_app", td::JsonValue::Type::Object, false)); + if (object.has_field("web_app")) { + TRY_RESULT(web_app, object.extract_required_field("web_app", td::JsonValue::Type::Object)); auto &web_app_object = web_app.get_object(); - TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + TRY_RESULT(url, web_app_object.get_required_string_field("url")); return make_object(text, make_object(url)); } @@ -6309,21 +6310,22 @@ td::Result> Client::get_inlin auto &object = value.get_object(); - TRY_RESULT(type, get_json_object_string_field(object, "type", false)); + TRY_RESULT(type, object.get_required_string_field("type")); td::to_lower_inplace(type); - TRY_RESULT(id, get_json_object_string_field(object, "id", false)); + TRY_RESULT(id, object.get_required_string_field("id")); bool is_input_message_content_required = (type == "article"); object_ptr input_message_content; TRY_RESULT(input_message_content_obj, - get_json_object_field(object, "input_message_content", td::JsonValue::Type::Object)); + object.extract_optional_field("input_message_content", td::JsonValue::Type::Object)); if (input_message_content_obj.type() == td::JsonValue::Type::Null) { - TRY_RESULT(message_text, get_json_object_string_field(object, "message_text", !is_input_message_content_required)); - TRY_RESULT(disable_web_page_preview, get_json_object_bool_field(object, "disable_web_page_preview")); - TRY_RESULT(parse_mode, get_json_object_string_field(object, "parse_mode")); - auto entities = get_json_object_field_force(object, "entities"); + TRY_RESULT(message_text, is_input_message_content_required ? object.get_required_string_field("message_text") + : object.get_optional_string_field("message_text")); + TRY_RESULT(disable_web_page_preview, object.get_optional_bool_field("disable_web_page_preview")); + TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); + auto entities = object.extract_field("entities"); if (is_input_message_content_required || !message_text.empty()) { TRY_RESULT(input_message_text, get_input_message_text(std::move(message_text), disable_web_page_preview, @@ -6335,12 +6337,12 @@ td::Result> Client::get_inlin get_input_message_content(input_message_content_obj, is_input_message_content_required)); input_message_content = std::move(input_message_content_result); } - TRY_RESULT(input_caption, get_json_object_string_field(object, "caption")); - TRY_RESULT(parse_mode, get_json_object_string_field(object, "parse_mode")); - auto entities = get_json_object_field_force(object, "caption_entities"); + TRY_RESULT(input_caption, object.get_optional_string_field("caption")); + TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); + auto entities = object.extract_field("caption_entities"); TRY_RESULT(caption, get_formatted_text(std::move(input_caption), std::move(parse_mode), std::move(entities))); - TRY_RESULT(reply_markup_object, get_json_object_field(object, "reply_markup", td::JsonValue::Type::Object)); + TRY_RESULT(reply_markup_object, object.extract_optional_field("reply_markup", td::JsonValue::Type::Object)); object_ptr reply_markup; if (reply_markup_object.type() != td::JsonValue::Type::Null) { TRY_RESULT_ASSIGN(reply_markup, get_reply_markup(std::move(reply_markup_object), bot_user_ids)); @@ -6349,23 +6351,22 @@ td::Result> Client::get_inlin auto thumbnail_url_field_name = td::Slice("thumbnail_url"); auto thumbnail_width_field_name = td::Slice("thumbnail_width"); auto thumbnail_height_field_name = td::Slice("thumbnail_height"); - if (!has_json_object_field(object, thumbnail_url_field_name) && - !has_json_object_field(object, thumbnail_width_field_name) && - !has_json_object_field(object, thumbnail_height_field_name)) { + if (!object.has_field(thumbnail_url_field_name) && !object.has_field(thumbnail_width_field_name) && + !object.has_field(thumbnail_height_field_name)) { thumbnail_url_field_name = td::Slice("thumb_url"); thumbnail_width_field_name = td::Slice("thumb_width"); thumbnail_height_field_name = td::Slice("thumb_height"); } - TRY_RESULT(thumbnail_url, get_json_object_string_field(object, thumbnail_url_field_name)); - TRY_RESULT(thumbnail_width, get_json_object_int_field(object, thumbnail_width_field_name)); - TRY_RESULT(thumbnail_height, get_json_object_int_field(object, thumbnail_height_field_name)); + TRY_RESULT(thumbnail_url, object.get_optional_string_field(thumbnail_url_field_name)); + TRY_RESULT(thumbnail_width, object.get_optional_int_field(thumbnail_width_field_name)); + TRY_RESULT(thumbnail_height, object.get_optional_int_field(thumbnail_height_field_name)); object_ptr result; if (type == "article") { - TRY_RESULT(url, get_json_object_string_field(object, "url")); - TRY_RESULT(hide_url, get_json_object_bool_field(object, "hide_url")); - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(description, get_json_object_string_field(object, "description")); + TRY_RESULT(url, object.get_optional_string_field("url")); + TRY_RESULT(hide_url, object.get_optional_bool_field("hide_url")); + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(description, object.get_optional_string_field("description")); CHECK(input_message_content != nullptr); return make_object( @@ -6373,12 +6374,13 @@ td::Result> Client::get_inlin std::move(reply_markup), std::move(input_message_content)); } if (type == "audio") { - TRY_RESULT(audio_url, get_json_object_string_field(object, "audio_url")); - TRY_RESULT(audio_duration, get_json_object_int_field(object, "audio_duration")); - TRY_RESULT(title, get_json_object_string_field(object, "title", audio_url.empty())); - TRY_RESULT(performer, get_json_object_string_field(object, "performer")); + TRY_RESULT(audio_url, object.get_optional_string_field("audio_url")); + TRY_RESULT(audio_duration, object.get_optional_int_field("audio_duration")); + TRY_RESULT(title, audio_url.empty() ? object.get_optional_string_field("title") + : object.get_required_string_field("title")); + TRY_RESULT(performer, object.get_optional_string_field("performer")); if (audio_url.empty()) { - TRY_RESULT_ASSIGN(audio_url, get_json_object_string_field(object, "audio_file_id", false)); + TRY_RESULT_ASSIGN(audio_url, object.get_required_string_field("audio_file_id")); } if (input_message_content == nullptr) { @@ -6389,10 +6391,10 @@ td::Result> Client::get_inlin std::move(reply_markup), std::move(input_message_content)); } if (type == "contact") { - TRY_RESULT(phone_number, get_json_object_string_field(object, "phone_number", false)); - TRY_RESULT(first_name, get_json_object_string_field(object, "first_name", false)); - TRY_RESULT(last_name, get_json_object_string_field(object, "last_name")); - TRY_RESULT(vcard, get_json_object_string_field(object, "vcard")); + TRY_RESULT(phone_number, object.get_required_string_field("phone_number")); + TRY_RESULT(first_name, object.get_required_string_field("first_name")); + TRY_RESULT(last_name, object.get_optional_string_field("last_name")); + TRY_RESULT(vcard, object.get_optional_string_field("vcard")); if (input_message_content == nullptr) { input_message_content = make_object( @@ -6403,12 +6405,13 @@ td::Result> Client::get_inlin thumbnail_height, std::move(reply_markup), std::move(input_message_content)); } if (type == "document") { - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(description, get_json_object_string_field(object, "description")); - TRY_RESULT(document_url, get_json_object_string_field(object, "document_url")); - TRY_RESULT(mime_type, get_json_object_string_field(object, "mime_type", document_url.empty())); + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(description, object.get_optional_string_field("description")); + TRY_RESULT(document_url, object.get_optional_string_field("document_url")); + TRY_RESULT(mime_type, document_url.empty() ? object.get_optional_string_field("mime_type") + : object.get_required_string_field("mime_type")); if (document_url.empty()) { - TRY_RESULT_ASSIGN(document_url, get_json_object_string_field(object, "document_file_id", false)); + TRY_RESULT_ASSIGN(document_url, object.get_required_string_field("document_file_id")); } if (input_message_content == nullptr) { @@ -6419,22 +6422,22 @@ td::Result> Client::get_inlin std::move(reply_markup), std::move(input_message_content)); } if (type == "game") { - TRY_RESULT(game_short_name, get_json_object_string_field(object, "game_short_name", false)); + TRY_RESULT(game_short_name, object.get_required_string_field("game_short_name")); return make_object(id, game_short_name, std::move(reply_markup)); } if (type == "gif") { - TRY_RESULT(title, get_json_object_string_field(object, "title")); - TRY_RESULT(gif_url, get_json_object_string_field(object, "gif_url")); + TRY_RESULT(title, object.get_optional_string_field("title")); + TRY_RESULT(gif_url, object.get_optional_string_field("gif_url")); auto thumbnail_mime_type_field_name = td::Slice("thumbnail_mime_type"); - if (!has_json_object_field(object, thumbnail_mime_type_field_name)) { + if (!object.has_field(thumbnail_mime_type_field_name)) { thumbnail_mime_type_field_name = td::Slice("thumb_mime_type"); } - TRY_RESULT(thumbnail_mime_type, get_json_object_string_field(object, thumbnail_mime_type_field_name)); - TRY_RESULT(gif_duration, get_json_object_int_field(object, "gif_duration")); - TRY_RESULT(gif_width, get_json_object_int_field(object, "gif_width")); - TRY_RESULT(gif_height, get_json_object_int_field(object, "gif_height")); + TRY_RESULT(thumbnail_mime_type, object.get_optional_string_field(thumbnail_mime_type_field_name)); + TRY_RESULT(gif_duration, object.get_optional_int_field("gif_duration")); + TRY_RESULT(gif_width, object.get_optional_int_field("gif_width")); + TRY_RESULT(gif_height, object.get_optional_int_field("gif_height")); if (gif_url.empty()) { - TRY_RESULT_ASSIGN(gif_url, get_json_object_string_field(object, "gif_file_id", false)); + TRY_RESULT_ASSIGN(gif_url, object.get_required_string_field("gif_file_id")); } if (input_message_content == nullptr) { @@ -6446,13 +6449,13 @@ td::Result> Client::get_inlin std::move(reply_markup), std::move(input_message_content)); } if (type == "location") { - TRY_RESULT(latitude, get_json_object_double_field(object, "latitude", false)); - TRY_RESULT(longitude, get_json_object_double_field(object, "longitude", false)); - TRY_RESULT(horizontal_accuracy, get_json_object_double_field(object, "horizontal_accuracy")); - TRY_RESULT(live_period, get_json_object_int_field(object, "live_period")); - TRY_RESULT(heading, get_json_object_int_field(object, "heading")); - TRY_RESULT(proximity_alert_radius, get_json_object_int_field(object, "proximity_alert_radius")); - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); + TRY_RESULT(latitude, object.get_required_double_field("latitude")); + TRY_RESULT(longitude, object.get_required_double_field("longitude")); + TRY_RESULT(horizontal_accuracy, object.get_optional_double_field("horizontal_accuracy")); + TRY_RESULT(live_period, object.get_optional_int_field("live_period")); + TRY_RESULT(heading, object.get_optional_int_field("heading")); + TRY_RESULT(proximity_alert_radius, object.get_optional_int_field("proximity_alert_radius")); + TRY_RESULT(title, object.get_required_string_field("title")); if (input_message_content == nullptr) { auto location = make_object(latitude, longitude, horizontal_accuracy); @@ -6464,18 +6467,18 @@ td::Result> Client::get_inlin thumbnail_width, thumbnail_height, std::move(reply_markup), std::move(input_message_content)); } if (type == "mpeg4_gif") { - TRY_RESULT(title, get_json_object_string_field(object, "title")); - TRY_RESULT(mpeg4_url, get_json_object_string_field(object, "mpeg4_url")); + TRY_RESULT(title, object.get_optional_string_field("title")); + TRY_RESULT(mpeg4_url, object.get_optional_string_field("mpeg4_url")); auto thumbnail_mime_type_field_name = td::Slice("thumbnail_mime_type"); - if (!has_json_object_field(object, thumbnail_mime_type_field_name)) { + if (!object.has_field(thumbnail_mime_type_field_name)) { thumbnail_mime_type_field_name = td::Slice("thumb_mime_type"); } - TRY_RESULT(thumbnail_mime_type, get_json_object_string_field(object, thumbnail_mime_type_field_name)); - TRY_RESULT(mpeg4_duration, get_json_object_int_field(object, "mpeg4_duration")); - TRY_RESULT(mpeg4_width, get_json_object_int_field(object, "mpeg4_width")); - TRY_RESULT(mpeg4_height, get_json_object_int_field(object, "mpeg4_height")); + TRY_RESULT(thumbnail_mime_type, object.get_optional_string_field(thumbnail_mime_type_field_name)); + TRY_RESULT(mpeg4_duration, object.get_optional_int_field("mpeg4_duration")); + TRY_RESULT(mpeg4_width, object.get_optional_int_field("mpeg4_width")); + TRY_RESULT(mpeg4_height, object.get_optional_int_field("mpeg4_height")); if (mpeg4_url.empty()) { - TRY_RESULT_ASSIGN(mpeg4_url, get_json_object_string_field(object, "mpeg4_file_id", false)); + TRY_RESULT_ASSIGN(mpeg4_url, object.get_required_string_field("mpeg4_file_id")); } if (input_message_content == nullptr) { @@ -6487,13 +6490,13 @@ td::Result> Client::get_inlin mpeg4_height, std::move(reply_markup), std::move(input_message_content)); } if (type == "photo") { - TRY_RESULT(title, get_json_object_string_field(object, "title")); - TRY_RESULT(description, get_json_object_string_field(object, "description")); - TRY_RESULT(photo_url, get_json_object_string_field(object, "photo_url")); - TRY_RESULT(photo_width, get_json_object_int_field(object, "photo_width")); - TRY_RESULT(photo_height, get_json_object_int_field(object, "photo_height")); + TRY_RESULT(title, object.get_optional_string_field("title")); + TRY_RESULT(description, object.get_optional_string_field("description")); + TRY_RESULT(photo_url, object.get_optional_string_field("photo_url")); + TRY_RESULT(photo_width, object.get_optional_int_field("photo_width")); + TRY_RESULT(photo_height, object.get_optional_int_field("photo_height")); if (photo_url.empty()) { - TRY_RESULT_ASSIGN(photo_url, get_json_object_string_field(object, "photo_file_id", false)); + TRY_RESULT_ASSIGN(photo_url, object.get_required_string_field("photo_file_id")); } if (input_message_content == nullptr) { @@ -6505,7 +6508,7 @@ td::Result> Client::get_inlin std::move(input_message_content)); } if (type == "sticker") { - TRY_RESULT(sticker_file_id, get_json_object_string_field(object, "sticker_file_id", false)); + TRY_RESULT(sticker_file_id, object.get_required_string_field("sticker_file_id")); if (input_message_content == nullptr) { input_message_content = make_object(nullptr, nullptr, 0, 0, td::string()); @@ -6514,15 +6517,15 @@ td::Result> Client::get_inlin std::move(input_message_content)); } if (type == "venue") { - TRY_RESULT(latitude, get_json_object_double_field(object, "latitude", false)); - TRY_RESULT(longitude, get_json_object_double_field(object, "longitude", false)); - TRY_RESULT(horizontal_accuracy, get_json_object_double_field(object, "horizontal_accuracy")); - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(address, get_json_object_string_field(object, "address", false)); - TRY_RESULT(foursquare_id, get_json_object_string_field(object, "foursquare_id")); - TRY_RESULT(foursquare_type, get_json_object_string_field(object, "foursquare_type")); - TRY_RESULT(google_place_id, get_json_object_string_field(object, "google_place_id")); - TRY_RESULT(google_place_type, get_json_object_string_field(object, "google_place_type")); + TRY_RESULT(latitude, object.get_required_double_field("latitude")); + TRY_RESULT(longitude, object.get_required_double_field("longitude")); + TRY_RESULT(horizontal_accuracy, object.get_optional_double_field("horizontal_accuracy")); + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(address, object.get_required_string_field("address")); + TRY_RESULT(foursquare_id, object.get_optional_string_field("foursquare_id")); + TRY_RESULT(foursquare_type, object.get_optional_string_field("foursquare_type")); + TRY_RESULT(google_place_id, object.get_optional_string_field("google_place_id")); + TRY_RESULT(google_place_type, object.get_optional_string_field("google_place_type")); td::string provider; td::string venue_id; @@ -6550,15 +6553,16 @@ td::Result> Client::get_inlin thumbnail_url, thumbnail_width, thumbnail_height, std::move(reply_markup), std::move(input_message_content)); } if (type == "video") { - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(description, get_json_object_string_field(object, "description")); - TRY_RESULT(video_url, get_json_object_string_field(object, "video_url")); - TRY_RESULT(mime_type, get_json_object_string_field(object, "mime_type", video_url.empty())); - TRY_RESULT(video_width, get_json_object_int_field(object, "video_width")); - TRY_RESULT(video_height, get_json_object_int_field(object, "video_height")); - TRY_RESULT(video_duration, get_json_object_int_field(object, "video_duration")); + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(description, object.get_optional_string_field("description")); + TRY_RESULT(video_url, object.get_optional_string_field("video_url")); + TRY_RESULT(mime_type, video_url.empty() ? object.get_optional_string_field("mime_type") + : object.get_required_string_field("mime_type")); + TRY_RESULT(video_width, object.get_optional_int_field("video_width")); + TRY_RESULT(video_height, object.get_optional_int_field("video_height")); + TRY_RESULT(video_duration, object.get_optional_int_field("video_duration")); if (video_url.empty()) { - TRY_RESULT_ASSIGN(video_url, get_json_object_string_field(object, "video_file_id", false)); + TRY_RESULT_ASSIGN(video_url, object.get_required_string_field("video_file_id")); } if (input_message_content == nullptr) { @@ -6571,11 +6575,11 @@ td::Result> Client::get_inlin std::move(reply_markup), std::move(input_message_content)); } if (type == "voice") { - TRY_RESULT(title, get_json_object_string_field(object, "title", false)); - TRY_RESULT(voice_note_url, get_json_object_string_field(object, "voice_url")); - TRY_RESULT(voice_note_duration, get_json_object_int_field(object, "voice_duration")); + TRY_RESULT(title, object.get_required_string_field("title")); + TRY_RESULT(voice_note_url, object.get_optional_string_field("voice_url")); + TRY_RESULT(voice_note_duration, object.get_optional_int_field("voice_duration")); if (voice_note_url.empty()) { - TRY_RESULT_ASSIGN(voice_note_url, get_json_object_string_field(object, "voice_file_id", false)); + TRY_RESULT_ASSIGN(voice_note_url, object.get_required_string_field("voice_file_id")); } if (input_message_content == nullptr) { @@ -6596,7 +6600,7 @@ td::Result Client::get_bot_command_scope(td::JsonValue auto &object = value.get_object(); - TRY_RESULT(type, get_json_object_string_field(object, "type", false)); + TRY_RESULT(type, object.get_required_string_field("type")); if (type == "default") { return BotCommandScope(make_object()); } @@ -6613,7 +6617,7 @@ td::Result Client::get_bot_command_scope(td::JsonValue return td::Status::Error(400, "Unsupported type specified"); } - TRY_RESULT(chat_id, get_json_object_string_field(object, "chat_id", false)); + TRY_RESULT(chat_id, object.get_required_string_field("chat_id")); if (chat_id.empty()) { return td::Status::Error(400, "Empty chat_id specified"); } @@ -6624,7 +6628,7 @@ td::Result Client::get_bot_command_scope(td::JsonValue return BotCommandScope(make_object(0), std::move(chat_id)); } - TRY_RESULT(user_id, get_json_object_long_field(object, "user_id", false)); + TRY_RESULT(user_id, object.get_required_long_field("user_id")); if (user_id <= 0) { return td::Status::Error(400, "Invalid user_id specified"); } @@ -6659,8 +6663,8 @@ td::Result> Client::get_bot_command(td::J auto &object = value.get_object(); - TRY_RESULT(command, get_json_object_string_field(object, "command", false)); - TRY_RESULT(description, get_json_object_string_field(object, "description", false)); + TRY_RESULT(command, object.get_required_string_field("command")); + TRY_RESULT(description, object.get_required_string_field("description")); return make_object(command, description); } @@ -6700,7 +6704,7 @@ td::Result> Client::get_bot_menu_butto auto &object = value.get_object(); - TRY_RESULT(type, get_json_object_string_field(object, "type", false)); + TRY_RESULT(type, object.get_required_string_field("type")); if (type == "default") { return make_object("", "default"); } @@ -6708,10 +6712,10 @@ td::Result> Client::get_bot_menu_butto return nullptr; } if (type == "web_app") { - TRY_RESULT(text, get_json_object_string_field(object, "text", false)); - TRY_RESULT(web_app, get_json_object_field(object, "web_app", td::JsonValue::Type::Object, false)); + TRY_RESULT(text, object.get_required_string_field("text")); + TRY_RESULT(web_app, object.extract_required_field("web_app", td::JsonValue::Type::Object)); auto &web_app_object = web_app.get_object(); - TRY_RESULT(url, get_json_object_string_field(web_app_object, "url", false)); + TRY_RESULT(url, web_app_object.get_required_string_field("url")); return make_object(text, url); } @@ -6745,18 +6749,18 @@ td::Result> Client::get_chat } auto &object = value.get_object(); - TRY_RESULT(can_manage_chat, get_json_object_bool_field(object, "can_manage_chat")); - TRY_RESULT(can_change_info, get_json_object_bool_field(object, "can_change_info")); - TRY_RESULT(can_post_messages, get_json_object_bool_field(object, "can_post_messages")); - TRY_RESULT(can_edit_messages, get_json_object_bool_field(object, "can_edit_messages")); - TRY_RESULT(can_delete_messages, get_json_object_bool_field(object, "can_delete_messages")); - TRY_RESULT(can_invite_users, get_json_object_bool_field(object, "can_invite_users")); - TRY_RESULT(can_restrict_members, get_json_object_bool_field(object, "can_restrict_members")); - TRY_RESULT(can_pin_messages, get_json_object_bool_field(object, "can_pin_messages")); - TRY_RESULT(can_manage_topics, get_json_object_bool_field(object, "can_manage_topics")); - TRY_RESULT(can_promote_members, get_json_object_bool_field(object, "can_promote_members")); - TRY_RESULT(can_manage_video_chats, get_json_object_bool_field(object, "can_manage_video_chats")); - TRY_RESULT(is_anonymous, get_json_object_bool_field(object, "is_anonymous")); + TRY_RESULT(can_manage_chat, object.get_optional_bool_field("can_manage_chat")); + TRY_RESULT(can_change_info, object.get_optional_bool_field("can_change_info")); + TRY_RESULT(can_post_messages, object.get_optional_bool_field("can_post_messages")); + TRY_RESULT(can_edit_messages, object.get_optional_bool_field("can_edit_messages")); + TRY_RESULT(can_delete_messages, object.get_optional_bool_field("can_delete_messages")); + TRY_RESULT(can_invite_users, object.get_optional_bool_field("can_invite_users")); + TRY_RESULT(can_restrict_members, object.get_optional_bool_field("can_restrict_members")); + TRY_RESULT(can_pin_messages, object.get_optional_bool_field("can_pin_messages")); + TRY_RESULT(can_manage_topics, object.get_optional_bool_field("can_manage_topics")); + TRY_RESULT(can_promote_members, object.get_optional_bool_field("can_promote_members")); + TRY_RESULT(can_manage_video_chats, object.get_optional_bool_field("can_manage_video_chats")); + TRY_RESULT(is_anonymous, object.get_optional_bool_field("is_anonymous")); return make_object(can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_manage_topics, @@ -6794,7 +6798,7 @@ td::Result> Client::get_mask_position(t auto &object = value.get_object(); - TRY_RESULT(point_str, get_json_object_string_field(object, "point", false)); + TRY_RESULT(point_str, object.get_required_string_field("point")); point_str = td::trim(td::to_lower(point_str)); int32 point; for (point = 0; point < MASK_POINTS_SIZE; point++) { @@ -6806,9 +6810,9 @@ td::Result> Client::get_mask_position(t return td::Status::Error(400, "Wrong point specified in MaskPosition"); } - TRY_RESULT(x_shift, get_json_object_double_field(object, "x_shift", false)); - TRY_RESULT(y_shift, get_json_object_double_field(object, "y_shift", false)); - TRY_RESULT(scale, get_json_object_double_field(object, "scale", false)); + TRY_RESULT(x_shift, object.get_required_double_field("x_shift")); + TRY_RESULT(y_shift, object.get_required_double_field("y_shift")); + TRY_RESULT(scale, object.get_required_double_field("scale")); return make_object(mask_index_to_point(point), x_shift, y_shift, scale); } @@ -6928,17 +6932,17 @@ td::Result> Client::get_input_sticker(c auto &object = value.get_object(); - TRY_RESULT(sticker, get_json_object_string_field(object, "sticker")); + TRY_RESULT(sticker, object.get_optional_string_field("sticker")); auto input_file = get_input_file(query, td::Slice(), sticker, false); if (input_file == nullptr) { return td::Status::Error(400, "sticker not found"); } - TRY_RESULT(emoji_list, get_json_object_field(object, "emoji_list", td::JsonValue::Type::Array, false)); + TRY_RESULT(emoji_list, object.extract_required_field("emoji_list", td::JsonValue::Type::Array)); TRY_RESULT(emojis, get_sticker_emojis(std::move(emoji_list))); - TRY_RESULT(mask_position, get_mask_position(get_json_object_field_force(object, "mask_position"))); + TRY_RESULT(mask_position, get_mask_position(object.extract_field("mask_position"))); td::vector input_keywords; - if (has_json_object_field(object, "keywords")) { - TRY_RESULT(keywords, get_json_object_field(object, "keywords", td::JsonValue::Type::Array, false)); + if (object.has_field("keywords")) { + TRY_RESULT(keywords, object.extract_required_field("keywords", td::JsonValue::Type::Array)); for (auto &keyword : keywords.get_array()) { if (keyword.type() != td::JsonValue::Type::String) { return td::Status::Error(400, "keyword must be a string"); @@ -7076,24 +7080,24 @@ td::Result Client::get_passport_element_hash(td::Slice encoded_hash) td::Result> Client::get_passport_element_error_source( td::JsonObject &object) { - TRY_RESULT(source, get_json_object_string_field(object, "source")); + TRY_RESULT(source, object.get_optional_string_field("source")); if (source.empty() || source == "unspecified") { - TRY_RESULT(element_hash, get_json_object_string_field(object, "element_hash", false)); + TRY_RESULT(element_hash, object.get_required_string_field("element_hash")); TRY_RESULT(hash, get_passport_element_hash(element_hash)); return make_object(hash); } if (source == "data") { - TRY_RESULT(data_hash, get_json_object_string_field(object, "data_hash", false)); + TRY_RESULT(data_hash, object.get_required_string_field("data_hash")); TRY_RESULT(hash, get_passport_element_hash(data_hash)); - TRY_RESULT(field_name, get_json_object_string_field(object, "field_name", false)); + TRY_RESULT(field_name, object.get_required_string_field("field_name")); return make_object(field_name, hash); } if (source == "file" || source == "selfie" || source == "translation_file" || source == "front_side" || source == "reverse_side") { - TRY_RESULT(file_hash, get_json_object_string_field(object, "file_hash", false)); + TRY_RESULT(file_hash, object.get_required_string_field("file_hash")); TRY_RESULT(hash, get_passport_element_hash(file_hash)); if (source == "front_side") { @@ -7115,7 +7119,7 @@ td::Result> Client:: } if (source == "files" || source == "translation_files") { td::vector input_hashes; - TRY_RESULT(file_hashes, get_json_object_field(object, "file_hashes", td::JsonValue::Type::Array, false)); + TRY_RESULT(file_hashes, object.extract_required_field("file_hashes", td::JsonValue::Type::Array)); for (auto &input_hash : file_hashes.get_array()) { if (input_hash.type() != td::JsonValue::Type::String) { return td::Status::Error(400, "hash must be a string"); @@ -7142,12 +7146,12 @@ td::Result> Client::get_pa auto &object = value.get_object(); - TRY_RESULT(input_type, get_json_object_string_field(object, "type", false)); + TRY_RESULT(input_type, object.get_required_string_field("type")); auto type = get_passport_element_type(input_type); if (type == nullptr) { return td::Status::Error(400, "wrong Telegram Passport element type specified"); } - TRY_RESULT(message, get_json_object_string_field(object, "message", false)); + TRY_RESULT(message, object.get_required_string_field("message")); TRY_RESULT(source, get_passport_element_error_source(object)); return make_object(std::move(type), message, std::move(source)); @@ -7199,7 +7203,7 @@ td::Result> Client::get_caption(const } td::Result> Client::get_text_entity_type(td::JsonObject &object) { - TRY_RESULT(type, get_json_object_string_field(object, "type", false)); + TRY_RESULT(type, object.get_required_string_field("type")); if (type.empty()) { return td::Status::Error("Type is not specified"); } @@ -7223,24 +7227,25 @@ td::Result> Client::get_text_entity_t return make_object(); } if (type == "pre") { - TRY_RESULT(language, get_json_object_string_field(object, "language")); + TRY_RESULT(language, object.get_optional_string_field("language")); if (language.empty()) { return make_object(); } return make_object(language); } if (type == "text_link") { - TRY_RESULT(url, get_json_object_string_field(object, "url", false)); + TRY_RESULT(url, object.get_required_string_field("url")); return make_object(url); } if (type == "text_mention") { - TRY_RESULT(user, get_json_object_field(object, "user", td::JsonValue::Type::Object, false)); + TRY_RESULT(user, object.extract_required_field("user", td::JsonValue::Type::Object)); CHECK(user.type() == td::JsonValue::Type::Object); - TRY_RESULT(user_id, get_json_object_long_field(user.get_object(), "id", false)); + const auto &user_object = user.get_object(); + TRY_RESULT(user_id, user_object.get_required_long_field("id")); return make_object(user_id); } if (type == "custom_emoji") { - TRY_RESULT(custom_emoji_id, get_json_object_long_field(object, "custom_emoji_id", false)); + TRY_RESULT(custom_emoji_id, object.get_required_long_field("custom_emoji_id")); return make_object(custom_emoji_id); } if (type == "mention" || type == "hashtag" || type == "cashtag" || type == "bot_command" || type == "url" || @@ -7257,8 +7262,8 @@ td::Result> Client::get_text_entity(td::J } auto &object = value.get_object(); - TRY_RESULT(offset, get_json_object_int_field(object, "offset", false)); - TRY_RESULT(length, get_json_object_int_field(object, "length", false)); + TRY_RESULT(offset, object.get_required_int_field("offset")); + TRY_RESULT(length, object.get_required_int_field("length")); TRY_RESULT(type, get_text_entity_type(object)); if (type == nullptr) { @@ -7374,30 +7379,29 @@ td::Result> Client::get_chat_permiss auto &object = value.get_object(); auto status = [&] { - TRY_RESULT_ASSIGN(can_send_messages, get_json_object_bool_field(object, "can_send_messages")); - TRY_RESULT_ASSIGN(can_send_polls, get_json_object_bool_field(object, "can_send_polls")); - TRY_RESULT_ASSIGN(can_send_other_messages, get_json_object_bool_field(object, "can_send_other_messages")); - TRY_RESULT_ASSIGN(can_add_web_page_previews, get_json_object_bool_field(object, "can_add_web_page_previews")); - TRY_RESULT_ASSIGN(can_change_info, get_json_object_bool_field(object, "can_change_info")); - TRY_RESULT_ASSIGN(can_invite_users, get_json_object_bool_field(object, "can_invite_users")); - TRY_RESULT_ASSIGN(can_pin_messages, get_json_object_bool_field(object, "can_pin_messages")); - if (has_json_object_field(object, "can_manage_topics")) { - TRY_RESULT_ASSIGN(can_manage_topics, get_json_object_bool_field(object, "can_manage_topics")); + TRY_RESULT_ASSIGN(can_send_messages, object.get_optional_bool_field("can_send_messages")); + TRY_RESULT_ASSIGN(can_send_polls, object.get_optional_bool_field("can_send_polls")); + TRY_RESULT_ASSIGN(can_send_other_messages, object.get_optional_bool_field("can_send_other_messages")); + TRY_RESULT_ASSIGN(can_add_web_page_previews, object.get_optional_bool_field("can_add_web_page_previews")); + TRY_RESULT_ASSIGN(can_change_info, object.get_optional_bool_field("can_change_info")); + TRY_RESULT_ASSIGN(can_invite_users, object.get_optional_bool_field("can_invite_users")); + TRY_RESULT_ASSIGN(can_pin_messages, object.get_optional_bool_field("can_pin_messages")); + if (object.has_field("can_manage_topics")) { + TRY_RESULT_ASSIGN(can_manage_topics, object.get_optional_bool_field("can_manage_topics")); } else { can_manage_topics = can_pin_messages; } - if (has_json_object_field(object, "can_send_audios") || has_json_object_field(object, "can_send_documents") || - has_json_object_field(object, "can_send_photos") || has_json_object_field(object, "can_send_videos") || - has_json_object_field(object, "can_send_video_notes") || - has_json_object_field(object, "can_send_voice_notes")) { - TRY_RESULT_ASSIGN(can_send_audios, get_json_object_bool_field(object, "can_send_audios")); - TRY_RESULT_ASSIGN(can_send_documents, get_json_object_bool_field(object, "can_send_documents")); - TRY_RESULT_ASSIGN(can_send_photos, get_json_object_bool_field(object, "can_send_photos")); - TRY_RESULT_ASSIGN(can_send_videos, get_json_object_bool_field(object, "can_send_videos")); - TRY_RESULT_ASSIGN(can_send_video_notes, get_json_object_bool_field(object, "can_send_video_notes")); - TRY_RESULT_ASSIGN(can_send_voice_notes, get_json_object_bool_field(object, "can_send_voice_notes")); + if (object.has_field("can_send_audios") || object.has_field("can_send_documents") || + object.has_field("can_send_photos") || object.has_field("can_send_videos") || + object.has_field("can_send_video_notes") || object.has_field("can_send_voice_notes")) { + TRY_RESULT_ASSIGN(can_send_audios, object.get_optional_bool_field("can_send_audios")); + TRY_RESULT_ASSIGN(can_send_documents, object.get_optional_bool_field("can_send_documents")); + TRY_RESULT_ASSIGN(can_send_photos, object.get_optional_bool_field("can_send_photos")); + TRY_RESULT_ASSIGN(can_send_videos, object.get_optional_bool_field("can_send_videos")); + TRY_RESULT_ASSIGN(can_send_video_notes, object.get_optional_bool_field("can_send_video_notes")); + TRY_RESULT_ASSIGN(can_send_voice_notes, object.get_optional_bool_field("can_send_voice_notes")); } else { - TRY_RESULT(can_send_media_messages, get_json_object_bool_field(object, "can_send_media_messages")); + TRY_RESULT(can_send_media_messages, object.get_optional_bool_field("can_send_media_messages")); can_send_audios = can_send_media_messages; can_send_documents = can_send_media_messages; can_send_photos = can_send_media_messages; @@ -7476,23 +7480,23 @@ td::Result> Client::get_input_me auto &object = input_media.get_object(); - TRY_RESULT(input_caption, get_json_object_string_field(object, "caption")); - TRY_RESULT(parse_mode, get_json_object_string_field(object, "parse_mode")); - auto entities = get_json_object_field_force(object, "caption_entities"); + TRY_RESULT(input_caption, object.get_optional_string_field("caption")); + TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); + auto entities = object.extract_field("caption_entities"); TRY_RESULT(caption, get_formatted_text(std::move(input_caption), std::move(parse_mode), std::move(entities))); - // TRY_RESULT(self_destruct_time, get_json_object_int_field(object, "self_destruct_time")); + // TRY_RESULT(self_destruct_time, object.get_optional_int_field("self_destruct_time")); int32 self_destruct_time = 0; - TRY_RESULT(has_spoiler, get_json_object_bool_field(object, "has_spoiler")); - TRY_RESULT(media, get_json_object_string_field(object, "media")); + TRY_RESULT(has_spoiler, object.get_optional_bool_field("has_spoiler")); + TRY_RESULT(media, object.get_optional_string_field("media")); auto input_file = get_input_file(query, td::Slice(), media, false); if (input_file == nullptr) { return td::Status::Error("media not found"); } - TRY_RESULT(thumbnail, get_json_object_string_field(object, "thumbnail")); + TRY_RESULT(thumbnail, object.get_optional_string_field("thumbnail")); if (thumbnail.empty()) { - TRY_RESULT_ASSIGN(thumbnail, get_json_object_string_field(object, "thumb")); + TRY_RESULT_ASSIGN(thumbnail, object.get_optional_string_field("thumb")); } auto thumbnail_input_file = get_input_file(query, td::Slice(), thumbnail, true); if (thumbnail_input_file == nullptr) { @@ -7506,16 +7510,16 @@ td::Result> Client::get_input_me input_thumbnail = make_object(std::move(thumbnail_input_file), 0, 0); } - TRY_RESULT(type, get_json_object_string_field(object, "type", false)); + TRY_RESULT(type, object.get_required_string_field("type")); if (type == "photo") { return make_object(std::move(input_file), nullptr, td::vector(), 0, 0, std::move(caption), self_destruct_time, has_spoiler); } if (type == "video") { - TRY_RESULT(width, get_json_object_int_field(object, "width")); - TRY_RESULT(height, get_json_object_int_field(object, "height")); - TRY_RESULT(duration, get_json_object_int_field(object, "duration")); - TRY_RESULT(supports_streaming, get_json_object_bool_field(object, "supports_streaming")); + TRY_RESULT(width, object.get_optional_int_field("width")); + TRY_RESULT(height, object.get_optional_int_field("height")); + TRY_RESULT(duration, object.get_optional_int_field("duration")); + TRY_RESULT(supports_streaming, object.get_optional_bool_field("supports_streaming")); width = td::clamp(width, 0, MAX_LENGTH); height = td::clamp(height, 0, MAX_LENGTH); duration = td::clamp(duration, 0, MAX_DURATION); @@ -7528,9 +7532,9 @@ td::Result> Client::get_input_me return td::Status::Error(PSLICE() << "type \"" << type << "\" can't be used in sendMediaGroup"); } if (type == "animation") { - TRY_RESULT(width, get_json_object_int_field(object, "width")); - TRY_RESULT(height, get_json_object_int_field(object, "height")); - TRY_RESULT(duration, get_json_object_int_field(object, "duration")); + TRY_RESULT(width, object.get_optional_int_field("width")); + TRY_RESULT(height, object.get_optional_int_field("height")); + TRY_RESULT(duration, object.get_optional_int_field("duration")); width = td::clamp(width, 0, MAX_LENGTH); height = td::clamp(height, 0, MAX_LENGTH); duration = td::clamp(duration, 0, MAX_DURATION); @@ -7539,15 +7543,15 @@ td::Result> Client::get_input_me has_spoiler); } if (type == "audio") { - TRY_RESULT(duration, get_json_object_int_field(object, "duration")); - TRY_RESULT(title, get_json_object_string_field(object, "title")); - TRY_RESULT(performer, get_json_object_string_field(object, "performer")); + TRY_RESULT(duration, object.get_optional_int_field("duration")); + TRY_RESULT(title, object.get_optional_string_field("title")); + TRY_RESULT(performer, object.get_optional_string_field("performer")); duration = td::clamp(duration, 0, MAX_DURATION); return make_object(std::move(input_file), std::move(input_thumbnail), duration, title, performer, std::move(caption)); } if (type == "document") { - TRY_RESULT(disable_content_type_detection, get_json_object_bool_field(object, "disable_content_type_detection")); + TRY_RESULT(disable_content_type_detection, object.get_optional_bool_field("disable_content_type_detection")); return make_object(std::move(input_file), std::move(input_thumbnail), disable_content_type_detection || for_album, std::move(caption)); } From 0868ee6beb9581a19e3f07f8e989eba4910d3985 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 31 Jul 2023 20:01:53 +0300 Subject: [PATCH 17/45] Simplify reply markup parsing. --- telegram-bot-api/Client.cpp | 147 +++++++++++++----------------------- 1 file changed, 54 insertions(+), 93 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index cabd34f..67bcf1d 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5747,114 +5747,75 @@ td::Result> Client::get_reply_markup(con td::Result> Client::get_reply_markup(td::JsonValue &&value, BotUserIds &bot_user_ids) { - td::vector>> rows; - td::vector>> inline_rows; - td::Slice input_field_placeholder; - bool resize_keyboard = false; - bool is_persistent = false; - bool one_time = false; - bool remove = false; - bool is_personal = false; - bool force_reply = false; - if (value.type() != td::JsonValue::Type::Object) { return td::Status::Error(400, "Object expected as reply markup"); } - for (auto &field_value : value.get_object().field_values_) { - if (field_value.first == "keyboard") { - auto keyboard = std::move(field_value.second); - if (keyboard.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Field \"keyboard\" of the ReplyKeyboardMarkup must be an Array"); - } - for (auto &row : keyboard.get_array()) { - td::vector> new_row; - if (row.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Field \"keyboard\" of the ReplyKeyboardMarkup must be an Array of Arrays"); - } - for (auto &button : row.get_array()) { - auto r_button = get_keyboard_button(button); - if (r_button.is_error()) { - return td::Status::Error(400, PSLICE() << "Can't parse keyboard button: " << r_button.error().message()); - } - new_row.push_back(r_button.move_as_ok()); - } + auto &object = value.get_object(); - rows.push_back(std::move(new_row)); + td::vector>> rows; + TRY_RESULT(keyboard, object.extract_optional_field("keyboard", td::JsonValue::Type::Array)); + if (keyboard.type() == td::JsonValue::Type::Array) { + for (auto &row : keyboard.get_array()) { + td::vector> new_row; + if (row.type() != td::JsonValue::Type::Array) { + return td::Status::Error(400, "Field \"keyboard\" must be an Array of Arrays"); } - } else if (field_value.first == "inline_keyboard") { - auto inline_keyboard = std::move(field_value.second); - if (inline_keyboard.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, "Field \"inline_keyboard\" of the InlineKeyboardMarkup must be an Array"); + for (auto &button : row.get_array()) { + auto r_button = get_keyboard_button(button); + if (r_button.is_error()) { + return td::Status::Error(400, PSLICE() << "Can't parse keyboard button: " << r_button.error().message()); + } + new_row.push_back(r_button.move_as_ok()); } - for (auto &inline_row : inline_keyboard.get_array()) { - td::vector> new_inline_row; - if (inline_row.type() != td::JsonValue::Type::Array) { - return td::Status::Error(400, - "Field \"inline_keyboard\" of the InlineKeyboardMarkup must be an Array of Arrays"); - } - for (auto &button : inline_row.get_array()) { - auto r_button = get_inline_keyboard_button(button, bot_user_ids); - if (r_button.is_error()) { - return td::Status::Error(400, PSLICE() - << "Can't parse inline keyboard button: " << r_button.error().message()); - } - new_inline_row.push_back(r_button.move_as_ok()); - } - inline_rows.push_back(std::move(new_inline_row)); - } - } else if (field_value.first == "resize_keyboard") { - if (field_value.second.type() != td::JsonValue::Type::Boolean) { - return td::Status::Error(400, - "Field \"resize_keyboard\" of the ReplyKeyboardMarkup must be of the type Boolean"); - } - resize_keyboard = field_value.second.get_boolean(); - } else if (field_value.first == "is_persistent") { - if (field_value.second.type() != td::JsonValue::Type::Boolean) { - return td::Status::Error(400, "Field \"is_persistent\" of the ReplyKeyboardMarkup must be of the type Boolean"); - } - is_persistent = field_value.second.get_boolean(); - } else if (field_value.first == "one_time_keyboard") { - if (field_value.second.type() != td::JsonValue::Type::Boolean) { - return td::Status::Error(400, - "Field \"one_time_keyboard\" of the ReplyKeyboardMarkup must be of the type Boolean"); - } - one_time = field_value.second.get_boolean(); - } else if (field_value.first == "hide_keyboard" || field_value.first == "remove_keyboard") { - if (field_value.second.type() != td::JsonValue::Type::Boolean) { - return td::Status::Error(400, - "Field \"remove_keyboard\" of the ReplyKeyboardRemove must be of the type Boolean"); - } - remove = field_value.second.get_boolean(); - } else if (field_value.first == "personal_keyboard" || field_value.first == "selective") { - if (field_value.second.type() != td::JsonValue::Type::Boolean) { - return td::Status::Error(400, "Field \"selective\" of the reply markup must be of the type Boolean"); - } - is_personal = field_value.second.get_boolean(); - } else if (field_value.first == "force_reply_keyboard" || field_value.first == "force_reply") { - if (field_value.second.type() != td::JsonValue::Type::Boolean) { - return td::Status::Error(400, "Field \"force_reply\" of the reply markup must be of the type Boolean"); - } - force_reply = field_value.second.get_boolean(); - } else if (field_value.first == "input_field_placeholder") { - if (field_value.second.type() != td::JsonValue::Type::String) { - return td::Status::Error(400, - "Field \"input_field_placeholder\" of the reply markup must be of the type String"); - } - input_field_placeholder = field_value.second.get_string(); + rows.push_back(std::move(new_row)); } } + td::vector>> inline_rows; + TRY_RESULT(inline_keyboard, object.extract_optional_field("inline_keyboard", td::JsonValue::Type::Array)); + if (inline_keyboard.type() == td::JsonValue::Type::Array) { + for (auto &inline_row : inline_keyboard.get_array()) { + td::vector> new_inline_row; + if (inline_row.type() != td::JsonValue::Type::Array) { + return td::Status::Error(400, + "Field \"inline_keyboard\" of the InlineKeyboardMarkup must be an Array of Arrays"); + } + for (auto &button : inline_row.get_array()) { + auto r_button = get_inline_keyboard_button(button, bot_user_ids); + if (r_button.is_error()) { + return td::Status::Error(400, PSLICE() + << "Can't parse inline keyboard button: " << r_button.error().message()); + } + new_inline_row.push_back(r_button.move_as_ok()); + } + + inline_rows.push_back(std::move(new_inline_row)); + } + } + + TRY_RESULT(hide_keyboard, object.get_optional_bool_field("hide_keyboard")); + TRY_RESULT(remove_keyboard, object.get_optional_bool_field("remove_keyboard")); + TRY_RESULT(personal_keyboard, object.get_optional_bool_field("personal_keyboard")); + TRY_RESULT(selective, object.get_optional_bool_field("selective")); + TRY_RESULT(force_reply_keyboard, object.get_optional_bool_field("force_reply_keyboard")); + TRY_RESULT(force_reply, object.get_optional_bool_field("force_reply")); + TRY_RESULT(input_field_placeholder, object.get_optional_string_field("input_field_placeholder")); + bool is_personal = personal_keyboard || selective; + object_ptr result; if (!rows.empty()) { - result = make_object(std::move(rows), is_persistent, resize_keyboard, one_time, - is_personal, input_field_placeholder.str()); + TRY_RESULT(resize_keyboard, object.get_optional_bool_field("resize_keyboard")); + TRY_RESULT(one_time_keyboard, object.get_optional_bool_field("one_time_keyboard")); + TRY_RESULT(is_persistent, object.get_optional_bool_field("is_persistent")); + result = make_object(std::move(rows), is_persistent, resize_keyboard, + one_time_keyboard, is_personal, input_field_placeholder); } else if (!inline_rows.empty()) { result = make_object(std::move(inline_rows)); - } else if (remove) { + } else if (hide_keyboard || remove_keyboard) { result = make_object(is_personal); - } else if (force_reply) { - result = make_object(is_personal, input_field_placeholder.str()); + } else if (force_reply || force_reply_keyboard) { + result = make_object(is_personal, input_field_placeholder); } if (result == nullptr || result->get_id() != td_api::replyMarkupInlineKeyboard::ID) { bot_user_ids.unresolved_bot_usernames_.clear(); From 980f98299ff618d75ba4da80f188648a3fcc6784 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 8 Aug 2023 18:00:24 +0300 Subject: [PATCH 18/45] Improve logging of big queries. --- telegram-bot-api/Query.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/Query.cpp b/telegram-bot-api/Query.cpp index 4162f8a..cf7d377 100644 --- a/telegram-bot-api/Query.cpp +++ b/telegram-bot-api/Query.cpp @@ -117,7 +117,23 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query) { sb << "[bot" << td::rpad(query.token().str(), 46, ' ') << "][time:" << padded_time << ']' << td::tag("method", td::lpad(query.method().str(), 20, ' ')); if (!query.args().empty()) { - sb << td::oneline(PSLICE() << query.args()); + sb << '{'; + for (const auto &arg : query.args()) { + sb << '['; + if (arg.first.size() > 128) { + sb << '<' << arg.first.size() << '>' << td::oneline(arg.first.substr(0, 128)) << "..."; + } else { + sb << td::oneline(arg.first); + } + sb << ':'; + if (arg.second.size() > 4096) { + sb << '<' << arg.second.size() << '>' << td::oneline(arg.second.substr(0, 4096)) << "..."; + } else { + sb << td::oneline(arg.second); + } + sb << ']'; + } + sb << '}'; } if (!query.files().empty()) { sb << query.files(); From 375b5d1b7c14d8b735db3a7a819a96f37f13ac27 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 17 Aug 2023 00:34:34 +0300 Subject: [PATCH 19/45] Update TDLib to 1.8.16. --- td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td b/td index 5388843..203e8cf 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 53888437cf11aca258aae7e76552a38c1750d6e7 +Subproject commit 203e8cf9c27ee703a8a90b225fc58c2b687bfe6d From 8b2b62bd6fac578a0c949621cd77d7eb9024bf30 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 18 Aug 2023 18:39:30 +0300 Subject: [PATCH 20/45] Update version to 6.8. --- CMakeLists.txt | 2 +- telegram-bot-api/telegram-bot-api.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fadd00d..2ba03f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.7.1 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.8 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 11bae39..19b87f8 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "6.7.1"; + parameters->version_ = "6.8"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); From 89383695edb4b5b1783b33028de7e0cad88507cf Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 31 Aug 2023 22:51:07 +0300 Subject: [PATCH 21/45] Make watchdog timeouts more precise. --- telegram-bot-api/ClientManager.cpp | 2 +- telegram-bot-api/telegram-bot-api.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index b94ab75..0cdfe05 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -531,7 +531,7 @@ void ClientManager::raw_event(const td::Event::Raw &event) { void ClientManager::timeout_expired() { send_closure(watchdog_id_, &Watchdog::kick); - set_timeout_in(WATCHDOG_TIMEOUT / 2); + set_timeout_in(WATCHDOG_TIMEOUT / 10); double now = td::Time::now(); if (now > next_tqueue_gc_time_) { diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 19b87f8..08bafb8 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -562,7 +562,7 @@ int main(int argc, char *argv[]) { if (now >= start_time + 600) { auto guard = sched.get_main_guard(); send_closure(watchdog_id, &Watchdog::kick); - next_watchdog_kick_time = now + WATCHDOG_TIMEOUT / 2; + next_watchdog_kick_time = now + WATCHDOG_TIMEOUT / 10; } if (!need_dump_statistics.test_and_set() || now > last_dump_time + 300.0) { From 1cab23c1f1e8bb6cc696bb9a16ddeda291a00910 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 3 Sep 2023 00:55:10 +0300 Subject: [PATCH 22/45] Improve ServerCpuStat. --- telegram-bot-api/ClientManager.cpp | 2 +- telegram-bot-api/Stats.cpp | 24 ++++++++++++++++-------- telegram-bot-api/Stats.h | 17 ++++------------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 0cdfe05..b1ea210 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -214,7 +214,7 @@ void ClientManager::get_stats(td::Promise promise, auto now = td::Time::now(); auto top_clients = get_top_clients(50, id_filter); - sb << stat_.get_description() << '\n'; + sb << BotStatActor::get_description() << '\n'; if (id_filter.empty()) { sb << "uptime\t" << now - parameters_->start_time_ << '\n'; sb << "bot_count\t" << clients_.size() << '\n'; diff --git a/telegram-bot-api/Stats.cpp b/telegram-bot-api/Stats.cpp index 350caf1..d682610 100644 --- a/telegram-bot-api/Stats.cpp +++ b/telegram-bot-api/Stats.cpp @@ -7,6 +7,7 @@ #include "telegram-bot-api/Stats.h" #include "td/utils/common.h" +#include "td/utils/logging.h" #include "td/utils/port/thread.h" #include "td/utils/SliceBuilder.h" #include "td/utils/StringBuilder.h" @@ -19,17 +20,24 @@ ServerCpuStat::ServerCpuStat() { } } -void ServerCpuStat::add_event(const td::CpuStat &cpu_stat, double now) { - std::lock_guard guard(mutex_); - for (auto &stat : stat_) { - stat.add_event(cpu_stat, now); +void ServerCpuStat::update(double now) { + auto r_cpu_stat = td::cpu_stat(); + if (r_cpu_stat.is_error()) { + return; } + + auto &cpu_stat = instance(); + std::lock_guard guard(cpu_stat.mutex_); + for (auto &stat : cpu_stat.stat_) { + stat.add_event(r_cpu_stat.ok(), now); + } + LOG(WARNING) << "CPU usage: " << cpu_stat.stat_[1].get_stat(now).as_vector()[0].value_; } -td::string ServerCpuStat::get_description() const { +td::string ServerCpuStat::get_description() { td::string res = "DURATION"; for (auto &descr : DESCR) { - res += "\t"; + res += '\t'; res += descr; } return res; @@ -37,7 +45,7 @@ td::string ServerCpuStat::get_description() const { static td::string to_percentage(td::uint64 ticks, td::uint64 total_ticks) { static double multiplier = 100.0 * (td::thread::hardware_concurrency() ? td::thread::hardware_concurrency() : 1); - return PSTRING() << (static_cast(ticks) / static_cast(total_ticks) * multiplier) << "%"; + return PSTRING() << (static_cast(ticks) / static_cast(total_ticks) * multiplier) << '%'; } td::vector CpuStat::as_vector() const { @@ -140,7 +148,7 @@ td::vector BotStatActor::as_vector(double now) { return res; } -td::string BotStatActor::get_description() const { +td::string BotStatActor::get_description() { td::string res = "DURATION"; for (auto &descr : DESCR) { res += "\t"; diff --git a/telegram-bot-api/Stats.h b/telegram-bot-api/Stats.h index ae38426..119f72f 100644 --- a/telegram-bot-api/Stats.h +++ b/telegram-bot-api/Stats.h @@ -9,7 +9,6 @@ #include "td/actor/actor.h" #include "td/utils/common.h" -#include "td/utils/logging.h" #include "td/utils/port/Stat.h" #include "td/utils/Time.h" #include "td/utils/TimedStat.h" @@ -49,16 +48,10 @@ class ServerCpuStat { static ServerCpuStat stat; return stat; } - static void update(double now) { - auto r_event = td::cpu_stat(); - if (r_event.is_error()) { - return; - } - instance().add_event(r_event.ok(), now); - LOG(WARNING) << "CPU usage: " << instance().stat_[1].get_stat(now).as_vector()[0].value_; - } - td::string get_description() const; + static void update(double now); + + static td::string get_description(); td::vector as_vector(double now); @@ -71,8 +64,6 @@ class ServerCpuStat { td::TimedStat stat_[SIZE]; ServerCpuStat(); - - void add_event(const td::CpuStat &stat, double now); }; class ServerBotInfo { @@ -183,7 +174,7 @@ class BotStatActor final : public td::Actor { td::vector as_vector(double now); - td::string get_description() const; + static td::string get_description(); double get_score(double now); From 18b5f287f76a7490bd072ed04b5cf239e8ff2a44 Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 3 Sep 2023 01:03:53 +0300 Subject: [PATCH 23/45] Update CPU statistics on a dedicated thread. --- telegram-bot-api/ClientParameters.h | 7 ++++++- telegram-bot-api/telegram-bot-api.cpp | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/telegram-bot-api/ClientParameters.h b/telegram-bot-api/ClientParameters.h index b22031d..b8558f9 100644 --- a/telegram-bot-api/ClientParameters.h +++ b/telegram-bot-api/ClientParameters.h @@ -93,9 +93,14 @@ struct SharedData { return 10; } - static td::int32 get_thread_count() { + static td::int32 get_statistics_thread_id() { + // the thread for CPU usage updating return 11; } + + static td::int32 get_thread_count() { + return 12; + } }; struct ClientParameters { diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 08bafb8..a942534 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -556,7 +556,9 @@ int main(int argc, char *argv[]) { next_cron_time = now; } next_cron_time += 1.0; - ServerCpuStat::update(now); + auto guard = sched.get_main_guard(); + td::Scheduler::instance()->run_on_scheduler(SharedData::get_statistics_thread_id(), + [](td::Unit) { ServerCpuStat::update(td::Time::now()); }); } if (now >= start_time + 600) { From 87cdeaadb6d588a0fc90826da0ddc9a86471447c Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 6 Sep 2023 16:17:47 +0300 Subject: [PATCH 24/45] Update TDLib to 1.8.17. --- td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td b/td index 203e8cf..edc5284 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 203e8cf9c27ee703a8a90b225fc58c2b687bfe6d +Subproject commit edc528457f109c188a909ffa07ad73539d3cbf02 From 70670d7217fec8bc73f79de95dd1d7159c225600 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 6 Sep 2023 17:06:21 +0300 Subject: [PATCH 25/45] Explicitly disallow message updates with "channel_chat_created" content. --- telegram-bot-api/Client.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 67bcf1d..cea1026 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -11092,6 +11092,11 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptris_supergroup && message->content_->get_id() == td_api::messageSupergroupChatCreate::ID) { + // don't send message about channel creation, even the bot was added at exactly the same time + return true; + } } if (message->self_destruct_time_ > 0) { From 95ff757c73787f6e9ecca6acd2957f27ac92b6ad Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 6 Sep 2023 18:48:41 +0300 Subject: [PATCH 26/45] Immediately return an error if more than 50 inline query results are provided. --- telegram-bot-api/Client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index cea1026..33d4f31 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -6232,6 +6232,10 @@ td::Result>> Clien if (values.type() != td::JsonValue::Type::Array) { return td::Status::Error(400, "Expected an Array of inline query results"); } + constexpr std::size_t MAX_INLINE_QUERY_RESULT_COUNT = 50; + if (values.get_array().size() > MAX_INLINE_QUERY_RESULT_COUNT) { + return td::Status::Error(400, "Too many inline query results specified"); + } td::vector> inline_query_results; for (auto &value : values.get_array()) { From e58e8d3989e7f2fa3dbe0c6019ad0ee9658a769b Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 8 Sep 2023 18:09:08 +0300 Subject: [PATCH 27/45] Improve query logging. --- telegram-bot-api/Query.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Query.cpp b/telegram-bot-api/Query.cpp index cf7d377..39a59da 100644 --- a/telegram-bot-api/Query.cpp +++ b/telegram-bot-api/Query.cpp @@ -43,7 +43,7 @@ Query::Query(td::vector &&container, td::Slice token, bool is_t } td::to_lower_inplace(method_); start_timestamp_ = td::Time::now(); - LOG(INFO) << "QUERY: create " << td::tag("ptr", this) << *this; + LOG(INFO) << "Query " << this << ": " << *this; if (shared_data_) { shared_data_->query_count_.fetch_add(1, std::memory_order_relaxed); if (method_ != "getupdates") { @@ -85,7 +85,7 @@ void Query::set_stat_actor(td::ActorId stat_actor) { void Query::set_ok(td::BufferSlice result) { CHECK(state_ == State::Query); - LOG(INFO) << "QUERY: got ok " << td::tag("ptr", this) << td::tag("text", result.as_slice()); + LOG(INFO) << "Query " << this << ": " << td::tag("method", method_) << td::tag("text", result.as_slice()); answer_ = std::move(result); state_ = State::OK; http_status_code_ = 200; @@ -93,7 +93,7 @@ void Query::set_ok(td::BufferSlice result) { } void Query::set_error(int http_status_code, td::BufferSlice result) { - LOG(INFO) << "QUERY: got error " << td::tag("ptr", this) << td::tag("code", http_status_code) + LOG(INFO) << "Query " << this << ": " << td::tag("method", method_) << td::tag("code", http_status_code) << td::tag("text", result.as_slice()); CHECK(state_ == State::Query); answer_ = std::move(result); @@ -115,7 +115,7 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query) { auto padded_time = td::lpad(PSTRING() << td::format::as_time(td::Time::now_cached() - query.start_timestamp()), 10, ' '); sb << "[bot" << td::rpad(query.token().str(), 46, ' ') << "][time:" << padded_time << ']' - << td::tag("method", td::lpad(query.method().str(), 20, ' ')); + << td::tag("method", td::lpad(query.method().str(), 25, ' ')); if (!query.args().empty()) { sb << '{'; for (const auto &arg : query.args()) { From 4e8ba658387087b447f7cf75423757a8101f71e6 Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 13 Sep 2023 23:14:57 +0300 Subject: [PATCH 28/45] Update TDLib to 1.8.18. --- td | 2 +- telegram-bot-api/Client.cpp | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/td b/td index edc5284..e79f540 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit edc528457f109c188a909ffa07ad73539d3cbf02 +Subproject commit e79f5409378e3e4b56d870619f3154ba2842a996 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 33d4f31..26434ce 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -6466,7 +6466,7 @@ td::Result> Client::get_inlin if (input_message_content == nullptr) { input_message_content = make_object(nullptr, nullptr, td::vector(), 0, 0, - std::move(caption), 0, false); + std::move(caption), nullptr, false); } return make_object(id, title, description, thumbnail_url, photo_url, photo_width, photo_height, std::move(reply_markup), @@ -6533,7 +6533,7 @@ td::Result> Client::get_inlin if (input_message_content == nullptr) { input_message_content = make_object(nullptr, nullptr, td::vector(), video_duration, video_width, - video_height, false, std::move(caption), 0, false); + video_height, false, std::move(caption), nullptr, false); } return make_object(id, title, description, thumbnail_url, video_url, mime_type, video_width, video_height, video_duration, @@ -7449,8 +7449,6 @@ td::Result> Client::get_input_me TRY_RESULT(parse_mode, object.get_optional_string_field("parse_mode")); auto entities = object.extract_field("caption_entities"); TRY_RESULT(caption, get_formatted_text(std::move(input_caption), std::move(parse_mode), std::move(entities))); - // TRY_RESULT(self_destruct_time, object.get_optional_int_field("self_destruct_time")); - int32 self_destruct_time = 0; TRY_RESULT(has_spoiler, object.get_optional_bool_field("has_spoiler")); TRY_RESULT(media, object.get_optional_string_field("media")); @@ -7478,7 +7476,7 @@ td::Result> Client::get_input_me TRY_RESULT(type, object.get_required_string_field("type")); if (type == "photo") { return make_object(std::move(input_file), nullptr, td::vector(), 0, 0, - std::move(caption), self_destruct_time, has_spoiler); + std::move(caption), nullptr, has_spoiler); } if (type == "video") { TRY_RESULT(width, object.get_optional_int_field("width")); @@ -7491,7 +7489,7 @@ td::Result> Client::get_input_me return make_object(std::move(input_file), std::move(input_thumbnail), td::vector(), duration, width, height, supports_streaming, - std::move(caption), self_destruct_time, has_spoiler); + std::move(caption), nullptr, has_spoiler); } if (for_album && type == "animation") { return td::Status::Error(PSLICE() << "type \"" << type << "\" can't be used in sendMediaGroup"); @@ -8069,10 +8067,9 @@ td::Status Client::process_send_photo_query(PromisedQueryPtr &query) { return td::Status::Error(400, "There is no photo in the request"); } TRY_RESULT(caption, get_caption(query.get())); - auto self_destruct_time = 0; auto has_spoiler = to_bool(query->arg("has_spoiler")); do_send_message(make_object(std::move(photo), nullptr, td::vector(), 0, 0, - std::move(caption), self_destruct_time, has_spoiler), + std::move(caption), nullptr, has_spoiler), std::move(query)); return td::Status::OK(); } @@ -8099,11 +8096,10 @@ td::Status Client::process_send_video_query(PromisedQueryPtr &query) { int32 height = get_integer_arg(query.get(), "height", 0, 0, MAX_LENGTH); bool supports_streaming = to_bool(query->arg("supports_streaming")); TRY_RESULT(caption, get_caption(query.get())); - auto self_destruct_time = 0; auto has_spoiler = to_bool(query->arg("has_spoiler")); do_send_message(make_object(std::move(video), std::move(thumbnail), td::vector(), duration, width, height, supports_streaming, - std::move(caption), self_destruct_time, has_spoiler), + std::move(caption), nullptr, has_spoiler), std::move(query)); return td::Status::OK(); } @@ -10024,7 +10020,7 @@ void Client::do_send_message(object_ptr input_messa if (file_size > 100000) { auto &last_send_message_time = last_send_message_time_[file_size]; auto now = td::Time::now(); - auto min_delay = td::clamp(static_cast(file_size) * 1e-7, 0.1, 0.5); + auto min_delay = td::clamp(static_cast(file_size) * 1e-7, 0.2, 0.9); auto max_bucket_volume = 1.0; if (last_send_message_time > now + 5.0) { return fail_query_flood_limit_exceeded(std::move(query)); @@ -11103,7 +11099,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptrself_destruct_time_ > 0) { + if (message->self_destruct_type_ != nullptr) { return true; } From 1ec733a3f817404fe72c0f50c0bdb5b17fe76cc5 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 14 Sep 2023 16:57:28 +0300 Subject: [PATCH 29/45] Don't update CPU statistics before returning it to avoid synchronous open of "/proc/stat". --- telegram-bot-api/ClientManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index b1ea210..0f4458c 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -230,7 +230,6 @@ void ClientManager::get_stats(td::Promise promise, LOG(INFO) << "Failed to get memory statistics: " << r_mem_stat.error(); } - ServerCpuStat::update(td::Time::now()); auto cpu_stats = ServerCpuStat::instance().as_vector(td::Time::now()); for (auto &stat : cpu_stats) { sb << stat.key_ << "\t" << stat.value_ << '\n'; From df1fe4c05f9f26c26e38e57c128d79e36241a2b7 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 14 Sep 2023 19:11:12 +0300 Subject: [PATCH 30/45] Fail request early if message/caption/explanation text is too long. --- telegram-bot-api/Client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 26434ce..63c5758 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -7240,6 +7240,10 @@ td::Result> Client::get_text_entity(td::J td::Result> Client::get_formatted_text(td::string text, td::string parse_mode, td::JsonValue &&input_entities) { + if (text.size() > (1 << 15)) { + return td::Status::Error(400, "Text is too long"); + } + td::to_lower_inplace(parse_mode); if (!text.empty() && !parse_mode.empty() && parse_mode != "none") { object_ptr text_parse_mode; From 11d19baa2e992ed3e0f699415bd7a9cde86a748c Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Sep 2023 19:26:43 +0300 Subject: [PATCH 31/45] Update TDLib to 1.8.19. --- td | 2 +- telegram-bot-api/Client.cpp | 37 ++++++++++++++++++------------------- telegram-bot-api/Client.h | 4 +++- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/td b/td index e79f540..2589c3f 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit e79f5409378e3e4b56d870619f3154ba2842a996 +Subproject commit 2589c3fd46925f5d57e4ec79233cd1bd0f5d0c09 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 63c5758..44adf7d 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5095,7 +5095,7 @@ void Client::on_update(object_ptr result) { case td_api::updateMessageSendFailed::ID: { auto update = move_object_as(result); on_message_send_failed(update->message_->chat_id_, update->old_message_id_, update->message_->id_, - td::Status::Error(update->error_code_, update->error_message_)); + std::move(update->error_)); break; } case td_api::updateMessageContent::ID: { @@ -6141,8 +6141,8 @@ td::Result> Client::get_input_me return make_object( make_object(currency, std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), - td::string(), false, need_name, need_phone_number, need_email_address, - need_shipping_address, send_phone_number_to_provider, + td::string(), td::string(), false, need_name, need_phone_number, + need_email_address, need_shipping_address, send_phone_number_to_provider, send_email_address_to_provider, is_flexible), title, description, photo_url, photo_size, photo_width, photo_height, payload, provider_token, provider_data, td::string(), nullptr); @@ -6726,10 +6726,10 @@ td::Result> Client::get_chat TRY_RESULT(can_promote_members, object.get_optional_bool_field("can_promote_members")); TRY_RESULT(can_manage_video_chats, object.get_optional_bool_field("can_manage_video_chats")); TRY_RESULT(is_anonymous, object.get_optional_bool_field("is_anonymous")); - return make_object(can_manage_chat, can_change_info, can_post_messages, - can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_manage_topics, - can_promote_members, can_manage_video_chats, is_anonymous); + return make_object( + can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_manage_topics, can_promote_members, can_manage_video_chats, false, + false, false, is_anonymous); } td::Result> Client::get_chat_administrator_rights( @@ -7640,7 +7640,7 @@ td::Result> Client::get_input_me return make_object( make_object(currency.str(), std::move(prices), max_tip_amount, std::move(suggested_tip_amounts), - td::string(), false, need_name, need_phone_number, need_email_address, + td::string(), td::string(), false, need_name, need_phone_number, need_email_address, need_shipping_address, send_phone_number_to_provider, send_email_address_to_provider, is_flexible), title.str(), description.str(), photo_url.str(), photo_size, photo_width, photo_height, payload.str(), @@ -7775,9 +7775,8 @@ void Client::on_message_send_succeeded(object_ptr &&message, in } } -void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, td::Status result) { - auto error = make_object(result.code(), result.message().str()); - +void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, + object_ptr &&error) { auto query_id = extract_yet_unsent_message_query_id(chat_id, old_message_id); auto &query = *pending_send_message_queries_[query_id]; if (query.is_multisend) { @@ -9163,10 +9162,10 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) { auto is_anonymous = to_bool(query->arg("is_anonymous")); auto status = make_object( td::string(), true, - make_object(can_manage_chat, can_change_info, can_post_messages, - can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_manage_topics, - can_promote_members, can_manage_video_chats, is_anonymous)); + make_object( + can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_manage_topics, can_promote_members, can_manage_video_chats, false, + false, false, is_anonymous)); check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id, status = std::move(status)](int64 chat_id, PromisedQueryPtr query) mutable { auto chat_info = get_chat(chat_id); @@ -11488,17 +11487,17 @@ td::unique_ptr Client::delete_message(int64 chat_id, int64 auto chat_info = get_chat(chat_id); CHECK(chat_info != nullptr); - td::Status error = - td::Status::Error(500, "Internal Server Error: sent message was immediately deleted and can't be returned"); + auto error = make_object( + 500, "Internal Server Error: sent message was immediately deleted and can't be returned"); if (chat_info->type == ChatInfo::Type::Supergroup) { auto supergroup_info = get_supergroup_info(chat_info->supergroup_id); CHECK(supergroup_info != nullptr); if (supergroup_info->status->get_id() == td_api::chatMemberStatusBanned::ID || supergroup_info->status->get_id() == td_api::chatMemberStatusLeft::ID) { if (supergroup_info->is_supergroup) { - error = td::Status::Error(403, "Forbidden: bot is not a member of the supergroup chat"); + error = make_object(403, "Forbidden: bot is not a member of the supergroup chat"); } else { - error = td::Status::Error(403, "Forbidden: bot is not a member of the channel chat"); + error = make_object(403, "Forbidden: bot is not a member of the channel chat"); } } } diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index fa93505..cd10f12 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -505,7 +505,9 @@ class Client final : public WebhookActor::Callback { int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id); void on_message_send_succeeded(object_ptr &&message, int64 old_message_id); - void on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, td::Status result); + + void on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, + object_ptr &&error); static bool init_methods(); From 2b43e08dca8e65048218995b0f8d013954be6f2f Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Sep 2023 19:37:24 +0300 Subject: [PATCH 32/45] Support "can_post_stories", "can_edit_stories" and "can_delete_stories" administrator rights. --- telegram-bot-api/Client.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 44adf7d..2762826 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -6725,11 +6725,14 @@ td::Result> Client::get_chat TRY_RESULT(can_manage_topics, object.get_optional_bool_field("can_manage_topics")); TRY_RESULT(can_promote_members, object.get_optional_bool_field("can_promote_members")); TRY_RESULT(can_manage_video_chats, object.get_optional_bool_field("can_manage_video_chats")); + TRY_RESULT(can_post_stories, object.get_optional_bool_field("can_post_stories")); + TRY_RESULT(can_edit_stories, object.get_optional_bool_field("can_edit_stories")); + TRY_RESULT(can_delete_stories, object.get_optional_bool_field("can_delete_stories")); TRY_RESULT(is_anonymous, object.get_optional_bool_field("is_anonymous")); return make_object( can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_manage_topics, can_promote_members, can_manage_video_chats, false, - false, false, is_anonymous); + can_restrict_members, can_pin_messages, can_manage_topics, can_promote_members, can_manage_video_chats, + can_post_stories, can_edit_stories, can_delete_stories, is_anonymous); } td::Result> Client::get_chat_administrator_rights( @@ -9159,13 +9162,16 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) { auto can_promote_members = to_bool(query->arg("can_promote_members")); auto can_manage_video_chats = to_bool(query->arg("can_manage_voice_chats")) || to_bool(query->arg("can_manage_video_chats")); + auto can_post_stories = to_bool(query->arg("can_post_stories")); + auto can_edit_stories = to_bool(query->arg("can_edit_stories")); + auto can_delete_stories = to_bool(query->arg("can_delete_stories")); auto is_anonymous = to_bool(query->arg("is_anonymous")); auto status = make_object( td::string(), true, make_object( can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_manage_topics, can_promote_members, can_manage_video_chats, false, - false, false, is_anonymous)); + can_restrict_members, can_pin_messages, can_manage_topics, can_promote_members, can_manage_video_chats, + can_post_stories, can_edit_stories, can_delete_stories, is_anonymous)); check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id, status = std::move(status)](int64 chat_id, PromisedQueryPtr query) mutable { auto chat_info = get_chat(chat_id); @@ -10675,6 +10681,11 @@ void Client::json_store_administrator_rights(td::JsonObjectScope &object, const } object("can_promote_members", td::JsonBool(rights->can_promote_members_)); object("can_manage_video_chats", td::JsonBool(rights->can_manage_video_chats_)); + if (chat_type == ChatType::Channel) { + object("can_post_stories", td::JsonBool(rights->can_post_stories_)); + object("can_edit_stories", td::JsonBool(rights->can_edit_stories_)); + object("can_delete_stories", td::JsonBool(rights->can_delete_stories_)); + } object("is_anonymous", td::JsonBool(rights->is_anonymous_)); } From d5783a1545c5627b808b5b970bb55150c3f70b41 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Sep 2023 20:37:10 +0300 Subject: [PATCH 33/45] Add more fields to WriteAccessAllowed. --- telegram-bot-api/Client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 2762826..149e7af 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1764,6 +1764,10 @@ class Client::JsonWriteAccessAllowed final : public td::Jsonable { auto object = scope->enter_object(); if (write_access_allowed_->web_app_ != nullptr) { object("web_app_name", write_access_allowed_->web_app_->short_name_); + } else if (write_access_allowed_->by_request_) { + object("from_request", td::JsonTrue()); + } else { + object("from_attachment_menu", td::JsonTrue()); } } From 9e7b09ff0a6a02d1b8b279f7fe7521a60caff7ab Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 22 Sep 2023 16:23:26 +0300 Subject: [PATCH 34/45] Update version to 6.9. --- CMakeLists.txt | 2 +- telegram-bot-api/telegram-bot-api.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ba03f2..a0b0fec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.8 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.9 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index a942534..ad9bbd8 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "6.8"; + parameters->version_ = "6.9"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); From 5d88023dd1e65b7d0926a71aea4487d6cac3bf13 Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 23 Sep 2023 16:39:14 +0300 Subject: [PATCH 35/45] Update TDLib and version to 6.9.1. --- CMakeLists.txt | 2 +- td | 2 +- telegram-bot-api/telegram-bot-api.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0b0fec..0ea1274 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.9 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.9.1 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/td b/td index 2589c3f..2de39ff 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 2589c3fd46925f5d57e4ec79233cd1bd0f5d0c09 +Subproject commit 2de39ffffe71dc41c538e66085658d21cecbae08 diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index ad9bbd8..a983d5e 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "6.9"; + parameters->version_ = "6.9.1"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); From 9c413c7f1168b34d4835900a15568d5da916e3cc Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 25 Sep 2023 19:39:51 +0300 Subject: [PATCH 36/45] Maintain last time when a file was uploaded for all requests. --- telegram-bot-api/Client.cpp | 83 +++++++++++++++++-------------------- telegram-bot-api/Client.h | 2 +- 2 files changed, 38 insertions(+), 47 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 149e7af..4fbad68 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -7815,7 +7815,7 @@ void Client::on_message_send_failed(int64 chat_id, int64 old_message_id, int64 n } } -void Client::on_cmd(PromisedQueryPtr query) { +void Client::on_cmd(PromisedQueryPtr query, bool force) { LOG(DEBUG) << "Process query " << *query; if (!td_client_.empty() && was_authorized_) { if (query->method() == "close") { @@ -7844,6 +7844,42 @@ void Client::on_cmd(PromisedQueryPtr query) { return fail_query(404, "Not Found: method not found", std::move(query)); } + if (!query->files().empty() && !parameters_->local_mode_ && !force) { + auto file_size = query->files_size(); + if (file_size > 100000) { + auto &last_send_message_time = last_send_message_time_[file_size]; + auto now = td::Time::now(); + auto min_delay = td::clamp(static_cast(file_size) * 1e-7, 0.2, 0.9); + auto max_bucket_volume = 1.0; + if (last_send_message_time > now + 5.0) { + return fail_query_flood_limit_exceeded(std::move(query)); + } + + last_send_message_time = td::max(last_send_message_time + min_delay, now - max_bucket_volume); + LOG(DEBUG) << "Query with files of size " << file_size << " can be processed in " << last_send_message_time - now + << " seconds"; + + td::create_actor( + "DeleteLastSendMessageTimeSleepActor", last_send_message_time + min_delay - (now - max_bucket_volume), + td::PromiseCreator::lambda([actor_id = actor_id(this), file_size, + max_delay = max_bucket_volume + min_delay](td::Result) mutable { + send_closure(actor_id, &Client::delete_last_send_message_time, file_size, max_delay); + })) + .release(); + + if (last_send_message_time > now) { + td::create_actor( + "DoSendMessageSleepActor", last_send_message_time - now, + td::PromiseCreator::lambda( + [actor_id = actor_id(this), query = std::move(query)](td::Result) mutable { + send_closure(actor_id, &Client::on_cmd, std::move(query), true); + })) + .release(); + return; + } + } + } + auto result = (this->*(method_it->second))(query); if (result.is_error()) { fail_query_with_error(std::move(query), result.code(), result.message()); @@ -10027,51 +10063,6 @@ void Client::delete_last_send_message_time(td::int64 file_size, double max_delay void Client::do_send_message(object_ptr input_message_content, PromisedQueryPtr query, bool force) { - if (!parameters_->local_mode_) { - if (!force) { - auto file_size = query->files_size(); - if (file_size > 100000) { - auto &last_send_message_time = last_send_message_time_[file_size]; - auto now = td::Time::now(); - auto min_delay = td::clamp(static_cast(file_size) * 1e-7, 0.2, 0.9); - auto max_bucket_volume = 1.0; - if (last_send_message_time > now + 5.0) { - return fail_query_flood_limit_exceeded(std::move(query)); - } - - last_send_message_time = td::max(last_send_message_time + min_delay, now - max_bucket_volume); - LOG(DEBUG) << "Query with files of size " << file_size << " can be processed in " - << last_send_message_time - now << " seconds"; - - td::create_actor( - "DeleteLastSendMessageTimeSleepActor", last_send_message_time + min_delay - (now - max_bucket_volume), - td::PromiseCreator::lambda([actor_id = actor_id(this), file_size, - max_delay = max_bucket_volume + min_delay](td::Result) mutable { - send_closure(actor_id, &Client::delete_last_send_message_time, file_size, max_delay); - })) - .release(); - - if (last_send_message_time > now) { - td::create_actor( - "DoSendMessageSleepActor", last_send_message_time - now, - td::PromiseCreator::lambda([actor_id = actor_id(this), - input_message_content = std::move(input_message_content), - query = std::move(query)](td::Result) mutable { - send_closure(actor_id, &Client::do_send_message, std::move(input_message_content), std::move(query), - true); - })) - .release(); - return; - } - } - } else { - if (logging_out_ || closing_) { - return fail_query_closing(std::move(query)); - } - CHECK(was_authorized_); - } - } - auto chat_id = query->arg("chat_id"); auto message_thread_id = get_message_id(query.get(), "message_thread_id"); auto reply_to_message_id = get_message_id(query.get(), "reply_to_message_id"); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index cd10f12..b18dc24 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -513,7 +513,7 @@ class Client final : public WebhookActor::Callback { static bool is_local_method(td::Slice method); - void on_cmd(PromisedQueryPtr query); + void on_cmd(PromisedQueryPtr query, bool force = false); td::Status process_get_me_query(PromisedQueryPtr &query); td::Status process_get_my_commands_query(PromisedQueryPtr &query); From f15bc7396e72d9460a462c0ed42f70155e9ddb5c Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 13 Oct 2023 01:15:42 +0300 Subject: [PATCH 37/45] Update TDLib to 1.8.20. --- td | 2 +- telegram-bot-api/Client.cpp | 58 ++++++++++++++++-------------- telegram-bot-api/ClientManager.cpp | 2 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/td b/td index 2de39ff..4ed0b23 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 2de39ffffe71dc41c538e66085658d21cecbae08 +Subproject commit 4ed0b23c9c99868ab4d2d28e8ff244687f7b3144 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 4fbad68..41d77ba 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1762,12 +1762,20 @@ class Client::JsonWriteAccessAllowed final : public td::Jsonable { } void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); - if (write_access_allowed_->web_app_ != nullptr) { - object("web_app_name", write_access_allowed_->web_app_->short_name_); - } else if (write_access_allowed_->by_request_) { - object("from_request", td::JsonTrue()); - } else { - object("from_attachment_menu", td::JsonTrue()); + switch (write_access_allowed_->reason_->get_id()) { + case td_api::botWriteAccessAllowReasonLaunchedWebApp::ID: + object("web_app_name", static_cast( + write_access_allowed_->reason_.get()) + ->web_app_->short_name_); + break; + case td_api::botWriteAccessAllowReasonAcceptedRequest::ID: + object("from_request", td::JsonTrue()); + break; + case td_api::botWriteAccessAllowReasonAddedToAttachmentMenu::ID: + object("from_attachment_menu", td::JsonTrue()); + break; + default: + UNREACHABLE(); } } @@ -2235,18 +2243,6 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { case td_api::messageAnimatedEmoji::ID: UNREACHABLE(); break; - case td_api::messageWebsiteConnected::ID: { - auto chat = client_->get_chat(message_->chat_id); - if (chat->type != ChatInfo::Type::Private) { - break; - } - - auto content = static_cast(message_->content.get()); - if (!content->domain_name_.empty()) { - object("connected_website", content->domain_name_); - } - break; - } case td_api::messagePassportDataSent::ID: break; case td_api::messagePassportDataReceived::ID: { @@ -2293,8 +2289,20 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { case td_api::messageSuggestProfilePhoto::ID: break; case td_api::messageBotWriteAccessAllowed::ID: { + auto chat = client_->get_chat(message_->chat_id); + if (chat->type != ChatInfo::Type::Private) { + break; + } + auto content = static_cast(message_->content.get()); - object("write_access_allowed", JsonWriteAccessAllowed(content)); + if (content->reason_->get_id() == td_api::botWriteAccessAllowReasonConnectedWebsite::ID) { + auto reason = static_cast(content->reason_.get()); + if (!reason->domain_name_.empty()) { + object("connected_website", reason->domain_name_); + } + } else { + object("write_access_allowed", JsonWriteAccessAllowed(content)); + } break; } case td_api::messageUserShared::ID: { @@ -11112,8 +11120,7 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptrforward_info_ != nullptr && - message->forward_info_->origin_->get_id() == td_api::messageForwardOriginMessageImport::ID) { + if (message->import_info_ != nullptr) { return true; } @@ -11544,6 +11551,7 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->initial_message_id = 0; message_info->initial_author_signature = td::string(); message_info->initial_sender_name = td::string(); + message_info->is_automatic_forward = false; if (message->forward_info_ != nullptr) { message_info->initial_send_date = message->forward_info_->date_; auto origin = std::move(message->forward_info_->origin_); @@ -11571,11 +11579,6 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->initial_author_signature = std::move(forward_info->author_signature_); break; } - case td_api::messageForwardOriginMessageImport::ID: { - auto forward_info = move_object_as(origin); - message_info->initial_sender_name = std::move(forward_info->sender_name_); - break; - } default: UNREACHABLE(); } @@ -11583,6 +11586,9 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->is_automatic_forward = from_chat_id != 0 && from_chat_id != chat_id && message->forward_info_->from_message_id_ != 0 && get_chat_type(chat_id) == ChatType::Supergroup && get_chat_type(from_chat_id) == ChatType::Channel; + } else if (message->import_info_ != nullptr) { + message_info->initial_send_date = message->import_info_->date_; + message_info->initial_sender_name = std::move(message->import_info_->sender_name_); } CHECK(message->sender_id_ != nullptr); diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 0f4458c..c47c25a 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -350,7 +350,7 @@ void ClientManager::start_up() { parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db); auto &webhook_db = *parameters_->shared_data_->webhook_db_; - for (auto key_value : webhook_db.get_all()) { + for (const auto &key_value : webhook_db.get_all()) { if (!token_range_(td::to_integer(key_value.first))) { LOG(WARNING) << "DROP WEBHOOK: " << key_value.first << " ---> " << key_value.second; webhook_db.erase(key_value.first); From d836f78e414b0d5becc754d3d816618d9e680336 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 23 Oct 2023 11:53:20 +0300 Subject: [PATCH 38/45] Log skipped updates. --- telegram-bot-api/Client.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 41d77ba..92151f8 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -10836,6 +10836,8 @@ void Client::add_update_impl(UpdateType update_type, const td::VirtuallyJsonable last_update_creation_time_ = td::Time::now(); if (((allowed_update_types_ >> static_cast(update_type)) & 1) == 0) { + LOG(DEBUG) << "Skip unallowed update of the type " << static_cast(update_type) << ", allowed update mask is " + << allowed_update_types_; return; } @@ -11034,6 +11036,9 @@ void Client::add_update_chat_member(object_ptr &&updat auto webhook_queue_id = update->chat_id_ + (static_cast(is_my ? 5 : 6) << 33); auto update_type = is_my ? UpdateType::MyChatMember : UpdateType::ChatMember; add_update(update_type, JsonChatMemberUpdated(update.get(), this), left_time, webhook_queue_id); + } else { + LOG(DEBUG) << "Skip updateChatMember with date " << update->date_ << ", because current date is " + << get_unix_time(); } } @@ -11044,6 +11049,9 @@ void Client::add_update_chat_join_request(object_ptr 0) { auto webhook_queue_id = update->chat_id_ + (static_cast(6) << 33); add_update(UpdateType::ChatJoinRequest, JsonChatJoinRequest(update.get(), this), left_time, webhook_queue_id); + } else { + LOG(DEBUG) << "Skip updateNewChatJoinRequest with date " << update->request_->date_ << ", because current date is " + << get_unix_time(); } } @@ -11090,6 +11098,8 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptredit_date_ == 0 ? message->date_ : message->edit_date_; if (message_date <= get_unix_time() - 86400) { // don't send messages received/edited more than 1 day ago + LOG(DEBUG) << "Skip update about message with date " << message_date << ", because current date is " + << get_unix_time(); return true; } From 9a7a293a8424e59edc6bd6e6549d0c87ffe54498 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 31 Oct 2023 03:10:35 +0300 Subject: [PATCH 39/45] Update TDLib to 1.8.21. --- td | 2 +- telegram-bot-api/Client.cpp | 91 +++++++++++++++++++++++++------------ telegram-bot-api/Client.h | 2 +- 3 files changed, 64 insertions(+), 31 deletions(-) diff --git a/td b/td index 4ed0b23..9184b3e 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 4ed0b23c9c99868ab4d2d28e8ff244687f7b3144 +Subproject commit 9184b3e62de59663a59d3500528aee7e5f0d83fa diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 92151f8..c7de091 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -532,7 +532,8 @@ class Client::JsonVectorEntities final : public td::Jsonable { for (auto &entity : entities_) { auto entity_type = entity->type_->get_id(); if (entity_type != td_api::textEntityTypeBankCardNumber::ID && - entity_type != td_api::textEntityTypeMediaTimestamp::ID) { + entity_type != td_api::textEntityTypeMediaTimestamp::ID && + entity_type != td_api::textEntityTypeBlockQuote::ID) { array << JsonEntity(entity.get(), client_); } } @@ -2317,6 +2318,12 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } case td_api::messageChatSetBackground::ID: break; + case td_api::messagePremiumGiftCode::ID: + break; + case td_api::messagePremiumGiveawayCreated::ID: + break; + case td_api::messagePremiumGiveaway::ID: + break; case td_api::messageStory::ID: object("story", JsonEmptyObject()); break; @@ -5528,9 +5535,9 @@ bool Client::to_bool(td::MutableSlice value) { return value == "true" || value == "yes" || value == "1"; } -td_api::object_ptr Client::get_message_reply_to(int64 reply_to_message_id) { +td_api::object_ptr Client::get_input_message_reply_to(int64 reply_to_message_id) { if (reply_to_message_id > 0) { - return make_object(0, reply_to_message_id); + return make_object(0, reply_to_message_id, nullptr); } return nullptr; } @@ -6168,7 +6175,8 @@ td::Result> Client::get_input_me td_api::object_ptr Client::get_message_send_options(bool disable_notification, bool protect_content) { - return make_object(disable_notification, false, protect_content, false, nullptr, 0); + return make_object(disable_notification, false, protect_content, false, nullptr, 0, + false); } td::Result> Client::get_inline_query_results_button( @@ -7312,7 +7320,9 @@ td::Result> Client::get_input_messa TRY_RESULT(formatted_text, get_formatted_text(std::move(text), std::move(parse_mode), std::move(input_entities))); - return make_object(std::move(formatted_text), disable_web_page_preview, false); + return make_object( + std::move(formatted_text), + make_object(disable_web_page_preview, td::string(), false, false, false), false); } td::Result> Client::get_location(const Query *query) { @@ -8363,17 +8373,17 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { auto message_count = input_message_contents.size(); count += static_cast(message_count); - send_request(make_object( - chat_id, message_thread_id, get_message_reply_to(reply_to_message_id), - get_message_send_options(disable_notification, protect_content), - std::move(input_message_contents), false), - td::make_unique(this, chat_id, message_count, std::move(query))); + send_request( + make_object( + chat_id, message_thread_id, get_input_message_reply_to(reply_to_message_id), + get_message_send_options(disable_notification, protect_content), std::move(input_message_contents)), + td::make_unique(this, chat_id, message_count, std::move(query))); }; check_message_thread(chat_id, message_thread_id, reply_to_message_id, std::move(query), std::move(on_message_thread_checked)); }; check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, - AccessRights::Write, "replied message", std::move(query), std::move(on_success)); + AccessRights::Write, "message to reply", std::move(query), std::move(on_success)); }); return td::Status::OK(); } @@ -10103,7 +10113,7 @@ void Client::do_send_message(object_ptr input_messa count++; send_request(make_object( - chat_id, message_thread_id, get_message_reply_to(reply_to_message_id), + chat_id, message_thread_id, get_input_message_reply_to(reply_to_message_id), get_message_send_options(disable_notification, protect_content), std::move(reply_markup), std::move(input_message_content)), td::make_unique(this, chat_id, std::move(query))); @@ -10112,7 +10122,7 @@ void Client::do_send_message(object_ptr input_messa std::move(on_message_thread_checked)); }; check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply, - AccessRights::Write, "replied message", std::move(query), std::move(on_success)); + AccessRights::Write, "message to reply", std::move(query), std::move(on_success)); }); } @@ -11196,6 +11206,12 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr &message) { - if (message->content_->get_id() == td_api::messagePinMessage::ID) { + auto content_message_id = [&] { + switch (message->content_->get_id()) { + case td_api::messagePinMessage::ID: + return static_cast(message->content_.get())->message_id_; + case td_api::messageGameScore::ID: + return static_cast(message->content_.get())->game_message_id_; + case td_api::messageChatSetBackground::ID: + return static_cast(message->content_.get()) + ->old_background_message_id_; + case td_api::messagePaymentSuccessful::ID: + UNREACHABLE(); + return static_cast(0); + default: + return static_cast(0); + } + }(); + if (content_message_id != 0) { CHECK(message->reply_to_ == nullptr); - return static_cast(message->content_.get())->message_id_; + return content_message_id; } if (message->reply_to_ != nullptr) { switch (message->reply_to_->get_id()) { case td_api::messageReplyToMessage::ID: { auto reply_to = static_cast(message->reply_to_.get()); CHECK(reply_to->message_id_ > 0); - CHECK(reply_to->chat_id_ == message->chat_id_); - return reply_to->message_id_; + if (reply_to->chat_id_ == message->chat_id_ && reply_to->origin_ == nullptr) { + return reply_to->message_id_; + } + break; } case td_api::messageReplyToStory::ID: break; @@ -11235,10 +11269,9 @@ td::int64 Client::get_reply_to_message_id(const object_ptr &mes void Client::drop_reply_to_message_in_another_chat(object_ptr &message) { if (message->reply_to_ != nullptr && message->reply_to_->get_id() == td_api::messageReplyToMessage::ID) { - auto reply_in_chat_id = static_cast(message->reply_to_.get())->chat_id_; - if (reply_in_chat_id != message->chat_id_) { - LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ - << " from another chat " << reply_in_chat_id; + auto *reply_to = static_cast(message->reply_to_.get()); + auto reply_in_chat_id = reply_to->chat_id_; + if (reply_in_chat_id != message->chat_id_ || reply_to->origin_ != nullptr) { message->reply_to_ = nullptr; } } @@ -11566,24 +11599,24 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->initial_send_date = message->forward_info_->date_; auto origin = std::move(message->forward_info_->origin_); switch (origin->get_id()) { - case td_api::messageForwardOriginUser::ID: { - auto forward_info = move_object_as(origin); + case td_api::messageOriginUser::ID: { + auto forward_info = move_object_as(origin); message_info->initial_sender_user_id = forward_info->sender_user_id_; break; } - case td_api::messageForwardOriginChat::ID: { - auto forward_info = move_object_as(origin); + case td_api::messageOriginChat::ID: { + auto forward_info = move_object_as(origin); message_info->initial_sender_chat_id = forward_info->sender_chat_id_; message_info->initial_author_signature = std::move(forward_info->author_signature_); break; } - case td_api::messageForwardOriginHiddenUser::ID: { - auto forward_info = move_object_as(origin); + case td_api::messageOriginHiddenUser::ID: { + auto forward_info = move_object_as(origin); message_info->initial_sender_name = std::move(forward_info->sender_name_); break; } - case td_api::messageForwardOriginChannel::ID: { - auto forward_info = move_object_as(origin); + case td_api::messageOriginChannel::ID: { + auto forward_info = move_object_as(origin); message_info->initial_chat_id = forward_info->chat_id_; message_info->initial_message_id = forward_info->message_id_; message_info->initial_author_signature = std::move(forward_info->author_signature_); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index b18dc24..6d9795a 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -331,7 +331,7 @@ class Client final : public WebhookActor::Callback { static bool to_bool(td::MutableSlice value); - static object_ptr get_message_reply_to(int64 reply_to_message_id); + static object_ptr get_input_message_reply_to(int64 reply_to_message_id); static td::Result> get_keyboard_button(td::JsonValue &button); From f169ae654cecc2944b085f813ca567935e889c5b Mon Sep 17 00:00:00 2001 From: levlam Date: Wed, 1 Nov 2023 23:01:08 +0300 Subject: [PATCH 40/45] Slowly recheck webhook IP addresses after loading them from database. --- telegram-bot-api/WebhookActor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index e2870b6..7e04a67 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -691,7 +691,12 @@ void WebhookActor::handle(td::unique_ptr response) { void WebhookActor::start_up() { max_loaded_updates_ = max_connections_ * 2; - next_ip_address_resolve_time_ = last_success_time_ = td::Time::now() - 3600; + last_success_time_ = td::Time::now() - 2 * IP_ADDRESS_CACHE_TIME; + if (from_db_flag_) { + next_ip_address_resolve_time_ = td::Time::now() + td::Random::fast(0, IP_ADDRESS_CACHE_TIME); + } else { + next_ip_address_resolve_time_ = last_success_time_; + } active_new_connection_flood_.add_limit(0.5, 10); From 9447ce07ea0431ee830f353e2314d6bcdea7a00d Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 4 Nov 2023 02:39:57 +0300 Subject: [PATCH 41/45] Minor improvements. --- telegram-bot-api/Client.cpp | 15 ++++----------- telegram-bot-api/Client.h | 4 ++-- telegram-bot-api/Stats.h | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index c7de091..039e8bc 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -5160,14 +5160,12 @@ void Client::on_update(object_ptr result) { auto update = move_object_as(result); auto chat = std::move(update->chat_); auto chat_info = add_chat(chat->id_); - bool need_warning = false; switch (chat->type_->get_id()) { case td_api::chatTypePrivate::ID: { auto type = move_object_as(chat->type_); chat_info->type = ChatInfo::Type::Private; auto user_id = type->user_id_; chat_info->user_id = user_id; - need_warning = get_user_info(user_id) == nullptr; break; } case td_api::chatTypeBasicGroup::ID: { @@ -5175,7 +5173,6 @@ void Client::on_update(object_ptr result) { chat_info->type = ChatInfo::Type::Group; auto group_id = type->basic_group_id_; chat_info->group_id = group_id; - need_warning = get_group_info(group_id) == nullptr; break; } case td_api::chatTypeSupergroup::ID: { @@ -5183,7 +5180,6 @@ void Client::on_update(object_ptr result) { chat_info->type = ChatInfo::Type::Supergroup; auto supergroup_id = type->supergroup_id_; chat_info->supergroup_id = supergroup_id; - need_warning = get_supergroup_info(supergroup_id) == nullptr; break; } case td_api::chatTypeSecret::ID: @@ -5192,9 +5188,6 @@ void Client::on_update(object_ptr result) { default: UNREACHABLE(); } - if (need_warning) { - LOG(ERROR) << "Received updateNewChat about chat " << chat->id_ << ", but hadn't received corresponding info"; - } chat_info->title = std::move(chat->title_); chat_info->photo_info = std::move(chat->photo_); @@ -10457,8 +10450,8 @@ void Client::set_group_photo(int64 group_id, object_ptr &&pho add_group_info(group_id)->photo = std::move(photo); } -void Client::set_group_description(int64 group_id, td::string &&descripton) { - add_group_info(group_id)->description = std::move(descripton); +void Client::set_group_description(int64 group_id, td::string &&description) { + add_group_info(group_id)->description = std::move(description); } void Client::set_group_invite_link(int64 group_id, td::string &&invite_link) { @@ -10486,8 +10479,8 @@ void Client::set_supergroup_photo(int64 supergroup_id, object_ptrphoto = std::move(photo); } -void Client::set_supergroup_description(int64 supergroup_id, td::string &&descripton) { - add_supergroup_info(supergroup_id)->description = std::move(descripton); +void Client::set_supergroup_description(int64 supergroup_id, td::string &&description) { + add_supergroup_info(supergroup_id)->description = std::move(description); } void Client::set_supergroup_invite_link(int64 supergroup_id, td::string &&invite_link) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 6d9795a..7762aac 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -746,7 +746,7 @@ class Client final : public WebhookActor::Callback { }; static void add_group(GroupInfo *group_info, object_ptr &&group); void set_group_photo(int64 group_id, object_ptr &&photo); - void set_group_description(int64 group_id, td::string &&descripton); + void set_group_description(int64 group_id, td::string &&description); void set_group_invite_link(int64 group_id, td::string &&invite_link); GroupInfo *add_group_info(int64 group_id); const GroupInfo *get_group_info(int64 group_id) const; @@ -774,7 +774,7 @@ class Client final : public WebhookActor::Callback { }; static void add_supergroup(SupergroupInfo *supergroup_info, object_ptr &&supergroup); void set_supergroup_photo(int64 supergroup_id, object_ptr &&photo); - void set_supergroup_description(int64 supergroup_id, td::string &&descripton); + void set_supergroup_description(int64 supergroup_id, td::string &&description); void set_supergroup_invite_link(int64 supergroup_id, td::string &&invite_link); void set_supergroup_sticker_set_id(int64 supergroup_id, int64 sticker_set_id); void set_supergroup_can_set_sticker_set(int64 supergroup_id, bool can_set_sticker_set); diff --git a/telegram-bot-api/Stats.h b/telegram-bot-api/Stats.h index 119f72f..3871053 100644 --- a/telegram-bot-api/Stats.h +++ b/telegram-bot-api/Stats.h @@ -147,7 +147,7 @@ class BotStatActor final : public td::Actor { } BotStatActor(const BotStatActor &) = delete; - BotStatActor &operator=(const BotStatActor &other) = delete; + BotStatActor &operator=(const BotStatActor &) = delete; BotStatActor(BotStatActor &&) = default; BotStatActor &operator=(BotStatActor &&other) noexcept { if (!empty()) { From 34ed6c3512b1767a2e34f9cceeb9e0eb447a846a Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 5 Nov 2023 21:54:58 +0300 Subject: [PATCH 42/45] Store td_api::messageReplyToMessage in MessageInfo. --- telegram-bot-api/Client.cpp | 90 ++++++++++++++++++++++--------------- telegram-bot-api/Client.h | 10 +++-- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 039e8bc..825e99e 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1982,13 +1982,20 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } object("forward_date", message_->initial_send_date); } - if (message_->reply_to_message_id > 0 && need_reply_) { - const MessageInfo *reply_to_message = client_->get_message(message_->chat_id, message_->reply_to_message_id, true); - if (reply_to_message != nullptr) { - object("reply_to_message", JsonMessage(reply_to_message, false, "reply in " + source_, client_)); + if (need_reply_ && message_->reply_to_message != nullptr) { + auto reply_to_message_id = get_same_chat_reply_to_message_id(message_->reply_to_message.get()); + if (reply_to_message_id > 0) { + // internal reply + CHECK(message_->reply_to_message->chat_id_ == message_->chat_id); + const MessageInfo *reply_to_message = client_->get_message(message_->chat_id, reply_to_message_id, true); + if (reply_to_message != nullptr) { + object("reply_to_message", JsonMessage(reply_to_message, false, "reply in " + source_, client_)); + } else { + LOG(INFO) << "Replied to unknown or deleted message " << reply_to_message_id << " in chat " << message_->chat_id + << " while storing " << source_ << ' ' << message_->id; + } } else { - LOG(INFO) << "Replied to unknown or deleted message " << message_->reply_to_message_id << " in chat " - << message_->chat_id << " while storing " << source_ << ' ' << message_->id; + // external reply } } if (message_->media_album_id != 0) { @@ -4423,7 +4430,7 @@ void Client::on_get_reply_message(int64 chat_id, object_ptr rep CHECK(!queue.queue_.empty()); object_ptr &message = queue.queue_.front().message; CHECK(chat_id == message->chat_id_); - int64 reply_to_message_id = get_reply_to_message_id(message); + int64 reply_to_message_id = get_same_chat_reply_to_message_id(message); CHECK(reply_to_message_id > 0); if (reply_to_message == nullptr) { LOG(INFO) << "Can't find message " << reply_to_message_id << " in chat " << chat_id @@ -4468,8 +4475,9 @@ void Client::on_get_callback_query_message(object_ptr message, process_new_callback_query_queue(user_id, state); return; } - LOG(INFO) << "Can't find callback query reply to message " << message_info->reply_to_message_id << " in chat " - << chat_id << ". It may be already deleted"; + auto reply_to_message_id = get_same_chat_reply_to_message_id(message_info->reply_to_message.get()); + LOG(INFO) << "Can't find callback query reply to message " << reply_to_message_id << " in chat " << chat_id + << ". It may be already deleted"; } } else { LOG(INFO) << "Receive callback query " << (state == 1 ? "reply to " : "") << "message " << message_id << " in chat " @@ -10953,7 +10961,8 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { state = 1; } if (state == 1) { - auto reply_to_message_id = message_info == nullptr ? 0 : message_info->reply_to_message_id; + auto reply_to_message_id = + message_info == nullptr ? 0 : get_same_chat_reply_to_message_id(message_info->reply_to_message.get()); if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, false) == nullptr) { queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id), @@ -10968,7 +10977,8 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { return send_request(make_object(message_sticker_set_id), td::make_unique(this, message_sticker_set_id, user_id, 0)); } - auto reply_to_message_id = message_info == nullptr ? 0 : message_info->reply_to_message_id; + auto reply_to_message_id = + message_info == nullptr ? 0 : get_same_chat_reply_to_message_id(message_info->reply_to_message.get()); if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id, true); auto reply_sticker_set_id = @@ -11219,7 +11229,30 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr &message) { +td::int64 Client::get_same_chat_reply_to_message_id(const td_api::messageReplyToMessage *reply_to) { + if (reply_to != nullptr && reply_to->origin_ == nullptr) { + CHECK(reply_to->message_id_ > 0); + return reply_to->message_id_; + } + return 0; +} + +td::int64 Client::get_same_chat_reply_to_message_id(const object_ptr &reply_to) { + if (reply_to != nullptr) { + switch (reply_to->get_id()) { + case td_api::messageReplyToMessage::ID: + return get_same_chat_reply_to_message_id(static_cast(reply_to.get())); + case td_api::messageReplyToStory::ID: + break; + default: + UNREACHABLE(); + break; + } + } + return 0; +} + +td::int64 Client::get_same_chat_reply_to_message_id(const object_ptr &message) { auto content_message_id = [&] { switch (message->content_->get_id()) { case td_api::messagePinMessage::ID: @@ -11241,30 +11274,18 @@ td::int64 Client::get_reply_to_message_id(const object_ptr &mes return content_message_id; } if (message->reply_to_ != nullptr) { - switch (message->reply_to_->get_id()) { - case td_api::messageReplyToMessage::ID: { - auto reply_to = static_cast(message->reply_to_.get()); - CHECK(reply_to->message_id_ > 0); - if (reply_to->chat_id_ == message->chat_id_ && reply_to->origin_ == nullptr) { - return reply_to->message_id_; - } - break; - } - case td_api::messageReplyToStory::ID: - break; - default: - UNREACHABLE(); - break; - } + return get_same_chat_reply_to_message_id(message->reply_to_); } return 0; } -void Client::drop_reply_to_message_in_another_chat(object_ptr &message) { +void Client::drop_internal_reply_to_message_in_another_chat(object_ptr &message) { if (message->reply_to_ != nullptr && message->reply_to_->get_id() == td_api::messageReplyToMessage::ID) { auto *reply_to = static_cast(message->reply_to_.get()); auto reply_in_chat_id = reply_to->chat_id_; - if (reply_in_chat_id != message->chat_id_ || reply_to->origin_ != nullptr) { + if (reply_in_chat_id != message->chat_id_ && reply_to->origin_ == nullptr) { + LOG(ERROR) << "Drop reply to message " << message->id_ << " in chat " << message->chat_id_ + << " from another chat " << reply_in_chat_id; message->reply_to_ = nullptr; } } @@ -11432,9 +11453,9 @@ void Client::process_new_message_queue(int64 chat_id, int state) { CHECK(chat_id == message_ref->chat_id_); int64 message_id = message_ref->id_; - drop_reply_to_message_in_another_chat(message_ref); + drop_internal_reply_to_message_in_another_chat(message_ref); - int64 reply_to_message_id = get_reply_to_message_id(message_ref); + int64 reply_to_message_id = get_same_chat_reply_to_message_id(message_ref); if (state == 0) { if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, false) == nullptr) { queue.has_active_request_ = true; @@ -11660,13 +11681,12 @@ Client::FullMessageId Client::add_message(object_ptr &&message, message_info->is_topic_message = message->is_topic_message_; message_info->author_signature = std::move(message->author_signature_); - drop_reply_to_message_in_another_chat(message); + drop_internal_reply_to_message_in_another_chat(message); if (message->reply_to_ != nullptr && message->reply_to_->get_id() == td_api::messageReplyToMessage::ID) { - message_info->reply_to_message_id = - static_cast(message->reply_to_.get())->message_id_; + message_info->reply_to_message = move_object_as(message->reply_to_); } else { - message_info->reply_to_message_id = 0; + message_info->reply_to_message = nullptr; } if (message_info->content == nullptr || force_update_content) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 7762aac..f9916fe 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -825,7 +825,7 @@ class Client final : public WebhookActor::Callback { td::string initial_author_signature; td::string initial_sender_name; td::string author_signature; - int64 reply_to_message_id = 0; + object_ptr reply_to_message; int64 media_album_id = 0; int64 via_bot_user_id = 0; object_ptr content; @@ -837,9 +837,13 @@ class Client final : public WebhookActor::Callback { mutable bool is_content_changed = false; }; - static int64 get_reply_to_message_id(const object_ptr &message); + static int64 get_same_chat_reply_to_message_id(const td_api::messageReplyToMessage *reply_to); - static void drop_reply_to_message_in_another_chat(object_ptr &message); + static int64 get_same_chat_reply_to_message_id(const object_ptr &reply_to); + + static int64 get_same_chat_reply_to_message_id(const object_ptr &message); + + static void drop_internal_reply_to_message_in_another_chat(object_ptr &message); static td::Slice get_sticker_type(const object_ptr &type); From 0566e21f933251c430d6b020da8d89c45598a03d Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 5 Nov 2023 22:08:06 +0300 Subject: [PATCH 43/45] Keep reply to the top thread message for external replies. --- telegram-bot-api/Client.cpp | 52 +++++++++++++++++++++---------------- telegram-bot-api/Client.h | 6 +++-- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 825e99e..1848b8a 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1982,11 +1982,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { } object("forward_date", message_->initial_send_date); } - if (need_reply_ && message_->reply_to_message != nullptr) { - auto reply_to_message_id = get_same_chat_reply_to_message_id(message_->reply_to_message.get()); + if (need_reply_) { + auto reply_to_message_id = + get_same_chat_reply_to_message_id(message_->reply_to_message.get(), message_->message_thread_id); if (reply_to_message_id > 0) { // internal reply - CHECK(message_->reply_to_message->chat_id_ == message_->chat_id); const MessageInfo *reply_to_message = client_->get_message(message_->chat_id, reply_to_message_id, true); if (reply_to_message != nullptr) { object("reply_to_message", JsonMessage(reply_to_message, false, "reply in " + source_, client_)); @@ -1994,10 +1994,11 @@ void Client::JsonMessage::store(td::JsonValueScope *scope) const { LOG(INFO) << "Replied to unknown or deleted message " << reply_to_message_id << " in chat " << message_->chat_id << " while storing " << source_ << ' ' << message_->id; } - } else { - // external reply } } + if (message_->reply_to_message != nullptr && message_->reply_to_message->origin_ != nullptr) { + // external reply + } if (message_->media_album_id != 0) { object("media_group_id", td::to_string(message_->media_album_id)); } @@ -4436,9 +4437,11 @@ void Client::on_get_reply_message(int64 chat_id, object_ptr rep LOG(INFO) << "Can't find message " << reply_to_message_id << " in chat " << chat_id << ". It is already deleted or inaccessible because of the chosen privacy mode"; } else { - CHECK(chat_id == reply_to_message->chat_id_); - CHECK(reply_to_message_id == reply_to_message->id_); - LOG(INFO) << "Receive reply to message " << reply_to_message_id << " in chat " << chat_id; + if (chat_id != reply_to_message->chat_id_ || reply_to_message_id != reply_to_message->id_) { + LOG(ERROR) << "Expect to get replied message " << reply_to_message_id << " in " << chat_id << ", but receive " + << reply_to_message->id_ << " in " << reply_to_message->chat_id_; + } + LOG(INFO) << "Receive reply to message " << reply_to_message->id_ << " in chat " << reply_to_message->chat_id_; add_message(std::move(reply_to_message)); } @@ -4475,7 +4478,8 @@ void Client::on_get_callback_query_message(object_ptr message, process_new_callback_query_queue(user_id, state); return; } - auto reply_to_message_id = get_same_chat_reply_to_message_id(message_info->reply_to_message.get()); + auto reply_to_message_id = + get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), message_info->message_thread_id); LOG(INFO) << "Can't find callback query reply to message " << reply_to_message_id << " in chat " << chat_id << ". It may be already deleted"; } @@ -10961,8 +10965,10 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { state = 1; } if (state == 1) { - auto reply_to_message_id = - message_info == nullptr ? 0 : get_same_chat_reply_to_message_id(message_info->reply_to_message.get()); + auto reply_to_message_id = message_info == nullptr + ? 0 + : get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), + message_info->message_thread_id); if (reply_to_message_id > 0 && get_message(chat_id, reply_to_message_id, false) == nullptr) { queue.has_active_request_ = true; return send_request(make_object(chat_id, message_id), @@ -10977,8 +10983,10 @@ void Client::process_new_callback_query_queue(int64 user_id, int state) { return send_request(make_object(message_sticker_set_id), td::make_unique(this, message_sticker_set_id, user_id, 0)); } - auto reply_to_message_id = - message_info == nullptr ? 0 : get_same_chat_reply_to_message_id(message_info->reply_to_message.get()); + auto reply_to_message_id = message_info == nullptr + ? 0 + : get_same_chat_reply_to_message_id(message_info->reply_to_message.get(), + message_info->message_thread_id); if (reply_to_message_id > 0) { auto reply_to_message_info = get_message(chat_id, reply_to_message_id, true); auto reply_sticker_set_id = @@ -11229,19 +11237,22 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptrorigin_ == nullptr) { CHECK(reply_to->message_id_ > 0); return reply_to->message_id_; } - return 0; + return message_thread_id; } -td::int64 Client::get_same_chat_reply_to_message_id(const object_ptr &reply_to) { +td::int64 Client::get_same_chat_reply_to_message_id(const object_ptr &reply_to, + int64 message_thread_id) { if (reply_to != nullptr) { switch (reply_to->get_id()) { case td_api::messageReplyToMessage::ID: - return get_same_chat_reply_to_message_id(static_cast(reply_to.get())); + return get_same_chat_reply_to_message_id(static_cast(reply_to.get()), + message_thread_id); case td_api::messageReplyToStory::ID: break; default: @@ -11249,7 +11260,7 @@ td::int64 Client::get_same_chat_reply_to_message_id(const object_ptr &message) { @@ -11273,10 +11284,7 @@ td::int64 Client::get_same_chat_reply_to_message_id(const object_ptrreply_to_ == nullptr); return content_message_id; } - if (message->reply_to_ != nullptr) { - return get_same_chat_reply_to_message_id(message->reply_to_); - } - return 0; + return get_same_chat_reply_to_message_id(message->reply_to_, message->message_thread_id_); } void Client::drop_internal_reply_to_message_in_another_chat(object_ptr &message) { diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index f9916fe..4daa014 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -837,9 +837,11 @@ class Client final : public WebhookActor::Callback { mutable bool is_content_changed = false; }; - static int64 get_same_chat_reply_to_message_id(const td_api::messageReplyToMessage *reply_to); + static int64 get_same_chat_reply_to_message_id(const td_api::messageReplyToMessage *reply_to, + int64 message_thread_id); - static int64 get_same_chat_reply_to_message_id(const object_ptr &reply_to); + static int64 get_same_chat_reply_to_message_id(const object_ptr &reply_to, + int64 message_thread_id); static int64 get_same_chat_reply_to_message_id(const object_ptr &message); From 96d0d1c6689451207bc66d179983c10949322823 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 9 Nov 2023 02:32:21 +0300 Subject: [PATCH 44/45] Update version to 6.9.2. --- CMakeLists.txt | 2 +- telegram-bot-api/telegram-bot-api.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ea1274..b52e30d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.9.1 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.9.2 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index a983d5e..ea0b469 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) { auto start_time = td::Time::now(); auto shared_data = std::make_shared(); auto parameters = std::make_unique(); - parameters->version_ = "6.9.1"; + parameters->version_ = "6.9.2"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); From a35ff4543b066e0401da3704e4fa38209b80589f Mon Sep 17 00:00:00 2001 From: a5r0n Date: Sat, 16 Dec 2023 22:29:04 +0200 Subject: [PATCH 45/45] fix merge conflicts --- telegram-bot-api/Client.cpp | 22 +++++++++++----------- telegram-bot-api/Client.h | 2 +- telegram-bot-api/ClientManager.cpp | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index d7f8b88..c810e6b 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -8530,26 +8530,26 @@ td::int64 Client::get_int64_arg(const Query *query, td::Slice field_name, int64 return td::clamp(td::to_integer(s_arg), min_value, max_value); } -td::Result> Client::get_report_reason(const Query *query, +td::Result> Client::get_report_reason(const Query *query, td::Slice field_name) { auto reason = query->arg(field_name); - object_ptr result; + object_ptr result; if (reason.empty()) { return td::Status::Error(400, "reason is not specified"); } else if (reason == "child_abuse") { - result = make_object(); + result = make_object(); } else if (reason == "copyright") { - result = make_object(); + result = make_object(); } else if (reason == "pornography") { - result = make_object(); + result = make_object(); } else if (reason == "spam") { - result = make_object(); + result = make_object(); } else if (reason == "unrelated_location") { - result = make_object(); + result = make_object(); } else if (reason == "violence") { - result = make_object(); + result = make_object(); } else { - result = make_object(); + result = make_object(); } return std::move(result); } @@ -9232,7 +9232,7 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) { send_request( make_object( chat_id, message_thread_id, get_input_message_reply_to(reply_to_message_id), - get_message_send_options(disable_notification, protect_content), std::move(input_message_contents)), + get_message_send_options(disable_notification, protect_content, std::move(send_at)), std::move(input_message_contents)), td::make_unique(this, chat_id, message_count, std::move(query))); }; check_message_thread(chat_id, message_thread_id, reply_to_message_id, std::move(query), @@ -10972,7 +10972,7 @@ td::Status Client::process_report_chat_query(PromisedQueryPtr &query) { [this, reason = std::move(reason), message_ids = std::move(message_ids)](int64 chat_id, PromisedQueryPtr query) mutable { - send_request(make_object(chat_id, std::move(message_ids), std::move(reason), reason->get_id() == td_api::chatReportReasonCustom::ID ? query->arg("reason").str() : td::string()), + send_request(make_object(chat_id, std::move(message_ids), std::move(reason), reason->get_id() == td_api::reportReasonCustom::ID ? query->arg("reason").str() : td::string()), td::make_unique(std::move(query))); }); return td::Status::OK(); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 47650d5..0ce31ab 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -560,7 +560,7 @@ class Client final : public WebhookActor::Callback { static int64 get_int64_arg(const Query *query, td::Slice field_name, int64 default_value, int64 min_value = std::numeric_limits::min(), int64 max_value = std::numeric_limits::max()); - static td::Result> get_report_reason(const Query *query, + static td::Result> get_report_reason(const Query *query, td::Slice field_name = td::Slice("reason")); static td::Result> get_search_messages_filter( diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 69fef73..f8aba7c 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -496,13 +496,13 @@ void ClientManager::start_up() { parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db); auto concurrent_user_db = td::make_unique>(); - status = concurrent_user_db->init(parameters_->working_directory_ + "user_db.binlog", td::DbKey::empty(), scheduler_id); + status = concurrent_user_db->init(parameters_->working_directory_ + "user_db.binlog", td::DbKey::empty(), SharedData::get_binlog_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 (const auto key_value : webhook_db.get_all()) { + for (const auto &key_value : webhook_db.get_all()) { if (!token_range_(td::to_integer(key_value.first))) { LOG(WARNING) << "DROP WEBHOOK: " << key_value.first << " ---> " << key_value.second; webhook_db.erase(key_value.first);