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/TermsOfService.cpp
td/telegram/ThemeManager.cpp
td/telegram/TopDialogCategory.cpp
td/telegram/TopDialogManager.cpp
td/telegram/UpdatesManager.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.
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),
[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;
//@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;
//@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->stickers_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->on_online_updated(false, true);
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
* 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 {
public:

View File

@ -18,7 +18,7 @@
namespace td {
// TODO can be made private in TDLib 2.0
// TODO can be removed in TDLib 2.0
class ClientJson final {
public:
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, ',');
string saved_bots = G()->td_db()->get_binlog_pmc()->get("recently_used_inline_bot_usernames");
auto bot_usernames = full_split(saved_bots, ',');
if (bot_ids.empty() && bot_usernames.empty()) {
if (bot_ids.empty()) {
recently_used_bots_loaded_ = 2;
if (!recently_used_bot_user_ids_.empty()) {
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_);
recently_used_bot_user_ids_.clear();
if (bot_ids.empty()) {
// legacy, can be removed in the future
for (auto it = bot_usernames.rbegin(); it != bot_usernames.rend(); ++it) {
auto dialog_id = td_->messages_manager_->resolve_dialog_username(*it);
if (dialog_id.get_type() == DialogType::User) {
update_bot_usage(dialog_id.get_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 = 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) {
update_bot_usage(*it);
}
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();
}
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));
if (recently_used_bots_loaded_ == 0) {
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) {
td_->messages_manager_->search_public_dialog(bot_username, false,
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;
}
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);
Dependencies dependencies;
@ -15445,13 +15445,14 @@ void MessagesManager::load_dialogs(vector<DialogId> dialog_ids, Promise<Unit> &&
}
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) {
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) {

View File

@ -516,7 +516,7 @@ class MessagesManager final : public Actor {
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);

View File

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

View File

@ -40,6 +40,7 @@
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/files/FileStats.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/FolderId.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") {
send_closure(storage_manager_, &StorageManager::update_use_storage_optimizer);
} 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") {
send_closure(notification_manager_actor_,
&NotificationManager::on_disable_contact_registered_notifications_changed);
} 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));
} else if (name == "connection_parameters") {
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;
theme_manager_.reset();
LOG(DEBUG) << "ThemeManager was cleared" << timer;
top_dialog_manager_.reset();
LOG(DEBUG) << "TopDialogManager was cleared" << timer;
updates_manager_.reset();
LOG(DEBUG) << "UpdatesManager was cleared" << timer;
video_notes_manager_.reset();
@ -3776,8 +3779,6 @@ void Td::clear() {
LOG(DEBUG) << "SecretChatsManager was cleared" << timer;
storage_manager_.reset();
LOG(DEBUG) << "StorageManager was cleared" << timer;
top_dialog_manager_.reset();
LOG(DEBUG) << "TopDialogManager was cleared" << timer;
verify_phone_number_manager_.reset();
LOG(DEBUG) << "VerifyPhoneNumberManager was cleared" << timer;
@ -3823,6 +3824,8 @@ void Td::clear() {
LOG(DEBUG) << "MemoryManager actor was cleared" << timer;
theme_manager_actor_.reset();
LOG(DEBUG) << "ThemeManager actor was cleared" << timer;
top_dialog_manager_actor_.reset();
LOG(DEBUG) << "TopDialogManager actor was cleared" << timer;
updates_manager_actor_.reset();
LOG(DEBUG) << "UpdatesManager actor was cleared" << timer;
web_pages_manager_actor_.reset();
@ -4293,6 +4296,9 @@ void Td::init_managers() {
theme_manager_ = make_unique<ThemeManager>(this, create_reference());
theme_manager_actor_ = register_actor("ThemeManager", theme_manager_.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_actor_ = register_actor("UpdatesManager", updates_manager_.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());
G()->set_secret_chats_manager(secret_chats_manager_.get());
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>(
"VerifyPhoneNumberManager", PhoneNumberManager::Type::VerifyPhone, create_reference());
}
@ -5239,14 +5243,8 @@ void Td::on_request(uint64 id, const td_api::setAutoDownloadSettings &request) {
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();
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();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<vector<DialogId>> result) mutable {
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()));
}
});
send_closure(top_dialog_manager_, &TopDialogManager::get_top_dialogs, get_top_dialog_category(*request.category_),
narrow_cast<size_t>(request.limit_), std::move(query_promise));
top_dialog_manager_->get_top_dialogs(get_top_dialog_category(request.category_), request.limit_,
std::move(query_promise));
}
void Td::on_request(uint64 id, const td_api::removeTopChat &request) {
CHECK_IS_USER();
if (request.category_ == nullptr) {
return send_error_raw(id, 400, "Top chat category must be non-empty");
}
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>());
CREATE_OK_REQUEST_PROMISE();
top_dialog_manager_->remove_dialog(get_top_dialog_category(request.category_), DialogId(request.chat_id_),
std::move(promise));
}
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_;
unique_ptr<ThemeManager> theme_manager_;
ActorOwn<ThemeManager> theme_manager_actor_;
unique_ptr<TopDialogManager> top_dialog_manager_;
ActorOwn<TopDialogManager> top_dialog_manager_actor_;
unique_ptr<UpdatesManager> updates_manager_;
ActorOwn<UpdatesManager> updates_manager_actor_;
unique_ptr<MemoryManager> memory_manager_;
@ -206,7 +208,6 @@ class Td final : public Actor {
ActorOwn<SecretChatsManager> secret_chats_manager_;
ActorOwn<StateManager> state_manager_;
ActorOwn<StorageManager> storage_manager_;
ActorOwn<TopDialogManager> top_dialog_manager_;
ActorOwn<PhoneNumberManager> verify_phone_number_manager_;
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, td_api::getTopChats &request);
void on_request(uint64 id, const td_api::getTopChats &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) {
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({});

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
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
@ -24,25 +26,12 @@ enum class TopDialogCategory : int32 {
Size
};
inline TopDialogCategory get_top_dialog_category(const td_api::TopChatCategory &category) {
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();
}
}
CSlice get_top_dialog_category_name(TopDialogCategory category);
TopDialogCategory get_top_dialog_category(const td_api::object_ptr<td_api::TopChatCategory> &category);
TopDialogCategory get_top_dialog_category(const telegram_api::object_ptr<telegram_api::TopPeerCategory> &category);
telegram_api::object_ptr<telegram_api::TopPeerCategory> get_input_top_peer_category(TopDialogCategory category);
} // namespace td

