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
//@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_position Approximate quote position in UTF-16 code units
//@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_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.
//-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
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
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
//@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
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
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
//@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
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
//@chat_id Target chat

View File

@ -53,7 +53,7 @@ void DraftMessage::parse(ParserT &parser) {
if (has_legacy_reply_to_message_id) {
MessageId legacy_reply_to_message_id;
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) {
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)};
remove_unallowed_quote_entities(quote_);
quote_position_ = max(0, reply_to->quote_offset_);
}
break;
}
@ -129,9 +130,12 @@ telegram_api::object_ptr<telegram_api::InputReplyTo> MessageInputReplyTo::get_in
if (!quote_entities.empty()) {
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>(
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
@ -150,7 +154,7 @@ td_api::object_ptr<td_api::InputMessageReplyTo> MessageInputReplyTo::get_input_m
}
return td_api::make_object<td_api::inputMessageReplyToMessage>(
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 {
@ -167,7 +171,8 @@ MessageFullId MessageInputReplyTo::get_reply_message_full_id(DialogId owner_dial
bool operator==(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) {
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) {
@ -182,6 +187,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReply
}
if (!input_reply_to.quote_.text.empty()) {
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;
}

View File

@ -27,6 +27,7 @@ class MessageInputReplyTo {
MessageId message_id_;
DialogId dialog_id_;
FormattedText quote_;
int32 quote_position_ = 0;
// or
StoryFullId story_full_id_;
@ -44,8 +45,11 @@ class MessageInputReplyTo {
MessageInputReplyTo &operator=(MessageInputReplyTo &&) = default;
~MessageInputReplyTo();
MessageInputReplyTo(MessageId message_id, DialogId dialog_id, FormattedText &&quote)
: message_id_(message_id), dialog_id_(dialog_id), quote_(std::move(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))
, quote_position_(max(0, quote_position)) {
remove_unallowed_quote_entities(quote_);
}
@ -66,8 +70,9 @@ class MessageInputReplyTo {
return !quote_.text.empty();
}
void set_quote(FormattedText &&quote) {
void set_quote(FormattedText &&quote, int32 quote_position) {
quote_ = std::move(quote);
quote_position_ = max(0, quote_position);
}
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_quote = !quote_.text.empty();
bool has_dialog_id = dialog_id_.is_valid();
bool has_quote_position = quote_position_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_message_id);
STORE_FLAG(has_story_full_id);
STORE_FLAG(has_quote);
STORE_FLAG(has_dialog_id);
STORE_FLAG(has_quote_position);
END_STORE_FLAGS();
if (has_message_id) {
td::store(message_id_, storer);
@ -38,6 +40,9 @@ void MessageInputReplyTo::store(StorerT &storer) const {
if (has_dialog_id) {
td::store(dialog_id_, storer);
}
if (has_quote_position) {
td::store(quote_position_, storer);
}
}
template <class ParserT>
@ -46,11 +51,13 @@ void MessageInputReplyTo::parse(ParserT &parser) {
bool has_story_full_id;
bool has_quote;
bool has_dialog_id;
bool has_quote_position;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_message_id);
PARSE_FLAG(has_story_full_id);
PARSE_FLAG(has_quote);
PARSE_FLAG(has_dialog_id);
PARSE_FLAG(has_quote_position);
END_PARSE_FLAGS();
if (has_message_id) {
td::parse(message_id_, parser);
@ -65,6 +72,9 @@ void MessageInputReplyTo::parse(ParserT &parser) {
if (has_dialog_id) {
td::parse(dialog_id_, parser);
}
if (has_quote_position) {
td::parse(quote_position_, parser);
}
}
} // namespace td

View File

@ -5180,7 +5180,7 @@ void MessagesManager::Message::parse(ParserT &parser) {
if (reply_to_story_full_id.is_valid()) {
input_reply_to = MessageInputReplyTo(reply_to_story_full_id);
} 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) {
@ -24518,7 +24518,7 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
}
if (reply_to == nullptr) {
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 {};
}
@ -24546,7 +24546,7 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
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, DialogId(), FormattedText()};
return MessageInputReplyTo{top_thread_message_id, DialogId(), FormattedText(), 0};
}
return {};
}
@ -24566,10 +24566,12 @@ MessageInputReplyTo MessagesManager::get_message_input_reply_to(
return {};
}
FormattedText quote;
int32 quote_position = 0;
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() && d->dialog_id.get_type() != DialogType::SecretChat) {
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");
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 &&
message_id <= reply_d->notification_info->max_push_notification_message_id_)) {
// 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()) {
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;
@ -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;
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:
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->get_reply_message_full_id(reply_d->dialog_id) == MessageFullId(dialog_id, m->message_id));
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);
}
@ -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) {
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()) {
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,
td_api::object_ptr<td_api::formattedText> &&quote) {
td_api::object_ptr<td_api::formattedText> &&quote,
int32 quote_position) {
if (message_ids.empty()) {
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 =
get_formatted_text(td_, get_my_dialog_id(), std::move(quote), td_->auth_manager_->is_bot(), true, true, true);
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) {
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()) {
update_message_reply_to_message_id(d, m, message_id, false);
} 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;
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);

