diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b6548a4fd..57e12dcae 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3929,6 +3929,9 @@ storyInteractions total_count:int32 total_forward_count:int32 total_reaction_cou //@reply_markup Inline keyboard reply markup for the message; may be null if none quickReplyMessage id:int53 sending_state:MessageSendingState can_be_edited:Bool reply_to_message_id:int53 via_bot_user_id:int53 media_album_id:int64 content:MessageContent reply_markup:ReplyMarkup = QuickReplyMessage; +//@description Contains a list of quick reply messages @messages List of quick reply messages; messages may be null +quickReplyMessages messages:vector = QuickReplyMessages; + //@description Describes a shortcut that can be used for a quick reply //@id Unique shortcut identifier //@name The name of the shortcut that can be used to use the shortcut @@ -8165,7 +8168,7 @@ deleteQuickReplyShortcutMessages shortcut_id:int32 message_ids:vector = O //@input_message_content The content of the message to be added; inputMessagePoll, inputMessageForwarded and inputMessageLocation with live_period aren't supported addQuickReplyShortcutMessage shortcut_name:string reply_to_message_id:int53 input_message_content:InputMessageContent = QuickReplyMessage; -//@description Adds a message to a quick reply shortcut. If shortcut doesn't exist and there are less than getOption("quick_reply_shortcut_count_max") shortcuts, then a new shortcut is created. +//@description Adds a message to a quick reply shortcut via inline bot. If shortcut doesn't exist and there are less than getOption("quick_reply_shortcut_count_max") shortcuts, then a new shortcut is created. //-The shortcut must not contain more than getOption("quick_reply_shortcut_message_count_max") messages after adding the new message. Returns the added message //@shortcut_name Name of the target shortcut //@reply_to_message_id Identifier of a quick reply message in the same shortcut to be replied; pass 0 if none @@ -8174,6 +8177,12 @@ addQuickReplyShortcutMessage shortcut_name:string reply_to_message_id:int53 inpu //@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") addQuickReplyShortcutInlineQueryResultMessage shortcut_name:string reply_to_message_id:int53 query_id:int64 result_id:string hide_via_bot:Bool = Message; +//@description Readds quick reply messages which failed to add. 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 readded, 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 readded, null will be returned instead of the message +//@shortcut_name Name of the target shortcut +//@message_ids Identifiers of the quick reply messages to readd. Message identifiers must be in a strictly increasing order +readdQuickReplyShortcutMessages shortcut_name:string message_ids:vector = QuickReplyMessages; + //@description Returns list of custom emojis, which can be used as forum topic icon by all users getForumTopicDefaultIcons = Stickers; diff --git a/td/telegram/QuickReplyManager.cpp b/td/telegram/QuickReplyManager.cpp index 1fd2a8c65..d91d3895f 100644 --- a/td/telegram/QuickReplyManager.cpp +++ b/td/telegram/QuickReplyManager.cpp @@ -46,6 +46,7 @@ #include "td/utils/utf8.h" #include +#include namespace td { @@ -1968,6 +1969,113 @@ void QuickReplyManager::on_message_media_uploaded(const QuickReplyMessage *m, td_->create_handler()->send(file_id, thumbnail_file_id, m, std::move(input_media)); } +int64 QuickReplyManager::generate_new_media_album_id() { + int64 media_album_id = 0; + do { + media_album_id = Random::secure_int64(); + } while (media_album_id >= 0); + return media_album_id; +} + +Result> QuickReplyManager::resend_messages( + const string &shortcut_name, vector message_ids) { + if (message_ids.empty()) { + return Status::Error(400, "There are no messages to resend"); + } + + load_quick_reply_shortcuts(); + + auto *s = get_shortcut(shortcut_name); + if (s == nullptr) { + return Status::Error(400, "Quick reply shortcut not found"); + } + + MessageId last_message_id; + for (auto &message_id : message_ids) { + const auto *m = get_message(s, message_id); + if (m == nullptr) { + return Status::Error(400, "Message not found"); + } + if (!m->is_failed_to_send) { + return Status::Error(400, "Message is not failed to send"); + } + if (!can_resend_quick_reply_message(m)) { + return Status::Error(400, "Message can't be re-sent"); + } + if (m->try_resend_at > Time::now()) { + return Status::Error(400, "Message can't be re-sent yet"); + } + if (last_message_id != MessageId() && m->message_id <= last_message_id) { + return Status::Error(400, "Message identifiers must be in a strictly increasing order"); + } + last_message_id = m->message_id; + } + + vector> new_contents(message_ids.size()); + std::unordered_map, Hash> new_media_album_ids; + for (size_t i = 0; i < message_ids.size(); i++) { + MessageId message_id = message_ids[i]; + const auto *m = get_message(s, message_id); + CHECK(m != nullptr); + + unique_ptr content = + dup_message_content(td_, td_->dialog_manager_->get_my_dialog_id(), m->content.get(), + m->inline_query_id != 0 ? MessageContentDupType::SendViaBot : MessageContentDupType::Send, + MessageCopyOptions()); + if (content == nullptr) { + LOG(INFO) << "Can't resend " << m->message_id; + continue; + } + + new_contents[i] = std::move(content); + + if (m->media_album_id != 0) { + auto &new_media_album_id = new_media_album_ids[m->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 + } + } + } + + bool is_changed = false; + auto result = td_api::make_object(); + for (size_t i = 0; i < message_ids.size(); i++) { + if (new_contents[i] == nullptr) { + result->messages_.push_back(nullptr); + continue; + } + + auto *m = get_message(s, message_ids[i]); + CHECK(m != nullptr); + m->message_id = get_next_yet_unsent_message_id(s); + m->media_album_id = new_media_album_ids[m->media_album_id].first; + m->is_failed_to_send = false; + m->send_error_code = 0; + m->send_error_message = string(); + m->try_resend_at = 0.0; + + do_send_message(m); + + result->messages_.push_back(get_quick_reply_message_object(m, "resend_message")); + is_changed = true; + } + + if (is_changed) { + sort_quick_reply_messages(s->messages_); + send_update_quick_reply_shortcut(s, "resend_message"); + send_update_quick_reply_shortcut_messages(s, "resend_message"); + save_quick_reply_shortcuts(); + } + + return result; +} + void QuickReplyManager::get_quick_reply_shortcut_messages(QuickReplyShortcutId shortcut_id, Promise &&promise) { load_quick_reply_shortcuts(); auto *s = get_shortcut(shortcut_id); diff --git a/td/telegram/QuickReplyManager.h b/td/telegram/QuickReplyManager.h index e34ca47f0..527d42be1 100644 --- a/td/telegram/QuickReplyManager.h +++ b/td/telegram/QuickReplyManager.h @@ -69,6 +69,9 @@ class QuickReplyManager final : public Actor { const string &result_id, bool hide_via_bot); + Result> resend_messages(const string &shortcut_name, + vector message_ids); + void reload_quick_reply_shortcuts(); void reload_quick_reply_messages(QuickReplyShortcutId shortcut_id, Promise &&promise); @@ -93,6 +96,8 @@ class QuickReplyManager final : public Actor { void get_current_state(vector> &updates) const; private: + static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server side limit + struct QuickReplyMessage { QuickReplyMessage() = default; QuickReplyMessage(const QuickReplyMessage &) = delete; @@ -360,6 +365,8 @@ class QuickReplyManager final : public Actor { telegram_api::object_ptr &&input_media, FileId file_id, FileId thumbnail_file_id); + static int64 generate_new_media_album_id(); + string get_quick_reply_shortcuts_database_key(); void save_quick_reply_shortcuts(); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index aece9a3f6..2c4bf5809 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5902,6 +5902,16 @@ void Td::on_request(uint64 id, td_api::addQuickReplyShortcutInlineQueryResultMes } } +void Td::on_request(uint64 id, td_api::readdQuickReplyShortcutMessages &request) { + CLEAN_INPUT_STRING(request.shortcut_name_); + auto r_messages = + quick_reply_manager_->resend_messages(request.shortcut_name_, MessageId::get_message_ids(request.message_ids_)); + if (r_messages.is_error()) { + return send_closure(actor_id(this), &Td::send_error, id, r_messages.move_as_error()); + } + send_closure(actor_id(this), &Td::send_result, id, r_messages.move_as_ok()); +} + void Td::on_request(uint64 id, const td_api::getStory &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index df051b637..aa140a578 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -903,6 +903,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::addQuickReplyShortcutInlineQueryResultMessage &request); + void on_request(uint64 id, td_api::readdQuickReplyShortcutMessages &request); + void on_request(uint64 id, const td_api::getStory &request); void on_request(uint64 id, const td_api::getChatsToSendStories &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 852ff3e2a..7e8221a21 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3929,9 +3929,14 @@ class CliClient final : public Actor { string quote; int32 quote_position; get_args(args, chat_id, message_ids, quote, quote_position); - send_request(td_api::make_object( - chat_id, as_message_ids(message_ids), - td_api::make_object(as_formatted_text(quote), quote_position))); + if (quick_reply_shortcut_name_.empty()) { + send_request(td_api::make_object( + chat_id, as_message_ids(message_ids), + td_api::make_object(as_formatted_text(quote), quote_position))); + } else { + send_request(td_api::make_object(quick_reply_shortcut_name_, + as_message_ids(message_ids))); + } } else if (op == "csc" || op == "CreateSecretChat") { send_request(td_api::make_object(as_secret_chat_id(args))); } else if (op == "cnsc" || op == "CreateNewSecretChat") {