diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 858c4ab6c..9dd76f9ea 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -7682,6 +7682,12 @@ sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to:Inpu //@remove_caption Pass true to remove media captions of message copies. Ignored if send_copy is false forwardMessages chat_id:int53 message_thread_id:int53 from_chat_id:int53 message_ids:vector options:messageSendOptions send_copy:Bool remove_caption:Bool = Messages; +//@description Sends messages from a quick reply shortcut. Requires Telegram Business subscription +//@chat_id Identifier of the chat to which to send messages. The chat must be a private chat with a regular user +//@shortcut_id Unique identifier of the quick reply shortcut +//@sending_id Non-persistent identifier, which will be returned back in messageSendingStatePending object and can be used to match sent messages and corresponding updateNewMessage updates +sendQuickReplyShortcutMessages chat_id:int53 shortcut_id:int32 sending_id:int32 = Messages; + //@description Resends messages which failed to send. Can be called only for messages for which messageSendingStateFailed.can_retry is true and after specified in messageSendingStateFailed.retry_after time passed. //-If a message is re-sent, the corresponding failed to send message is deleted. Returns the sent messages in the same order as the message identifiers passed in message_ids. If a message can't be re-sent, null will be returned instead of the message //@chat_id Identifier of the chat to send messages diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index caa601f86..9767c1a21 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -66,6 +66,7 @@ #include "td/telegram/Photo.h" #include "td/telegram/PollId.h" #include "td/telegram/PublicDialogType.h" +#include "td/telegram/QuickReplyManager.h" #include "td/telegram/ReactionManager.h" #include "td/telegram/RepliedMessageInfo.hpp" #include "td/telegram/ReplyMarkup.h" @@ -3504,6 +3505,90 @@ class ForwardMessagesQuery final : public Td::ResultHandler { } }; +class SendQuickReplyMessagesQuery final : public Td::ResultHandler { + Promise promise_; + vector random_ids_; + DialogId dialog_id_; + + public: + explicit SendQuickReplyMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, QuickReplyShortcutId shortcut_id, const vector &message_ids, + vector &&random_ids) { + random_ids_ = std::move(random_ids); + dialog_id_ = dialog_id; + + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Have no write access to the chat")); + } + + auto query = G()->net_query_creator().create( + telegram_api::messages_sendQuickReplyMessages(std::move(input_peer), shortcut_id.get()), + {{dialog_id, MessageContentType::Text}, {dialog_id, MessageContentType::Photo}}); + if (td_->option_manager_->get_option_boolean("use_quick_ack")) { + query->quick_ack_promise_ = PromiseCreator::lambda([random_ids = random_ids_](Result result) { + if (result.is_ok()) { + for (auto random_id : random_ids) { + send_closure(G()->messages_manager(), &MessagesManager::on_send_message_get_quick_ack, random_id); + } + } + }); + } + send_query(std::move(query)); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for SendQuickReplyMessagesQuery for " << format::as_array(random_ids_) << ": " + << to_string(ptr); + auto sent_messages = UpdatesManager::get_new_messages(ptr.get()); + bool is_result_wrong = false; + if (random_ids_.size() != sent_messages.size()) { + is_result_wrong = true; + } + for (auto &sent_message : sent_messages) { + if (DialogId::get_message_dialog_id(sent_message.first) != dialog_id_) { + is_result_wrong = true; + } + } + if (is_result_wrong) { + LOG(ERROR) << "Receive wrong result for sending quick reply messages with random_ids " + << format::as_array(random_ids_) << " to " << dialog_id_ << ": " << oneline(to_string(ptr)); + td_->updates_manager_->schedule_get_difference("Wrong sendQuickReplyMessages result"); + for (auto &random_id : random_ids_) { + td_->messages_manager_->on_send_message_fail(random_id, Status::Error(500, "Receive invalid response")); + } + } else { + // generate fake updates + for (size_t i = 0; i < random_ids_.size(); i++) { + td_->messages_manager_->on_update_message_id( + random_ids_[i], MessageId::get_message_id(sent_messages[i].first, false), "SendQuickReplyMessagesQuery"); + } + } + td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_)); + } + + void on_error(Status status) final { + LOG(INFO) << "Receive error for SendQuickReplyMessagesQuery: " << status; + if (G()->close_flag() && G()->use_message_database()) { + // do not send error, messages will be re-sent after restart + return; + } + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "SendQuickReplyMessagesQuery"); + for (auto &random_id : random_ids_) { + td_->messages_manager_->on_send_message_fail(random_id, status.clone()); + } + promise_.set_error(std::move(status)); + } +}; + class SendScreenshotNotificationQuery final : public Td::ResultHandler { Promise promise_; int64 random_id_; @@ -26556,6 +26641,106 @@ Result> MessagesManager::forward_messages( return get_messages_object(-1, std::move(result), false); } +Result> MessagesManager::send_quick_reply_shortcut_messages( + DialogId dialog_id, QuickReplyShortcutId shortcut_id, int32 sending_id) { + TRY_RESULT(message_contents, td_->quick_reply_manager_->get_quick_reply_message_contents(dialog_id, shortcut_id)); + + if (message_contents.empty()) { + return td_api::object_ptr(); + } + + std::unordered_map, Hash> new_media_album_ids; + for (auto &content : message_contents) { + if (content.media_album_id_ == 0) { + continue; + } + auto &new_media_album_id = new_media_album_ids[content.media_album_id_]; + new_media_album_id.second++; + if (new_media_album_id.second == 2) { // have at least 2 messages in the new album + CHECK(new_media_album_id.first == 0); + new_media_album_id.first = generate_new_media_album_id(); + } + if (new_media_album_id.second == MAX_GROUPED_MESSAGES + 1) { + CHECK(new_media_album_id.first != 0); + new_media_album_id.first = 0; // just in case + } + } + for (auto &content : message_contents) { + content.media_album_id_ = new_media_album_ids[content.media_album_id_].first; + } + + auto *d = get_dialog(dialog_id); + CHECK(d != nullptr); + + MessageSendOptions message_send_options(false, false, false, false, false, 0, sending_id); + FlatHashMap original_message_id_to_new_message_id; + vector> result; + vector sent_messages; + vector sent_message_ids; + bool need_update_dialog_pos = false; + for (auto &content : message_contents) { + MessageInputReplyTo input_reply_to; + if (content.original_reply_to_message_id_.is_valid()) { + auto it = original_message_id_to_new_message_id.find(content.original_reply_to_message_id_); + if (it != original_message_id_to_new_message_id.end()) { + input_reply_to = MessageInputReplyTo{it->second, DialogId(), FormattedText(), 0}; + } + } + + Message *m = get_message_to_send(d, MessageId(), std::move(input_reply_to), message_send_options, + std::move(content.content_), content.invert_media_, &need_update_dialog_pos, false, + nullptr, DialogId(), true); + m->disable_web_page_preview = content.disable_web_page_preview_; + m->media_album_id = content.media_album_id_; + original_message_id_to_new_message_id.emplace(content.original_message_id_, m->message_id); + + if (!td_->auth_manager_->is_bot()) { + send_update_new_message(d, m); + } + sent_messages.push_back(m); + sent_message_ids.push_back(m->message_id); + + result.push_back(get_message_object(dialog_id, m, "send_quick_reply_shortcut_messages")); + } + + do_send_quick_reply_shortcut_messages(dialog_id, shortcut_id, sent_messages, sent_message_ids, 0); + + if (need_update_dialog_pos) { + send_update_chat_last_message(d, "send_quick_reply_shortcut_messages"); + } + + return get_messages_object(-1, std::move(result), false); +} + +void MessagesManager::do_send_quick_reply_shortcut_messages(DialogId dialog_id, QuickReplyShortcutId shortcut_id, + const vector &messages, + const vector &message_ids, uint64 log_event_id) { + if (G()->close_flag()) { + return; + } + + CHECK(messages.size() == message_ids.size()); + if (messages.empty()) { + return; + } + + if (log_event_id == 0 && G()->use_message_database()) { + // log_event_id = save_send_quick_reply_shortcut_messages_log_event(dialog_id, shprtcut_id, messages, message_ids); + } + + vector random_ids = + transform(messages, [this, dialog_id](const Message *m) { return begin_send_message(dialog_id, m); }); + send_closure_later(actor_id(this), &MessagesManager::send_send_quick_reply_messages_query, dialog_id, shortcut_id, + message_ids, std::move(random_ids), get_erase_log_event_promise(log_event_id)); +} + +void MessagesManager::send_send_quick_reply_messages_query(DialogId dialog_id, QuickReplyShortcutId shortcut_id, + vector message_ids, vector random_ids, + Promise promise) { + td_->create_handler(std::move(promise)) + ->send(dialog_id, shortcut_id, std::move(message_ids), std::move(random_ids)); +} + Result> MessagesManager::resend_messages(DialogId dialog_id, vector message_ids, td_api::object_ptr &"e) { if (message_ids.empty()) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 2c276b66d..b410b07ef 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -52,6 +52,7 @@ #include "td/telegram/NotificationId.h" #include "td/telegram/NotificationSettingsScope.h" #include "td/telegram/OrderedMessage.h" +#include "td/telegram/QuickReplyShortcutId.h" #include "td/telegram/ReactionType.h" #include "td/telegram/ReactionUnavailabilityReason.h" #include "td/telegram/RecentDialogList.h" @@ -436,6 +437,9 @@ class MessagesManager final : public Actor { tl_object_ptr &&options, bool in_game_share, vector &©_options) TD_WARN_UNUSED_RESULT; + Result> send_quick_reply_shortcut_messages( + DialogId dialog_id, QuickReplyShortcutId shortcut_id, int32 sending_id) TD_WARN_UNUSED_RESULT; + Result> resend_messages(DialogId dialog_id, vector message_ids, td_api::object_ptr &"e) TD_WARN_UNUSED_RESULT; @@ -1746,6 +1750,14 @@ class MessagesManager final : public Actor { const vector &message_ids, bool drop_author, bool drop_media_captions, uint64 log_event_id); + void do_send_quick_reply_shortcut_messages(DialogId dialog_id, QuickReplyShortcutId shortcut_id, + const vector &messages, const vector &message_ids, + uint64 log_event_id); + + void send_send_quick_reply_messages_query(DialogId dialog_id, QuickReplyShortcutId shortcut_id, + vector message_ids, vector random_ids, + Promise promise); + void send_forward_message_query(int32 flags, DialogId to_dialog_id, const MessageId top_thread_message_id, DialogId from_dialog_id, tl_object_ptr as_input_peer, vector message_ids, vector random_ids, int32 schedule_date, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 0a0472d54..6e94cb821 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6008,6 +6008,16 @@ void Td::on_request(uint64 id, td_api::forwardMessages &request) { } } +void Td::on_request(uint64 id, const td_api::sendQuickReplyShortcutMessages &request) { + auto r_messages = messages_manager_->send_quick_reply_shortcut_messages( + DialogId(request.chat_id_), QuickReplyShortcutId(request.shortcut_id_), request.sending_id_); + if (r_messages.is_error()) { + send_closure(actor_id(this), &Td::send_error, id, r_messages.move_as_error()); + } else { + send_closure(actor_id(this), &Td::send_result, id, r_messages.move_as_ok()); + } +} + void Td::on_request(uint64 id, td_api::resendMessages &request) { DialogId dialog_id(request.chat_id_); auto r_message_ids = messages_manager_->resend_messages(dialog_id, MessageId::get_message_ids(request.message_ids_), diff --git a/td/telegram/Td.h b/td/telegram/Td.h index ecacc4820..ac0ccb062 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -935,6 +935,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::forwardMessages &request); + void on_request(uint64 id, const td_api::sendQuickReplyShortcutMessages &request); + void on_request(uint64 id, td_api::resendMessages &request); void on_request(uint64 id, td_api::getWebPagePreview &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 2f6cea87f..b197ed502 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3861,6 +3861,12 @@ class CliClient final : public Actor { send_request(td_api::make_object( chat_id, message_thread_id_, from_chat_id, as_message_ids(message_ids), default_message_send_options(), op[0] == 'c', rand_bool())); + } else if (op == "sqrsm") { + ChatId chat_id; + ShortcutId shortcut_id; + get_args(args, chat_id, shortcut_id); + send_request( + td_api::make_object(chat_id, shortcut_id, Random::fast(-1000, -1))); } else if (op == "resend") { ChatId chat_id; string message_ids;