diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 558977306..26f954c30 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4515,6 +4515,9 @@ viewMessages chat_id:int53 message_thread_id:int53 message_ids:vector for //@description Informs TDLib that the message content has been opened (e.g., the user has opened a photo, video, document, location or venue, or has listened to an audio file or voice note message). An updateMessageContentOpened update will be generated if something has changed @chat_id Chat identifier of the message @message_id Identifier of the message with the opened content openMessageContent chat_id:int53 message_id:int53 = Ok; +//@description Informs TDLib that a message with an animated emoji was clicked by the user. Returns a big animated sticker to be played or a 404 error if usual animation needs to be played @chat_id Chat identifier of the message @message_id Identifier of the clicked message +clickAnimatedEmojiMessage chat_id:int53 message_id:int53 = Sticker; + //@description Returns information about the type of an internal link. Returns a 404 error if the link is not internal. Can be called before authorization @link The link getInternalLinkType link:string = InternalLinkType; diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 13664672d..346bbe7d3 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -5300,6 +5300,20 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte } } +void get_message_content_animated_emoji_click_sticker(const MessageContent *content, FullMessageId full_message_id, + Td *td, Promise> &&promise) { + if (content->get_type() != MessageContentType::Text) { + return promise.set_error(Status::Error(400, "Message is not an animated emoji message")); + } + + auto &text = static_cast(content)->text; + if (!text.entities.empty()) { + // TODO enable the error + // return promise.set_error(Status::Error(400, "Message is not an animated emoji message")); + } + td->stickers_manager_->get_animated_emoji_click_sticker(text.text, full_message_id, std::move(promise)); +} + bool need_reget_message_content(const MessageContent *content) { CHECK(content != nullptr); switch (content->get_type()) { diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h index 07056e0eb..712906e8e 100644 --- a/td/telegram/MessageContent.h +++ b/td/telegram/MessageContent.h @@ -225,6 +225,9 @@ vector get_message_content_file_ids(const MessageContent *content, const string get_message_content_search_text(const Td *td, const MessageContent *content); +void get_message_content_animated_emoji_click_sticker(const MessageContent *content, FullMessageId full_message_id, + Td *td, Promise> &&promise); + bool need_reget_message_content(const MessageContent *content); bool need_delay_message_content_notification(const MessageContent *content, UserId my_user_id); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index ca501867d..5fd97b473 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -19703,6 +19703,27 @@ void MessagesManager::read_message_contents_on_server(DialogId dialog_id, vector } } +void MessagesManager::click_animated_emoji_message(FullMessageId full_message_id, + Promise> &&promise) { + auto dialog_id = full_message_id.get_dialog_id(); + Dialog *d = get_dialog_force(dialog_id, "click_animated_emoji_message"); + if (d == nullptr) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + auto message_id = get_persistent_message_id(d, full_message_id.get_message_id()); + auto *m = get_message_force(d, message_id, "click_animated_emoji_message"); + if (m == nullptr) { + return promise.set_error(Status::Error(400, "Message not found")); + } + + if (m->message_id.is_scheduled() || dialog_id.get_type() != DialogType::User || !m->message_id.is_server()) { + return promise.set_value(nullptr); + } + + get_message_content_animated_emoji_click_sticker(m->content.get(), full_message_id, td_, std::move(promise)); +} + void MessagesManager::open_dialog(Dialog *d) { DialogId dialog_id = d->dialog_id; if (!have_input_peer(dialog_id, AccessRights::Read)) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 23fb3258c..0d420e73c 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -647,6 +647,9 @@ class MessagesManager final : public Actor { Status open_message_content(FullMessageId full_message_id) TD_WARN_UNUSED_RESULT; + void click_animated_emoji_message(FullMessageId full_message_id, + Promise> &&promise); + td_api::object_ptr get_update_scope_notification_settings_object( NotificationSettingsScope scope) const; diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 1303a605e..8b139a4e7 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1224,9 +1224,7 @@ void StickersManager::init() { if (!G()->is_test_dc()) { // add animated emoji click sticker set auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click()); - init_special_sticker_set(sticker_set, 1258816259751996, 1, "EmojiAnimations"); load_special_sticker_set_info_from_binlog(sticker_set); - G()->shared_config().set_option_string(PSLICE() << sticker_set.type_.type_ << "_name", sticker_set.short_name_); } dice_emojis_str_ = @@ -1357,14 +1355,25 @@ void StickersManager::on_load_special_sticker_set(const SpecialStickerSetType &t special_sticker_set.is_being_loaded_ = false; - auto emoji = type.get_dice_emoji(); - CHECK(!emoji.empty()); - CHECK(special_sticker_set.id_.is_valid()); auto sticker_set = get_sticker_set(special_sticker_set.id_); CHECK(sticker_set != nullptr); CHECK(sticker_set->was_loaded); + if (type.type_ == SpecialStickerSetType::animated_emoji_click()) { + auto pending_requests = std::move(pending_get_animated_emoji_click_stickers_); + reset_to_empty(pending_get_animated_emoji_click_stickers_); + for (auto &pending_request : pending_requests) { + choose_animated_emoji_click_sticker(sticker_set, std::move(pending_request.message_text_), + pending_request.full_message_id_, pending_request.start_time_, + std::move(pending_request.promise_)); + } + return; + } + + auto emoji = type.get_dice_emoji(); + CHECK(!emoji.empty()); + auto it = dice_messages_.find(emoji); if (it == dice_messages_.end()) { return; @@ -1770,12 +1779,12 @@ tl_object_ptr StickersManager::get_sticker_set_object(Sticke for (auto sticker_id : sticker_set->sticker_ids) { stickers.push_back(get_sticker_object(sticker_id)); + vector sticker_emojis; auto it = sticker_set->sticker_emojis_map_.find(sticker_id); - if (it == sticker_set->sticker_emojis_map_.end()) { - emojis.push_back(Auto()); - } else { - emojis.push_back(make_tl_object(vector(it->second))); + if (it != sticker_set->sticker_emojis_map_.end()) { + sticker_emojis = it->second; } + emojis.push_back(make_tl_object(std::move(sticker_emojis))); } auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); @@ -2826,8 +2835,6 @@ void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &ty << ' ' << sticker_set.short_name_); if (type.type_ == SpecialStickerSetType::animated_emoji()) { G()->shared_config().set_option_string(PSLICE() << type.type_ << "_name", sticker_set.short_name_); - } else if (type.type_ == SpecialStickerSetType::animated_emoji_click()) { - G()->shared_config().set_option_string(PSLICE() << type.type_ << "_name", sticker_set.short_name_); } else if (!type.get_dice_emoji().empty()) { sticker_set.is_being_loaded_ = true; } @@ -3973,6 +3980,79 @@ void StickersManager::unregister_dice(const string &emoji, int32 value, FullMess } } +void StickersManager::get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id, + Promise> &&promise) { + auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click()); + if (!special_sticker_set.id_.is_valid()) { + // don't wait for the first load of the sticker set from the server + if (!special_sticker_set.is_being_loaded_) { + load_special_sticker_set(special_sticker_set); + } + return promise.set_value(nullptr); + } + + auto sticker_set = get_sticker_set(special_sticker_set.id_); + CHECK(sticker_set != nullptr); + if (sticker_set->was_loaded) { + return choose_animated_emoji_click_sticker(sticker_set, message_text, full_message_id, Time::now(), + std::move(promise)); + } + + LOG(INFO) << "Waiting for an emoji click sticker set needed in " << full_message_id; + if (!special_sticker_set.is_being_loaded_) { + load_special_sticker_set(special_sticker_set); + } + + PendingGetAnimatedEmojiClickSticker pending_request; + pending_request.message_text_ = message_text; + pending_request.full_message_id_ = full_message_id; + pending_request.start_time_ = Time::now(); + pending_request.promise_ = std::move(promise); + pending_get_animated_emoji_click_stickers_.push_back(std::move(pending_request)); +} + +int StickersManager::get_emoji_number(Slice emoji) { + // '0'-'9' + U+20E3 + auto data = emoji.ubegin(); + if (emoji.size() != 4 || emoji[0] < '0' || emoji[0] > '9' || data[1] != 0xE2 || data[2] != 0x83 || data[3] != 0xA3) { + return -1; + } + return emoji[0] - '0'; +} + +void StickersManager::choose_animated_emoji_click_sticker(const StickerSet *sticker_set, string message_text, + FullMessageId full_message_id, double start_time, + Promise> &&promise) { + CHECK(sticker_set->was_loaded); + message_text = remove_emoji_modifiers(message_text); + if (message_text.empty()) { + return promise.set_error(Status::Error(400, "Message is not an animated emoji message")); + } + + vector> found_stickers; + for (auto sticker_id : sticker_set->sticker_ids) { + auto s = get_sticker(sticker_id); + CHECK(s != nullptr); + if (s->alt == message_text) { + auto it = sticker_set->sticker_emojis_map_.find(sticker_id); + if (it != sticker_set->sticker_emojis_map_.end()) { + for (auto &emoji : it->second) { + auto number = get_emoji_number(emoji); + if (number > 0) { + found_stickers.emplace_back(number, sticker_id); + } + } + } + } + } + if (found_stickers.empty()) { + return promise.set_value(nullptr); + } + + auto result = found_stickers[Random::fast(0, narrow_cast(found_stickers.size()) - 1)]; + promise.set_value(get_sticker_object(result.second)); +} + void StickersManager::view_featured_sticker_sets(const vector &sticker_set_ids) { for (auto sticker_set_id : sticker_set_ids) { auto set = get_sticker_set(sticker_set_id); diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index c780e9371..57a54022c 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -69,6 +69,9 @@ class StickersManager final : public Actor { void unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source); + void get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id, + Promise> &&promise); + void create_sticker(FileId file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions, tl_object_ptr sticker, bool is_animated, MultiPromiseActor *load_data_multipromise_ptr); @@ -389,6 +392,13 @@ class StickersManager final : public Actor { Promise<> promise; }; + struct PendingGetAnimatedEmojiClickSticker { + string message_text_; + FullMessageId full_message_id_; + double start_time_ = 0; + Promise> promise_; + }; + struct SpecialStickerSet { StickerSetId id_; int64 access_hash_ = 0; @@ -572,6 +582,12 @@ class StickersManager final : public Actor { bool update_sticker_set_cache(const StickerSet *sticker_set, Promise &promise); + static int get_emoji_number(Slice emoji); + + void choose_animated_emoji_click_sticker(const StickerSet *sticker_set, string message_text, + FullMessageId full_message_id, double start_time, + Promise> &&promise); + td_api::object_ptr get_update_dice_emojis_object() const; void start_up() final; @@ -722,6 +738,8 @@ class StickersManager final : public Actor { std::unordered_map> pending_set_sticker_set_thumbnails_; + vector pending_get_animated_emoji_click_stickers_; + std::shared_ptr upload_sticker_file_callback_; std::unordered_map>, FileIdHash> being_uploaded_files_; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 8e960a94c..c7b8dd301 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5398,6 +5398,13 @@ void Td::on_request(uint64 id, const td_api::openMessageContent &request) { id, messages_manager_->open_message_content({DialogId(request.chat_id_), MessageId(request.message_id_)})); } +void Td::on_request(uint64 id, const td_api::clickAnimatedEmojiMessage &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + messages_manager_->click_animated_emoji_message({DialogId(request.chat_id_), MessageId(request.message_id_)}, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getInternalLinkType &request) { auto type = link_manager_->parse_internal_link(request.link_); send_closure(actor_id(this), &Td::send_result, id, type == nullptr ? nullptr : type->get_internal_link_type_object()); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 32c72e22a..13e53ca76 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -600,6 +600,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::openMessageContent &request); + void on_request(uint64 id, const td_api::clickAnimatedEmojiMessage &request); + void on_request(uint64 id, const td_api::getInternalLinkType &request); void on_request(uint64 id, td_api::getExternalLinkInfo &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index fc5bdb4ab..b75f7f170 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4099,6 +4099,12 @@ class CliClient final : public Actor { string message_id; get_args(args, chat_id, message_id); send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id))); + } else if (op == "caem") { + string chat_id; + string message_id; + get_args(args, chat_id, message_id); + send_request( + td_api::make_object(as_chat_id(chat_id), as_message_id(message_id))); } else if (op == "gilt") { string link = args; send_request(td_api::make_object(link));