Support quote position for replies.

This commit is contained in:
levlam 2023-11-10 13:29:37 +03:00
parent 9207ba4c78
commit 95304c611c
12 changed files with 93 additions and 32 deletions

View File

@ -1236,13 +1236,14 @@ messageSendingStateFailed error:error can_retry:Bool need_another_sender:Bool ne
//@chat_id The identifier of the chat to which the message belongs; may be 0 if the replied message is in unknown chat //@chat_id The identifier of the chat to which the message belongs; may be 0 if the replied message is in unknown chat
//@message_id The identifier of the message; may be 0 if the replied message is in unknown chat //@message_id The identifier of the message; may be 0 if the replied message is in unknown chat
//@quote Manually or automatically chosen quote from the replied message; may be null if none. Only Bold, Italic, Underline, Strikethrough, Spoiler, and CustomEmoji entities can be present in the quote //@quote Manually or automatically chosen quote from the replied message; may be null if none. Only Bold, Italic, Underline, Strikethrough, Spoiler, and CustomEmoji entities can be present in the quote
//@quote_position Approximate quote position in UTF-16 code units
//@is_quote_manual True, if the quote was manually chosen by the message sender //@is_quote_manual True, if the quote was manually chosen by the message sender
//@origin Information about origin of the message if the message was from another chat or topic; may be null for messages from the same chat //@origin Information about origin of the message if the message was from another chat or topic; may be null for messages from the same chat
//@origin_send_date Point in time (Unix timestamp) when the message was sent if the message was from another chat or topic; 0 for messages from the same chat //@origin_send_date Point in time (Unix timestamp) when the message was sent if the message was from another chat or topic; 0 for messages from the same chat
//@content Media content of the message if the message was from another chat or topic; may be null for messages from the same chat and messages without media. //@content Media content of the message if the message was from another chat or topic; may be null for messages from the same chat and messages without media.
//-Can be only one of the following types: messageAnimation, messageAudio, messageContact, messageDice, messageDocument, messageGame, messageInvoice, messageLocation, //-Can be only one of the following types: messageAnimation, messageAudio, messageContact, messageDice, messageDocument, messageGame, messageInvoice, messageLocation,
//-messagePhoto, messagePoll, messagePremiumGiveaway, messageSticker, messageStory, messageText (for link preview), messageVenue, messageVideo, messageVideoNote, or messageVoiceNote //-messagePhoto, messagePoll, messagePremiumGiveaway, messageSticker, messageStory, messageText (for link preview), messageVenue, messageVideo, messageVideoNote, or messageVoiceNote
messageReplyToMessage chat_id:int53 message_id:int53 quote:formattedText is_quote_manual:Bool origin:MessageOrigin origin_send_date:int32 content:MessageContent = MessageReplyTo; messageReplyToMessage chat_id:int53 message_id:int53 quote:formattedText quote_position:int32 is_quote_manual:Bool origin:MessageOrigin origin_send_date:int32 content:MessageContent = MessageReplyTo;
//@description Describes a story replied by a given message @story_sender_chat_id The identifier of the sender of the story @story_id The identifier of the story //@description Describes a story replied by a given message @story_sender_chat_id The identifier of the sender of the story @story_id The identifier of the story
messageReplyToStory story_sender_chat_id:int53 story_id:int32 = MessageReplyTo; messageReplyToStory story_sender_chat_id:int53 story_id:int32 = MessageReplyTo;
@ -1255,7 +1256,8 @@ messageReplyToStory story_sender_chat_id:int53 story_id:int32 = MessageReplyTo;
//@message_id The identifier of the message to be replied in the same or the specified chat //@message_id The identifier of the message to be replied in the same or the specified chat
//@quote Manually chosen quote from the message to be replied; pass null if none; 0-getOption("message_reply_quote_length_max") characters. Must always be null for replies in secret chats. //@quote Manually chosen quote from the message to be replied; pass null if none; 0-getOption("message_reply_quote_length_max") characters. Must always be null for replies in secret chats.
//-Only Bold, Italic, Underline, Strikethrough, Spoiler, and CustomEmoji entities are allowed to be kept and must be kept in the quote //-Only Bold, Italic, Underline, Strikethrough, Spoiler, and CustomEmoji entities are allowed to be kept and must be kept in the quote
inputMessageReplyToMessage chat_id:int53 message_id:int53 quote:formattedText = InputMessageReplyTo; //@quote_position Quote position in UTF-16 code units
inputMessageReplyToMessage chat_id:int53 message_id:int53 quote:formattedText quote_position:int32 = InputMessageReplyTo;
//@description Describes a story to be replied @story_sender_chat_id The identifier of the sender of the story. Currently, stories can be replied only in the sender's chat @story_id The identifier of the story //@description Describes a story to be replied @story_sender_chat_id The identifier of the sender of the story. Currently, stories can be replied only in the sender's chat @story_id The identifier of the story
inputMessageReplyToStory story_sender_chat_id:int53 story_id:int32 = InputMessageReplyTo; inputMessageReplyToStory story_sender_chat_id:int53 story_id:int32 = InputMessageReplyTo;
@ -7074,7 +7076,8 @@ forwardMessages chat_id:int53 message_thread_id:int53 from_chat_id:int53 message
//@chat_id Identifier of the chat to send messages //@chat_id Identifier of the chat to send messages
//@message_ids Identifiers of the messages to resend. Message identifiers must be in a strictly increasing order //@message_ids Identifiers of the messages to resend. Message identifiers must be in a strictly increasing order
//@quote New manually chosen quote from the message to be replied; pass null if none. Ignored if more than one message is re-sent, or if messageSendingStateFailed.need_another_reply_quote == false //@quote New manually chosen quote from the message to be replied; pass null if none. Ignored if more than one message is re-sent, or if messageSendingStateFailed.need_another_reply_quote == false
resendMessages chat_id:int53 message_ids:vector<int53> quote:formattedText = Messages; //@quote_position New quote position in UTF-16 code units
resendMessages chat_id:int53 message_ids:vector<int53> quote:formattedText quote_position:int32 = Messages;
//@description Adds a local message to a chat. The message is persistent across application restarts only if the message database is used. Returns the added message //@description Adds a local message to a chat. The message is persistent across application restarts only if the message database is used. Returns the added message
//@chat_id Target chat //@chat_id Target chat