View File

@ -150,6 +150,7 @@ RepliedMessageInfo::RepliedMessageInfo(Td *td, tl_object_ptr<telegram_api::messa
entities.clear();
}
quote_ = FormattedText{std::move(reply_header->quote_text_), std::move(entities)};
quote_position_ = max(0, reply_header->quote_offset_);
remove_unallowed_quote_entities(quote_);
}
}
@ -161,6 +162,7 @@ RepliedMessageInfo::RepliedMessageInfo(Td *td, const MessageInputReplyTo &input_
message_id_ = input_reply_to.message_id_;
if (!input_reply_to.quote_.text.empty()) {
quote_ = input_reply_to.quote_;
quote_position_ = input_reply_to.quote_position_;
is_quote_manual_ = true;
}
if (input_reply_to.dialog_id_ != DialogId()) {
@ -206,6 +208,7 @@ RepliedMessageInfo RepliedMessageInfo::clone(Td *td) const {
MessageContentDupType::Forward, MessageCopyOptions());
}
result.quote_ = quote_;
result.quote_position_ = quote_position_;
result.is_quote_manual_ = is_quote_manual_;
return result;
}
@ -227,6 +230,12 @@ bool RepliedMessageInfo::need_reply_changed_warning(
// only signature can change in the message origin
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_) {
// quote manual property can't change
return true;
@ -336,8 +345,12 @@ td_api::object_ptr<td_api::messageReplyToMessage> RepliedMessageInfo::get_messag
}
td_api::object_ptr<td_api::formattedText> quote;
int32 quote_position = 0;
bool is_quote_manual = false;
if (!quote_.text.empty()) {
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;
@ -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),
is_quote_manual_, std::move(origin), origin_date_,
std::move(content));
quote_position, is_quote_manual, std::move(origin),
origin_date_, std::move(content));
}
MessageInputReplyTo RepliedMessageInfo::get_input_reply_to() const {
CHECK(!is_external());
if (message_id_.is_valid()) {
return MessageInputReplyTo{message_id_, dialog_id_, FormattedText{quote_}};
return MessageInputReplyTo{message_id_, dialog_id_, FormattedText{quote_}, quote_position_};
}
return {};
}
@ -404,7 +417,7 @@ void RepliedMessageInfo::unregister_content(Td *td) const {
bool operator==(const RepliedMessageInfo &lhs, const RepliedMessageInfo &rhs) {
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.is_quote_manual_ == rhs.is_quote_manual_)) {
lhs.quote_position_ == rhs.quote_position_ && lhs.is_quote_manual_ == rhs.is_quote_manual_)) {
return false;
}
bool need_update = false;
@ -431,6 +444,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const RepliedMessageInf
if (!info.quote_.text.empty()) {
string_builder << " with " << info.quote_.text.size() << (info.is_quote_manual_ ? " manually" : "")
<< " quoted bytes";
if (info.quote_position_ != 0) {
string_builder << " at position " << info.quote_position_;
}
}
if (info.content_ != nullptr) {
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
unique_ptr<MessageContent> content_; // for replies in other chats
FormattedText quote_;
int32 quote_position_ = 0;
bool is_quote_manual_ = false;
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_quote = !quote_.text.empty();
bool has_content = content_ != nullptr;
bool has_quote_position = quote_position_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_message_id);
STORE_FLAG(has_dialog_id);
@ -32,6 +33,7 @@ void RepliedMessageInfo::store(StorerT &storer) const {
STORE_FLAG(has_quote);
STORE_FLAG(is_quote_manual_);
STORE_FLAG(has_content);
STORE_FLAG(has_quote_position);
END_STORE_FLAGS();
if (has_message_id) {
td::store(message_id_, storer);
@ -51,6 +53,9 @@ void RepliedMessageInfo::store(StorerT &storer) const {
if (has_content) {
store_message_content(content_.get(), storer);
}
if (has_quote_position) {
td::store(quote_position_, storer);
}
}
template <class ParserT>
@ -61,6 +66,7 @@ void RepliedMessageInfo::parse(ParserT &parser) {
bool has_origin;
bool has_quote;
bool has_content;
bool has_quote_position;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_message_id);
PARSE_FLAG(has_dialog_id);
@ -69,6 +75,7 @@ void RepliedMessageInfo::parse(ParserT &parser) {
PARSE_FLAG(has_quote);
PARSE_FLAG(is_quote_manual_);
PARSE_FLAG(has_content);
PARSE_FLAG(has_quote_position);
END_PARSE_FLAGS();
if (has_message_id) {
td::parse(message_id_, parser);
@ -89,6 +96,9 @@ void RepliedMessageInfo::parse(ParserT &parser) {
if (has_content) {
parse_message_content(content_, parser);
}
if (has_quote_position) {
td::parse(quote_position_, parser);
}
}
} // 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) {
DialogId dialog_id(request.chat_id_);
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()) {
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 {
if (reply_message_id_ != 0) {
return td_api::make_object<td_api::inputMessageReplyToMessage>(reply_chat_id_, reply_message_id_,
as_formatted_text(reply_quote_));
return td_api::make_object<td_api::inputMessageReplyToMessage>(
reply_chat_id_, reply_message_id_, as_formatted_text(reply_quote_), reply_quote_position_);
}
if (reply_user_id_ != 0 || reply_story_id_ != 0) {
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;
string message_ids;
string quote;
get_args(args, chat_id, message_ids, quote);
send_request(
td_api::make_object<td_api::resendMessages>(chat_id, as_message_ids(message_ids), as_formatted_text(quote)));
int32 quote_position;
get_args(args, chat_id, message_ids, quote, quote_position);
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") {
send_request(td_api::make_object<td_api::createSecretChat>(as_secret_chat_id(args)));
} else if (op == "cnsc" || op == "CreateNewSecretChat") {
@ -4506,6 +4507,8 @@ class CliClient final : public Actor {
get_args(args, reply_message_id_, reply_chat_id_);
} else if (op == "smrq") {
reply_quote_ = args;
} else if (op == "smrqp") {
reply_quote_position_ = to_integer<int32>(args);
} else if (op == "smrs") {
get_args(args, reply_user_id_, reply_story_id_);
} else if (op == "slpo") {
@ -6297,6 +6300,7 @@ class CliClient final : public Actor {
ChatId reply_chat_id_;
MessageId reply_message_id_;
string reply_quote_;
int32 reply_quote_position_ = 0;
UserId reply_user_id_;
StoryId reply_story_id_;
string link_preview_url_;