Add MessageSource to viewMessages.

This commit is contained in:
levlam 2023-02-07 21:08:54 +03:00
parent f6615189af
commit fbea859a11
8 changed files with 295 additions and 82 deletions

View File

@ -392,6 +392,7 @@ set(TDLIB_SOURCE
td/telegram/MessageSender.cpp
td/telegram/MessagesInfo.cpp
td/telegram/MessagesManager.cpp
td/telegram/MessageSource.cpp
td/telegram/MessageThreadDb.cpp
td/telegram/MessageTtl.cpp
td/telegram/misc.cpp
@ -644,6 +645,7 @@ set(TDLIB_SOURCE
td/telegram/MessageSender.h
td/telegram/MessagesInfo.h
td/telegram/MessagesManager.h
td/telegram/MessageSource.h
td/telegram/MessageThreadDb.h
td/telegram/MessageThreadInfo.h
td/telegram/MessageTtl.h

View File

@ -1139,6 +1139,36 @@ messageCalendarDay total_count:int32 message:message = MessageCalendarDay;
messageCalendar total_count:int32 days:vector<messageCalendarDay> = MessageCalendar;
//@class MessageSource @description Describes source of a message
//@description The message is from a chat history
messageSourceChatHistory = MessageSource;
//@description The message is from a message thread history
messageSourceMessageThreadHistory = MessageSource;
//@description The message is from a forum topic history
messageSourceForumTopicHistory = MessageSource;
//@description The message is from chat, message thread or forum topic history preview
messageSourceHistoryPreview = MessageSource;
//@description The message is from a chat list or a forum topic list
messageSourceChatList = MessageSource;
//@description The message is from search results, including file downloads, local file list, outgoing document messages, calendar
messageSourceSearch = MessageSource;
//@description The message is from a chat event log
messageSourceChatEventLog = MessageSource;
//@description The message is from a notification
messageSourceNotification = MessageSource;
//@description The message is from some other source
messageSourceOther = MessageSource;
//@description Describes a sponsored message
//@message_id Message identifier; unique for the chat to which the sponsored message belongs among both ordinary and sponsored messages
//@is_recommended True, if the message needs to be labeled as "recommended" instead of "sponsored"
@ -6571,10 +6601,10 @@ closeChat chat_id:int53 = Ok;
//@description Informs TDLib that messages are being viewed by the user. Sponsored messages must be marked as viewed only when the entire text of the message is shown on the screen (excluding the button).
//-Many useful activities depend on whether the messages are currently being viewed or not (e.g., marking messages as read, incrementing a view counter, updating a view counter, removing deleted messages in supergroups and channels)
//@chat_id Chat identifier
//@message_thread_id If not 0, a message thread identifier in which the messages are being viewed
//@message_ids The identifiers of the messages being viewed
//@force_read Pass true to mark as read the specified messages even the chat is closed
viewMessages chat_id:int53 message_thread_id:int53 message_ids:vector<int53> force_read:Bool = Ok;
//@source Source of the message view
//@force_read Pass true to mark as read the specified messages even the chat is closed; pass null to guess the source based on chat open state
viewMessages chat_id:int53 message_ids:vector<int53> source:MessageSource force_read:Bool = Ok;
//@description Informs TDLib that the message content has been opened (e.g., the user has opened a photo, video, document, location or venue, or has listened to an audio file or voice note message).
//-An updateMessageContentOpened update will be generated if something has changed

View File

@ -0,0 +1,67 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/MessageSource.h"
namespace td {
StringBuilder &operator<<(StringBuilder &string_builder, MessageSource source) {
switch (source) {
case MessageSource::Auto:
return string_builder << "Auto";
case MessageSource::DialogHistory:
return string_builder << "ChatHistory";
case MessageSource::MessageThreadHistory:
return string_builder << "MessageThreadHistory";
case MessageSource::ForumTopicHistory:
return string_builder << "ForumTopicHistory";
case MessageSource::HistoryPreview:
return string_builder << "HistoryPreview";
case MessageSource::DialogList:
return string_builder << "DialogList";
case MessageSource::Search:
return string_builder << "Search";
case MessageSource::DialogEventLog:
return string_builder << "DialogEventLog";
case MessageSource::Notification:
return string_builder << "Notification";
case MessageSource::Other:
return string_builder << "Other";
default:
UNREACHABLE();
}
}
MessageSource get_message_source(const td_api::object_ptr<td_api::MessageSource> &source) {
if (source == nullptr) {
return MessageSource::Auto;
}
switch (source->get_id()) {
case td_api::messageSourceChatHistory::ID:
return MessageSource::DialogHistory;
case td_api::messageSourceMessageThreadHistory::ID:
return MessageSource::MessageThreadHistory;
case td_api::messageSourceForumTopicHistory::ID:
return MessageSource::ForumTopicHistory;
case td_api::messageSourceHistoryPreview::ID:
return MessageSource::HistoryPreview;
case td_api::messageSourceChatList::ID:
return MessageSource::DialogList;
case td_api::messageSourceSearch::ID:
return MessageSource::Search;
case td_api::messageSourceChatEventLog::ID:
return MessageSource::DialogEventLog;
case td_api::messageSourceNotification::ID:
return MessageSource::Notification;
case td_api::messageSourceOther::ID:
return MessageSource::Other;
default:
UNREACHABLE();
return MessageSource::Other;
}
}
} // namespace td

View File

@ -0,0 +1,33 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
namespace td {
enum class MessageSource : int32 {
Auto,
DialogHistory,
MessageThreadHistory,
ForumTopicHistory,
HistoryPreview,
DialogList,
Search,
DialogEventLog,
Notification,
Other
};
StringBuilder &operator<<(StringBuilder &string_builder, MessageSource source);
MessageSource get_message_source(const td_api::object_ptr<td_api::MessageSource> &source);
} // namespace td

View File

@ -21490,56 +21490,133 @@ DialogId MessagesManager::get_my_dialog_id() const {
return DialogId(td_->contacts_manager_->get_my_id());
}
Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_message_id,
const vector<MessageId> &message_ids, bool force_read) {
Status MessagesManager::view_messages(DialogId dialog_id, vector<MessageId> message_ids, MessageSource source,
bool force_read) {
CHECK(!td_->auth_manager_->is_bot());
Dialog *d = get_dialog_force(dialog_id, "view_messages");
if (d == nullptr) {
return Status::Error(400, "Chat not found");
}
for (auto message_id : message_ids) {
if (!message_id.is_valid() && !message_id.is_valid_scheduled()) {
if (message_id.is_valid_sponsored()) {
if (d->is_opened) {
td_->sponsored_message_manager_->view_sponsored_message(dialog_id, message_id);
}
continue;
}
return Status::Error(400, "Invalid message identifier");
}
}
if (!have_input_peer(dialog_id, AccessRights::Read)) {
return Status::Error(400, "Can't access the chat");
}
MessageId max_thread_message_id;
if (top_thread_message_id != MessageId()) {
if (!top_thread_message_id.is_valid() || !top_thread_message_id.is_server()) {
return Status::Error(400, "Invalid message thread ID specified");
if (source == MessageSource::Auto) {
if (d->is_opened) {
source = MessageSource::DialogHistory;
} else {
source = MessageSource::Other;
}
if (dialog_id.get_type() != DialogType::Channel || is_broadcast_channel(dialog_id)) {
return Status::Error(400, "There are no message threads in the chat");
}
bool is_dialog_history = source == MessageSource::DialogHistory || source == MessageSource::MessageThreadHistory ||
source == MessageSource::ForumTopicHistory;
bool need_read = force_read || is_dialog_history;
bool need_update_view_count = is_dialog_history || source == MessageSource::HistoryPreview ||
source == MessageSource::DialogList || source == MessageSource::Other;
bool need_mark_download_as_viewed = is_dialog_history || source == MessageSource::HistoryPreview ||
source == MessageSource::Search || source == MessageSource::Other;
// keep only valid message identifiers
size_t pos = 0;
for (auto message_id : message_ids) {
if (!message_id.is_valid()) {
if (message_id.is_valid_scheduled()) {
// nothing to do for scheduled messages
continue;
}
if (message_id.is_valid_sponsored()) {
if (is_dialog_history) {
td_->sponsored_message_manager_->view_sponsored_message(dialog_id, message_id);
continue;
} else {
return Status::Error(400, "Can't view the message from the specified source");
}
}
return Status::Error(400, "Invalid message identifier");
}
const auto *top_m = get_message_force(d, top_thread_message_id, "view_messages 6");
if (top_m != nullptr && !top_m->reply_info.is_comment_) {
max_thread_message_id = top_m->reply_info.max_message_id_;
message_ids[pos++] = message_id;
}
message_ids.resize(pos);
if (message_ids.empty()) {
// nothing to do
return Status::OK();
}
for (auto message_id : message_ids) {
auto *m = get_message_force(d, message_id, "view_messages 20");
if (m != nullptr) {
auto file_ids = get_message_content_file_ids(m->content.get(), td_);
for (auto file_id : file_ids) {
td_->file_manager_->check_local_location_async(file_id, true);
}
}
}
if (source == MessageSource::DialogEventLog) {
// nothing more to do
return Status::OK();
}
// get information about thread of the messages
MessageId top_thread_message_id;
MessageId max_thread_message_id;
if (source == MessageSource::MessageThreadHistory) {
if (dialog_id.get_type() != DialogType::Channel || is_broadcast_channel(dialog_id)) {
return Status::Error(400, "There are no message threads in the chat");
}
for (auto message_id : message_ids) {
auto *m = get_message_force(d, message_id, "view_messages 1");
if (m != nullptr) {
if (top_thread_message_id.is_valid()) {
if (m->top_thread_message_id != top_thread_message_id) {
return Status::Error(400, "All messages must be from the same message thread");
}
} else {
if (!m->top_thread_message_id.is_valid()) {
return Status::Error(400, "Messages must be from a message thread");
}
top_thread_message_id = m->top_thread_message_id;
const auto *top_m = get_message_force(d, top_thread_message_id, "view_messages 2");
if (top_m != nullptr && !top_m->reply_info.is_comment_) {
max_thread_message_id = top_m->reply_info.max_message_id_;
}
}
}
}
}
// get forum topic identifier for the messages
MessageId forum_topic_id;
if (source == MessageSource::ForumTopicHistory) {
if (!is_forum_channel(dialog_id)) {
return Status::Error(400, "Chat has no topics");
}
for (auto message_id : message_ids) {
auto *m = get_message_force(d, message_id, "view_messages 3");
if (m != nullptr) {
auto message_forum_topic_id = m->is_topic_message ? m->top_thread_message_id : MessageId(ServerMessageId(1));
if (forum_topic_id.is_valid()) {
if (message_forum_topic_id != forum_topic_id) {
return Status::Error(400, "All messages must be from the same forum topic");
}
} else {
forum_topic_id = message_forum_topic_id;
}
}
}
}
bool need_read = force_read || d->is_opened;
MessageId max_message_id; // max server or local viewed message_id
vector<MessageId> read_content_message_ids;
vector<MessageId> new_viewed_message_ids;
vector<MessageId> viewed_reaction_message_ids;
for (auto message_id : message_ids) {
if (!message_id.is_valid()) {
continue;
}
auto *m = get_message_force(d, message_id, "view_messages 1");
auto *m = get_message_force(d, message_id, "view_messages 4");
if (m != nullptr) {
if (m->message_id.is_server() && m->view_count > 0) {
if (m->message_id.is_server() && m->view_count > 0 && need_update_view_count) {
d->pending_viewed_message_ids.insert(m->message_id);
}
@ -21554,20 +21631,20 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
if (need_read && message_content_type != MessageContentType::VoiceNote &&
message_content_type != MessageContentType::VideoNote &&
update_message_contains_unread_mention(d, m, false, "view_messages")) {
update_message_contains_unread_mention(d, m, false, "view_messages 5")) {
CHECK(m->message_id.is_server());
read_content_message_ids.push_back(m->message_id);
on_message_changed(d, m, true, "view_messages");
on_message_changed(d, m, true, "view_messages 6");
}
if (need_read && remove_message_unread_reactions(d, m, "view_messages")) {
if (need_read && remove_message_unread_reactions(d, m, "view_messages 7")) {
CHECK(m->message_id.is_server());
read_content_message_ids.push_back(m->message_id);
on_message_changed(d, m, true, "view_messages");
on_message_changed(d, m, true, "view_messages 8");
}
auto file_source_id = full_message_id_to_file_source_id_.get({dialog_id, m->message_id});
if (file_source_id.is_valid()) {
if (file_source_id.is_valid() && need_mark_download_as_viewed) {
LOG(INFO) << "Have " << file_source_id << " for " << m->message_id;
CHECK(file_source_id.is_valid());
for (auto file_id : get_message_file_ids(m)) {
@ -21595,11 +21672,6 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
view_id = ++info->current_view_id;
info->recently_viewed_messages[view_id] = message_id;
}
auto file_ids = get_message_content_file_ids(m->content.get(), td_);
for (auto file_id : file_ids) {
td_->file_manager_->check_local_location_async(file_id, true);
}
} else if (!message_id.is_yet_unsent() && message_id > max_message_id) {
if (message_id <= d->max_notification_message_id || message_id <= d->last_new_message_id ||
message_id <= max_thread_message_id) {
@ -21609,7 +21681,7 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
}
if (!d->pending_viewed_message_ids.empty()) {
pending_message_views_timeout_.add_timeout_in(dialog_id.get(), MAX_MESSAGE_VIEW_DELAY);
d->increment_view_counter |= d->is_opened;
d->increment_view_counter |= is_dialog_history;
}
if (!read_content_message_ids.empty()) {
read_message_contents_on_server(dialog_id, std::move(read_content_message_ids), 0, Auto());
@ -21637,15 +21709,15 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
return Status::OK();
}
if (top_thread_message_id.is_valid() && max_message_id.is_valid()) {
if (source == MessageSource::MessageThreadHistory && top_thread_message_id.is_valid() && max_message_id.is_valid()) {
MessageId prev_last_read_inbox_message_id;
max_thread_message_id = MessageId();
Message *top_m = get_message_force(d, top_thread_message_id, "view_messages 2");
Message *top_m = get_message_force(d, top_thread_message_id, "view_messages 9");
if (top_m != nullptr && is_active_message_reply_info(dialog_id, top_m->reply_info)) {
prev_last_read_inbox_message_id = top_m->reply_info.last_read_inbox_message_id_;
if (top_m->reply_info.update_max_message_ids(MessageId(), max_message_id, MessageId())) {
on_message_reply_info_changed(dialog_id, top_m);
on_message_changed(d, top_m, true, "view_messages 3");
on_message_changed(d, top_m, true, "view_messages 10");
}
max_thread_message_id = top_m->reply_info.max_message_id_;
@ -21654,14 +21726,14 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
auto linked_d = get_dialog(linked_dialog_id);
CHECK(linked_d != nullptr);
CHECK(linked_dialog_id.get_type() == DialogType::Channel);
auto *linked_m = get_message_force(linked_d, top_m->forward_info->from_message_id, "view_messages 4");
auto *linked_m = get_message_force(linked_d, top_m->forward_info->from_message_id, "view_messages 11");
if (linked_m != nullptr && is_active_message_reply_info(linked_dialog_id, linked_m->reply_info)) {
if (linked_m->reply_info.last_read_inbox_message_id_ < prev_last_read_inbox_message_id) {
prev_last_read_inbox_message_id = linked_m->reply_info.last_read_inbox_message_id_;
}
if (linked_m->reply_info.update_max_message_ids(MessageId(), max_message_id, MessageId())) {
on_message_reply_info_changed(linked_dialog_id, linked_m);
on_message_changed(linked_d, linked_m, true, "view_messages 5");
on_message_changed(linked_d, linked_m, true, "view_messages 12");
}
if (linked_m->reply_info.max_message_id_ > max_thread_message_id) {
max_thread_message_id = linked_m->reply_info.max_message_id_;
@ -21678,35 +21750,42 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
return Status::OK();
}
if (max_message_id > d->last_read_inbox_message_id) {
const MessageId last_read_message_id = max_message_id;
const MessageId prev_last_read_inbox_message_id = d->last_read_inbox_message_id;
MessageId read_history_on_server_message_id;
if (dialog_id.get_type() != DialogType::SecretChat) {
if (last_read_message_id.get_prev_server_message_id().get() >
prev_last_read_inbox_message_id.get_prev_server_message_id().get()) {
read_history_on_server_message_id = last_read_message_id.get_prev_server_message_id();
}
} else {
if (last_read_message_id > prev_last_read_inbox_message_id) {
read_history_on_server_message_id = last_read_message_id;
}
}
if (read_history_on_server_message_id.is_valid()) {
// add dummy timeout to not try to repair unread_count in read_history_inbox before server request succeeds
// the timeout will be overwritten in the read_history_on_server call
pending_read_history_timeout_.add_timeout_in(dialog_id.get(), 0);
}
read_history_inbox(d->dialog_id, last_read_message_id, -1, "view_messages");
if (read_history_on_server_message_id.is_valid()) {
// call read_history_on_server after read_history_inbox to not have delay before request if all messages are read
read_history_on_server(d, read_history_on_server_message_id);
}
if (source == MessageSource::ForumTopicHistory) {
// TODO read forum topic history
return Status::OK();
}
if (d->is_marked_as_unread) {
set_dialog_is_marked_as_unread(d, false);
if (source == MessageSource::DialogHistory) {
if (max_message_id > d->last_read_inbox_message_id) {
const MessageId last_read_message_id = max_message_id;
const MessageId prev_last_read_inbox_message_id = d->last_read_inbox_message_id;
MessageId read_history_on_server_message_id;
if (dialog_id.get_type() != DialogType::SecretChat) {
if (last_read_message_id.get_prev_server_message_id().get() >
prev_last_read_inbox_message_id.get_prev_server_message_id().get()) {
read_history_on_server_message_id = last_read_message_id.get_prev_server_message_id();
}
} else {
if (last_read_message_id > prev_last_read_inbox_message_id) {
read_history_on_server_message_id = last_read_message_id;
}
}
if (read_history_on_server_message_id.is_valid()) {
// add dummy timeout to not try to repair unread_count in read_history_inbox before server request succeeds
// the timeout will be overwritten in the read_history_on_server call
pending_read_history_timeout_.add_timeout_in(dialog_id.get(), 0);
}
read_history_inbox(d->dialog_id, last_read_message_id, -1, "view_messages 13");
if (read_history_on_server_message_id.is_valid()) {
// call read_history_on_server after read_history_inbox to not have delay before request if all messages are read
read_history_on_server(d, read_history_on_server_message_id);
}
}
if (d->is_marked_as_unread) {
set_dialog_is_marked_as_unread(d, false);
}
return Status::OK();
}
return Status::OK();

View File

@ -38,6 +38,7 @@
#include "td/telegram/MessageReplyInfo.h"
#include "td/telegram/MessageSearchFilter.h"
#include "td/telegram/MessagesInfo.h"
#include "td/telegram/MessageSource.h"
#include "td/telegram/MessageThreadInfo.h"
#include "td/telegram/MessageTtl.h"
#include "td/telegram/net/DcId.h"
@ -716,7 +717,7 @@ class MessagesManager final : public Actor {
Status close_dialog(DialogId dialog_id) TD_WARN_UNUSED_RESULT;
Status view_messages(DialogId dialog_id, MessageId top_thread_message_id, const vector<MessageId> &message_ids,
Status view_messages(DialogId dialog_id, vector<MessageId> message_ids, MessageSource source,
bool force_read) TD_WARN_UNUSED_RESULT;
void finish_get_message_views(DialogId dialog_id, const vector<MessageId> &message_ids);

View File

@ -75,6 +75,7 @@
#include "td/telegram/MessageSearchFilter.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/MessageSource.h"
#include "td/telegram/MessageThreadInfo.h"
#include "td/telegram/MessageTtl.h"
#include "td/telegram/misc.h"
@ -5095,8 +5096,8 @@ void Td::on_request(uint64 id, const td_api::closeChat &request) {
void Td::on_request(uint64 id, const td_api::viewMessages &request) {
CHECK_IS_USER();
answer_ok_query(
id, messages_manager_->view_messages(DialogId(request.chat_id_), MessageId(request.message_thread_id_),
MessageId::get_message_ids(request.message_ids_), request.force_read_));
id, messages_manager_->view_messages(DialogId(request.chat_id_), MessageId::get_message_ids(request.message_ids_),
get_message_source(request.source_), request.force_read_));
}
void Td::on_request(uint64 id, const td_api::openMessageContent &request) {

View File

@ -5010,14 +5010,14 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::removeRecentHashtag>(hashtag));
} else if (op == "view" || op == "viewt") {
ChatId chat_id;
MessageThreadId message_thread_id;
string message_ids;
get_args(args, chat_id, message_ids);
td_api::object_ptr<td_api::MessageSource> source;
if (op == "viewt") {
get_args(message_ids, message_thread_id, message_ids);
source = td_api::make_object<td_api::messageSourceMessageThreadHistory>();
}
send_request(
td_api::make_object<td_api::viewMessages>(chat_id, message_thread_id, as_message_ids(message_ids), true));
td_api::make_object<td_api::viewMessages>(chat_id, as_message_ids(message_ids), std::move(source), true));
} else if (op == "omc") {
ChatId chat_id;
MessageId message_id;