diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 7d3639b84..ceb098e9b 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3998,6 +3998,9 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent } if (G()->use_sqlite_pmc()) { G()->td_db()->get_sqlite_pmc()->erase_by_prefix("us_bot_info", Auto()); + if (!G()->use_message_database()) { + G()->td_db()->get_sqlite_pmc()->erase_by_prefix("channel_recommendations", Auto()); + } } was_online_local_ = to_integer(G()->td_db()->get_binlog_pmc()->get("my_was_online_local")); @@ -5497,6 +5500,30 @@ void ContactsManager::SecretChat::parse(ParserT &parser) { } } +template +void ContactsManager::RecommendedDialogs::store(StorerT &storer) const { + bool has_dialog_ids = !dialog_ids_.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_dialog_ids); + END_STORE_FLAGS(); + if (has_dialog_ids) { + td::store(dialog_ids_, storer); + } + store_time(next_reload_time_, storer); +} + +template +void ContactsManager::RecommendedDialogs::parse(ParserT &parser) { + bool has_dialog_ids; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_dialog_ids); + END_PARSE_FLAGS(); + if (has_dialog_ids) { + td::parse(dialog_ids_, parser); + } + parse_time(next_reload_time_, parser); +} + Result> ContactsManager::get_input_user(UserId user_id) const { if (user_id == get_my_id()) { return make_tl_object(); @@ -9623,6 +9650,7 @@ void ContactsManager::get_channel_recommendations(DialogId dialog_id, if (!is_broadcast_channel(channel_id) || get_input_channel(channel_id) == nullptr) { return promise.set_value(td_api::make_object()); } + bool use_database = true; auto it = channel_recommended_dialogs_.find(channel_id); if (it != channel_recommended_dialogs_.end()) { if (are_suitable_recommended_dialogs(it->second)) { @@ -9636,32 +9664,90 @@ void ContactsManager::get_channel_recommendations(DialogId dialog_id, } else { LOG(INFO) << "Drop cache for similar chats of " << dialog_id; channel_recommended_dialogs_.erase(it); + G()->td_db()->get_sqlite_pmc()->erase(get_channel_recommendations_database_key(channel_id), Auto()); } + use_database = false; } - reload_channel_recommendations(channel_id, std::move(promise)); + load_channel_recommendations(channel_id, use_database, std::move(promise)); } -void ContactsManager::reload_channel_recommendations(ChannelId channel_id, - Promise> &&promise) { +string ContactsManager::get_channel_recommendations_database_key(ChannelId channel_id) { + return PSTRING() << "channel_recommendations" << channel_id.get(); +} + +void ContactsManager::load_channel_recommendations(ChannelId channel_id, bool use_database, + Promise> &&promise) { auto &queries = get_channel_recommendations_queries_[channel_id]; queries.push_back(std::move(promise)); if (queries.size() == 1) { - auto query_promise = PromiseCreator::lambda( - [actor_id = actor_id(this), channel_id](Result>> &&result) { - send_closure(actor_id, &ContactsManager::on_get_channel_recommendations, channel_id, std::move(result)); - }); - td_->create_handler(std::move(query_promise))->send(channel_id); + if (G()->use_message_database() && use_database) { + G()->td_db()->get_sqlite_pmc()->get( + get_channel_recommendations_database_key(channel_id), + PromiseCreator::lambda([actor_id = actor_id(this), channel_id](string value) { + send_closure(actor_id, &ContactsManager::on_load_channel_recommendations_from_database, channel_id, + std::move(value)); + })); + } else { + reload_channel_recommendations(channel_id); + } } } +void ContactsManager::on_load_channel_recommendations_from_database(ChannelId channel_id, string value) { + if (G()->close_flag()) { + auto it = get_channel_recommendations_queries_.find(channel_id); + CHECK(it != get_channel_recommendations_queries_.end()); + auto promises = std::move(it->second); + CHECK(!promises.empty()); + get_channel_recommendations_queries_.erase(it); + return fail_promises(promises, G()->close_status()); + } + + if (value.empty()) { + return reload_channel_recommendations(channel_id); + } + auto &recommended_dialogs = channel_recommended_dialogs_[channel_id]; + if (log_event_parse(recommended_dialogs, value).is_error() || + !are_suitable_recommended_dialogs(recommended_dialogs)) { + channel_recommended_dialogs_.erase(channel_id); + G()->td_db()->get_sqlite_pmc()->erase(get_channel_recommendations_database_key(channel_id), Auto()); + return reload_channel_recommendations(channel_id); + } + + auto it = get_channel_recommendations_queries_.find(channel_id); + CHECK(it != get_channel_recommendations_queries_.end()); + auto promises = std::move(it->second); + CHECK(!promises.empty()); + get_channel_recommendations_queries_.erase(it); + auto dialog_ids = recommended_dialogs.dialog_ids_; + auto next_reload_time = recommended_dialogs.next_reload_time_; + for (auto &promise : promises) { + if (promise) { + promise.set_value( + td_->messages_manager_->get_chats_object(-1, dialog_ids, "on_load_channel_recommendations_from_database")); + } + } + if (next_reload_time <= Time::now()) { + load_channel_recommendations(channel_id, false, Auto()); + } +} + +void ContactsManager::reload_channel_recommendations(ChannelId channel_id) { + auto query_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), channel_id](Result>> &&result) { + send_closure(actor_id, &ContactsManager::on_get_channel_recommendations, channel_id, std::move(result)); + }); + td_->create_handler(std::move(query_promise))->send(channel_id); +} + void ContactsManager::on_get_channel_recommendations(ChannelId channel_id, Result>> &&r_chats) { G()->ignore_result_if_closing(r_chats); auto it = get_channel_recommendations_queries_.find(channel_id); CHECK(it != get_channel_recommendations_queries_.end()); - CHECK(!it->second.empty()); auto promises = std::move(it->second); + CHECK(!promises.empty()); get_channel_recommendations_queries_.erase(it); if (r_chats.is_error()) { @@ -9681,7 +9767,10 @@ void ContactsManager::on_get_channel_recommendations(ChannelId channel_id, recommended_dialogs.dialog_ids_ = dialog_ids; recommended_dialogs.next_reload_time_ = Time::now() + CHANNEL_RECOMMENDATIONS_CACHE_TIME; - // save_channel_recommendations(channel_id); + if (G()->use_message_database()) { + G()->td_db()->get_sqlite_pmc()->set(get_channel_recommendations_database_key(channel_id), + log_event_store(recommended_dialogs).as_slice().str(), Promise()); + } for (auto &promise : promises) { if (promise) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 854b4f1d8..7f87c843c 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -1208,6 +1208,12 @@ class ContactsManager final : public Actor { struct RecommendedDialogs { vector dialog_ids_; double next_reload_time_ = 0.0; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; class UserLogEvent; @@ -1722,7 +1728,14 @@ class ContactsManager final : public Actor { bool are_suitable_recommended_dialogs(const RecommendedDialogs &recommended_dialogs) const; - void reload_channel_recommendations(ChannelId channel_id, Promise> &&promise); + static string get_channel_recommendations_database_key(ChannelId channel_id); + + void load_channel_recommendations(ChannelId channel_id, bool use_database, + Promise> &&promise); + + void on_load_channel_recommendations_from_database(ChannelId channel_id, string value); + + void reload_channel_recommendations(ChannelId channel_id); void on_get_channel_recommendations(ChannelId channel_id, Result>> &&r_chats); diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp index a217d9f66..faca5922b 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -459,8 +459,10 @@ Status TdDb::init_sqlite(const Parameters ¶meters, const DbKey &key, const D common_kv_async_ = create_sqlite_key_value_async(common_kv_safe_); if (was_dialog_db_created_) { - get_sqlite_sync_pmc()->erase("calls_db_state"); - get_sqlite_sync_pmc()->erase("di_active_live_location_messages"); + auto *sqlite_pmc = get_sqlite_sync_pmc(); + sqlite_pmc->erase("calls_db_state"); + sqlite_pmc->erase("di_active_live_location_messages"); + sqlite_pmc->erase_by_prefix("channel_recommendations"); } if (use_dialog_db) {