// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // 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/CountryInfoManager.h" #include "td/telegram/AuthManager.h" #include "td/telegram/Global.h" #include "td/telegram/LanguagePackManager.h" #include "td/telegram/misc.h" #include "td/telegram/OptionManager.h" #include "td/telegram/Td.h" #include "td/telegram/telegram_api.h" #include "td/utils/algorithm.h" #include "td/utils/base64.h" #include "td/utils/buffer.h" #include "td/utils/Gzip.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Random.h" #include "td/utils/Time.h" #include "td/utils/tl_parsers.h" namespace td { class GetNearestDcQuery final : public Td::ResultHandler { Promise promise_; public: explicit GetNearestDcQuery(Promise &&promise) : promise_(std::move(promise)) { } void send() { send_query(G()->net_query_creator().create_unauth(telegram_api::help_getNearestDc())); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } auto result = result_ptr.move_as_ok(); promise_.set_value(std::move(result->country_)); } void on_error(Status status) final { if (!G()->is_expected_error(status) && status.message() != "BOT_METHOD_INVALID") { LOG(ERROR) << "GetNearestDc returned " << status; } promise_.set_error(std::move(status)); } }; class GetCountriesListQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetCountriesListQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const string &language_code, int32 hash) { send_query(G()->net_query_creator().create_unauth(telegram_api::help_getCountriesList(language_code, hash))); } void on_result(BufferSlice packet) final { // LOG(ERROR) << base64url_encode(gzencode(packet, 0.9)); auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } promise_.set_value(result_ptr.move_as_ok()); } void on_error(Status status) final { if (!G()->is_expected_error(status)) { LOG(ERROR) << "GetCountriesList returned " << status; } promise_.set_error(std::move(status)); } }; struct CountryInfoManager::CallingCodeInfo { string calling_code; vector prefixes; vector patterns; }; struct CountryInfoManager::CountryInfo { string country_code; string default_name; string name; vector calling_codes; bool is_hidden = false; td_api::object_ptr get_country_info_object() const { return td_api::make_object( country_code, name.empty() ? default_name : name, default_name, is_hidden, transform(calling_codes, [](const CallingCodeInfo &info) { return info.calling_code; })); } }; struct CountryInfoManager::CountryList { vector countries; int32 hash = 0; double next_reload_time = 0.0; td_api::object_ptr get_countries_object() const { return td_api::make_object( transform(countries, [](const CountryInfo &info) { return info.get_country_info_object(); })); } }; CountryInfoManager::CountryInfoManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { on_update_fragment_prefixes(); } CountryInfoManager::~CountryInfoManager() = default; void CountryInfoManager::start_up() { std::lock_guard country_lock(country_mutex_); manager_count_++; } void CountryInfoManager::tear_down() { parent_.reset(); std::lock_guard country_lock(country_mutex_); manager_count_--; if (manager_count_ == 0 && !countries_.empty()) { LOG(INFO) << "Clear country info"; countries_.clear(); } } bool CountryInfoManager::is_fragment_phone_number(string phone_number) { if (phone_number.empty()) { return false; } if (fragment_prefixes_.empty()) { fragment_prefixes_str_ = "888"; fragment_prefixes_.push_back(fragment_prefixes_str_); } clean_phone_number(phone_number); for (auto &prefix : fragment_prefixes_) { if (begins_with(phone_number, prefix)) { return true; } } return false; } void CountryInfoManager::on_update_fragment_prefixes() { if (G()->close_flag()) { return; } if (td_->auth_manager_->is_bot()) { return; } if (!td_->option_manager_->have_option("fragment_prefixes")) { return; } auto fragment_prefixes_str = td_->option_manager_->get_option_string("fragment_prefixes", "888"); std::lock_guard country_lock(country_mutex_); if (fragment_prefixes_str == fragment_prefixes_str_) { return; } fragment_prefixes_str_ = std::move(fragment_prefixes_str); fragment_prefixes_ = full_split(fragment_prefixes_str_, ','); } string CountryInfoManager::get_main_language_code() { return to_lower(td_->language_pack_manager_.get_actor_unsafe()->get_main_language_code()); } void CountryInfoManager::get_countries(Promise> &&promise) { do_get_countries(get_main_language_code(), false, std::move(promise)); } void CountryInfoManager::do_get_countries(string language_code, bool is_recursive, Promise> &&promise) { if (is_recursive) { auto main_language_code = get_main_language_code(); if (language_code != main_language_code) { language_code = std::move(main_language_code); is_recursive = false; } } { std::lock_guard country_lock(country_mutex_); auto list = get_country_list(this, language_code); if (list != nullptr) { return promise.set_value(list->get_countries_object()); } } if (is_recursive) { return promise.set_error(Status::Error(500, "Requested data is inaccessible")); } if (language_code.empty()) { return promise.set_error(Status::Error(400, "Invalid language code specified")); } load_country_list(language_code, 0, PromiseCreator::lambda([actor_id = actor_id(this), language_code, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &CountryInfoManager::do_get_countries, std::move(language_code), true, std::move(promise)); })); } void CountryInfoManager::get_phone_number_info(string phone_number_prefix, Promise> &&promise) { clean_phone_number(phone_number_prefix); if (phone_number_prefix.empty()) { return promise.set_value(td_api::make_object(nullptr, string(), string(), false)); } do_get_phone_number_info(std::move(phone_number_prefix), get_main_language_code(), false, std::move(promise)); } void CountryInfoManager::do_get_phone_number_info(string phone_number_prefix, string language_code, bool is_recursive, Promise> &&promise) { if (is_recursive) { auto main_language_code = get_main_language_code(); if (language_code != main_language_code) { language_code = std::move(main_language_code); is_recursive = false; } } { std::lock_guard country_lock(country_mutex_); auto list = get_country_list(this, language_code); if (list != nullptr) { return promise.set_value(get_phone_number_info_object(list, phone_number_prefix)); } } if (is_recursive) { return promise.set_error(Status::Error(500, "Requested data is inaccessible")); } if (language_code.empty()) { return promise.set_error(Status::Error(400, "Invalid language code specified")); } load_country_list(language_code, 0, PromiseCreator::lambda([actor_id = actor_id(this), phone_number_prefix, language_code, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &CountryInfoManager::do_get_phone_number_info, std::move(phone_number_prefix), std::move(language_code), true, std::move(promise)); })); } td_api::object_ptr CountryInfoManager::get_phone_number_info_sync(const string &language_code, string phone_number_prefix) { clean_phone_number(phone_number_prefix); if (phone_number_prefix.empty()) { return td_api::make_object(nullptr, string(), string(), false); } std::lock_guard country_lock(country_mutex_); auto list = get_country_list(nullptr, language_code); if (list == nullptr) { list = get_country_list(nullptr, "en"); } return get_phone_number_info_object(list, phone_number_prefix); } td_api::object_ptr CountryInfoManager::get_phone_number_info_object(const CountryList *list, Slice phone_number) { CHECK(list != nullptr); const CountryInfo *best_country = nullptr; const CallingCodeInfo *best_calling_code = nullptr; size_t best_length = 0; bool is_prefix = false; // is phone number a prefix of a valid country_code + prefix bool is_anonymous = is_fragment_phone_number(phone_number.str()); for (auto &country : list->countries) { for (auto &calling_code : country.calling_codes) { if (begins_with(phone_number, calling_code.calling_code)) { auto calling_code_size = calling_code.calling_code.size(); for (auto &prefix : calling_code.prefixes) { if (begins_with(prefix, phone_number.substr(calling_code_size))) { is_prefix = true; } if (calling_code_size + prefix.size() > best_length && begins_with(phone_number.substr(calling_code_size), prefix)) { best_country = &country; best_calling_code = &calling_code; best_length = calling_code_size + prefix.size(); } } } if (begins_with(calling_code.calling_code, phone_number)) { is_prefix = true; } } } if (best_country == nullptr) { return td_api::make_object(nullptr, is_prefix ? phone_number.str() : string(), is_prefix ? string() : phone_number.str(), is_anonymous); } Slice formatted_part = phone_number.substr(best_calling_code->calling_code.size()); string formatted_phone_number; size_t max_matched_digits = 0; for (auto &pattern : best_calling_code->patterns) { string result; size_t current_pattern_pos = 0; bool is_failed_match = false; size_t matched_digits = 0; for (auto &c : formatted_part) { while (current_pattern_pos < pattern.size() && pattern[current_pattern_pos] != 'X' && !is_digit(pattern[current_pattern_pos])) { result += pattern[current_pattern_pos++]; } if (current_pattern_pos == pattern.size()) { // result += ' '; } if (current_pattern_pos >= pattern.size() || pattern[current_pattern_pos] == 'X') { result += c; current_pattern_pos++; } else { CHECK(is_digit(pattern[current_pattern_pos])); if (c == pattern[current_pattern_pos]) { matched_digits++; result += c; current_pattern_pos++; } else { is_failed_match = true; break; } } } for (size_t i = current_pattern_pos; i < pattern.size(); i++) { if (is_digit(pattern[i])) { is_failed_match = true; } } if (!is_failed_match && matched_digits >= max_matched_digits) { max_matched_digits = matched_digits; while (current_pattern_pos < pattern.size()) { if (pattern[current_pattern_pos] == 'X') { result.push_back('-'); } else { CHECK(!is_digit(pattern[current_pattern_pos])); result.push_back(' '); } current_pattern_pos++; } formatted_phone_number = std::move(result); } } return td_api::make_object( best_country->get_country_info_object(), best_calling_code->calling_code, formatted_phone_number.empty() ? formatted_part.str() : formatted_phone_number, is_anonymous); } void CountryInfoManager::get_current_country_code(Promise &&promise) { td_->create_handler(std::move(promise))->send(); } void CountryInfoManager::load_country_list(string language_code, int32 hash, Promise &&promise) { auto &queries = pending_load_country_queries_[language_code]; if (!promise && !queries.empty()) { return; } queries.push_back(std::move(promise)); if (queries.size() == 1) { auto query_promise = PromiseCreator::lambda( [actor_id = actor_id(this), language_code](Result> &&result) { send_closure(actor_id, &CountryInfoManager::on_get_country_list, language_code, std::move(result)); }); td_->create_handler(std::move(query_promise))->send(language_code, hash); } } void CountryInfoManager::on_get_country_list(const string &language_code, Result> r_country_list) { auto query_it = pending_load_country_queries_.find(language_code); CHECK(query_it != pending_load_country_queries_.end()); auto promises = std::move(query_it->second); CHECK(!promises.empty()); pending_load_country_queries_.erase(query_it); if (r_country_list.is_error()) { { std::lock_guard country_lock(country_mutex_); auto it = countries_.find(language_code); if (it != countries_.end()) { // don't try to reload countries more often than once in 1-2 minutes it->second->next_reload_time = max(Time::now() + Random::fast(60, 120), it->second->next_reload_time); // if we have data for the language, then we don't need to fail promises set_promises(promises); return; } } fail_promises(promises, r_country_list.move_as_error()); return; } { std::lock_guard country_lock(country_mutex_); on_get_country_list_impl(language_code, r_country_list.move_as_ok()); } set_promises(promises); } void CountryInfoManager::on_get_country_list_impl(const string &language_code, tl_object_ptr country_list) { CHECK(country_list != nullptr); LOG(DEBUG) << "Receive " << to_string(country_list); auto &countries = countries_[language_code]; switch (country_list->get_id()) { case telegram_api::help_countriesListNotModified::ID: if (countries == nullptr) { LOG(ERROR) << "Receive countriesListNotModified for unknown list with language code " << language_code; countries_.erase(language_code); } else { LOG(INFO) << "List of countries with language code " << language_code << " is not modified"; countries->next_reload_time = Time::now() + Random::fast(86400, 2 * 86400); } break; case telegram_api::help_countriesList::ID: { auto list = move_tl_object_as(country_list); if (countries == nullptr) { countries = make_unique(); } else { countries->countries.clear(); } for (auto &c : list->countries_) { CountryInfo info; info.country_code = std::move(c->iso2_); info.default_name = std::move(c->default_name_); info.name = std::move(c->name_); info.is_hidden = c->hidden_; for (auto &code : c->country_codes_) { auto r_calling_code = to_integer_safe(code->country_code_); if (r_calling_code.is_error() || r_calling_code.ok() <= 0) { LOG(ERROR) << "Receive invalid calling code " << code->country_code_ << " for country " << info.country_code; } else { CallingCodeInfo calling_code_info; calling_code_info.calling_code = std::move(code->country_code_); calling_code_info.prefixes = std::move(code->prefixes_); if (calling_code_info.prefixes.empty()) { calling_code_info.prefixes.resize(1); } calling_code_info.patterns = std::move(code->patterns_); info.calling_codes.push_back(std::move(calling_code_info)); } } if (info.calling_codes.empty()) { LOG(ERROR) << "Receive empty list of calling codes for " << info.country_code; continue; } countries->countries.push_back(std::move(info)); } countries->hash = list->hash_; countries->next_reload_time = Time::now() + Random::fast(86400, 2 * 86400); break; } default: UNREACHABLE(); } } const CountryInfoManager::CountryList *CountryInfoManager::get_country_list(CountryInfoManager *manager, const string &language_code) { auto it = countries_.find(language_code); if (it == countries_.end()) { if (language_code == "en") { static const BufferSlice en = gzdecode( base64url_decode( "eJyNW81y40hyrib13-qe6d3ZWR8cE4xwxHod4dkg8UfgyH9RJCg2AervViRryBqBgAYEpKVeYG8--AH2sgfHHnyzn6Dtky9-Ar-" "AD34FJ0hRAIlkoSO6pzVUZVVWVuaXX2YV_xz-959-8x___rf_Swj5u3_60xf4h-QqdXJccSee79Pod-_gs__7z3w1B__m5bK2-" "ezk9raw-rMl2yDfDV0esEmh4tNRoTHnPg3YAn6XmssolzafvV9NtPp7G8_VJO8rP01n1OWLgLq78jlDJgn5twli-" "Rb5VHEDPg1p4XeFKvVH4YSm9TgoSZqe0O9ko8jWvtrkpOJOQ-" "44FNlLNIeSOUcX7OqMYDuIXVVVbAsTZP05Q2XLyuazs1g2se4VOQLdPdA8JSspitiGA3Ja8acMzOju2G71e1VJrGORj5U58_" "mYugWLzr30egclTc-2kw17DReBn95rTonP_Gwju7XXIegbyVKHI2ed00oJn1lJ79j5hhxW_HCEyOYloyzW-56cVV6YP6L8Z-oi_" "m4owjOuVsh3VW8BRwzuesH8Fzb1nlZmT88l62XhmVer5CTyeDrxsNgDf1W0rHOo1slZlbpTh07YYpbWQdeLYh0a5LjKnCkP56lzkKW3-" "c5vN0CygyXVJjmvhv4DmKDQpAsPs4Mkads2Xf-J52iBHUJnSn2-a0eyjjsjHnsB-" "tKZT3kKawCr5M1np7exsWLZNsiGfuhOeFpHdeesCts-W70kh1WIbBfdnyG2sRnZ2J8DsqXPWFFKmWfcI5-qoDbjhTr1w8WCOnSO6KHF-" "8fnuQI9PIc_IfikGqV4D29un5D9TH6oei7lPvvHgsXdoNCACKYBDxcQCBYd0fS5qUbi3AbkqOrTF-" "6k411V3z77sIGL1NlZq3Onc7rA4kTKtKEN68_CAI35sio-" "vxvwTy9YPFM8ziUtI87vVjEWHR2SF8Q5pXoPejOHvzDEvsXS27hahRzVQL_J9jnk4d93pbXOv49klKKcV_" "VyvqwX87qk5iVZA98v5rWiAv4j5ctlPS_Bz4pcgvm1fLlo5I0ifK5LURxDLjLykm7kZUXOy5qaV0oajC3n1ZKRVxU9r5XkvKbA_" "EU1rxfLIBuN0WEM_IV11JKSV8swVi_ldZDRyzAPrKPJ8HNsx_NEnts6x1qdfKp5kCgLv6-z-R8KA_b4h3_" "AzkSRhXatNcl3NciYkIMKlZ_W6TCaCz1fWYxftRY5f9XpVR1MH0mszwV5bz3zAFKTQ91JOqcmcyIm3yYfa1_-LWCFyd-" "3nzwIUwyn1O04K6TjrNaJ9uI9FNqLSI_dvLTyO02X4vFdclibcYcheVzVxHm8ZpKTGgUy4nkubvc3f0DxvNZbrb3LeVZr61oSUxKrJ-" "WvYH3P8eajVN5Z61_ezX-IPw7IWc0DJCwMwIfSe4AYEu9hSA5qKyqDrJ_kUQgm165hbfrICtfMn2DnLetCTKwBrtVCn375F7qbt9-" "94vfms3dGUu4OsGb5CHCGcB4148zuwU9f2HgWRVs4cvg4PYciFRO-nua7deAsLcin1F2muK5ixGvVL8lJ_Wc-8sKAo_" "6lysK4rnfIcZ25c-o_pOMxzlnoPuvg23Vvzt3IKzBuV9bKJCNn1a_Ix80cr-gUjc9vzaMXjT3zJMZIXzFG2Tcm1uc-" "qo2mDOH8eakk78RKmis2auS4MQ6B7voI_" "xDjdQPOvLEIPLy2kpJ6r7Nvct0WOWxMl48BEmNSMRnjSZhIyA9gbZ8HPkP2HfOm0zj1J2Qtcmg9Uow35mRFXBs3bHLSCGbce0TxCfw3w" "dkQ3tEEvtvk7m4-Ies41eNxl-SgyX9GY0QrG0KfaHbIt03qPESLiHKGWizGMiY5M_nY91y24Ahv1IyY1zSvyIcm9T0WT46cwRvOHe_" "asAm8s-lTd5zGx5wsJ3JEYctpI_l3K3k76k947nLuAdPthfMR8zEddH2lQ_Tzob72ARQXWhVy2KIjD60jlG0O_qpMLFslH1_" "7Nh3uTifePL0nZdunNsV3PEcdsNNnEVFEapGynIUBrRX2ev4UiUPDUIX-3GrCWcLaAP6tkFOsVwE4oMTjL8BWs9S419wmxotWm5y2-" "AjoXUB9rDZXi3HcYufUBXmfsVX0oD5n7PW5lkmOWnTFKhAOVhTbtweyIXcZKivuA7X65KwF8MocL3xE-" "IBqvK29F6Nbn8mnxi8hDTxAeafwqgrmq-Ic3YK4i-yHxt0W5uK8qmWD_UENNqcOtr5alLZiZZcbtYBXgfwcwbSDklbOrLlbN-TDevM_" "VjlU3CFqA1U8x110lsvIgZGziPWHn7V1mMayFx1yegE1RaED_8HwRhXv_6JHTkB-AvQO7euqRUXo_xeQ82q-BwU-" "km91cb18YZPDC8oj0oWsa4jXHZLji9CdUn-Z9huNCH2uXSenbXfymlaQvmLcz0L7km3AtrbPsPoL8EKMN-0uOWovfMrS_" "Y28URbXfm2oY0BvVGejtI3nr1rHslfkvM4Z1J4t6o_38ARFE_pp-zM5aPv0F0zW0JTMWG0PVvJYDZcz9OxYb1tg9_E-" "uyti3cHX2gF1lhi_MrLXvjTJ8SWdU6DYaZzQy5n918srcnTp-RO0r6Qlzh2pfy_75PCSPlKMG-qlpO44L-" "00yGGHuUsUn1Vxrui0yFln6U-XLwu8J2ZowljpXKxq95E3wX1OV8UY0WmTkw73-YjitZmmi-" "vmDpxbzZt7vpfuqUmamLN2euTXFo16mB0eBFH_sseeONJX1LXMmqjTJ-97nh_MAKh36gOytkPMezsD8t6CYhQZuz5zKRnrb2cey9-" "Qo074DMiK-Zoqttcd1N10CTXzG5FO71eGqjZrv_fgN_SFPsxSfrPqNZbX8tF877T4_9cc-" "80RClu6dSvkoEvROxCwn5aQLyRcKSFfJcddNqJA01P2N7S4lujWwP6rc--" "G43Reg5pczbxj7LbJhy5n41nA3EXA0PsARRL34buQ2y2fF7rUfUAxX9n1g23c6EJu7vIRXofL4vvarhXZauEFMw-" "JG00Ys13gZF0ezMLodhTj1OUtTr3GjOS-h-SsG_6RAWqE_" "hTj5Mk8mcas7jU56tLgCePVcrkkXvuOHILNcKws6UKbmRVybALSjMeIzUri3G7WyJHpuXTsYTqLe2VmPVrXmXhPWM9DFp6V2YAa23PBS" "9nU3157FRdyoodrQi4woSCc0sUYiiXEL95se56MwaRPmxfkW5P6ixl1HGHv2Eis2yHfrLHTpGM2wXs7epIvbtJQYt0uOTCpg_" "f3JPF9oAk5xAR6Pqf-rp45Q02MAy5tRv19B891Rnknbna4uHlFDmGHaA2hqxk69snfrGzEfBfM5Edl8x77bmqbYhaGmZ-j8_" "YD7vJfQuxeydDisYNobOjzYBX0iI0lsf7W2g8XzPdpOm-BvtlvMkw7sh8U8mgdL76bMYfkdK0_" "D9HekSzuA5jXcPbUmfCnPe9jNHE9b0LOBnn6zLG4Et_9mbcgy_7Ix-" "n4zalSYo27lY50uUDvz3NaMTH2PjqPl6g7sXv2r7xRjIU9wMIenfMRlns08duJXo186LHnQo0662hH75bK8Xjgtz0-ZVj_" "RpLKwnPvNclHiJufPGdzlYWsVY6xqNeCffE9vW05WQPhXLzXJqc9KCJ8Og0xP1WLYn7W6wKPZFGYr0I7XcvE9QDWq1zPAbUI7PmZLpG-" "YHyXdfbGwRIY1YNapMceqYPiWzl7_wOQhzgLsTMtxz293pAc9Dh4Hnr2cjzuPrLHc-GeUfQ-" "VIs5EmrPK5McXM3RmhRiVhfK9ivkqA84O0fv88Q5u98gB30WmQG5z9uu5VP8pt8k3772R_" "ues3xtY2B2it9V9Fvk2z59DGkhstamXYedgRrLXJD3_Rl3-OMjDE_7mhb3xffWzf0OOenTh9WjP2yvRurtEDJHF-" "zspWJz7a96hq1M8ts1l-9zSC0MijgzQjQHqQHUYnzX0YcarB8yP_CiC1tvU6skagB9371cPMaQM-vCvkVO-4BzC8iyyN04xNRu_3QbK_" "s22BfyfjiNQhLLeyWxfW7IIay_yzle_SeuSft30TmuUGuJraNm9PQ_V8jhZ4r32I2MN4-DBjkZfPlr6PI99_-" "auB4YXJHjgTfH3mrmlKLYfwYWObKYj_" "boZV18rzYYkl8NwgVEp1tosgkDYhPtYHeeZO27NwYGwA8GzxACGLdSd31kew9WhZxbNJzw1TPePdxU2-" "FHO35mVck3VvQGwntrDCDYEedkq0bOLLYcz5gDzo31GvXYdnGSiuXr5NAKJ1jPK3pbJNa1AWf2DAZP2zqnbPWGEdkW1NzcndJHz8feqm" "gZ9_nWRWTrCG8umMPe3oHlknaSlHLifujt7I52_cdqkxPL8Z6Yi5-ZrIvrcKuzlgf4ReUVSYwNVhf2EqEmLXSZ5-" "7m4lfOI4l1MMEP6LokcbH6shxjrtUjx1ZUiFIH4XHifoUFMW5BjDsYL1MzdByAnYD6QyZH96hmvBW2rE2_buWxSO2fobtNvre-_" "MUr2N78y18hQ_X9L__qjjl2NyfJyZ7lpn2R0OWavG84BYs6T_j7iaK4jrRuozMH9zUpVH4Mw1vIfVKJZOQ16w7id-mj_R9Dk8V-" "d09OGotnGhWeqM9l8DK7Rr63Q_8h6tjWKGTvhaCXqWW_Z7Hr5KA2oxPc_3dqs536wm6RA9ub7nnvrAv90r4gJ_aM8vXVMoJFGe_" "m7EtyZtOf-Sv7Sp-DIe5J2R1ybHsPDNhBGuuNmBvYUJPYfO75P3aBxzAkLyTGmuQ8Opvo2w97OCHolfTRdC_" "HBpywgQ0s0PdFYky0r8ih7QG-o9w37mfYA3IU6cmQGsn4intp2yafbIAUPqETcEPbG9HICdL-p2d_X8W-" "jnR5ok6IcbQYP23gCDblz9hZ67omjDkbYs6m7gvdk2ukjO-0DKHeHz5Ez-1ZSlaOv1tw_ia70xcctsjRcLqP34i5ydAi-" "aFVSfGq0lfwquEd6O2HEa1FcH-bo-z264b35Gz4MmL740vP3Pd1jfyw5gvX3B0z-Pd3BajuC-" "u3N1HZheFv9vdurhvk9Bpy6QuUOggG59SvuG-9bpHvq1FHbDED7fwpF90JSV-" "hU5t8Glq7U6H3S8XMuQADrjkLIGfvxkROT3yP6XoI46gb0gDBsAT3uGmSb26o4_AoaTTDIHTTd3SaHt8R3UTv9VZfi8JiclVDR5_" "lblvkh5YD0e8UTG_EHQapOQBOzAP4aQloOU_L64l1bjvkqOMtvCfsXkDfm8vXa1-T79tuwHx3VXiACj0WPHuQGNOcVNelxM9xf-" "WuQQ7v2JxhPNzQyvswZbX-HdTtdz-m6vbV7xQpvv9T1jXQ25r3Ub2y4lPrF_" "cIHuWkMhFiwr1Jju73va2Ke7G47A05uefzER0945xXQ994_M8_z__r_wF2XX6H") .ok()); TlBufferParser parser(&en); auto result = telegram_api::help_getCountriesList::fetch_result(parser); parser.fetch_end(); CHECK(parser.get_error() == nullptr); on_get_country_list_impl(language_code, std::move(result)); it = countries_.find(language_code); CHECK(it != countries_.end()); auto *country = it->second.get(); if (manager != nullptr) { manager->load_country_list(language_code, country->hash, Auto()); } return country; } return nullptr; } auto *country = it->second.get(); CHECK(country != nullptr); if (manager != nullptr && country->next_reload_time < Time::now()) { manager->load_country_list(language_code, country->hash, Auto()); } return country; } int32 CountryInfoManager::manager_count_ = 0; std::mutex CountryInfoManager::country_mutex_; FlatHashMap> CountryInfoManager::countries_; string CountryInfoManager::fragment_prefixes_str_; vector CountryInfoManager::fragment_prefixes_; } // namespace td