Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2021-09-26 23:08:36 +02:00
commit 9ca532c6a6
28 changed files with 528 additions and 410 deletions

View File

@ -416,6 +416,7 @@ set(TDLIB_SOURCE
td/telegram/TdDb.cpp td/telegram/TdDb.cpp
td/telegram/TermsOfService.cpp td/telegram/TermsOfService.cpp
td/telegram/ThemeManager.cpp td/telegram/ThemeManager.cpp
td/telegram/TopDialogCategory.cpp
td/telegram/TopDialogManager.cpp td/telegram/TopDialogManager.cpp
td/telegram/UpdatesManager.cpp td/telegram/UpdatesManager.cpp
td/telegram/Venue.cpp td/telegram/Venue.cpp

View File

@ -173,7 +173,7 @@ See [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](ht
TDLib can be used from the Rust programming language through the [JSON](https://github.com/tdlib/td#using-json) interface. TDLib can be used from the Rust programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
See [rust-tdlib](https://github.com/aCLr/rust-tdlib) or [tdlib-rs](https://github.com/agnipau/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects. See [rust-tdlib](https://github.com/aCLr/rust-tdlib), [tdgrand](https://github.com/melix99/tdgrand), or [tdlib-rs](https://github.com/agnipau/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures), See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures),
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), or [tdlib-sys](https://github.com/nuxeh/tdlib-sys), or

View File

@ -4690,7 +4690,7 @@ joinChat chat_id:int53 = Ok;
leaveChat chat_id:int53 = Ok; leaveChat chat_id:int53 = Ok;
//@description Adds a new member to a chat. Members can't be added to private or secret chats //@description Adds a new member to a chat. Members can't be added to private or secret chats
//@chat_id Chat identifier @user_id Identifier of the user @forward_limit The number of earlier messages from the chat to be forwarded to the new member; up to 100. Ignored for supergroups and channels //@chat_id Chat identifier @user_id Identifier of the user @forward_limit The number of earlier messages from the chat to be forwarded to the new member; up to 100. Ignored for supergroups and channels, or if the added user is a bot
addChatMember chat_id:int53 user_id:int53 forward_limit:int32 = Ok; addChatMember chat_id:int53 user_id:int53 forward_limit:int32 = Ok;
//@description Adds multiple new members to a chat. Currently this method is only available for supergroups and channels. This method can't be used to join a chat. Members can't be added to a channel if it has more than 200 members //@description Adds multiple new members to a chat. Currently this method is only available for supergroups and channels. This method can't be used to join a chat. Members can't be added to a channel if it has more than 200 members

View File

@ -784,7 +784,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
td->notification_manager_->init(); td->notification_manager_->init();
td->stickers_manager_->init(); td->stickers_manager_->init();
td->theme_manager_->init(); td->theme_manager_->init();
send_closure(td->top_dialog_manager_, &TopDialogManager::do_start_up); td->top_dialog_manager_->init();
td->updates_manager_->get_difference("on_get_authorization"); td->updates_manager_->get_difference("on_get_authorization");
td->on_online_updated(false, true); td->on_online_updated(false, true);
if (!is_bot()) { if (!is_bot()) {

View File

@ -25,7 +25,7 @@ class Td;
/** /**
* This is a low-level Actor interface for interaction with TDLib. The interface is a lot more flexible than * This is a low-level Actor interface for interaction with TDLib. The interface is a lot more flexible than
* the Client interface, however, for most usages the Client interface should be sufficient. * the ClientManager interface, however, for most usages the ClientManager interface should be sufficient.
*/ */
class ClientActor final : public Actor { class ClientActor final : public Actor {
public: public:

View File

@ -18,7 +18,7 @@
namespace td { namespace td {
// TODO can be made private in TDLib 2.0 // TODO can be removed in TDLib 2.0
class ClientJson final { class ClientJson final {
public: public:
void send(Slice request); void send(Slice request);

View File

@ -1743,7 +1743,7 @@ bool InlineQueriesManager::load_recently_used_bots(Promise<Unit> &promise) {
auto bot_ids = full_split(saved_bot_ids, ','); auto bot_ids = full_split(saved_bot_ids, ',');
string saved_bots = G()->td_db()->get_binlog_pmc()->get("recently_used_inline_bot_usernames"); string saved_bots = G()->td_db()->get_binlog_pmc()->get("recently_used_inline_bot_usernames");
auto bot_usernames = full_split(saved_bots, ','); auto bot_usernames = full_split(saved_bots, ',');
if (bot_ids.empty() && bot_usernames.empty()) { if (bot_ids.empty()) {
recently_used_bots_loaded_ = 2; recently_used_bots_loaded_ = 2;
if (!recently_used_bot_user_ids_.empty()) { if (!recently_used_bot_user_ids_.empty()) {
save_recently_used_bots(); save_recently_used_bots();
@ -1757,29 +1757,19 @@ bool InlineQueriesManager::load_recently_used_bots(Promise<Unit> &promise) {
auto newly_used_bots = std::move(recently_used_bot_user_ids_); auto newly_used_bots = std::move(recently_used_bot_user_ids_);
recently_used_bot_user_ids_.clear(); recently_used_bot_user_ids_.clear();
if (bot_ids.empty()) { for (auto it = bot_ids.rbegin(); it != bot_ids.rend(); ++it) {
// legacy, can be removed in the future UserId user_id(to_integer<int64>(*it));
for (auto it = bot_usernames.rbegin(); it != bot_usernames.rend(); ++it) { if (td_->contacts_manager_->have_user(user_id)) {
auto dialog_id = td_->messages_manager_->resolve_dialog_username(*it); update_bot_usage(user_id);
if (dialog_id.get_type() == DialogType::User) { } else {
update_bot_usage(dialog_id.get_user_id()); LOG(ERROR) << "Can't find " << user_id;
}
}
} else {
for (auto it = bot_ids.rbegin(); it != bot_ids.rend(); ++it) {
UserId user_id(to_integer<int64>(*it));
if (td_->contacts_manager_->have_user(user_id)) {
update_bot_usage(user_id);
} else {
LOG(ERROR) << "Can't find " << user_id;
}
} }
} }
for (auto it = newly_used_bots.rbegin(); it != newly_used_bots.rend(); ++it) { for (auto it = newly_used_bots.rbegin(); it != newly_used_bots.rend(); ++it) {
update_bot_usage(*it); update_bot_usage(*it);
} }
recently_used_bots_loaded_ = 2; recently_used_bots_loaded_ = 2;
if (!newly_used_bots.empty() || (bot_ids.empty() && !bot_usernames.empty())) { if (!newly_used_bots.empty()) {
save_recently_used_bots(); save_recently_used_bots();
} }
return true; return true;
@ -1788,7 +1778,7 @@ bool InlineQueriesManager::load_recently_used_bots(Promise<Unit> &promise) {
resolve_recent_inline_bots_multipromise_.add_promise(std::move(promise)); resolve_recent_inline_bots_multipromise_.add_promise(std::move(promise));
if (recently_used_bots_loaded_ == 0) { if (recently_used_bots_loaded_ == 0) {
resolve_recent_inline_bots_multipromise_.set_ignore_errors(true); resolve_recent_inline_bots_multipromise_.set_ignore_errors(true);
if (bot_ids.empty() || !G()->parameters().use_chat_info_db) { if (!G()->parameters().use_chat_info_db) {
for (auto &bot_username : bot_usernames) { for (auto &bot_username : bot_usernames) {
td_->messages_manager_->search_public_dialog(bot_username, false, td_->messages_manager_->search_public_dialog(bot_username, false,
resolve_recent_inline_bots_multipromise_.get_promise()); resolve_recent_inline_bots_multipromise_.get_promise());

View File

@ -15434,7 +15434,7 @@ bool MessagesManager::have_dialog(DialogId dialog_id) const {
return dialogs_.count(dialog_id) > 0; return dialogs_.count(dialog_id) > 0;
} }
void MessagesManager::load_dialogs(vector<DialogId> dialog_ids, Promise<Unit> &&promise) { void MessagesManager::load_dialogs(vector<DialogId> dialog_ids, Promise<vector<DialogId>> &&promise) {
LOG(INFO) << "Load chats " << format::as_array(dialog_ids); LOG(INFO) << "Load chats " << format::as_array(dialog_ids);
Dependencies dependencies; Dependencies dependencies;
@ -15445,13 +15445,14 @@ void MessagesManager::load_dialogs(vector<DialogId> dialog_ids, Promise<Unit> &&
} }
resolve_dependencies_force(td_, dependencies, "load_dialogs"); resolve_dependencies_force(td_, dependencies, "load_dialogs");
td::remove_if(dialog_ids, [this](DialogId dialog_id) { return !have_dialog_info(dialog_id); });
for (auto dialog_id : dialog_ids) { for (auto dialog_id : dialog_ids) {
if (dialog_id.is_valid()) { force_create_dialog(dialog_id, "load_dialogs");
force_create_dialog(dialog_id, "load_dialogs");
}
} }
promise.set_value(Unit()); LOG(INFO) << "Loaded chats " << format::as_array(dialog_ids);
promise.set_value(std::move(dialog_ids));
} }
bool MessagesManager::load_dialog(DialogId dialog_id, int left_tries, Promise<Unit> &&promise) { bool MessagesManager::load_dialog(DialogId dialog_id, int left_tries, Promise<Unit> &&promise) {

View File

@ -516,7 +516,7 @@ class MessagesManager final : public Actor {
bool load_dialog(DialogId dialog_id, int left_tries, Promise<Unit> &&promise); bool load_dialog(DialogId dialog_id, int left_tries, Promise<Unit> &&promise);
void load_dialogs(vector<DialogId> dialog_ids, Promise<Unit> &&promise); void load_dialogs(vector<DialogId> dialog_ids, Promise<vector<DialogId>> &&promise);
void load_dialog_filter(DialogFilterId dialog_id, bool force, Promise<Unit> &&promise); void load_dialog_filter(DialogFilterId dialog_id, bool force, Promise<Unit> &&promise);

View File

@ -46,12 +46,14 @@ void RecentDialogList::save_dialogs() const {
SliceBuilder sb; SliceBuilder sb;
for (auto &dialog_id : dialog_ids_) { for (auto &dialog_id : dialog_ids_) {
sb << ','; sb << ',';
if (!G()->parameters().use_message_db) { if (!G()->parameters().use_chat_info_db) {
// if there is no dialog database, prefer to save dialogs by username // if there is no dialog info database, prefer to save dialogs by username
string username; string username;
switch (dialog_id.get_type()) { switch (dialog_id.get_type()) {
case DialogType::User: case DialogType::User:
username = td_->contacts_manager_->get_user_username(dialog_id.get_user_id()); if (!td_->contacts_manager_->is_user_contact(dialog_id.get_user_id())) {
username = td_->contacts_manager_->get_user_username(dialog_id.get_user_id());
}
break; break;
case DialogType::Chat: case DialogType::Chat:
break; break;
@ -104,17 +106,13 @@ void RecentDialogList::load_dialogs(Promise<Unit> &&promise) {
dialog_ids.push_back(DialogId(to_integer<int64>(found_dialog))); dialog_ids.push_back(DialogId(to_integer<int64>(found_dialog)));
} }
} }
if (!dialog_ids.empty()) { if (!dialog_ids.empty() && !G()->parameters().use_chat_info_db) {
if (G()->parameters().use_message_db) { td_->messages_manager_->get_dialogs_from_list(
td_->messages_manager_->load_dialogs(std::move(dialog_ids), mpas.get_promise()); DialogListId(FolderId::main()), 102,
} else { PromiseCreator::lambda([promise = mpas.get_promise()](td_api::object_ptr<td_api::chats> &&chats) mutable {
td_->messages_manager_->get_dialogs_from_list( promise.set_value(Unit());
DialogListId(FolderId::main()), 102, }));
PromiseCreator::lambda([promise = mpas.get_promise()](td_api::object_ptr<td_api::chats> &&chats) mutable { td_->contacts_manager_->search_contacts("", 1, mpas.get_promise());
promise.set_value(Unit());
}));
td_->contacts_manager_->search_contacts("", 1, mpas.get_promise());
}
} }
lock.set_value(Unit()); lock.set_value(Unit());
@ -135,6 +133,7 @@ void RecentDialogList::on_load_dialogs(vector<string> &&found_dialogs) {
dialog_id = DialogId(to_integer<int64>(*it)); dialog_id = DialogId(to_integer<int64>(*it));
} }
if (dialog_id.is_valid() && removed_dialog_ids_.count(dialog_id) == 0 && if (dialog_id.is_valid() && removed_dialog_ids_.count(dialog_id) == 0 &&
td_->messages_manager_->have_dialog_info(dialog_id) &&
td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) { td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) {
td_->messages_manager_->force_create_dialog(dialog_id, "recent dialog"); td_->messages_manager_->force_create_dialog(dialog_id, "recent dialog");
do_add_dialog(dialog_id); do_add_dialog(dialog_id);

View File

@ -4583,7 +4583,7 @@ void StickersManager::invalidate_old_featured_sticker_sets() {
auto promises = std::move(load_old_featured_sticker_sets_queries_); auto promises = std::move(load_old_featured_sticker_sets_queries_);
load_old_featured_sticker_sets_queries_.clear(); load_old_featured_sticker_sets_queries_.clear();
for (auto &promise : promises) { for (auto &promise : promises) {
promise.set_error(Status::Error(400, "Trending sticker sets was updated")); promise.set_error(Status::Error(400, "Trending sticker sets were updated"));
} }
} }

View File

@ -166,7 +166,7 @@ void StorageManager::on_all_files(FileGcParameters gc_parameters, Result<FileSta
create_gc_worker(); create_gc_worker();
send_closure(gc_worker_, &FileGcWorker::run_gc, std::move(gc_parameters), std::move(r_file_stats.ok_ref().all_files), send_closure(gc_worker_, &FileGcWorker::run_gc, std::move(gc_parameters), r_file_stats.ok_ref().get_all_files(),
PromiseCreator::lambda([actor_id = actor_id(this), dialog_limit](Result<FileGcResult> r_file_gc_result) { PromiseCreator::lambda([actor_id = actor_id(this), dialog_limit](Result<FileGcResult> r_file_gc_result) {
send_closure(actor_id, &StorageManager::on_gc_finished, dialog_limit, std::move(r_file_gc_result)); send_closure(actor_id, &StorageManager::on_gc_finished, dialog_limit, std::move(r_file_gc_result));
})); }));
@ -264,11 +264,13 @@ void StorageManager::send_stats(FileStats &&stats, int32 dialog_limit, std::vect
stats.apply_dialog_limit(dialog_limit); stats.apply_dialog_limit(dialog_limit);
auto dialog_ids = stats.get_dialog_ids(); auto dialog_ids = stats.get_dialog_ids();
auto promise = PromiseCreator::lambda([promises = std::move(promises), stats = std::move(stats)](Unit) mutable { auto promise = PromiseCreator::lambda(
for (auto &promise : promises) { [promises = std::move(promises), stats = std::move(stats)](vector<DialogId> dialog_ids) mutable {
promise.set_value(FileStats(stats)); stats.apply_dialog_ids(dialog_ids);
} for (auto &promise : promises) {
}); promise.set_value(FileStats(stats));
}
});
send_closure(G()->messages_manager(), &MessagesManager::load_dialogs, std::move(dialog_ids), std::move(promise)); send_closure(G()->messages_manager(), &MessagesManager::load_dialogs, std::move(dialog_ids), std::move(promise));
} }

View File

@ -40,6 +40,7 @@
#include "td/telegram/files/FileId.h" #include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileSourceId.h" #include "td/telegram/files/FileSourceId.h"
#include "td/telegram/files/FileStats.h"
#include "td/telegram/files/FileType.h" #include "td/telegram/files/FileType.h"
#include "td/telegram/FolderId.h" #include "td/telegram/FolderId.h"
#include "td/telegram/FullMessageId.h" #include "td/telegram/FullMessageId.h"
@ -3437,12 +3438,12 @@ void Td::on_config_option_updated(const string &name) {
} else if (name == "use_storage_optimizer") { } else if (name == "use_storage_optimizer") {
send_closure(storage_manager_, &StorageManager::update_use_storage_optimizer); send_closure(storage_manager_, &StorageManager::update_use_storage_optimizer);
} else if (name == "rating_e_decay") { } else if (name == "rating_e_decay") {
return send_closure(top_dialog_manager_, &TopDialogManager::update_rating_e_decay); return send_closure(top_dialog_manager_actor_, &TopDialogManager::update_rating_e_decay);
} else if (name == "disable_contact_registered_notifications") { } else if (name == "disable_contact_registered_notifications") {
send_closure(notification_manager_actor_, send_closure(notification_manager_actor_,
&NotificationManager::on_disable_contact_registered_notifications_changed); &NotificationManager::on_disable_contact_registered_notifications_changed);
} else if (name == "disable_top_chats") { } else if (name == "disable_top_chats") {
send_closure(top_dialog_manager_, &TopDialogManager::update_is_enabled, send_closure(top_dialog_manager_actor_, &TopDialogManager::update_is_enabled,
!G()->shared_config().get_option_boolean(name)); !G()->shared_config().get_option_boolean(name));
} else if (name == "connection_parameters") { } else if (name == "connection_parameters") {
if (G()->mtproto_header().set_parameters(G()->shared_config().get_option_string(name))) { if (G()->mtproto_header().set_parameters(G()->shared_config().get_option_string(name))) {
@ -3616,6 +3617,8 @@ void Td::dec_actor_refcnt() {
LOG(DEBUG) << "MemoryManager was cleared" << timer; LOG(DEBUG) << "MemoryManager was cleared" << timer;
theme_manager_.reset(); theme_manager_.reset();
LOG(DEBUG) << "ThemeManager was cleared" << timer; LOG(DEBUG) << "ThemeManager was cleared" << timer;
top_dialog_manager_.reset();
LOG(DEBUG) << "TopDialogManager was cleared" << timer;
updates_manager_.reset(); updates_manager_.reset();
LOG(DEBUG) << "UpdatesManager was cleared" << timer; LOG(DEBUG) << "UpdatesManager was cleared" << timer;
video_notes_manager_.reset(); video_notes_manager_.reset();
@ -3776,8 +3779,6 @@ void Td::clear() {
LOG(DEBUG) << "SecretChatsManager was cleared" << timer; LOG(DEBUG) << "SecretChatsManager was cleared" << timer;
storage_manager_.reset(); storage_manager_.reset();
LOG(DEBUG) << "StorageManager was cleared" << timer; LOG(DEBUG) << "StorageManager was cleared" << timer;
top_dialog_manager_.reset();
LOG(DEBUG) << "TopDialogManager was cleared" << timer;
verify_phone_number_manager_.reset(); verify_phone_number_manager_.reset();
LOG(DEBUG) << "VerifyPhoneNumberManager was cleared" << timer; LOG(DEBUG) << "VerifyPhoneNumberManager was cleared" << timer;
@ -3823,6 +3824,8 @@ void Td::clear() {
LOG(DEBUG) << "MemoryManager actor was cleared" << timer; LOG(DEBUG) << "MemoryManager actor was cleared" << timer;
theme_manager_actor_.reset(); theme_manager_actor_.reset();
LOG(DEBUG) << "ThemeManager actor was cleared" << timer; LOG(DEBUG) << "ThemeManager actor was cleared" << timer;
top_dialog_manager_actor_.reset();
LOG(DEBUG) << "TopDialogManager actor was cleared" << timer;
updates_manager_actor_.reset(); updates_manager_actor_.reset();
LOG(DEBUG) << "UpdatesManager actor was cleared" << timer; LOG(DEBUG) << "UpdatesManager actor was cleared" << timer;
web_pages_manager_actor_.reset(); web_pages_manager_actor_.reset();
@ -4293,6 +4296,9 @@ void Td::init_managers() {
theme_manager_ = make_unique<ThemeManager>(this, create_reference()); theme_manager_ = make_unique<ThemeManager>(this, create_reference());
theme_manager_actor_ = register_actor("ThemeManager", theme_manager_.get()); theme_manager_actor_ = register_actor("ThemeManager", theme_manager_.get());
G()->set_theme_manager(theme_manager_actor_.get()); G()->set_theme_manager(theme_manager_actor_.get());
top_dialog_manager_ = make_unique<TopDialogManager>(this, create_reference());
top_dialog_manager_actor_ = register_actor("TopDialogManager", top_dialog_manager_.get());
G()->set_top_dialog_manager(top_dialog_manager_actor_.get());
updates_manager_ = make_unique<UpdatesManager>(this, create_reference()); updates_manager_ = make_unique<UpdatesManager>(this, create_reference());
updates_manager_actor_ = register_actor("UpdatesManager", updates_manager_.get()); updates_manager_actor_ = register_actor("UpdatesManager", updates_manager_.get());
G()->set_updates_manager(updates_manager_actor_.get()); G()->set_updates_manager(updates_manager_actor_.get());
@ -4316,8 +4322,6 @@ void Td::init_managers() {
secret_chats_manager_ = create_actor<SecretChatsManager>("SecretChatsManager", create_reference()); secret_chats_manager_ = create_actor<SecretChatsManager>("SecretChatsManager", create_reference());
G()->set_secret_chats_manager(secret_chats_manager_.get()); G()->set_secret_chats_manager(secret_chats_manager_.get());
secure_manager_ = create_actor<SecureManager>("SecureManager", create_reference()); secure_manager_ = create_actor<SecureManager>("SecureManager", create_reference());
top_dialog_manager_ = create_actor<TopDialogManager>("TopDialogManager", create_reference());
G()->set_top_dialog_manager(top_dialog_manager_.get());
verify_phone_number_manager_ = create_actor<PhoneNumberManager>( verify_phone_number_manager_ = create_actor<PhoneNumberManager>(
"VerifyPhoneNumberManager", PhoneNumberManager::Type::VerifyPhone, create_reference()); "VerifyPhoneNumberManager", PhoneNumberManager::Type::VerifyPhone, create_reference());
} }
@ -5239,14 +5243,8 @@ void Td::on_request(uint64 id, const td_api::setAutoDownloadSettings &request) {
std::move(promise)); std::move(promise));
} }
void Td::on_request(uint64 id, td_api::getTopChats &request) { void Td::on_request(uint64 id, const td_api::getTopChats &request) {
CHECK_IS_USER(); CHECK_IS_USER();
if (request.category_ == nullptr) {
return send_error_raw(id, 400, "Top chat category must be non-empty");
}
if (request.limit_ <= 0) {
return send_error_raw(id, 400, "Limit must be positive");
}
CREATE_REQUEST_PROMISE(); CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<vector<DialogId>> result) mutable { auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<vector<DialogId>> result) mutable {
if (result.is_error()) { if (result.is_error()) {
@ -5255,23 +5253,15 @@ void Td::on_request(uint64 id, td_api::getTopChats &request) {
promise.set_value(MessagesManager::get_chats_object(-1, result.ok())); promise.set_value(MessagesManager::get_chats_object(-1, result.ok()));
} }
}); });
send_closure(top_dialog_manager_, &TopDialogManager::get_top_dialogs, get_top_dialog_category(*request.category_), top_dialog_manager_->get_top_dialogs(get_top_dialog_category(request.category_), request.limit_,
narrow_cast<size_t>(request.limit_), std::move(query_promise)); std::move(query_promise));
} }
void Td::on_request(uint64 id, const td_api::removeTopChat &request) { void Td::on_request(uint64 id, const td_api::removeTopChat &request) {
CHECK_IS_USER(); CHECK_IS_USER();
if (request.category_ == nullptr) { CREATE_OK_REQUEST_PROMISE();
return send_error_raw(id, 400, "Top chat category must be non-empty"); top_dialog_manager_->remove_dialog(get_top_dialog_category(request.category_), DialogId(request.chat_id_),
} std::move(promise));
DialogId dialog_id(request.chat_id_);
if (!dialog_id.is_valid()) {
return send_error_raw(id, 400, "Invalid chat identifier");
}
send_closure(top_dialog_manager_, &TopDialogManager::remove_dialog, get_top_dialog_category(*request.category_),
dialog_id, messages_manager_->get_input_peer(dialog_id, AccessRights::Read));
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::ok>());
} }
void Td::on_request(uint64 id, const td_api::loadChats &request) { void Td::on_request(uint64 id, const td_api::loadChats &request) {

View File

@ -185,6 +185,8 @@ class Td final : public Actor {
ActorOwn<StickersManager> stickers_manager_actor_; ActorOwn<StickersManager> stickers_manager_actor_;
unique_ptr<ThemeManager> theme_manager_; unique_ptr<ThemeManager> theme_manager_;
ActorOwn<ThemeManager> theme_manager_actor_; ActorOwn<ThemeManager> theme_manager_actor_;
unique_ptr<TopDialogManager> top_dialog_manager_;
ActorOwn<TopDialogManager> top_dialog_manager_actor_;
unique_ptr<UpdatesManager> updates_manager_; unique_ptr<UpdatesManager> updates_manager_;
ActorOwn<UpdatesManager> updates_manager_actor_; ActorOwn<UpdatesManager> updates_manager_actor_;
unique_ptr<MemoryManager> memory_manager_; unique_ptr<MemoryManager> memory_manager_;
@ -206,7 +208,6 @@ class Td final : public Actor {
ActorOwn<SecretChatsManager> secret_chats_manager_; ActorOwn<SecretChatsManager> secret_chats_manager_;
ActorOwn<StateManager> state_manager_; ActorOwn<StateManager> state_manager_;
ActorOwn<StorageManager> storage_manager_; ActorOwn<StorageManager> storage_manager_;
ActorOwn<TopDialogManager> top_dialog_manager_;
ActorOwn<PhoneNumberManager> verify_phone_number_manager_; ActorOwn<PhoneNumberManager> verify_phone_number_manager_;
class ResultHandler : public std::enable_shared_from_this<ResultHandler> { class ResultHandler : public std::enable_shared_from_this<ResultHandler> {
@ -564,7 +565,7 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::setAutoDownloadSettings &request); void on_request(uint64 id, const td_api::setAutoDownloadSettings &request);
void on_request(uint64 id, td_api::getTopChats &request); void on_request(uint64 id, const td_api::getTopChats &request);
void on_request(uint64 id, const td_api::removeTopChat &request); void on_request(uint64 id, const td_api::removeTopChat &request);

View File

@ -352,6 +352,9 @@ Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters &parameters, DbK
} }
if (user_version == 0) { if (user_version == 0) {
binlog_pmc.erase("next_contacts_sync_date"); binlog_pmc.erase("next_contacts_sync_date");
binlog_pmc.erase("saved_contact_count");
binlog_pmc.erase("old_featured_sticker_set_count");
binlog_pmc.erase("invalidate_old_featured_sticker_sets");
} }
binlog_pmc.force_sync({}); binlog_pmc.force_sync({});

View File

@ -0,0 +1,108 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/TopDialogCategory.h"
namespace td {
CSlice get_top_dialog_category_name(TopDialogCategory category) {
switch (category) {
case TopDialogCategory::Correspondent:
return CSlice("correspondent");
case TopDialogCategory::BotPM:
return CSlice("bot_pm");
case TopDialogCategory::BotInline:
return CSlice("bot_inline");
case TopDialogCategory::Group:
return CSlice("group");
case TopDialogCategory::Channel:
return CSlice("channel");
case TopDialogCategory::Call:
return CSlice("call");
case TopDialogCategory::ForwardUsers:
return CSlice("forward_users");
case TopDialogCategory::ForwardChats:
return CSlice("forward_chats");
default:
UNREACHABLE();
return CSlice();
}
}
TopDialogCategory get_top_dialog_category(const td_api::object_ptr<td_api::TopChatCategory> &category) {
if (category == nullptr) {
return TopDialogCategory::Size;
}
switch (category->get_id()) {
case td_api::topChatCategoryUsers::ID:
return TopDialogCategory::Correspondent;
case td_api::topChatCategoryBots::ID:
return TopDialogCategory::BotPM;
case td_api::topChatCategoryInlineBots::ID:
return TopDialogCategory::BotInline;
case td_api::topChatCategoryGroups::ID:
return TopDialogCategory::Group;
case td_api::topChatCategoryChannels::ID:
return TopDialogCategory::Channel;
case td_api::topChatCategoryCalls::ID:
return TopDialogCategory::Call;
case td_api::topChatCategoryForwardChats::ID:
return TopDialogCategory::ForwardUsers;
default:
UNREACHABLE();
return TopDialogCategory::Size;
}
}
TopDialogCategory get_top_dialog_category(const telegram_api::object_ptr<telegram_api::TopPeerCategory> &category) {
CHECK(category != nullptr);
switch (category->get_id()) {
case telegram_api::topPeerCategoryCorrespondents::ID:
return TopDialogCategory::Correspondent;
case telegram_api::topPeerCategoryBotsPM::ID:
return TopDialogCategory::BotPM;
case telegram_api::topPeerCategoryBotsInline::ID:
return TopDialogCategory::BotInline;
case telegram_api::topPeerCategoryGroups::ID:
return TopDialogCategory::Group;
case telegram_api::topPeerCategoryChannels::ID:
return TopDialogCategory::Channel;
case telegram_api::topPeerCategoryPhoneCalls::ID:
return TopDialogCategory::Call;
case telegram_api::topPeerCategoryForwardUsers::ID:
return TopDialogCategory::ForwardUsers;
case telegram_api::topPeerCategoryForwardChats::ID:
return TopDialogCategory::ForwardChats;
default:
UNREACHABLE();
return TopDialogCategory::Size;
}
}
telegram_api::object_ptr<telegram_api::TopPeerCategory> get_input_top_peer_category(TopDialogCategory category) {
switch (category) {
case TopDialogCategory::Correspondent:
return make_tl_object<telegram_api::topPeerCategoryCorrespondents>();
case TopDialogCategory::BotPM:
return make_tl_object<telegram_api::topPeerCategoryBotsPM>();
case TopDialogCategory::BotInline:
return make_tl_object<telegram_api::topPeerCategoryBotsInline>();
case TopDialogCategory::Group:
return make_tl_object<telegram_api::topPeerCategoryGroups>();
case TopDialogCategory::Channel:
return make_tl_object<telegram_api::topPeerCategoryChannels>();
case TopDialogCategory::Call:
return make_tl_object<telegram_api::topPeerCategoryPhoneCalls>();
case TopDialogCategory::ForwardUsers:
return make_tl_object<telegram_api::topPeerCategoryForwardUsers>();
case TopDialogCategory::ForwardChats:
return make_tl_object<telegram_api::topPeerCategoryForwardChats>();
default:
UNREACHABLE();
return nullptr;
}
}
} // namespace td

View File

@ -7,8 +7,10 @@
#pragma once #pragma once
#include "td/telegram/td_api.h" #include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h" #include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td { namespace td {
@ -24,25 +26,12 @@ enum class TopDialogCategory : int32 {
Size Size
}; };
inline TopDialogCategory get_top_dialog_category(const td_api::TopChatCategory &category) { CSlice get_top_dialog_category_name(TopDialogCategory category);
switch (category.get_id()) {
case td_api::topChatCategoryUsers::ID: TopDialogCategory get_top_dialog_category(const td_api::object_ptr<td_api::TopChatCategory> &category);
return TopDialogCategory::Correspondent;
case td_api::topChatCategoryBots::ID: TopDialogCategory get_top_dialog_category(const telegram_api::object_ptr<telegram_api::TopPeerCategory> &category);
return TopDialogCategory::BotPM;
case td_api::topChatCategoryInlineBots::ID: telegram_api::object_ptr<telegram_api::TopPeerCategory> get_input_top_peer_category(TopDialogCategory category);
return TopDialogCategory::BotInline;
case td_api::topChatCategoryGroups::ID:
return TopDialogCategory::Group;
case td_api::topChatCategoryChannels::ID:
return TopDialogCategory::Channel;
case td_api::topChatCategoryCalls::ID:
return TopDialogCategory::Call;
case td_api::topChatCategoryForwardChats::ID:
return TopDialogCategory::ForwardUsers;
default:
UNREACHABLE();
}
}
} // namespace td } // namespace td

View File

@ -6,6 +6,7 @@
// //
#include "td/telegram/TopDialogManager.h" #include "td/telegram/TopDialogManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h" #include "td/telegram/AuthManager.h"
#include "td/telegram/ConfigShared.h" #include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h" #include "td/telegram/ContactsManager.h"
@ -23,6 +24,7 @@
#include "td/telegram/telegram_api.h" #include "td/telegram/telegram_api.h"
#include "td/utils/algorithm.h" #include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h" #include "td/utils/logging.h"
#include "td/utils/misc.h" #include "td/utils/misc.h"
#include "td/utils/port/Clocks.h" #include "td/utils/port/Clocks.h"
@ -38,78 +40,97 @@
namespace td { namespace td {
static CSlice top_dialog_category_name(TopDialogCategory category) { class GetTopPeersQuery final : public Td::ResultHandler {
switch (category) { Promise<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> promise_;
case TopDialogCategory::Correspondent:
return CSlice("correspondent");
case TopDialogCategory::BotPM:
return CSlice("bot_pm");
case TopDialogCategory::BotInline:
return CSlice("bot_inline");
case TopDialogCategory::Group:
return CSlice("group");
case TopDialogCategory::Channel:
return CSlice("channel");
case TopDialogCategory::Call:
return CSlice("call");
case TopDialogCategory::ForwardUsers:
return CSlice("forward_users");
case TopDialogCategory::ForwardChats:
return CSlice("forward_chats");
default:
UNREACHABLE();
}
}
static TopDialogCategory get_top_dialog_category(const telegram_api::TopPeerCategory &category) { public:
switch (category.get_id()) { explicit GetTopPeersQuery(Promise<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> &&promise)
case telegram_api::topPeerCategoryCorrespondents::ID: : promise_(std::move(promise)) {
return TopDialogCategory::Correspondent;
case telegram_api::topPeerCategoryBotsPM::ID:
return TopDialogCategory::BotPM;
case telegram_api::topPeerCategoryBotsInline::ID:
return TopDialogCategory::BotInline;
case telegram_api::topPeerCategoryGroups::ID:
return TopDialogCategory::Group;
case telegram_api::topPeerCategoryChannels::ID:
return TopDialogCategory::Channel;
case telegram_api::topPeerCategoryPhoneCalls::ID:
return TopDialogCategory::Call;
case telegram_api::topPeerCategoryForwardUsers::ID:
return TopDialogCategory::ForwardUsers;
case telegram_api::topPeerCategoryForwardChats::ID:
return TopDialogCategory::ForwardChats;
default:
UNREACHABLE();
} }
}
static tl_object_ptr<telegram_api::TopPeerCategory> get_top_peer_category(TopDialogCategory category) { void send(int64 hash) {
switch (category) { int32 flags =
case TopDialogCategory::Correspondent: telegram_api::contacts_getTopPeers::CORRESPONDENTS_MASK | telegram_api::contacts_getTopPeers::BOTS_PM_MASK |
return make_tl_object<telegram_api::topPeerCategoryCorrespondents>(); telegram_api::contacts_getTopPeers::BOTS_INLINE_MASK | telegram_api::contacts_getTopPeers::GROUPS_MASK |
case TopDialogCategory::BotPM: telegram_api::contacts_getTopPeers::CHANNELS_MASK | telegram_api::contacts_getTopPeers::PHONE_CALLS_MASK |
return make_tl_object<telegram_api::topPeerCategoryBotsPM>(); telegram_api::contacts_getTopPeers::FORWARD_USERS_MASK | telegram_api::contacts_getTopPeers::FORWARD_CHATS_MASK;
case TopDialogCategory::BotInline: send_query(G()->net_query_creator().create(telegram_api::contacts_getTopPeers(
return make_tl_object<telegram_api::topPeerCategoryBotsInline>(); flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
case TopDialogCategory::Group: false /*ignored*/, false /*ignored*/, false /*ignored*/, 0 /*offset*/, 100 /*limit*/, hash)));
return make_tl_object<telegram_api::topPeerCategoryGroups>();
case TopDialogCategory::Channel:
return make_tl_object<telegram_api::topPeerCategoryChannels>();
case TopDialogCategory::Call:
return make_tl_object<telegram_api::topPeerCategoryPhoneCalls>();
case TopDialogCategory::ForwardUsers:
return make_tl_object<telegram_api::topPeerCategoryForwardUsers>();
case TopDialogCategory::ForwardChats:
return make_tl_object<telegram_api::topPeerCategoryForwardChats>();
default:
UNREACHABLE();
} }
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::contacts_getTopPeers>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(uint64 id, Status status) final {
promise_.set_error(std::move(status));
}
};
class ToggleTopPeersQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ToggleTopPeersQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(bool is_enabled) {
send_query(G()->net_query_creator().create(telegram_api::contacts_toggleTopPeers(is_enabled)));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::contacts_toggleTopPeers>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetTopPeerRatingQuery final : public Td::ResultHandler {
DialogId dialog_id_;
public:
void send(TopDialogCategory category, DialogId dialog_id) {
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
return;
}
dialog_id_ = dialog_id;
send_query(G()->net_query_creator().create(
telegram_api::contacts_resetTopPeerRating(get_input_top_peer_category(category), std::move(input_peer))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::contacts_resetTopPeerRating>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
// ignore the result
}
void on_error(uint64 id, Status status) final {
if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ResetTopPeerRatingQuery")) {
LOG(INFO) << "Receive error for resetTopPeerRating: " << status;
}
}
};
void TopDialogManager::update_is_enabled(bool is_enabled) { void TopDialogManager::update_is_enabled(bool is_enabled) {
auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get(); if (td_->auth_manager_ == nullptr || !td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) {
if (auth_manager == nullptr || !auth_manager->is_authorized() || auth_manager->is_bot()) {
return; return;
} }
@ -128,11 +149,15 @@ bool TopDialogManager::set_is_enabled(bool is_enabled) {
LOG(DEBUG) << "Change top chats is_enabled to " << is_enabled; LOG(DEBUG) << "Change top chats is_enabled to " << is_enabled;
is_enabled_ = is_enabled; is_enabled_ = is_enabled;
init(); try_start();
return true; return true;
} }
void TopDialogManager::send_toggle_top_peers(bool is_enabled) { void TopDialogManager::send_toggle_top_peers(bool is_enabled) {
if (G()->close_flag()) {
return;
}
if (have_toggle_top_peers_query_) { if (have_toggle_top_peers_query_) {
have_pending_toggle_top_peers_query_ = true; have_pending_toggle_top_peers_query_ = true;
pending_toggle_top_peers_query_ = is_enabled; pending_toggle_top_peers_query_ = is_enabled;
@ -141,9 +166,32 @@ void TopDialogManager::send_toggle_top_peers(bool is_enabled) {
LOG(DEBUG) << "Send toggle top peers query to " << is_enabled; LOG(DEBUG) << "Send toggle top peers query to " << is_enabled;
have_toggle_top_peers_query_ = true; have_toggle_top_peers_query_ = true;
toggle_top_peers_query_is_enabled_ = is_enabled;
auto net_query = G()->net_query_creator().create(telegram_api::contacts_toggleTopPeers(is_enabled)); auto promise = PromiseCreator::lambda([actor_id = actor_id(this), is_enabled](Result<Unit> result) {
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 2)); send_closure(actor_id, &TopDialogManager::on_toggle_top_peers, is_enabled, std::move(result));
});
td_->create_handler<ToggleTopPeersQuery>(std::move(promise))->send(is_enabled);
}
void TopDialogManager::on_toggle_top_peers(bool is_enabled, Result<Unit> &&result) {
CHECK(have_toggle_top_peers_query_);
have_toggle_top_peers_query_ = false;
if (have_pending_toggle_top_peers_query_) {
have_pending_toggle_top_peers_query_ = false;
if (pending_toggle_top_peers_query_ != is_enabled) {
send_toggle_top_peers(pending_toggle_top_peers_query_);
return;
}
}
if (result.is_ok()) {
// everything is synchronized
G()->td_db()->get_binlog_pmc()->erase("top_peers_enabled");
} else {
// let's resend the query forever
send_toggle_top_peers(is_enabled);
}
} }
void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date) { void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date) {
@ -175,7 +223,7 @@ void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialo
it = next; it = next;
} }
LOG(INFO) << "Update " << top_dialog_category_name(category) << " rating of " << dialog_id << " by " << delta; LOG(INFO) << "Update " << get_top_dialog_category_name(category) << " rating of " << dialog_id << " by " << delta;
if (!first_unsync_change_) { if (!first_unsync_change_) {
first_unsync_change_ = Timestamp::now_cached(); first_unsync_change_ = Timestamp::now_cached();
@ -183,12 +231,16 @@ void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialo
loop(); loop();
} }
void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog_id, void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog_id, Promise<Unit> &&promise) {
tl_object_ptr<telegram_api::InputPeer> input_peer) { if (category == TopDialogCategory::Size) {
if (!is_active_ || !is_enabled_) { return promise.set_error(Status::Error(400, "Top chat category must be non-empty"));
return; }
if (!td_->messages_manager_->have_dialog_force(dialog_id, "remove_dialog")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (!is_active_ || !is_enabled_) {
return promise.set_value(Unit());
} }
CHECK(dialog_id.is_valid());
if (category == TopDialogCategory::ForwardUsers && dialog_id.get_type() != DialogType::User) { if (category == TopDialogCategory::ForwardUsers && dialog_id.get_type() != DialogType::User) {
category = TopDialogCategory::ForwardChats; category = TopDialogCategory::ForwardChats;
@ -198,18 +250,12 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog
CHECK(pos < by_category_.size()); CHECK(pos < by_category_.size());
auto &top_dialogs = by_category_[pos]; auto &top_dialogs = by_category_[pos];
LOG(INFO) << "Remove " << top_dialog_category_name(category) << " rating of " << dialog_id; td_->create_handler<ResetTopPeerRatingQuery>()->send(category, dialog_id);
if (input_peer != nullptr) {
auto query = telegram_api::contacts_resetTopPeerRating(get_top_peer_category(category), std::move(input_peer));
auto net_query = G()->net_query_creator().create(query);
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 1));
}
auto it = std::find_if(top_dialogs.dialogs.begin(), top_dialogs.dialogs.end(), auto it = std::find_if(top_dialogs.dialogs.begin(), top_dialogs.dialogs.end(),
[&](auto &top_dialog) { return top_dialog.dialog_id == dialog_id; }); [&](auto &top_dialog) { return top_dialog.dialog_id == dialog_id; });
if (it == top_dialogs.dialogs.end()) { if (it == top_dialogs.dialogs.end()) {
return; return promise.set_value(Unit());
} }
top_dialogs.is_dirty = true; top_dialogs.is_dirty = true;
@ -218,21 +264,26 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog
first_unsync_change_ = Timestamp::now_cached(); first_unsync_change_ = Timestamp::now_cached();
} }
loop(); loop();
promise.set_value(Unit());
} }
void TopDialogManager::get_top_dialogs(TopDialogCategory category, size_t limit, Promise<vector<DialogId>> promise) { void TopDialogManager::get_top_dialogs(TopDialogCategory category, int32 limit, Promise<vector<DialogId>> promise) {
if (category == TopDialogCategory::Size) {
return promise.set_error(Status::Error(400, "Top chat category must be non-empty"));
}
if (limit <= 0) {
return promise.set_error(Status::Error(400, "Limit must be positive"));
}
if (!is_active_) { if (!is_active_) {
promise.set_error(Status::Error(400, "Not supported without chat info database")); return promise.set_error(Status::Error(400, "Not supported without chat info database"));
return;
} }
if (!is_enabled_) { if (!is_enabled_) {
promise.set_error(Status::Error(400, "Top chats computation is disabled")); return promise.set_error(Status::Error(400, "Top chats computation is disabled"));
return;
} }
GetTopDialogsQuery query; GetTopDialogsQuery query;
query.category = category; query.category = category;
query.limit = limit; query.limit = static_cast<size_t>(limit);
query.promise = std::move(promise); query.promise = std::move(promise);
pending_get_top_dialogs_.push_back(std::move(query)); pending_get_top_dialogs_.push_back(std::move(query));
loop(); loop();
@ -315,163 +366,112 @@ void TopDialogManager::do_get_top_dialogs(GetTopDialogsQuery &&query) {
} }
} }
auto promise = PromiseCreator::lambda(
[actor_id = actor_id(this), query = std::move(query)](Result<vector<DialogId>> r_dialog_ids) mutable {
if (r_dialog_ids.is_error()) {
return query.promise.set_error(r_dialog_ids.move_as_error());
}
send_closure(actor_id, &TopDialogManager::on_load_dialogs, std::move(query), r_dialog_ids.move_as_ok());
});
td_->messages_manager_->load_dialogs(std::move(dialog_ids), std::move(promise));
}
void TopDialogManager::on_load_dialogs(GetTopDialogsQuery &&query, vector<DialogId> &&dialog_ids) {
auto limit = std::min({query.limit, MAX_TOP_DIALOGS_LIMIT, dialog_ids.size()}); auto limit = std::min({query.limit, MAX_TOP_DIALOGS_LIMIT, dialog_ids.size()});
vector<DialogId> result;
auto promise = PromiseCreator::lambda([query = std::move(query), dialog_ids, limit](Result<Unit>) mutable { result.reserve(limit);
vector<DialogId> result; for (auto dialog_id : dialog_ids) {
result.reserve(limit); if (dialog_id.get_type() == DialogType::User) {
for (auto dialog_id : dialog_ids) { auto user_id = dialog_id.get_user_id();
if (dialog_id.get_type() == DialogType::User) { if (td_->contacts_manager_->is_user_deleted(user_id)) {
auto user_id = dialog_id.get_user_id(); LOG(INFO) << "Skip deleted " << user_id;
if (G()->td().get_actor_unsafe()->contacts_manager_->is_user_deleted(user_id)) { continue;
LOG(INFO) << "Skip deleted " << user_id;
continue;
}
if (G()->td().get_actor_unsafe()->contacts_manager_->get_my_id() == user_id) {
LOG(INFO) << "Skip self " << user_id;
continue;
}
if (query.category == TopDialogCategory::BotInline || query.category == TopDialogCategory::BotPM) {
auto r_bot_info = G()->td().get_actor_unsafe()->contacts_manager_->get_bot_data(user_id);
if (r_bot_info.is_error()) {
LOG(INFO) << "Skip not a bot " << user_id;
continue;
}
if (query.category == TopDialogCategory::BotInline &&
(r_bot_info.ok().username.empty() || !r_bot_info.ok().is_inline)) {
LOG(INFO) << "Skip not inline bot " << user_id;
continue;
}
}
} }
if (td_->contacts_manager_->get_my_id() == user_id) {
result.push_back(dialog_id); LOG(INFO) << "Skip self " << user_id;
if (result.size() == limit) { continue;
break; }
if (query.category == TopDialogCategory::BotInline || query.category == TopDialogCategory::BotPM) {
auto r_bot_info = td_->contacts_manager_->get_bot_data(user_id);
if (r_bot_info.is_error()) {
LOG(INFO) << "Skip not a bot " << user_id;
continue;
}
if (query.category == TopDialogCategory::BotInline &&
(r_bot_info.ok().username.empty() || !r_bot_info.ok().is_inline)) {
LOG(INFO) << "Skip not inline bot " << user_id;
continue;
}
} }
} }
query.promise.set_value(std::move(result)); result.push_back(dialog_id);
}); if (result.size() == limit) {
send_closure(G()->messages_manager(), &MessagesManager::load_dialogs, std::move(dialog_ids), std::move(promise)); break;
}
}
query.promise.set_value(std::move(result));
} }
void TopDialogManager::do_get_top_peers() { void TopDialogManager::do_get_top_peers() {
LOG(INFO) << "Send get top peers request";
using telegram_api::contacts_getTopPeers;
std::vector<uint64> ids; std::vector<uint64> ids;
for (auto &category : by_category_) { for (auto &category : by_category_) {
for (auto &top_dialog : category.dialogs) { for (auto &top_dialog : category.dialogs) {
auto dialog_id = top_dialog.dialog_id; auto dialog_id = top_dialog.dialog_id;
switch (dialog_id.get_type()) { switch (dialog_id.get_type()) {
case DialogType::Channel:
ids.push_back(dialog_id.get_channel_id().get());
break;
case DialogType::User: case DialogType::User:
ids.push_back(dialog_id.get_user_id().get()); ids.push_back(dialog_id.get_user_id().get());
break; break;
case DialogType::Chat: case DialogType::Chat:
ids.push_back(dialog_id.get_chat_id().get()); ids.push_back(dialog_id.get_chat_id().get());
break; break;
case DialogType::Channel:
ids.push_back(dialog_id.get_channel_id().get());
break;
default: default:
break; break;
} }
} }
} }
auto promise = PromiseCreator::lambda(
int64 hash = get_vector_hash(ids); [actor_id = actor_id(this)](Result<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> result) {
send_closure(actor_id, &TopDialogManager::on_get_top_peers, std::move(result));
int32 flags = contacts_getTopPeers::CORRESPONDENTS_MASK | contacts_getTopPeers::BOTS_PM_MASK | });
contacts_getTopPeers::BOTS_INLINE_MASK | contacts_getTopPeers::GROUPS_MASK | td_->create_handler<GetTopPeersQuery>(std::move(promise))->send(get_vector_hash(ids));
contacts_getTopPeers::CHANNELS_MASK | contacts_getTopPeers::PHONE_CALLS_MASK |
contacts_getTopPeers::FORWARD_USERS_MASK | contacts_getTopPeers::FORWARD_CHATS_MASK;
contacts_getTopPeers query{flags,
true /*correspondents*/,
true /*bot_pm*/,
true /*bot_inline */,
true /*phone_calls*/,
true /*groups*/,
true /*channels*/,
true /*forward_users*/,
true /*forward_chats*/,
0 /*offset*/,
100 /*limit*/,
hash};
auto net_query = G()->net_query_creator().create(query);
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this));
} }
void TopDialogManager::on_result(NetQueryPtr net_query) { void TopDialogManager::on_get_top_peers(Result<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> result) {
auto query_type = get_link_token();
if (query_type == 2) { // toggleTopPeers
CHECK(have_toggle_top_peers_query_);
have_toggle_top_peers_query_ = false;
if (have_pending_toggle_top_peers_query_) {
have_pending_toggle_top_peers_query_ = false;
if (pending_toggle_top_peers_query_ != toggle_top_peers_query_is_enabled_) {
send_toggle_top_peers(pending_toggle_top_peers_query_);
return;
}
}
auto r_result = fetch_result<telegram_api::contacts_toggleTopPeers>(std::move(net_query));
if (r_result.is_ok()) {
// everything is synchronized
G()->td_db()->get_binlog_pmc()->erase("top_peers_enabled");
} else {
// let's resend the query forever
if (!G()->close_flag()) {
send_toggle_top_peers(toggle_top_peers_query_is_enabled_);
}
}
return;
}
if (query_type == 1) { // resetTopPeerRating
// ignore result
return;
}
SCOPE_EXIT {
loop();
};
normalize_rating(); // once a day too normalize_rating(); // once a day too
auto r_top_peers = fetch_result<telegram_api::contacts_getTopPeers>(std::move(net_query)); if (result.is_error()) {
if (r_top_peers.is_error()) {
last_server_sync_ = Timestamp::in(SERVER_SYNC_RESEND_DELAY - SERVER_SYNC_DELAY); last_server_sync_ = Timestamp::in(SERVER_SYNC_RESEND_DELAY - SERVER_SYNC_DELAY);
loop();
return; return;
} }
last_server_sync_ = Timestamp::now(); last_server_sync_ = Timestamp::now();
server_sync_state_ = SyncState::Ok; server_sync_state_ = SyncState::Ok;
SCOPE_EXIT {
G()->td_db()->get_binlog_pmc()->set("top_dialogs_ts", to_string(static_cast<uint32>(Clocks::system())));
};
auto top_peers_parent = r_top_peers.move_as_ok(); auto top_peers_parent = result.move_as_ok();
LOG(DEBUG) << "Receive contacts_getTopPeers result: " << to_string(top_peers_parent); LOG(DEBUG) << "Receive contacts_getTopPeers result: " << to_string(top_peers_parent);
switch (top_peers_parent->get_id()) { switch (top_peers_parent->get_id()) {
case telegram_api::contacts_topPeersNotModified::ID: case telegram_api::contacts_topPeersNotModified::ID:
// nothing to do // nothing to do
return; break;
case telegram_api::contacts_topPeersDisabled::ID: case telegram_api::contacts_topPeersDisabled::ID:
G()->shared_config().set_option_boolean("disable_top_chats", true); G()->shared_config().set_option_boolean("disable_top_chats", true);
set_is_enabled(false); // apply immediately set_is_enabled(false); // apply immediately
return; break;
case telegram_api::contacts_topPeers::ID: { case telegram_api::contacts_topPeers::ID: {
G()->shared_config().set_option_empty("disable_top_chats"); G()->shared_config().set_option_empty("disable_top_chats");
set_is_enabled(true); // apply immediately set_is_enabled(true); // apply immediately
auto top_peers = move_tl_object_as<telegram_api::contacts_topPeers>(std::move(top_peers_parent)); auto top_peers = move_tl_object_as<telegram_api::contacts_topPeers>(std::move(top_peers_parent));
send_closure(G()->contacts_manager(), &ContactsManager::on_get_users, std::move(top_peers->users_), td_->contacts_manager_->on_get_users(std::move(top_peers->users_), "on get top chats");
"on get top chats"); td_->contacts_manager_->on_get_chats(std::move(top_peers->chats_), "on get top chats");
send_closure(G()->contacts_manager(), &ContactsManager::on_get_chats, std::move(top_peers->chats_),
"on get top chats");
for (auto &category : top_peers->categories_) { for (auto &category : top_peers->categories_) {
auto dialog_category = get_top_dialog_category(*category->category_); auto dialog_category = get_top_dialog_category(category->category_);
auto pos = static_cast<size_t>(dialog_category); auto pos = static_cast<size_t>(dialog_category);
CHECK(pos < by_category_.size()); CHECK(pos < by_category_.size());
auto &top_dialogs = by_category_[pos]; auto &top_dialogs = by_category_[pos];
@ -491,13 +491,16 @@ void TopDialogManager::on_result(NetQueryPtr net_query) {
default: default:
UNREACHABLE(); UNREACHABLE();
} }
G()->td_db()->get_binlog_pmc()->set("top_dialogs_ts", to_string(static_cast<uint32>(Clocks::system())));
loop();
} }
void TopDialogManager::do_save_top_dialogs() { void TopDialogManager::do_save_top_dialogs() {
LOG(INFO) << "Save top chats"; LOG(INFO) << "Save top chats";
for (size_t top_dialog_category_i = 0; top_dialog_category_i < by_category_.size(); top_dialog_category_i++) { for (size_t top_dialog_category_i = 0; top_dialog_category_i < by_category_.size(); top_dialog_category_i++) {
auto top_dialog_category = TopDialogCategory(top_dialog_category_i); auto top_dialog_category = TopDialogCategory(top_dialog_category_i);
auto key = PSTRING() << "top_dialogs#" << top_dialog_category_name(top_dialog_category); auto key = PSTRING() << "top_dialogs#" << get_top_dialog_category_name(top_dialog_category);
auto &top_dialogs = by_category_[top_dialog_category_i]; auto &top_dialogs = by_category_[top_dialog_category_i];
if (!top_dialogs.is_dirty) { if (!top_dialogs.is_dirty) {
@ -512,16 +515,19 @@ void TopDialogManager::do_save_top_dialogs() {
} }
void TopDialogManager::start_up() { void TopDialogManager::start_up() {
do_start_up(); init();
} }
void TopDialogManager::do_start_up() { void TopDialogManager::tear_down() {
auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get(); parent_.reset();
if (auth_manager == nullptr || !auth_manager->is_authorized()) { }
void TopDialogManager::init() {
if (td_->auth_manager_ == nullptr || !td_->auth_manager_->is_authorized()) {
return; return;
} }
is_active_ = G()->parameters().use_chat_info_db && !auth_manager->is_bot(); is_active_ = G()->parameters().use_chat_info_db && !td_->auth_manager_->is_bot();
is_enabled_ = !G()->shared_config().get_option_boolean("disable_top_chats"); is_enabled_ = !G()->shared_config().get_option_boolean("disable_top_chats");
update_rating_e_decay(); update_rating_e_decay();
@ -530,11 +536,11 @@ void TopDialogManager::do_start_up() {
send_toggle_top_peers(need_update_top_peers[0] == '1'); send_toggle_top_peers(need_update_top_peers[0] == '1');
} }
init(); try_start();
loop(); loop();
} }
void TopDialogManager::init() { void TopDialogManager::try_start() {
was_first_sync_ = false; was_first_sync_ = false;
first_unsync_change_ = Timestamp(); first_unsync_change_ = Timestamp();
server_sync_state_ = SyncState::None; server_sync_state_ = SyncState::None;
@ -558,7 +564,7 @@ void TopDialogManager::init() {
if (is_enabled_) { if (is_enabled_) {
for (size_t top_dialog_category_i = 0; top_dialog_category_i < by_category_.size(); top_dialog_category_i++) { for (size_t top_dialog_category_i = 0; top_dialog_category_i < by_category_.size(); top_dialog_category_i++) {
auto top_dialog_category = TopDialogCategory(top_dialog_category_i); auto top_dialog_category = TopDialogCategory(top_dialog_category_i);
auto key = PSTRING() << "top_dialogs#" << top_dialog_category_name(top_dialog_category); auto key = PSTRING() << "top_dialogs#" << get_top_dialog_category_name(top_dialog_category);
auto value = G()->td_db()->get_binlog_pmc()->get(key); auto value = G()->td_db()->get_binlog_pmc()->get(key);
auto &top_dialogs = by_category_[top_dialog_category_i]; auto &top_dialogs = by_category_[top_dialog_category_i];
@ -585,9 +591,9 @@ void TopDialogManager::init() {
void TopDialogManager::on_first_sync() { void TopDialogManager::on_first_sync() {
was_first_sync_ = true; was_first_sync_ = true;
if (!G()->close_flag() && G()->td().get_actor_unsafe()->auth_manager_->is_bot()) { if (!G()->close_flag() && td_->auth_manager_->is_bot()) {
is_active_ = false; is_active_ = false;
init(); try_start();
} }
loop(); loop();
} }

View File

@ -7,8 +7,6 @@
#pragma once #pragma once
#include "td/telegram/DialogId.h" #include "td/telegram/DialogId.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/TopDialogCategory.h" #include "td/telegram/TopDialogCategory.h"
#include "td/actor/actor.h" #include "td/actor/actor.h"
@ -22,18 +20,20 @@
namespace td { namespace td {
class TopDialogManager final : public NetQueryCallback { class Td;
class TopDialogManager final : public Actor {
public: public:
explicit TopDialogManager(ActorShared<> parent) : parent_(std::move(parent)) { TopDialogManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
} }
void do_start_up(); void init();
void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date); void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date);
void remove_dialog(TopDialogCategory category, DialogId dialog_id, tl_object_ptr<telegram_api::InputPeer> input_peer); void remove_dialog(TopDialogCategory category, DialogId dialog_id, Promise<Unit> &&promise);
void get_top_dialogs(TopDialogCategory category, size_t limit, Promise<vector<DialogId>> promise); void get_top_dialogs(TopDialogCategory category, int32 limit, Promise<vector<DialogId>> promise);
void update_rating_e_decay(); void update_rating_e_decay();
@ -44,14 +44,15 @@ class TopDialogManager final : public NetQueryCallback {
static constexpr int32 SERVER_SYNC_DELAY = 86400; // seconds static constexpr int32 SERVER_SYNC_DELAY = 86400; // seconds
static constexpr int32 SERVER_SYNC_RESEND_DELAY = 60; // seconds static constexpr int32 SERVER_SYNC_RESEND_DELAY = 60; // seconds
static constexpr int32 DB_SYNC_DELAY = 5; // seconds static constexpr int32 DB_SYNC_DELAY = 5; // seconds
Td *td_;
ActorShared<> parent_; ActorShared<> parent_;
bool is_active_{false}; bool is_active_ = false;
bool is_enabled_{true}; bool is_enabled_ = true;
int32 rating_e_decay_ = 241920; int32 rating_e_decay_ = 241920;
bool have_toggle_top_peers_query_ = false; bool have_toggle_top_peers_query_ = false;
bool toggle_top_peers_query_is_enabled_ = false;
bool have_pending_toggle_top_peers_query_ = false; bool have_pending_toggle_top_peers_query_ = false;
bool pending_toggle_top_peers_query_ = false; bool pending_toggle_top_peers_query_ = false;
bool was_first_sync_{false}; bool was_first_sync_{false};
@ -97,21 +98,30 @@ class TopDialogManager final : public NetQueryCallback {
void normalize_rating(); void normalize_rating();
bool set_is_enabled(bool is_enabled); bool set_is_enabled(bool is_enabled);
void send_toggle_top_peers(bool is_enabled); void send_toggle_top_peers(bool is_enabled);
void on_toggle_top_peers(bool is_enabled, Result<Unit> &&result);
void do_get_top_dialogs(GetTopDialogsQuery &&query); void do_get_top_dialogs(GetTopDialogsQuery &&query);
void on_load_dialogs(GetTopDialogsQuery &&query, vector<DialogId> &&dialog_ids);
void do_get_top_peers(); void do_get_top_peers();
void do_save_top_dialogs(); void do_save_top_dialogs();
void on_first_sync(); void on_first_sync();
void on_result(NetQueryPtr net_query) final; void on_get_top_peers(Result<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> result);
void init(); void try_start();
void start_up() final; void start_up() final;
void loop() final; void loop() final;
void tear_down() final;
}; };
} // namespace td } // namespace td

View File

@ -2425,7 +2425,7 @@ class CliClient final : public Actor {
td_api::make_object<td_api::addNetworkStatistics>(td_api::make_object<td_api::networkStatisticsEntryFile>( td_api::make_object<td_api::addNetworkStatistics>(td_api::make_object<td_api::networkStatisticsEntryFile>(
td_api::make_object<td_api::fileTypeDocument>(), get_network_type(network_type), sent_bytes, td_api::make_object<td_api::fileTypeDocument>(), get_network_type(network_type), sent_bytes,
received_bytes))); received_bytes)));
} else if (op == "top_chats") { } else if (op == "gtc") {
send_request(td_api::make_object<td_api::getTopChats>(get_top_chat_category(args), 50)); send_request(td_api::make_object<td_api::getTopChats>(get_top_chat_category(args), 50));
} else if (op == "rtc") { } else if (op == "rtc") {
string chat_id; string chat_id;

View File

@ -82,9 +82,8 @@ void FileGcWorker::run_gc(const FileGcParameters &parameters, std::vector<FullFi
total_size += info.size; total_size += info.size;
} }
FileStats new_stats; FileStats new_stats(false, parameters.dialog_limit != 0);
FileStats removed_stats; FileStats removed_stats(false, parameters.dialog_limit != 0);
removed_stats.split_by_owner_dialog_id = new_stats.split_by_owner_dialog_id = parameters.dialog_limit != 0;
auto do_remove_file = [&removed_stats](const FullFileInfo &info) { auto do_remove_file = [&removed_stats](const FullFileInfo &info) {
removed_stats.add_copy(info); removed_stats.add_copy(info);

View File

@ -17,7 +17,6 @@
#include "td/telegram/files/FileLoadManager.h" #include "td/telegram/files/FileLoadManager.h"
#include "td/telegram/files/FileLocation.h" #include "td/telegram/files/FileLocation.h"
#include "td/telegram/files/FileSourceId.h" #include "td/telegram/files/FileSourceId.h"
#include "td/telegram/files/FileStats.h"
#include "td/telegram/files/FileType.h" #include "td/telegram/files/FileType.h"
#include "td/telegram/Location.h" #include "td/telegram/Location.h"
#include "td/telegram/PhotoSizeSource.h" #include "td/telegram/PhotoSizeSource.h"

View File

@ -10,6 +10,7 @@
#include "td/telegram/files/FileLoaderUtils.h" #include "td/telegram/files/FileLoaderUtils.h"
#include "td/utils/algorithm.h"
#include "td/utils/common.h" #include "td/utils/common.h"
#include "td/utils/format.h" #include "td/utils/format.h"
#include "td/utils/misc.h" #include "td/utils/misc.h"
@ -27,34 +28,34 @@ tl_object_ptr<td_api::storageStatisticsFast> FileStatsFast::get_storage_statisti
void FileStats::add(StatByType &by_type, FileType file_type, int64 size) { void FileStats::add(StatByType &by_type, FileType file_type, int64 size) {
auto pos = static_cast<size_t>(file_type); auto pos = static_cast<size_t>(file_type);
CHECK(pos < stat_by_type.size()); CHECK(pos < stat_by_type_.size());
by_type[pos].size += size; by_type[pos].size += size;
by_type[pos].cnt++; by_type[pos].cnt++;
} }
void FileStats::add_impl(const FullFileInfo &info) { void FileStats::add_impl(const FullFileInfo &info) {
if (split_by_owner_dialog_id) { if (split_by_owner_dialog_id_) {
add(stat_by_owner_dialog_id[info.owner_dialog_id], info.file_type, info.size); add(stat_by_owner_dialog_id_[info.owner_dialog_id], info.file_type, info.size);
} else { } else {
add(stat_by_type, info.file_type, info.size); add(stat_by_type_, info.file_type, info.size);
} }
} }
void FileStats::add_copy(const FullFileInfo &info) { void FileStats::add_copy(const FullFileInfo &info) {
add_impl(info); add_impl(info);
if (need_all_files) { if (need_all_files_) {
all_files.push_back(info); all_files_.push_back(info);
} }
} }
void FileStats::add(FullFileInfo &&info) { void FileStats::add(FullFileInfo &&info) {
add_impl(info); add_impl(info);
if (need_all_files) { if (need_all_files_) {
all_files.push_back(std::move(info)); all_files_.push_back(std::move(info));
} }
} }
FileTypeStat get_nontemp_stat(const FileStats::StatByType &by_type) { FileTypeStat FileStats::get_nontemp_stat(const FileStats::StatByType &by_type) {
FileTypeStat stat; FileTypeStat stat;
for (int32 i = 0; i < MAX_FILE_TYPE; i++) { for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
if (FileType(i) != FileType::Temp) { if (FileType(i) != FileType::Temp) {
@ -66,11 +67,11 @@ FileTypeStat get_nontemp_stat(const FileStats::StatByType &by_type) {
} }
FileTypeStat FileStats::get_total_nontemp_stat() const { FileTypeStat FileStats::get_total_nontemp_stat() const {
if (!split_by_owner_dialog_id) { if (!split_by_owner_dialog_id_) {
return get_nontemp_stat(stat_by_type); return get_nontemp_stat(stat_by_type_);
} }
FileTypeStat stat; FileTypeStat stat;
for (auto &dialog : stat_by_owner_dialog_id) { for (auto &dialog : stat_by_owner_dialog_id_) {
auto tmp = get_nontemp_stat(dialog.second); auto tmp = get_nontemp_stat(dialog.second);
stat.size += tmp.size; stat.size += tmp.size;
stat.cnt += tmp.cnt; stat.cnt += tmp.cnt;
@ -82,12 +83,12 @@ void FileStats::apply_dialog_limit(int32 limit) {
if (limit == -1) { if (limit == -1) {
return; return;
} }
if (!split_by_owner_dialog_id) { if (!split_by_owner_dialog_id_) {
return; return;
} }
std::vector<std::pair<int64, DialogId>> dialogs; std::vector<std::pair<int64, DialogId>> dialogs;
for (auto &dialog : stat_by_owner_dialog_id) { for (auto &dialog : stat_by_owner_dialog_id_) {
if (!dialog.first.is_valid()) { if (!dialog.first.is_valid()) {
continue; continue;
} }
@ -105,15 +106,14 @@ void FileStats::apply_dialog_limit(int32 limit) {
[](const auto &x, const auto &y) { return x.first > y.first; }); [](const auto &x, const auto &y) { return x.first > y.first; });
dialogs.resize(prefix); dialogs.resize(prefix);
std::unordered_set<DialogId, DialogIdHash> all_dialogs; apply_dialog_ids(transform(dialogs, [](const auto &dialog) { return dialog.second; }));
}
for (auto &dialog : dialogs) {
all_dialogs.insert(dialog.second);
}
void FileStats::apply_dialog_ids(const vector<DialogId> &dialog_ids) {
std::unordered_set<DialogId, DialogIdHash> all_dialogs(dialog_ids.begin(), dialog_ids.end());
StatByType other_stats; StatByType other_stats;
bool other_flag = false; bool other_flag = false;
for (auto it = stat_by_owner_dialog_id.begin(); it != stat_by_owner_dialog_id.end();) { for (auto it = stat_by_owner_dialog_id_.begin(); it != stat_by_owner_dialog_id_.end();) {
if (all_dialogs.count(it->first)) { if (all_dialogs.count(it->first)) {
++it; ++it;
} else { } else {
@ -122,24 +122,24 @@ void FileStats::apply_dialog_limit(int32 limit) {
other_stats[i].cnt += it->second[i].cnt; other_stats[i].cnt += it->second[i].cnt;
} }
other_flag = true; other_flag = true;
it = stat_by_owner_dialog_id.erase(it); it = stat_by_owner_dialog_id_.erase(it);
} }
} }
if (other_flag) { if (other_flag) {
DialogId other_dialog_id; DialogId other_dialog_id; // prevents MSVC warning C4709: comma operator within array index expression
stat_by_owner_dialog_id[other_dialog_id] = other_stats; stat_by_owner_dialog_id_[other_dialog_id] = other_stats;
} }
} }
static tl_object_ptr<td_api::storageStatisticsByChat> get_storage_statistics_by_chat_object( td_api::object_ptr<td_api::storageStatisticsByChat> FileStats::get_storage_statistics_by_chat_object(
DialogId dialog_id, const FileStats::StatByType &stat_by_type) { DialogId dialog_id, const FileStats::StatByType &stat_by_type_) {
auto stats = make_tl_object<td_api::storageStatisticsByChat>(dialog_id.get(), 0, 0, Auto()); auto stats = make_tl_object<td_api::storageStatisticsByChat>(dialog_id.get(), 0, 0, Auto());
FileStats::StatByType aggregated_stats; FileStats::StatByType aggregated_stats;
for (int32 i = 0; i < MAX_FILE_TYPE; i++) { for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
size_t file_type = narrow_cast<size_t>(get_main_file_type(static_cast<FileType>(i))); size_t file_type = narrow_cast<size_t>(get_main_file_type(static_cast<FileType>(i)));
aggregated_stats[file_type].size += stat_by_type[i].size; aggregated_stats[file_type].size += stat_by_type_[i].size;
aggregated_stats[file_type].cnt += stat_by_type[i].cnt; aggregated_stats[file_type].cnt += stat_by_type_[i].cnt;
} }
for (int32 i = 0; i < MAX_FILE_TYPE; i++) { for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
@ -161,12 +161,12 @@ static tl_object_ptr<td_api::storageStatisticsByChat> get_storage_statistics_by_
tl_object_ptr<td_api::storageStatistics> FileStats::get_storage_statistics_object() const { tl_object_ptr<td_api::storageStatistics> FileStats::get_storage_statistics_object() const {
auto stats = make_tl_object<td_api::storageStatistics>(0, 0, Auto()); auto stats = make_tl_object<td_api::storageStatistics>(0, 0, Auto());
if (!split_by_owner_dialog_id) { if (!split_by_owner_dialog_id_) {
stats->by_chat_.reserve(1); stats->by_chat_.reserve(1);
stats->by_chat_.push_back(get_storage_statistics_by_chat_object(DialogId(), stat_by_type)); stats->by_chat_.push_back(get_storage_statistics_by_chat_object(DialogId(), stat_by_type_));
} else { } else {
stats->by_chat_.reserve(stat_by_owner_dialog_id.size()); stats->by_chat_.reserve(stat_by_owner_dialog_id_.size());
for (auto &by_dialog : stat_by_owner_dialog_id) { for (auto &by_dialog : stat_by_owner_dialog_id_) {
stats->by_chat_.push_back(get_storage_statistics_by_chat_object(by_dialog.first, by_dialog.second)); stats->by_chat_.push_back(get_storage_statistics_by_chat_object(by_dialog.first, by_dialog.second));
} }
std::sort(stats->by_chat_.begin(), stats->by_chat_.end(), [](const auto &x, const auto &y) { std::sort(stats->by_chat_.begin(), stats->by_chat_.end(), [](const auto &x, const auto &y) {
@ -183,13 +183,13 @@ tl_object_ptr<td_api::storageStatistics> FileStats::get_storage_statistics_objec
return stats; return stats;
} }
std::vector<DialogId> FileStats::get_dialog_ids() const { vector<DialogId> FileStats::get_dialog_ids() const {
std::vector<DialogId> res; vector<DialogId> res;
if (!split_by_owner_dialog_id) { if (!split_by_owner_dialog_id_) {
return res; return res;
} }
res.reserve(stat_by_owner_dialog_id.size()); res.reserve(stat_by_owner_dialog_id_.size());
for (auto &by_dialog : stat_by_owner_dialog_id) { for (auto &by_dialog : stat_by_owner_dialog_id_) {
if (by_dialog.first.is_valid()) { if (by_dialog.first.is_valid()) {
res.push_back(by_dialog.first); res.push_back(by_dialog.first);
} }
@ -197,27 +197,31 @@ std::vector<DialogId> FileStats::get_dialog_ids() const {
return res; return res;
} }
StringBuilder &operator<<(StringBuilder &sb, const FileTypeStat &stat) { vector<FullFileInfo> FileStats::get_all_files() {
return std::move(all_files_);
}
static StringBuilder &operator<<(StringBuilder &sb, const FileTypeStat &stat) {
return sb << tag("size", format::as_size(stat.size)) << tag("count", stat.cnt); return sb << tag("size", format::as_size(stat.size)) << tag("count", stat.cnt);
} }
StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats) { StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats) {
if (!file_stats.split_by_owner_dialog_id) { if (!file_stats.split_by_owner_dialog_id_) {
FileTypeStat total_stat; FileTypeStat total_stat;
for (auto &type_stat : file_stats.stat_by_type) { for (auto &type_stat : file_stats.stat_by_type_) {
total_stat.size += type_stat.size; total_stat.size += type_stat.size;
total_stat.cnt += type_stat.cnt; total_stat.cnt += type_stat.cnt;
} }
sb << "[FileStat " << tag("total", total_stat); sb << "[FileStat " << tag("total", total_stat);
for (int32 i = 0; i < MAX_FILE_TYPE; i++) { for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
sb << tag(get_file_type_name(FileType(i)), file_stats.stat_by_type[i]); sb << tag(get_file_type_name(FileType(i)), file_stats.stat_by_type_[i]);
} }
sb << "]"; sb << "]";
} else { } else {
{ {
FileTypeStat total_stat; FileTypeStat total_stat;
for (auto &by_type : file_stats.stat_by_owner_dialog_id) { for (auto &by_type : file_stats.stat_by_owner_dialog_id_) {
for (auto &type_stat : by_type.second) { for (auto &type_stat : by_type.second) {
total_stat.size += type_stat.size; total_stat.size += type_stat.size;
total_stat.cnt += type_stat.cnt; total_stat.cnt += type_stat.cnt;
@ -225,7 +229,7 @@ StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats) {
} }
sb << "[FileStat " << tag("total", total_stat); sb << "[FileStat " << tag("total", total_stat);
} }
for (auto &by_type : file_stats.stat_by_owner_dialog_id) { for (auto &by_type : file_stats.stat_by_owner_dialog_id_) {
FileTypeStat dialog_stat; FileTypeStat dialog_stat;
for (auto &type_stat : by_type.second) { for (auto &type_stat : by_type.second) {
dialog_stat.size += type_stat.size; dialog_stat.size += type_stat.size;

View File

@ -66,28 +66,47 @@ struct FileStatsFast {
tl_object_ptr<td_api::storageStatisticsFast> get_storage_statistics_fast_object() const; tl_object_ptr<td_api::storageStatisticsFast> get_storage_statistics_fast_object() const;
}; };
struct FileStats { class FileStats {
bool need_all_files{false}; bool need_all_files_{false};
bool split_by_owner_dialog_id{false}; bool split_by_owner_dialog_id_{false};
using StatByType = std::array<FileTypeStat, MAX_FILE_TYPE>; using StatByType = std::array<FileTypeStat, MAX_FILE_TYPE>;
StatByType stat_by_type; StatByType stat_by_type_;
std::unordered_map<DialogId, StatByType, DialogIdHash> stat_by_owner_dialog_id; std::unordered_map<DialogId, StatByType, DialogIdHash> stat_by_owner_dialog_id_;
vector<FullFileInfo> all_files_;
std::vector<FullFileInfo> all_files; void add_impl(const FullFileInfo &info);
void add(StatByType &by_type, FileType file_type, int64 size);
static FileTypeStat get_nontemp_stat(const StatByType &by_type);
static td_api::object_ptr<td_api::storageStatisticsByChat> get_storage_statistics_by_chat_object(
DialogId dialog_id, const StatByType &stat_by_type_);
friend StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats);
public:
FileStats(bool need_all_files, bool split_by_owner_dialog_id)
: need_all_files_(need_all_files), split_by_owner_dialog_id_(split_by_owner_dialog_id) {
}
void add_copy(const FullFileInfo &info); void add_copy(const FullFileInfo &info);
void add(FullFileInfo &&info); void add(FullFileInfo &&info);
void apply_dialog_limit(int32 limit); void apply_dialog_limit(int32 limit);
void apply_dialog_ids(const vector<DialogId> &dialog_ids);
tl_object_ptr<td_api::storageStatistics> get_storage_statistics_object() const; tl_object_ptr<td_api::storageStatistics> get_storage_statistics_object() const;
std::vector<DialogId> get_dialog_ids() const;
vector<DialogId> get_dialog_ids() const;
FileTypeStat get_total_nontemp_stat() const; FileTypeStat get_total_nontemp_stat() const;
private: vector<FullFileInfo> get_all_files();
void add_impl(const FullFileInfo &info);
void add(StatByType &by_type, FileType file_type, int64 size);
}; };
StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats); StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats);

View File

@ -147,8 +147,7 @@ void FileStatsWorker::get_stats(bool need_all_files, bool split_by_owner_dialog_
split_by_owner_dialog_id = false; split_by_owner_dialog_id = false;
} }
if (!split_by_owner_dialog_id) { if (!split_by_owner_dialog_id) {
FileStats file_stats; FileStats file_stats(need_all_files, false);
file_stats.need_all_files = need_all_files;
auto start = Time::now(); auto start = Time::now();
scan_fs(token_, [&](FsFileInfo &fs_info) { scan_fs(token_, [&](FsFileInfo &fs_info) {
FullFileInfo info; FullFileInfo info;
@ -207,9 +206,7 @@ void FileStatsWorker::get_stats(bool need_all_files, bool split_by_owner_dialog_
return promise.set_error(Status::Error(500, "Request aborted")); return promise.set_error(Status::Error(500, "Request aborted"));
} }
FileStats file_stats; FileStats file_stats(need_all_files, split_by_owner_dialog_id);
file_stats.need_all_files = need_all_files;
file_stats.split_by_owner_dialog_id = split_by_owner_dialog_id;
for (auto &full_info : full_infos) { for (auto &full_info : full_infos) {
file_stats.add(std::move(full_info)); file_stats.add(std::move(full_info));
if (token_) { if (token_) {

View File

@ -13,35 +13,32 @@
#include <cstring> #include <cstring>
void *TdCClientCreate() { static td::ClientManager *GetClientManager() {
return new td::Client(); return td::ClientManager::get_manager_singleton();
} }
void TdCClientSend(void *instance, struct TdRequest request) { int TdCClientCreateId() {
auto client = static_cast<td::Client *>(instance); return GetClientManager()->create_client_id();
td::Client::Request client_request; }
client_request.id = request.id;
client_request.function = TdConvertToInternal(request.function); void TdCClientSend(int client_id, struct TdRequest request) {
GetClientManager()->send(client_id, request.request_id, TdConvertToInternal(request.function));
TdDestroyObjectFunction(request.function); TdDestroyObjectFunction(request.function);
client->send(std::move(client_request));
} }
TdResponse TdCClientReceive(void *instance, double timeout) { TdResponse TdCClientReceive(double timeout) {
auto client = static_cast<td::Client *>(instance); auto response = GetClientManager()->receive(timeout);
auto response = client->receive(timeout);
TdResponse c_response; TdResponse c_response;
c_response.id = response.id; c_response.client_id = response.client_id;
c_response.request_id = response.request_id;
c_response.object = response.object == nullptr ? nullptr : TdConvertFromInternal(*response.object); c_response.object = response.object == nullptr ? nullptr : TdConvertFromInternal(*response.object);
return c_response; return c_response;
} }
void TdCClientDestroy(void *instance) { TdObject *TdCClientExecute(TdFunction *function) {
auto client = static_cast<td::Client *>(instance); auto result = td::ClientManager::execute(TdConvertToInternal(function));
delete client; TdDestroyObjectFunction(function);
} return TdConvertFromInternal(*result);
void TdCClientSetVerbosity(int new_verbosity_level) {
td::Log::set_verbosity_level(new_verbosity_level);
} }
TdVectorInt *TdCreateObjectVectorInt(int size, int *data) { TdVectorInt *TdCreateObjectVectorInt(int size, int *data) {

View File

@ -18,21 +18,23 @@ struct TdVectorObject *TdCreateObjectVectorObject(int size, struct TdObject **da
struct TdBytes TdCreateObjectBytes(unsigned char *data, int len); struct TdBytes TdCreateObjectBytes(unsigned char *data, int len);
struct TdRequest { struct TdRequest {
long long id; long long request_id;
TdFunction *function; TdFunction *function;
}; };
struct TdResponse { struct TdResponse {
long long id; long long request_id;
int client_id;
TdObject *object; TdObject *object;
}; };
void *TdCClientCreate(); int TdCClientCreateId();
void TdCClientSend(void *instance, struct TdRequest cmd);
struct TdResponse TdCClientSendCommandSync(void *instance, double timeout);
void TdCClientDestroy(void *instance);
void TdCClientSetVerbosity(int new_verbosity_level); void TdCClientSend(int client_id, struct TdRequest request);
TdResponse TdCClientReceive(double timeout);
TdObject *TdCClientExecute(TdFunction *function);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -6,6 +6,7 @@
// //
#include "td/db/SqliteKeyValue.h" #include "td/db/SqliteKeyValue.h"
#include "td/utils/base64.h"
#include "td/utils/logging.h" #include "td/utils/logging.h"
#include "td/utils/ScopeGuard.h" #include "td/utils/ScopeGuard.h"
@ -54,7 +55,7 @@ SqliteKeyValue::SeqNo SqliteKeyValue::set(Slice key, Slice value) {
set_stmt_.bind_blob(2, value).ensure(); set_stmt_.bind_blob(2, value).ensure();
auto status = set_stmt_.step(); auto status = set_stmt_.step();
if (status.is_error()) { if (status.is_error()) {
LOG(FATAL) << "Failed to set \"" << key << "\": " << status.error(); LOG(FATAL) << "Failed to set \"" << base64_encode(key) << "\": " << status.error();
} }
// set_stmt_.step().ensure(); // set_stmt_.step().ensure();
set_stmt_.reset(); set_stmt_.reset();