diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ed1095d1..5e0ac94c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -391,6 +391,7 @@ set(TDLIB_SOURCE td/telegram/MessageEntity.cpp td/telegram/MessageExtendedMedia.cpp td/telegram/MessageId.cpp + td/telegram/MessageInputReplyTo.cpp td/telegram/MessageReaction.cpp td/telegram/MessageReplyHeader.cpp td/telegram/MessageReplyInfo.cpp @@ -659,6 +660,7 @@ set(TDLIB_SOURCE td/telegram/MessageEntity.h td/telegram/MessageExtendedMedia.h td/telegram/MessageId.h + td/telegram/MessageInputReplyTo.h td/telegram/MessageLinkInfo.h td/telegram/MessageReaction.h td/telegram/MessageReplyHeader.h diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b6b7084c4..6acf00124 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1131,12 +1131,12 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n //@class MessageReplyTo @description Contains information about the message or the story a message is replying to //@description Describes a replied message -//@chat_id The identifier of the chat to which the replied message belongs. For example, messages in the Replies chat are replies to messages in different chats +//@chat_id The identifier of the chat to which the replied message belongs; ignored for outgoing replies. For example, messages in the Replies chat are replies to messages in different chats //@message_id The identifier of the replied message messageReplyToMessage chat_id:int53 message_id:int53 = MessageReplyTo; -//@description Describes a replied story @sender_user_id The identifier of the sender of the replied story @story_id The identifier of the replied story -messageReplyToStory sender_user_id:int53 story_id:int53 = MessageReplyTo; +//@description Describes a replied story @sender_user_id The identifier of the sender of the replied story. Currently, stories can be replied only in the corresponding private chats @story_id The identifier of the replied story +messageReplyToStory sender_user_id:int53 story_id:int32 = MessageReplyTo; //@description Describes a message @@ -4359,7 +4359,7 @@ notificationTypeNewSecretChat = NotificationType; notificationTypeNewCall call_id:int32 = NotificationType; //@description New message was received through a push notification -//@message_id The message identifier. The message will not be available in the chat history, but the ID can be used in viewMessages, or as reply_to_message_id +//@message_id The message identifier. The message will not be available in the chat history, but the ID can be used in viewMessages, or as a message to reply //@sender_id Identifier of the sender of the message. Corresponding user or chat may be inaccessible //@sender_name Name of the sender //@is_outgoing True, if the message is outgoing @@ -6376,20 +6376,20 @@ setChatMessageSender chat_id:int53 message_sender_id:MessageSender = Ok; //@description Sends a message. Returns the sent message //@chat_id Target chat //@message_thread_id If not 0, a message thread identifier in which the message will be sent -//@reply_to_message_id Identifier of the replied message; 0 if none +//@reply_to Identifier of the replied message or story; pass null if none //@options Options to be used to send the message; pass null to use default options //@reply_markup Markup for replying to the message; pass null if none; for bots only //@input_message_content The content of the message to be sent -sendMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message; +sendMessage chat_id:int53 message_thread_id:int53 reply_to:MessageReplyTo options:messageSendOptions reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message; //@description Sends 2-10 messages grouped together into an album. Currently, only audio, document, photo and video messages can be grouped into an album. Documents and audio files can be only grouped in an album with messages of the same type. Returns sent messages //@chat_id Target chat //@message_thread_id If not 0, a message thread identifier in which the messages will be sent -//@reply_to_message_id Identifier of a replied message; 0 if none +//@reply_to Identifier of the replied message or story; pass null if none //@options Options to be used to send the messages; pass null to use default options //@input_message_contents Contents of messages to be sent. At most 10 messages can be added to an album //@only_preview Pass true to get fake messages instead of actually sending them -sendMessageAlbum chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions input_message_contents:vector only_preview:Bool = Messages; +sendMessageAlbum chat_id:int53 message_thread_id:int53 reply_to:MessageReplyTo options:messageSendOptions input_message_contents:vector only_preview:Bool = Messages; //@description Invites a bot to a chat (if it is not yet a member) and sends it the /start command. Bots can't be invited to a private chat other than the chat with the bot. Bots can't be invited to channels (although they can be added as admins) and secret chats. Returns the sent message //@bot_user_id Identifier of the bot @@ -6400,12 +6400,12 @@ sendBotStartMessage bot_user_id:int53 chat_id:int53 parameter:string = Message; //@description Sends the result of an inline query as a message. Returns the sent message. Always clears a chat draft message //@chat_id Target chat //@message_thread_id If not 0, a message thread identifier in which the message will be sent -//@reply_to_message_id Identifier of a replied message; 0 if none +//@reply_to Identifier of the replied message or story; pass null if none //@options Options to be used to send the message; pass null to use default options //@query_id Identifier of the inline query //@result_id Identifier of the inline result //@hide_via_bot Pass true to hide the bot, via which the message is sent. Can be used only for bots getOption("animation_search_bot_username"), getOption("photo_search_bot_username"), and getOption("venue_search_bot_username") -sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions query_id:int64 result_id:string hide_via_bot:Bool = Message; +sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to:MessageReplyTo options:messageSendOptions query_id:int64 result_id:string hide_via_bot:Bool = Message; //@description Forwards previously sent messages. Returns the forwarded messages in the same order as the message identifiers passed in message_ids. If a message can't be forwarded, null will be returned instead of the message //@chat_id Identifier of the chat to which to forward messages @@ -6427,10 +6427,10 @@ resendMessages chat_id:int53 message_ids:vector = 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 //@sender_id Identifier of the sender of the message -//@reply_to_message_id Identifier of the replied message; 0 if none +//@reply_to Identifier of the replied message or story; pass null if none //@disable_notification Pass true to disable notification for the message //@input_message_content The content of the message to be added -addLocalMessage chat_id:int53 sender_id:MessageSender reply_to_message_id:int53 disable_notification:Bool input_message_content:InputMessageContent = Message; +addLocalMessage chat_id:int53 sender_id:MessageSender reply_to:MessageReplyTo disable_notification:Bool input_message_content:InputMessageContent = Message; //@description Deletes messages @chat_id Chat identifier @message_ids Identifiers of the messages to be deleted @revoke Pass true to delete messages for all chat members. Always true for supergroups, channels and secret chats deleteMessages chat_id:int53 message_ids:vector revoke:Bool = Ok; @@ -6778,8 +6778,8 @@ sendWebAppData bot_user_id:int53 button_text:string data:string = Ok; //@theme Preferred Web App theme; pass null to use the default theme //@application_name Short name of the application; 0-64 English letters, digits, and underscores //@message_thread_id If not 0, a message thread identifier in which the message will be sent -//@reply_to_message_id Identifier of the replied message for the message sent by the Web App; 0 if none -openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters application_name:string message_thread_id:int53 reply_to_message_id:int53 = WebAppInfo; +//@reply_to Identifier of the replied message or story for the message sent by the Web App; pass null if none +openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters application_name:string message_thread_id:int53 reply_to:MessageReplyTo = WebAppInfo; //@description Informs TDLib that a previously opened Web App was closed @web_app_launch_id Identifier of Web App launch, received from openWebApp closeWebApp web_app_launch_id:int64 = Ok; diff --git a/td/telegram/AttachMenuManager.cpp b/td/telegram/AttachMenuManager.cpp index 53412c9d2..020d3166d 100644 --- a/td/telegram/AttachMenuManager.cpp +++ b/td/telegram/AttachMenuManager.cpp @@ -119,7 +119,7 @@ class RequestWebViewQuery final : public Td::ResultHandler { DialogId dialog_id_; UserId bot_user_id_; MessageId top_thread_message_id_; - MessageId reply_to_message_id_; + MessageInputReplyTo input_reply_to_; DialogId as_dialog_id_; bool from_attach_menu_ = false; @@ -130,11 +130,11 @@ class RequestWebViewQuery final : public Td::ResultHandler { void send(DialogId dialog_id, UserId bot_user_id, tl_object_ptr &&input_user, string &&url, td_api::object_ptr &&theme, string &&platform, MessageId top_thread_message_id, - MessageId reply_to_message_id, bool silent, DialogId as_dialog_id) { + MessageInputReplyTo input_reply_to, bool silent, DialogId as_dialog_id) { dialog_id_ = dialog_id; bot_user_id_ = bot_user_id; top_thread_message_id_ = top_thread_message_id; - reply_to_message_id_ = reply_to_message_id; + input_reply_to_ = input_reply_to; as_dialog_id_ = as_dialog_id; int32 flags = 0; @@ -167,7 +167,7 @@ class RequestWebViewQuery final : public Td::ResultHandler { flags |= telegram_api::messages_requestWebView::THEME_PARAMS_MASK; } - auto reply_to = MessagesManager::get_input_reply_to(reply_to_message_id, top_thread_message_id); + auto reply_to = input_reply_to_.get_input_reply_to(td_, top_thread_message_id); if (reply_to != nullptr) { flags |= telegram_api::messages_requestWebView::REPLY_TO_MASK; } @@ -197,7 +197,7 @@ class RequestWebViewQuery final : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); td_->attach_menu_manager_->open_web_view(ptr->query_id_, dialog_id_, bot_user_id_, top_thread_message_id_, - reply_to_message_id_, as_dialog_id_); + input_reply_to_, as_dialog_id_); promise_.set_value(td_api::make_object(ptr->query_id_, ptr->url_)); } @@ -216,7 +216,7 @@ class ProlongWebViewQuery final : public Td::ResultHandler { public: void send(DialogId dialog_id, UserId bot_user_id, int64 query_id, MessageId top_thread_message_id, - MessageId reply_to_message_id, bool silent, DialogId as_dialog_id) { + MessageInputReplyTo input_reply_to, bool silent, DialogId as_dialog_id) { dialog_id_ = dialog_id; auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); @@ -226,7 +226,7 @@ class ProlongWebViewQuery final : public Td::ResultHandler { } int32 flags = 0; - auto reply_to = MessagesManager::get_input_reply_to(reply_to_message_id, top_thread_message_id); + auto reply_to = input_reply_to.get_input_reply_to(td_, top_thread_message_id); if (reply_to != nullptr) { flags |= telegram_api::messages_prolongWebView::REPLY_TO_MASK; } @@ -673,7 +673,7 @@ void AttachMenuManager::ping_web_view() { bool silent = td_->messages_manager_->get_dialog_silent_send_message(opened_web_view.dialog_id_); td_->create_handler()->send( opened_web_view.dialog_id_, opened_web_view.bot_user_id_, it.first, opened_web_view.top_thread_message_id_, - opened_web_view.reply_to_message_id_, silent, opened_web_view.as_dialog_id_); + opened_web_view.input_reply_to_, silent, opened_web_view.as_dialog_id_); } schedule_ping_web_view(); @@ -753,7 +753,7 @@ void AttachMenuManager::request_app_web_view(DialogId dialog_id, UserId bot_user } void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId top_thread_message_id, - MessageId reply_to_message_id, string &&url, + td_api::object_ptr &&reply_to, string &&url, td_api::object_ptr &&theme, string &&platform, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->contacts_manager_->get_bot_data(bot_user_id)); @@ -781,26 +781,24 @@ void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, return promise.set_error(Status::Error(400, "Have no write access to the chat")); } - if (!reply_to_message_id.is_valid() || !reply_to_message_id.is_server() || - !td_->messages_manager_->have_message_force({dialog_id, reply_to_message_id}, "request_web_view")) { - reply_to_message_id = MessageId(); - } if (!top_thread_message_id.is_valid() || !top_thread_message_id.is_server() || dialog_id.get_type() != DialogType::Channel || !td_->contacts_manager_->is_megagroup_channel(dialog_id.get_channel_id())) { top_thread_message_id = MessageId(); } + auto input_reply_to = + td_->messages_manager_->get_message_input_reply_to(dialog_id, top_thread_message_id, std::move(reply_to), false); bool silent = td_->messages_manager_->get_dialog_silent_send_message(dialog_id); DialogId as_dialog_id = td_->messages_manager_->get_dialog_default_send_message_as_dialog_id(dialog_id); td_->create_handler(std::move(promise)) ->send(dialog_id, bot_user_id, std::move(input_user), std::move(url), std::move(theme), std::move(platform), - top_thread_message_id, reply_to_message_id, silent, as_dialog_id); + top_thread_message_id, input_reply_to, silent, as_dialog_id); } void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, - MessageId top_thread_message_id, MessageId reply_to_message_id, + MessageId top_thread_message_id, MessageInputReplyTo input_reply_to, DialogId as_dialog_id) { if (query_id == 0) { LOG(ERROR) << "Receive Web App query identifier == 0"; @@ -814,7 +812,7 @@ void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId opened_web_view.dialog_id_ = dialog_id; opened_web_view.bot_user_id_ = bot_user_id; opened_web_view.top_thread_message_id_ = top_thread_message_id; - opened_web_view.reply_to_message_id_ = reply_to_message_id; + opened_web_view.input_reply_to_ = std::move(input_reply_to); opened_web_view.as_dialog_id_ = as_dialog_id; opened_web_views_.emplace(query_id, std::move(opened_web_view)); } diff --git a/td/telegram/AttachMenuManager.h b/td/telegram/AttachMenuManager.h index 3983bfb9c..8b1e4afcb 100644 --- a/td/telegram/AttachMenuManager.h +++ b/td/telegram/AttachMenuManager.h @@ -10,6 +10,7 @@ #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/MessageId.h" +#include "td/telegram/MessageInputReplyTo.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -42,12 +43,12 @@ class AttachMenuManager final : public Actor { string &&platform, bool allow_write_access, Promise &&promise); void request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId top_thread_message_id, - MessageId reply_to_message_id, string &&url, + td_api::object_ptr &&reply_to, string &&url, td_api::object_ptr &&theme, string &&platform, Promise> &&promise); void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId top_thread_message_id, - MessageId reply_to_message_id, DialogId as_dialog_id); + MessageInputReplyTo input_reply_to, DialogId as_dialog_id); void close_web_view(int64 query_id, Promise &&promise); @@ -175,7 +176,7 @@ class AttachMenuManager final : public Actor { DialogId dialog_id_; UserId bot_user_id_; MessageId top_thread_message_id_; - MessageId reply_to_message_id_; + MessageInputReplyTo input_reply_to_; DialogId as_dialog_id_; }; FlatHashMap opened_web_views_; diff --git a/td/telegram/DraftMessage.cpp b/td/telegram/DraftMessage.cpp index 96b07e6ad..c085b4465 100644 --- a/td/telegram/DraftMessage.cpp +++ b/td/telegram/DraftMessage.cpp @@ -194,8 +194,12 @@ Result> DraftMessage::get_draft_message( if (result->reply_to_message_id_ != MessageId() && !result->reply_to_message_id_.is_valid()) { return Status::Error(400, "Invalid reply_to_message_id specified"); } - result->reply_to_message_id_ = td->messages_manager_->get_reply_to_message_id(dialog_id, top_thread_message_id, - result->reply_to_message_id_, true); + result->reply_to_message_id_ = + td->messages_manager_ + ->get_message_input_reply_to( + dialog_id, top_thread_message_id, + td_api::make_object(0, result->reply_to_message_id_.get()), true) + .message_id_; auto input_message_content = std::move(draft_message->input_message_text_); if (input_message_content != nullptr) { diff --git a/td/telegram/MessageCopyOptions.h b/td/telegram/MessageCopyOptions.h index 2335b30e7..f945ac046 100644 --- a/td/telegram/MessageCopyOptions.h +++ b/td/telegram/MessageCopyOptions.h @@ -8,6 +8,7 @@ #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageId.h" +#include "td/telegram/MessageInputReplyTo.h" #include "td/telegram/ReplyMarkup.h" #include "td/utils/common.h" @@ -19,7 +20,7 @@ struct MessageCopyOptions { bool send_copy = false; bool replace_caption = false; FormattedText new_caption; - MessageId reply_to_message_id; + MessageInputReplyTo input_reply_to; unique_ptr reply_markup; MessageCopyOptions() = default; @@ -30,7 +31,7 @@ struct MessageCopyOptions { if (!send_copy) { return true; } - if ((replace_caption && !new_caption.text.empty()) || reply_to_message_id.is_valid() || reply_markup != nullptr) { + if ((replace_caption && !new_caption.text.empty()) || input_reply_to.is_valid() || reply_markup != nullptr) { return false; } return true; @@ -43,8 +44,8 @@ inline StringBuilder &operator<<(StringBuilder &string_builder, MessageCopyOptio if (copy_options.replace_caption) { string_builder << ", new_caption = " << copy_options.new_caption; } - if (copy_options.reply_to_message_id.is_valid()) { - string_builder << ", in reply to " << copy_options.reply_to_message_id; + if (copy_options.input_reply_to.is_valid()) { + string_builder << ", in reply to " << copy_options.input_reply_to; } if (copy_options.reply_markup != nullptr) { string_builder << ", with reply markup"; diff --git a/td/telegram/MessageInputReplyTo.cpp b/td/telegram/MessageInputReplyTo.cpp new file mode 100644 index 000000000..b02ad13c7 --- /dev/null +++ b/td/telegram/MessageInputReplyTo.cpp @@ -0,0 +1,74 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/MessageInputReplyTo.h" + +#include "td/telegram/ContactsManager.h" +#include "td/telegram/Td.h" + +namespace td { + +MessageInputReplyTo::MessageInputReplyTo(const td_api::object_ptr &reply_to_ptr) { + if (reply_to_ptr == nullptr) { + return; + } + switch (reply_to_ptr->get_id()) { + case td_api::messageReplyToMessage::ID: { + auto reply_to = static_cast(reply_to_ptr.get()); + message_id_ = MessageId(reply_to->message_id_); + break; + } + case td_api::messageReplyToStory::ID: { + auto reply_to = static_cast(reply_to_ptr.get()); + story_full_id_ = {DialogId(reply_to->sender_user_id_), StoryId(reply_to->story_id_)}; + break; + } + default: + UNREACHABLE(); + } +} + +telegram_api::object_ptr MessageInputReplyTo::get_input_reply_to( + Td *td, MessageId top_thread_message_id) const { + if (story_full_id_.is_valid()) { + auto dialog_id = story_full_id_.get_dialog_id(); + CHECK(dialog_id.get_type() == DialogType::User); + auto r_input_user = td->contacts_manager_->get_input_user(dialog_id.get_user_id()); + if (r_input_user.is_error()) { + LOG(ERROR) << "Failed to get input user for " << story_full_id_; + return nullptr; + } + return telegram_api::make_object(r_input_user.move_as_ok(), + story_full_id_.get_story_id().get()); + } + auto reply_to_message_id = message_id_; + if (reply_to_message_id == MessageId()) { + if (top_thread_message_id == MessageId()) { + return nullptr; + } + reply_to_message_id = top_thread_message_id; + } + CHECK(reply_to_message_id.is_server()); + int32 flags = 0; + if (top_thread_message_id != MessageId()) { + CHECK(top_thread_message_id.is_server()); + flags |= telegram_api::inputReplyToMessage::TOP_MSG_ID_MASK; + } + return telegram_api::make_object( + flags, reply_to_message_id.get_server_message_id().get(), top_thread_message_id.get_server_message_id().get()); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReplyTo &input_reply_to) { + if (input_reply_to.message_id_.is_valid()) { + return string_builder << input_reply_to.message_id_; + } + if (input_reply_to.story_full_id_.is_valid()) { + return string_builder << input_reply_to.story_full_id_; + } + return string_builder << "nothing"; +} + +} // namespace td diff --git a/td/telegram/MessageInputReplyTo.h b/td/telegram/MessageInputReplyTo.h new file mode 100644 index 000000000..441a16571 --- /dev/null +++ b/td/telegram/MessageInputReplyTo.h @@ -0,0 +1,45 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/MessageId.h" +#include "td/telegram/StoryFullId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class Td; + +struct MessageInputReplyTo { + MessageId message_id_; + // or + StoryFullId story_full_id_; + + bool is_valid() const { + return message_id_.is_valid() || story_full_id_.is_valid(); + } + + MessageInputReplyTo() = default; + + MessageInputReplyTo(MessageId message_id, StoryFullId story_full_id) + : message_id_(message_id), story_full_id_(story_full_id) { + CHECK(!story_full_id_.is_valid() || !message_id_.is_valid()); + } + + explicit MessageInputReplyTo(const td_api::object_ptr &reply_to_ptr); + + telegram_api::object_ptr get_input_reply_to(Td *td, + MessageId top_thread_message_id) const; +}; + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReplyTo &input_reply_to); + +} // namespace td diff --git a/td/telegram/MessageReplyHeader.h b/td/telegram/MessageReplyHeader.h index b52269355..5f596768b 100644 --- a/td/telegram/MessageReplyHeader.h +++ b/td/telegram/MessageReplyHeader.h @@ -10,7 +10,6 @@ #include "td/telegram/MessageId.h" #include "td/telegram/StoryFullId.h" #include "td/telegram/telegram_api.h" -#include "td/telegram/UserId.h" #include "td/utils/common.h" diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 61cfec2d7..6d91b1ce0 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -3132,7 +3132,7 @@ class SendMessageQuery final : public Td::ResultHandler { public: void send(int32 flags, DialogId dialog_id, tl_object_ptr as_input_peer, - MessageId reply_to_message_id, MessageId top_thread_message_id, int32 schedule_date, + MessageInputReplyTo input_reply_to, MessageId top_thread_message_id, int32 schedule_date, tl_object_ptr &&reply_markup, vector> &&entities, const string &text, bool is_copy, int64 random_id, NetQueryRef *send_query_ref) { @@ -3144,6 +3144,11 @@ class SendMessageQuery final : public Td::ResultHandler { return on_error(Status::Error(400, "Have no write access to the chat")); } + auto reply_to = input_reply_to.get_input_reply_to(td_, top_thread_message_id); + + if (reply_to != nullptr) { + flags |= telegram_api::messages_sendMessage::REPLY_TO_MASK; + } if (!entities.empty()) { flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_ENTITIES; } @@ -3154,9 +3159,8 @@ class SendMessageQuery final : public Td::ResultHandler { auto query = G()->net_query_creator().create( telegram_api::messages_sendMessage( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, std::move(input_peer), - MessagesManager::get_input_reply_to(reply_to_message_id, top_thread_message_id), text, random_id, - std::move(reply_markup), std::move(entities), schedule_date, std::move(as_input_peer)), + false /*ignored*/, std::move(input_peer), std::move(reply_to), text, random_id, std::move(reply_markup), + std::move(entities), schedule_date, std::move(as_input_peer)), {{dialog_id, MessageContentType::Text}, {dialog_id, is_copy ? MessageContentType::Photo : MessageContentType::Text}}); if (td_->option_manager_->get_option_boolean("use_quick_ack")) { @@ -3270,25 +3274,27 @@ class SendInlineBotResultQuery final : public Td::ResultHandler { public: NetQueryRef send(int32 flags, DialogId dialog_id, tl_object_ptr as_input_peer, - MessageId reply_to_message_id, MessageId top_thread_message_id, int32 schedule_date, int64 random_id, - int64 query_id, const string &result_id) { + MessageInputReplyTo input_reply_to, MessageId top_thread_message_id, int32 schedule_date, + int64 random_id, int64 query_id, const string &result_id) { random_id_ = random_id; dialog_id_ = dialog_id; auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); CHECK(input_peer != nullptr); + auto reply_to = input_reply_to.get_input_reply_to(td_, top_thread_message_id); + + if (reply_to != nullptr) { + flags |= telegram_api::messages_sendInlineBotResult::REPLY_TO_MASK; + } if (as_input_peer != nullptr) { flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_SEND_AS; } - CHECK(reply_to_message_id == MessageId() || reply_to_message_id.is_server()); - CHECK(top_thread_message_id == MessageId() || top_thread_message_id.is_server()); auto query = G()->net_query_creator().create( telegram_api::messages_sendInlineBotResult( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer), - MessagesManager::get_input_reply_to(reply_to_message_id, top_thread_message_id), random_id, query_id, - result_id, schedule_date, std::move(as_input_peer)), + std::move(reply_to), random_id, query_id, result_id, schedule_date, std::move(as_input_peer)), {{dialog_id, MessageContentType::Text}, {dialog_id, MessageContentType::Photo}}); auto send_query_ref = query.get_weak(); send_query(std::move(query)); @@ -3326,7 +3332,7 @@ class SendMultiMediaQuery final : public Td::ResultHandler { public: void send(int32 flags, DialogId dialog_id, tl_object_ptr as_input_peer, - MessageId reply_to_message_id, MessageId top_thread_message_id, int32 schedule_date, + MessageInputReplyTo input_reply_to, MessageId top_thread_message_id, int32 schedule_date, vector &&file_ids, vector> &&input_single_media, bool is_copy) { for (auto &single_media : input_single_media) { @@ -3343,18 +3349,21 @@ class SendMultiMediaQuery final : public Td::ResultHandler { return on_error(Status::Error(400, "Have no write access to the chat")); } + auto reply_to = input_reply_to.get_input_reply_to(td_, top_thread_message_id); + + if (reply_to != nullptr) { + flags |= telegram_api::messages_sendMultiMedia::REPLY_TO_MASK; + } if (as_input_peer != nullptr) { flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_SEND_AS; } // no quick ack, because file reference errors are very likely to happen - CHECK(reply_to_message_id == MessageId() || reply_to_message_id.is_server()); - CHECK(top_thread_message_id == MessageId() || top_thread_message_id.is_server()); send_query(G()->net_query_creator().create( - telegram_api::messages_sendMultiMedia( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_peer), MessagesManager::get_input_reply_to(reply_to_message_id, top_thread_message_id), - std::move(input_single_media), schedule_date, std::move(as_input_peer)), + telegram_api::messages_sendMultiMedia(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, + false /*ignored*/, false /*ignored*/, std::move(input_peer), + std::move(reply_to), std::move(input_single_media), schedule_date, + std::move(as_input_peer)), {{dialog_id, is_copy ? MessageContentType::Text : MessageContentType::Photo}, {dialog_id, MessageContentType::Photo}})); } @@ -3441,7 +3450,7 @@ class SendMediaQuery final : public Td::ResultHandler { public: void send(FileId file_id, FileId thumbnail_file_id, int32 flags, DialogId dialog_id, - tl_object_ptr as_input_peer, MessageId reply_to_message_id, + tl_object_ptr as_input_peer, MessageInputReplyTo input_reply_to, MessageId top_thread_message_id, int32 schedule_date, tl_object_ptr &&reply_markup, vector> &&entities, const string &text, @@ -3460,6 +3469,11 @@ class SendMediaQuery final : public Td::ResultHandler { return on_error(Status::Error(400, "Have no write access to the chat")); } + auto reply_to = input_reply_to.get_input_reply_to(td_, top_thread_message_id); + + if (reply_to != nullptr) { + flags |= telegram_api::messages_sendMedia::REPLY_TO_MASK; + } if (!entities.empty()) { flags |= telegram_api::messages_sendMedia::ENTITIES_MASK; } @@ -3467,14 +3481,11 @@ class SendMediaQuery final : public Td::ResultHandler { flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_SEND_AS; } - CHECK(reply_to_message_id == MessageId() || reply_to_message_id.is_server()); - CHECK(top_thread_message_id == MessageId() || top_thread_message_id.is_server()); auto query = G()->net_query_creator().create( telegram_api::messages_sendMedia( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_peer), MessagesManager::get_input_reply_to(reply_to_message_id, top_thread_message_id), - std::move(input_media), text, random_id, std::move(reply_markup), std::move(entities), schedule_date, - std::move(as_input_peer)), + std::move(input_peer), std::move(reply_to), std::move(input_media), text, random_id, + std::move(reply_markup), std::move(entities), schedule_date, std::move(as_input_peer)), {{dialog_id, content_type}, {dialog_id, is_copy ? MessageContentType::Text : content_type}}); if (td_->option_manager_->get_option_boolean("use_quick_ack") && was_uploaded_) { query->quick_ack_promise_ = PromiseCreator::lambda([random_id](Result result) { @@ -12031,7 +12042,7 @@ void MessagesManager::read_all_dialog_mentions(DialogId dialog_id, MessageId top return promise.set_error(Status::Error(400, "Chat not found")); } - TRY_STATUS_PROMISE(promise, can_use_top_thread_message_id(d, top_thread_message_id, MessageId())); + TRY_STATUS_PROMISE(promise, can_use_top_thread_message_id(d, top_thread_message_id, MessageInputReplyTo())); if (!have_input_peer(dialog_id, AccessRights::Read)) { return promise.set_error(Status::Error(400, "Chat is not accessible")); @@ -12134,7 +12145,7 @@ void MessagesManager::read_all_dialog_reactions(DialogId dialog_id, MessageId to return promise.set_error(Status::Error(400, "Chat not found")); } - TRY_STATUS_PROMISE(promise, can_use_top_thread_message_id(d, top_thread_message_id, MessageId())); + TRY_STATUS_PROMISE(promise, can_use_top_thread_message_id(d, top_thread_message_id, MessageInputReplyTo())); if (!have_input_peer(dialog_id, AccessRights::Read)) { return promise.set_error(Status::Error(400, "Chat is not accessible")); @@ -19001,7 +19012,7 @@ Status MessagesManager::set_dialog_draft_message(DialogId dialog_id, MessageId t } TRY_STATUS(can_send_message(dialog_id)); - TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, MessageId())); + TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, MessageInputReplyTo())); TRY_RESULT(new_draft_message, DraftMessage::get_draft_message(td_, dialog_id, top_thread_message_id, std::move(draft_message))); @@ -24298,9 +24309,10 @@ DialogId MessagesManager::get_dialog_default_send_message_as_dialog_id(DialogId return d->default_send_message_as_dialog_id; } -MessageId MessagesManager::get_reply_to_message_id(DialogId dialog_id, MessageId top_thread_message_id, - MessageId message_id, bool for_draft) { - return get_reply_to_message_id(get_dialog(dialog_id), top_thread_message_id, message_id, for_draft); +MessageInputReplyTo MessagesManager::get_message_input_reply_to(DialogId dialog_id, MessageId top_thread_message_id, + td_api::object_ptr &&reply_to, + bool for_draft) { + return get_message_input_reply_to(get_dialog(dialog_id), top_thread_message_id, std::move(reply_to), for_draft); } int64 MessagesManager::generate_new_random_id(const Dialog *d) { @@ -24313,7 +24325,7 @@ int64 MessagesManager::generate_new_random_id(const Dialog *d) { } unique_ptr MessagesManager::create_message_to_send( - Dialog *d, MessageId top_thread_message_id, MessageId reply_to_message_id, const MessageSendOptions &options, + Dialog *d, MessageId top_thread_message_id, MessageInputReplyTo input_reply_to, const MessageSendOptions &options, unique_ptr &&content, bool suppress_reply_info, unique_ptr forward_info, bool is_copy, DialogId send_as_dialog_id) const { CHECK(d != nullptr); @@ -24327,23 +24339,23 @@ unique_ptr MessagesManager::create_message_to_send( int64 reply_to_random_id = 0; bool is_topic_message = false; - if (reply_to_message_id.is_valid()) { - // the message was forcely preloaded in get_reply_to_message_id + if (input_reply_to.message_id_.is_valid()) { + // the message was forcely preloaded in get_message_input_reply_to // it can be missing, only if it is unknown message from a push notification, or an unknown top thread message - const Message *reply_m = get_message(d, reply_to_message_id); + const Message *reply_m = get_message(d, input_reply_to.message_id_); if (reply_m != nullptr) { if (reply_m->top_thread_message_id.is_valid()) { top_thread_message_id = reply_m->top_thread_message_id; } is_topic_message = reply_m->is_topic_message; } - if (dialog_type == DialogType::SecretChat || reply_to_message_id.is_yet_unsent()) { + if (dialog_type == DialogType::SecretChat || input_reply_to.message_id_.is_yet_unsent()) { if (reply_m != nullptr) { reply_to_random_id = reply_m->random_id; } else { CHECK(dialog_type == DialogType::SecretChat); CHECK(top_thread_message_id == MessageId()); - reply_to_message_id = MessageId(); + input_reply_to.message_id_ = MessageId(); } } } else if (top_thread_message_id.is_valid()) { @@ -24388,8 +24400,9 @@ unique_ptr MessagesManager::create_message_to_send( } m->send_date = G()->unix_time(); m->date = is_scheduled ? options.schedule_date : m->send_date; - m->reply_to_message_id = reply_to_message_id; + m->reply_to_message_id = input_reply_to.message_id_; m->reply_to_random_id = reply_to_random_id; + m->reply_to_story_full_id = input_reply_to.story_full_id_; m->top_thread_message_id = top_thread_message_id; m->is_topic_message = is_topic_message; m->is_channel_post = is_channel_post; @@ -24415,7 +24428,7 @@ unique_ptr MessagesManager::create_message_to_send( if (is_channel_post) { return td_->contacts_manager_->get_channel_has_linked_channel(dialog_id.get_channel_id()); } - return !reply_to_message_id.is_valid(); + return !input_reply_to.is_valid(); }()) { m->reply_info.reply_count_ = 0; if (is_channel_post) { @@ -24452,12 +24465,12 @@ unique_ptr MessagesManager::create_message_to_send( } MessagesManager::Message *MessagesManager::get_message_to_send( - Dialog *d, MessageId top_thread_message_id, MessageId reply_to_message_id, const MessageSendOptions &options, + Dialog *d, MessageId top_thread_message_id, MessageInputReplyTo input_reply_to, const MessageSendOptions &options, unique_ptr &&content, bool *need_update_dialog_pos, bool suppress_reply_info, unique_ptr forward_info, bool is_copy, DialogId send_as_dialog_id) { d->was_opened = true; - auto message = create_message_to_send(d, top_thread_message_id, reply_to_message_id, options, std::move(content), + auto message = create_message_to_send(d, top_thread_message_id, input_reply_to, options, std::move(content), suppress_reply_info, std::move(forward_info), is_copy, send_as_dialog_id); MessageId message_id = options.schedule_date != 0 ? get_next_yet_unsent_scheduled_message_id(d, options.schedule_date) @@ -24531,38 +24544,60 @@ MessageId MessagesManager::get_persistent_message_id(const Dialog *d, MessageId return message_id; } -MessageId MessagesManager::get_reply_to_message_id(Dialog *d, MessageId top_thread_message_id, MessageId message_id, - bool for_draft) { +MessageInputReplyTo MessagesManager::get_message_input_reply_to(Dialog *d, MessageId top_thread_message_id, + td_api::object_ptr &&reply_to, + bool for_draft) { CHECK(d != nullptr); - if (top_thread_message_id.is_valid() && !have_message_force(d, top_thread_message_id, "get_reply_to_message_id 1")) { - LOG(INFO) << "Have reply to " << message_id << " in the thread of unknown " << top_thread_message_id; + if (top_thread_message_id.is_valid() && + !have_message_force(d, top_thread_message_id, "get_message_input_reply_to 1")) { + LOG(INFO) << "Have reply in the thread of unknown " << top_thread_message_id; + } + if (reply_to != nullptr && reply_to->get_id() == td_api::messageReplyToStory::ID) { + CHECK(!for_draft); + auto reply_to_story = td_api::move_object_as(reply_to); + auto story_id = StoryId(reply_to_story->story_id_); + auto sender_user_id = UserId(reply_to_story->sender_user_id_); + if (d->dialog_id != DialogId(sender_user_id)) { + LOG(INFO) << "Ignore reply to story from " << sender_user_id << " in a wrong " << d->dialog_id; + return {}; + } + if (!story_id.is_server()) { + LOG(INFO) << "Ignore reply to invalid " << story_id; + return {}; + } + return {MessageId(), StoryFullId(DialogId(sender_user_id), story_id)}; + } + MessageId message_id; + if (reply_to != nullptr && reply_to->get_id() == td_api::messageReplyToMessage::ID) { + auto reply_to_message = td_api::move_object_as(reply_to); + message_id = MessageId(reply_to_message->message_id_); } if (!message_id.is_valid()) { if (!for_draft && message_id == MessageId() && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) { - return top_thread_message_id; + return {top_thread_message_id, StoryFullId()}; } - return MessageId(); + return {}; } message_id = get_persistent_message_id(d, message_id); - const Message *m = get_message_force(d, message_id, "get_reply_to_message_id 2"); + const Message *m = get_message_force(d, message_id, "get_message_input_reply_to 2"); if (m == nullptr || m->message_id.is_yet_unsent() || (m->message_id.is_local() && d->dialog_id.get_type() != DialogType::SecretChat)) { if (message_id.is_server() && d->dialog_id.get_type() != DialogType::SecretChat && message_id > d->last_new_message_id && (d->notification_info != nullptr && message_id <= d->notification_info->max_notification_message_id_)) { // allow to reply yet unreceived server message - return message_id; + return {message_id, StoryFullId()}; } if (!for_draft && top_thread_message_id.is_valid() && top_thread_message_id.is_server()) { - return top_thread_message_id; + return {top_thread_message_id, StoryFullId()}; } // TODO local replies to local messages can be allowed // TODO replies to yet unsent messages can be allowed with special handling of them on application restart - return MessageId(); + return {}; } - return m->message_id; + return {m->message_id, StoryFullId()}; } void MessagesManager::fix_server_reply_to_message_id(DialogId dialog_id, MessageId message_id, @@ -24919,7 +24954,7 @@ class MessagesManager::SendMessageLogEvent { }; Result> MessagesManager::send_message( - DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id, + DialogId dialog_id, MessageId top_thread_message_id, td_api::object_ptr &&reply_to, tl_object_ptr &&options, tl_object_ptr &&reply_markup, tl_object_ptr &&input_message_content) { if (input_message_content == nullptr) { @@ -24931,14 +24966,12 @@ Result> MessagesManager::send_message( return Status::Error(400, "Chat not found"); } - LOG(INFO) << "Begin to send message to " << dialog_id << " in reply to " << reply_to_message_id; - - reply_to_message_id = get_reply_to_message_id(d, top_thread_message_id, reply_to_message_id, false); + auto input_reply_to = get_message_input_reply_to(d, top_thread_message_id, std::move(reply_to), false); if (input_message_content->get_id() == td_api::inputMessageForwarded::ID) { auto input_message = td_api::move_object_as(input_message_content); TRY_RESULT(copy_options, process_message_copy_options(dialog_id, std::move(input_message->copy_options_))); - copy_options.reply_to_message_id = reply_to_message_id; + copy_options.input_reply_to = std::move(input_reply_to); TRY_RESULT_ASSIGN(copy_options.reply_markup, get_dialog_reply_markup(dialog_id, std::move(reply_markup))); return forward_message(dialog_id, top_thread_message_id, DialogId(input_message->from_chat_id_), MessageId(input_message->message_id_), std::move(options), input_message->in_game_share_, @@ -24950,12 +24983,12 @@ Result> MessagesManager::send_message( TRY_RESULT(message_content, process_input_message_content(dialog_id, std::move(input_message_content))); TRY_RESULT(message_send_options, process_message_send_options(dialog_id, std::move(options), true)); TRY_STATUS(can_use_message_send_options(message_send_options, message_content)); - TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, reply_to_message_id)); + TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, input_reply_to)); // there must be no errors after get_message_to_send call bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, top_thread_message_id, reply_to_message_id, message_send_options, + Message *m = get_message_to_send(d, top_thread_message_id, input_reply_to, message_send_options, dup_message_content(td_, dialog_id, message_content.content.get(), MessageContentDupType::Send, MessageCopyOptions()), &need_update_dialog_pos, false, nullptr, message_content.via_bot_user_id.is_valid()); @@ -25126,7 +25159,7 @@ Status MessagesManager::can_use_message_send_options(const MessageSendOptions &o } Status MessagesManager::can_use_top_thread_message_id(Dialog *d, MessageId top_thread_message_id, - MessageId reply_to_message_id) { + const MessageInputReplyTo &input_reply_to) { if (top_thread_message_id == MessageId()) { return Status::OK(); } @@ -25137,8 +25170,11 @@ Status MessagesManager::can_use_top_thread_message_id(Dialog *d, MessageId top_t if (d->dialog_id.get_type() != DialogType::Channel || is_broadcast_channel(d->dialog_id)) { return Status::Error(400, "Chat doesn't have threads"); } - if (reply_to_message_id.is_valid()) { - const Message *reply_m = get_message_force(d, reply_to_message_id, "can_use_top_thread_message_id 1"); + if (input_reply_to.story_full_id_.is_valid()) { + return Status::Error(400, "Can't send story replies to the thread"); + } + if (input_reply_to.message_id_.is_valid()) { + const Message *reply_m = get_message_force(d, input_reply_to.message_id_, "can_use_top_thread_message_id 1"); if (reply_m != nullptr && top_thread_message_id != reply_m->top_thread_message_id) { if (reply_m->top_thread_message_id.is_valid() || reply_m->media_album_id == 0) { return Status::Error(400, "The message to reply is not in the specified message thread"); @@ -25165,7 +25201,7 @@ int64 MessagesManager::generate_new_media_album_id() { } Result> MessagesManager::send_message_group( - DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id, + DialogId dialog_id, MessageId top_thread_message_id, td_api::object_ptr &&reply_to, tl_object_ptr &&options, vector> &&input_message_contents, bool only_preview) { if (input_message_contents.size() > MAX_GROUPED_MESSAGES) { @@ -25204,8 +25240,8 @@ Result> MessagesManager::send_message_group } } - reply_to_message_id = get_reply_to_message_id(d, top_thread_message_id, reply_to_message_id, false); - TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, reply_to_message_id)); + auto input_reply_to = get_message_input_reply_to(d, top_thread_message_id, std::move(reply_to), false); + TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, input_reply_to)); int64 media_album_id = 0; if (message_contents.size() > 1) { @@ -25221,7 +25257,7 @@ Result> MessagesManager::send_message_group unique_ptr message; Message *m; if (only_preview) { - message = create_message_to_send(d, top_thread_message_id, reply_to_message_id, message_send_options, + message = create_message_to_send(d, top_thread_message_id, input_reply_to, message_send_options, std::move(message_content.first), i != 0, nullptr, false, DialogId()); MessageId new_message_id = message_send_options.schedule_date != 0 ? get_next_yet_unsent_scheduled_message_id(d, message_send_options.schedule_date) @@ -25229,7 +25265,7 @@ Result> MessagesManager::send_message_group message->message_id = new_message_id; m = message.get(); } else { - m = get_message_to_send(d, top_thread_message_id, reply_to_message_id, message_send_options, + m = get_message_to_send(d, top_thread_message_id, input_reply_to, message_send_options, dup_message_content(td_, dialog_id, message_content.first.get(), MessageContentDupType::Send, MessageCopyOptions()), &need_update_dialog_pos, i != 0); @@ -25398,8 +25434,8 @@ void MessagesManager::on_message_media_uploaded(DialogId dialog_id, const Messag int64 random_id = begin_send_message(dialog_id, m); td_->create_handler()->send( file_id, thumbnail_file_id, get_message_flags(m), dialog_id, get_send_message_as_input_peer(m), - m->reply_to_message_id, m->top_thread_message_id, get_message_schedule_date(m), - get_input_reply_markup(td_->contacts_manager_.get(), m->reply_markup), + {m->reply_to_message_id, m->reply_to_story_full_id}, m->top_thread_message_id, + get_message_schedule_date(m), get_input_reply_markup(td_->contacts_manager_.get(), m->reply_markup), get_input_message_entities(td_->contacts_manager_.get(), caption, "on_message_media_uploaded"), caption == nullptr ? "" : caption->text, std::move(input_media), m->content->get_type(), m->is_copy, random_id, &m->send_query_ref); @@ -25699,7 +25735,7 @@ void MessagesManager::do_send_message_group(int64 media_album_id) { vector random_ids; vector> input_single_media; tl_object_ptr as_input_peer; - MessageId reply_to_message_id; + MessageInputReplyTo input_reply_to; MessageId top_thread_message_id; int32 flags = 0; int32 schedule_date = 0; @@ -25712,7 +25748,7 @@ void MessagesManager::do_send_message_group(int64 media_album_id) { continue; } - reply_to_message_id = m->reply_to_message_id; + input_reply_to = {m->reply_to_message_id, m->reply_to_story_full_id}; top_thread_message_id = m->top_thread_message_id; flags = get_message_flags(m); schedule_date = get_message_schedule_date(m); @@ -25779,7 +25815,7 @@ void MessagesManager::do_send_message_group(int64 media_album_id) { if (input_single_media.empty()) { LOG(INFO) << "Media group " << media_album_id << " from " << dialog_id << " is empty"; } - td_->create_handler()->send(flags, dialog_id, std::move(as_input_peer), reply_to_message_id, + td_->create_handler()->send(flags, dialog_id, std::move(as_input_peer), input_reply_to, top_thread_message_id, schedule_date, std::move(file_ids), std::move(input_single_media), is_copy); } @@ -25811,8 +25847,8 @@ void MessagesManager::on_text_message_ready_to_send(DialogId dialog_id, MessageI int64 random_id = begin_send_message(dialog_id, m); td_->create_handler()->send( - get_message_flags(m), dialog_id, get_send_message_as_input_peer(m), m->reply_to_message_id, - m->top_thread_message_id, get_message_schedule_date(m), + get_message_flags(m), dialog_id, get_send_message_as_input_peer(m), + {m->reply_to_message_id, m->reply_to_story_full_id}, m->top_thread_message_id, get_message_schedule_date(m), get_input_reply_markup(td_->contacts_manager_.get(), m->reply_markup), get_input_message_entities(td_->contacts_manager_.get(), message_text->entities, "do_send_message"), message_text->text, m->is_copy, random_id, &m->send_query_ref); @@ -25892,7 +25928,6 @@ void MessagesManager::on_yet_unsent_media_queue_updated(DialogId dialog_id) { Result MessagesManager::send_bot_start_message(UserId bot_user_id, DialogId dialog_id, const string ¶meter) { - LOG(INFO) << "Begin to send bot start message to " << dialog_id; CHECK(!td_->auth_manager_->is_bot()); TRY_RESULT(bot_data, td_->contacts_manager_->get_bot_data(bot_user_id)); @@ -25963,7 +25998,7 @@ Result MessagesManager::send_bot_start_message(UserId bot_user_id, Di vector text_entities; text_entities.emplace_back(MessageEntity::Type::BotCommand, 0, narrow_cast(text.size())); bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, MessageId(), MessageId(), MessageSendOptions(), + Message *m = get_message_to_send(d, MessageId(), MessageInputReplyTo(), MessageSendOptions(), create_text_message_content(text, std::move(text_entities), WebPageId()), &need_update_dialog_pos); m->is_bot_start_message = true; @@ -26056,13 +26091,9 @@ void MessagesManager::do_send_bot_start_message(UserId bot_user_id, DialogId dia std::move(input_peer), parameter, random_id); } -Result MessagesManager::send_inline_query_result_message(DialogId dialog_id, MessageId top_thread_message_id, - MessageId reply_to_message_id, - tl_object_ptr &&options, - int64 query_id, const string &result_id, - bool hide_via_bot) { - LOG(INFO) << "Begin to send inline query result message to " << dialog_id << " in reply to " << reply_to_message_id; - +Result MessagesManager::send_inline_query_result_message( + DialogId dialog_id, MessageId top_thread_message_id, td_api::object_ptr &&reply_to, + tl_object_ptr &&options, int64 query_id, const string &result_id, bool hide_via_bot) { Dialog *d = get_dialog_force(dialog_id, "send_inline_query_result_message"); if (d == nullptr) { return Status::Error(400, "Chat not found"); @@ -26097,13 +26128,13 @@ Result MessagesManager::send_inline_query_result_message(DialogId dia return Status::Error(400, "Inline query result not found"); } - reply_to_message_id = get_reply_to_message_id(d, top_thread_message_id, reply_to_message_id, false); + auto input_reply_to = get_message_input_reply_to(d, top_thread_message_id, std::move(reply_to), false); TRY_STATUS(can_use_message_send_options(message_send_options, content->message_content, 0)); TRY_STATUS(can_send_message_content(dialog_id, content->message_content.get(), false, td_)); - TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, reply_to_message_id)); + TRY_STATUS(can_use_top_thread_message_id(d, top_thread_message_id, input_reply_to)); bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, top_thread_message_id, reply_to_message_id, message_send_options, + Message *m = get_message_to_send(d, top_thread_message_id, input_reply_to, message_send_options, dup_message_content(td_, dialog_id, content->message_content.get(), MessageContentDupType::SendViaBot, MessageCopyOptions()), &need_update_dialog_pos, false, nullptr, true); @@ -26204,8 +26235,8 @@ void MessagesManager::do_send_inline_query_result_message(DialogId dialog_id, Me flags |= telegram_api::messages_sendInlineBotResult::HIDE_VIA_MASK; } m->send_query_ref = td_->create_handler()->send( - flags, dialog_id, get_send_message_as_input_peer(m), m->reply_to_message_id, m->top_thread_message_id, - get_message_schedule_date(m), random_id, query_id, result_id); + flags, dialog_id, get_send_message_as_input_peer(m), {m->reply_to_message_id, m->reply_to_story_full_id}, + m->top_thread_message_id, get_message_schedule_date(m), random_id, query_id, result_id); } bool MessagesManager::has_qts_messages(DialogId dialog_id) const { @@ -26521,7 +26552,6 @@ void MessagesManager::edit_message_text(FullMessageId full_message_id, return promise.set_error(Status::Error(400, "Input message content type must be InputMessageText")); } - LOG(INFO) << "Begin to edit text of " << full_message_id; auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog_force(dialog_id, "edit_message_text"); if (d == nullptr) { @@ -26575,7 +26605,6 @@ void MessagesManager::edit_message_live_location(FullMessageId full_message_id, tl_object_ptr &&reply_markup, tl_object_ptr &&input_location, int32 heading, int32 proximity_alert_radius, Promise &&promise) { - LOG(INFO) << "Begin to edit live location of " << full_message_id; auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog_force(dialog_id, "edit_message_live_location"); if (d == nullptr) { @@ -26748,7 +26777,6 @@ void MessagesManager::edit_message_media(FullMessageId full_message_id, return promise.set_error(Status::Error(400, "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, "edit_message_media"); if (d == nullptr) { @@ -26824,8 +26852,6 @@ void MessagesManager::edit_message_caption(FullMessageId full_message_id, tl_object_ptr &&reply_markup, tl_object_ptr &&input_caption, Promise &&promise) { - LOG(INFO) << "Begin to edit caption of " << full_message_id; - auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog_force(dialog_id, "edit_message_caption"); if (d == nullptr) { @@ -26874,7 +26900,6 @@ void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id, Promise &&promise) { CHECK(td_->auth_manager_->is_bot()); - LOG(INFO) << "Begin to edit reply markup of " << full_message_id; auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog_force(dialog_id, "edit_message_reply_markup"); if (d == nullptr) { @@ -27091,8 +27116,6 @@ void MessagesManager::edit_message_scheduling_state( } auto schedule_date = r_schedule_date.move_as_ok(); - LOG(INFO) << "Begin to reschedule " << full_message_id << " to " << schedule_date; - auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog_force(dialog_id, "edit_message_scheduling_state"); if (d == nullptr) { @@ -27331,9 +27354,6 @@ bool MessagesManager::get_message_disable_web_page_preview(const Message *m) { int32 MessagesManager::get_message_flags(const Message *m) { int32 flags = 0; - if (m->reply_to_message_id.is_valid() || m->top_thread_message_id.is_valid()) { - flags |= SEND_MESSAGE_FLAG_IS_REPLY; - } if (m->disable_web_page_preview) { flags |= SEND_MESSAGE_FLAG_DISABLE_WEB_PAGE_PREVIEW; } @@ -27886,7 +27906,7 @@ Result MessagesManager::get_forwarded_messag TRY_STATUS(can_send_message(to_dialog_id)); TRY_RESULT(message_send_options, process_message_send_options(to_dialog_id, std::move(options), false)); - TRY_STATUS(can_use_top_thread_message_id(to_dialog, top_thread_message_id, MessageId())); + TRY_STATUS(can_use_top_thread_message_id(to_dialog, top_thread_message_id, MessageInputReplyTo())); { MessageId last_message_id; @@ -27965,7 +27985,7 @@ Result MessagesManager::get_forwarded_messag auto type = need_copy ? (is_local_copy ? MessageContentDupType::Copy : MessageContentDupType::ServerCopy) : MessageContentDupType::Forward; - auto reply_to_message_id = copy_options[i].reply_to_message_id; + auto input_reply_to = std::move(copy_options[i].input_reply_to); auto reply_markup = std::move(copy_options[i].reply_markup); unique_ptr content = dup_message_content(td_, to_dialog_id, forwarded_message->content.get(), type, std::move(copy_options[i])); @@ -27974,8 +27994,6 @@ Result MessagesManager::get_forwarded_messag continue; } - reply_to_message_id = get_reply_to_message_id(to_dialog, top_thread_message_id, reply_to_message_id, false); - auto can_send_status = can_send_message_content(to_dialog_id, content.get(), !is_local_copy, td_); if (can_send_status.is_error()) { LOG(INFO) << "Can't forward " << message_id << ": " << can_send_status.message(); @@ -27988,7 +28006,7 @@ Result MessagesManager::get_forwarded_messag continue; } - if (can_use_top_thread_message_id(to_dialog, top_thread_message_id, reply_to_message_id).is_error()) { + if (can_use_top_thread_message_id(to_dialog, top_thread_message_id, input_reply_to).is_error()) { LOG(INFO) << "Ignore invalid message thread ID " << top_thread_message_id; top_thread_message_id = MessageId(); } @@ -28010,7 +28028,7 @@ Result MessagesManager::get_forwarded_messag if (is_local_copy) { auto original_reply_to_message_id = forwarded_message->reply_in_dialog_id == DialogId() ? forwarded_message->reply_to_message_id : MessageId(); - copied_messages.push_back({std::move(content), reply_to_message_id, forwarded_message->message_id, + copied_messages.push_back({std::move(content), input_reply_to, forwarded_message->message_id, original_reply_to_message_id, std::move(reply_markup), forwarded_message->media_album_id, get_message_disable_web_page_preview(forwarded_message), i}); @@ -28108,9 +28126,9 @@ Result> MessagesManager::forward_messages( unique_ptr message; Message *m; if (only_preview) { - message = create_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options, - std::move(content), j + 1 != forwarded_message_contents.size(), - std::move(forward_info), false, DialogId()); + message = create_message_to_send( + to_dialog, top_thread_message_id, {reply_to_message_id, StoryFullId()}, message_send_options, + std::move(content), j + 1 != forwarded_message_contents.size(), std::move(forward_info), false, DialogId()); MessageId new_message_id = message_send_options.schedule_date != 0 ? get_next_yet_unsent_scheduled_message_id(to_dialog, message_send_options.schedule_date) @@ -28118,9 +28136,9 @@ Result> MessagesManager::forward_messages( message->message_id = new_message_id; m = message.get(); } else { - m = get_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options, - std::move(content), &need_update_dialog_pos, j + 1 != forwarded_message_contents.size(), - std::move(forward_info)); + m = get_message_to_send(to_dialog, top_thread_message_id, {reply_to_message_id, StoryFullId()}, + message_send_options, std::move(content), &need_update_dialog_pos, + j + 1 != forwarded_message_contents.size(), std::move(forward_info)); } fix_forwarded_message(m, to_dialog_id, forwarded_message, forwarded_message_contents[j].media_album_id, drop_author); @@ -28159,18 +28177,18 @@ Result> MessagesManager::forward_messages( forwarded_message_id_to_new_message_id.emplace(copied_message.original_message_id, MessageId()); } for (auto &copied_message : copied_messages) { - MessageId reply_to_message_id = copied_message.reply_to_message_id; - if (!reply_to_message_id.is_valid() && copied_message.original_reply_to_message_id.is_valid() && is_secret) { + auto input_reply_to = copied_message.input_reply_to; + 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()) { - reply_to_message_id = it->second; + input_reply_to.message_id_ = it->second; } } unique_ptr message; Message *m; if (only_preview) { - message = create_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options, + message = create_message_to_send(to_dialog, top_thread_message_id, input_reply_to, message_send_options, std::move(copied_message.content), false, nullptr, is_copy, DialogId()); MessageId new_message_id = message_send_options.schedule_date != 0 @@ -28185,7 +28203,7 @@ Result> MessagesManager::forward_messages( extract_authentication_codes(from_dialog_id, forwarded_message, authentication_codes); } - m = get_message_to_send(to_dialog, top_thread_message_id, reply_to_message_id, message_send_options, + m = get_message_to_send(to_dialog, top_thread_message_id, input_reply_to, message_send_options, std::move(copied_message.content), &need_update_dialog_pos, false, nullptr, is_copy); } m->disable_web_page_preview = copied_message.disable_web_page_preview; @@ -28309,8 +28327,7 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v message->update_stickersets_order, message->noforwards, get_message_schedule_date(message.get()), message->sending_id); Message *m = get_message_to_send( - d, message->top_thread_message_id, - get_reply_to_message_id(d, message->top_thread_message_id, message->reply_to_message_id, false), options, + d, message->top_thread_message_id, {message->reply_to_message_id, message->reply_to_story_full_id}, options, std::move(new_contents[i]), &need_update_dialog_pos, false, nullptr, message->is_copy, need_another_sender ? DialogId() : get_message_sender(message.get())); m->reply_markup = std::move(message->reply_markup); @@ -28344,7 +28361,7 @@ void MessagesManager::send_screenshot_taken_notification_message(Dialog *d) { auto dialog_type = d->dialog_id.get_type(); if (dialog_type == DialogType::User) { bool need_update_dialog_pos = false; - const Message *m = get_message_to_send(d, MessageId(), MessageId(), MessageSendOptions(), + const Message *m = get_message_to_send(d, MessageId(), MessageInputReplyTo(), MessageSendOptions(), create_screenshot_taken_message_content(), &need_update_dialog_pos); do_send_screenshot_taken_notification_message(d->dialog_id, m, 0); @@ -28441,13 +28458,13 @@ void MessagesManager::share_dialog_with_bot(FullMessageId full_message_id, int32 } Result MessagesManager::add_local_message( - DialogId dialog_id, td_api::object_ptr &&sender, MessageId reply_to_message_id, - bool disable_notification, tl_object_ptr &&input_message_content) { + DialogId dialog_id, td_api::object_ptr &&sender, + td_api::object_ptr &&reply_to, bool disable_notification, + tl_object_ptr &&input_message_content) { if (input_message_content == nullptr) { return Status::Error(400, "Can't add local message without content"); } - LOG(INFO) << "Begin to add local message to " << dialog_id << " in reply to " << reply_to_message_id; Dialog *d = get_dialog_force(dialog_id, "add_local_message"); if (d == nullptr) { return Status::Error(400, "Chat not found"); @@ -28505,6 +28522,8 @@ Result MessagesManager::add_local_message( } } + auto input_reply_to = get_message_input_reply_to(d, MessageId(), std::move(reply_to), false); + MessageId message_id = get_next_local_message_id(d); auto message = make_unique(); @@ -28521,7 +28540,8 @@ Result MessagesManager::add_local_message( m->sender_dialog_id = sender_dialog_id; } m->date = G()->unix_time(); - m->reply_to_message_id = get_reply_to_message_id(d, MessageId(), reply_to_message_id, false); + m->reply_to_message_id = input_reply_to.message_id_; + m->reply_to_story_full_id = input_reply_to.story_full_id_; if (m->reply_to_message_id.is_valid() && !message_id.is_scheduled()) { const Message *reply_m = get_message(d, m->reply_to_message_id); if (reply_m != nullptr) { @@ -33696,8 +33716,6 @@ void MessagesManager::set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Prom return promise.set_error(Status::Error(400, "Have no write access to the chat")); } - LOG(INFO) << "Begin to set message auto-delete time in " << dialog_id << " to " << ttl; - switch (dialog_id.get_type()) { case DialogType::User: if (dialog_id == get_my_dialog_id() || @@ -33734,7 +33752,7 @@ void MessagesManager::set_dialog_message_ttl(DialogId dialog_id, int32 ttl, Prom td_->create_handler(std::move(promise))->send(dialog_id, ttl); } else { bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, MessageId(), MessageId(), MessageSendOptions(), + Message *m = get_message_to_send(d, MessageId(), MessageInputReplyTo(), MessageSendOptions(), create_chat_set_ttl_message_content(ttl, UserId()), &need_update_dialog_pos); send_update_new_message(d, m); @@ -33975,7 +33993,7 @@ void MessagesManager::unpin_all_dialog_messages(DialogId dialog_id, MessageId to return promise.set_error(Status::Error(400, "Chat not found")); } TRY_STATUS_PROMISE(promise, can_pin_messages(dialog_id)); - TRY_STATUS_PROMISE(promise, can_use_top_thread_message_id(d, top_thread_message_id, MessageId())); + TRY_STATUS_PROMISE(promise, can_use_top_thread_message_id(d, top_thread_message_id, MessageInputReplyTo())); if (!td_->auth_manager_->is_bot()) { auto message_ids = find_dialog_messages(d, [top_thread_message_id](const Message *m) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index b8ce05455..404494bdd 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -34,6 +34,7 @@ #include "td/telegram/MessageCopyOptions.h" #include "td/telegram/MessageDb.h" #include "td/telegram/MessageId.h" +#include "td/telegram/MessageInputReplyTo.h" #include "td/telegram/MessageLinkInfo.h" #include "td/telegram/MessageReplyHeader.h" #include "td/telegram/MessageReplyInfo.h" @@ -140,7 +141,6 @@ class MessagesManager final : public Actor { static constexpr int32 MESSAGE_FLAG_HAS_TTL_PERIOD = 1 << 25; static constexpr int32 MESSAGE_FLAG_NOFORWARDS = 1 << 26; - static constexpr int32 SEND_MESSAGE_FLAG_IS_REPLY = 1 << 0; static constexpr int32 SEND_MESSAGE_FLAG_DISABLE_WEB_PAGE_PREVIEW = 1 << 1; static constexpr int32 SEND_MESSAGE_FLAG_HAS_REPLY_MARKUP = 1 << 2; static constexpr int32 SEND_MESSAGE_FLAG_HAS_ENTITIES = 1 << 3; @@ -165,24 +165,6 @@ class MessagesManager final : public Actor { MessagesManager &operator=(MessagesManager &&) = delete; ~MessagesManager() final; - static telegram_api::object_ptr get_input_reply_to(MessageId reply_to_message_id, - MessageId top_thread_message_id) { - if (reply_to_message_id == MessageId()) { - if (top_thread_message_id == MessageId()) { - return nullptr; - } - reply_to_message_id = top_thread_message_id; - } - CHECK(reply_to_message_id.is_server()); - int32 flags = 0; - if (top_thread_message_id != MessageId()) { - CHECK(top_thread_message_id.is_server()); - flags |= telegram_api::inputReplyToMessage::TOP_MSG_ID_MASK; - } - return telegram_api::make_object( - flags, reply_to_message_id.get_server_message_id().get(), top_thread_message_id.get_server_message_id().get()); - } - tl_object_ptr get_input_peer(DialogId dialog_id, AccessRights access_rights) const; static tl_object_ptr get_input_peer_force(DialogId dialog_id); @@ -463,16 +445,16 @@ class MessagesManager final : public Actor { DialogId get_dialog_default_send_message_as_dialog_id(DialogId dialog_id) const; - MessageId get_reply_to_message_id(DialogId dialog_id, MessageId top_thread_message_id, MessageId message_id, - bool for_draft); + MessageInputReplyTo get_message_input_reply_to(DialogId dialog_id, MessageId top_thread_message_id, + td_api::object_ptr &&reply_to, bool for_draft); Result> send_message( - DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id, + DialogId dialog_id, MessageId top_thread_message_id, td_api::object_ptr &&reply_to, tl_object_ptr &&options, tl_object_ptr &&reply_markup, tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; Result> send_message_group( - DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id, + DialogId dialog_id, MessageId top_thread_message_id, td_api::object_ptr &&reply_to, tl_object_ptr &&options, vector> &&input_message_contents, bool only_preview) TD_WARN_UNUSED_RESULT; @@ -481,7 +463,7 @@ class MessagesManager final : public Actor { const string ¶meter) TD_WARN_UNUSED_RESULT; Result send_inline_query_result_message(DialogId dialog_id, MessageId top_thread_message_id, - MessageId reply_to_message_id, + td_api::object_ptr &&reply_to, tl_object_ptr &&options, int64 query_id, const string &result_id, bool hide_via_bot) TD_WARN_UNUSED_RESULT; @@ -501,7 +483,7 @@ class MessagesManager final : public Actor { bool expect_user, bool only_check, Promise &&promise); Result add_local_message(DialogId dialog_id, td_api::object_ptr &&sender, - MessageId reply_to_message_id, bool disable_notification, + td_api::object_ptr &&reply_to, bool disable_notification, tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; @@ -1815,18 +1797,20 @@ class MessagesManager final : public Actor { static Status can_use_message_send_options(const MessageSendOptions &options, const InputMessageContent &content); - Status can_use_top_thread_message_id(Dialog *d, MessageId top_thread_message_id, MessageId reply_to_message_id); + Status can_use_top_thread_message_id(Dialog *d, MessageId top_thread_message_id, + const MessageInputReplyTo &input_reply_to); bool is_anonymous_administrator(DialogId dialog_id, string *author_signature) const; int64 generate_new_random_id(const Dialog *d); - unique_ptr create_message_to_send(Dialog *d, MessageId top_thread_message_id, MessageId reply_to_message_id, - const MessageSendOptions &options, unique_ptr &&content, - bool suppress_reply_info, unique_ptr forward_info, - bool is_copy, DialogId send_as_dialog_id) const; + unique_ptr create_message_to_send(Dialog *d, MessageId top_thread_message_id, + MessageInputReplyTo input_reply_to, const MessageSendOptions &options, + unique_ptr &&content, bool suppress_reply_info, + unique_ptr forward_info, bool is_copy, + DialogId send_as_dialog_id) const; - Message *get_message_to_send(Dialog *d, MessageId top_thread_message_id, MessageId reply_to_message_id, + Message *get_message_to_send(Dialog *d, MessageId top_thread_message_id, MessageInputReplyTo input_reply_to, const MessageSendOptions &options, unique_ptr &&content, bool *need_update_dialog_pos, bool suppress_reply_info = false, unique_ptr forward_info = nullptr, bool is_copy = false, @@ -1864,7 +1848,8 @@ class MessagesManager final : public Actor { static FullMessageId get_replied_message_id(DialogId dialog_id, const Message *m); - MessageId get_reply_to_message_id(Dialog *d, MessageId top_thread_message_id, MessageId message_id, bool for_draft); + MessageInputReplyTo get_message_input_reply_to(Dialog *d, MessageId top_thread_message_id, + td_api::object_ptr &&reply_to, bool for_draft); void fix_server_reply_to_message_id(DialogId dialog_id, MessageId message_id, DialogId reply_in_dialog_id, MessageId &reply_to_message_id) const; @@ -1915,7 +1900,7 @@ class MessagesManager final : public Actor { struct ForwardedMessages { struct CopiedMessage { unique_ptr content; - MessageId reply_to_message_id; + MessageInputReplyTo input_reply_to; MessageId original_message_id; MessageId original_reply_to_message_id; unique_ptr reply_markup; diff --git a/td/telegram/StoryFullId.h b/td/telegram/StoryFullId.h index 2fbfa2877..ba9483a7c 100644 --- a/td/telegram/StoryFullId.h +++ b/td/telegram/StoryFullId.h @@ -42,6 +42,10 @@ struct StoryFullId { return story_id; } + bool is_valid() const { + return dialog_id.is_valid() && story_id.is_valid(); + } + template void store(StorerT &storer) const { dialog_id.store(storer); @@ -57,8 +61,7 @@ struct StoryFullId { struct StoryFullIdHash { uint32 operator()(StoryFullId story_full_id) const { - return DialogIdHash()(story_full_id.get_dialog_id()) * 2023654985u + - StoryIdHash()(story_full_id.get_story_id()); + return DialogIdHash()(story_full_id.get_dialog_id()) * 2023654985u + StoryIdHash()(story_full_id.get_story_id()); } }; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index eb69a340d..3ef6316ca 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5475,7 +5475,7 @@ void Td::on_request(uint64 id, const td_api::setChatMessageSender &request) { void Td::on_request(uint64 id, td_api::sendMessage &request) { auto r_sent_message = messages_manager_->send_message( - DialogId(request.chat_id_), MessageId(request.message_thread_id_), MessageId(request.reply_to_message_id_), + DialogId(request.chat_id_), MessageId(request.message_thread_id_), std::move(request.reply_to_), std::move(request.options_), std::move(request.reply_markup_), std::move(request.input_message_content_)); if (r_sent_message.is_error()) { send_closure(actor_id(this), &Td::send_error, id, r_sent_message.move_as_error()); @@ -5486,7 +5486,7 @@ void Td::on_request(uint64 id, td_api::sendMessage &request) { void Td::on_request(uint64 id, td_api::sendMessageAlbum &request) { auto r_messages = messages_manager_->send_message_group( - DialogId(request.chat_id_), MessageId(request.message_thread_id_), MessageId(request.reply_to_message_id_), + DialogId(request.chat_id_), MessageId(request.message_thread_id_), std::move(request.reply_to_), std::move(request.options_), std::move(request.input_message_contents_), request.only_preview_); if (r_messages.is_error()) { send_closure(actor_id(this), &Td::send_error, id, r_messages.move_as_error()); @@ -5517,8 +5517,8 @@ void Td::on_request(uint64 id, td_api::sendInlineQueryResultMessage &request) { DialogId dialog_id(request.chat_id_); auto r_new_message_id = messages_manager_->send_inline_query_result_message( - dialog_id, MessageId(request.message_thread_id_), MessageId(request.reply_to_message_id_), - std::move(request.options_), request.query_id_, request.result_id_, request.hide_via_bot_); + dialog_id, MessageId(request.message_thread_id_), std::move(request.reply_to_), std::move(request.options_), + request.query_id_, request.result_id_, request.hide_via_bot_); if (r_new_message_id.is_error()) { return send_closure(actor_id(this), &Td::send_error, id, r_new_message_id.move_as_error()); } @@ -5533,9 +5533,9 @@ void Td::on_request(uint64 id, td_api::addLocalMessage &request) { CHECK_IS_USER(); DialogId dialog_id(request.chat_id_); - auto r_new_message_id = messages_manager_->add_local_message( - dialog_id, std::move(request.sender_id_), MessageId(request.reply_to_message_id_), request.disable_notification_, - std::move(request.input_message_content_)); + auto r_new_message_id = + messages_manager_->add_local_message(dialog_id, std::move(request.sender_id_), std::move(request.reply_to_), + request.disable_notification_, std::move(request.input_message_content_)); if (r_new_message_id.is_error()) { return send_closure(actor_id(this), &Td::send_error, id, r_new_message_id.move_as_error()); } @@ -8100,7 +8100,7 @@ void Td::on_request(uint64 id, td_api::openWebApp &request) { CLEAN_INPUT_STRING(request.application_name_); CREATE_REQUEST_PROMISE(); attach_menu_manager_->request_web_view(DialogId(request.chat_id_), UserId(request.bot_user_id_), - MessageId(request.message_thread_id_), MessageId(request.reply_to_message_id_), + MessageId(request.message_thread_id_), std::move(request.reply_to_), std::move(request.url_), std::move(request.theme_), std::move(request.application_name_), std::move(promise)); } diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 2e70600af..73e4da292 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -878,6 +878,38 @@ class CliClient final : public Actor { arg.file_id = as_file_id(args); } + struct MessageReplyTo { + int64 message_id = 0; + // or + int64 user_id = 0; + int32 story_id = 0; + + operator td_api::object_ptr() const { + if (message_id == 0 && user_id == 0 && story_id == 0) { + return nullptr; + } + if (message_id != 0) { + return td_api::make_object(-1, message_id); + } else { + return td_api::make_object(user_id, story_id); + } + } + }; + + void get_args(string &args, MessageReplyTo &arg) const { + if (!args.empty() && args != "0") { + if (args.find('_') == string::npos) { + arg.message_id = as_message_id(args); + } else { + string user_id; + string story_id; + std::tie(user_id, story_id) = split(args, '_'); + arg.user_id = as_user_id(user_id); + arg.story_id = as_story_id(story_id); + } + } + } + struct InputInvoice { int64 chat_id = 0; int64 message_id = 0; @@ -2059,9 +2091,10 @@ class CliClient final : public Actor { } void send_message(int64 chat_id, td_api::object_ptr &&input_message_content, - bool disable_notification = false, bool from_background = false, int64 reply_to_message_id = 0) { + bool disable_notification = false, bool from_background = false, + MessageReplyTo message_reply_to = {}) { auto id = send_request(td_api::make_object( - chat_id, message_thread_id_, reply_to_message_id, + chat_id, message_thread_id_, message_reply_to, td_api::make_object(disable_notification, from_background, true, true, as_message_scheduling_state(schedule_date_), Random::fast(1, 1000)), @@ -4057,11 +4090,11 @@ class CliClient final : public Actor { ChatId chat_id; UserId bot_user_id; string url; - MessageId reply_to_message_id; + MessageReplyTo message_reply_to; MessageThreadId message_thread_id; - get_args(args, chat_id, bot_user_id, url, reply_to_message_id, message_thread_id); + get_args(args, chat_id, bot_user_id, url, message_reply_to, message_thread_id); send_request(td_api::make_object(chat_id, bot_user_id, url, as_theme_parameters(), "android", - reply_to_message_id, message_thread_id)); + message_thread_id, message_reply_to)); } else if (op == "cwa") { int64 launch_id; get_args(args, launch_id); @@ -4115,17 +4148,17 @@ class CliClient final : public Actor { send_request(td_api::make_object(chat_id, as_message_sender(sender_id))); } else if (op == "sm" || op == "sms" || op == "smr" || op == "smf") { ChatId chat_id; - MessageId reply_to_message_id; + MessageReplyTo message_reply_to; string message; get_args(args, chat_id, message); if (op == "smr") { - get_args(message, reply_to_message_id, message); + get_args(message, message_reply_to, message); } if (op == "smf") { message = string(5097, 'a'); } send_message(chat_id, td_api::make_object(as_formatted_text(message), false, true), - op == "sms", false, reply_to_message_id); + op == "sms", false, message_reply_to); } else if (op == "smce") { ChatId chat_id; get_args(args, chat_id); @@ -4137,26 +4170,25 @@ class CliClient final : public Actor { entities.push_back(td_api::make_object( 6, 5, td_api::make_object(5368324170671202286))); auto text = as_formatted_text("👍 😉 🧑‍🚒", std::move(entities)); - send_message(chat_id, td_api::make_object(std::move(text), false, true), false, false, - 0); + send_message(chat_id, td_api::make_object(std::move(text), false, true)); } else if (op == "alm" || op == "almr") { ChatId chat_id; string sender_id; - MessageId reply_to_message_id; + MessageReplyTo message_reply_to; string message; get_args(args, chat_id, sender_id, message); if (op == "almr") { - get_args(message, reply_to_message_id, message); + get_args(message, message_reply_to, message); } send_request(td_api::make_object( - chat_id, as_message_sender(sender_id), reply_to_message_id, false, + chat_id, as_message_sender(sender_id), message_reply_to, false, td_api::make_object(as_formatted_text(message), false, true))); } else if (op == "smap" || op == "smapr" || op == "smapp" || op == "smaprp") { ChatId chat_id; - MessageId reply_to_message_id; + MessageReplyTo message_reply_to; get_args(args, chat_id, args); if (op == "smapr" || op == "smaprp") { - get_args(args, reply_to_message_id, args); + get_args(args, message_reply_to, args); } auto input_message_contents = transform(full_split(args), [this](const string &photo) { td_api::object_ptr content = td_api::make_object( @@ -4165,7 +4197,7 @@ class CliClient final : public Actor { return content; }); send_request(td_api::make_object( - chat_id, message_thread_id_, reply_to_message_id, default_message_send_options(), + chat_id, message_thread_id_, message_reply_to, default_message_send_options(), std::move(input_message_contents), op == "smapp" || op == "smaprp")); } else if (op == "smad" || op == "smadp") { ChatId chat_id; @@ -4175,7 +4207,7 @@ class CliClient final : public Actor { td_api::make_object(as_input_file(document), nullptr, true, as_caption("")); return content; }); - send_request(td_api::make_object(chat_id, message_thread_id_, 0, + send_request(td_api::make_object(chat_id, message_thread_id_, nullptr, default_message_send_options(), std::move(input_message_contents), op.back() == 'p')); } else if (op == "gmft") { @@ -4368,7 +4400,7 @@ class CliClient final : public Actor { string result_id; get_args(args, chat_id, query_id, result_id); send_request(td_api::make_object( - chat_id, message_thread_id_, 0, default_message_send_options(), query_id, result_id, op == "siqrh")); + chat_id, message_thread_id_, nullptr, default_message_send_options(), query_id, result_id, op == "siqrh")); } else if (op == "gcqa") { ChatId chat_id; MessageId message_id;