From 2c61318a1153e640e3aec33439a63f3f7db580d5 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 11 Mar 2024 01:42:08 +0300 Subject: [PATCH] Move some methods to PeopleNearbyManager. --- td/telegram/ContactsManager.cpp | 415 +------------------------- td/telegram/ContactsManager.h | 65 ----- td/telegram/OptionManager.cpp | 6 +- td/telegram/PeopleNearbyManager.cpp | 431 ++++++++++++++++++++++++++++ td/telegram/PeopleNearbyManager.h | 77 +++++ td/telegram/Td.cpp | 4 +- td/telegram/UpdatesManager.cpp | 3 +- 7 files changed, 519 insertions(+), 482 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 402d39b3f..d7c9d185b 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -47,6 +47,7 @@ #include "td/telegram/NotificationManager.h" #include "td/telegram/OptionManager.h" #include "td/telegram/PeerColor.h" +#include "td/telegram/PeopleNearbyManager.h" #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" #include "td/telegram/PhotoSize.h" @@ -443,40 +444,6 @@ class ResetContactsQuery final : public Td::ResultHandler { } }; -class SearchDialogsNearbyQuery final : public Td::ResultHandler { - Promise> promise_; - - public: - explicit SearchDialogsNearbyQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(const Location &location, bool from_background, int32 expire_date) { - int32 flags = 0; - if (from_background) { - flags |= telegram_api::contacts_getLocated::BACKGROUND_MASK; - } - if (expire_date != -1) { - flags |= telegram_api::contacts_getLocated::SELF_EXPIRES_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::contacts_getLocated(flags, false /*ignored*/, location.get_input_geo_point(), expire_date))); - } - - 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()); - } - - promise_.set_value(result_ptr.move_as_ok()); - } - - void on_error(Status status) final { - promise_.set_error(std::move(status)); - } -}; - class UploadProfilePhotoQuery final : public Td::ResultHandler { Promise promise_; UserId user_id_; @@ -2851,21 +2818,6 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent if (was_online_local_ >= unix_time && !td_->is_online()) { was_online_local_ = unix_time - 1; } - - location_visibility_expire_date_ = - to_integer(G()->td_db()->get_binlog_pmc()->get("location_visibility_expire_date")); - if (location_visibility_expire_date_ != 0 && location_visibility_expire_date_ <= G()->unix_time()) { - location_visibility_expire_date_ = 0; - G()->td_db()->get_binlog_pmc()->erase("location_visibility_expire_date"); - } - auto pending_location_visibility_expire_date_string = - G()->td_db()->get_binlog_pmc()->get("pending_location_visibility_expire_date"); - if (!pending_location_visibility_expire_date_string.empty()) { - pending_location_visibility_expire_date_ = to_integer(pending_location_visibility_expire_date_string); - } - update_is_location_visible(); - LOG(INFO) << "Loaded location_visibility_expire_date = " << location_visibility_expire_date_ - << " and pending_location_visibility_expire_date = " << pending_location_visibility_expire_date_; } user_online_timeout_.set_callback(on_user_online_timeout_callback); @@ -2880,9 +2832,6 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent channel_unban_timeout_.set_callback(on_channel_unban_timeout_callback); channel_unban_timeout_.set_callback_data(static_cast(this)); - user_nearby_timeout_.set_callback(on_user_nearby_timeout_callback); - user_nearby_timeout_.set_callback_data(static_cast(this)); - slow_mode_delay_timeout_.set_callback(on_slow_mode_delay_timeout_callback); slow_mode_delay_timeout_.set_callback_data(static_cast(this)); @@ -2928,12 +2877,6 @@ ContactsManager::~ContactsManager() { restricted_user_ids_, restricted_channel_ids_); } -void ContactsManager::start_up() { - if (!pending_location_visibility_expire_date_) { - try_send_set_location_visibility_query(); - } -} - void ContactsManager::tear_down() { parent_.reset(); @@ -3066,35 +3009,6 @@ void ContactsManager::on_channel_unban_timeout(ChannelId channel_id) { update_channel(c, channel_id); // always call, because in case of failure we need to reactivate timeout } -void ContactsManager::on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long) { - if (G()->close_flag()) { - return; - } - - auto contacts_manager = static_cast(contacts_manager_ptr); - send_closure_later(contacts_manager->actor_id(contacts_manager), &ContactsManager::on_user_nearby_timeout, - UserId(user_id_long)); -} - -void ContactsManager::on_user_nearby_timeout(UserId user_id) { - if (G()->close_flag()) { - return; - } - - auto u = get_user(user_id); - CHECK(u != nullptr); - - LOG(INFO) << "Remove " << user_id << " from nearby list"; - DialogId dialog_id(user_id); - for (size_t i = 0; i < users_nearby_.size(); i++) { - if (users_nearby_[i].dialog_id == dialog_id) { - users_nearby_.erase(users_nearby_.begin() + i); - send_update_users_nearby(); - return; - } - } -} - void ContactsManager::on_slow_mode_delay_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long) { if (G()->close_flag()) { return; @@ -6133,330 +6047,6 @@ void ContactsManager::share_phone_number(UserId user_id, Promise &&promise td_->create_handler(std::move(promise))->send(user_id, std::move(input_user)); } -void ContactsManager::search_dialogs_nearby(const Location &location, - Promise> &&promise) { - if (location.empty()) { - return promise.set_error(Status::Error(400, "Invalid location specified")); - } - last_user_location_ = location; - try_send_set_location_visibility_query(); - - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( - Result> result) mutable { - send_closure(actor_id, &ContactsManager::on_get_dialogs_nearby, std::move(result), std::move(promise)); - }); - td_->create_handler(std::move(query_promise))->send(location, false, -1); -} - -vector> ContactsManager::get_chats_nearby_object( - const vector &dialogs_nearby) const { - return transform(dialogs_nearby, [td = td_](const DialogNearby &dialog_nearby) { - return td_api::make_object( - td->dialog_manager_->get_chat_id_object(dialog_nearby.dialog_id, "chatNearby"), dialog_nearby.distance); - }); -} - -void ContactsManager::send_update_users_nearby() const { - send_closure(G()->td(), &Td::send_update, - td_api::make_object(get_chats_nearby_object(users_nearby_))); -} - -void ContactsManager::on_get_dialogs_nearby(Result> result, - Promise> &&promise) { - if (result.is_error()) { - return promise.set_error(result.move_as_error()); - } - - auto updates_ptr = result.move_as_ok(); - if (updates_ptr->get_id() != telegram_api::updates::ID) { - LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates"; - return promise.set_error(Status::Error(500, "Receive unsupported response from the server")); - } - - auto update = telegram_api::move_object_as(updates_ptr); - LOG(INFO) << "Receive chats nearby in " << to_string(update); - - on_get_users(std::move(update->users_), "on_get_dialogs_nearby"); - on_get_chats(std::move(update->chats_), "on_get_dialogs_nearby"); - - for (auto &dialog_nearby : users_nearby_) { - user_nearby_timeout_.cancel_timeout(dialog_nearby.dialog_id.get_user_id().get()); - } - auto old_users_nearby = std::move(users_nearby_); - users_nearby_.clear(); - channels_nearby_.clear(); - int32 location_visibility_expire_date = 0; - for (auto &update_ptr : update->updates_) { - if (update_ptr->get_id() != telegram_api::updatePeerLocated::ID) { - LOG(ERROR) << "Receive unexpected " << to_string(update); - continue; - } - - auto expire_date = on_update_peer_located( - std::move(static_cast(update_ptr.get())->peers_), false); - if (expire_date != -1) { - location_visibility_expire_date = expire_date; - } - } - if (location_visibility_expire_date != location_visibility_expire_date_) { - set_location_visibility_expire_date(location_visibility_expire_date); - update_is_location_visible(); - } - - std::sort(users_nearby_.begin(), users_nearby_.end()); - if (old_users_nearby != users_nearby_) { - send_update_users_nearby(); // for other clients connected to the same TDLib instance - } - promise.set_value(td_api::make_object(get_chats_nearby_object(users_nearby_), - get_chats_nearby_object(channels_nearby_))); -} - -void ContactsManager::set_location(const Location &location, Promise &&promise) { - if (location.empty()) { - return promise.set_error(Status::Error(400, "Invalid location specified")); - } - last_user_location_ = location; - try_send_set_location_visibility_query(); - - auto query_promise = PromiseCreator::lambda( - [promise = std::move(promise)](Result> result) mutable { - promise.set_value(Unit()); - }); - td_->create_handler(std::move(query_promise))->send(location, true, -1); -} - -void ContactsManager::set_location_visibility(Td *td) { - bool is_location_visible = td->option_manager_->get_option_boolean("is_location_visible"); - auto pending_location_visibility_expire_date = is_location_visible ? std::numeric_limits::max() : 0; - if (td->contacts_manager_ == nullptr) { - G()->td_db()->get_binlog_pmc()->set("pending_location_visibility_expire_date", - to_string(pending_location_visibility_expire_date)); - return; - } - if (td->contacts_manager_->pending_location_visibility_expire_date_ == -1 && - pending_location_visibility_expire_date == td->contacts_manager_->location_visibility_expire_date_) { - return; - } - if (td->contacts_manager_->pending_location_visibility_expire_date_ != pending_location_visibility_expire_date) { - td->contacts_manager_->pending_location_visibility_expire_date_ = pending_location_visibility_expire_date; - G()->td_db()->get_binlog_pmc()->set("pending_location_visibility_expire_date", - to_string(pending_location_visibility_expire_date)); - } - td->contacts_manager_->try_send_set_location_visibility_query(); -} - -void ContactsManager::try_send_set_location_visibility_query() { - if (G()->close_flag()) { - return; - } - if (pending_location_visibility_expire_date_ == -1) { - return; - } - - LOG(INFO) << "Trying to send set location visibility query"; - if (is_set_location_visibility_request_sent_) { - return; - } - if (pending_location_visibility_expire_date_ != 0 && last_user_location_.empty()) { - return; - } - - is_set_location_visibility_request_sent_ = true; - auto query_promise = - PromiseCreator::lambda([actor_id = actor_id(this), set_expire_date = pending_location_visibility_expire_date_]( - Result> result) { - send_closure(actor_id, &ContactsManager::on_set_location_visibility_expire_date, set_expire_date, - result.is_ok() ? 0 : result.error().code()); - }); - td_->create_handler(std::move(query_promise)) - ->send(last_user_location_, true, pending_location_visibility_expire_date_); -} - -void ContactsManager::on_set_location_visibility_expire_date(int32 set_expire_date, int32 error_code) { - bool success = error_code == 0; - is_set_location_visibility_request_sent_ = false; - - if (set_expire_date != pending_location_visibility_expire_date_) { - try_send_set_location_visibility_query(); - return; - } - - if (success) { - set_location_visibility_expire_date(pending_location_visibility_expire_date_); - } else { - if (G()->close_flag()) { - // request will be re-sent after restart - return; - } - if (error_code != 406) { - LOG(ERROR) << "Failed to set location visibility expire date to " << pending_location_visibility_expire_date_; - } - } - G()->td_db()->get_binlog_pmc()->erase("pending_location_visibility_expire_date"); - pending_location_visibility_expire_date_ = -1; - update_is_location_visible(); -} - -void ContactsManager::get_is_location_visible(Promise &&promise) { - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( - Result> result) mutable { - send_closure(actor_id, &ContactsManager::on_get_is_location_visible, std::move(result), std::move(promise)); - }); - td_->create_handler(std::move(query_promise))->send(Location(), true, -1); -} - -void ContactsManager::on_get_is_location_visible(Result> &&result, - Promise &&promise) { - TRY_STATUS_PROMISE(promise, G()->close_status()); - if (result.is_error()) { - if (result.error().message() == "GEO_POINT_INVALID" && pending_location_visibility_expire_date_ == -1 && - location_visibility_expire_date_ > 0) { - set_location_visibility_expire_date(0); - update_is_location_visible(); - } - return promise.set_value(Unit()); - } - - auto updates_ptr = result.move_as_ok(); - if (updates_ptr->get_id() != telegram_api::updates::ID) { - LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates"; - return promise.set_value(Unit()); - } - - auto updates = std::move(telegram_api::move_object_as(updates_ptr)->updates_); - if (updates.size() != 1 || updates[0]->get_id() != telegram_api::updatePeerLocated::ID) { - LOG(ERROR) << "Receive unexpected " << to_string(updates); - return promise.set_value(Unit()); - } - - auto peers = std::move(static_cast(updates[0].get())->peers_); - if (peers.size() != 1 || peers[0]->get_id() != telegram_api::peerSelfLocated::ID) { - LOG(ERROR) << "Receive unexpected " << to_string(peers); - return promise.set_value(Unit()); - } - - auto location_visibility_expire_date = static_cast(peers[0].get())->expires_; - if (location_visibility_expire_date != location_visibility_expire_date_) { - set_location_visibility_expire_date(location_visibility_expire_date); - update_is_location_visible(); - } - - promise.set_value(Unit()); -} - -int32 ContactsManager::on_update_peer_located(vector> &&peers, - bool from_update) { - auto now = G()->unix_time(); - bool need_update = false; - int32 location_visibility_expire_date = -1; - for (auto &peer_located_ptr : peers) { - if (peer_located_ptr->get_id() == telegram_api::peerSelfLocated::ID) { - auto peer_self_located = telegram_api::move_object_as(peer_located_ptr); - if (peer_self_located->expires_ == 0 || peer_self_located->expires_ > G()->unix_time()) { - location_visibility_expire_date = peer_self_located->expires_; - } - continue; - } - - CHECK(peer_located_ptr->get_id() == telegram_api::peerLocated::ID); - auto peer_located = telegram_api::move_object_as(peer_located_ptr); - DialogId dialog_id(peer_located->peer_); - int32 expires_at = peer_located->expires_; - int32 distance = peer_located->distance_; - if (distance < 0 || distance > 50000000) { - LOG(ERROR) << "Receive wrong distance to " << to_string(peer_located); - continue; - } - if (expires_at <= now) { - LOG(INFO) << "Skip expired result " << to_string(peer_located); - continue; - } - - auto dialog_type = dialog_id.get_type(); - if (dialog_type == DialogType::User) { - auto user_id = dialog_id.get_user_id(); - if (!have_user(user_id)) { - LOG(ERROR) << "Can't find " << user_id; - continue; - } - if (expires_at < now + 86400) { - user_nearby_timeout_.set_timeout_in(user_id.get(), expires_at - now + 1); - } - } else if (dialog_type == DialogType::Channel) { - auto channel_id = dialog_id.get_channel_id(); - if (!have_channel(channel_id)) { - LOG(ERROR) << "Can't find " << channel_id; - continue; - } - if (expires_at != std::numeric_limits::max()) { - LOG(ERROR) << "Receive expiring at " << expires_at << " group location in " << to_string(peer_located); - } - if (from_update) { - LOG(ERROR) << "Receive nearby " << channel_id << " from update"; - continue; - } - } else { - LOG(ERROR) << "Receive chat of wrong type in " << to_string(peer_located); - continue; - } - - td_->dialog_manager_->force_create_dialog(dialog_id, "on_update_peer_located"); - - if (from_update) { - CHECK(dialog_type == DialogType::User); - bool is_found = false; - for (auto &dialog_nearby : users_nearby_) { - if (dialog_nearby.dialog_id == dialog_id) { - if (dialog_nearby.distance != distance) { - dialog_nearby.distance = distance; - need_update = true; - } - is_found = true; - break; - } - } - if (!is_found) { - users_nearby_.emplace_back(dialog_id, distance); - all_users_nearby_.insert(dialog_id.get_user_id()); - need_update = true; - } - } else { - if (dialog_type == DialogType::User) { - users_nearby_.emplace_back(dialog_id, distance); - all_users_nearby_.insert(dialog_id.get_user_id()); - } else { - channels_nearby_.emplace_back(dialog_id, distance); - } - } - } - if (need_update) { - std::sort(users_nearby_.begin(), users_nearby_.end()); - send_update_users_nearby(); - } - return location_visibility_expire_date; -} - -void ContactsManager::set_location_visibility_expire_date(int32 expire_date) { - if (location_visibility_expire_date_ == expire_date) { - return; - } - - LOG(INFO) << "Set set_location_visibility_expire_date to " << expire_date; - location_visibility_expire_date_ = expire_date; - if (expire_date == 0) { - G()->td_db()->get_binlog_pmc()->erase("location_visibility_expire_date"); - } else { - G()->td_db()->get_binlog_pmc()->set("location_visibility_expire_date", to_string(expire_date)); - } - // the caller must call update_is_location_visible() itself -} - -void ContactsManager::update_is_location_visible() { - auto expire_date = pending_location_visibility_expire_date_ != -1 ? pending_location_visibility_expire_date_ - : location_visibility_expire_date_; - td_->option_manager_->set_option_boolean("is_location_visible", expire_date != 0); -} - void ContactsManager::on_update_bot_menu_button(UserId bot_user_id, tl_object_ptr &&bot_menu_button) { if (!bot_user_id.is_valid()) { @@ -15081,7 +14671,8 @@ bool ContactsManager::is_user_status_exact(UserId user_id) const { bool ContactsManager::can_report_user(UserId user_id) const { auto u = get_user(user_id); - return u != nullptr && !u->is_deleted && !u->is_support && (u->is_bot || all_users_nearby_.count(user_id) != 0); + return u != nullptr && !u->is_deleted && !u->is_support && + (u->is_bot || td_->people_nearby_manager_->is_user_nearby(user_id)); } const ContactsManager::User *ContactsManager::get_user(UserId user_id) const { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 53a214ee3..9b982c029 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -23,7 +23,6 @@ #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/FolderId.h" -#include "td/telegram/Location.h" #include "td/telegram/MessageFullId.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessageTtl.h" @@ -306,8 +305,6 @@ class ContactsManager final : public Actor { void on_update_channel_administrator_count(ChannelId channel_id, int32 administrator_count); void on_update_channel_bot_commands(ChannelId channel_id, BotCommands &&bot_commands); - int32 on_update_peer_located(vector> &&peers, bool from_update); - void on_update_bot_menu_button(UserId bot_user_id, tl_object_ptr &&bot_menu_button); void speculative_add_channel_participants(ChannelId channel_id, const vector &added_user_ids, @@ -423,14 +420,6 @@ class ContactsManager final : public Actor { void share_phone_number(UserId user_id, Promise &&promise); - void search_dialogs_nearby(const Location &location, Promise> &&promise); - - void set_location(const Location &location, Promise &&promise); - - static void set_location_visibility(Td *td); - - void get_is_location_visible(Promise &&promise); - void register_suggested_profile_photo(const Photo &photo); FileId get_profile_photo_file_id(int64 photo_id) const; @@ -1138,26 +1127,6 @@ class ContactsManager final : public Actor { vector pending_requests; }; - struct DialogNearby { - DialogId dialog_id; - int32 distance; - - DialogNearby(DialogId dialog_id, int32 distance) : dialog_id(dialog_id), distance(distance) { - } - - bool operator<(const DialogNearby &other) const { - return distance < other.distance || (distance == other.distance && dialog_id.get() < other.dialog_id.get()); - } - - bool operator==(const DialogNearby &other) const { - return distance == other.distance && dialog_id == other.dialog_id; - } - - bool operator!=(const DialogNearby &other) const { - return !(*this == other); - } - }; - struct RecommendedDialogs { int32 total_count_ = 0; vector dialog_ids_; @@ -1654,24 +1623,6 @@ class ContactsManager final : public Actor { void on_clear_imported_contacts(vector &&contacts, vector contacts_unique_id, std::pair, vector> &&to_add, Promise &&promise); - vector> get_chats_nearby_object( - const vector &dialogs_nearby) const; - - void send_update_users_nearby() const; - - void on_get_dialogs_nearby(Result> result, - Promise> &&promise); - - void try_send_set_location_visibility_query(); - - void on_set_location_visibility_expire_date(int32 set_expire_date, int32 error_code); - - void set_location_visibility_expire_date(int32 expire_date); - - void on_get_is_location_visible(Result> &&result, Promise &&promise); - - void update_is_location_visible(); - bool is_suitable_recommended_channel(DialogId dialog_id) const; bool is_suitable_recommended_channel(ChannelId channel_id) const; @@ -1802,8 +1753,6 @@ class ContactsManager final : public Actor { static void on_channel_unban_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long); - static void on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long); - static void on_slow_mode_delay_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long); void on_user_online_timeout(UserId user_id); @@ -1814,12 +1763,8 @@ class ContactsManager final : public Actor { void on_channel_unban_timeout(ChannelId channel_id); - void on_user_nearby_timeout(UserId user_id); - void on_slow_mode_delay_timeout(ChannelId channel_id); - void start_up() final; - void tear_down() final; Td *td_; @@ -1959,15 +1904,6 @@ class ContactsManager final : public Actor { bool are_imported_contacts_changing_ = false; bool need_clear_imported_contacts_ = false; - vector users_nearby_; - vector channels_nearby_; - FlatHashSet all_users_nearby_; - - int32 location_visibility_expire_date_ = 0; - int32 pending_location_visibility_expire_date_ = -1; - bool is_set_location_visibility_request_sent_ = false; - Location last_user_location_; - FlatHashMap user_full_contact_require_premium_; WaitFreeHashMap linked_channel_ids_; @@ -1986,7 +1922,6 @@ class ContactsManager final : public Actor { MultiTimeout user_emoji_status_timeout_{"UserEmojiStatusTimeout"}; MultiTimeout channel_emoji_status_timeout_{"ChannelEmojiStatusTimeout"}; MultiTimeout channel_unban_timeout_{"ChannelUnbanTimeout"}; - MultiTimeout user_nearby_timeout_{"UserNearbyTimeout"}; MultiTimeout slow_mode_delay_timeout_{"SlowModeDelayTimeout"}; }; diff --git a/td/telegram/OptionManager.cpp b/td/telegram/OptionManager.cpp index 87f96ed6d..9708b850c 100644 --- a/td/telegram/OptionManager.cpp +++ b/td/telegram/OptionManager.cpp @@ -21,6 +21,7 @@ #include "td/telegram/net/MtprotoHeader.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/NotificationManager.h" +#include "td/telegram/PeopleNearbyManager.h" #include "td/telegram/ReactionType.h" #include "td/telegram/StateManager.h" #include "td/telegram/StickersManager.h" @@ -636,7 +637,8 @@ void OptionManager::get_option(const string &name, Promisecontacts_manager_actor_, &ContactsManager::get_is_location_visible, wrap_promise()); + send_closure_later(td_->people_nearby_manager_actor_, &PeopleNearbyManager::get_is_location_visible, + wrap_promise()); } else { pending_get_options_.emplace_back(name, std::move(promise)); } @@ -842,7 +844,7 @@ void OptionManager::set_option(const string &name, td_api::object_ptr +#include + namespace td { +class SearchDialogsNearbyQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit SearchDialogsNearbyQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const Location &location, bool from_background, int32 expire_date) { + int32 flags = 0; + if (from_background) { + flags |= telegram_api::contacts_getLocated::BACKGROUND_MASK; + } + if (expire_date != -1) { + flags |= telegram_api::contacts_getLocated::SELF_EXPIRES_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::contacts_getLocated(flags, false /*ignored*/, location.get_input_geo_point(), expire_date))); + } + + 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()); + } + + promise_.set_value(result_ptr.move_as_ok()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + PeopleNearbyManager::PeopleNearbyManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { + if (!td_->auth_manager_->is_bot()) { + location_visibility_expire_date_ = + to_integer(G()->td_db()->get_binlog_pmc()->get("location_visibility_expire_date")); + if (location_visibility_expire_date_ != 0 && location_visibility_expire_date_ <= G()->unix_time()) { + location_visibility_expire_date_ = 0; + G()->td_db()->get_binlog_pmc()->erase("location_visibility_expire_date"); + } + auto pending_location_visibility_expire_date_string = + G()->td_db()->get_binlog_pmc()->get("pending_location_visibility_expire_date"); + if (!pending_location_visibility_expire_date_string.empty()) { + pending_location_visibility_expire_date_ = to_integer(pending_location_visibility_expire_date_string); + } + update_is_location_visible(); + LOG(INFO) << "Loaded location_visibility_expire_date = " << location_visibility_expire_date_ + << " and pending_location_visibility_expire_date = " << pending_location_visibility_expire_date_; + } + + user_nearby_timeout_.set_callback(on_user_nearby_timeout_callback); + user_nearby_timeout_.set_callback_data(static_cast(this)); } void PeopleNearbyManager::tear_down() { parent_.reset(); } +void PeopleNearbyManager::start_up() { + if (!pending_location_visibility_expire_date_) { + try_send_set_location_visibility_query(); + } +} + +void PeopleNearbyManager::on_user_nearby_timeout_callback(void *people_nearby_manager_ptr, int64 user_id_long) { + if (G()->close_flag()) { + return; + } + + auto people_nearby_manager = static_cast(people_nearby_manager_ptr); + send_closure_later(people_nearby_manager->actor_id(people_nearby_manager), + &PeopleNearbyManager::on_user_nearby_timeout, UserId(user_id_long)); +} + +void PeopleNearbyManager::on_user_nearby_timeout(UserId user_id) { + if (G()->close_flag()) { + return; + } + + LOG(INFO) << "Remove " << user_id << " from nearby list"; + DialogId dialog_id(user_id); + for (size_t i = 0; i < users_nearby_.size(); i++) { + if (users_nearby_[i].dialog_id == dialog_id) { + users_nearby_.erase(users_nearby_.begin() + i); + send_update_users_nearby(); + return; + } + } +} + +bool PeopleNearbyManager::is_user_nearby(UserId user_id) const { + return all_users_nearby_.count(user_id) != 0; +} + +void PeopleNearbyManager::search_dialogs_nearby(const Location &location, + Promise> &&promise) { + if (location.empty()) { + return promise.set_error(Status::Error(400, "Invalid location specified")); + } + last_user_location_ = location; + try_send_set_location_visibility_query(); + + auto query_promise = + PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( + Result> result) mutable { + send_closure(actor_id, &PeopleNearbyManager::on_get_dialogs_nearby, std::move(result), std::move(promise)); + }); + td_->create_handler(std::move(query_promise))->send(location, false, -1); +} + +vector> PeopleNearbyManager::get_chats_nearby_object( + const vector &dialogs_nearby) const { + return transform(dialogs_nearby, [td = td_](const DialogNearby &dialog_nearby) { + return td_api::make_object( + td->dialog_manager_->get_chat_id_object(dialog_nearby.dialog_id, "chatNearby"), dialog_nearby.distance); + }); +} + +void PeopleNearbyManager::send_update_users_nearby() const { + send_closure(G()->td(), &Td::send_update, + td_api::make_object(get_chats_nearby_object(users_nearby_))); +} + +void PeopleNearbyManager::on_get_dialogs_nearby(Result> result, + Promise> &&promise) { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + + auto updates_ptr = result.move_as_ok(); + if (updates_ptr->get_id() != telegram_api::updates::ID) { + LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates"; + return promise.set_error(Status::Error(500, "Receive unsupported response from the server")); + } + + auto update = telegram_api::move_object_as(updates_ptr); + LOG(INFO) << "Receive chats nearby in " << to_string(update); + + td_->contacts_manager_->on_get_users(std::move(update->users_), "on_get_dialogs_nearby"); + td_->contacts_manager_->on_get_chats(std::move(update->chats_), "on_get_dialogs_nearby"); + + for (auto &dialog_nearby : users_nearby_) { + user_nearby_timeout_.cancel_timeout(dialog_nearby.dialog_id.get_user_id().get()); + } + auto old_users_nearby = std::move(users_nearby_); + users_nearby_.clear(); + channels_nearby_.clear(); + int32 location_visibility_expire_date = 0; + for (auto &update_ptr : update->updates_) { + if (update_ptr->get_id() != telegram_api::updatePeerLocated::ID) { + LOG(ERROR) << "Receive unexpected " << to_string(update); + continue; + } + + auto expire_date = on_update_peer_located( + std::move(static_cast(update_ptr.get())->peers_), false); + if (expire_date != -1) { + location_visibility_expire_date = expire_date; + } + } + if (location_visibility_expire_date != location_visibility_expire_date_) { + set_location_visibility_expire_date(location_visibility_expire_date); + update_is_location_visible(); + } + + std::sort(users_nearby_.begin(), users_nearby_.end()); + if (old_users_nearby != users_nearby_) { + send_update_users_nearby(); // for other clients connected to the same TDLib instance + } + promise.set_value(td_api::make_object(get_chats_nearby_object(users_nearby_), + get_chats_nearby_object(channels_nearby_))); +} + +void PeopleNearbyManager::set_location(const Location &location, Promise &&promise) { + if (location.empty()) { + return promise.set_error(Status::Error(400, "Invalid location specified")); + } + last_user_location_ = location; + try_send_set_location_visibility_query(); + + auto query_promise = PromiseCreator::lambda( + [promise = std::move(promise)](Result> result) mutable { + promise.set_value(Unit()); + }); + td_->create_handler(std::move(query_promise))->send(location, true, -1); +} + +void PeopleNearbyManager::set_location_visibility(Td *td) { + bool is_location_visible = td->option_manager_->get_option_boolean("is_location_visible"); + auto pending_location_visibility_expire_date = is_location_visible ? std::numeric_limits::max() : 0; + if (td->people_nearby_manager_ == nullptr) { + G()->td_db()->get_binlog_pmc()->set("pending_location_visibility_expire_date", + to_string(pending_location_visibility_expire_date)); + return; + } + if (td->people_nearby_manager_->pending_location_visibility_expire_date_ == -1 && + pending_location_visibility_expire_date == td->people_nearby_manager_->location_visibility_expire_date_) { + return; + } + if (td->people_nearby_manager_->pending_location_visibility_expire_date_ != pending_location_visibility_expire_date) { + td->people_nearby_manager_->pending_location_visibility_expire_date_ = pending_location_visibility_expire_date; + G()->td_db()->get_binlog_pmc()->set("pending_location_visibility_expire_date", + to_string(pending_location_visibility_expire_date)); + } + td->people_nearby_manager_->try_send_set_location_visibility_query(); +} + +void PeopleNearbyManager::try_send_set_location_visibility_query() { + if (G()->close_flag()) { + return; + } + if (pending_location_visibility_expire_date_ == -1) { + return; + } + + LOG(INFO) << "Trying to send set location visibility query"; + if (is_set_location_visibility_request_sent_) { + return; + } + if (pending_location_visibility_expire_date_ != 0 && last_user_location_.empty()) { + return; + } + + is_set_location_visibility_request_sent_ = true; + auto query_promise = + PromiseCreator::lambda([actor_id = actor_id(this), set_expire_date = pending_location_visibility_expire_date_]( + Result> result) { + send_closure(actor_id, &PeopleNearbyManager::on_set_location_visibility_expire_date, set_expire_date, + result.is_ok() ? 0 : result.error().code()); + }); + td_->create_handler(std::move(query_promise)) + ->send(last_user_location_, true, pending_location_visibility_expire_date_); +} + +void PeopleNearbyManager::on_set_location_visibility_expire_date(int32 set_expire_date, int32 error_code) { + bool success = error_code == 0; + is_set_location_visibility_request_sent_ = false; + + if (set_expire_date != pending_location_visibility_expire_date_) { + return try_send_set_location_visibility_query(); + } + + if (success) { + set_location_visibility_expire_date(pending_location_visibility_expire_date_); + } else { + if (G()->close_flag()) { + // request will be re-sent after restart + return; + } + if (error_code != 406) { + LOG(ERROR) << "Failed to set location visibility expire date to " << pending_location_visibility_expire_date_; + } + } + G()->td_db()->get_binlog_pmc()->erase("pending_location_visibility_expire_date"); + pending_location_visibility_expire_date_ = -1; + update_is_location_visible(); +} + +void PeopleNearbyManager::get_is_location_visible(Promise &&promise) { + auto query_promise = + PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( + Result> result) mutable { + send_closure(actor_id, &PeopleNearbyManager::on_get_is_location_visible, std::move(result), std::move(promise)); + }); + td_->create_handler(std::move(query_promise))->send(Location(), true, -1); +} + +void PeopleNearbyManager::on_get_is_location_visible(Result> &&result, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + if (result.is_error()) { + if (result.error().message() == "GEO_POINT_INVALID" && pending_location_visibility_expire_date_ == -1 && + location_visibility_expire_date_ > 0) { + set_location_visibility_expire_date(0); + update_is_location_visible(); + } + return promise.set_value(Unit()); + } + + auto updates_ptr = result.move_as_ok(); + if (updates_ptr->get_id() != telegram_api::updates::ID) { + LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates"; + return promise.set_value(Unit()); + } + + auto updates = std::move(telegram_api::move_object_as(updates_ptr)->updates_); + if (updates.size() != 1 || updates[0]->get_id() != telegram_api::updatePeerLocated::ID) { + LOG(ERROR) << "Receive unexpected " << to_string(updates); + return promise.set_value(Unit()); + } + + auto peers = std::move(static_cast(updates[0].get())->peers_); + if (peers.size() != 1 || peers[0]->get_id() != telegram_api::peerSelfLocated::ID) { + LOG(ERROR) << "Receive unexpected " << to_string(peers); + return promise.set_value(Unit()); + } + + auto location_visibility_expire_date = static_cast(peers[0].get())->expires_; + if (location_visibility_expire_date != location_visibility_expire_date_) { + set_location_visibility_expire_date(location_visibility_expire_date); + update_is_location_visible(); + } + + promise.set_value(Unit()); +} + +int32 PeopleNearbyManager::on_update_peer_located(vector> &&peers, + bool from_update) { + auto now = G()->unix_time(); + bool need_update = false; + int32 location_visibility_expire_date = -1; + for (auto &peer_located_ptr : peers) { + if (peer_located_ptr->get_id() == telegram_api::peerSelfLocated::ID) { + auto peer_self_located = telegram_api::move_object_as(peer_located_ptr); + if (peer_self_located->expires_ == 0 || peer_self_located->expires_ > G()->unix_time()) { + location_visibility_expire_date = peer_self_located->expires_; + } + continue; + } + + CHECK(peer_located_ptr->get_id() == telegram_api::peerLocated::ID); + auto peer_located = telegram_api::move_object_as(peer_located_ptr); + DialogId dialog_id(peer_located->peer_); + int32 expires_at = peer_located->expires_; + int32 distance = peer_located->distance_; + if (distance < 0 || distance > 50000000) { + LOG(ERROR) << "Receive wrong distance to " << to_string(peer_located); + continue; + } + if (expires_at <= now) { + LOG(INFO) << "Skip expired result " << to_string(peer_located); + continue; + } + + auto dialog_type = dialog_id.get_type(); + if (dialog_type == DialogType::User) { + auto user_id = dialog_id.get_user_id(); + if (!td_->contacts_manager_->have_user(user_id)) { + LOG(ERROR) << "Can't find " << user_id; + continue; + } + if (expires_at < now + 86400) { + user_nearby_timeout_.set_timeout_in(user_id.get(), expires_at - now + 1); + } + } else if (dialog_type == DialogType::Channel) { + auto channel_id = dialog_id.get_channel_id(); + if (!td_->contacts_manager_->have_channel(channel_id)) { + LOG(ERROR) << "Can't find " << channel_id; + continue; + } + if (expires_at != std::numeric_limits::max()) { + LOG(ERROR) << "Receive expiring at " << expires_at << " group location in " << to_string(peer_located); + } + if (from_update) { + LOG(ERROR) << "Receive nearby " << channel_id << " from update"; + continue; + } + } else { + LOG(ERROR) << "Receive chat of wrong type in " << to_string(peer_located); + continue; + } + + td_->dialog_manager_->force_create_dialog(dialog_id, "on_update_peer_located"); + + if (from_update) { + CHECK(dialog_type == DialogType::User); + bool is_found = false; + for (auto &dialog_nearby : users_nearby_) { + if (dialog_nearby.dialog_id == dialog_id) { + if (dialog_nearby.distance != distance) { + dialog_nearby.distance = distance; + need_update = true; + } + is_found = true; + break; + } + } + if (!is_found) { + users_nearby_.emplace_back(dialog_id, distance); + all_users_nearby_.insert(dialog_id.get_user_id()); + need_update = true; + } + } else { + if (dialog_type == DialogType::User) { + users_nearby_.emplace_back(dialog_id, distance); + all_users_nearby_.insert(dialog_id.get_user_id()); + } else { + channels_nearby_.emplace_back(dialog_id, distance); + } + } + } + if (need_update) { + std::sort(users_nearby_.begin(), users_nearby_.end()); + send_update_users_nearby(); + } + return location_visibility_expire_date; +} + +void PeopleNearbyManager::set_location_visibility_expire_date(int32 expire_date) { + if (location_visibility_expire_date_ == expire_date) { + return; + } + + LOG(INFO) << "Set set_location_visibility_expire_date to " << expire_date; + location_visibility_expire_date_ = expire_date; + if (expire_date == 0) { + G()->td_db()->get_binlog_pmc()->erase("location_visibility_expire_date"); + } else { + G()->td_db()->get_binlog_pmc()->set("location_visibility_expire_date", to_string(expire_date)); + } + // the caller must call update_is_location_visible() itself +} + +void PeopleNearbyManager::update_is_location_visible() { + auto expire_date = pending_location_visibility_expire_date_ != -1 ? pending_location_visibility_expire_date_ + : location_visibility_expire_date_; + td_->option_manager_->set_option_boolean("is_location_visible", expire_date != 0); +} + } // namespace td diff --git a/td/telegram/PeopleNearbyManager.h b/td/telegram/PeopleNearbyManager.h index 49a245104..acf9f34f4 100644 --- a/td/telegram/PeopleNearbyManager.h +++ b/td/telegram/PeopleNearbyManager.h @@ -6,9 +6,19 @@ // #pragma once +#include "td/telegram/DialogId.h" +#include "td/telegram/Location.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" + #include "td/actor/actor.h" +#include "td/actor/MultiTimeout.h" #include "td/utils/common.h" +#include "td/utils/FlatHashSet.h" +#include "td/utils/Promise.h" +#include "td/utils/Status.h" namespace td { @@ -18,11 +28,78 @@ class PeopleNearbyManager final : public Actor { public: PeopleNearbyManager(Td *td, ActorShared<> parent); + void search_dialogs_nearby(const Location &location, Promise> &&promise); + + void set_location(const Location &location, Promise &&promise); + + static void set_location_visibility(Td *td); + + void get_is_location_visible(Promise &&promise); + + int32 on_update_peer_located(vector> &&peers, bool from_update); + + bool is_user_nearby(UserId user_id) const; + private: + struct DialogNearby { + DialogId dialog_id; + int32 distance; + + DialogNearby(DialogId dialog_id, int32 distance) : dialog_id(dialog_id), distance(distance) { + } + + bool operator<(const DialogNearby &other) const { + return distance < other.distance || (distance == other.distance && dialog_id.get() < other.dialog_id.get()); + } + + bool operator==(const DialogNearby &other) const { + return distance == other.distance && dialog_id == other.dialog_id; + } + + bool operator!=(const DialogNearby &other) const { + return !(*this == other); + } + }; + + void start_up() final; + void tear_down() final; + static void on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long); + + void on_user_nearby_timeout(UserId user_id); + + vector> get_chats_nearby_object( + const vector &dialogs_nearby) const; + + void send_update_users_nearby() const; + + void on_get_dialogs_nearby(Result> result, + Promise> &&promise); + + void try_send_set_location_visibility_query(); + + void on_set_location_visibility_expire_date(int32 set_expire_date, int32 error_code); + + void set_location_visibility_expire_date(int32 expire_date); + + void on_get_is_location_visible(Result> &&result, + Promise &&promise); + + void update_is_location_visible(); + Td *td_; ActorShared<> parent_; + + vector users_nearby_; + vector channels_nearby_; + FlatHashSet all_users_nearby_; + MultiTimeout user_nearby_timeout_{"UserNearbyTimeout"}; + + int32 location_visibility_expire_date_ = 0; + int32 pending_location_visibility_expire_date_ = -1; + bool is_set_location_visibility_request_sent_ = false; + Location last_user_location_; }; } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 56f3a796d..a2d86265e 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5164,7 +5164,7 @@ void Td::on_request(uint64 id, td_api::searchChatsOnServer &request) { void Td::on_request(uint64 id, const td_api::searchChatsNearby &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - contacts_manager_->search_dialogs_nearby(Location(request.location_), std::move(promise)); + people_nearby_manager_->search_dialogs_nearby(Location(request.location_), std::move(promise)); } void Td::on_request(uint64 id, const td_api::getGroupsInCommon &request) { @@ -7816,7 +7816,7 @@ void Td::on_request(uint64 id, const td_api::getBotInfoShortDescription &request void Td::on_request(uint64 id, const td_api::setLocation &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->set_location(Location(request.location_), std::move(promise)); + people_nearby_manager_->set_location(Location(request.location_), std::move(promise)); } void Td::on_request(uint64 id, td_api::setBusinessLocation &request) { diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 72f4ab380..42911a7a1 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -48,6 +48,7 @@ #include "td/telegram/NotificationSettingsScope.h" #include "td/telegram/OptionManager.h" #include "td/telegram/OrderInfo.h" +#include "td/telegram/PeopleNearbyManager.h" #include "td/telegram/PollId.h" #include "td/telegram/PollManager.h" #include "td/telegram/PrivacyManager.h" @@ -3693,7 +3694,7 @@ void UpdatesManager::on_update(tl_object_ptr } void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - td_->contacts_manager_->on_update_peer_located(std::move(update->peers_), true); + td_->people_nearby_manager_->on_update_peer_located(std::move(update->peers_), true); promise.set_value(Unit()); }