Edit message media.

GitOrigin-RevId: eba2f32f4e033720ea1143463a9f3d1eae54880d
This commit is contained in:
levlam 2018-06-19 02:31:34 +03:00
parent 744df9f511
commit 4d5197d31c
7 changed files with 507 additions and 81 deletions

View File

@ -2498,32 +2498,40 @@ deleteMessages chat_id:int53 message_ids:vector<int53> revoke:Bool = Ok;
deleteChatMessagesFromUser chat_id:int53 user_id:int32 = Ok; deleteChatMessagesFromUser chat_id:int53 user_id:int32 = Ok;
//@description Edits the text of a message (or a text of a game message). Non-bot users can edit messages for a limited period of time. Returns the edited message after the edit is completed on the server side //@description Edits the text of a message (or a text of a game message). Returns the edited message after the edit is completed on the server side
//@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup; for bots only @input_message_content New text content of the message. Should be of type InputMessageText //@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup; for bots only @input_message_content New text content of the message. Should be of type InputMessageText
editMessageText chat_id:int53 message_id:int53 reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message; editMessageText chat_id:int53 message_id:int53 reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message;
//@description Edits the message content of a live location. Messages can be edited for a limited period of time specified in the live location. Returns the edited message after the edit is completed server-side //@description Edits the message content of a live location. Messages can be edited for a limited period of time specified in the live location. Returns the edited message after the edit is completed server-side
//@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup Tew message reply markup; for bots only @location New location content of the message; may be null. Pass null to stop sharing the live location //@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup; for bots only @location New location content of the message; may be null. Pass null to stop sharing the live location
editMessageLiveLocation chat_id:int53 message_id:int53 reply_markup:ReplyMarkup location:location = Message; editMessageLiveLocation chat_id:int53 message_id:int53 reply_markup:ReplyMarkup location:location = Message;
//@description Edits the message content caption. Non-bots can edit messages for a limited period of time. Returns the edited message after the edit is completed server-side //@description Edits the message content of an animation, an audio, a document, a photo or a video. A message media can't be edited if the message is self-destructed or to self-destructed message. A message in a message album can be edited only to a photo or a video. Returns the edited message after the edit is completed server-side
//@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup; for bots only @input_message_content New content of the message. Must be one of the following types: InputMessageAnimation, InputMessageAudio, InputMessageDocument, InputMessagePhoto or InputMessageVideo
editMessageMedia chat_id:int53 message_id:int53 reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message;
//@description Edits the message content caption. Returns the edited message after the edit is completed server-side
//@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup; for bots only @caption New message content caption; 0-200 characters //@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup; for bots only @caption New message content caption; 0-200 characters
editMessageCaption chat_id:int53 message_id:int53 reply_markup:ReplyMarkup caption:formattedText = Message; editMessageCaption chat_id:int53 message_id:int53 reply_markup:ReplyMarkup caption:formattedText = Message;
//@description Edits the message reply markup; for bots only. Returns the edited message after the edit is completed server-side //@description Edits the message reply markup; for bots only. Returns the edited message after the edit is completed server-side
//@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup New message reply markup //@chat_id The chat the message belongs to @message_id Identifier of the message @reply_markup The new message reply markup
editMessageReplyMarkup chat_id:int53 message_id:int53 reply_markup:ReplyMarkup = Message; editMessageReplyMarkup chat_id:int53 message_id:int53 reply_markup:ReplyMarkup = Message;
//@description Edits the text of an inline text or game message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup New message reply markup @input_message_content New text content of the message. Should be of type InputMessageText //@description Edits the text of an inline text or game message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup The new message reply markup @input_message_content New text content of the message. Should be of type InputMessageText
editInlineMessageText inline_message_id:string reply_markup:ReplyMarkup input_message_content:InputMessageContent = Ok; editInlineMessageText inline_message_id:string reply_markup:ReplyMarkup input_message_content:InputMessageContent = Ok;
//@description Edits the content of a live location in an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup New message reply markup @location New location content of the message; may be null. Pass null to stop sharing the live location //@description Edits the content of a live location in an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup The new message reply markup @location New location content of the message; may be null. Pass null to stop sharing the live location
editInlineMessageLiveLocation inline_message_id:string reply_markup:ReplyMarkup location:location = Ok; editInlineMessageLiveLocation inline_message_id:string reply_markup:ReplyMarkup location:location = Ok;
//@description Edits the caption of an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup New message reply markup @caption New message content caption; 0-200 characters //@description Edits the message content of an animation, an audio, a document, a photo or a video in an inline message sent via a bot; for bots only @inline_message_id Inline message identifier
//@reply_markup The new message reply markup; for bots only @input_message_content New content of the message. Must be one of the following types: InputMessageAnimation, InputMessageAudio, InputMessageDocument, InputMessagePhoto or InputMessageVideo
editInlineMessageMedia inline_message_id:string reply_markup:ReplyMarkup input_message_content:InputMessageContent = Ok;
//@description Edits the caption of an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup The new message reply markup @caption New message content caption; 0-200 characters
editInlineMessageCaption inline_message_id:string reply_markup:ReplyMarkup caption:formattedText = Ok; editInlineMessageCaption inline_message_id:string reply_markup:ReplyMarkup caption:formattedText = Ok;
//@description Edits the reply markup of an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup New message reply markup //@description Edits the reply markup of an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup The new message reply markup
editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup = Ok; editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup = Ok;

Binary file not shown.

View File