View File

@ -53,7 +53,7 @@ void DraftMessage::parse(ParserT &parser) {
if (has_legacy_reply_to_message_id) { if (has_legacy_reply_to_message_id) {
MessageId legacy_reply_to_message_id; MessageId legacy_reply_to_message_id;
td::parse(legacy_reply_to_message_id, parser); td::parse(legacy_reply_to_message_id, parser);
message_input_reply_to_ = MessageInputReplyTo{legacy_reply_to_message_id, DialogId(), FormattedText()}; message_input_reply_to_ = MessageInputReplyTo{legacy_reply_to_message_id, DialogId(), FormattedText(), 0};
} }
if (has_input_message_text) { if (has_input_message_text) {
td::parse(input_message_text_, parser); td::parse(input_message_text_, parser);

View File

@ -73,6 +73,7 @@ MessageInputReplyTo::MessageInputReplyTo(Td *td,
} }
quote_ = FormattedText{std::move(reply_to->quote_text_), std::move(entities)}; quote_ = FormattedText{std::move(reply_to->quote_text_), std::move(entities)};
remove_unallowed_quote_entities(quote_); remove_unallowed_quote_entities(quote_);
quote_position_ = max(0, reply_to->quote_offset_);
} }
break; break;
} }
@ -129,9 +130,12 @@ telegram_api::object_ptr<telegram_api::InputReplyTo> MessageInputReplyTo::get_in
if (!quote_entities.empty()) { if (!quote_entities.empty()) {
flags |= telegram_api::inputReplyToMessage::QUOTE_ENTITIES_MASK; flags |= telegram_api::inputReplyToMessage::QUOTE_ENTITIES_MASK;
} }
if (quote_position_ != 0) {
flags |= telegram_api::inputReplyToMessage::QUOTE_OFFSET_MASK;
}
return telegram_api::make_object<telegram_api::inputReplyToMessage>( return telegram_api::make_object<telegram_api::inputReplyToMessage>(
flags, reply_to_message_id.get_server_message_id().get(), top_thread_message_id.get_server_message_id().get(), flags, reply_to_message_id.get_server_message_id().get(), top_thread_message_id.get_server_message_id().get(),
std::move(input_peer), quote_.text, std::move(quote_entities), 0); std::move(input_peer), quote_.text, std::move(quote_entities), quote_position_);
} }
// only for draft messages // only for draft messages
@ -150,7 +154,7 @@ td_api::object_ptr<td_api::InputMessageReplyTo> MessageInputReplyTo::get_input_m
} }
return td_api::make_object<td_api::inputMessageReplyToMessage>( return td_api::make_object<td_api::inputMessageReplyToMessage>(
td->messages_manager_->get_chat_id_object(dialog_id_, "inputMessageReplyToMessage"), message_id_.get(), td->messages_manager_->get_chat_id_object(dialog_id_, "inputMessageReplyToMessage"), message_id_.get(),
std::move(quote)); std::move(quote), quote_position_);
} }
MessageId MessageInputReplyTo::get_same_chat_reply_to_message_id() const { MessageId MessageInputReplyTo::get_same_chat_reply_to_message_id() const {
@ -167,7 +171,8 @@ MessageFullId MessageInputReplyTo::get_reply_message_full_id(DialogId owner_dial
bool operator==(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) { bool operator==(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) {
return lhs.message_id_ == rhs.message_id_ && lhs.dialog_id_ == rhs.dialog_id_ && return lhs.message_id_ == rhs.message_id_ && lhs.dialog_id_ == rhs.dialog_id_ &&
lhs.story_full_id_ == rhs.story_full_id_ && lhs.quote_ == rhs.quote_; lhs.story_full_id_ == rhs.story_full_id_ && lhs.quote_ == rhs.quote_ &&
lhs.quote_position_ == rhs.quote_position_;
} }
bool operator!=(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) { bool operator!=(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) {
@ -182,6 +187,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReply
} }
if (!input_reply_to.quote_.text.empty()) { if (!input_reply_to.quote_.text.empty()) {
string_builder << " with " << input_reply_to.quote_.text.size() << " quoted bytes"; string_builder << " with " << input_reply_to.quote_.text.size() << " quoted bytes";
if (input_reply_to.quote_position_ != 0) {
string_builder << " at position " << input_reply_to.quote_position_;
}
} }
return string_builder; return string_builder;
} }

View File

@ -27,6 +27,7 @@ class MessageInputReplyTo {
MessageId message_id_; MessageId message_id_;
DialogId dialog_id_; DialogId dialog_id_;
FormattedText quote_; FormattedText quote_;
int32 quote_position_ = 0;
// or // or
StoryFullId story_full_id_; StoryFullId story_full_id_;
@ -44,8 +45,11 @@ class MessageInputReplyTo {
MessageInputReplyTo &operator=(MessageInputReplyTo &&) = default; MessageInputReplyTo &operator=(MessageInputReplyTo &&) = default;
~MessageInputReplyTo(); ~MessageInputReplyTo();
MessageInputReplyTo(MessageId message_id, DialogId dialog_id, FormattedText &&quote) MessageInputReplyTo(MessageId message_id, DialogId dialog_id, FormattedText &&quote, int32 quote_position)
: message_id_(message_id), dialog_id_(dialog_id), quote_(std::move(quote)) { : message_id_(message_id)
, dialog_id_(dialog_id)
, quote_(std::move(quote))
, quote_position_(max(0, quote_position)) {
remove_unallowed_quote_entities(quote_); remove_unallowed_quote_entities(quote_);
} }
@ -66,8 +70,9 @@ class MessageInputReplyTo {
return !quote_.text.empty(); return !quote_.text.empty();
} }
void set_quote(FormattedText &&quote) { void set_quote(FormattedText &&quote, int32 quote_position) {
quote_ = std::move(quote); quote_ = std::move(quote);
quote_position_ = max(0, quote_position);
} }
StoryFullId get_story_full_id() const { StoryFullId get_story_full_id() const {

View File

@ -20,11 +20,13 @@ void MessageInputReplyTo::store(StorerT &storer) const {
bool has_story_full_id = story_full_id_.is_valid(); bool has_story_full_id = story_full_id_.is_valid();
bool has_quote = !quote_.text.empty(); bool has_quote = !quote_.text.empty();
bool has_dialog_id = dialog_id_.is_valid(); bool has_dialog_id = dialog_id_.is_valid();
bool has_quote_position = quote_position_ != 0;
BEGIN_STORE_FLAGS(); BEGIN_STORE_FLAGS();
STORE_FLAG(has_message_id); STORE_FLAG(has_message_id);
STORE_FLAG(has_story_full_id); STORE_FLAG(has_story_full_id);
STORE_FLAG(has_quote); STORE_FLAG(has_quote);
STORE_FLAG(has_dialog_id); STORE_FLAG(has_dialog_id);
STORE_FLAG(has_quote_position);
END_STORE_FLAGS(); END_STORE_FLAGS();
if (has_message_id) { if (has_message_id) {
td::store(message_id_, storer); td::store(message_id_, storer);
@ -38,6 +40,9 @@ void MessageInputReplyTo::store(StorerT &storer) const {
if (has_dialog_id) { if (has_dialog_id) {
td::store(dialog_id_, storer); td::store(dialog_id_, storer);
} }
if (has_quote_position) {
td::store(quote_position_, storer);
}
} }
template <class ParserT> template <class ParserT>
@ -46,11 +51,13 @@ void MessageInputReplyTo::parse(ParserT &parser) {
bool has_story_full_id; bool has_story_full_id;
bool has_quote; bool has_quote;
bool has_dialog_id; bool has_dialog_id;
bool has_quote_position;
BEGIN_PARSE_FLAGS(); BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_message_id); PARSE_FLAG(has_message_id);
PARSE_FLAG(has_story_full_id); PARSE_FLAG(has_story_full_id);
PARSE_FLAG(has_quote); PARSE_FLAG(has_quote);
PARSE_FLAG(has_dialog_id); PARSE_FLAG(has_dialog_id);
PARSE_FLAG(has_quote_position);
END_PARSE_FLAGS(); END_PARSE_FLAGS();
if (has_message_id) { if (has_message_id) {
td::parse(message_id_, parser); td::parse(message_id_, parser);
@ -65,6 +72,9 @@ void MessageInputReplyTo::parse(ParserT &parser) {
if (has_dialog_id) { if (has_dialog_id) {
td::parse(dialog_id_, parser); td::parse(dialog_id_, parser);
} }
if (has_quote_position) {
td::parse(quote_position_, parser);
}
} }
} // namespace td } // namespace td

View File

@ -5180,7 +5180,7 @@ void MessagesManager::Message::parse(ParserT &parser) {
if (reply_to_story_full_id.is_valid()) { if (reply_to_story_full_id.is_valid()) {
input_reply_to = MessageInputReplyTo(reply_to_story_full_id); input_reply_to = MessageInputReplyTo(reply_to_story_full_id);
} else if (legacy_reply_to_message_id.is_valid()) { } else if (legacy_reply_to_message_id.is_valid()) {
input_reply_to = MessageInputReplyTo{legacy_reply_to_message_id, DialogId(), FormattedText()}; input_reply_to = MessageInputReplyTo{legacy_reply_to_message_id, DialogId(), FormattedText(), 0};
} }
} }
if (has_replied_message_info) { if (has_replied_message_info) {
@ -24518,7 +24518,7 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
} }
if (reply_to == nullptr) { if (reply_to == nullptr) {
if (!for_draft && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) { if (!for_draft && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) {
return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText()}; return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText(), 0};
} }
return {}; return {};
} }
@ -24546,7 +24546,7 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
if (!message_id.is_valid()) { if (!message_id.is_valid()) {
if (!for_draft && message_id == MessageId() && top_thread_message_id.is_valid() && if (!for_draft && message_id == MessageId() && top_thread_message_id.is_valid() &&
top_thread_message_id.is_server()) { top_thread_message_id.is_server()) {
return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText()}; return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText(), 0};
} }
return {}; return {};
} }
@ -24566,10 +24566,12 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
return {}; return {};
} }
FormattedText quote; FormattedText quote;
int32 quote_position = 0;
auto r_quote = get_formatted_text(td_, get_my_dialog_id(), std::move(reply_to_message->quote_), auto r_quote = get_formatted_text(td_, get_my_dialog_id(), std::move(reply_to_message->quote_),
td_->auth_manager_->is_bot(), true, true, true); td_->auth_manager_->is_bot(), true, true, true);
if (r_quote.is_ok() && d->dialog_id.get_type() != DialogType::SecretChat) { if (r_quote.is_ok() && d->dialog_id.get_type() != DialogType::SecretChat) {
quote = r_quote.move_as_ok(); quote = r_quote.move_as_ok();
quote_position = reply_to_message->quote_position_;
} }
const Message *m = get_message_force(reply_d, message_id, "get_message_input_reply_to 2"); const Message *m = get_message_force(reply_d, message_id, "get_message_input_reply_to 2");
if (m == nullptr || m->message_id.is_yet_unsent() || if (m == nullptr || m->message_id.is_yet_unsent() ||
@ -24579,10 +24581,10 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
(reply_d->notification_info != nullptr && (reply_d->notification_info != nullptr &&
message_id <= reply_d->notification_info->max_push_notification_message_id_)) { message_id <= reply_d->notification_info->max_push_notification_message_id_)) {
// allow to reply yet unreceived server message in the same chat // allow to reply yet unreceived server message in the same chat
return MessageInputReplyTo{message_id, reply_dialog_id, std::move(quote)}; return MessageInputReplyTo{message_id, reply_dialog_id, std::move(quote), quote_position};
} }
if (!for_draft && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) { if (!for_draft && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) {
return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText()}; return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText(), 0};
} }
LOG(INFO) << "Can't find " << message_id << " in " << reply_d->dialog_id; LOG(INFO) << "Can't find " << message_id << " in " << reply_d->dialog_id;
@ -24594,7 +24596,7 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
LOG(INFO) << "Can't reply in another chat " << m->message_id << " in " << reply_d->dialog_id; LOG(INFO) << "Can't reply in another chat " << m->message_id << " in " << reply_d->dialog_id;
return {}; return {};
} }
return MessageInputReplyTo{m->message_id, reply_dialog_id, std::move(quote)}; return MessageInputReplyTo{m->message_id, reply_dialog_id, std::move(quote), quote_position};
} }
default: default:
UNREACHABLE(); UNREACHABLE();
@ -24711,7 +24713,7 @@ void MessagesManager::cancel_send_message_query(DialogId dialog_id, Message *m)
CHECK(input_reply_to != nullptr); CHECK(input_reply_to != nullptr);
CHECK(input_reply_to->get_reply_message_full_id(reply_d->dialog_id) == MessageFullId(dialog_id, m->message_id)); CHECK(input_reply_to->get_reply_message_full_id(reply_d->dialog_id) == MessageFullId(dialog_id, m->message_id));
set_message_reply(reply_d, replied_m, set_message_reply(reply_d, replied_m,
MessageInputReplyTo{replied_m->top_thread_message_id, DialogId(), FormattedText()}, true); MessageInputReplyTo{replied_m->top_thread_message_id, DialogId(), FormattedText(), 0}, true);
} }
replied_yet_unsent_messages_.erase(it); replied_yet_unsent_messages_.erase(it);
} }
@ -28252,7 +28254,7 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
if (!input_reply_to.is_valid() && copied_message.original_reply_to_message_id.is_valid() && is_secret) { if (!input_reply_to.is_valid() && copied_message.original_reply_to_message_id.is_valid() && is_secret) {
auto it = forwarded_message_id_to_new_message_id.find(copied_message.original_reply_to_message_id); auto it = forwarded_message_id_to_new_message_id.find(copied_message.original_reply_to_message_id);
if (it != forwarded_message_id_to_new_message_id.end()) { if (it != forwarded_message_id_to_new_message_id.end()) {
input_reply_to = MessageInputReplyTo{it->second, DialogId(), FormattedText()}; input_reply_to = MessageInputReplyTo{it->second, DialogId(), FormattedText(), 0};
} }
} }
@ -28308,7 +28310,8 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
} }
Result<vector<MessageId>> MessagesManager::resend_messages(DialogId dialog_id, vector<MessageId> message_ids, Result<vector<MessageId>> MessagesManager::resend_messages(DialogId dialog_id, vector<MessageId> message_ids,
td_api::object_ptr<td_api::formattedText> &&quote) { td_api::object_ptr<td_api::formattedText> &&quote,
int32 quote_position) {
if (message_ids.empty()) { if (message_ids.empty()) {
return Status::Error(400, "There are no messages to resend"); return Status::Error(400, "There are no messages to resend");
} }
@ -28407,7 +28410,7 @@ Result<vector<MessageId>> MessagesManager::resend_messages(DialogId dialog_id, v
auto r_quote = auto r_quote =
get_formatted_text(td_, get_my_dialog_id(), std::move(quote), td_->auth_manager_->is_bot(), true, true, true); get_formatted_text(td_, get_my_dialog_id(), std::move(quote), td_->auth_manager_->is_bot(), true, true, true);
if (r_quote.is_ok()) { if (r_quote.is_ok()) {
message->input_reply_to.set_quote(r_quote.move_as_ok()); message->input_reply_to.set_quote(r_quote.move_as_ok(), quote_position);
} }
} else if (need_drop_reply) { } else if (need_drop_reply) {
message->input_reply_to = {}; message->input_reply_to = {};
@ -39424,7 +39427,7 @@ void MessagesManager::restore_message_reply_to_message_id(Dialog *d, Message *m)
if (message_id.is_valid() || message_id.is_valid_scheduled()) { if (message_id.is_valid() || message_id.is_valid_scheduled()) {
update_message_reply_to_message_id(d, m, message_id, false); update_message_reply_to_message_id(d, m, message_id, false);
} else { } else {
set_message_reply(d, m, MessageInputReplyTo{m->top_thread_message_id, DialogId(), FormattedText()}, false); set_message_reply(d, m, MessageInputReplyTo{m->top_thread_message_id, DialogId(), FormattedText(), 0}, false);
} }
} }

View File

@ -466,7 +466,8 @@ class MessagesManager final : public Actor {
vector<MessageCopyOptions> &&copy_options) TD_WARN_UNUSED_RESULT; vector<MessageCopyOptions> &&copy_options) TD_WARN_UNUSED_RESULT;
Result<vector<MessageId>> resend_messages(DialogId dialog_id, vector<MessageId> message_ids, Result<vector<MessageId>> resend_messages(DialogId dialog_id, vector<MessageId> message_ids,
td_api::object_ptr<td_api::formattedText> &&quote) TD_WARN_UNUSED_RESULT; td_api::object_ptr<td_api::formattedText> &&quote,
int32 quote_position) TD_WARN_UNUSED_RESULT;
void set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Promise<Unit> &&promise); void set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Promise<Unit> &&promise);

