diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 29d0d4323..6de0b0ae2 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -7849,10 +7849,10 @@ setAutoDownloadSettings settings:autoDownloadSettings type:NetworkType = Ok; //@description Returns autosave settings for the current user getAutosaveSettings = AutosaveSettings; -//@description Sets autosave settings for the given scope @scope Autosave settings scope @settings New autosave settings for the scope; pass null to set autosave settings to default +//@description Sets autosave settings for the given scope. The method is guaranteed to work only after at least one call to getAutosaveSettings @scope Autosave settings scope @settings New autosave settings for the scope; pass null to set autosave settings to default setAutosaveSettings scope:AutosaveSettingsScope settings:scopeAutosaveSettings = Ok; -//@description Clears the list of all autosave settings exceptions +//@description Clears the list of all autosave settings exceptions. The method is guaranteed to work only after at least one call to getAutosaveSettings clearAutosaveSettingsExceptions = Ok; diff --git a/td/telegram/AutosaveManager.cpp b/td/telegram/AutosaveManager.cpp index f6b37460a..e04f067d8 100644 --- a/td/telegram/AutosaveManager.cpp +++ b/td/telegram/AutosaveManager.cpp @@ -7,9 +7,12 @@ #include "td/telegram/AutosaveManager.h" #include "td/telegram/ContactsManager.h" +#include "td/telegram/Dependencies.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/Td.h" +#include "td/db/SqliteKeyValueAsync.h" + #include "td/utils/algorithm.h" #include "td/utils/buffer.h" #include "td/utils/misc.h" @@ -88,7 +91,7 @@ class SaveAutoSaveSettingsQuery final : public Td::ResultHandler { void on_error(Status status) final { promise_.set_error(std::move(status)); - td_->autosave_manager_->reload_autosave_settings(Auto()); + td_->autosave_manager_->reload_autosave_settings(); } }; @@ -114,7 +117,7 @@ class DeleteAutoSaveExceptionsQuery final : public Td::ResultHandler { void on_error(Status status) final { promise_.set_error(std::move(status)); - td_->autosave_manager_->reload_autosave_settings(Auto()); + td_->autosave_manager_->reload_autosave_settings(); } }; @@ -181,6 +184,26 @@ bool AutosaveManager::DialogAutosaveSettings::operator!=(const DialogAutosaveSet return !operator==(other); } +template +void AutosaveManager::DialogAutosaveSettings::store(StorerT &storer) const { + CHECK(are_inited_); + BEGIN_STORE_FLAGS(); + STORE_FLAG(autosave_photos_); + STORE_FLAG(autosave_videos_); + END_STORE_FLAGS(); + td::store(max_video_file_size_, storer); +} + +template +void AutosaveManager::DialogAutosaveSettings::parse(ParserT &parser) { + are_inited_ = true; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(autosave_photos_); + PARSE_FLAG(autosave_videos_); + END_PARSE_FLAGS(); + td::parse(max_video_file_size_, parser); +} + td_api::object_ptr AutosaveManager::AutosaveSettings::get_autosave_settings_object() const { CHECK(are_inited_); auto exceptions = transform(exceptions_, [](const auto &exception) { @@ -191,20 +214,140 @@ td_api::object_ptr AutosaveManager::AutosaveSettings:: broadcast_settings_.get_scope_autosave_settings_object(), std::move(exceptions)); } +template +void AutosaveManager::AutosaveSettings::store(StorerT &storer) const { + CHECK(are_inited_); + bool has_exceptions = !exceptions_.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_exceptions); + END_STORE_FLAGS(); + td::store(user_settings_, storer); + td::store(chat_settings_, storer); + td::store(broadcast_settings_, storer); + if (has_exceptions) { + td::store(narrow_cast(exceptions_.size()), storer); + for (auto &exception : exceptions_) { + td::store(exception.first, storer); + td::store(exception.second, storer); + } + } +} + +template +void AutosaveManager::AutosaveSettings::parse(ParserT &parser) { + are_inited_ = true; + bool has_exceptions; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_exceptions); + END_PARSE_FLAGS(); + td::parse(user_settings_, parser); + td::parse(chat_settings_, parser); + td::parse(broadcast_settings_, parser); + if (has_exceptions) { + uint32 size; + td::parse(size, parser); + for (size_t i = 0; i < size; i++) { + DialogId dialog_id; + DialogAutosaveSettings settings; + td::parse(dialog_id, parser); + td::parse(settings, parser); + CHECK(dialog_id.is_valid()); + exceptions_.emplace(dialog_id, std::move(settings)); + } + } +} + void AutosaveManager::get_autosave_settings(Promise> &&promise) { if (settings_.are_inited_) { return promise.set_value(settings_.get_autosave_settings_object()); } - reload_autosave_settings(std::move(promise)); + load_autosave_settings(std::move(promise)); } -void AutosaveManager::reload_autosave_settings(Promise> &&promise) { +string AutosaveManager::get_autosave_settings_database_key() { + return "autosave_settings"; +} + +void AutosaveManager::load_autosave_settings(Promise> &&promise) { load_settings_queries_.push_back(std::move(promise)); if (load_settings_queries_.size() != 1) { return; } + if (G()->parameters().use_message_db) { + G()->td_db()->get_sqlite_pmc()->get( + get_autosave_settings_database_key(), + PromiseCreator::lambda( + [actor_id = actor_id(this)](string value) mutable { + send_closure(actor_id, &AutosaveManager::on_load_autosave_settings_from_database, std::move(value)); + })); + return; + } + + reload_autosave_settings(); +} + +void AutosaveManager::on_load_autosave_settings_from_database(string value) { + if (settings_.are_inited_) { + CHECK(load_settings_queries_.empty()); + return; + } + if (G()->close_flag()) { + return fail_promises(load_settings_queries_, Global::request_aborted_error()); + } + if (value.empty()) { + LOG(INFO) << "Autosave settings aren't found in database"; + return reload_autosave_settings(); + } + + LOG(INFO) << "Successfully loaded autosave settings from database"; + + auto status = log_event_parse(settings_, value); + if (status.is_error()) { + LOG(ERROR) << "Can't load autosave settings: " << status; + settings_ = {}; + return reload_autosave_settings(); + } + + Dependencies dependencies; + for (auto &exception : settings_.exceptions_) { + dependencies.add_dialog_and_dependencies(exception.first); + } + if (!dependencies.resolve_force(td_, "on_load_autosave_settings_from_database")) { + G()->td_db()->get_binlog_pmc()->erase(get_autosave_settings_database_key()); + settings_ = {}; + return reload_autosave_settings(); + } + + settings_.are_inited_ = true; + send_update_autosave_settings(td_api::make_object(), + settings_.user_settings_); + send_update_autosave_settings(td_api::make_object(), + settings_.chat_settings_); + send_update_autosave_settings(td_api::make_object(), + settings_.broadcast_settings_); + for (auto &exception : settings_.exceptions_) { + send_update_autosave_settings(td_api::make_object(exception.first.get()), + exception.second); + } + + auto promises = std::move(load_settings_queries_); + for (auto &promise : promises) { + promise.set_value(settings_.get_autosave_settings_object()); + } +} + +void AutosaveManager::reload_autosave_settings() { + if (G()->close_flag()) { + return fail_promises(load_settings_queries_, Global::request_aborted_error()); + } + if (settings_.are_being_reloaded_) { + settings_.need_reload_ = true; + return; + } + settings_.are_being_reloaded_ = true; + auto query_promise = PromiseCreator::lambda( [actor_id = actor_id(this)](Result> r_settings) { send_closure(actor_id, &AutosaveManager::on_get_autosave_settings, std::move(r_settings)); @@ -214,6 +357,14 @@ void AutosaveManager::reload_autosave_settings(Promise> r_settings) { + CHECK(settings_.are_being_reloaded_); + settings_.are_being_reloaded_ = false; + SCOPE_EXIT { + if (settings_.need_reload_) { + settings_.need_reload_ = false; + reload_autosave_settings(); + } + }; if (G()->close_flag() && r_settings.is_ok()) { r_settings = Global::request_aborted_error(); } @@ -270,11 +421,19 @@ void AutosaveManager::on_get_autosave_settings( DialogAutosaveSettings()); } + save_autosave_settings(); + auto promises = std::move(load_settings_queries_); for (auto &promise : promises) { - if (promise) { - promise.set_value(settings_.get_autosave_settings_object()); - } + promise.set_value(settings_.get_autosave_settings_object()); + } +} + +void AutosaveManager::save_autosave_settings() { + if (G()->parameters().use_message_db) { + LOG(INFO) << "Save autosave settings to database"; + G()->td_db()->get_sqlite_pmc()->set(get_autosave_settings_database_key(), + log_event_store(settings_).as_slice().str(), Auto()); } } @@ -284,6 +443,9 @@ void AutosaveManager::set_autosave_settings(td_api::object_ptrcreate_handler(std::move(promise)) ->send(users, chats, broadcasts, dialog_id, new_settings.get_input_auto_save_settings()); } void AutosaveManager::clear_autosave_settings_excpetions(Promise &&promise) { + if (!settings_.are_inited_) { + return promise.set_error(Status::Error(400, "Autosave settings must be loaded first")); + } for (const auto &exception : settings_.exceptions_) { send_update_autosave_settings(td_api::make_object(exception.first.get()), DialogAutosaveSettings()); } settings_.exceptions_.clear(); + save_autosave_settings(); td_->create_handler(std::move(promise))->send(); } diff --git a/td/telegram/AutosaveManager.h b/td/telegram/AutosaveManager.h index 6a432500c..e37e208da 100644 --- a/td/telegram/AutosaveManager.h +++ b/td/telegram/AutosaveManager.h @@ -25,7 +25,7 @@ class AutosaveManager final : public Actor { public: AutosaveManager(Td *td, ActorShared<> parent); - void reload_autosave_settings(Promise> &&promise); + void reload_autosave_settings(); void get_autosave_settings(Promise> &&promise); @@ -63,22 +63,44 @@ class AutosaveManager final : public Actor { bool operator==(const DialogAutosaveSettings &other) const; bool operator!=(const DialogAutosaveSettings &other) const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; struct AutosaveSettings { bool are_inited_ = false; + bool are_being_reloaded_ = false; + bool need_reload_ = false; DialogAutosaveSettings user_settings_; DialogAutosaveSettings chat_settings_; DialogAutosaveSettings broadcast_settings_; FlatHashMap exceptions_; td_api::object_ptr get_autosave_settings_object() const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; void tear_down() final; + string get_autosave_settings_database_key(); + + void load_autosave_settings(Promise> &&promise); + + void on_load_autosave_settings_from_database(string value); + void on_get_autosave_settings(Result> r_settings); + void save_autosave_settings(); + static td_api::object_ptr get_update_autosave_settings( td_api::object_ptr &&scope, const DialogAutosaveSettings &settings); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index a48e01cc0..75e1bca18 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -3917,7 +3917,7 @@ void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - td_->autosave_manager_->reload_autosave_settings(Auto()); + td_->autosave_manager_->reload_autosave_settings(); promise.set_value(Unit()); }