@ -1834,6 +1834,7 @@ class SendSecretMessageActor : public NetActor {
int64 random_id) { int64 random_id) {
if (false && !media.empty()) { if (false && !media.empty()) {
td->messages_manager_->on_send_secret_message_error(random_id, Status::Error(400, "FILE_PART_1_MISSING"), Auto()); td->messages_manager_->on_send_secret_message_error(random_id, Status::Error(400, "FILE_PART_1_MISSING"), Auto());
stop();
return; return;
} }
@ -1886,6 +1887,7 @@ class SendMessageActor : public NetActorOnce {
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
if (input_peer == nullptr) { if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Have no write access to the chat")); on_error(0, Status::Error(400, "Have no write access to the chat"));
stop();
return; return;
} }
@ -2061,6 +2063,7 @@ class SendMultiMediaActor : public NetActorOnce {
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
if (input_peer == nullptr) { if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Have no write access to the chat")); on_error(0, Status::Error(400, "Have no write access to the chat"));
stop();
return; return;
} }
@ -2160,6 +2163,7 @@ class SendMediaActor : public NetActorOnce {
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
if (input_peer == nullptr) { if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Have no write access to the chat")); on_error(0, Status::Error(400, "Have no write access to the chat"));
stop();
return; return;
} }
if (!entities.empty()) { if (!entities.empty()) {
@ -2210,6 +2214,10 @@ class SendMediaActor : public NetActorOnce {
return; return;
} }
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendMediaActor"); td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendMediaActor");
if (thumbnail_file_id_.is_valid()) {
// always delete partial remote location for the thumbnail, because it can't be reused anyway
td->file_manager_->delete_partial_remote_location(thumbnail_file_id_);
}
if (file_id_.is_valid()) { if (file_id_.is_valid()) {
if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) { if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) {
td->messages_manager_->on_send_message_file_part_missing(random_id_, td->messages_manager_->on_send_message_file_part_missing(random_id_,
@ -2221,10 +2229,6 @@ class SendMediaActor : public NetActorOnce {
} }
} }
} }
if (thumbnail_file_id_.is_valid()) {
// always delete partial remote location for the thumbnail, because it can't be reused anyway
td->file_manager_->delete_partial_remote_location(thumbnail_file_id_);
}
td->messages_manager_->on_send_message_fail(random_id_, std::move(status)); td->messages_manager_->on_send_message_fail(random_id_, std::move(status));
} }
}; };
@ -2260,6 +2264,11 @@ class UploadMediaQuery : public Td::ResultHandler {
return on_error(id, result_ptr.move_as_error()); return on_error(id, result_ptr.move_as_error());
} }
if (thumbnail_file_id_.is_valid()) {
// always delete partial remote location for the thumbnail, because it can't be reused anyway
td->file_manager_->delete_partial_remote_location(thumbnail_file_id_);
}
auto ptr = result_ptr.move_as_ok(); auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for uploadMedia: " << to_string(ptr); LOG(INFO) << "Receive result for uploadMedia: " << to_string(ptr);
td->messages_manager_->on_upload_message_media_success(dialog_id_, message_id_, std::move(ptr)); td->messages_manager_->on_upload_message_media_success(dialog_id_, message_id_, std::move(ptr));
@ -2272,6 +2281,10 @@ class UploadMediaQuery : public Td::ResultHandler {
return; return;
} }
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "UploadMediaQuery"); td->messages_manager_->on_get_dialog_error(dialog_id_, status, "UploadMediaQuery");
if (thumbnail_file_id_.is_valid()) {
// always delete partial remote location for the thumbnail, because it can't be reused anyway
td->file_manager_->delete_partial_remote_location(thumbnail_file_id_);
}
if (file_id_.is_valid()) { if (file_id_.is_valid()) {
if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) { if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) {
td->messages_manager_->on_upload_message_media_file_part_missing( td->messages_manager_->on_upload_message_media_file_part_missing(
@ -2297,13 +2310,21 @@ class EditMessageActor : public NetActorOnce {
void send(int32 flags, DialogId dialog_id, MessageId message_id, const string &message, void send(int32 flags, DialogId dialog_id, MessageId message_id, const string &message,
vector<tl_object_ptr<telegram_api::MessageEntity>> &&entities, vector<tl_object_ptr<telegram_api::MessageEntity>> &&entities,
tl_object_ptr<telegram_api::InputMedia> &&input_media,
tl_object_ptr<telegram_api::InputGeoPoint> &&input_geo_point, tl_object_ptr<telegram_api::InputGeoPoint> &&input_geo_point,
tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup, uint64 sequence_dispatcher_id) { tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup, uint64 sequence_dispatcher_id) {
if (false && input_media != nullptr) {
on_error(0, Status::Error(400, "FILE_PART_1_MISSING"));
stop();
return;
}
dialog_id_ = dialog_id; dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit);
if (input_peer == nullptr) { if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Can't access the chat")); on_error(0, Status::Error(400, "Can't access the chat"));
stop();
return; return;
} }
@ -2316,6 +2337,9 @@ class EditMessageActor : public NetActorOnce {
if (!message.empty()) { if (!message.empty()) {
flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_MESSAGE; flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_MESSAGE;
} }
if (input_media != nullptr) {
flags |= telegram_api::messages_editMessage::MEDIA_MASK;
}
if (input_geo_point != nullptr) { if (input_geo_point != nullptr) {
flags |= telegram_api::messages_editMessage::GEO_POINT_MASK; flags |= telegram_api::messages_editMessage::GEO_POINT_MASK;
} }
@ -2323,7 +2347,7 @@ class EditMessageActor : public NetActorOnce {
auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_editMessage( auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_editMessage(
flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(),
message, nullptr, std::move(reply_markup), std::move(entities), std::move(input_geo_point)))); message, std::move(input_media), std::move(reply_markup), std::move(entities), std::move(input_geo_point))));
query->debug("send to MessagesManager::MultiSequenceDispatcher"); query->debug("send to MessagesManager::MultiSequenceDispatcher");
send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
@ -2367,6 +2391,7 @@ class EditInlineMessageQuery : public Td::ResultHandler {
void send(int32 flags, tl_object_ptr<telegram_api::inputBotInlineMessageID> input_bot_inline_message_id, void send(int32 flags, tl_object_ptr<telegram_api::inputBotInlineMessageID> input_bot_inline_message_id,
const string &message, vector<tl_object_ptr<telegram_api::MessageEntity>> &&entities, const string &message, vector<tl_object_ptr<telegram_api::MessageEntity>> &&entities,
tl_object_ptr<telegram_api::InputMedia> &&input_media,
tl_object_ptr<telegram_api::InputGeoPoint> &&input_geo_point, tl_object_ptr<telegram_api::InputGeoPoint> &&input_geo_point,
tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup) { tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup) {
CHECK(input_bot_inline_message_id != nullptr); CHECK(input_bot_inline_message_id != nullptr);
@ -2383,13 +2408,16 @@ class EditInlineMessageQuery : public Td::ResultHandler {
if (input_geo_point != nullptr) { if (input_geo_point != nullptr) {
flags |= telegram_api::messages_editInlineBotMessage::GEO_POINT_MASK; flags |= telegram_api::messages_editInlineBotMessage::GEO_POINT_MASK;
} }
if (input_media != nullptr) {
flags |= telegram_api::messages_editInlineBotMessage::MEDIA_MASK;
}
LOG(DEBUG) << "Edit inline message with flags " << flags; LOG(DEBUG) << "Edit inline message with flags " << flags;
auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_); auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_);
send_query(G()->net_query_creator().create( send_query(G()->net_query_creator().create(
create_storer(telegram_api::messages_editInlineBotMessage( create_storer(telegram_api::messages_editInlineBotMessage(
flags, false /*ignored*/, false /*ignored*/, std::move(input_bot_inline_message_id), message, nullptr, flags, false /*ignored*/, false /*ignored*/, std::move(input_bot_inline_message_id), message,
std::move(reply_markup), std::move(entities), std::move(input_geo_point))), std::move(input_media), std::move(reply_markup), std::move(entities), std::move(input_geo_point))),
dc_id)); dc_id));
} }
@ -2433,6 +2461,7 @@ class SetGameScoreActor : public NetActorOnce {
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit);
if (input_peer == nullptr) { if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Can't access the chat")); on_error(0, Status::Error(400, "Can't access the chat"));
stop();
return; return;
} }
@ -2611,12 +2640,14 @@ class ForwardMessagesActor : public NetActorOnce {
auto to_input_peer = td->messages_manager_->get_input_peer(to_dialog_id, AccessRights::Write); auto to_input_peer = td->messages_manager_->get_input_peer(to_dialog_id, AccessRights::Write);
if (to_input_peer == nullptr) { if (to_input_peer == nullptr) {
on_error(0, Status::Error(400, "Have no write access to the chat")); on_error(0, Status::Error(400, "Have no write access to the chat"));
stop();
return; return;
} }
auto from_input_peer = td->messages_manager_->get_input_peer(from_dialog_id, AccessRights::Read); auto from_input_peer = td->messages_manager_->get_input_peer(from_dialog_id, AccessRights::Read);
if (from_input_peer == nullptr) { if (from_input_peer == nullptr) {
on_error(0, Status::Error(400, "Can't access the chat to forward messages from")); on_error(0, Status::Error(400, "Can't access the chat to forward messages from"));
stop();
return; return;
} }
@ -6749,15 +6780,16 @@ void MessagesManager::on_upload_media(FileId file_id, tl_object_ptr<telegram_api
Message *m = get_message(full_message_id); Message *m = get_message(full_message_id);
if (m == nullptr) { if (m == nullptr) {
// message has already been deleted by the user or sent to inaccessible channel, do not need to send it // message has already been deleted by the user or sent to inaccessible channel, do not need to send or edit it
// file upload should be already cancelled in cancel_send_message_query, it shouldn't happen // file upload should be already cancelled in cancel_send_message_query, it shouldn't happen
LOG(ERROR) << "Message with a media has already been deleted"; LOG(ERROR) << "Message with a media has already been deleted";
return; return;
} }
bool is_edit = m->message_id.is_server();
auto dialog_id = full_message_id.get_dialog_id(); auto dialog_id = full_message_id.get_dialog_id();
auto can_send_status = can_send_message(dialog_id); auto can_send_status = can_send_message(dialog_id);
if (can_send_status.is_error()) { if (!is_edit && can_send_status.is_error()) {
// user has left the chat during upload of the file or lost his privileges // user has left the chat during upload of the file or lost his privileges
LOG(INFO) << "Can't send a message to " << dialog_id << ": " << can_send_status.error(); LOG(INFO) << "Can't send a message to " << dialog_id << ": " << can_send_status.error();
@ -6806,8 +6838,20 @@ void MessagesManager::do_send_media(DialogId dialog_id, Message *m, FileId file_
thumbnail_file_id = FileId(); thumbnail_file_id = FileId();
} }
CHECK(m != nullptr); CHECK(m != nullptr);
on_message_media_uploaded(
dialog_id, m, get_input_media(m->content.get(), std::move(input_file), std::move(input_thumbnail), m->ttl), MessageContent *content = nullptr;
if (m->message_id.is_server()) {
content = m->edited_content.get();
if (content == nullptr) {
LOG(ERROR) << "Message has no edited content";
return;
}
} else {
content = m->content.get();
}
on_message_media_uploaded(dialog_id, m,
get_input_media(content, std::move(input_file), std::move(input_thumbnail), m->ttl),
file_id, thumbnail_file_id); file_id, thumbnail_file_id);
} }
@ -6821,6 +6865,7 @@ void MessagesManager::do_send_secret_media(DialogId dialog_id, Message *m, FileI
CHECK(dialog_id.get_type() == DialogType::SecretChat); CHECK(dialog_id.get_type() == DialogType::SecretChat);
CHECK(m != nullptr); CHECK(m != nullptr);
CHECK(m->message_id.is_yet_unsent());
auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id()); auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id());
on_secret_message_media_uploaded( on_secret_message_media_uploaded(
dialog_id, m, dialog_id, m,
@ -6847,8 +6892,13 @@ void MessagesManager::on_upload_media_error(FileId file_id, Status status) {
being_uploaded_files_.erase(it); being_uploaded_files_.erase(it);
bool is_edit = full_message_id.get_message_id().is_server();
if (is_edit) {
fail_edit_message_media(full_message_id, Status::Error(status.code() > 0 ? status.code() : 500, status.message()));
} else {
fail_send_message(full_message_id, status.code() > 0 ? status.code() : 500, fail_send_message(full_message_id, status.code() > 0 ? status.code() : 500,
status.message().str()); // TODO CHECK that status has always a code status.message().str()); // TODO CHECK that status has always a code
}
} }
void MessagesManager::on_load_secret_thumbnail(FileId thumbnail_file_id, BufferSlice thumbnail) { void MessagesManager::on_load_secret_thumbnail(FileId thumbnail_file_id, BufferSlice thumbnail) {
@ -6879,6 +6929,7 @@ void MessagesManager::on_load_secret_thumbnail(FileId thumbnail_file_id, BufferS
LOG(INFO) << "Message with a media has already been deleted"; LOG(INFO) << "Message with a media has already been deleted";
return; return;
} }
CHECK(m->message_id.is_yet_unsent());
if (thumbnail.empty()) { if (thumbnail.empty()) {
delete_message_content_thumbnail(m->content.get()); delete_message_content_thumbnail(m->content.get());
@ -6921,19 +6972,21 @@ void MessagesManager::on_upload_thumbnail(FileId thumbnail_file_id,
Message *m = get_message(full_message_id); Message *m = get_message(full_message_id);
if (m == nullptr) { if (m == nullptr) {
// message has already been deleted by the user or sent to inaccessible channel, do not need to send it // message has already been deleted by the user or sent to inaccessible channel, do not need to send or edit it
// thumbnail file upload should be already cancelled in cancel_send_message_query // thumbnail file upload should be already cancelled in cancel_send_message_query
LOG(ERROR) << "Message with a media has already been deleted"; LOG(ERROR) << "Message with a media has already been deleted";
return; return;
} }
bool is_edit = m->message_id.is_server();
if (thumbnail_input_file == nullptr) { if (thumbnail_input_file == nullptr) {
delete_message_content_thumbnail(m->content.get()); delete_message_content_thumbnail(is_edit ? m->edited_content.get() : m->content.get());
} }
auto dialog_id = full_message_id.get_dialog_id(); auto dialog_id = full_message_id.get_dialog_id();
auto can_send_status = can_send_message(dialog_id); auto can_send_status = can_send_message(dialog_id);
if (can_send_status.is_error()) { if (!is_edit && can_send_status.is_error()) {
// user has left the chat during upload of the thumbnail or lost his privileges // user has left the chat during upload of the thumbnail or lost his privileges
LOG(INFO) << "Can't send a message to " << dialog_id << ": " << can_send_status.error(); LOG(INFO) << "Can't send a message to " << dialog_id << ": " << can_send_status.error();
@ -11211,11 +11264,11 @@ bool MessagesManager::can_unload_message(const Dialog *d, const Message *m) cons
// don't want to unload messages from opened dialogs // don't want to unload messages from opened dialogs
// don't want to unload messages to which there are replies in yet unsent messages // don't want to unload messages to which there are replies in yet unsent messages
// don't want to unload messages with pending web pages // don't want to unload messages with pending web pages
// can't unload from memory last dialog, last database messages, yet unsent messages and active live locations // can't unload from memory last dialog, last database messages, yet unsent messages, being edited media messages and active live locations
FullMessageId full_message_id{d->dialog_id, m->message_id}; FullMessageId full_message_id{d->dialog_id, m->message_id};
return !d->is_opened && m->message_id != d->last_message_id && m->message_id != d->last_database_message_id && return !d->is_opened && m->message_id != d->last_message_id && m->message_id != d->last_database_message_id &&
!m->message_id.is_yet_unsent() && active_live_location_full_message_ids_.count(full_message_id) == 0 && !m->message_id.is_yet_unsent() && active_live_location_full_message_ids_.count(full_message_id) == 0 &&
replied_by_yet_unsent_messages_.count(full_message_id) == 0 && replied_by_yet_unsent_messages_.count(full_message_id) == 0 && m->edited_content == nullptr &&
waiting_for_web_page_messages_.count(full_message_id) == 0; waiting_for_web_page_messages_.count(full_message_id) == 0;
} }
@ -11406,6 +11459,8 @@ unique_ptr<MessagesManager::Message> MessagesManager::do_delete_message(Dialog *
if (!only_from_memory) { if (!only_from_memory) {
if (message_id.is_yet_unsent()) { if (message_id.is_yet_unsent()) {
cancel_send_message_query(d->dialog_id, result); cancel_send_message_query(d->dialog_id, result);
} else {
cancel_edit_message_media(d->dialog_id, result.get());
} }
if (need_get_history && !td_->auth_manager_->is_bot() && have_input_peer(d->dialog_id, AccessRights::Read)) { if (need_get_history && !td_->auth_manager_->is_bot() && have_input_peer(d->dialog_id, AccessRights::Read)) {
@ -11492,6 +11547,8 @@ void MessagesManager::do_delete_all_dialog_messages(Dialog *d, unique_ptr<Messag
if (message_id.is_yet_unsent()) { if (message_id.is_yet_unsent()) {
cancel_send_message_query(d->dialog_id, m); cancel_send_message_query(d->dialog_id, m);
} else {
cancel_edit_message_media(d->dialog_id, m.get());
} }
switch (d->dialog_id.get_type()) { switch (d->dialog_id.get_type()) {
@ -15505,6 +15562,7 @@ MessagesManager::Message *MessagesManager::get_message_to_send(Dialog *d, Messag
int64 MessagesManager::begin_send_message(DialogId dialog_id, const Message *m) { int64 MessagesManager::begin_send_message(DialogId dialog_id, const Message *m) {
LOG(INFO) << "Begin to send " << FullMessageId(dialog_id, m->message_id) << " with random_id = " << m->random_id; LOG(INFO) << "Begin to send " << FullMessageId(dialog_id, m->message_id) << " with random_id = " << m->random_id;
CHECK(m->random_id != 0 && being_sent_messages_.find(m->random_id) == being_sent_messages_.end()); CHECK(m->random_id != 0 && being_sent_messages_.find(m->random_id) == being_sent_messages_.end());
CHECK(m->message_id.is_yet_unsent());
being_sent_messages_[m->random_id] = FullMessageId(dialog_id, m->message_id); being_sent_messages_[m->random_id] = FullMessageId(dialog_id, m->message_id);
debug_being_sent_messages_[m->random_id] = dialog_id; debug_being_sent_messages_[m->random_id] = dialog_id;
return m->random_id; return m->random_id;
@ -15945,6 +16003,8 @@ void MessagesManager::cancel_send_message_query(DialogId dialog_id, unique_ptr<M
cancel_upload_message_content_files(m->content.get()); cancel_upload_message_content_files(m->content.get());
CHECK(m->edited_content == nullptr);
if (!m->send_query_ref.empty()) { if (!m->send_query_ref.empty()) {
LOG(INFO) << "Cancel send query for " << m->message_id; LOG(INFO) << "Cancel send query for " << m->message_id;
cancel_query(m->send_query_ref); cancel_query(m->send_query_ref);
@ -16424,7 +16484,7 @@ Result<MessagesManager::InputMessageContent> MessagesManager::process_input_mess
clear_draft = input_message_text.clear_draft; clear_draft = input_message_text.clear_draft;
WebPageId web_page_id; WebPageId web_page_id;
if (!disable_web_page_preview && if (!td_->auth_manager_->is_bot() && !disable_web_page_preview &&
(dialog_id.get_type() != DialogType::Channel || (dialog_id.get_type() != DialogType::Channel ||
td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_add_web_page_previews())) { td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_add_web_page_previews())) {
web_page_id = td_->web_pages_manager_->get_web_page_by_url( web_page_id = td_->web_pages_manager_->get_web_page_by_url(
@ -16703,7 +16763,9 @@ Result<MessagesManager::InputMessageContent> MessagesManager::process_input_mess
return Status::Error(10, "Message TTL can be specified only in private chats"); return Status::Error(10, "Message TTL can be specified only in private chats");
} }
if (dialog_id != DialogId()) {
TRY_STATUS(can_send_message_content(dialog_id, content.get(), false)); TRY_STATUS(can_send_message_content(dialog_id, content.get(), false));
}
return InputMessageContent{std::move(content), disable_web_page_preview, clear_draft, ttl, via_bot_user_id}; return InputMessageContent{std::move(content), disable_web_page_preview, clear_draft, ttl, via_bot_user_id};
} }
@ -16788,10 +16850,11 @@ void MessagesManager::save_send_message_logevent(DialogId dialog_id, Message *m)
} }
void MessagesManager::do_send_message(DialogId dialog_id, Message *m, vector<int> bad_parts) { void MessagesManager::do_send_message(DialogId dialog_id, Message *m, vector<int> bad_parts) {
LOG(INFO) << "Do send " << FullMessageId(dialog_id, m->message_id); bool is_edit = m->message_id.is_server();
LOG(INFO) << "Do " << (is_edit ? "edit" : "send") << ' ' << FullMessageId(dialog_id, m->message_id);
bool is_secret = dialog_id.get_type() == DialogType::SecretChat; bool is_secret = dialog_id.get_type() == DialogType::SecretChat;
if (m->media_album_id != 0 && bad_parts.empty() && !is_secret) { if (m->media_album_id != 0 && bad_parts.empty() && !is_secret && !is_edit) {
auto &request = pending_message_group_sends_[m->media_album_id]; auto &request = pending_message_group_sends_[m->media_album_id];
request.dialog_id = dialog_id; request.dialog_id = dialog_id;
request.message_ids.push_back(m->message_id); request.message_ids.push_back(m->message_id);
@ -16800,7 +16863,8 @@ void MessagesManager::do_send_message(DialogId dialog_id, Message *m, vector<int
request.results.push_back(Status::OK()); request.results.push_back(Status::OK());
} }
auto content = m->content.get(); auto content = is_edit ? m->edited_content.get() : m->content.get();
CHECK(content != nullptr);
auto content_type = content->get_id(); auto content_type = content->get_id();
if (content_type == MessageText::ID) { if (content_type == MessageText::ID) {
auto message_text = static_cast<const MessageText *>(content); auto message_text = static_cast<const MessageText *>(content);
@ -16862,9 +16926,29 @@ void MessagesManager::on_message_media_uploaded(DialogId dialog_id, Message *m,
FileId thumbnail_file_id) { FileId thumbnail_file_id) {
CHECK(m != nullptr); CHECK(m != nullptr);
CHECK(input_media != nullptr); CHECK(input_media != nullptr);
auto message_id = m->message_id;
if (message_id.is_server()) {
auto caption = get_message_content_caption(m->edited_content.get());
auto input_reply_markup = get_input_reply_markup(m->edited_reply_markup);
LOG(INFO) << "Edit media from " << message_id << " in " << dialog_id;
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, message_id, file_id, thumbnail_file_id,
generation = m->edit_generation](Result<Unit> result) {
send_closure(actor_id, &MessagesManager::on_message_media_edited, dialog_id, message_id, file_id,
thumbnail_file_id, generation, std::move(result));
});
send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, 1 << 11,
dialog_id, message_id, caption.text,
get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_message_media"),
std::move(input_media), nullptr, std::move(input_reply_markup),
get_sequence_dispatcher_id(dialog_id, -1));
return;
}
if (m->media_album_id == 0) { if (m->media_album_id == 0) {
on_media_message_ready_to_send( on_media_message_ready_to_send(
dialog_id, m->message_id, dialog_id, message_id,
PromiseCreator::lambda([this, dialog_id, input_media = std::move(input_media), file_id, PromiseCreator::lambda([this, dialog_id, input_media = std::move(input_media), file_id,
thumbnail_file_id](Result<Message *> result) mutable { thumbnail_file_id](Result<Message *> result) mutable {
if (result.is_error() || G()->close_flag()) { if (result.is_error() || G()->close_flag()) {
@ -16896,19 +16980,19 @@ void MessagesManager::on_message_media_uploaded(DialogId dialog_id, Message *m,
case telegram_api::inputMediaUploadedPhoto::ID: case telegram_api::inputMediaUploadedPhoto::ID:
case telegram_api::inputMediaDocumentExternal::ID: case telegram_api::inputMediaDocumentExternal::ID:
case telegram_api::inputMediaPhotoExternal::ID: case telegram_api::inputMediaPhotoExternal::ID:
LOG(INFO) << "Upload media from " << m->message_id << " in " << dialog_id; LOG(INFO) << "Upload media from " << message_id << " in " << dialog_id;
td_->create_handler<UploadMediaQuery>()->send(dialog_id, m->message_id, file_id, thumbnail_file_id, td_->create_handler<UploadMediaQuery>()->send(dialog_id, message_id, file_id, thumbnail_file_id,
std::move(input_media)); std::move(input_media));
break; break;
case telegram_api::inputMediaDocument::ID: case telegram_api::inputMediaDocument::ID:
case telegram_api::inputMediaPhoto::ID: case telegram_api::inputMediaPhoto::ID:
send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id, send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id,
dialog_id, m->message_id, Status::OK()); dialog_id, message_id, Status::OK());
break; break;
default: default:
LOG(ERROR) << "Have wrong input media " << to_string(input_media); LOG(ERROR) << "Have wrong input media " << to_string(input_media);
send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id, send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id,
dialog_id, m->message_id, Status::Error(400, "Wrong input media")); dialog_id, message_id, Status::Error(400, "Wrong input media"));
} }
} }
} }
@ -16933,7 +17017,7 @@ void MessagesManager::on_secret_message_media_uploaded(DialogId dialog_id, Messa
return send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id, return send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id,
dialog_id, m->message_id, Status::Error(400, "Wrong input media")); dialog_id, m->message_id, Status::Error(400, "Wrong input media"));
} }
*/ */
// TODO use file_id, thumbnail_file_id, invalidate partial remote location for file_id in case of failed upload // TODO use file_id, thumbnail_file_id, invalidate partial remote location for file_id in case of failed upload
// even message has already been deleted // even message has already been deleted
on_media_message_ready_to_send( on_media_message_ready_to_send(
@ -17712,7 +17796,7 @@ void MessagesManager::edit_message_text(FullMessageId full_message_id,
td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, flags, dialog_id, td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, flags, dialog_id,
message_id, input_message_text.text.text, message_id, input_message_text.text.text,
get_input_message_entities(td_->contacts_manager_.get(), input_message_text.text.entities, "edit_message_text"), get_input_message_entities(td_->contacts_manager_.get(), input_message_text.text.entities, "edit_message_text"),
nullptr, std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, -1)); nullptr, nullptr, std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, -1));
} }
void MessagesManager::edit_message_live_location(FullMessageId full_message_id, void MessagesManager::edit_message_live_location(FullMessageId full_message_id,
@ -17762,11 +17846,146 @@ void MessagesManager::edit_message_live_location(FullMessageId full_message_id,
flags |= telegram_api::messages_editMessage::STOP_GEO_LIVE_MASK; flags |= telegram_api::messages_editMessage::STOP_GEO_LIVE_MASK;
} }
send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, flags, dialog_id, send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, flags, dialog_id,
message_id, string(), vector<tl_object_ptr<telegram_api::MessageEntity>>(), message_id, string(), vector<tl_object_ptr<telegram_api::MessageEntity>>(), nullptr,
location.empty() ? nullptr : location.get_input_geo_point(), std::move(input_reply_markup), location.empty() ? nullptr : location.get_input_geo_point(), std::move(input_reply_markup),
get_sequence_dispatcher_id(dialog_id, -1)); get_sequence_dispatcher_id(dialog_id, -1));
} }
void MessagesManager::cancel_edit_message_media(DialogId dialog_id, Message *m) {
if (m->edited_content == nullptr) {
return;
}
cancel_upload_message_content_files(m->edited_content.get());
m->edited_content = nullptr;
m->edited_reply_markup = nullptr;
m->edit_generation = 0;
m->edit_promise.set_error(Status::Error(400, "Cancelled"));
}
void MessagesManager::on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id,
FileId thumbnail_file_id, uint64 generation, Result<Unit> &&result) {
CHECK(message_id.is_server());
auto m = get_message({dialog_id, message_id});
if (m == nullptr || m->edit_generation != generation) {
// message is already deleted or was edited again
return;
}
CHECK(m->edited_content != nullptr);
if (result.is_ok()) {
std::swap(m->content, m->edited_content);
update_message_content(dialog_id, m, std::move(m->edited_content),
m->edited_content->get_id() == MessagePhoto::ID && m->content->get_id() == MessagePhoto::ID,
true);
} else {
auto error_message = result.error().message();
if (thumbnail_file_id.is_valid()) {
// always delete partial remote location for the thumbnail, because it can't be reused anyway
td_->file_manager_->delete_partial_remote_location(thumbnail_file_id);
}
if (file_id.is_valid()) {
if (begins_with(error_message, "FILE_PART_") && ends_with(error_message, "_MISSING")) {
do_send_message(dialog_id, m, {to_integer<int32>(error_message.substr(10))});
return;
}
if (result.error().code() != 429 && result.error().code() < 500 && !G()->close_flag()) {
td_->file_manager_->delete_partial_remote_location(file_id);
}
}
cancel_upload_message_content_files(m->edited_content.get());
}
m->edited_content = nullptr;
m->edited_reply_markup = nullptr;
m->edit_generation = 0;
if (result.is_ok()) {
m->edit_promise.set_value(Unit());
} else {
m->edit_promise.set_error(result.move_as_error());
}
}
void MessagesManager::edit_message_media(FullMessageId full_message_id,
tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content,
Promise<Unit> &&promise) {
if (input_message_content == nullptr) {
return promise.set_error(Status::Error(5, "Can't edit message without new content"));
}
int32 new_message_content_type = input_message_content->get_id();
if (new_message_content_type != td_api::inputMessageAnimation::ID &&
new_message_content_type != td_api::inputMessageAudio::ID &&
new_message_content_type != td_api::inputMessageDocument::ID &&
new_message_content_type != td_api::inputMessagePhoto::ID &&
new_message_content_type != td_api::inputMessageVideo::ID) {
return promise.set_error(Status::Error(5, "Unsupported input message content type"));
}
LOG(INFO) << "Begin to edit media of " << full_message_id;
auto dialog_id = full_message_id.get_dialog_id();
Dialog *d = get_dialog_force(dialog_id);
if (d == nullptr) {
return promise.set_error(Status::Error(5, "Chat not found"));
}
if (!have_input_peer(dialog_id, AccessRights::Edit)) {
return promise.set_error(Status::Error(5, "Can't access the chat"));
}
auto message_id = full_message_id.get_message_id();
Message *m = get_message_force(d, message_id);
if (m == nullptr) {
return promise.set_error(Status::Error(5, "Message not found"));
}
if (!can_edit_message(dialog_id, m, true)) {
return promise.set_error(Status::Error(5, "Message can't be edited"));
}
CHECK(message_id.is_server());
int32 old_message_content_type = m->content->get_id();
if (old_message_content_type != MessageAnimation::ID && old_message_content_type != MessageAudio::ID &&
old_message_content_type != MessageDocument::ID && old_message_content_type != MessagePhoto::ID &&
old_message_content_type != MessageVideo::ID) {
return promise.set_error(Status::Error(5, "There is no media in the message to edit"));
}
if (m->media_album_id != 0 && new_message_content_type != td_api::inputMessagePhoto::ID &&
new_message_content_type != td_api::inputMessageVideo::ID) {
return promise.set_error(Status::Error(5, "Message can be edit only to Photo or Video"));
}
if (m->ttl > 0) {
return promise.set_error(Status::Error(5, "Can't edit media in self-destructing message"));
}
auto r_input_message_content = process_input_message_content(dialog_id, std::move(input_message_content));
if (r_input_message_content.is_error()) {
return promise.set_error(r_input_message_content.move_as_error());
}
InputMessageContent content = r_input_message_content.move_as_ok();
if (content.ttl > 0) {
return promise.set_error(Status::Error(5, "Can't enable self-destruction for media"));
}
auto r_new_reply_markup = get_reply_markup(std::move(reply_markup), td_->auth_manager_->is_bot(), true, false,
!is_broadcast_channel(dialog_id));
if (r_new_reply_markup.is_error()) {
return promise.set_error(r_new_reply_markup.move_as_error());
}
cancel_edit_message_media(dialog_id, m);
m->edited_content = dup_message_content(dialog_id, content.content.get(), false);
m->edited_reply_markup = r_new_reply_markup.move_as_ok();
m->edit_generation = ++current_message_edit_generation_;
m->edit_promise = std::move(promise);
do_send_message(dialog_id, m);
}
void MessagesManager::edit_message_caption(FullMessageId full_message_id, void MessagesManager::edit_message_caption(FullMessageId full_message_id,
tl_object_ptr<td_api::ReplyMarkup> &&reply_markup, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::formattedText> &&input_caption, tl_object_ptr<td_api::formattedText> &&input_caption,
@ -17813,7 +18032,7 @@ void MessagesManager::edit_message_caption(FullMessageId full_message_id,
send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, 1 << 11, dialog_id, send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, 1 << 11, dialog_id,
message_id, caption.text, message_id, caption.text,
get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_message_caption"), get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_message_caption"),
nullptr, std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, -1)); nullptr, nullptr, std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, -1));
} }
void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id, void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id,
@ -17851,7 +18070,7 @@ void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id,
} }
auto input_reply_markup = get_input_reply_markup(r_new_reply_markup.ok()); auto input_reply_markup = get_input_reply_markup(r_new_reply_markup.ok());
send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, 0, dialog_id, send_closure(td_->create_net_actor<EditMessageActor>(std::move(promise)), &EditMessageActor::send, 0, dialog_id,
message_id, string(), vector<tl_object_ptr<telegram_api::MessageEntity>>(), nullptr, message_id, string(), vector<tl_object_ptr<telegram_api::MessageEntity>>(), nullptr, nullptr,
std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, -1)); std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, -1));
} }
@ -17896,7 +18115,7 @@ void MessagesManager::edit_inline_message_text(const string &inline_message_id,
->send(flags, std::move(input_bot_inline_message_id), input_message_text.text.text, ->send(flags, std::move(input_bot_inline_message_id), input_message_text.text.text,
get_input_message_entities(td_->contacts_manager_.get(), input_message_text.text.entities, get_input_message_entities(td_->contacts_manager_.get(), input_message_text.text.entities,
"edit_inline_message_text"), "edit_inline_message_text"),
nullptr, get_input_reply_markup(r_new_reply_markup.ok())); nullptr, nullptr, get_input_reply_markup(r_new_reply_markup.ok()));
} }
void MessagesManager::edit_inline_message_live_location(const string &inline_message_id, void MessagesManager::edit_inline_message_live_location(const string &inline_message_id,
@ -17928,10 +18147,62 @@ void MessagesManager::edit_inline_message_live_location(const string &inline_mes
} }
td_->create_handler<EditInlineMessageQuery>(std::move(promise)) td_->create_handler<EditInlineMessageQuery>(std::move(promise))
->send(flags, std::move(input_bot_inline_message_id), "", vector<tl_object_ptr<telegram_api::MessageEntity>>(), ->send(flags, std::move(input_bot_inline_message_id), "", vector<tl_object_ptr<telegram_api::MessageEntity>>(),
location.empty() ? nullptr : location.get_input_geo_point(), nullptr, location.empty() ? nullptr : location.get_input_geo_point(),
get_input_reply_markup(r_new_reply_markup.ok())); get_input_reply_markup(r_new_reply_markup.ok()));
} }
void MessagesManager::edit_inline_message_media(const string &inline_message_id,
tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content,
Promise<Unit> &&promise) {
if (!td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(3, "Method is available only for bots"));
}
if (input_message_content == nullptr) {
return promise.set_error(Status::Error(5, "Can't edit message without new content"));
}
int32 new_message_content_type = input_message_content->get_id();
if (new_message_content_type != td_api::inputMessageAnimation::ID &&
new_message_content_type != td_api::inputMessageAudio::ID &&
new_message_content_type != td_api::inputMessageDocument::ID &&
new_message_content_type != td_api::inputMessagePhoto::ID &&
new_message_content_type != td_api::inputMessageVideo::ID) {
return promise.set_error(Status::Error(5, "Unsupported input message content type"));
}
auto r_input_message_content = process_input_message_content(DialogId(), std::move(input_message_content));
if (r_input_message_content.is_error()) {
return promise.set_error(r_input_message_content.move_as_error());
}
InputMessageContent content = r_input_message_content.move_as_ok();
if (content.ttl > 0) {
LOG(ERROR) << "Have message content with ttl " << content.ttl;
return promise.set_error(Status::Error(5, "Can't enable self-destruction for media"));
}
auto r_new_reply_markup = get_reply_markup(std::move(reply_markup), td_->auth_manager_->is_bot(), true, false, true);
if (r_new_reply_markup.is_error()) {
return promise.set_error(r_new_reply_markup.move_as_error());
}
auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
if (input_bot_inline_message_id == nullptr) {
return promise.set_error(Status::Error(400, "Wrong inline message identifier specified"));
}
auto input_media = get_input_media(content.content.get(), nullptr, nullptr, 0);
if (input_media == nullptr) {
return promise.set_error(Status::Error(400, "Wrong message content specified"));
}
auto caption = get_message_content_caption(content.content.get());
td_->create_handler<EditInlineMessageQuery>(std::move(promise))
->send(1 << 11, std::move(input_bot_inline_message_id), caption.text,
get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_inline_message_media"),
std::move(input_media), nullptr, get_input_reply_markup(r_new_reply_markup.ok()));
}
void MessagesManager::edit_inline_message_caption(const string &inline_message_id, void MessagesManager::edit_inline_message_caption(const string &inline_message_id,
tl_object_ptr<td_api::ReplyMarkup> &&reply_markup, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::formattedText> &&input_caption, tl_object_ptr<td_api::formattedText> &&input_caption,
@ -17959,7 +18230,7 @@ void MessagesManager::edit_inline_message_caption(const string &inline_message_i
td_->create_handler<EditInlineMessageQuery>(std::move(promise)) td_->create_handler<EditInlineMessageQuery>(std::move(promise))
->send(1 << 11, std::move(input_bot_inline_message_id), caption.text, ->send(1 << 11, std::move(input_bot_inline_message_id), caption.text,
get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_inline_message_caption"), get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_inline_message_caption"),
nullptr, get_input_reply_markup(r_new_reply_markup.ok())); nullptr, nullptr, get_input_reply_markup(r_new_reply_markup.ok()));
} }
void MessagesManager::edit_inline_message_reply_markup(const string &inline_message_id, void MessagesManager::edit_inline_message_reply_markup(const string &inline_message_id,
@ -17981,7 +18252,7 @@ void MessagesManager::edit_inline_message_reply_markup(const string &inline_mess
td_->create_handler<EditInlineMessageQuery>(std::move(promise)) td_->create_handler<EditInlineMessageQuery>(std::move(promise))
->send(0, std::move(input_bot_inline_message_id), string(), vector<tl_object_ptr<telegram_api::MessageEntity>>(), ->send(0, std::move(input_bot_inline_message_id), string(), vector<tl_object_ptr<telegram_api::MessageEntity>>(),
nullptr, get_input_reply_markup(r_new_reply_markup.ok())); nullptr, nullptr, get_input_reply_markup(r_new_reply_markup.ok()));
} }
int32 MessagesManager::get_message_flags(const Message *m) { int32 MessagesManager::get_message_flags(const Message *m) {
@ -19737,6 +20008,23 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error
} }
} }
void MessagesManager::fail_edit_message_media(FullMessageId full_message_id, Status &&error) {
auto dialog_id = full_message_id.get_dialog_id();
Dialog *d = get_dialog(dialog_id);
CHECK(d != nullptr);
MessageId message_id = full_message_id.get_message_id();
CHECK(message_id.is_server());
auto m = get_message(d, message_id);
if (m == nullptr) {
// message has already been deleted by the user or sent to inaccessible channel
return;
}
CHECK(m->edited_content != nullptr);
m->edit_promise.set_error(std::move(error));
cancel_edit_message_media(dialog_id, m);
}
void MessagesManager::on_update_dialog_draft_message(DialogId dialog_id, void MessagesManager::on_update_dialog_draft_message(DialogId dialog_id,
tl_object_ptr<telegram_api::DraftMessage> &&draft_message) { tl_object_ptr<telegram_api::DraftMessage> &&draft_message) {
if (!dialog_id.is_valid()) { if (!dialog_id.is_valid()) {
@ -23380,7 +23668,7 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me
const bool can_delete_old_document = old_message->message_id.is_yet_unsent() && false; const bool can_delete_old_document = old_message->message_id.is_yet_unsent() && false;
auto old_file_id = get_message_content_file_id(old_content.get()); auto old_file_id = get_message_content_file_id(old_content.get());
bool need_finish_upload = old_file_id.is_valid() && old_message->message_id.is_yet_unsent(); bool need_finish_upload = old_file_id.is_valid() && need_merge_files;
if (old_content_type != new_content_type) { if (old_content_type != new_content_type) {
need_update = true; need_update = true;
LOG(INFO) << "Message content has changed its type from " << old_content_type << " to " << new_content_type; LOG(INFO) << "Message content has changed its type from " << old_content_type << " to " << new_content_type;

View File

@ -1109,6 +1109,9 @@ class MessagesManager : public Actor {
void edit_message_live_location(FullMessageId full_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup, void edit_message_live_location(FullMessageId full_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::location> &&input_location, Promise<Unit> &&promise); tl_object_ptr<td_api::location> &&input_location, Promise<Unit> &&promise);
void edit_message_media(FullMessageId full_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content, Promise<Unit> &&promise);
void edit_message_caption(FullMessageId full_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup, void edit_message_caption(FullMessageId full_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::formattedText> &&input_caption, Promise<Unit> &&promise); tl_object_ptr<td_api::formattedText> &&input_caption, Promise<Unit> &&promise);
@ -1123,6 +1126,10 @@ class MessagesManager : public Actor {
tl_object_ptr<td_api::ReplyMarkup> &&reply_markup, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::location> &&input_location, Promise<Unit> &&promise); tl_object_ptr<td_api::location> &&input_location, Promise<Unit> &&promise);
void edit_inline_message_media(const string &inline_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content,
Promise<Unit> &&promise);
void edit_inline_message_caption(const string &inline_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup, void edit_inline_message_caption(const string &inline_message_id, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::formattedText> &&input_caption, Promise<Unit> &&promise); tl_object_ptr<td_api::formattedText> &&input_caption, Promise<Unit> &&promise);
@ -1541,6 +1548,11 @@ class MessagesManager : public Actor {
unique_ptr<ReplyMarkup> reply_markup; unique_ptr<ReplyMarkup> reply_markup;
unique_ptr<MessageContent> edited_content;
unique_ptr<ReplyMarkup> edited_reply_markup;
uint64 edit_generation = 0;
Promise<Unit> edit_promise;
unique_ptr<Message> left; unique_ptr<Message> left;
unique_ptr<Message> right; unique_ptr<Message> right;
@ -1942,6 +1954,11 @@ class MessagesManager : public Actor {
bool can_report_dialog(DialogId dialog_id) const; bool can_report_dialog(DialogId dialog_id) const;
void cancel_edit_message_media(DialogId dialog_id, Message *m);
void on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id, FileId thumbnail_file_id,
uint64 generation, Result<Unit> &&result);
MessageId get_persistent_message_id(const Dialog *d, MessageId message_id) const; MessageId get_persistent_message_id(const Dialog *d, MessageId message_id) const;
static MessageId get_replied_message_id(const Message *m); static MessageId get_replied_message_id(const Message *m);
@ -2146,6 +2163,8 @@ class MessagesManager : public Actor {
void fail_send_message(FullMessageId full_message_id, int error_code, const string &error_message); void fail_send_message(FullMessageId full_message_id, int error_code, const string &error_message);
void fail_edit_message_media(FullMessageId full_message_id, Status &&error);
void on_dialog_updated(DialogId dialog_id, const char *source); void on_dialog_updated(DialogId dialog_id, const char *source);
BufferSlice get_dialog_database_value(const Dialog *d); BufferSlice get_dialog_database_value(const Dialog *d);
@ -2839,6 +2858,8 @@ class MessagesManager : public Actor {
int64 current_pinned_dialog_order_ = DEFAULT_ORDER; int64 current_pinned_dialog_order_ = DEFAULT_ORDER;
uint64 current_message_edit_generation_ = 0;
std::set<DialogDate> ordered_dialogs_; std::set<DialogDate> ordered_dialogs_;
std::set<DialogDate> ordered_server_dialogs_; std::set<DialogDate> ordered_server_dialogs_;

View File

@ -1282,6 +1282,31 @@ class EditMessageLiveLocationRequest : public RequestOnceActor {
} }
}; };
class EditMessageMediaRequest : public RequestOnceActor {
FullMessageId full_message_id_;
tl_object_ptr<td_api::ReplyMarkup> reply_markup_;
tl_object_ptr<td_api::InputMessageContent> input_message_content_;
void do_run(Promise<Unit> &&promise) override {
td->messages_manager_->edit_message_media(full_message_id_, std::move(reply_markup_),
std::move(input_message_content_), std::move(promise));
}
void do_send_result() override {
send_result(td->messages_manager_->get_message_object(full_message_id_));
}
public:
EditMessageMediaRequest(ActorShared<Td> td, uint64 request_id, int64 dialog_id, int64 message_id,
tl_object_ptr<td_api::ReplyMarkup> reply_markup,
tl_object_ptr<td_api::InputMessageContent> input_message_content)
: RequestOnceActor(std::move(td), request_id)
, full_message_id_(DialogId(dialog_id), MessageId(message_id))
, reply_markup_(std::move(reply_markup))
, input_message_content_(std::move(input_message_content)) {
}
};
class EditMessageCaptionRequest : public RequestOnceActor { class EditMessageCaptionRequest : public RequestOnceActor {
FullMessageId full_message_id_; FullMessageId full_message_id_;
tl_object_ptr<td_api::ReplyMarkup> reply_markup_; tl_object_ptr<td_api::ReplyMarkup> reply_markup_;
@ -1370,6 +1395,27 @@ class EditInlineMessageLiveLocationRequest : public RequestOnceActor {
} }
}; };
class EditInlineMessageMediaRequest : public RequestOnceActor {
string inline_message_id_;
tl_object_ptr<td_api::ReplyMarkup> reply_markup_;
tl_object_ptr<td_api::InputMessageContent> input_message_content_;
void do_run(Promise<Unit> &&promise) override {
td->messages_manager_->edit_inline_message_media(inline_message_id_, std::move(reply_markup_),
std::move(input_message_content_), std::move(promise));
}
public:
EditInlineMessageMediaRequest(ActorShared<Td> td, uint64 request_id, string inline_message_id,
tl_object_ptr<td_api::ReplyMarkup> reply_markup,
tl_object_ptr<td_api::InputMessageContent> input_message_content)
: RequestOnceActor(std::move(td), request_id)
, inline_message_id_(std::move(inline_message_id))
, reply_markup_(std::move(reply_markup))
, input_message_content_(std::move(input_message_content)) {
}
};
class EditInlineMessageCaptionRequest : public RequestOnceActor { class EditInlineMessageCaptionRequest : public RequestOnceActor {
string inline_message_id_; string inline_message_id_;
tl_object_ptr<td_api::ReplyMarkup> reply_markup_; tl_object_ptr<td_api::ReplyMarkup> reply_markup_;
@ -5823,6 +5869,11 @@ void Td::on_request(uint64 id, td_api::editMessageLiveLocation &request) {
std::move(request.reply_markup_), std::move(request.location_)); std::move(request.reply_markup_), std::move(request.location_));
} }
void Td::on_request(uint64 id, td_api::editMessageMedia &request) {
CREATE_REQUEST(EditMessageMediaRequest, request.chat_id_, request.message_id_, std::move(request.reply_markup_),
std::move(request.input_message_content_));
}
void Td::on_request(uint64 id, td_api::editMessageCaption &request) { void Td::on_request(uint64 id, td_api::editMessageCaption &request) {
CREATE_REQUEST(EditMessageCaptionRequest, request.chat_id_, request.message_id_, std::move(request.reply_markup_), CREATE_REQUEST(EditMessageCaptionRequest, request.chat_id_, request.message_id_, std::move(request.reply_markup_),
std::move(request.caption_)); std::move(request.caption_));
@ -5848,6 +5899,13 @@ void Td::on_request(uint64 id, td_api::editInlineMessageLiveLocation &request) {
std::move(request.reply_markup_), std::move(request.location_)); std::move(request.reply_markup_), std::move(request.location_));
} }
void Td::on_request(uint64 id, td_api::editInlineMessageMedia &request) {
CHECK_IS_BOT();
CLEAN_INPUT_STRING(request.inline_message_id_);
CREATE_REQUEST(EditInlineMessageMediaRequest, std::move(request.inline_message_id_), std::move(request.reply_markup_),
std::move(request.input_message_content_));
}
void Td::on_request(uint64 id, td_api::editInlineMessageCaption &request) { void Td::on_request(uint64 id, td_api::editInlineMessageCaption &request) {
CHECK_IS_BOT(); CHECK_IS_BOT();
CLEAN_INPUT_STRING(request.inline_message_id_); CLEAN_INPUT_STRING(request.inline_message_id_);

View File

@ -503,6 +503,8 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::editMessageLiveLocation &request); void on_request(uint64 id, td_api::editMessageLiveLocation &request);
void on_request(uint64 id, td_api::editMessageMedia &request);
void on_request(uint64 id, td_api::editMessageCaption &request); void on_request(uint64 id, td_api::editMessageCaption &request);
void on_request(uint64 id, td_api::editMessageReplyMarkup &request); void on_request(uint64 id, td_api::editMessageReplyMarkup &request);
@ -511,6 +513,8 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::editInlineMessageLiveLocation &request); void on_request(uint64 id, td_api::editInlineMessageLiveLocation &request);
void on_request(uint64 id, td_api::editInlineMessageMedia &request);
void on_request(uint64 id, td_api::editInlineMessageCaption &request); void on_request(uint64 id, td_api::editInlineMessageCaption &request);
void on_request(uint64 id, td_api::editInlineMessageReplyMarkup &request); void on_request(uint64 id, td_api::editInlineMessageReplyMarkup &request);

View File

@ -830,16 +830,21 @@ class CliClient final : public Actor {
} }
#endif #endif
static tl_object_ptr<td_api::formattedText> as_formatted_text(
string text, vector<td_api::object_ptr<td_api::textEntity>> entities = {}) {
if (entities.empty()) {
auto parsed_text =
execute(make_tl_object<td_api::parseTextEntities>(text, make_tl_object<td_api::textParseModeMarkdown>()));
if (parsed_text->get_id() == td_api::formattedText::ID) {
return td_api::move_object_as<td_api::formattedText>(parsed_text);
}
}
return make_tl_object<td_api::formattedText>(text, std::move(entities));
}
static tl_object_ptr<td_api::formattedText> as_caption(string caption, static tl_object_ptr<td_api::formattedText> as_caption(string caption,
vector<td_api::object_ptr<td_api::textEntity>> entities = {}) { vector<td_api::object_ptr<td_api::textEntity>> entities = {}) {
if (entities.empty()) { return as_formatted_text(caption, std::move(entities));
auto parsed_caption =
execute(make_tl_object<td_api::parseTextEntities>(caption, make_tl_object<td_api::textParseModeMarkdown>()));
if (parsed_caption->get_id() == td_api::formattedText::ID) {
return td_api::move_object_as<td_api::formattedText>(parsed_caption);
}
}
return make_tl_object<td_api::formattedText>(caption, std::move(entities));
} }
tl_object_ptr<td_api::NotificationSettingsScope> get_notification_settings_scope(Slice scope) const { tl_object_ptr<td_api::NotificationSettingsScope> get_notification_settings_scope(Slice scope) const {
@ -2243,8 +2248,7 @@ class CliClient final : public Actor {
draft_message = make_tl_object<td_api::draftMessage>( draft_message = make_tl_object<td_api::draftMessage>(
as_message_id(reply_to_message_id), as_message_id(reply_to_message_id),
make_tl_object<td_api::inputMessageText>( make_tl_object<td_api::inputMessageText>(as_formatted_text(message, std::move(entities)), true, false));
make_tl_object<td_api::formattedText>(message, std::move(entities)), true, false));
} }
send_request(make_tl_object<td_api::setChatDraftMessage>(as_chat_id(chat_id), std::move(draft_message))); send_request(make_tl_object<td_api::setChatDraftMessage>(as_chat_id(chat_id), std::move(draft_message)));
} else if (op == "tcip") { } else if (op == "tcip") {
@ -2281,10 +2285,7 @@ class CliClient final : public Actor {
send_message(chat_id, make_tl_object<td_api::inputMessagePhoto>(as_local_file("rgb.jpg"), nullptr, Auto(), 0, send_message(chat_id, make_tl_object<td_api::inputMessagePhoto>(as_local_file("rgb.jpg"), nullptr, Auto(), 0,
0, as_caption(message), 0)); 0, as_caption(message), 0));
} else { } else {
send_message(chat_id, send_message(chat_id, make_tl_object<td_api::inputMessageText>(as_formatted_text(message), false, true));
make_tl_object<td_api::inputMessageText>(
make_tl_object<td_api::formattedText>(message, vector<tl_object_ptr<td_api::textEntity>>()),
false, true));
} }
} }
} else if (op == "ssm") { } else if (op == "ssm") {
@ -2315,15 +2316,7 @@ class CliClient final : public Actor {
message = string(5097, 'a'); message = string(5097, 'a');
} }
auto parsed_text = send_message(chat_id, make_tl_object<td_api::inputMessageText>(as_formatted_text(message), false, true),
execute(make_tl_object<td_api::parseTextEntities>(message, make_tl_object<td_api::textParseModeMarkdown>()));
if (parsed_text->get_id() == td_api::error::ID) {
parsed_text = make_tl_object<td_api::formattedText>(message, vector<tl_object_ptr<td_api::textEntity>>());
}
send_message(
chat_id,
make_tl_object<td_api::inputMessageText>(move_tl_object_as<td_api::formattedText>(parsed_text), false, true),
op == "sms", false, as_message_id(reply_to_message_id)); op == "sms", false, as_message_id(reply_to_message_id));
} else if (op == "alm" || op == "almr") { } else if (op == "alm" || op == "almr") {
string chat_id; string chat_id;
@ -2337,16 +2330,9 @@ class CliClient final : public Actor {
std::tie(reply_to_message_id, message) = split(message); std::tie(reply_to_message_id, message) = split(message);
} }
auto parsed_text =
execute(make_tl_object<td_api::parseTextEntities>(message, make_tl_object<td_api::textParseModeMarkdown>()));
if (parsed_text->get_id() == td_api::error::ID) {
parsed_text = make_tl_object<td_api::formattedText>(message, vector<tl_object_ptr<td_api::textEntity>>());
}
send_request(make_tl_object<td_api::addLocalMessage>( send_request(make_tl_object<td_api::addLocalMessage>(
as_chat_id(chat_id), as_user_id(user_id), as_message_id(reply_to_message_id), false, as_chat_id(chat_id), as_user_id(user_id), as_message_id(reply_to_message_id), false,
make_tl_object<td_api::inputMessageText>(move_tl_object_as<td_api::formattedText>(parsed_text), false, make_tl_object<td_api::inputMessageText>(as_formatted_text(message), false, true)));
true)));
} else if (op == "smap" || op == "smapr") { } else if (op == "smap" || op == "smapr") {
string chat_id; string chat_id;
string reply_to_message_id; string reply_to_message_id;
@ -2373,9 +2359,66 @@ class CliClient final : public Actor {
std::tie(message_id, message) = split(args); std::tie(message_id, message) = split(args);
send_request(make_tl_object<td_api::editMessageText>( send_request(make_tl_object<td_api::editMessageText>(
as_chat_id(chat_id), as_message_id(message_id), nullptr, as_chat_id(chat_id), as_message_id(message_id), nullptr,
make_tl_object<td_api::inputMessageText>( make_tl_object<td_api::inputMessageText>(as_formatted_text(message), true, true)));
make_tl_object<td_api::formattedText>(message, vector<tl_object_ptr<td_api::textEntity>>()), true, } else if (op == "eman") {
true))); string chat_id;
string message_id;
string animation;
std::tie(chat_id, args) = split(args);
std::tie(message_id, animation) = split(args);
send_request(make_tl_object<td_api::editMessageMedia>(
as_chat_id(chat_id), as_message_id(message_id), nullptr,
make_tl_object<td_api::inputMessageAnimation>(as_input_file(animation), nullptr, 0, 0, 0,
as_caption("animation"))));
} else if (op == "emc") {
string chat_id;
string message_id;
string caption;
std::tie(chat_id, args) = split(args);
std::tie(message_id, caption) = split(args);
send_request(make_tl_object<td_api::editMessageCaption>(as_chat_id(chat_id), as_message_id(message_id), nullptr,
as_caption(caption)));
} else if (op == "emd") {
string chat_id;
string message_id;
string document;
std::tie(chat_id, args) = split(args);
std::tie(message_id, document) = split(args);
send_request(make_tl_object<td_api::editMessageMedia>(
as_chat_id(chat_id), as_message_id(message_id), nullptr,
make_tl_object<td_api::inputMessageDocument>(as_input_file(document), nullptr, as_caption(""))));
} else if (op == "emp") {
string chat_id;
string message_id;
string photo;
std::tie(chat_id, args) = split(args);
std::tie(message_id, photo) = split(args);
send_request(make_tl_object<td_api::editMessageMedia>(
as_chat_id(chat_id), as_message_id(message_id), nullptr,
make_tl_object<td_api::inputMessagePhoto>(as_input_file(photo), as_input_thumbnail(as_input_file(photo)),
Auto(), 0, 0, as_caption(""), 0)));
} else if (op == "empttl") {
string chat_id;
string message_id;
string photo;
std::tie(chat_id, args) = split(args);
std::tie(message_id, photo) = split(args);
send_request(make_tl_object<td_api::editMessageMedia>(
as_chat_id(chat_id), as_message_id(message_id), nullptr,
make_tl_object<td_api::inputMessagePhoto>(as_input_file(photo), as_input_thumbnail(as_input_file(photo)),
Auto(), 0, 0, as_caption(""), 10)));
} else if (op == "emvt") {
string chat_id;
string message_id;
string video;
string thumbnail;
std::tie(chat_id, args) = split(args);
std::tie(message_id, args) = split(args);
std::tie(video, thumbnail) = split(args);
send_request(make_tl_object<td_api::editMessageMedia>(
as_chat_id(chat_id), as_message_id(message_id), nullptr,
make_tl_object<td_api::inputMessageVideo>(as_input_file(video), as_input_thumbnail(as_input_file(thumbnail)),
Auto(), 1, 2, 3, true, as_caption(""), 0)));
} else if (op == "emll") { } else if (op == "emll") {
string chat_id; string chat_id;
string message_id; string message_id;
@ -3163,6 +3206,10 @@ class CliClient final : public Actor {
fd.truncate_to_current_position(size).ignore(); fd.truncate_to_current_position(size).ignore();
} else if (op == "SetVerbosity" || op == "SV") { } else if (op == "SetVerbosity" || op == "SV") {
td::Log::set_verbosity_level(to_integer<int>(args)); td::Log::set_verbosity_level(to_integer<int>(args));
} else if (op[0] == 'v' && op[1] == 'v') {
td::Log::set_verbosity_level(static_cast<int>(op.size()));
} else if (op[0] == 'v' && ('0' <= op[1] && op[1] <= '9')) {
td::Log::set_verbosity_level(to_integer<int>(op.substr(1)));
} else if (op == "q" || op == "Quit") { } else if (op == "q" || op == "Quit") {
quit(); quit();
} else if (op == "dnq" || op == "DumpNetQueries") { } else if (op == "dnq" || op == "DumpNetQueries") {