diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index d12667e42..23d7a3871 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4307,7 +4307,7 @@ getChatSparseMessagePositions chat_id:int53 filter:SearchMessagesFilter from_mes //@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 //@chat_id Identifier of the chat in which to return information about messages -//@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 messages; use 0 to get results from the last message getChatMessageCalendar chat_id:int53 filter:SearchMessagesFilter from_message_id:int53 = MessageCalendar; diff --git a/td/telegram/MessagesDb.cpp b/td/telegram/MessagesDb.cpp index 79069cfa7..d42b31e22 100644 --- a/td/telegram/MessagesDb.cpp +++ b/td/telegram/MessagesDb.cpp @@ -611,6 +611,38 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { return std::make_pair(std::move(messages), next_expires_till); } + Result get_dialog_message_calendar(MessagesDbDialogCalendarQuery query) final { + auto &stmt = get_messages_from_index_stmts_[message_search_filter_index(query.filter)].desc_stmt_; + SCOPE_EXIT { + stmt.reset(); + }; + int32 limit = 1000; + stmt.bind_int64(1, query.dialog_id.get()).ensure(); + stmt.bind_int64(2, query.from_message_id.get()).ensure(); + stmt.bind_int32(3, limit).ensure(); + + vector messages; + vector total_counts; + stmt.step().ensure(); + int32 current_day = std::numeric_limits::max(); + while (stmt.has_row()) { + auto data_slice = stmt.view_blob(0); + MessageId message_id(stmt.view_int64(1)); + auto info = get_message_info(message_id, data_slice, false); + auto day = (query.tz_offset + info.second) / 86400; + if (day >= current_day) { + CHECK(!total_counts.empty()); + total_counts.back()++; + } else { + current_day = day; + messages.push_back(MessagesDbDialogMessage{message_id, BufferSlice(data_slice)}); + total_counts.push_back(1); + } + stmt.step().ensure(); + } + return MessagesDbCalendar{std::move(messages), std::move(total_counts)}; + } + 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); @@ -892,7 +924,11 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { } static std::pair get_message_info(const MessagesDbDialogMessage &message, bool from_data = false) { - LogEventParser message_date_parser(message.data.as_slice()); + return get_message_info(message.message_id, message.data.as_slice(), from_data); + } + + static std::pair get_message_info(MessageId message_id, Slice data, bool from_data) { + LogEventParser message_date_parser(data); int32 flags; int32 flags2 = 0; int32 flags3 = 0; @@ -904,17 +940,17 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { } } bool has_sender = (flags & (1 << 10)) != 0; - MessageId message_id; - td::parse(message_id, message_date_parser); + MessageId data_message_id; + td::parse(data_message_id, message_date_parser); UserId sender_user_id; if (has_sender) { td::parse(sender_user_id, message_date_parser); } int32 date; td::parse(date, message_date_parser); - LOG(INFO) << "Loaded " << message.message_id << "(aka " << message_id << ") sent at " << date << " by " + LOG(INFO) << "Loaded " << message_id << "(aka " << data_message_id << ") sent at " << date << " by " << sender_user_id; - return {from_data ? message_id : message.message_id, date}; + return {from_data ? data_message_id : message_id, date}; } }; @@ -980,6 +1016,10 @@ class MessagesDbAsync final : public MessagesDbAsyncInterface { std::move(promise)); } + void get_dialog_message_calendar(MessagesDbDialogCalendarQuery query, Promise promise) final { + send_closure_later(impl_, &Impl::get_dialog_message_calendar, 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)); } @@ -1071,6 +1111,11 @@ class MessagesDbAsync final : public MessagesDbAsyncInterface { promise.set_result(sync_db_->get_dialog_message_by_date(dialog_id, first_message_id, last_message_id, date)); } + void get_dialog_message_calendar(MessagesDbDialogCalendarQuery query, Promise promise) { + add_read_query(); + promise.set_result(sync_db_->get_dialog_message_calendar(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 5e8216814..30139a35a 100644 --- a/td/telegram/MessagesDb.h +++ b/td/telegram/MessagesDb.h @@ -46,6 +46,18 @@ struct MessagesDbMessage { BufferSlice data; }; +struct MessagesDbDialogCalendarQuery { + DialogId dialog_id; + MessageSearchFilter filter{MessageSearchFilter::Empty}; + MessageId from_message_id; + int32 tz_offset{0}; +}; + +struct MessagesDbCalendar { + vector messages; + vector total_counts; +}; + struct MessagesDbFtsQuery { string query; DialogId dialog_id; @@ -90,6 +102,8 @@ class MessagesDbSyncInterface { virtual Result get_dialog_message_by_date(DialogId dialog_id, MessageId first_message_id, MessageId last_message_id, int32 date) = 0; + virtual Result get_dialog_message_calendar(MessagesDbDialogCalendarQuery 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, @@ -141,6 +155,9 @@ class MessagesDbAsyncInterface { virtual void get_dialog_message_by_date(DialogId dialog_id, MessageId first_message_id, MessageId last_message_id, int32 date, Promise promise) = 0; + virtual void get_dialog_message_calendar(MessagesDbDialogCalendarQuery 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 24a330dbe..78156f76b 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -67,6 +67,7 @@ #include "td/utils/format.h" #include "td/utils/misc.h" #include "td/utils/PathView.h" +#include "td/utils/port/Clocks.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" @@ -21529,7 +21530,21 @@ td_api::object_ptr MessagesManager::get_dialog_message_ LOG(INFO) << "Get message calendar in " << dialog_id << " from " << fixed_from_message_id << ", have up to " << first_db_message_id << ", message_count = " << message_count; if (first_db_message_id < fixed_from_message_id && message_count != -1) { - // TODO + LOG(INFO) << "Get message calendar from database in " << dialog_id << " from " << fixed_from_message_id; + auto new_promise = + PromiseCreator::lambda([random_id, dialog_id, fixed_from_message_id, first_db_message_id, filter, + promise = std::move(promise)](Result r_calendar) mutable { + send_closure(G()->messages_manager(), &MessagesManager::on_get_message_calendar_from_database, random_id, + dialog_id, fixed_from_message_id, first_db_message_id, filter, std::move(r_calendar), + std::move(promise)); + }); + MessagesDbDialogCalendarQuery db_query; + db_query.dialog_id = dialog_id; + db_query.filter = filter; + db_query.from_message_id = fixed_from_message_id; + db_query.tz_offset = Clocks::tz_offset(); + G()->td_db()->get_messages_db_async()->get_dialog_message_calendar(db_query, std::move(new_promise)); + return {}; } } if (filter == MessageSearchFilter::FailedToSend) { @@ -21557,6 +21572,60 @@ td_api::object_ptr MessagesManager::get_dialog_message_ return {}; } +void MessagesManager::on_get_message_calendar_from_database(int64 random_id, DialogId dialog_id, + MessageId from_message_id, MessageId first_db_message_id, + MessageSearchFilter filter, + Result r_calendar, + Promise promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + if (r_calendar.is_error()) { + LOG(ERROR) << "Failed to get message calendar from the database: " << r_calendar.error(); + if (first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat && + filter != MessageSearchFilter::FailedToSend) { + found_dialog_message_calendars_.erase(random_id); + } + return promise.set_value(Unit()); + } + CHECK(!from_message_id.is_scheduled()); + CHECK(!first_db_message_id.is_scheduled()); + + auto calendar = r_calendar.move_as_ok(); + + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + + auto it = found_dialog_message_calendars_.find(random_id); + CHECK(it != found_dialog_message_calendars_.end()); + CHECK(it->second == nullptr); + + vector> periods; + periods.reserve(calendar.messages.size()); + for (size_t i = 0; i < calendar.messages.size(); i++) { + auto m = on_get_message_from_database(d, calendar.messages[i], false, "on_get_message_calendar_from_database"); + if (m != nullptr && first_db_message_id <= m->message_id) { + CHECK(!m->message_id.is_scheduled()); + periods.emplace_back(m->message_id, calendar.total_counts[i]); + } + } + + if (periods.empty() && first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat) { + LOG(INFO) << "No messages found in database"; + found_dialog_message_calendars_.erase(it); + } else { + auto total_count = d->message_count_by_index[message_search_filter_index(filter)]; + vector> days; + for (auto &period : periods) { + const auto *m = get_message(d, period.first); + CHECK(m != nullptr); + days.push_back(td_api::make_object( + period.second, get_message_object(dialog_id, m, "on_get_message_calendar_from_database"))); + } + it->second = td_api::make_object(total_count, Clocks::tz_offset(), std::move(days)); + } + promise.set_value(Unit()); +} + std::pair> MessagesManager::search_dialog_messages( DialogId dialog_id, const string &query, const td_api::object_ptr &sender, MessageId from_message_id, int32 offset, int32 limit, MessageSearchFilter filter, MessageId top_thread_message_id, @@ -22233,7 +22302,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo MessageId from_message_id, MessageId first_db_message_id, MessageSearchFilter filter, int32 offset, int32 limit, Result> r_messages, - Promise<> promise) { + Promise promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); if (r_messages.is_error()) { @@ -22287,7 +22356,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo } it->second.first = message_count; if (res.empty() && first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat) { - LOG(INFO) << "No messages in database found"; + LOG(INFO) << "No messages found in database"; found_dialog_messages_.erase(it); } else { LOG(INFO) << "Found " << res.size() << " messages out of " << message_count << " in database"; @@ -22437,7 +22506,7 @@ void MessagesManager::on_messages_db_calls_result(Result it->second.first = calls_db_state_.message_count_by_index[call_message_search_filter_index(filter)]; if (res.empty() && first_db_message_id != MessageId::min()) { - LOG(INFO) << "No messages in database found"; + LOG(INFO) << "No messages found in database"; found_call_messages_.erase(it); } diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 1ff7a089a..d720b2644 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -2817,13 +2817,17 @@ class MessagesManager final : public Actor { static MessageId get_first_database_message_id_by_index(const Dialog *d, MessageSearchFilter filter); + void on_get_message_calendar_from_database(int64 random_id, DialogId dialog_id, MessageId from_message_id, + MessageId first_db_message_id, MessageSearchFilter filter, + Result r_calendar, Promise promise); + void on_search_dialog_messages_db_result(int64 random_id, DialogId dialog_id, MessageId from_message_id, MessageId first_db_message_id, MessageSearchFilter filter, int32 offset, int32 limit, Result> r_messages, - Promise<> promise); + Promise promise); void on_messages_db_fts_result(Result result, string offset, int32 limit, int64 random_id, - Promise<> &&promise); + Promise &&promise); void on_messages_db_calls_result(Result result, int64 random_id, MessageId first_db_message_id, MessageSearchFilter filter, Promise &&promise);