// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // 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) // #include "td/telegram/ReactionType.h" #include "td/telegram/misc.h" #include "td/utils/as.h" #include "td/utils/base64.h" #include "td/utils/crypto.h" #include "td/utils/emoji.h" #include "td/utils/SliceBuilder.h" #include "td/utils/utf8.h" namespace td { static int64 get_custom_emoji_id(const string &reaction) { auto r_decoded = base64_decode(Slice(&reaction[1], reaction.size() - 1)); CHECK(r_decoded.is_ok()); CHECK(r_decoded.ok().size() == 8); return as(r_decoded.ok().c_str()); } static string get_custom_emoji_string(int64 custom_emoji_id) { char s[8]; as(&s) = custom_emoji_id; return PSTRING() << '#' << base64_encode(Slice(s, 8)); } ReactionType::ReactionType(string &&emoji) : reaction_(std::move(emoji)) { } ReactionType::ReactionType(const telegram_api::object_ptr &reaction) { if (reaction == nullptr) { return; } switch (reaction->get_id()) { case telegram_api::reactionEmpty::ID: break; case telegram_api::reactionEmoji::ID: reaction_ = static_cast(reaction.get())->emoticon_; if (is_custom_reaction()) { reaction_ = string(); } break; case telegram_api::reactionCustomEmoji::ID: reaction_ = get_custom_emoji_string(static_cast(reaction.get())->document_id_); break; default: UNREACHABLE(); break; } } ReactionType::ReactionType(const td_api::object_ptr &type) { if (type == nullptr) { return; } switch (type->get_id()) { case td_api::reactionTypeEmoji::ID: { const string &emoji = static_cast(type.get())->emoji_; if (!check_utf8(emoji)) { break; } reaction_ = emoji; if (is_custom_reaction()) { reaction_ = string(); break; } break; } case td_api::reactionTypeCustomEmoji::ID: reaction_ = get_custom_emoji_string(static_cast(type.get())->custom_emoji_id_); break; default: UNREACHABLE(); break; } } telegram_api::object_ptr ReactionType::get_input_reaction() const { if (is_empty()) { return telegram_api::make_object(); } if (is_custom_reaction()) { return telegram_api::make_object(get_custom_emoji_id(reaction_)); } return telegram_api::make_object(reaction_); } td_api::object_ptr ReactionType::get_reaction_type_object() const { CHECK(!is_empty()); if (is_custom_reaction()) { return td_api::make_object(get_custom_emoji_id(reaction_)); } return td_api::make_object(reaction_); } td_api::object_ptr ReactionType::get_update_default_reaction_type() const { if (is_empty()) { return nullptr; } return td_api::make_object(get_reaction_type_object()); } bool ReactionType::is_custom_reaction() const { return reaction_[0] == '#'; } bool ReactionType::is_active_reaction( const FlatHashMap &active_reaction_pos) const { return !reaction_.empty() && (is_custom_reaction() || active_reaction_pos.count(*this) > 0); } bool operator<(const ReactionType &lhs, const ReactionType &rhs) { return lhs.reaction_ < rhs.reaction_; } bool operator==(const ReactionType &lhs, const ReactionType &rhs) { return lhs.reaction_ == rhs.reaction_; } StringBuilder &operator<<(StringBuilder &string_builder, const ReactionType &reaction_type) { return string_builder << reaction_type.reaction_; } int64 get_reaction_types_hash(const vector &reaction_types) { vector numbers; for (auto &reaction_type : reaction_types) { if (reaction_type.is_custom_reaction()) { auto custom_emoji_id = static_cast(get_custom_emoji_id(reaction_type.get_string())); numbers.push_back(custom_emoji_id >> 32); numbers.push_back(custom_emoji_id & 0xFFFFFFFF); } else { auto emoji = remove_emoji_selectors(reaction_type.get_string()); unsigned char hash[16]; md5(emoji, {hash, sizeof(hash)}); auto get = [hash](int num) { return static_cast(hash[num]); }; numbers.push_back(0); numbers.push_back(static_cast((get(0) << 24) + (get(1) << 16) + (get(2) << 8) + get(3))); } } return get_vector_hash(numbers); } } // namespace td