diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b0abfd6d1..e8ec25f88 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3732,6 +3732,15 @@ storyInteractions total_count:int32 total_forward_count:int32 total_reaction_cou //@content Content of the message quickReplyMessage id:int53 sending_state:MessageSendingState forward_info:messageForwardInfo reply_to_message_id:int53 via_bot_user_id:int53 media_album_id:int64 content:MessageContent = QuickReplyMessage; +//@description Describes a shortcut that can be used for a quick reply +//@name The name of the shortcut that can be used to use the shortcut +//@first_message The first shortcut message +//@message_count The total number of messages in the shortcut +quickReplyShortcut name:string first_message:quickReplyMessage message_count:int32 = QuickReplyShortcut; + +//@description Represents a list of quick reply shortcuts created by the current user @shortcuts A list of shortcuts +quickReplyShortcuts shortcuts:vector = QuickReplyShortcuts; + //@class PublicForward @description Describes a public forward or repost of a story @@ -7701,6 +7710,10 @@ editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup = editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:MessageSchedulingState = Ok; +//@description Returns shortcuts created by the current user +getQuickReplyShortcuts = QuickReplyShortcuts; + + //@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 98ec43a85..51b7c936c 100644 --- a/td/telegram/QuickReplyManager.cpp +++ b/td/telegram/QuickReplyManager.cpp @@ -10,6 +10,7 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/Dependencies.h" #include "td/telegram/DialogManager.h" +#include "td/telegram/Global.h" #include "td/telegram/MessageContent.h" #include "td/telegram/MessageForwardInfo.h" #include "td/telegram/MessageReplyHeader.h" @@ -17,8 +18,45 @@ #include "td/telegram/RepliedMessageInfo.h" #include "td/telegram/Td.h" +#include "td/utils/algorithm.h" +#include "td/utils/buffer.h" +#include "td/utils/FlatHashSet.h" +#include "td/utils/logging.h" + namespace td { +class GetQuickRepliesQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetQuickRepliesQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(int64 hash) { + send_query(G()->net_query_creator().create(telegram_api::messages_getQuickReplies(hash))); + } + + 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(DEBUG) << "Receive result for GetQuickRepliesQuery: " << to_string(ptr); + promise_.set_value(std::move(ptr)); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +QuickReplyManager::QuickReplyMessage::~QuickReplyMessage() = default; + +QuickReplyManager::Shortcut::~Shortcut() = default; + QuickReplyManager::QuickReplyManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { } @@ -35,6 +73,7 @@ unique_ptr QuickReplyManager::create_messa case telegram_api::messageEmpty::ID: break; case telegram_api::message::ID: { + auto message_id = MessageId::get_message_id(message_ptr, false); auto message = move_tl_object_as(message_ptr); if (message->quick_reply_shortcut_id_ == 0) { LOG(ERROR) << "Receive a quick reply without shortcut from " << source; @@ -42,17 +81,16 @@ unique_ptr QuickReplyManager::create_messa } auto my_dialog_id = td_->dialog_manager_->get_my_dialog_id(); - if (DialogId(message->peer_id_) != my_dialog_id || message->from_id_ == nullptr || - DialogId(message->from_id_) != my_dialog_id || message->views_ != 0 || message->forwards_ != 0 || + if (DialogId(message->peer_id_) != my_dialog_id || message->from_id_ != nullptr || + message->saved_peer_id_ != nullptr || message->views_ != 0 || message->forwards_ != 0 || message->replies_ != nullptr || message->reactions_ != nullptr || message->edit_date_ != 0 || message->ttl_period_ != 0 || !message->out_ || message->post_ || message->edit_hide_ || message->from_scheduled_ || message->pinned_ || message->noforwards_ || message->mentioned_ || message->media_unread_ || message->reply_markup_ != nullptr || !message->restriction_reason_.empty() || - !message->post_author_.empty() || message->from_boosts_applied_ != 0 || message->saved_peer_id_ != nullptr) { + !message->post_author_.empty() || message->from_boosts_applied_ != 0) { LOG(ERROR) << "Receive an invalid quick reply from " << source << ": " << to_string(message); } - auto message_id = MessageId::get_message_id(message_ptr, false); auto forward_header = std::move(message->fwd_from_); UserId via_bot_user_id; if (message->flags_ & telegram_api::message::VIA_BOT_ID_MASK) { @@ -96,6 +134,7 @@ unique_ptr QuickReplyManager::create_messa } auto result = make_unique(); + result->shortcut_id = message->quick_reply_shortcut_id_; result->message_id = message_id; result->disable_web_page_preview = disable_web_page_preview; result->forward_info = MessageForwardInfo::get_message_forward_info(td_, std::move(forward_header)); @@ -195,4 +234,133 @@ td_api::object_ptr QuickReplyManager::get_quick_reply get_quick_reply_message_message_content_object(m)); } +td_api::object_ptr QuickReplyManager::get_quick_reply_shortcut_object( + const Shortcut *s, const char *source) const { + CHECK(s != nullptr); + CHECK(!s->messages_.empty()); + return td_api::make_object( + s->name_, get_quick_reply_message_object(s->messages_[0].get(), source), s->total_count_); +} + +td_api::object_ptr QuickReplyManager::get_quick_reply_shortcuts_object( + const char *source) const { + CHECK(shortcuts_.are_inited_); + return td_api::make_object( + transform(shortcuts_.shortcuts_, [this, source](const unique_ptr &shortcut) { + return get_quick_reply_shortcut_object(shortcut.get(), source); + })); +} + +void QuickReplyManager::get_quick_reply_shortcuts(Promise> &&promise) { + if (shortcuts_.are_inited_) { + return promise.set_value(get_quick_reply_shortcuts_object("get_quick_reply_shortcuts")); + } + + load_quick_reply_shortcuts(std::move(promise)); +} + +void QuickReplyManager::load_quick_reply_shortcuts(Promise> &&promise) { + shortcuts_.load_queries_.push_back(std::move(promise)); + if (shortcuts_.load_queries_.size() != 1) { + return; + } + reload_quick_reply_shortcuts(); +} + +void QuickReplyManager::reload_quick_reply_shortcuts() { + auto promise = PromiseCreator::lambda( + [actor_id = actor_id(this)](Result> r_shortcuts) { + send_closure(actor_id, &QuickReplyManager::on_reload_quick_reply_shortcuts, std::move(r_shortcuts)); + }); + td_->create_handler(std::move(promise))->send(0); +} + +void QuickReplyManager::on_reload_quick_reply_shortcuts( + Result> r_shortcuts) { + G()->ignore_result_if_closing(r_shortcuts); + if (r_shortcuts.is_error()) { + return on_load_quick_reply_fail(r_shortcuts.move_as_error()); + } + auto shortcuts_ptr = r_shortcuts.move_as_ok(); + switch (shortcuts_ptr->get_id()) { + case telegram_api::messages_quickRepliesNotModified::ID: + if (!shortcuts_.are_inited_) { + shortcuts_.are_inited_ = true; + } + break; + case telegram_api::messages_quickReplies::ID: { + auto shortcuts = telegram_api::move_object_as(shortcuts_ptr); + td_->contacts_manager_->on_get_users(std::move(shortcuts->users_), "messages.quickReplies"); + td_->contacts_manager_->on_get_chats(std::move(shortcuts->chats_), "messages.quickReplies"); + + FlatHashMap, MessageIdHash> message_id_to_message; + for (auto &message : shortcuts->messages_) { + auto message_id = MessageId::get_message_id(message, false); + if (!message_id.is_valid()) { + continue; + } + message_id_to_message[message_id] = std::move(message); + } + + FlatHashSet added_shortcut_ids; + FlatHashSet added_shortcut_names; + vector> new_shortcuts; + for (auto &quick_reply : shortcuts->quick_replies_) { + if (quick_reply->shortcut_id_ <= 0 || quick_reply->shortcut_.empty() || quick_reply->count_ <= 0 || + quick_reply->top_message_ <= 0) { + LOG(ERROR) << "Receive " << to_string(quick_reply); + continue; + } + if (added_shortcut_ids.count(quick_reply->shortcut_id_) || added_shortcut_names.count(quick_reply->shortcut_)) { + LOG(ERROR) << "Receive duplicate " << to_string(quick_reply); + continue; + } + added_shortcut_ids.insert(quick_reply->shortcut_id_); + added_shortcut_names.insert(quick_reply->shortcut_); + + MessageId first_message_id(ServerMessageId(quick_reply->top_message_)); + auto it = message_id_to_message.find(first_message_id); + if (it == message_id_to_message.end()) { + LOG(ERROR) << "Can't find last " << first_message_id << " in shortcut " << quick_reply->shortcut_; + continue; + } + auto message = create_message(std::move(it->second), "on_reload_quick_reply_shortcuts"); + message_id_to_message.erase(it); + if (message == nullptr) { + continue; + } + if (message->shortcut_id != quick_reply->shortcut_id_) { + LOG(ERROR) << "Receive message from shortcut " << message->shortcut_id << " instead of " + << quick_reply->shortcut_id_; + } + + auto shortcut = td::make_unique(); + shortcut->name_ = std::move(quick_reply->shortcut_); + shortcut->shortcut_id_ = quick_reply->shortcut_id_; + shortcut->total_count_ = quick_reply->count_; + shortcut->messages_.push_back(std::move(message)); + new_shortcuts.push_back(std::move(shortcut)); + } + shortcuts_.shortcuts_ = std::move(new_shortcuts); + shortcuts_.are_inited_ = true; + break; + } + } + on_load_quick_reply_success(); +} + +void QuickReplyManager::on_load_quick_reply_success() { + auto promises = std::move(shortcuts_.load_queries_); + reset_to_empty(shortcuts_.load_queries_); + for (auto &promise : promises) { + if (promise) { + promise.set_value(get_quick_reply_shortcuts_object("on_load_quick_reply_success")); + } + } +} + +void QuickReplyManager::on_load_quick_reply_fail(Status error) { + fail_promises(shortcuts_.load_queries_, std::move(error)); +} + } // namespace td diff --git a/td/telegram/QuickReplyManager.h b/td/telegram/QuickReplyManager.h index 87d86fa01..2d2013aa8 100644 --- a/td/telegram/QuickReplyManager.h +++ b/td/telegram/QuickReplyManager.h @@ -26,9 +26,21 @@ class QuickReplyManager final : public Actor { public: QuickReplyManager(Td *td, ActorShared<> parent); + void get_quick_reply_shortcuts(Promise> &&promise); + + void reload_quick_reply_shortcuts(); + private: struct QuickReplyMessage { + QuickReplyMessage() = default; + QuickReplyMessage(const QuickReplyMessage &) = delete; + QuickReplyMessage &operator=(const QuickReplyMessage &) = delete; + QuickReplyMessage(QuickReplyMessage &&) = delete; + QuickReplyMessage &operator=(QuickReplyMessage &&) = delete; + ~QuickReplyMessage(); + MessageId message_id; + int32 shortcut_id = 0; int32 sending_id = 0; // for yet unsent messages int64 random_id = 0; // for send_message @@ -66,13 +78,27 @@ class QuickReplyManager final : public Actor { unique_ptr content; mutable uint64 send_message_log_event_id = 0; + }; - QuickReplyMessage() = default; - QuickReplyMessage(const QuickReplyMessage &) = delete; - QuickReplyMessage &operator=(const QuickReplyMessage &) = delete; - QuickReplyMessage(QuickReplyMessage &&) = delete; - QuickReplyMessage &operator=(QuickReplyMessage &&) = delete; - ~QuickReplyMessage() = default; + struct Shortcut { + Shortcut() = default; + Shortcut(const Shortcut &) = delete; + Shortcut &operator=(const Shortcut &) = delete; + Shortcut(Shortcut &&) = delete; + Shortcut &operator=(Shortcut &&) = delete; + ~Shortcut(); + + string name_; + int32 shortcut_id_ = 0; + int32 total_count_ = 0; + vector> messages_; + }; + + struct Shortcuts { + vector> shortcuts_; + bool are_inited_ = false; + + vector>> load_queries_; }; void tear_down() final; @@ -92,6 +118,22 @@ class QuickReplyManager final : public Actor { td_api::object_ptr get_quick_reply_message_object(const QuickReplyMessage *m, const char *source) const; + td_api::object_ptr get_quick_reply_shortcut_object(const Shortcut *s, + const char *source) const; + + td_api::object_ptr get_quick_reply_shortcuts_object(const char *source) const; + + void load_quick_reply_shortcuts(Promise> &&promise); + + void on_reload_quick_reply_shortcuts( + Result> r_shortcuts); + + void on_load_quick_reply_success(); + + void on_load_quick_reply_fail(Status error); + + Shortcuts shortcuts_; + Td *td_; ActorShared<> parent_; }; diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index f42ae191c..273147063 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -92,6 +92,9 @@ class GetAllStickersQuery final : public Td::ResultHandler { static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, + ""); auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 2c890d721..daeb69f64 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5757,6 +5757,12 @@ void Td::on_request(uint64 id, td_api::editMessageSchedulingState &request) { std::move(request.scheduling_state_), std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getQuickReplyShortcuts &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + quick_reply_manager_->get_quick_reply_shortcuts(std::move(promise)); +} + 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 2af064526..c3e732cfb 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -864,6 +864,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::editMessageSchedulingState &request); + void on_request(uint64 id, const td_api::getQuickReplyShortcuts &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/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 75feda806..cc22bd9b3 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -52,6 +52,7 @@ #include "td/telegram/PollManager.h" #include "td/telegram/PrivacyManager.h" #include "td/telegram/PublicDialogType.h" +#include "td/telegram/QuickReplyManager.h" #include "td/telegram/ReactionListType.h" #include "td/telegram/ReactionManager.h" #include "td/telegram/ReactionType.h" @@ -2268,6 +2269,7 @@ void UpdatesManager::try_reload_data() { Auto()); td_->notification_settings_manager_->send_get_scope_notification_settings_query(NotificationSettingsScope::Channel, Auto()); + td_->quick_reply_manager_->reload_quick_reply_shortcuts(); td_->reaction_manager_->reload_reactions(); for (int32 type = 0; type < MAX_REACTION_LIST_TYPE; type++) { diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index d4866c0d9..f10ff30fd 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4825,6 +4825,8 @@ class CliClient final : public Actor { get_args(args, chat_id, message_id, date); send_request(td_api::make_object(chat_id, message_id, as_message_scheduling_state(date))); + } else if (op == "gqrs") { + send_request(td_api::make_object()); } else if (op == "gftdi") { send_request(td_api::make_object()); } else if (op == "cft") {