View File

@ -6,6 +6,7 @@
//
#include "td/telegram/TopDialogManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
@ -23,6 +24,7 @@
#include "td/telegram/telegram_api.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Clocks.h"
@ -38,78 +40,97 @@
namespace td {
static CSlice 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();
}
}
class GetTopPeersQuery final : public Td::ResultHandler {
Promise<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> promise_;
static TopDialogCategory get_top_dialog_category(const telegram_api::TopPeerCategory &category) {
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();
public:
explicit GetTopPeersQuery(Promise<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> &&promise)
: promise_(std::move(promise)) {
}
}
static tl_object_ptr<telegram_api::TopPeerCategory> get_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();
void send(int64 hash) {
int32 flags =
telegram_api::contacts_getTopPeers::CORRESPONDENTS_MASK | telegram_api::contacts_getTopPeers::BOTS_PM_MASK |
telegram_api::contacts_getTopPeers::BOTS_INLINE_MASK | telegram_api::contacts_getTopPeers::GROUPS_MASK |
telegram_api::contacts_getTopPeers::CHANNELS_MASK | telegram_api::contacts_getTopPeers::PHONE_CALLS_MASK |
telegram_api::contacts_getTopPeers::FORWARD_USERS_MASK | telegram_api::contacts_getTopPeers::FORWARD_CHATS_MASK;
send_query(G()->net_query_creator().create(telegram_api::contacts_getTopPeers(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, 0 /*offset*/, 100 /*limit*/, hash)));
}
}
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) {
auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get();
if (auth_manager == nullptr || !auth_manager->is_authorized() || auth_manager->is_bot()) {
if (td_->auth_manager_ == nullptr || !td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) {
return;
}
@ -128,11 +149,15 @@ bool TopDialogManager::set_is_enabled(bool is_enabled) {
LOG(DEBUG) << "Change top chats is_enabled to " << is_enabled;
is_enabled_ = is_enabled;
init();
try_start();
return true;
}
void TopDialogManager::send_toggle_top_peers(bool is_enabled) {
if (G()->close_flag()) {
return;
}
if (have_toggle_top_peers_query_) {
have_pending_toggle_top_peers_query_ = true;
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;
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));
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 2));
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), is_enabled](Result<Unit> result) {
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) {
@ -175,7 +223,7 @@ void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialo
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_) {
first_unsync_change_ = Timestamp::now_cached();
@ -183,12 +231,16 @@ void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialo
loop();
}
void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog_id,
tl_object_ptr<telegram_api::InputPeer> input_peer) {
if (!is_active_ || !is_enabled_) {
return;
void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog_id, Promise<Unit> &&promise) {
if (category == TopDialogCategory::Size) {
return promise.set_error(Status::Error(400, "Top chat category must be non-empty"));
}
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) {
category = TopDialogCategory::ForwardChats;
@ -198,18 +250,12 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog
CHECK(pos < by_category_.size());
auto &top_dialogs = by_category_[pos];
LOG(INFO) << "Remove " << top_dialog_category_name(category) << " rating of " << 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));
}
td_->create_handler<ResetTopPeerRatingQuery>()->send(category, dialog_id);
auto it = std::find_if(top_dialogs.dialogs.begin(), top_dialogs.dialogs.end(),
[&](auto &top_dialog) { return top_dialog.dialog_id == dialog_id; });
if (it == top_dialogs.dialogs.end()) {
return;
return promise.set_value(Unit());
}
top_dialogs.is_dirty = true;
@ -218,21 +264,26 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog
first_unsync_change_ = Timestamp::now_cached();
}
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_) {
promise.set_error(Status::Error(400, "Not supported without chat info database"));
return;
return promise.set_error(Status::Error(400, "Not supported without chat info database"));
}
if (!is_enabled_) {
promise.set_error(Status::Error(400, "Top chats computation is disabled"));
return;
return promise.set_error(Status::Error(400, "Top chats computation is disabled"));
}
GetTopDialogsQuery query;
query.category = category;
query.limit = limit;
query.limit = static_cast<size_t>(limit);
query.promise = std::move(promise);
pending_get_top_dialogs_.push_back(std::move(query));
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 promise = PromiseCreator::lambda([query = std::move(query), dialog_ids, limit](Result<Unit>) mutable {
vector<DialogId> result;
result.reserve(limit);
for (auto dialog_id : dialog_ids) {
if (dialog_id.get_type() == DialogType::User) {
auto user_id = dialog_id.get_user_id();
if (G()->td().get_actor_unsafe()->contacts_manager_->is_user_deleted(user_id)) {
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;
}
}
vector<DialogId> result;
result.reserve(limit);
for (auto dialog_id : dialog_ids) {
if (dialog_id.get_type() == DialogType::User) {
auto user_id = dialog_id.get_user_id();
if (td_->contacts_manager_->is_user_deleted(user_id)) {
LOG(INFO) << "Skip deleted " << user_id;
continue;
}
result.push_back(dialog_id);
if (result.size() == limit) {
break;
if (td_->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 = 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));
});
send_closure(G()->messages_manager(), &MessagesManager::load_dialogs, std::move(dialog_ids), std::move(promise));
result.push_back(dialog_id);
if (result.size() == limit) {
break;
}
}
query.promise.set_value(std::move(result));
}
void TopDialogManager::do_get_top_peers() {
LOG(INFO) << "Send get top peers request";
using telegram_api::contacts_getTopPeers;
std::vector<uint64> ids;
for (auto &category : by_category_) {
for (auto &top_dialog : category.dialogs) {
auto dialog_id = top_dialog.dialog_id;
switch (dialog_id.get_type()) {
case DialogType::Channel:
ids.push_back(dialog_id.get_channel_id().get());
break;
case DialogType::User:
ids.push_back(dialog_id.get_user_id().get());
break;
case DialogType::Chat:
ids.push_back(dialog_id.get_chat_id().get());
break;
case DialogType::Channel:
ids.push_back(dialog_id.get_channel_id().get());
break;
default:
break;
}
}
}
int64 hash = get_vector_hash(ids);
int32 flags = contacts_getTopPeers::CORRESPONDENTS_MASK | contacts_getTopPeers::BOTS_PM_MASK |
contacts_getTopPeers::BOTS_INLINE_MASK | contacts_getTopPeers::GROUPS_MASK |
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));
auto promise = PromiseCreator::lambda(
[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));
});
td_->create_handler<GetTopPeersQuery>(std::move(promise))->send(get_vector_hash(ids));
}
void TopDialogManager::on_result(NetQueryPtr net_query) {
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();
};
void TopDialogManager::on_get_top_peers(Result<telegram_api::object_ptr<telegram_api::contacts_TopPeers>> result) {
normalize_rating(); // once a day too
auto r_top_peers = fetch_result<telegram_api::contacts_getTopPeers>(std::move(net_query));
if (r_top_peers.is_error()) {
if (result.is_error()) {
last_server_sync_ = Timestamp::in(SERVER_SYNC_RESEND_DELAY - SERVER_SYNC_DELAY);
loop();
return;
}
last_server_sync_ = Timestamp::now();
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);
switch (top_peers_parent->get_id()) {
case telegram_api::contacts_topPeersNotModified::ID:
// nothing to do
return;
break;
case telegram_api::contacts_topPeersDisabled::ID:
G()->shared_config().set_option_boolean("disable_top_chats", true);
set_is_enabled(false); // apply immediately
return;
break;
case telegram_api::contacts_topPeers::ID: {
G()->shared_config().set_option_empty("disable_top_chats");
set_is_enabled(true); // apply immediately
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_),
"on get top chats");
send_closure(G()->contacts_manager(), &ContactsManager::on_get_chats, std::move(top_peers->chats_),
"on get top chats");
td_->contacts_manager_->on_get_users(std::move(top_peers->users_), "on get top chats");
td_->contacts_manager_->on_get_chats(std::move(top_peers->chats_), "on get top chats");
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);
CHECK(pos < by_category_.size());
auto &top_dialogs = by_category_[pos];
@ -491,13 +491,16 @@ void TopDialogManager::on_result(NetQueryPtr net_query) {
default:
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() {
LOG(INFO) << "Save top chats";
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 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];
if (!top_dialogs.is_dirty) {
@ -512,16 +515,19 @@ void TopDialogManager::do_save_top_dialogs() {
}
void TopDialogManager::start_up() {
do_start_up();
init();
}
void TopDialogManager::do_start_up() {
auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get();
if (auth_manager == nullptr || !auth_manager->is_authorized()) {
void TopDialogManager::tear_down() {
parent_.reset();
}
void TopDialogManager::init() {
if (td_->auth_manager_ == nullptr || !td_->auth_manager_->is_authorized()) {
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");
update_rating_e_decay();
@ -530,11 +536,11 @@ void TopDialogManager::do_start_up() {
send_toggle_top_peers(need_update_top_peers[0] == '1');
}
init();
try_start();
loop();
}
void TopDialogManager::init() {
void TopDialogManager::try_start() {
was_first_sync_ = false;
first_unsync_change_ = Timestamp();
server_sync_state_ = SyncState::None;
@ -558,7 +564,7 @@ void TopDialogManager::init() {
if (is_enabled_) {
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 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 &top_dialogs = by_category_[top_dialog_category_i];
@ -585,9 +591,9 @@ void TopDialogManager::init() {
void TopDialogManager::on_first_sync() {
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;
init();
try_start();
}
loop();
}

View File

@ -7,8 +7,6 @@
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/TopDialogCategory.h"
#include "td/actor/actor.h"
@ -22,18 +20,20 @@
namespace td {
class TopDialogManager final : public NetQueryCallback {
class Td;
class TopDialogManager final : public Actor {
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 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();
@ -44,14 +44,15 @@ class TopDialogManager final : public NetQueryCallback {
static constexpr int32 SERVER_SYNC_DELAY = 86400; // seconds
static constexpr int32 SERVER_SYNC_RESEND_DELAY = 60; // seconds
static constexpr int32 DB_SYNC_DELAY = 5; // seconds
Td *td_;
ActorShared<> parent_;
bool is_active_{false};
bool is_enabled_{true};
bool is_active_ = false;
bool is_enabled_ = true;
int32 rating_e_decay_ = 241920;
bool have_toggle_top_peers_query_ = false;
bool toggle_top_peers_query_is_enabled_ = false;
bool have_pending_toggle_top_peers_query_ = false;
bool pending_toggle_top_peers_query_ = false;
bool was_first_sync_{false};
@ -97,21 +98,30 @@ class TopDialogManager final : public NetQueryCallback {
void normalize_rating();
bool set_is_enabled(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 on_load_dialogs(GetTopDialogsQuery &&query, vector<DialogId> &&dialog_ids);
void do_get_top_peers();
void do_save_top_dialogs();
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 loop() final;
void tear_down() final;
};
} // 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::fileTypeDocument>(), get_network_type(network_type), sent_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));
} else if (op == "rtc") {
string chat_id;

View File

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

View File

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

View File

@ -10,6 +10,7 @@
#include "td/telegram/files/FileLoaderUtils.h"
#include "td/utils/algorithm.h"
#include "td/utils/common.h"
#include "td/utils/format.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) {
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].cnt++;
}
void FileStats::add_impl(const FullFileInfo &info) {
if (split_by_owner_dialog_id) {
add(stat_by_owner_dialog_id[info.owner_dialog_id], info.file_type, info.size);
if (split_by_owner_dialog_id_) {
add(stat_by_owner_dialog_id_[info.owner_dialog_id], info.file_type, info.size);
} 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) {
add_impl(info);
if (need_all_files) {
all_files.push_back(info);
if (need_all_files_) {
all_files_.push_back(info);
}
}
void FileStats::add(FullFileInfo &&info) {
add_impl(info);
if (need_all_files) {
all_files.push_back(std::move(info));
if (need_all_files_) {
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;
for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
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 {
if (!split_by_owner_dialog_id) {
return get_nontemp_stat(stat_by_type);
if (!split_by_owner_dialog_id_) {
return get_nontemp_stat(stat_by_type_);
}
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);
stat.size += tmp.size;
stat.cnt += tmp.cnt;
@ -82,12 +83,12 @@ void FileStats::apply_dialog_limit(int32 limit) {
if (limit == -1) {
return;
}
if (!split_by_owner_dialog_id) {
if (!split_by_owner_dialog_id_) {
return;
}
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()) {
continue;
}
@ -105,15 +106,14 @@ void FileStats::apply_dialog_limit(int32 limit) {
[](const auto &x, const auto &y) { return x.first > y.first; });
dialogs.resize(prefix);
std::unordered_set<DialogId, DialogIdHash> all_dialogs;
for (auto &dialog : dialogs) {
all_dialogs.insert(dialog.second);
}
apply_dialog_ids(transform(dialogs, [](const auto &dialog) { return 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;
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)) {
++it;
} else {
@ -122,24 +122,24 @@ void FileStats::apply_dialog_limit(int32 limit) {
other_stats[i].cnt += it->second[i].cnt;
}
other_flag = true;
it = stat_by_owner_dialog_id.erase(it);
it = stat_by_owner_dialog_id_.erase(it);
}
}
if (other_flag) {
DialogId other_dialog_id;
stat_by_owner_dialog_id[other_dialog_id] = other_stats;
DialogId other_dialog_id; // prevents MSVC warning C4709: comma operator within array index expression
stat_by_owner_dialog_id_[other_dialog_id] = other_stats;
}
}
static tl_object_ptr<td_api::storageStatisticsByChat> get_storage_statistics_by_chat_object(
DialogId dialog_id, const FileStats::StatByType &stat_by_type) {
td_api::object_ptr<td_api::storageStatisticsByChat> FileStats::get_storage_statistics_by_chat_object(
DialogId dialog_id, const FileStats::StatByType &stat_by_type_) {
auto stats = make_tl_object<td_api::storageStatisticsByChat>(dialog_id.get(), 0, 0, Auto());
FileStats::StatByType aggregated_stats;
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)));
aggregated_stats[file_type].size += stat_by_type[i].size;
aggregated_stats[file_type].cnt += stat_by_type[i].cnt;
aggregated_stats[file_type].size += stat_by_type_[i].size;
aggregated_stats[file_type].cnt += stat_by_type_[i].cnt;
}
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 {
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_.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 {
stats->by_chat_.reserve(stat_by_owner_dialog_id.size());
for (auto &by_dialog : stat_by_owner_dialog_id) {
stats->by_chat_.reserve(stat_by_owner_dialog_id_.size());
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));
}
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;
}
std::vector<DialogId> FileStats::get_dialog_ids() const {
std::vector<DialogId> res;
if (!split_by_owner_dialog_id) {
vector<DialogId> FileStats::get_dialog_ids() const {
vector<DialogId> res;
if (!split_by_owner_dialog_id_) {
return res;
}
res.reserve(stat_by_owner_dialog_id.size());
for (auto &by_dialog : stat_by_owner_dialog_id) {
res.reserve(stat_by_owner_dialog_id_.size());
for (auto &by_dialog : stat_by_owner_dialog_id_) {
if (by_dialog.first.is_valid()) {
res.push_back(by_dialog.first);
}
@ -197,27 +197,31 @@ std::vector<DialogId> FileStats::get_dialog_ids() const {
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);
}
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;
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.cnt += type_stat.cnt;
}
sb << "[FileStat " << tag("total", total_stat);
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 << "]";
} else {
{
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) {
total_stat.size += type_stat.size;
total_stat.cnt += type_stat.cnt;
@ -225,7 +229,7 @@ StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats) {
}
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;
for (auto &type_stat : by_type.second) {
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;
};
struct FileStats {
bool need_all_files{false};
bool split_by_owner_dialog_id{false};
class FileStats {
bool need_all_files_{false};
bool split_by_owner_dialog_id_{false};
using StatByType = std::array<FileTypeStat, MAX_FILE_TYPE>;
StatByType stat_by_type;
std::unordered_map<DialogId, StatByType, DialogIdHash> stat_by_owner_dialog_id;
StatByType stat_by_type_;
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(FullFileInfo &&info);
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;
std::vector<DialogId> get_dialog_ids() const;
vector<DialogId> get_dialog_ids() const;
FileTypeStat get_total_nontemp_stat() const;
private:
void add_impl(const FullFileInfo &info);
void add(StatByType &by_type, FileType file_type, int64 size);
vector<FullFileInfo> get_all_files();
};
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;
}
if (!split_by_owner_dialog_id) {
FileStats file_stats;
file_stats.need_all_files = need_all_files;
FileStats file_stats(need_all_files, false);
auto start = Time::now();
scan_fs(token_, [&](FsFileInfo &fs_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"));
}
FileStats file_stats;
file_stats.need_all_files = need_all_files;
file_stats.split_by_owner_dialog_id = split_by_owner_dialog_id;
FileStats file_stats(need_all_files, split_by_owner_dialog_id);
for (auto &full_info : full_infos) {
file_stats.add(std::move(full_info));
if (token_) {

View File

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

View File

@ -6,6 +6,7 @@
//
#include "td/db/SqliteKeyValue.h"
#include "td/utils/base64.h"
#include "td/utils/logging.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();
auto status = set_stmt_.step();
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_.reset();