From b6bb02baad2d05bc23b63ecd0682487e881bddd7 Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 20 Nov 2023 22:03:21 +0300 Subject: [PATCH] Add td_api::getSimilarChats. --- td/generate/scheme/td_api.tl | 3 + td/telegram/ContactsManager.cpp | 138 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 15 ++++ td/telegram/Td.cpp | 6 ++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 4 + 6 files changed, 168 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index f1e4fb817..6eaad9639 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -6832,6 +6832,9 @@ searchChatsOnServer query:string limit:int32 = Chats; //@location Current user location searchChatsNearby location:location = ChatsNearby; +//@description Returns a list of chats similar to the given chat @chat_id Identifier of the target chat; must be an identifier of a channel chat +getSimilarChats chat_id:int53 = Chats; + //@description Returns a list of frequently used chats @category Category of chats to be returned @limit The maximum number of chats to be returned; up to 30 getTopChats category:TopChatCategory limit:int32 = Chats; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index c439bcfab..2d3c974ab 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3277,6 +3277,54 @@ class MigrateChatQuery final : public Td::ResultHandler { } }; +class GetChannelRecommendationsQuery final : public Td::ResultHandler { + Promise>> promise_; + ChannelId channel_id_; + + public: + explicit GetChannelRecommendationsQuery(Promise>> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id) { + channel_id_ = channel_id; + + auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + send_query( + G()->net_query_creator().create(telegram_api::channels_getChannelRecommendations(std::move(input_channel)))); + } + + 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 chats_ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChannelRecommendationsQuery: " << to_string(chats_ptr); + switch (chats_ptr->get_id()) { + case telegram_api::messages_chats::ID: { + auto chats = move_tl_object_as(chats_ptr); + return promise_.set_value(std::move(chats->chats_)); + } + case telegram_api::messages_chatsSlice::ID: { + auto chats = move_tl_object_as(chats_ptr); + LOG(ERROR) << "Receive chatsSlice in result of GetChannelRecommendationsQuery"; + return promise_.set_value(std::move(chats->chats_)); + } + default: + UNREACHABLE(); + return promise_.set_error(Status::Error("Unreachable")); + } + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetChannelRecommendationsQuery"); + promise_.set_error(std::move(status)); + } +}; + class GetCreatedPublicChannelsQuery final : public Td::ResultHandler { Promise promise_; PublicDialogType type_; @@ -9539,6 +9587,96 @@ vector ContactsManager::get_dialog_ids(vector> &&promise) { + if (!td_->messages_manager_->have_dialog_force(dialog_id, "get_channel_recommendations")) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + if (dialog_id.get_type() != DialogType::Channel) { + return promise.set_value(td_api::make_object()); + } + auto channel_id = dialog_id.get_channel_id(); + if (!is_broadcast_channel(channel_id) || get_input_channel(channel_id) == nullptr) { + return promise.set_value(td_api::make_object()); + } + auto it = channel_recommended_dialog_ids_.find(channel_id); + if (it != channel_recommended_dialog_ids_.end()) { + bool is_valid = true; + for (auto recommended_dialog_id : it->second) { + if (!is_suitable_recommended_channel(recommended_dialog_id)) { + is_valid = false; + break; + } + } + if (is_valid) { + return promise.set_value(td_->messages_manager_->get_chats_object(-1, it->second, "get_channel_recommendations")); + } + + LOG(INFO) << "Drop cache for similar chats of " << dialog_id; + channel_recommended_dialog_ids_.erase(it); + } + reload_channel_recommendations(channel_id, std::move(promise)); +} + +void ContactsManager::reload_channel_recommendations(ChannelId channel_id, + 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); + } +} + +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); + get_channel_recommendations_queries_.erase(it); + + if (r_chats.is_error()) { + return fail_promises(promises, r_chats.move_as_error()); + } + + auto channel_ids = get_channel_ids(r_chats.move_as_ok(), "on_get_channel_recommendations"); + vector dialog_ids; + for (auto recommended_channel_id : channel_ids) { + td_->messages_manager_->force_create_dialog(DialogId(recommended_channel_id), "on_get_channel_recommendations"); + if (is_suitable_recommended_channel(recommended_channel_id)) { + dialog_ids.push_back(DialogId(recommended_channel_id)); + } + } + channel_recommended_dialog_ids_[channel_id] = dialog_ids; + + // save_channel_recommendations(channel_id); + + for (auto &promise : promises) { + promise.set_value(td_->messages_manager_->get_chats_object(-1, dialog_ids, "on_get_channel_recommendations")); + } +} + void ContactsManager::return_created_public_dialogs(Promise> &&promise, const vector &channel_ids) { if (!promise) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index ecaa7e855..10c68d41c 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -561,6 +561,8 @@ class ContactsManager final : public Actor { ChannelId migrate_chat_to_megagroup(ChatId chat_id, Promise &promise); + void get_channel_recommendations(DialogId dialog_id, Promise> &&promise); + void get_created_public_dialogs(PublicDialogType type, Promise> &&promise, bool from_binlog); @@ -1708,6 +1710,15 @@ class ContactsManager final : public Actor { void update_is_location_visible(); + bool is_suitable_recommended_channel(DialogId dialog_id) const; + + bool is_suitable_recommended_channel(ChannelId channel_id) const; + + void reload_channel_recommendations(ChannelId channel_id, Promise> &&promise); + + void on_get_channel_recommendations(ChannelId channel_id, + Result>> &&r_chats); + static bool is_channel_public(const Channel *c); static bool is_suitable_created_public_channel(PublicDialogType type, const Channel *c); @@ -1988,6 +1999,10 @@ class ContactsManager final : public Actor { FlatHashMap> invite_link_infos_; FlatHashMap dialog_access_by_invite_link_; + FlatHashMap, ChannelIdHash> channel_recommended_dialog_ids_; + FlatHashMap>>, ChannelIdHash> + get_channel_recommendations_queries_; + bool created_public_channels_inited_[2] = {false, false}; vector created_public_channels_[2]; vector>> get_created_public_channels_queries_[2]; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index df8ec79dc..8d4f42189 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5085,6 +5085,12 @@ void Td::on_request(uint64 id, const td_api::clearAutosaveSettingsExceptions &re autosave_manager_->clear_autosave_settings_exceptions(std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getSimilarChats &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_channel_recommendations(DialogId(request.chat_id_), std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getTopChats &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index c5f156654..00e78c58e 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -634,6 +634,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::clearAutosaveSettingsExceptions &request); + void on_request(uint64 id, const td_api::getSimilarChats &request); + void on_request(uint64 id, const td_api::getTopChats &request); void on_request(uint64 id, const td_api::removeTopChat &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 8d024f69b..0a0cf5944 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -5150,6 +5150,10 @@ class CliClient final : public Actor { ChatId chat_id; get_args(args, chat_id); send_request(td_api::make_object(chat_id)); + } else if (op == "gscs") { + ChatId chat_id; + get_args(args, chat_id); + send_request(td_api::make_object(chat_id)); } else if (op == "gcpc") { send_request(td_api::make_object()); } else if (op == "gcpcl") {