diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 87300760..0172ad20 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2609,6 +2609,9 @@ getFileExtension mime_type:string = Text; //@description Removes potentially dangerous characters from the name of a file. The encoding of the file name is supposed to be UTF-8. Returns an empty string on failure. This is an offline method. Can be called before authorization. Can be called synchronously @file_name File name or path to the file cleanFileName file_name:string = Text; +//@description Returns a string stored in a local database from specified language pack and language by its key. Returns a 404 error if the string is not found. This is an offline method. Can be called before authorization. Can be called synchronously @language_database_path Path to a language database in which strings are stored @language_pack Language pack of string to return @language_code Language code of string to return @key Language pack key of string to return +getLanguagePackString language_database_path:string language_pack:string language_code:string key:string = LanguagePackString; + //@description Sends an inline query to a bot and returns its results. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @bot_user_id The identifier of the target bot //@chat_id Identifier of the chat, where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index db80463f..052c3108 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/LanguagePackManager.cpp b/td/telegram/LanguagePackManager.cpp index 1ec54707..5d7b0cd9 100644 --- a/td/telegram/LanguagePackManager.cpp +++ b/td/telegram/LanguagePackManager.cpp @@ -102,6 +102,29 @@ static int32 load_database_language_version(SqliteKeyValue *kv) { return to_integer(str_version); } +LanguagePackManager::LanguageDatabase *LanguagePackManager::add_language_database(const string &path) { + auto it = language_databases_.find(path); + if (it != language_databases_.end()) { + return it->second.get(); + } + + SqliteDb database; + if (!path.empty()) { + auto r_database = open_database(path); + if (r_database.is_error()) { + LOG(ERROR) << "Can't open language database " << path << ": " << r_database.error(); + return add_language_database(string()); + } + + database = r_database.move_as_ok(); + } + + it = language_databases_.emplace(path, make_unique()).first; + it->second->path_ = std::move(path); + it->second->database_ = std::move(database); + return it->second.get(); +} + void LanguagePackManager::start_up() { std::lock_guard lock(language_database_mutex_); manager_count_++; @@ -110,29 +133,7 @@ void LanguagePackManager::start_up() { CHECK(check_language_pack_name(language_pack_)); CHECK(check_language_code_name(language_code_)); - string database_path = G()->shared_config().get_option_string("language_database_path"); - auto it = language_databases_.find(database_path); - if (it == language_databases_.end()) { - SqliteDb database; - if (!database_path.empty()) { - auto r_database = open_database(database_path); - if (r_database.is_error()) { - LOG(ERROR) << "Can't open language database " << database_path << ": " << r_database.error(); - database_path = string(); - it = language_databases_.find(database_path); - } else { - database = r_database.move_as_ok(); - } - } - - if (it == language_databases_.end()) { - it = language_databases_.emplace(database_path, make_unique()).first; - it->second->path_ = std::move(database_path); - it->second->database_ = std::move(database); - } - } - database_ = it->second.get(); - + database_ = add_language_database(G()->shared_config().get_option_string("language_database_path")); auto language = add_language(database_, language_pack_, language_code_); LOG(INFO) << "Use language pack " << language_pack_ << " with language " << language_code_ << " of version " @@ -412,6 +413,21 @@ td_api::object_ptr LanguagePackManager::get_language return td_api::make_object(str); } +td_api::object_ptr LanguagePackManager::get_language_pack_string_object(Language *language, + const string &key) { + CHECK(language != nullptr); + auto ordinary_it = language->ordinary_strings_.find(key); + if (ordinary_it != language->ordinary_strings_.end()) { + return get_language_pack_string_object(*ordinary_it); + } + auto pluralized_it = language->pluralized_strings_.find(key); + if (pluralized_it != language->pluralized_strings_.end()) { + return get_language_pack_string_object(*pluralized_it); + } + LOG_IF(ERROR, !language->is_full_ && language->deleted_strings_.count(key) == 0) << "Have no string for key " << key; + return get_language_pack_string_object(key); +} + td_api::object_ptr LanguagePackManager::get_language_pack_strings_object( Language *language, const vector &keys) { CHECK(language != nullptr); @@ -427,19 +443,7 @@ td_api::object_ptr LanguagePackManager::get_languag } } else { for (auto &key : keys) { - auto ordinary_it = language->ordinary_strings_.find(key); - if (ordinary_it != language->ordinary_strings_.end()) { - strings.push_back(get_language_pack_string_object(*ordinary_it)); - continue; - } - auto pluralized_it = language->pluralized_strings_.find(key); - if (pluralized_it != language->pluralized_strings_.end()) { - strings.push_back(get_language_pack_string_object(*pluralized_it)); - continue; - } - LOG_IF(ERROR, !language->is_full_ && language->deleted_strings_.count(key) == 0) - << "Have no string for key " << key; - strings.push_back(get_language_pack_string_object(key)); + strings.push_back(get_language_pack_string_object(language, key)); } } @@ -525,6 +529,37 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector } } +td_api::object_ptr LanguagePackManager::get_language_pack_string(const string &database_path, + const string &language_pack, + const string &language_code, + const string &key) { + if (!check_language_pack_name(language_pack) || language_pack.empty()) { + return td_api::make_object(400, "Language pack is invalid"); + } + if (!check_language_code_name(language_code) || language_code.empty()) { + return td_api::make_object(400, "Language code is invalid"); + } + if (!is_valid_key(key)) { + return td_api::make_object(400, "Key is invalid"); + } + + std::unique_lock language_databases_lock(language_database_mutex_); + LanguageDatabase *database = add_language_database(database_path); + CHECK(database != nullptr); + language_databases_lock.unlock(); + + Language *language = add_language(database, language_pack, language_code); + vector keys{key}; + if (language_has_strings(language, keys)) { + std::lock_guard lock(language->mutex_); + return get_language_pack_string_object(language, key); + } + if (!database->database_.empty() && load_language_strings(database, language, keys)) { + return get_language_pack_string_object(language, key); + } + return td_api::make_object(404, "Not Found"); +} + bool LanguagePackManager::is_valid_key(Slice key) { for (auto c : key) { if (!is_alnum(c) && c != '_' && c != '.' && c != '-') { diff --git a/td/telegram/LanguagePackManager.h b/td/telegram/LanguagePackManager.h index f1a939f2..f046934d 100644 --- a/td/telegram/LanguagePackManager.h +++ b/td/telegram/LanguagePackManager.h @@ -49,6 +49,10 @@ class LanguagePackManager : public NetQueryCallback { void get_language_pack_strings(string language_code, vector keys, Promise> promise); + static td_api::object_ptr get_language_pack_string(const string &database_path, + const string &language_pack, + const string &language_code, const string &key); + void on_update_language_pack(tl_object_ptr difference); private: @@ -68,6 +72,8 @@ class LanguagePackManager : public NetQueryCallback { static std::mutex language_database_mutex_; static std::unordered_map> language_databases_; + static LanguageDatabase *add_language_database(const string &path); + static Language *get_language(LanguageDatabase *database, const string &language_pack, const string &language_code); static Language *get_language(LanguagePack *language_pack, const string &language_code); @@ -85,6 +91,9 @@ class LanguagePackManager : public NetQueryCallback { const std::pair &str); static td_api::object_ptr get_language_pack_string_object(const string &str); + static td_api::object_ptr get_language_pack_string_object(Language *language, + const string &key); + static td_api::object_ptr get_language_pack_strings_object(Language *language, const vector &keys); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 9cd4f265..dbe1fbc2 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3236,6 +3236,7 @@ bool Td::is_synchronous_request(int32 id) { case td_api::getFileMimeType::ID: case td_api::getFileExtension::ID: case td_api::cleanFileName::ID: + case td_api::getLanguagePackString::ID: return true; default: return false; @@ -6618,6 +6619,10 @@ void Td::on_request(uint64 id, const td_api::cleanFileName &request) { send_closure(actor_id(this), &Td::send_result, id, do_static_request(request)); } +void Td::on_request(uint64 id, const td_api::getLanguagePackString &request) { + send_closure(actor_id(this), &Td::send_result, id, do_static_request(request)); +} + template td_api::object_ptr Td::do_static_request(const T &request) { return make_error(400, "Function can't be executed synchronously"); @@ -6672,6 +6677,11 @@ td_api::object_ptr Td::do_static_request(const td_api::cleanFile return make_tl_object(clean_filename(request.file_name_)); } +td_api::object_ptr Td::do_static_request(const td_api::getLanguagePackString &request) { + return LanguagePackManager::get_language_pack_string(request.language_database_path_, request.language_pack_, + request.language_code_, request.key_); +} + // test void Td::on_request(uint64 id, td_api::testNetwork &request) { create_handler(id)->send(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 6310e7ce..a1d27eaf 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -871,6 +871,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::cleanFileName &request); + void on_request(uint64 id, const td_api::getLanguagePackString &request); + // test void on_request(uint64 id, td_api::testNetwork &request); void on_request(uint64 id, td_api::testGetDifference &request); @@ -892,6 +894,7 @@ class Td final : public NetQueryCallback { static td_api::object_ptr do_static_request(const td_api::getFileMimeType &request); static td_api::object_ptr do_static_request(const td_api::getFileExtension &request); static td_api::object_ptr do_static_request(const td_api::cleanFileName &request); + static td_api::object_ptr do_static_request(const td_api::getLanguagePackString &request); Status init(DbKey key) TD_WARN_UNUSED_RESULT; void clear(); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 9c3d76e1..41a92b3f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1741,6 +1741,17 @@ class CliClient final : public Actor { std::tie(language_code, keys) = split(args); send_request(make_tl_object(language_code, full_split(keys))); + } else if (op == "glpss") { + string language_database_path; + string language_pack; + string language_code; + string key; + + std::tie(language_database_path, args) = split(args); + std::tie(language_pack, args) = split(args); + std::tie(language_code, key) = split(args); + send_request( + make_tl_object(language_database_path, language_pack, language_code, key)); } else if (op == "go") { send_request(make_tl_object(args)); } else if (op == "sob") {