From 79a66c06cccbd94e1d318afa91a5c40c2b9dd988 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 16 Jan 2024 16:26:58 +0300 Subject: [PATCH] Add td_api::getSavedMessagesTopics. --- td/generate/scheme/td_api.tl | 15 +++ td/telegram/MessagesManager.cpp | 185 ++++++++++++++++++++++++++++++++ td/telegram/MessagesManager.h | 6 ++ td/telegram/Td.cpp | 7 ++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 5 + 6 files changed, 220 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 702324769..8542bcdc8 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1869,6 +1869,16 @@ savedMessagesTopicAuthorHidden = SavedMessagesTopic; savedMessagesTopicSavedFromChat chat_id:int53 = SavedMessagesTopic; +//@description Contains information about a found saved messages topic @topic The topic @last_message Last message in the topic; may be null if none or unknown +foundSavedMessagesTopic topic:SavedMessagesTopic last_message:message = FoundSavedMessagesTopic; + +//@description Contains a list of Saved Messages topics +//@total_count Total number of Saved Messages topics found +//@topics List of Saved Messages topics +//@next_offset The offset for the next request. If empty, then there are no more results +foundSavedMessagesTopics total_count:int32 topics:vector next_offset:string = FoundSavedMessagesTopics; + + //@description Describes a forum topic icon @color Color of the topic icon in RGB format @custom_emoji_id Unique identifier of the custom emoji shown on the topic icon; 0 if none forumTopicIcon color:int32 custom_emoji_id:int64 = ForumTopicIcon; @@ -7145,6 +7155,11 @@ getSuitableDiscussionChats = Chats; //@description Returns a list of recently inactive supergroups and channels. Can be used when user reaches limit on the number of joined supergroups and channels and receives CHANNELS_TOO_MUCH error. Also, the limit can be increased with Telegram Premium getInactiveSupergroupChats = Chats; +//@description Returns list of non-pinned saved messages topic from the specified offset +//@offset Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results +//@limit The maximum number of saved messages topics to be returned; up to 100 +getSavedMessagesTopics offset:string limit:int32 = FoundSavedMessagesTopics; + //@description Returns a list of common group chats with a given user. Chats are sorted by their type and creation date //@user_id User identifier diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index be3016bef..5c33da074 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -188,6 +188,40 @@ class GetPinnedDialogsQuery final : public Td::ResultHandler { } }; +class GetSavedDialogsQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetSavedDialogsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(int32 offset_date, MessageId offset_message_id, DialogId offset_dialog_id, int32 limit) { + auto input_peer = DialogManager::get_input_peer_force(offset_dialog_id); + CHECK(input_peer != nullptr); + + int32 flags = telegram_api::messages_getSavedDialogs::EXCLUDE_PINNED_MASK; + send_query(G()->net_query_creator().create(telegram_api::messages_getSavedDialogs( + flags, false /*ignored*/, offset_date, offset_message_id.get_server_message_id().get(), std::move(input_peer), + limit, 0))); + } + + 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(); + LOG(INFO) << "Receive Saved Messages topics: " << to_string(result); + td_->messages_manager_->on_get_saved_messages_topics(std::move(result), std::move(promise_)); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + class GetDialogUnreadMarksQuery final : public Td::ResultHandler { public: void send() { @@ -15822,6 +15856,157 @@ void MessagesManager::reload_pinned_dialogs(DialogListId dialog_list_id, Promise } } +void MessagesManager::get_saved_messages_topics( + const string &offset, int32 limit, Promise> &&promise) { + int32 offset_date = std::numeric_limits::max(); + DialogId offset_dialog_id; + MessageId offset_message_id; + bool is_offset_valid = [&] { + if (offset.empty()) { + return true; + } + + auto parts = full_split(offset, ','); + if (parts.size() != 3) { + return false; + } + auto r_offset_date = to_integer_safe(parts[0]); + auto r_offset_dialog_id = to_integer_safe(parts[1]); + auto r_offset_message_id = to_integer_safe(parts[2]); + if (r_offset_date.is_error() || r_offset_date.ok() <= 0 || r_offset_message_id.is_error() || + r_offset_dialog_id.is_error()) { + return false; + } + offset_date = r_offset_date.ok(); + offset_message_id = MessageId(ServerMessageId(r_offset_message_id.ok())); + offset_dialog_id = DialogId(r_offset_dialog_id.ok()); + if (!offset_message_id.is_valid() || !offset_dialog_id.is_valid() || + DialogManager::get_input_peer_force(offset_dialog_id)->get_id() == telegram_api::inputPeerEmpty::ID) { + return false; + } + return true; + }(); + if (!is_offset_valid) { + return promise.set_error(Status::Error(400, "Invalid offset specified")); + } + + if (limit < 0) { + return promise.set_error(Status::Error(400, "Limit must be non-negative")); + } + + td_->create_handler(std::move(promise)) + ->send(offset_date, offset_message_id, offset_dialog_id, limit); +} + +void MessagesManager::on_get_saved_messages_topics( + telegram_api::object_ptr &&saved_dialogs_ptr, + Promise> &&promise) { + CHECK(saved_dialogs_ptr != nullptr); + int32 total_count = -1; + vector> dialogs; + vector> messages; + vector> chats; + vector> users; + switch (saved_dialogs_ptr->get_id()) { + case telegram_api::messages_savedDialogsNotModified::ID: + LOG(ERROR) << "Receive messages.savedDialogsNotModified"; + return promise.set_error(Status::Error(500, "Receive messages.savedDialogsNotModified")); + case telegram_api::messages_savedDialogs::ID: { + auto saved_dialogs = telegram_api::move_object_as(saved_dialogs_ptr); + total_count = static_cast(saved_dialogs->dialogs_.size()); + dialogs = std::move(saved_dialogs->dialogs_); + messages = std::move(saved_dialogs->messages_); + chats = std::move(saved_dialogs->chats_); + users = std::move(saved_dialogs->users_); + break; + } + case telegram_api::messages_savedDialogsSlice::ID: { + auto saved_dialogs = telegram_api::move_object_as(saved_dialogs_ptr); + total_count = saved_dialogs->count_; + if (total_count < static_cast(saved_dialogs->dialogs_.size())) { + LOG(ERROR) << "Receive total_count = " << total_count << ", but " << saved_dialogs->dialogs_.size() + << " Saved Messages topics"; + total_count = static_cast(saved_dialogs->dialogs_.size()); + } + dialogs = std::move(saved_dialogs->dialogs_); + messages = std::move(saved_dialogs->messages_); + chats = std::move(saved_dialogs->chats_); + users = std::move(saved_dialogs->users_); + break; + } + default: + UNREACHABLE(); + } + td_->contacts_manager_->on_get_users(std::move(users), "on_get_saved_messages_topics"); + td_->contacts_manager_->on_get_chats(std::move(chats), "on_get_saved_messages_topics"); + + FlatHashMap, MessageIdHash> message_id_to_message; + for (auto &message : messages) { + auto message_id = MessageId::get_message_id(message, false); + if (!message_id.is_valid()) { + continue; + } + message_id_to_message[message_id] = std::move(message); + } + + int32 last_message_date = 0; + MessageId last_message_id; + DialogId last_dialog_id; + vector added_saved_messages_topic_ids; + vector> found_saved_messages_topics; + for (auto &dialog : dialogs) { + SavedMessagesTopicId saved_messages_topic_id(DialogId(dialog->peer_)); + if (td::contains(added_saved_messages_topic_ids, saved_messages_topic_id)) { + LOG(ERROR) << "Receive " << saved_messages_topic_id + << " twice in result of getSavedMessagesTopics with total_count = " << total_count; + total_count--; + continue; + } + added_saved_messages_topic_ids.push_back(saved_messages_topic_id); + + MessageId last_topic_message_id(ServerMessageId(dialog->top_message_)); + if (!last_topic_message_id.is_valid()) { + // skip topics without messages + LOG(ERROR) << "Receive " << saved_messages_topic_id << " without last message"; + total_count--; + continue; + } + + auto it = message_id_to_message.find(last_topic_message_id); + if (it == message_id_to_message.end()) { + LOG(ERROR) << "Can't find last " << last_topic_message_id << " in " << saved_messages_topic_id; + total_count--; + continue; + } + auto message_date = get_message_date(it->second); + auto dialog_id = DialogId::get_message_dialog_id(it->second); + if (message_date > 0 && dialog_id.is_valid()) { + last_message_date = message_date; + last_message_id = last_topic_message_id; + last_dialog_id = dialog_id; + } + auto full_message_id = on_get_message(std::move(it->second), false, false, false, "on_get_saved_messages_topics"); + message_id_to_message.erase(it); + + if (full_message_id.get_dialog_id() != DialogId() && + full_message_id.get_dialog_id() != td_->dialog_manager_->get_my_dialog_id()) { + LOG(ERROR) << "Can't add last " << last_message_id << " to " << saved_messages_topic_id; + total_count--; + continue; + } + found_saved_messages_topics.push_back(td_api::make_object( + saved_messages_topic_id.get_saved_messages_topic_object(td_), + get_message_object(full_message_id, "on_get_saved_messages_topics"))); + } + string next_offset; + if (last_message_date > 0) { + next_offset = PSTRING() << last_message_date << ',' << last_dialog_id.get() << ',' + << last_message_id.get_server_message_id().get(); + } + promise.set_value(td_api::make_object( + total_count, std::move(found_saved_messages_topics), next_offset)); +} + vector MessagesManager::search_public_dialogs(const string &query, Promise &&promise) { LOG(INFO) << "Search public chats with query = \"" << query << '"'; diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 6e8aed0fc..d1bb8b79b 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -529,6 +529,12 @@ class MessagesManager final : public Actor { void read_all_dialogs_from_list(DialogListId dialog_list_id, Promise &&promise, bool is_recursive = false); + void get_saved_messages_topics(const string &offset, int32 limit, + Promise> &&promise); + + void on_get_saved_messages_topics(telegram_api::object_ptr &&saved_dialogs_ptr, + Promise> &&promise); + vector search_public_dialogs(const string &query, Promise &&promise); std::pair> search_dialogs(const string &query, int32 limit, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index eae3d0fd6..8fc0d6a2a 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5021,6 +5021,13 @@ void Td::on_request(uint64 id, const td_api::getChats &request) { messages_manager_->get_dialogs_from_list(DialogListId(request.chat_list_), request.limit_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getSavedMessagesTopics &request) { + CHECK_IS_USER(); + // don't need to check offset UTF-8 correctness + CREATE_REQUEST_PROMISE(); + messages_manager_->get_saved_messages_topics(request.offset_, request.limit_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::searchPublicChat &request) { CLEAN_INPUT_STRING(request.username_); CREATE_REQUEST(SearchPublicChatRequest, request.username_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 57161d9f5..6e4ce7a8d 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -671,6 +671,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::getChats &request); + void on_request(uint64 id, const td_api::getSavedMessagesTopics &request); + void on_request(uint64 id, td_api::searchPublicChat &request); void on_request(uint64 id, td_api::searchPublicChats &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 26bf8cec0..f2c653892 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2750,6 +2750,11 @@ class CliClient final : public Actor { send_request(td_api::make_object(nullptr, 1)); send_request(td_api::make_object(nullptr, 10)); send_request(td_api::make_object(nullptr, 5)); + } else if (op == "gsmt") { + string limit; + string offset; + get_args(args, limit, offset); + send_request(td_api::make_object(offset, as_limit(limit))); } else if (op == "gcc" || op == "GetCommonChats") { UserId user_id; ChatId offset_chat_id;