// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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/DialogId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/MessageId.h" #include "td/telegram/QuickReplyMessageFullId.h" #include "td/telegram/QuickReplyShortcutId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" #include "td/actor/actor.h" #include "td/utils/common.h" #include "td/utils/FlatHashMap.h" #include "td/utils/FlatHashSet.h" #include "td/utils/Promise.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include #include #include namespace td { class Dependencies; struct InputMessageContent; class MessageContent; struct ReplyMarkup; class Td; class QuickReplyManager final : public Actor { public: QuickReplyManager(Td *td, ActorShared<> parent); static Status check_shortcut_name(CSlice name); void get_quick_reply_shortcuts(Promise &&promise); void set_quick_reply_shortcut_name(QuickReplyShortcutId shortcut_id, const string &name, Promise &&promise); void delete_quick_reply_shortcut(QuickReplyShortcutId shortcut_id, Promise &&promise); void reorder_quick_reply_shortcuts(const vector &shortcut_ids, Promise &&promise); void update_quick_reply_message(telegram_api::object_ptr &&message_ptr); void delete_pending_message_web_page(QuickReplyMessageFullId message_full_id); void on_external_update_message_content(QuickReplyMessageFullId message_full_id, const char *source, bool expect_no_message = false); void delete_quick_reply_messages_from_updates(QuickReplyShortcutId shortcut_id, const vector &message_ids); void get_quick_reply_shortcut_messages(QuickReplyShortcutId shortcut_id, Promise &&promise); void delete_quick_reply_shortcut_messages(QuickReplyShortcutId shortcut_id, const vector &message_ids, Promise &&promise); Result> send_message( const string &shortcut_name, MessageId reply_to_message_id, td_api::object_ptr &&input_message_content); Result> send_inline_query_result_message(const string &shortcut_name, MessageId reply_to_message_id, int64 query_id, const string &result_id, bool hide_via_bot); Result> send_message_group( const string &shortcut_name, MessageId reply_to_message_id, vector> &&input_message_contents); Result> resend_messages(const string &shortcut_name, vector message_ids); void edit_quick_reply_message(QuickReplyShortcutId shortcut_id, MessageId message_id, td_api::object_ptr &&input_message_content, Promise &&promise); void reload_quick_reply_shortcuts(); void reload_quick_reply_messages(QuickReplyShortcutId shortcut_id, Promise &&promise); void reload_quick_reply_message(QuickReplyShortcutId shortcut_id, MessageId message_id, Promise &&promise); struct QuickReplyMessageContent { unique_ptr content_; MessageId original_message_id_; MessageId original_reply_to_message_id_; unique_ptr reply_markup_; UserId via_bot_user_id_; int64 media_album_id_; bool invert_media_; bool disable_web_page_preview_; }; Result> get_quick_reply_message_contents(DialogId dialog_id, QuickReplyShortcutId shortcut_id) const; FileSourceId get_quick_reply_message_file_source_id(QuickReplyMessageFullId message_full_id); 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; QuickReplyMessage &operator=(const QuickReplyMessage &) = delete; QuickReplyMessage(QuickReplyMessage &&) = delete; QuickReplyMessage &operator=(QuickReplyMessage &&) = delete; ~QuickReplyMessage(); MessageId message_id; QuickReplyShortcutId shortcut_id; int32 edit_date = 0; int64 random_id = 0; // for send_message MessageId reply_to_message_id; string send_emoji; // for send_message int64 inline_query_id = 0; // for send_message string inline_result_id; // for send_message UserId via_bot_user_id; bool is_failed_to_send = false; bool disable_notification = false; bool invert_media = false; bool disable_web_page_preview = false; bool from_background = false; // for send_message bool hide_via_bot = false; // for resend_message bool edited_invert_media = false; bool edited_disable_web_page_preview = false; int32 legacy_layer = 0; int32 send_error_code = 0; string send_error_message; double try_resend_at = 0; int64 media_album_id = 0; unique_ptr content; unique_ptr reply_markup; unique_ptr edited_content; int64 edit_generation = 0; template void store(StorerT &storer) const; template void parse(ParserT &parser); }; struct Shortcut { Shortcut() = default; Shortcut(const Shortcut &) = delete; Shortcut &operator=(const Shortcut &) = delete; Shortcut(Shortcut &&) = delete; Shortcut &operator=(Shortcut &&) = delete; ~Shortcut(); string name_; QuickReplyShortcutId shortcut_id_; int32 server_total_count_ = 0; int32 local_total_count_ = 0; vector> messages_; MessageId last_assigned_message_id_; template void store(StorerT &storer) const; template void parse(ParserT &parser); }; struct Shortcuts { vector> shortcuts_; bool are_inited_ = false; bool are_loaded_from_database_ = false; vector> load_queries_; template void store(StorerT &storer) const; template void parse(ParserT &parser); }; class EditQuickReplyMessageQuery; class SendQuickReplyInlineMessageQuery; class SendQuickReplyMediaQuery; class SendQuickReplyMessageQuery; class SendQuickReplyMultiMediaQuery; class UploadQuickReplyMediaQuery; class UploadMediaCallback; class UploadThumbnailCallback; std::shared_ptr upload_media_callback_; std::shared_ptr upload_thumbnail_callback_; void tear_down() final; static bool is_shortcut_name_letter(uint32 code); void add_quick_reply_message_dependencies(Dependencies &dependencies, const QuickReplyMessage *m) const; unique_ptr create_message(telegram_api::object_ptr message_ptr, const char *source) const; bool can_edit_quick_reply_message(const QuickReplyMessage *m) const; bool can_resend_quick_reply_message(const QuickReplyMessage *m) const; td_api::object_ptr get_message_sending_state_object(const QuickReplyMessage *m) const; td_api::object_ptr get_quick_reply_message_message_content_object( const QuickReplyMessage *m) const; 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; static int32 get_shortcut_message_count(const Shortcut *s); static bool have_all_shortcut_messages(const Shortcut *s); void on_reload_quick_reply_shortcuts( Result> r_shortcuts); void on_load_quick_reply_success(); void on_load_quick_reply_fail(Status error); void on_set_quick_reply_shortcut_name(QuickReplyShortcutId shortcut_id, const string &name, Promise &&promise); int64 get_shortcuts_hash() const; void on_reload_quick_reply_messages(QuickReplyShortcutId shortcut_id, Result> r_messages); static int64 get_quick_reply_messages_hash(const Shortcut *s); void on_reload_quick_reply_message(QuickReplyShortcutId shortcut_id, MessageId message_id, Result> r_messages, Promise &&promise); void on_get_quick_reply_message(Shortcut *s, unique_ptr message); void update_quick_reply_message(unique_ptr &old_message, unique_ptr &&new_message); void delete_quick_reply_messages(Shortcut *s, const vector &message_ids, const char *source); Shortcut *get_shortcut(QuickReplyShortcutId shortcut_id); const Shortcut *get_shortcut(QuickReplyShortcutId shortcut_id) const; Shortcut *get_shortcut(const string &name); vector>::iterator get_shortcut_it(QuickReplyShortcutId shortcut_id); vector>::iterator get_message_it(Shortcut *s, MessageId message_id); const QuickReplyMessage *get_message(QuickReplyMessageFullId message_full_id) const; const QuickReplyMessage *get_message(const Shortcut *s, MessageId message_id) const; QuickReplyMessage *get_message_editable(QuickReplyMessageFullId message_full_id); QuickReplyMessage *get_message_editable(Shortcut *s, MessageId message_id); Result create_new_local_shortcut(const string &name, int32 new_message_count); static MessageId get_input_reply_to_message_id(const Shortcut *s, MessageId reply_to_message_id); Result process_input_message_content( td_api::object_ptr &&input_message_content); MessageId get_next_message_id(Shortcut *s, MessageType type) const; MessageId get_next_yet_unsent_message_id(Shortcut *s) const; MessageId get_next_local_message_id(Shortcut *s) const; QuickReplyMessage *add_local_message(Shortcut *s, MessageId reply_to_message_id, unique_ptr &&content, bool invert_media, UserId via_bot_user_id, bool hide_via_bot, bool disable_web_page_preview, string &&send_emoji); bool is_shortcut_list_changed(const vector> &new_shortcuts) const; vector get_shortcut_ids() const; vector get_server_shortcut_ids() const; static void sort_quick_reply_messages(vector> &messages); using QuickReplyMessageUniqueId = std::pair; static QuickReplyMessageUniqueId get_quick_reply_unique_id(const QuickReplyMessage *m); static vector get_quick_reply_unique_ids( const vector> &messages); static vector get_server_quick_reply_unique_ids( const vector> &messages); void update_shortcut_from(Shortcut *new_shortcut, Shortcut *old_shortcut, bool is_partial, bool *is_shortcut_changed, bool *are_messages_changed); td_api::object_ptr get_update_quick_reply_shortcut_object(const Shortcut *s, const char *source) const; void send_update_quick_reply_shortcut(const Shortcut *s, const char *source); td_api::object_ptr get_update_quick_reply_shortcut_deleted_object( const Shortcut *s) const; void send_update_quick_reply_shortcut_deleted(const Shortcut *s); td_api::object_ptr get_update_quick_reply_shortcuts_object() const; void send_update_quick_reply_shortcuts(); td_api::object_ptr get_update_quick_reply_shortcut_messages_object( const Shortcut *s, const char *source) const; void send_update_quick_reply_shortcut_messages(const Shortcut *s, const char *source); void set_quick_reply_shortcut_name_on_server(QuickReplyShortcutId shortcut_id, const string &name, Promise &&promise); void delete_quick_reply_shortcut_from_server(QuickReplyShortcutId shortcut_id, Promise &&promise); void reorder_quick_reply_shortcuts_on_server(vector shortcut_ids, Promise &&promise); void delete_quick_reply_messages_on_server(QuickReplyShortcutId shortcut_id, const vector &message_ids, Promise &&promise); telegram_api::object_ptr get_input_quick_reply_shortcut( QuickReplyShortcutId shortcut_id) const; Status check_send_quick_reply_messages_response(QuickReplyShortcutId shortcut_id, const telegram_api::object_ptr &updates_ptr, const vector &random_ids); void process_send_quick_reply_updates(QuickReplyShortcutId shortcut_id, telegram_api::object_ptr updates_ptr, vector random_ids); void on_failed_send_quick_reply_messages(QuickReplyShortcutId shortcut_id, vector random_ids, Status error); void update_message_content(const QuickReplyMessage *old_message, QuickReplyMessage *new_message, bool is_edit); void update_message_content(const unique_ptr &old_content, unique_ptr &new_content, bool need_merge_files); void do_send_message(const QuickReplyMessage *m, vector bad_parts = {}); void on_send_message_file_parts_missing(QuickReplyShortcutId shortcut_id, int64 random_id, vector &&bad_parts); void on_send_message_file_reference_error(QuickReplyShortcutId shortcut_id, int64 random_id); void on_upload_media(FileId file_id, telegram_api::object_ptr input_file); void do_send_media(const QuickReplyMessage *m, FileId file_id, FileId thumbnail_file_id, telegram_api::object_ptr input_file, telegram_api::object_ptr input_thumbnail); void on_upload_media_error(FileId file_id, Status status); void on_upload_thumbnail(FileId thumbnail_file_id, telegram_api::object_ptr thumbnail_input_file); void on_upload_message_media_success(QuickReplyShortcutId shortcut_id, MessageId message_id, FileId file_id, telegram_api::object_ptr &&media); void on_upload_message_media_fail(QuickReplyShortcutId shortcut_id, MessageId message_id, Status error); void on_upload_message_media_finished(int64 media_album_id, QuickReplyShortcutId shortcut_id, MessageId message_id, Status result); void do_send_message_group(QuickReplyShortcutId shortcut_id, int64 media_album_id); void on_message_media_uploaded(const QuickReplyMessage *m, telegram_api::object_ptr &&input_media, FileId file_id, FileId thumbnail_file_id); void on_send_media_group_file_reference_error(QuickReplyShortcutId shortcut_id, vector random_ids); int64 generate_new_media_album_id() const; void on_edit_quick_reply_message(QuickReplyShortcutId shortcut_id, MessageId message_id, int64 edit_generation, FileId file_id, bool was_uploaded, telegram_api::object_ptr updates_ptr); void fail_edit_quick_reply_message(QuickReplyShortcutId shortcut_id, MessageId message_id, int64 edit_generation, FileId file_id, FileId thumbnail_file_id, string file_reference, bool was_uploaded, bool was_thumbnail_uploaded, Status status); string get_quick_reply_shortcuts_database_key(); void save_quick_reply_shortcuts(); void load_quick_reply_shortcuts(); vector get_message_file_ids(const QuickReplyMessage *m) const; void delete_message_files(const QuickReplyMessage *m) const; void change_message_files(const QuickReplyMessage *m, const vector &old_file_ids); void register_message_content(const QuickReplyMessage *m, const char *source) const; void unregister_message_content(const QuickReplyMessage *m, const char *source) const; Shortcuts shortcuts_; int32 next_local_shortcut_id_ = QuickReplyShortcutId::MAX_SERVER_SHORTCUT_ID + 1; FlatHashSet deleted_shortcut_ids_; FlatHashMap persistent_shortcut_ids_; FlatHashMap>, QuickReplyShortcutIdHash> get_shortcut_messages_queries_; FlatHashSet deleted_message_full_ids_; FlatHashMap message_full_id_to_file_source_id_; FlatHashMap, FileIdHash> being_uploaded_files_; // file_id -> message, thumbnail_file_id struct UploadedThumbnailInfo { QuickReplyMessageFullId quick_reply_message_full_id; FileId file_id; // original file file_id telegram_api::object_ptr input_file; // original file InputFile int64 edit_generation; }; FlatHashMap being_uploaded_thumbnails_; // thumbnail_file_id -> ... struct PendingMessageGroupSend { size_t finished_count = 0; vector message_ids; vector is_finished; vector results; }; FlatHashMap pending_message_group_sends_; // media_album_id -> ... int64 current_message_edit_generation_ = 0; Td *td_; ActorShared<> parent_; }; } // namespace td