View File

@ -150,6 +150,7 @@ RepliedMessageInfo::RepliedMessageInfo(Td *td, tl_object_ptr<telegram_api::messa
entities.clear(); entities.clear();
} }
quote_ = FormattedText{std::move(reply_header->quote_text_), std::move(entities)}; quote_ = FormattedText{std::move(reply_header->quote_text_), std::move(entities)};
quote_position_ = max(0, reply_header->quote_offset_);
remove_unallowed_quote_entities(quote_); remove_unallowed_quote_entities(quote_);
} }
} }
@ -161,6 +162,7 @@ RepliedMessageInfo::RepliedMessageInfo(Td *td, const MessageInputReplyTo &input_
message_id_ = input_reply_to.message_id_; message_id_ = input_reply_to.message_id_;
if (!input_reply_to.quote_.text.empty()) { if (!input_reply_to.quote_.text.empty()) {
quote_ = input_reply_to.quote_; quote_ = input_reply_to.quote_;
quote_position_ = input_reply_to.quote_position_;
is_quote_manual_ = true; is_quote_manual_ = true;
} }
if (input_reply_to.dialog_id_ != DialogId()) { if (input_reply_to.dialog_id_ != DialogId()) {
@ -206,6 +208,7 @@ RepliedMessageInfo RepliedMessageInfo::clone(Td *td) const {
MessageContentDupType::Forward, MessageCopyOptions()); MessageContentDupType::Forward, MessageCopyOptions());
} }
result.quote_ = quote_; result.quote_ = quote_;
result.quote_position_ = quote_position_;
result.is_quote_manual_ = is_quote_manual_; result.is_quote_manual_ = is_quote_manual_;
return result; return result;
} }
@ -227,6 +230,12 @@ bool RepliedMessageInfo::need_reply_changed_warning(
// only signature can change in the message origin // only signature can change in the message origin
return true; return true;
} }
if (old_info.quote_position_ != new_info.quote_position_ &&
max(old_info.quote_position_, new_info.quote_position_) <
static_cast<int32>(min(old_info.quote_.text.size(), new_info.quote_.text.size()))) {
// quote position can't change
return true;
}
if (old_info.is_quote_manual_ != new_info.is_quote_manual_) { if (old_info.is_quote_manual_ != new_info.is_quote_manual_) {
// quote manual property can't change // quote manual property can't change
return true; return true;
@ -336,8 +345,12 @@ td_api::object_ptr<td_api::messageReplyToMessage> RepliedMessageInfo::get_messag
} }
td_api::object_ptr<td_api::formattedText> quote; td_api::object_ptr<td_api::formattedText> quote;
int32 quote_position = 0;
bool is_quote_manual = false;
if (!quote_.text.empty()) { if (!quote_.text.empty()) {
quote = get_formatted_text_object(quote_, true, -1); quote = get_formatted_text_object(quote_, true, -1);
quote_position = quote_position_;
is_quote_manual = is_quote_manual_;
} }
td_api::object_ptr<td_api::MessageOrigin> origin; td_api::object_ptr<td_api::MessageOrigin> origin;
@ -357,14 +370,14 @@ td_api::object_ptr<td_api::messageReplyToMessage> RepliedMessageInfo::get_messag
} }
return td_api::make_object<td_api::messageReplyToMessage>(chat_id, message_id_.get(), std::move(quote), return td_api::make_object<td_api::messageReplyToMessage>(chat_id, message_id_.get(), std::move(quote),
is_quote_manual_, std::move(origin), origin_date_, quote_position, is_quote_manual, std::move(origin),
std::move(content)); origin_date_, std::move(content));
} }
MessageInputReplyTo RepliedMessageInfo::get_input_reply_to() const { MessageInputReplyTo RepliedMessageInfo::get_input_reply_to() const {
CHECK(!is_external()); CHECK(!is_external());
if (message_id_.is_valid()) { if (message_id_.is_valid()) {
return MessageInputReplyTo{message_id_, dialog_id_, FormattedText{quote_}}; return MessageInputReplyTo{message_id_, dialog_id_, FormattedText{quote_}, quote_position_};
} }
return {}; return {};
} }
@ -404,7 +417,7 @@ void RepliedMessageInfo::unregister_content(Td *td) const {
bool operator==(const RepliedMessageInfo &lhs, const RepliedMessageInfo &rhs) { bool operator==(const RepliedMessageInfo &lhs, const RepliedMessageInfo &rhs) {
if (!(lhs.message_id_ == rhs.message_id_ && lhs.dialog_id_ == rhs.dialog_id_ && if (!(lhs.message_id_ == rhs.message_id_ && lhs.dialog_id_ == rhs.dialog_id_ &&
lhs.origin_date_ == rhs.origin_date_ && lhs.origin_ == rhs.origin_ && lhs.quote_ == rhs.quote_ && lhs.origin_date_ == rhs.origin_date_ && lhs.origin_ == rhs.origin_ && lhs.quote_ == rhs.quote_ &&
lhs.is_quote_manual_ == rhs.is_quote_manual_)) { lhs.quote_position_ == rhs.quote_position_ && lhs.is_quote_manual_ == rhs.is_quote_manual_)) {
return false; return false;
} }
bool need_update = false; bool need_update = false;
@ -431,6 +444,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const RepliedMessageInf
if (!info.quote_.text.empty()) { if (!info.quote_.text.empty()) {
string_builder << " with " << info.quote_.text.size() << (info.is_quote_manual_ ? " manually" : "") string_builder << " with " << info.quote_.text.size() << (info.is_quote_manual_ ? " manually" : "")
<< " quoted bytes"; << " quoted bytes";
if (info.quote_position_ != 0) {
string_builder << " at position " << info.quote_position_;
}
} }
if (info.content_ != nullptr) { if (info.content_ != nullptr) {
string_builder << " and content of the type " << info.content_->get_type(); string_builder << " and content of the type " << info.content_->get_type();

View File

@ -37,6 +37,7 @@ class RepliedMessageInfo {
MessageOrigin origin_; // for replies in other chats MessageOrigin origin_; // for replies in other chats
unique_ptr<MessageContent> content_; // for replies in other chats unique_ptr<MessageContent> content_; // for replies in other chats
FormattedText quote_; FormattedText quote_;
int32 quote_position_ = 0;
bool is_quote_manual_ = false; bool is_quote_manual_ = false;
friend bool operator==(const RepliedMessageInfo &lhs, const RepliedMessageInfo &rhs); friend bool operator==(const RepliedMessageInfo &lhs, const RepliedMessageInfo &rhs);

View File

@ -24,6 +24,7 @@ void RepliedMessageInfo::store(StorerT &storer) const {
bool has_origin = !origin_.is_empty(); bool has_origin = !origin_.is_empty();
bool has_quote = !quote_.text.empty(); bool has_quote = !quote_.text.empty();
bool has_content = content_ != nullptr; bool has_content = content_ != nullptr;
bool has_quote_position = quote_position_ != 0;
BEGIN_STORE_FLAGS(); BEGIN_STORE_FLAGS();
STORE_FLAG(has_message_id); STORE_FLAG(has_message_id);
STORE_FLAG(has_dialog_id); STORE_FLAG(has_dialog_id);
@ -32,6 +33,7 @@ void RepliedMessageInfo::store(StorerT &storer) const {
STORE_FLAG(has_quote); STORE_FLAG(has_quote);
STORE_FLAG(is_quote_manual_); STORE_FLAG(is_quote_manual_);
STORE_FLAG(has_content); STORE_FLAG(has_content);
STORE_FLAG(has_quote_position);
END_STORE_FLAGS(); END_STORE_FLAGS();
if (has_message_id) { if (has_message_id) {
td::store(message_id_, storer); td::store(message_id_, storer);
@ -51,6 +53,9 @@ void RepliedMessageInfo::store(StorerT &storer) const {
if (has_content) { if (has_content) {
store_message_content(content_.get(), storer); store_message_content(content_.get(), storer);
} }
if (has_quote_position) {
td::store(quote_position_, storer);
}
} }
template <class ParserT> template <class ParserT>
@ -61,6 +66,7 @@ void RepliedMessageInfo::parse(ParserT &parser) {
bool has_origin; bool has_origin;
bool has_quote; bool has_quote;
bool has_content; bool has_content;
bool has_quote_position;
BEGIN_PARSE_FLAGS(); BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_message_id); PARSE_FLAG(has_message_id);
PARSE_FLAG(has_dialog_id); PARSE_FLAG(has_dialog_id);
@ -69,6 +75,7 @@ void RepliedMessageInfo::parse(ParserT &parser) {
PARSE_FLAG(has_quote); PARSE_FLAG(has_quote);
PARSE_FLAG(is_quote_manual_); PARSE_FLAG(is_quote_manual_);
PARSE_FLAG(has_content); PARSE_FLAG(has_content);
PARSE_FLAG(has_quote_position);
END_PARSE_FLAGS(); END_PARSE_FLAGS();
if (has_message_id) { if (has_message_id) {
td::parse(message_id_, parser); td::parse(message_id_, parser);
@ -89,6 +96,9 @@ void RepliedMessageInfo::parse(ParserT &parser) {
if (has_content) { if (has_content) {
parse_message_content(content_, parser); parse_message_content(content_, parser);
} }
if (has_quote_position) {
td::parse(quote_position_, parser);
}
} }
} // namespace td } // namespace td

View File

@ -5915,7 +5915,7 @@ void Td::on_request(uint64 id, td_api::forwardMessages &request) {
void Td::on_request(uint64 id, td_api::resendMessages &request) { void Td::on_request(uint64 id, td_api::resendMessages &request) {
DialogId dialog_id(request.chat_id_); DialogId dialog_id(request.chat_id_);
auto r_message_ids = messages_manager_->resend_messages(dialog_id, MessageId::get_message_ids(request.message_ids_), auto r_message_ids = messages_manager_->resend_messages(dialog_id, MessageId::get_message_ids(request.message_ids_),
std::move(request.quote_)); std::move(request.quote_), request.quote_position_);
if (r_message_ids.is_error()) { if (r_message_ids.is_error()) {
return send_closure(actor_id(this), &Td::send_error, id, r_message_ids.move_as_error()); return send_closure(actor_id(this), &Td::send_error, id, r_message_ids.move_as_error());
} }

View File

@ -925,8 +925,8 @@ class CliClient final : public Actor {
td_api::object_ptr<td_api::InputMessageReplyTo> get_input_message_reply_to() const { td_api::object_ptr<td_api::InputMessageReplyTo> get_input_message_reply_to() const {
if (reply_message_id_ != 0) { if (reply_message_id_ != 0) {
return td_api::make_object<td_api::inputMessageReplyToMessage>(reply_chat_id_, reply_message_id_, return td_api::make_object<td_api::inputMessageReplyToMessage>(
as_formatted_text(reply_quote_)); reply_chat_id_, reply_message_id_, as_formatted_text(reply_quote_), reply_quote_position_);
} }
if (reply_user_id_ != 0 || reply_story_id_ != 0) { if (reply_user_id_ != 0 || reply_story_id_ != 0) {
return td_api::make_object<td_api::inputMessageReplyToStory>(reply_user_id_, reply_story_id_); return td_api::make_object<td_api::inputMessageReplyToStory>(reply_user_id_, reply_story_id_);
@ -3722,9 +3722,10 @@ class CliClient final : public Actor {
ChatId chat_id; ChatId chat_id;
string message_ids; string message_ids;
string quote; string quote;
get_args(args, chat_id, message_ids, quote); int32 quote_position;
send_request( get_args(args, chat_id, message_ids, quote, quote_position);
td_api::make_object<td_api::resendMessages>(chat_id, as_message_ids(message_ids), as_formatted_text(quote))); send_request(td_api::make_object<td_api::resendMessages>(chat_id, as_message_ids(message_ids),
as_formatted_text(quote), quote_position));
} else if (op == "csc" || op == "CreateSecretChat") { } else if (op == "csc" || op == "CreateSecretChat") {
send_request(td_api::make_object<td_api::createSecretChat>(as_secret_chat_id(args))); send_request(td_api::make_object<td_api::createSecretChat>(as_secret_chat_id(args)));
} else if (op == "cnsc" || op == "CreateNewSecretChat") { } else if (op == "cnsc" || op == "CreateNewSecretChat") {
@ -4506,6 +4507,8 @@ class CliClient final : public Actor {
get_args(args, reply_message_id_, reply_chat_id_); get_args(args, reply_message_id_, reply_chat_id_);
} else if (op == "smrq") { } else if (op == "smrq") {
reply_quote_ = args; reply_quote_ = args;
} else if (op == "smrqp") {
reply_quote_position_ = to_integer<int32>(args);
} else if (op == "smrs") { } else if (op == "smrs") {
get_args(args, reply_user_id_, reply_story_id_); get_args(args, reply_user_id_, reply_story_id_);
} else if (op == "slpo") { } else if (op == "slpo") {
@ -6297,6 +6300,7 @@ class CliClient final : public Actor {
ChatId reply_chat_id_; ChatId reply_chat_id_;
MessageId reply_message_id_; MessageId reply_message_id_;
string reply_quote_; string reply_quote_;
int32 reply_quote_position_ = 0;
UserId reply_user_id_; UserId reply_user_id_;
StoryId reply_story_id_; StoryId reply_story_id_;
string link_preview_url_; string link_preview_url_;