From 7150cfc8b201abc26ebcc4c9ef533163758e66ec Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 26 Jun 2018 22:51:00 +0300 Subject: [PATCH] Add disable_top_chats option. GitOrigin-RevId: 732ba115f67d19585b4e2f4aae024194c6e65174 --- td/mtproto/Transport.h | 2 + td/telegram/Td.cpp | 7 + td/telegram/TopDialogManager.cpp | 228 ++++++++++++++++++++++--------- td/telegram/TopDialogManager.h | 19 ++- tdutils/td/utils/Status.h | 2 +- 5 files changed, 192 insertions(+), 66 deletions(-) diff --git a/td/mtproto/Transport.h b/td/mtproto/Transport.h index 2e732142a..6590f8b0c 100644 --- a/td/mtproto/Transport.h +++ b/td/mtproto/Transport.h @@ -5,6 +5,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once + #include "td/mtproto/utils.h" #include "td/utils/common.h" @@ -234,5 +235,6 @@ class Transport { static void write_crypto_impl(int X, const Storer &storer, const AuthKey &auth_key, PacketInfo *info, HeaderT *header, size_t data_size); }; + } // namespace mtproto } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 95282c95b..04c53aeb5 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -4262,9 +4262,13 @@ void Td::on_config_option_updated(const string &name) { 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); + } else if (name == "disable_top_chats") { + send_closure(top_dialog_manager_, &TopDialogManager::update_is_enabled, + !G()->shared_config().get_option_boolean(name)); } else if (is_internal_config_option(name)) { return; } + // send_closure was already used in the callback send_update(make_tl_object(name, G()->shared_config().get_option_value(name))); } @@ -6682,6 +6686,9 @@ void Td::on_request(uint64 id, td_api::setOption &request) { if (set_boolean_option("disable_contact_registered_notifications")) { return; } + if (set_boolean_option("disable_top_chats")) { + return; + } break; case 'i': if (set_boolean_option("ignore_inline_thumbnails")) { diff --git a/td/telegram/TopDialogManager.cpp b/td/telegram/TopDialogManager.cpp index 73098c1ba..bfc32ef3a 100644 --- a/td/telegram/TopDialogManager.cpp +++ b/td/telegram/TopDialogManager.cpp @@ -90,8 +90,42 @@ static tl_object_ptr top_dialog_category_as_teleg } } +void TopDialogManager::update_is_enabled(bool is_enabled) { + if (set_is_enabled(is_enabled)) { + G()->td_db()->get_binlog_pmc()->set("top_peers_enabled", is_enabled ? "1" : "0"); + send_toggle_top_peers(is_enabled); + + loop(); + } +} + +bool TopDialogManager::set_is_enabled(bool is_enabled) { + if (is_enabled_ == is_enabled) { + return false; + } + + LOG(DEBUG) << "Change top chats is_enabled to " << is_enabled; + is_enabled_ = is_enabled; + init(); + return true; +} + +void TopDialogManager::send_toggle_top_peers(bool is_enabled) { + if (have_toggle_top_peers_query_) { + have_pending_toggle_top_peers_query_ = true; + pending_toggle_top_peers_query_ = is_enabled; + return; + } + + 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(create_storer(telegram_api::contacts_toggleTopPeers(is_enabled))); + G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 2)); +} + void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date) { - if (!is_active_) { + if (!is_active_ || !is_enabled_) { return; } auto pos = static_cast(category); @@ -129,7 +163,7 @@ void TopDialogManager::on_dialog_used(TopDialogCategory category, DialogId dialo void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog_id, tl_object_ptr input_peer) { - if (!is_active_) { + if (!is_active_ || !is_enabled_) { return; } @@ -165,6 +199,11 @@ void TopDialogManager::get_top_dialogs(TopDialogCategory category, size_t limit, promise.set_error(Status::Error(400, "Not supported without chat info database")); return; } + if (!is_enabled_) { + promise.set_error(Status::Error(400, "Top chats computation is disabled")); + return; + } + GetTopDialogsQuery query; query.category = category; query.limit = limit; @@ -302,53 +341,91 @@ void TopDialogManager::do_get_top_peers() { } void TopDialogManager::on_result(NetQueryPtr net_query) { - if (get_link_token() == 1) { + 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(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 - last_server_sync_ = Timestamp::now(); - server_sync_state_ = SyncState::Ok; - G()->td_db()->get_binlog_pmc()->set("top_dialogs_ts", to_string(static_cast(Clocks::system()))); auto r_top_peers = fetch_result(std::move(net_query)); if (r_top_peers.is_error()) { - LOG(ERROR) << "contacts_getTopPeers failed: " << r_top_peers.error(); + last_server_sync_ = Timestamp::in(SERVER_SYNC_RESEND_DELAY - SERVER_SYNC_DELAY); 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(Clocks::system()))); + }; + auto top_peers_parent = r_top_peers.move_as_ok(); - LOG(INFO) << "contacts_getTopPeers returned " << to_string(top_peers_parent); - if (top_peers_parent->get_id() == telegram_api::contacts_topPeersNotModified::ID) { - return; - } - if (top_peers_parent->get_id() == telegram_api::contacts_topPeersDisabled::ID) { - // FIXME - return; - } + LOG(DEBUG) << "contacts_getTopPeers returned " << to_string(top_peers_parent); + switch (top_peers_parent->get_id()) { + case telegram_api::contacts_topPeersNotModified::ID: + // nothing to do + return; + case telegram_api::contacts_topPeersDisabled::ID: + G()->shared_config().set_option_boolean("disable_top_chats", true); + set_is_enabled(false); // apply immediately + return; + 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(std::move(top_peers_parent)); - CHECK(top_peers_parent->get_id() == telegram_api::contacts_topPeers::ID); - auto top_peers = move_tl_object_as(std::move(top_peers_parent)); + send_closure(G()->contacts_manager(), &ContactsManager::on_get_users, std::move(top_peers->users_)); + send_closure(G()->contacts_manager(), &ContactsManager::on_get_chats, std::move(top_peers->chats_)); + for (auto &category : top_peers->categories_) { + auto dialog_category = top_dialog_category_from_telegram_api(*category->category_); + auto pos = static_cast(dialog_category); + CHECK(pos < by_category_.size()); + auto &top_dialogs = by_category_[pos]; - send_closure(G()->contacts_manager(), &ContactsManager::on_get_users, std::move(top_peers->users_)); - send_closure(G()->contacts_manager(), &ContactsManager::on_get_chats, std::move(top_peers->chats_)); - for (auto &category : top_peers->categories_) { - auto dialog_category = top_dialog_category_from_telegram_api(*category->category_); - auto pos = static_cast(dialog_category); - CHECK(pos < by_category_.size()); - auto &top_dialogs = by_category_[pos]; - - top_dialogs.is_dirty = true; - top_dialogs.dialogs.clear(); - for (auto &top_peer : category->peers_) { - TopDialog top_dialog; - top_dialog.dialog_id = DialogId(top_peer->peer_); - top_dialog.rating = top_peer->rating_; - top_dialogs.dialogs.push_back(std::move(top_dialog)); + top_dialogs.is_dirty = true; + top_dialogs.dialogs.clear(); + for (auto &top_peer : category->peers_) { + TopDialog top_dialog; + top_dialog.dialog_id = DialogId(top_peer->peer_); + top_dialog.rating = top_peer->rating_; + top_dialogs.dialogs.push_back(std::move(top_dialog)); + } + } + db_sync_state_ = SyncState::None; + break; } + default: + UNREACHABLE(); } - db_sync_state_ = SyncState::None; } void TopDialogManager::do_save_top_dialogs() { @@ -370,12 +447,31 @@ void TopDialogManager::do_save_top_dialogs() { } void TopDialogManager::start_up() { - if (!G()->parameters().use_chat_info_db) { + is_active_ = G()->parameters().use_chat_info_db; + is_enabled_ = !G()->shared_config().get_option_boolean("disable_top_chats"); + update_rating_e_decay(); + + string need_update_top_peers = G()->td_db()->get_binlog_pmc()->get("top_peers_enabled"); + if (!need_update_top_peers.empty()) { + send_toggle_top_peers(need_update_top_peers[0] == '1'); + } + + init(); + loop(); +} + +void TopDialogManager::init() { + was_first_sync_ = false; + first_unsync_change_ = Timestamp(); + server_sync_state_ = SyncState::None; + last_server_sync_ = Timestamp(); + CHECK(pending_get_top_dialogs_.empty()); + + LOG(DEBUG) << "Init is enabled: " << is_enabled_; + if (!is_active_) { G()->td_db()->get_binlog_pmc()->erase_by_prefix("top_dialogs"); - is_active_ = false; return; } - is_active_ = true; auto di_top_dialogs_ts = G()->td_db()->get_binlog_pmc()->get("top_dialogs_ts"); if (!di_top_dialogs_ts.empty()) { @@ -384,27 +480,33 @@ void TopDialogManager::start_up() { server_sync_state_ = SyncState::Ok; } } - update_rating_e_decay(); - 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 value = G()->td_db()->get_binlog_pmc()->get(key); + 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 value = G()->td_db()->get_binlog_pmc()->get(key); - auto &top_dialogs = by_category_[top_dialog_category_i]; - top_dialogs.is_dirty = false; - if (value.empty()) { - continue; + auto &top_dialogs = by_category_[top_dialog_category_i]; + top_dialogs.is_dirty = false; + if (value.empty()) { + continue; + } + log_event_parse(top_dialogs, value).ensure(); + } + normalize_rating(); + } else { + G()->td_db()->get_binlog_pmc()->erase_by_prefix("top_dialogs#"); + for (auto &top_dialogs : by_category_) { + top_dialogs.is_dirty = false; + top_dialogs.rating_timestamp = 0; + top_dialogs.dialogs.clear(); } - log_event_parse(top_dialogs, value).ensure(); } - normalize_rating(); db_sync_state_ = SyncState::Ok; send_closure(G()->state_manager(), &StateManager::wait_first_sync, PromiseCreator::event(self_closure(this, &TopDialogManager::on_first_sync))); - - loop(); } void TopDialogManager::on_first_sync() { @@ -441,22 +543,24 @@ void TopDialogManager::loop() { do_get_top_peers(); } - // db sync - Timestamp db_sync_timeout; - if (db_sync_state_ == SyncState::Ok) { - if (first_unsync_change_) { - db_sync_timeout = Timestamp::at(first_unsync_change_.at() + DB_SYNC_DELAY); - if (db_sync_timeout.is_in_past()) { - db_sync_state_ = SyncState::None; + if (is_enabled_) { + // db sync + Timestamp db_sync_timeout; + if (db_sync_state_ == SyncState::Ok) { + if (first_unsync_change_) { + db_sync_timeout = Timestamp::at(first_unsync_change_.at() + DB_SYNC_DELAY); + if (db_sync_timeout.is_in_past()) { + db_sync_state_ = SyncState::None; + } } } - } - if (db_sync_state_ == SyncState::Ok) { - wakeup_timeout.relax(db_sync_timeout); - } else if (db_sync_state_ == SyncState::None) { - if (server_sync_state_ == SyncState::Ok) { - do_save_top_dialogs(); + if (db_sync_state_ == SyncState::Ok) { + wakeup_timeout.relax(db_sync_timeout); + } else if (db_sync_state_ == SyncState::None) { + if (server_sync_state_ == SyncState::Ok) { + do_save_top_dialogs(); + } } } diff --git a/td/telegram/TopDialogManager.h b/td/telegram/TopDialogManager.h index 77407cd75..da40c486d 100644 --- a/td/telegram/TopDialogManager.h +++ b/td/telegram/TopDialogManager.h @@ -57,13 +57,23 @@ class TopDialogManager : public NetQueryCallback { void update_rating_e_decay(); + void update_is_enabled(bool is_enabled); + private: static constexpr size_t MAX_TOP_DIALOGS_LIMIT = 30; - static constexpr int32 SERVER_SYNC_DELAY = 86400; // seconds - static constexpr int32 DB_SYNC_DELAY = 5; // seconds + 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 ActorShared<> parent_; 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}; enum class SyncState : int32 { None, Pending, Ok }; SyncState db_sync_state_ = SyncState::None; @@ -106,7 +116,8 @@ class TopDialogManager : public NetQueryCallback { double current_rating_add(double rating_timestamp) const; void normalize_rating(); - int32 rating_e_decay_ = 241920; + bool set_is_enabled(bool is_enabled); + void send_toggle_top_peers(bool is_enabled); void do_get_top_dialogs(GetTopDialogsQuery &&query); @@ -117,6 +128,8 @@ class TopDialogManager : public NetQueryCallback { void on_result(NetQueryPtr net_query) override; + void init(); + void start_up() override; void loop() override; }; diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index 43f81229f..2b83ab9ee 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -176,7 +176,7 @@ class Status { void ensure_impl(CSlice file_name, int line) const { if (!is_ok()) { - LOG(FATAL) << "Unexpexted Status " << to_string() << " in file " << file_name << " at line " << line; + LOG(FATAL) << "Unexpected Status " << to_string() << " in file " << file_name << " at line " << line; } } void ensure_error_impl(CSlice file_name, int line) const {