diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 9ea7e5ebf..c9f992aca 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1380,8 +1380,12 @@ messageVenue venue:venue = MessageContent; //@description A message with a user contact @contact The contact description messageContact contact:contact = MessageContent; -//@description A dice message. The dice value is randomly generated by the server @emoji Emoji on which the dice throw animation is based @value The dice value; 0-6. If the value is 0, the dice must roll infinitely -messageDice emoji:string value:int32 = MessageContent; +//@description A dice message. The dice value is randomly generated by the server +//@initial_state_sticker The animated sticker with the initial dice animation; may be null if unknown. updateMessageContent will be sent when the sticker became known +//@final_state_sticker The animated sticker with the final dice animation; may be null if unknown. updateMessageContent will be sent when the sticker became known +//@emoji Emoji on which the dice throw animation is based +//@value The dice value. If the value is 0, the dice don't have final state yet +messageDice initial_state_sticker:sticker final_state_sticker:sticker emoji:string value:int32 = MessageContent; //@description A message with a game @game The game description messageGame game:game = MessageContent; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 17d799cb2..d928d3c8c 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index a50f3a58e..f6d7186f8 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -3273,6 +3273,10 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage case MessageContentType::Poll: return td->poll_manager_->register_poll(static_cast(content)->poll_id, full_message_id, source); + case MessageContentType::Dice: { + auto dice = static_cast(content); + return td->stickers_manager_->register_dice(dice->emoji, dice->dice_value, full_message_id, source); + } default: return; } @@ -3296,6 +3300,14 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const return; } break; + case MessageContentType::Dice: + if (static_cast(old_content)->emoji == + static_cast(new_content)->emoji && + static_cast(old_content)->dice_value == + static_cast(new_content)->dice_value) { + return; + } + break; default: return; } @@ -3313,6 +3325,10 @@ void unregister_message_content(Td *td, const MessageContent *content, FullMessa case MessageContentType::Poll: return td->poll_manager_->unregister_poll(static_cast(content)->poll_id, full_message_id, source); + case MessageContentType::Dice: { + auto dice = static_cast(content); + return td->stickers_manager_->unregister_dice(dice->emoji, dice->dice_value, full_message_id, source); + } default: return; } @@ -4480,7 +4496,11 @@ tl_object_ptr get_message_content_object(const MessageCo } case MessageContentType::Dice: { const MessageDice *m = static_cast(content); - return make_tl_object(m->emoji, m->dice_value); + auto initial_state = td->stickers_manager_->get_dice_sticker_object(m->emoji, 0); + auto final_state = + m->dice_value == 0 ? nullptr : td->stickers_manager_->get_dice_sticker_object(m->emoji, m->dice_value); + return make_tl_object(std::move(initial_state), std::move(final_state), m->emoji, + m->dice_value); } default: UNREACHABLE(); diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 7d2a87803..6ff68bf45 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -35,6 +35,7 @@ #include "td/actor/MultiPromise.h" #include "td/actor/PromiseFuture.h" +#include "td/actor/SleepActor.h" #include "td/db/SqliteKeyValue.h" #include "td/db/SqliteKeyValueAsync.h" @@ -710,12 +711,15 @@ class ReloadSpecialStickerSetQuery : public Td::ResultHandler { auto sticker_set_id = td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true, "ReloadSpecialStickerSetQuery"); if (sticker_set_id.is_valid()) { - td->stickers_manager_->on_get_special_sticker_set(sticker_set_id, type_); + td->stickers_manager_->on_get_special_sticker_set(type_, sticker_set_id); + } else { + on_error(id, Status::Error(500, "Failed to add special sticker set")); } } void on_error(uint64 id, Status status) override { LOG(WARNING) << "Receive error for ReloadSpecialStickerSetQuery: " << status; + td->stickers_manager_->on_load_special_sticker_set(type_, std::move(status)); } }; @@ -1141,24 +1145,20 @@ void StickersManager::init() { { // add animated emoji sticker set - auto animated_emoji_sticker_set_type = SpecialStickerSetType::animated_emoji(); - auto &animated_emoji_sticker_set = special_sticker_sets_[animated_emoji_sticker_set_type]; - animated_emoji_sticker_set.type_.type_ = std::move(animated_emoji_sticker_set_type); + auto &animated_emoji_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji()); if (G()->is_test_dc()) { init_special_sticker_set(animated_emoji_sticker_set, 1258816259751954, 4879754868529595811, "emojies"); } else { init_special_sticker_set(animated_emoji_sticker_set, 1258816259751983, 5100237018658464041, "AnimatedEmojies"); } - load_special_sticker_set(animated_emoji_sticker_set); + load_special_sticker_set_info_from_binlog(animated_emoji_sticker_set); G()->shared_config().set_option_string(PSLICE() << animated_emoji_sticker_set.type_.type_ << "_name", animated_emoji_sticker_set.short_name_); } for (auto &dice_emoji : dice_emojis_) { - auto animated_dice_sticker_set_type = SpecialStickerSetType::animated_dice(dice_emoji); - auto &animated_dice_sticker_set = special_sticker_sets_[animated_dice_sticker_set_type]; - animated_dice_sticker_set.type_.type_ = std::move(animated_dice_sticker_set_type); - load_special_sticker_set(animated_dice_sticker_set); + auto &animated_dice_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(dice_emoji)); + load_special_sticker_set_info_from_binlog(animated_dice_sticker_set); } if (G()->parameters().use_file_db) { @@ -1178,6 +1178,16 @@ void StickersManager::init() { G()->shared_config().set_option_empty("animated_dice_sticker_set_name"); // legacy } +StickersManager::SpecialStickerSet &StickersManager::add_special_sticker_set(const string &type) { + auto &result = special_sticker_sets_[type]; + if (result.type_.type_.empty()) { + result.type_.type_ = type; + } else { + CHECK(result.type_.type_ == type); + } + return result; +} + void StickersManager::init_special_sticker_set(SpecialStickerSet &sticker_set, int64 sticker_set_id, int64 access_hash, string name) { sticker_set.id_ = StickerSetId(sticker_set_id); @@ -1185,7 +1195,7 @@ void StickersManager::init_special_sticker_set(SpecialStickerSet &sticker_set, i sticker_set.short_name_ = std::move(name); } -void StickersManager::load_special_sticker_set(SpecialStickerSet &sticker_set) { +void StickersManager::load_special_sticker_set_info_from_binlog(SpecialStickerSet &sticker_set) { if (G()->parameters().use_file_db) { string sticker_set_string = G()->td_db()->get_binlog_pmc()->get(sticker_set.type_.type_); if (!sticker_set_string.empty()) { @@ -1217,6 +1227,80 @@ void StickersManager::load_special_sticker_set(SpecialStickerSet &sticker_set) { short_name_to_sticker_set_id_.emplace(sticker_set.short_name_, sticker_set.id_); } +void StickersManager::load_special_sticker_set_by_type(const SpecialStickerSetType &type) { + if (G()->close_flag()) { + return; + } + + auto &sticker_set = add_special_sticker_set(type.type_); + CHECK(sticker_set.is_being_loaded_); + sticker_set.is_being_loaded_ = false; + load_special_sticker_set(sticker_set); +} + +void StickersManager::load_special_sticker_set(SpecialStickerSet &sticker_set) { + CHECK(!sticker_set.is_being_loaded_); + sticker_set.is_being_loaded_ = true; + if (sticker_set.id_.is_valid()) { + auto promise = PromiseCreator::lambda([actor_id = actor_id(this), type = sticker_set.type_](Result &&result) { + send_closure(actor_id, &StickersManager::on_load_special_sticker_set, type, + result.is_ok() ? Status::OK() : result.move_as_error()); + }); + load_sticker_sets({sticker_set.id_}, std::move(promise)); + } else { + reload_special_sticker_set(sticker_set); + } +} + +void StickersManager::reload_special_sticker_set(SpecialStickerSet &sticker_set) { + td_->create_handler()->send(sticker_set.type_); +} + +void StickersManager::on_load_special_sticker_set(const SpecialStickerSetType &type, Status result) { + if (G()->close_flag()) { + return; + } + + auto &special_sticker_set = add_special_sticker_set(type.type_); + if (!special_sticker_set.is_being_loaded_) { + return; + } + + if (result.is_error()) { + // failed to load the special sticker set; repeat after some time + create_actor("RetryLoadSpecialStickerSetActor", Random::fast(300, 600), + PromiseCreator::lambda([actor_id = actor_id(this), type](Result result) { + send_closure(actor_id, &StickersManager::load_special_sticker_set_by_type, type); + })) + .release(); + return; + } + + 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); + + auto it = dice_messages_.find(emoji); + if (it == dice_messages_.end()) { + return; + } + + vector full_message_ids; + for (auto full_message_id : it->second) { + full_message_ids.push_back(full_message_id); + } + CHECK(!full_message_ids.empty()); + for (auto full_message_id : full_message_ids) { + td_->messages_manager_->on_external_update_message_content(full_message_id); + } +} + void StickersManager::tear_down() { parent_.reset(); } @@ -1269,6 +1353,30 @@ tl_object_ptr StickersManager::get_stickers_object(const vecto return result; } +tl_object_ptr StickersManager::get_dice_sticker_object(const string &emoji, int32 value) const { + if (td_->auth_manager_->is_bot()) { + return nullptr; + } + if (!td::contains(dice_emojis_, emoji)) { + return nullptr; + } + + auto it = special_sticker_sets_.find(SpecialStickerSetType::animated_dice(emoji)); + if (it == special_sticker_sets_.end()) { + return nullptr; + } + + auto sticker_set_id = it->second.id_; + if (sticker_set_id.is_valid()) { + auto sticker_set = get_sticker_set(sticker_set_id); + CHECK(sticker_set != nullptr); + if (sticker_set->was_loaded && value >= 0 && value < static_cast(sticker_set->sticker_ids.size())) { + return get_sticker_object(sticker_set->sticker_ids[value]); + } + } + return nullptr; +} + tl_object_ptr StickersManager::get_sticker_set_object(StickerSetId sticker_set_id) const { const StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); @@ -1524,7 +1632,7 @@ StickerSetId StickersManager::get_sticker_set_id(const tl_object_ptris_inited); CHECK(s->is_loaded); - auto &sticker_set = special_sticker_sets_[type.type_]; + auto &sticker_set = add_special_sticker_set(type.type_); if (sticker_set_id == sticker_set.id_ && s->short_name == sticker_set.short_name_ && !s->short_name.empty()) { + on_load_special_sticker_set(type, Status::OK()); return; } @@ -2261,7 +2370,10 @@ void StickersManager::on_get_special_sticker_set(StickerSetId sticker_set_id, co << ' ' << 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.get_dice_emoji().empty()) { + sticker_set.is_being_loaded_ = true; } + on_load_special_sticker_set(type, Status::OK()); } void StickersManager::on_get_installed_sticker_sets(bool is_masks, @@ -3269,6 +3381,61 @@ void StickersManager::on_update_sticker_sets() { reload_installed_sticker_sets(true, true); } +void StickersManager::register_dice(const string &emoji, int32 value, FullMessageId full_message_id, + const char *source) { + CHECK(!emoji.empty()); + if (td_->auth_manager_->is_bot()) { + return; + } + + LOG(INFO) << "Register dice " << emoji << " with value " << value << " from " << full_message_id << " from " + << source; + bool is_inserted = dice_messages_[emoji].insert(full_message_id).second; + LOG_CHECK(is_inserted) << source << " " << emoji << " " << value << " " << full_message_id; + + if (!td::contains(dice_emojis_, emoji) && !full_message_id.get_message_id().is_any_server()) { + return; + } + + auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(emoji)); + bool need_load = false; + if (!special_sticker_set.id_.is_valid()) { + need_load = true; + } else { + auto sticker_set = get_sticker_set(special_sticker_set.id_); + CHECK(sticker_set != nullptr); + need_load = !sticker_set->was_loaded; + } + + if (need_load) { + LOG(INFO) << "Waiting for a dice sticker set needed in " << full_message_id; + if (!special_sticker_set.is_being_loaded_) { + load_special_sticker_set(special_sticker_set); + } + } else { + // TODO reload once in a while + // reload_special_sticker_set(special_sticker_set); + } +} + +void StickersManager::unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id, + const char *source) { + CHECK(!emoji.empty()); + if (td_->auth_manager_->is_bot()) { + return; + } + + LOG(INFO) << "Unregister dice " << emoji << " with value " << value << " from " << full_message_id << " from " + << source; + auto &message_ids = dice_messages_[emoji]; + auto is_deleted = message_ids.erase(full_message_id); + LOG_CHECK(is_deleted) << source << " " << emoji << " " << value << " " << full_message_id; + + if (message_ids.empty()) { + dice_messages_.erase(emoji); + } +} + 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); @@ -5918,9 +6085,7 @@ void StickersManager::after_get_difference() { get_recent_stickers(true, Auto()); get_favorite_stickers(Auto()); - SpecialStickerSetType type; - type.type_ = SpecialStickerSetType::animated_emoji(); - td_->create_handler()->send(std::move(type)); + reload_special_sticker_set(add_special_sticker_set(SpecialStickerSetType::animated_emoji())); } } diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index d71ead951..7ba3136ca 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -13,6 +13,7 @@ #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/Photo.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/SpecialStickerSetType.h" @@ -51,6 +52,8 @@ class StickersManager : public Actor { tl_object_ptr get_stickers_object(const vector &sticker_ids) const; + tl_object_ptr get_dice_sticker_object(const string &emoji, int32 value) const; + tl_object_ptr get_sticker_set_object(StickerSetId sticker_set_id) const; tl_object_ptr get_sticker_sets_object(int32 total_count, @@ -59,6 +62,10 @@ class StickersManager : public Actor { tl_object_ptr get_input_sticker_set(StickerSetId sticker_set_id) const; + void register_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source); + + void unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source); + void create_sticker(FileId file_id, PhotoSize thumbnail, Dimensions dimensions, tl_object_ptr sticker, bool is_animated, MultiPromiseActor *load_data_multipromise_ptr); @@ -111,7 +118,9 @@ class StickersManager : public Actor { StickerSetId on_get_sticker_set_covered(tl_object_ptr &&set_ptr, bool is_changed, const char *source); - void on_get_special_sticker_set(StickerSetId sticker_set_id, const SpecialStickerSetType &type); + void on_get_special_sticker_set(const SpecialStickerSetType &type, StickerSetId sticker_set_id); + + void on_load_special_sticker_set(const SpecialStickerSetType &type, Status result); void on_load_sticker_set_fail(StickerSetId sticker_set_id, const Status &error); @@ -368,6 +377,7 @@ class StickersManager : public Actor { int64 access_hash_ = 0; string short_name_; SpecialStickerSetType type_; + bool is_being_loaded_ = false; }; class StickerListLogEvent; @@ -547,11 +557,19 @@ class StickersManager : public Actor { void tear_down() override; + SpecialStickerSet &add_special_sticker_set(const string &type); + static void init_special_sticker_set(SpecialStickerSet &sticker_set, int64 sticker_set_id, int64 access_hash, string name); + void load_special_sticker_set_info_from_binlog(SpecialStickerSet &sticker_set); + + void load_special_sticker_set_by_type(const SpecialStickerSetType &type); + void load_special_sticker_set(SpecialStickerSet &sticker_set); + void reload_special_sticker_set(SpecialStickerSet &sticker_set); + static void add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail); static string get_sticker_mime_type(const Sticker *s); @@ -688,6 +706,8 @@ class StickersManager : public Actor { std::unordered_map>> load_language_codes_queries_; std::unordered_map emoji_suggestions_urls_; + std::unordered_map> dice_messages_; + string dice_emojis_str_; vector dice_emojis_; };