Add inputMessageReplyToMessage.quote.

This commit is contained in:
levlam 2023-10-27 13:06:38 +03:00
parent d43fd3beb3
commit ec231f0c99
9 changed files with 133 additions and 75 deletions

View File

@ -1246,7 +1246,8 @@ messageReplyToStory story_sender_chat_id:int53 story_id:int32 = MessageReplyTo;
//@description Describes a message to be replied //@description Describes a message to be replied
//@message_id The identifier of the message to be replied in the same chat //@message_id The identifier of the message to be replied in the same chat
inputMessageReplyToMessage message_id:int53 = InputMessageReplyTo; //@quote Manually chosen quote from the replied message; pass null if none; 0-getOption("message_reply_quote_length_max") characters
inputMessageReplyToMessage message_id:int53 quote:formattedText = 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;

View File

@ -49,7 +49,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); message_input_reply_to_ = MessageInputReplyTo(legacy_reply_to_message_id, FormattedText());
} }
if (has_input_message_text) { if (has_input_message_text) {
td::parse(input_message_text_, parser); td::parse(input_message_text_, parser);

View File

@ -2468,12 +2468,8 @@ static Result<InputMessageContent> create_input_message_content(
if (correct_option_id < 0 || correct_option_id >= static_cast<int32>(input_poll->options_.size())) { if (correct_option_id < 0 || correct_option_id >= static_cast<int32>(input_poll->options_.size())) {
return Status::Error(400, "Wrong correct option ID specified"); return Status::Error(400, "Wrong correct option ID specified");
} }
auto r_explanation = TRY_RESULT_ASSIGN(
get_formatted_text(td, dialog_id, std::move(type->explanation_), is_bot, true, true, false); explanation, get_formatted_text(td, dialog_id, std::move(type->explanation_), is_bot, true, true, false));
if (r_explanation.is_error()) {
return r_explanation.move_as_error();
}
explanation = r_explanation.move_as_ok();
break; break;
} }
default: default:

View File

@ -10,6 +10,7 @@
#include "td/telegram/DialogId.h" #include "td/telegram/DialogId.h"
#include "td/telegram/InputDialogId.h" #include "td/telegram/InputDialogId.h"
#include "td/telegram/MessagesManager.h" #include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/StoryId.h" #include "td/telegram/StoryId.h"
#include "td/telegram/Td.h" #include "td/telegram/Td.h"
@ -52,8 +53,20 @@ MessageInputReplyTo::MessageInputReplyTo(Td *td,
return; return;
} }
} }
// TODO quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity>
message_id_ = message_id; message_id_ = message_id;
if (!reply_to->quote_text_.empty()) {
auto entities = get_message_entities(td->contacts_manager_.get(), std::move(reply_to->quote_entities_),
"inputReplyToMessage");
auto status = fix_formatted_text(reply_to->quote_text_, entities, true, true, true, true, false);
if (status.is_error()) {
if (!clean_input_string(reply_to->quote_text_)) {
reply_to->quote_text_.clear();
}
entities.clear();
}
quote_ = FormattedText{std::move(reply_to->quote_text_), std::move(entities)};
}
break; break;
} }
default: default:
@ -87,9 +100,16 @@ telegram_api::object_ptr<telegram_api::InputReplyTo> MessageInputReplyTo::get_in
CHECK(top_thread_message_id.is_server()); CHECK(top_thread_message_id.is_server());
flags |= telegram_api::inputReplyToMessage::TOP_MSG_ID_MASK; flags |= telegram_api::inputReplyToMessage::TOP_MSG_ID_MASK;
} }
if (!quote_.text.empty()) {
flags |= telegram_api::inputReplyToMessage::QUOTE_TEXT_MASK;
}
auto quote_entities = get_input_message_entities(td->contacts_manager_.get(), quote_.entities, "get_input_reply_to");
if (!quote_entities.empty()) {
flags |= telegram_api::inputReplyToMessage::QUOTE_ENTITIES_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(),
nullptr, string(), Auto()); nullptr, quote_.text, std::move(quote_entities));
} }
td_api::object_ptr<td_api::InputMessageReplyTo> MessageInputReplyTo::get_input_message_reply_to_object( td_api::object_ptr<td_api::InputMessageReplyTo> MessageInputReplyTo::get_input_message_reply_to_object(
@ -103,7 +123,11 @@ td_api::object_ptr<td_api::InputMessageReplyTo> MessageInputReplyTo::get_input_m
if (!message_id_.is_valid() && !message_id_.is_valid_scheduled()) { if (!message_id_.is_valid() && !message_id_.is_valid_scheduled()) {
return nullptr; return nullptr;
} }
return td_api::make_object<td_api::inputMessageReplyToMessage>(message_id_.get()); td_api::object_ptr<td_api::formattedText> quote;
if (!quote_.text.empty()) {
quote = get_formatted_text_object(quote_, true, -1);
}
return td_api::make_object<td_api::inputMessageReplyToMessage>(message_id_.get(), std::move(quote));
} }
MessageId MessageInputReplyTo::get_same_chat_reply_to_message_id() const { MessageId MessageInputReplyTo::get_same_chat_reply_to_message_id() const {
@ -118,7 +142,7 @@ 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.story_full_id_ == rhs.story_full_id_; return lhs.message_id_ == rhs.message_id_ && lhs.story_full_id_ == rhs.story_full_id_ && lhs.quote_ == rhs.quote_;
} }
bool operator!=(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) { bool operator!=(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) {
@ -127,7 +151,11 @@ bool operator!=(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs)
StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReplyTo &input_reply_to) { StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReplyTo &input_reply_to) {
if (input_reply_to.message_id_.is_valid() || input_reply_to.message_id_.is_valid_scheduled()) { if (input_reply_to.message_id_.is_valid() || input_reply_to.message_id_.is_valid_scheduled()) {
return string_builder << input_reply_to.message_id_; string_builder << input_reply_to.message_id_;
if (!input_reply_to.quote_.text.empty()) {
string_builder << " with " << input_reply_to.quote_.text.size() << " quoted bytes";
}
return string_builder;
} }
if (input_reply_to.story_full_id_.is_valid()) { if (input_reply_to.story_full_id_.is_valid()) {
return string_builder << input_reply_to.story_full_id_; return string_builder << input_reply_to.story_full_id_;

View File

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "td/telegram/DialogId.h" #include "td/telegram/DialogId.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessageFullId.h" #include "td/telegram/MessageFullId.h"
#include "td/telegram/MessageId.h" #include "td/telegram/MessageId.h"
#include "td/telegram/StoryFullId.h" #include "td/telegram/StoryFullId.h"
@ -22,6 +23,7 @@ class Td;
class MessageInputReplyTo { class MessageInputReplyTo {
MessageId message_id_; MessageId message_id_;
FormattedText quote_;
// or // or
StoryFullId story_full_id_; StoryFullId story_full_id_;
@ -39,7 +41,7 @@ class MessageInputReplyTo {
MessageInputReplyTo &operator=(MessageInputReplyTo &&) = default; MessageInputReplyTo &operator=(MessageInputReplyTo &&) = default;
~MessageInputReplyTo(); ~MessageInputReplyTo();
explicit MessageInputReplyTo(MessageId message_id) : message_id_(message_id) { MessageInputReplyTo(MessageId message_id, FormattedText &&quote) : message_id_(message_id), quote_(std::move(quote)) {
} }
explicit MessageInputReplyTo(StoryFullId story_full_id) : story_full_id_(story_full_id) { explicit MessageInputReplyTo(StoryFullId story_full_id) : story_full_id_(story_full_id) {
@ -56,7 +58,7 @@ class MessageInputReplyTo {
} }
bool is_same_chat_reply() const { bool is_same_chat_reply() const {
return message_id_.is_valid(); return message_id_.is_valid() || message_id_.is_valid_scheduled();
} }
StoryFullId get_story_full_id() const { StoryFullId get_story_full_id() const {

View File

@ -18,9 +18,11 @@ template <class StorerT>
void MessageInputReplyTo::store(StorerT &storer) const { void MessageInputReplyTo::store(StorerT &storer) const {
bool has_message_id = message_id_.is_valid(); bool has_message_id = message_id_.is_valid();
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();
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);
END_STORE_FLAGS(); END_STORE_FLAGS();
if (has_message_id) { if (has_message_id) {
td::store(message_id_, storer); td::store(message_id_, storer);
@ -28,15 +30,20 @@ void MessageInputReplyTo::store(StorerT &storer) const {
if (has_story_full_id) { if (has_story_full_id) {
td::store(story_full_id_, storer); td::store(story_full_id_, storer);
} }
if (has_quote) {
td::store(quote_, storer);
}
} }
template <class ParserT> template <class ParserT>
void MessageInputReplyTo::parse(ParserT &parser) { void MessageInputReplyTo::parse(ParserT &parser) {
bool has_message_id = message_id_.is_valid(); bool has_message_id;
bool has_story_full_id = story_full_id_.is_valid(); bool has_story_full_id;
bool has_quote;
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);
END_PARSE_FLAGS(); END_PARSE_FLAGS();
if (has_message_id) { if (has_message_id) {
td::parse(message_id_, parser); td::parse(message_id_, parser);
@ -44,6 +51,9 @@ void MessageInputReplyTo::parse(ParserT &parser) {
if (has_story_full_id) { if (has_story_full_id) {
td::parse(story_full_id_, parser); td::parse(story_full_id_, parser);
} }
if (has_quote) {
td::parse(quote_, parser);
}
} }
} // namespace td } // namespace td

View File

@ -5168,7 +5168,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); input_reply_to = MessageInputReplyTo{legacy_reply_to_message_id, FormattedText()};
} }
} }
if (has_replied_message_info) { if (has_replied_message_info) {
@ -24489,57 +24489,74 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
!have_message_force(d, top_thread_message_id, "get_message_input_reply_to 1")) { !have_message_force(d, top_thread_message_id, "get_message_input_reply_to 1")) {
LOG(INFO) << "Have reply in the thread of unknown " << top_thread_message_id; LOG(INFO) << "Have reply in the thread of unknown " << top_thread_message_id;
} }
if (reply_to != nullptr && reply_to->get_id() == td_api::inputMessageReplyToStory::ID) { if (reply_to == nullptr) {
if (for_draft) {
return {};
}
auto reply_to_story = td_api::move_object_as<td_api::inputMessageReplyToStory>(reply_to);
auto story_id = StoryId(reply_to_story->story_id_);
auto sender_dialog_id = DialogId(reply_to_story->story_sender_chat_id_);
if (d->dialog_id != sender_dialog_id || sender_dialog_id.get_type() != DialogType::User) {
LOG(INFO) << "Ignore reply to story from " << sender_dialog_id << " in a wrong " << d->dialog_id;
return {};
}
if (!story_id.is_server()) {
LOG(INFO) << "Ignore reply to invalid " << story_id;
return {};
}
return MessageInputReplyTo{StoryFullId(sender_dialog_id, story_id)};
}
MessageId message_id;
if (reply_to != nullptr && reply_to->get_id() == td_api::inputMessageReplyToMessage::ID) {
auto reply_to_message = td_api::move_object_as<td_api::inputMessageReplyToMessage>(reply_to);
message_id = MessageId(reply_to_message->message_id_);
}
if (!message_id.is_valid()) {
if (!for_draft && message_id == MessageId() && top_thread_message_id.is_valid() &&
top_thread_message_id.is_server()) {
return MessageInputReplyTo{top_thread_message_id};
}
return {};
}
message_id = get_persistent_message_id(d, message_id);
if (message_id == MessageId(ServerMessageId(1)) && d->dialog_id.get_type() == DialogType::Channel) {
return {};
}
const Message *m = get_message_force(d, message_id, "get_message_input_reply_to 2");
if (m == nullptr || m->message_id.is_yet_unsent() ||
(m->message_id.is_local() && d->dialog_id.get_type() != DialogType::SecretChat)) {
if (message_id.is_server() && d->dialog_id.get_type() != DialogType::SecretChat &&
message_id > d->last_new_message_id &&
(d->notification_info != nullptr && message_id <= d->notification_info->max_push_notification_message_id_)) {
// allow to reply yet unreceived server message
return MessageInputReplyTo{message_id};
}
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}; return MessageInputReplyTo{top_thread_message_id, FormattedText()};
} }
// TODO local replies to local messages can be allowed
// TODO replies to yet unsent messages can be allowed with special handling of them on application restart
return {}; return {};
} }
return MessageInputReplyTo{m->message_id}; switch (reply_to->get_id()) {
case td_api::inputMessageReplyToStory::ID: {
if (for_draft) {
return {};
}
auto reply_to_story = td_api::move_object_as<td_api::inputMessageReplyToStory>(reply_to);
auto story_id = StoryId(reply_to_story->story_id_);
auto sender_dialog_id = DialogId(reply_to_story->story_sender_chat_id_);
if (d->dialog_id != sender_dialog_id || sender_dialog_id.get_type() != DialogType::User) {
LOG(INFO) << "Ignore reply to story from " << sender_dialog_id << " in a wrong " << d->dialog_id;
return {};
}
if (!story_id.is_server()) {
LOG(INFO) << "Ignore reply to invalid " << story_id;
return {};
}
return MessageInputReplyTo{StoryFullId(sender_dialog_id, story_id)};
}
case td_api::inputMessageReplyToMessage::ID: {
auto reply_to_message = td_api::move_object_as<td_api::inputMessageReplyToMessage>(reply_to);
auto message_id = MessageId(reply_to_message->message_id_);
if (!message_id.is_valid()) {
if (!for_draft && message_id == MessageId() && top_thread_message_id.is_valid() &&
top_thread_message_id.is_server()) {
return MessageInputReplyTo{top_thread_message_id, FormattedText()};
}
return {};
}
message_id = get_persistent_message_id(d, message_id);
if (message_id == MessageId(ServerMessageId(1)) && d->dialog_id.get_type() == DialogType::Channel) {
return {};
}
FormattedText 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);
if (r_quote.is_ok()) {
quote = r_quote.move_as_ok();
}
const Message *m = get_message_force(d, message_id, "get_message_input_reply_to 2");
if (m == nullptr || m->message_id.is_yet_unsent() ||
(m->message_id.is_local() && d->dialog_id.get_type() != DialogType::SecretChat)) {
if (message_id.is_server() && d->dialog_id.get_type() != DialogType::SecretChat &&
message_id > d->last_new_message_id &&
(d->notification_info != nullptr &&
message_id <= d->notification_info->max_push_notification_message_id_)) {
// allow to reply yet unreceived server message
return MessageInputReplyTo{message_id, std::move(quote)};
}
if (!for_draft && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) {
return MessageInputReplyTo{top_thread_message_id, FormattedText()};
}
// TODO local replies to local messages can be allowed
// TODO replies to yet unsent messages can be allowed with special handling of them on application restart
return {};
}
return MessageInputReplyTo{m->message_id, std::move(quote)};
}
default:
UNREACHABLE();
return {};
}
} }
const MessageInputReplyTo *MessagesManager::get_message_input_reply_to(const Message *m) { const MessageInputReplyTo *MessagesManager::get_message_input_reply_to(const Message *m) {
@ -28097,9 +28114,9 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
Message *m; Message *m;
if (message_send_options.only_preview) { if (message_send_options.only_preview) {
message = create_message_to_send( message = create_message_to_send(
to_dialog, top_thread_message_id, MessageInputReplyTo{reply_to_message_id}, message_send_options, to_dialog, top_thread_message_id, MessageInputReplyTo{reply_to_message_id, FormattedText()},
std::move(content), forwarded_message_contents[j].invert_media, j + 1 != forwarded_message_contents.size(), message_send_options, std::move(content), forwarded_message_contents[j].invert_media,
std::move(forward_info), false, DialogId()); j + 1 != forwarded_message_contents.size(), std::move(forward_info), false, DialogId());
MessageId new_message_id = MessageId new_message_id =
message_send_options.schedule_date != 0 message_send_options.schedule_date != 0
? get_next_yet_unsent_scheduled_message_id(to_dialog, message_send_options.schedule_date) ? get_next_yet_unsent_scheduled_message_id(to_dialog, message_send_options.schedule_date)
@ -28107,10 +28124,10 @@ Result<td_api::object_ptr<td_api::messages>> MessagesManager::forward_messages(
message->message_id = new_message_id; message->message_id = new_message_id;
m = message.get(); m = message.get();
} else { } else {
m = get_message_to_send(to_dialog, top_thread_message_id, MessageInputReplyTo{reply_to_message_id}, m = get_message_to_send(to_dialog, top_thread_message_id,
message_send_options, std::move(content), forwarded_message_contents[j].invert_media, MessageInputReplyTo{reply_to_message_id, FormattedText()}, message_send_options,
&need_update_dialog_pos, j + 1 != forwarded_message_contents.size(), std::move(content), forwarded_message_contents[j].invert_media, &need_update_dialog_pos,
std::move(forward_info)); j + 1 != forwarded_message_contents.size(), std::move(forward_info));
} }
fix_forwarded_message(m, to_dialog_id, forwarded_message, forwarded_message_contents[j].media_album_id, fix_forwarded_message(m, to_dialog_id, forwarded_message, forwarded_message_contents[j].media_album_id,
drop_author); drop_author);
@ -28153,7 +28170,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}; input_reply_to = MessageInputReplyTo{it->second, FormattedText()};
} }
} }
@ -39152,7 +39169,7 @@ void MessagesManager::set_message_reply(const Dialog *d, Message *m, MessageId r
} }
} }
if (!m->message_id.is_server()) { if (!m->message_id.is_server()) {
m->input_reply_to = MessageInputReplyTo(reply_to_message_id); m->input_reply_to = MessageInputReplyTo{reply_to_message_id, FormattedText()};
} }
if (is_message_in_dialog) { if (is_message_in_dialog) {
register_message_reply(d->dialog_id, m); register_message_reply(d->dialog_id, m);

View File

@ -155,6 +155,10 @@ RepliedMessageInfo::RepliedMessageInfo(Td *td, const MessageInputReplyTo &input_
return; return;
} }
message_id_ = input_reply_to.message_id_; message_id_ = input_reply_to.message_id_;
if (!input_reply_to.quote_.text.empty()) {
quote_ = input_reply_to.quote_;
is_quote_manual_ = true;
}
} }
bool RepliedMessageInfo::need_reget() const { bool RepliedMessageInfo::need_reget() const {

View File

@ -942,7 +942,7 @@ class CliClient final : public Actor {
return nullptr; return nullptr;
} }
if (message_id != 0) { if (message_id != 0) {
return td_api::make_object<td_api::inputMessageReplyToMessage>(message_id); return td_api::make_object<td_api::inputMessageReplyToMessage>(message_id, nullptr);
} else { } else {
return td_api::make_object<td_api::inputMessageReplyToStory>(user_id, story_id); return td_api::make_object<td_api::inputMessageReplyToStory>(user_id, story_id);
} }