diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index bc6f4a017..e34ffbffb 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -7933,6 +7933,12 @@ searchCallMessages offset:string limit:int32 only_missed:Bool = FoundMessages; //@limit The maximum number of messages to be returned; up to 100 searchOutgoingDocumentMessages query:string limit:int32 = FoundMessages; +//@description Searches for public channel posts with the given hashtag. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit +//@hashtag Hashtag to search for +//@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 messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit +searchPublicHashtagMessages hashtag:string offset:string limit:int32 = FoundMessages; + //@description Deletes all call messages @revoke Pass true to delete the messages for all users deleteAllCallMessages revoke:Bool = Ok; diff --git a/td/telegram/MessageSearchOffset.cpp b/td/telegram/MessageSearchOffset.cpp index 820f0d62d..6186c8c6a 100644 --- a/td/telegram/MessageSearchOffset.cpp +++ b/td/telegram/MessageSearchOffset.cpp @@ -45,8 +45,7 @@ Result MessageSearchOffset::from_string(const string &offse 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_message_id.is_error() || - r_offset_dialog_id.is_error()) { + if (r_offset_date.is_error() || r_offset_message_id.is_error() || r_offset_dialog_id.is_error()) { return false; } result.date_ = r_offset_date.ok(); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index b596341d2..4d7a86df0 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -2126,6 +2126,60 @@ class SearchMessagesGlobalQuery final : public Td::ResultHandler { } }; +class SearchPostsQuery final : public Td::ResultHandler { + Promise> promise_; + string hashtag_; + MessageSearchOffset offset_; + int32 limit_; + + public: + explicit SearchPostsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &hashtag, MessageSearchOffset offset, int32 limit) { + hashtag_ = hashtag; + offset_ = offset; + limit_ = limit; + + auto input_peer = DialogManager::get_input_peer_force(offset.dialog_id_); + CHECK(input_peer != nullptr); + + send_query(G()->net_query_creator().create(telegram_api::channels_searchPosts( + hashtag, offset.date_, std::move(input_peer), offset.message_id_.get_server_message_id().get(), limit))); + } + + 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 info = get_messages_info(td_, DialogId(), result_ptr.move_as_ok(), "SearchPostsQuery"); + td_->messages_manager_->get_channel_differences_if_needed( + std::move(info), + PromiseCreator::lambda([actor_id = td_->messages_manager_actor_.get(), hashtag = std::move(hashtag_), + offset = offset_, limit = limit_, + promise = std::move(promise_)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + auto info = result.move_as_ok(); + send_closure(actor_id, &MessagesManager::on_get_hashtag_search_result, hashtag, offset, limit, + info.total_count, std::move(info.messages), info.next_rate, std::move(promise)); + } + }), + "SearchPostsQuery"); + } + + void on_error(Status status) final { + if (status.message() == "SEARCH_QUERY_EMPTY") { + return promise_.set_value(td_->messages_manager_->get_found_messages_object({}, "SearchPostsQuery")); + } + promise_.set_error(std::move(status)); + } +}; + class GetAllScheduledMessagesQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -9250,7 +9304,6 @@ void MessagesManager::on_get_messages_search_result(const string &query, int32 o FoundMessages found_messages; auto &result = found_messages.message_full_ids; - CHECK(result.empty()); MessageSearchOffset next_offset; for (auto &message : messages) { next_offset.update_from_message(message); @@ -9278,6 +9331,41 @@ void MessagesManager::on_get_messages_search_result(const string &query, int32 o promise.set_value(get_found_messages_object(found_messages, "on_get_messages_search_result")); } +void MessagesManager::on_get_hashtag_search_result(const string &hashtag, const MessageSearchOffset &old_offset, + int32 limit, int32 total_count, + vector> &&messages, + int32 next_rate, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + FoundMessages found_messages; + auto &result = found_messages.message_full_ids; + MessageSearchOffset next_offset; + for (auto &message : messages) { + next_offset.update_from_message(message); + + auto new_message_full_id = on_get_message(std::move(message), false, true, false, "search hashtag"); + if (new_message_full_id != MessageFullId()) { + result.push_back(new_message_full_id); + } else { + total_count--; + } + } + if (total_count < static_cast(result.size())) { + LOG(ERROR) << "Receive " << result.size() << " valid messages out of " << total_count << " in " << messages.size() + << " messages"; + total_count = static_cast(result.size()); + } + found_messages.total_count = total_count; + if (!result.empty()) { + if (next_rate > 0) { + next_offset.date_ = next_rate; + } + found_messages.next_offset = next_offset.to_string(); + } + promise.set_value(get_found_messages_object(found_messages, "on_get_hashtag_search_result")); +} + void MessagesManager::on_get_outgoing_document_messages(vector> &&messages, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); @@ -20424,6 +20512,27 @@ void MessagesManager::search_outgoing_document_messages(const string &query, int td_->create_handler(std::move(promise))->send(query, limit); } +void MessagesManager::search_hashtag_posts(string hashtag, string offset_str, int32 limit, + Promise> &&promise) { + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + if (limit > MAX_SEARCH_MESSAGES) { + limit = MAX_SEARCH_MESSAGES; + } + + TRY_RESULT_PROMISE(promise, offset, MessageSearchOffset::from_string(offset_str)); + + if (hashtag[0] == '#') { + hashtag = hashtag.substr(1); + } + if (hashtag.empty()) { + return promise.set_value(get_found_messages_object({}, "search_hashtag_posts")); + } + + td_->create_handler(std::move(promise))->send(hashtag, offset, limit); +} + void MessagesManager::search_dialog_recent_location_messages(DialogId dialog_id, int32 limit, Promise> &&promise) { LOG(INFO) << "Search recent location messages in " << dialog_id << " with limit " << limit; diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 38b60111c..bdeb99de4 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -115,6 +115,7 @@ struct InputMessageContent; class MessageContent; class MessageForwardInfo; struct MessageReactions; +struct MessageSearchOffset; class MissingInvitees; class Td; class Usernames; @@ -198,6 +199,10 @@ class MessagesManager final : public Actor { vector> &&messages, int32 next_rate, Promise> &&promise); + void on_get_hashtag_search_result(const string &hashtag, const MessageSearchOffset &old_offset, int32 limit, + int32 total_count, vector> &&messages, + int32 next_rate, Promise> &&promise); + void on_get_outgoing_document_messages(vector> &&messages, Promise> &&promise); @@ -745,6 +750,9 @@ class MessagesManager final : public Actor { void search_outgoing_document_messages(const string &query, int32 limit, Promise> &&promise); + void search_hashtag_posts(string hashtag, string offset_str, int32 limit, + Promise> &&promise); + void search_dialog_recent_location_messages(DialogId dialog_id, int32 limit, Promise> &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 9be0a03ca..e923db03e 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5353,6 +5353,15 @@ void Td::on_request(uint64 id, td_api::searchOutgoingDocumentMessages &request) messages_manager_->search_outgoing_document_messages(request.query_, request.limit_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::searchPublicHashtagMessages &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.hashtag_); + CLEAN_INPUT_STRING(request.offset_); + CREATE_REQUEST_PROMISE(); + messages_manager_->search_hashtag_posts(std::move(request.hashtag_), std::move(request.offset_), request.limit_, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::deleteAllCallMessages &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index f32909dd2..77354df80 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -793,6 +793,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::searchOutgoingDocumentMessages &request); + void on_request(uint64 id, td_api::searchPublicHashtagMessages &request); + void on_request(uint64 id, const td_api::deleteAllCallMessages &request); void on_request(uint64 id, const td_api::searchChatRecentLocationMessages &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index be05c7984..8654558f9 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3047,6 +3047,12 @@ class CliClient final : public Actor { SearchQuery query; get_args(args, query); send_request(td_api::make_object(query.query, query.limit)); + } else if (op == "sphm") { + string hashtag; + string limit; + string offset; + get_args(args, hashtag, limit, offset); + send_request(td_api::make_object(hashtag, offset, as_limit(limit))); } else if (op == "DeleteAllCallMessages") { bool revoke = as_bool(args); send_request(td_api::make_object(revoke));