From a3b71ca82c0dc5e162310eb78e9bf6245dab72ed Mon Sep 17 00:00:00 2001 From: levlam Date: Mon, 1 Nov 2021 16:30:03 +0300 Subject: [PATCH] Support getChatSparseMessagePositions in secret chats when message database enabled. --- td/generate/scheme/td_api.tl | 9 +++--- td/telegram/MessagesDb.cpp | 55 +++++++++++++++++++++++++++++++-- td/telegram/MessagesDb.h | 24 ++++++++++++++ td/telegram/MessagesManager.cpp | 35 +++++++++++++++++++-- 4 files changed, 113 insertions(+), 10 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 23d7a3871..5fa99fc85 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -813,7 +813,7 @@ messages total_count:int32 messages:vector = Messages; //@description Contains a list of messages found by a search @total_count Approximate total count of messages found; -1 if unknown @messages List of messages @next_offset The offset for the next request. If empty, there are no more results foundMessages total_count:int32 messages:vector next_offset:string = FoundMessages; -//@description Contains information about a message in a specific position @position 1-based message position in the full list of suitable messages @message_id Message identifier @date Point in time (Unix timestamp) when the message was sent +//@description Contains information about a message in a specific position @position 0-based message position in the full list of suitable messages @message_id Message identifier @date Point in time (Unix timestamp) when the message was sent messagePosition position:int32 message_id:int53 date:int32 = MessagePosition; //@description Contains a list of message positions @total_count Total count of messages found @positions List of message positions @@ -4298,11 +4298,12 @@ getActiveLiveLocationMessages = Messages; //@description Returns the last message sent in a chat no later than the specified date @chat_id Chat identifier @date Point in time (Unix timestamp) relative to which to search for messages getChatMessageByDate chat_id:int53 date:int32 = Message; -//@description Returns sparse positions of messages of the specified type in the chat to be used for shared media scroll implementation. Returns the results in reverse chronological order (i.e., in order of decreasing message_id) +//@description Returns sparse positions of messages of the specified type in the chat to be used for shared media scroll implementation. Returns the results in reverse chronological order (i.e., in order of decreasing message_id). +//-Cannot be used in secret chats or with searchMessagesFilterFailedToSend filter without an enabled message database //@chat_id Identifier of the chat in which to return information about message positions -//@filter Filter for message content. Filters searchMessagesFilterEmpty, searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention, searchMessagesFilterUnreadMention and searchMessagesFilterFailedToSend are unsupported in this function +//@filter Filter for message content. Filters searchMessagesFilterEmpty, searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention and searchMessagesFilterUnreadMention are unsupported in this function //@from_message_id The message identifier from which to return information about message positions -//@limit The expected number of message positions to be returned. A smaller number of positions can be returned, if there are not enough appropriate messages +//@limit The expected number of message positions to be returned; 50-2000. A smaller number of positions can be returned, if there are not enough appropriate messages getChatSparseMessagePositions chat_id:int53 filter:SearchMessagesFilter from_message_id:int53 limit:int32 = MessagePositions; //@description Returns information about the next messages of the specified type in the chat splitted by days. Returns the results in reverse chronological order. Can return partial result for the last returned day diff --git a/td/telegram/MessagesDb.cpp b/td/telegram/MessagesDb.cpp index d42b31e22..841cac167 100644 --- a/td/telegram/MessagesDb.cpp +++ b/td/telegram/MessagesDb.cpp @@ -230,6 +230,12 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { "ORDER BY rowid DESC LIMIT ?3) ORDER BY search_id DESC")); for (int32 i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { + TRY_RESULT_ASSIGN( + get_message_ids_stmts_[i], + db_.get_statement( + PSLICE() << "SELECT message_id FROM messages WHERE dialog_id = ?1 AND message_id < ?2 AND (index_mask & " + << (1 << i) << ") != 0 ORDER BY message_id DESC LIMIT 1000000")); + TRY_RESULT_ASSIGN( get_messages_from_index_stmts_[i].desc_stmt_, db_.get_statement( @@ -469,16 +475,16 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { return Status::Error("Not found"); } MessageId received_message_id(stmt.view_int64(0)); - MessagesDbDialogMessage result{received_message_id, BufferSlice(stmt.view_blob(1))}; + Slice data = stmt.view_blob(1); if (is_scheduled_server) { CHECK(received_message_id.is_scheduled()); CHECK(received_message_id.is_scheduled_server()); CHECK(received_message_id.get_scheduled_server_message_id() == message_id.get_scheduled_server_message_id()); } else { LOG_CHECK(received_message_id == message_id) - << received_message_id << ' ' << message_id << ' ' << get_message_info(result, true).first; + << received_message_id << ' ' << message_id << ' ' << get_message_info(received_message_id, data, true).first; } - return std::move(result); + return MessagesDbDialogMessage{received_message_id, BufferSlice(data)}; } Result get_message_by_unique_message_id(ServerMessageId unique_message_id) final { @@ -643,6 +649,37 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { return MessagesDbCalendar{std::move(messages), std::move(total_counts)}; } + Result get_dialog_sparse_message_positions( + MessagesDbGetDialogSparseMessagePositionsQuery query) final { + auto &stmt = get_message_ids_stmts_[message_search_filter_index(query.filter)]; + SCOPE_EXIT { + stmt.reset(); + }; + stmt.bind_int64(1, query.dialog_id.get()).ensure(); + stmt.bind_int64(2, query.from_message_id.get()).ensure(); + + vector message_ids; + stmt.step().ensure(); + while (stmt.has_row()) { + message_ids.push_back(MessageId(stmt.view_int64(0))); + stmt.step().ensure(); + } + + int32 limit = min(query.limit, static_cast(message_ids.size())); + double delta = static_cast(message_ids.size()) / limit; + vector positions; + positions.reserve(limit); + for (int32 i = 0; i < limit; i++) { + auto position = static_cast((i + 0.5) * delta); + auto message_id = message_ids[position]; + TRY_RESULT(message, get_message({query.dialog_id, message_id})); + auto date = get_message_info(message).second; + positions.push_back(MessagesDbMessagePosition{position, date, message_id}); + } + + return MessagesDbMessagePositions{static_cast(message_ids.size()), std::move(positions)}; + } + Result> get_messages(MessagesDbMessagesQuery query) final { if (query.filter != MessageSearchFilter::Empty) { return get_messages_from_index(query.dialog_id, query.from_message_id, query.filter, query.offset, query.limit); @@ -836,6 +873,7 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { SqliteStatement get_scheduled_messages_stmt_; SqliteStatement get_messages_from_notification_id_stmt_; + std::array get_message_ids_stmts_; std::array get_messages_from_index_stmts_; std::array get_calls_stmts_; @@ -1020,6 +1058,11 @@ class MessagesDbAsync final : public MessagesDbAsyncInterface { send_closure_later(impl_, &Impl::get_dialog_message_calendar, std::move(query), std::move(promise)); } + void get_dialog_sparse_message_positions(MessagesDbGetDialogSparseMessagePositionsQuery query, + Promise promise) final { + send_closure_later(impl_, &Impl::get_dialog_sparse_message_positions, std::move(query), std::move(promise)); + } + void get_messages(MessagesDbMessagesQuery query, Promise> promise) final { send_closure_later(impl_, &Impl::get_messages, std::move(query), std::move(promise)); } @@ -1116,6 +1159,12 @@ class MessagesDbAsync final : public MessagesDbAsyncInterface { promise.set_result(sync_db_->get_dialog_message_calendar(std::move(query))); } + void get_dialog_sparse_message_positions(MessagesDbGetDialogSparseMessagePositionsQuery query, + Promise promise) { + add_read_query(); + promise.set_result(sync_db_->get_dialog_sparse_message_positions(std::move(query))); + } + void get_messages(MessagesDbMessagesQuery query, Promise> promise) { add_read_query(); promise.set_result(sync_db_->get_messages(std::move(query))); diff --git a/td/telegram/MessagesDb.h b/td/telegram/MessagesDb.h index 30139a35a..f2017db93 100644 --- a/td/telegram/MessagesDb.h +++ b/td/telegram/MessagesDb.h @@ -58,6 +58,24 @@ struct MessagesDbCalendar { vector total_counts; }; +struct MessagesDbGetDialogSparseMessagePositionsQuery { + DialogId dialog_id; + MessageSearchFilter filter{MessageSearchFilter::Empty}; + MessageId from_message_id; + int32 limit{0}; +}; + +struct MessagesDbMessagePosition { + int32 position{0}; + int32 date{0}; + MessageId message_id; +}; + +struct MessagesDbMessagePositions { + int32 total_count{0}; + vector positions; +}; + struct MessagesDbFtsQuery { string query; DialogId dialog_id; @@ -104,6 +122,9 @@ class MessagesDbSyncInterface { virtual Result get_dialog_message_calendar(MessagesDbDialogCalendarQuery query) = 0; + virtual Result get_dialog_sparse_message_positions( + MessagesDbGetDialogSparseMessagePositionsQuery query) = 0; + virtual Result> get_messages(MessagesDbMessagesQuery query) = 0; virtual Result> get_scheduled_messages(DialogId dialog_id, int32 limit) = 0; virtual Result> get_messages_from_notification_id(DialogId dialog_id, @@ -158,6 +179,9 @@ class MessagesDbAsyncInterface { virtual void get_dialog_message_calendar(MessagesDbDialogCalendarQuery query, Promise promise) = 0; + virtual void get_dialog_sparse_message_positions(MessagesDbGetDialogSparseMessagePositionsQuery query, + Promise promise) = 0; + virtual void get_messages(MessagesDbMessagesQuery query, Promise> promise) = 0; virtual void get_scheduled_messages(DialogId dialog_id, int32 limit, Promise> promise) = 0; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 78156f76b..db172bff5 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -22763,11 +22763,13 @@ void MessagesManager::get_dialog_sparse_message_positions( if (d == nullptr) { return promise.set_error(Status::Error(400, "Chat not found")); } + if (limit < 50 || limit > 2000) { // server-side limits + return promise.set_error(Status::Error(400, "Invalid limit specified")); + } if (filter == MessageSearchFilter::Empty || filter == MessageSearchFilter::Call || filter == MessageSearchFilter::MissedCall || filter == MessageSearchFilter::Mention || - filter == MessageSearchFilter::UnreadMention || filter == MessageSearchFilter::FailedToSend || - filter == MessageSearchFilter::Pinned) { + filter == MessageSearchFilter::UnreadMention || filter == MessageSearchFilter::Pinned) { return promise.set_error(Status::Error(400, "The filter is not supported")); } @@ -22784,6 +22786,34 @@ void MessagesManager::get_dialog_sparse_message_positions( from_message_id = from_message_id.get_next_server_message_id(); } + if (filter == MessageSearchFilter::FailedToSend || dialog_id.get_type() == DialogType::SecretChat) { + if (!G()->parameters().use_message_db) { + return promise.set_error(Status::Error(400, "Unsupported without message database")); + } + + LOG(INFO) << "Get sparse message positions from database"; + auto new_promise = + PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + + auto positions = result.move_as_ok(); + promise.set_value(td_api::make_object( + positions.total_count, transform(positions.positions, [](const MessagesDbMessagePosition &position) { + return td_api::make_object(position.position, position.message_id.get(), + position.date); + }))); + }); + MessagesDbGetDialogSparseMessagePositionsQuery db_query; + db_query.dialog_id = dialog_id; + db_query.filter = filter; + db_query.from_message_id = from_message_id; + db_query.limit = limit; + G()->td_db()->get_messages_db_async()->get_dialog_sparse_message_positions(db_query, std::move(new_promise)); + return; + } + switch (dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: @@ -22792,7 +22822,6 @@ void MessagesManager::get_dialog_sparse_message_positions( ->send(dialog_id, filter, from_message_id, limit); break; case DialogType::SecretChat: - return promise.set_error(Status::Error(400, "Secret chats aren't supported")); case DialogType::None: default: UNREACHABLE();