From 739d2b292dd468dd96b5cefb801cbb08bdabed72 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 20 Jan 2022 22:54:34 +0300 Subject: [PATCH] Add MessageReactions. --- CMakeLists.txt | 3 + td/generate/scheme/td_api.tl | 10 ++- td/telegram/ContactsManager.cpp | 2 +- td/telegram/MessageReaction.cpp | 143 ++++++++++++++++++++++++++++++++ td/telegram/MessageReaction.h | 106 +++++++++++++++++++++++ td/telegram/MessageReaction.hpp | 86 +++++++++++++++++++ td/telegram/MessagesManager.cpp | 82 +++++++++++++++--- td/telegram/MessagesManager.h | 17 +++- 8 files changed, 431 insertions(+), 18 deletions(-) create mode 100644 td/telegram/MessageReaction.cpp create mode 100644 td/telegram/MessageReaction.h create mode 100644 td/telegram/MessageReaction.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b7e8905cf..ddca43602 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -356,6 +356,7 @@ set(TDLIB_SOURCE td/telegram/MessageContentType.cpp td/telegram/MessageEntity.cpp td/telegram/MessageId.cpp + td/telegram/MessageReaction.cpp td/telegram/MessageReplyInfo.cpp td/telegram/MessagesDb.cpp td/telegram/MessageSearchFilter.cpp @@ -561,6 +562,7 @@ set(TDLIB_SOURCE td/telegram/MessageEntity.h td/telegram/MessageId.h td/telegram/MessageLinkInfo.h + td/telegram/MessageReaction.h td/telegram/MessageReplyInfo.h td/telegram/MessageThreadInfo.h td/telegram/MessagesDb.h @@ -677,6 +679,7 @@ set(TDLIB_SOURCE td/telegram/Game.hpp td/telegram/InputMessageText.hpp td/telegram/MessageEntity.hpp + td/telegram/MessageReaction.hpp td/telegram/MessageReplyInfo.hpp td/telegram/MinChannel.hpp td/telegram/NotificationSettings.hpp diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 0e57897a5..700b0b7e9 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -754,11 +754,19 @@ messageForwardInfo origin:MessageForwardOrigin date:int32 public_service_announc //@last_message_id Identifier of the last reply to the message messageReplyInfo reply_count:int32 recent_replier_ids:vector last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 last_message_id:int53 = MessageReplyInfo; +//@description Contains information about a reaction to a message +//@reaction Text representation of the reaction +//@choose_count Number of times the reaction was chosen +//@is_chosen True, if the reaction is chosen by the user +//@recent_chooser_user_ids Identifiers of at most 3 recent users, chosen the reaction +messageReaction reaction:string choose_count:int32 is_chosen:Bool recent_chooser_user_ids:vector = MessageReaction; + //@description Contains information about interactions with a message //@view_count Number of times the message was viewed //@forward_count Number of times the message was forwarded //@reply_info Information about direct or indirect replies to the message; may be null. Currently, available only in channels with a discussion supergroup and discussion supergroups for messages, which are not replies itself -messageInteractionInfo view_count:int32 forward_count:int32 reply_info:messageReplyInfo = MessageInteractionInfo; +//@reactions The list of reactions chosen for the message +messageInteractionInfo view_count:int32 forward_count:int32 reply_info:messageReplyInfo reactions:vector = MessageInteractionInfo; //@class MessageSendingState @description Contains information about the sending state of the message diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index f77cfcbf6..3b04d9bae 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3085,7 +3085,7 @@ class GetBroadcastStatsQuery final : public Td::ResultHandler { for (auto &info : result->recent_message_interactions_) { td_->messages_manager_->on_update_message_interaction_info({DialogId(channel_id_), MessageId(info->message_id_)}, info->view_count_, info->forward_count_, false, - nullptr); + nullptr, false, nullptr); } promise_.set_value(std::move(result)); } diff --git a/td/telegram/MessageReaction.cpp b/td/telegram/MessageReaction.cpp new file mode 100644 index 000000000..76c66b740 --- /dev/null +++ b/td/telegram/MessageReaction.cpp @@ -0,0 +1,143 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 +// +// 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/MessageReaction.h" + +#include "td/telegram/ContactsManager.h" +#include "td/telegram/Td.h" + +#include "td/utils/algorithm.h" +#include "td/utils/logging.h" + +#include + +namespace td { + +td_api::object_ptr MessageReaction::get_message_reaction_object(Td *td) const { + CHECK(!is_empty()); + + vector recent_choosers; + for (auto user_id : recent_chooser_user_ids_) { + if (td->contacts_manager_->have_min_user(user_id)) { + recent_choosers.push_back(td->contacts_manager_->get_user_id_object(user_id, "get_message_reaction_object")); + } else { + LOG(ERROR) << "Skip unknown reacted " << user_id; + } + } + return td_api::make_object(reaction_, choose_count_, is_chosen_, std::move(recent_choosers)); +} + +bool operator==(const MessageReaction &lhs, const MessageReaction &rhs) { + return lhs.reaction_ == rhs.reaction_ && lhs.choose_count_ == rhs.choose_count_ && lhs.is_chosen_ == rhs.is_chosen_ && + lhs.recent_chooser_user_ids_ == rhs.recent_chooser_user_ids_; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageReaction &reaction) { + string_builder << '[' << reaction.reaction_ << (reaction.is_chosen_ ? " X " : " x ") << reaction.choose_count_; + if (!reaction.recent_chooser_user_ids_.empty()) { + string_builder << " by " << reaction.recent_chooser_user_ids_; + } + return string_builder << ']'; +} + +unique_ptr MessageReactions::get_message_reactions( + Td *td, tl_object_ptr &&reactions, bool is_bot) { + if (reactions == nullptr || is_bot) { + return nullptr; + } + + auto result = make_unique(); + result->can_see_all_choosers_ = reactions->can_see_list_; + result->is_min_ = reactions->min_; + + std::unordered_set reaction_strings; + for (auto &reaction_count : reactions->results_) { + if (reaction_count->count_ <= 0 || reaction_count->count_ >= MessageReaction::MAX_CHOOSE_COUNT) { + LOG(ERROR) << "Receive reaction " << reaction_count->reaction_ << " with invalid count " + << reaction_count->count_; + continue; + } + + if (!reaction_strings.insert(reaction_count->reaction_).second) { + LOG(ERROR) << "Receive duplicate reaction " << reaction_count->reaction_; + continue; + } + + vector recent_chooser_user_ids; + for (auto &user_reaction : reactions->recent_reactons_) { + if (user_reaction->reaction_ == reaction_count->reaction_) { + UserId user_id(user_reaction->user_id_); + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive invalid " << user_id; + continue; + } + if (td::contains(recent_chooser_user_ids, user_id)) { + LOG(ERROR) << "Receive duplicate " << user_id; + continue; + } + if (!td->contacts_manager_->have_min_user(user_id)) { + LOG(ERROR) << "Have no info about " << user_id; + continue; + } + + recent_chooser_user_ids.push_back(user_id); + if (recent_chooser_user_ids.size() == MessageReaction::MAX_RECENT_CHOOSERS) { + break; + } + } + } + + result->reactions_.emplace_back(std::move(reaction_count->reaction_), reaction_count->count_, + reaction_count->chosen_, std::move(recent_chooser_user_ids)); + } + return result; +} + +void MessageReactions::update_from(const MessageReactions &old_reactions) { + if (old_reactions.has_pending_reaction_) { + // we will ignore all updates, received while there is a pending reaction, so there are no reasons to update + return; + } + CHECK(!has_pending_reaction_); + + if (is_min_ && !old_reactions.is_min_) { + // chosen reaction was known, keep it + is_min_ = false; + for (const auto &old_reaction : old_reactions.reactions_) { + if (old_reaction.is_chosen()) { + for (auto &reaction : reactions_) { + if (reaction.get_reaction() == old_reaction.get_reaction()) { + reaction.set_is_chosen(true); + } + } + } + } + } +} + +bool MessageReactions::need_update_message_reactions(const MessageReactions *old_reactions, + const MessageReactions *new_reactions) { + if (old_reactions == nullptr) { + // add reactions + return new_reactions != nullptr; + } + if (old_reactions->has_pending_reaction_) { + // ignore all updates, received while there is a pending reaction + return false; + } + if (new_reactions == nullptr) { + // remove reactions when they are disabled + return true; + } + + // has_pending_reaction_ and old_chosen_reaction_ don't affect visible state + // compare all other fields + return old_reactions->reactions_ != new_reactions->reactions_ || old_reactions->is_min_ != new_reactions->is_min_ || + old_reactions->can_see_all_choosers_ != new_reactions->can_see_all_choosers_ || + old_reactions->need_polling_ != new_reactions->need_polling_; +} + +} // namespace td diff --git a/td/telegram/MessageReaction.h b/td/telegram/MessageReaction.h new file mode 100644 index 000000000..e25f781dd --- /dev/null +++ b/td/telegram/MessageReaction.h @@ -0,0 +1,106 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 +// +// 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/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class Td; + +class MessageReaction { + string reaction_; + int32 choose_count_ = 0; + bool is_chosen_ = false; + vector recent_chooser_user_ids_; + + friend bool operator==(const MessageReaction &lhs, const MessageReaction &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const MessageReaction &message_reaction); + + public: + static constexpr size_t MAX_RECENT_CHOOSERS = 3; + static constexpr int32 MAX_CHOOSE_COUNT = 2147483640; + + MessageReaction() = default; + + MessageReaction(string &&reaction, int32 choose_count, bool is_chosen, vector &&recent_chooser_user_ids) + : reaction_(std::move(reaction)) + , choose_count_(choose_count) + , is_chosen_(is_chosen) + , recent_chooser_user_ids_(std::move(recent_chooser_user_ids)) { + } + + bool is_empty() const { + return choose_count_ <= 0; + } + + const string &get_reaction() const { + return reaction_; + } + + bool is_chosen() const { + return is_chosen_; + } + + void set_is_chosen(bool is_chosen) { + is_chosen_ = is_chosen; + } + + const vector &get_recent_chooser_user_ids() const { + return recent_chooser_user_ids_; + } + + td_api::object_ptr get_message_reaction_object(Td *td) const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +bool operator==(const MessageReaction &lhs, const MessageReaction &rhs); + +inline bool operator!=(const MessageReaction &lhs, const MessageReaction &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageReaction &message_reaction); + +struct MessageReactions { + vector reactions_; + bool is_min_ = false; + bool need_polling_ = true; + bool can_see_all_choosers_ = false; + bool has_pending_reaction_ = false; + string old_chosen_reaction_; + + MessageReactions() = default; + + static unique_ptr get_message_reactions(Td *td, + tl_object_ptr &&reactions, + bool is_bot); + + void update_from(const MessageReactions &old_reactions); + + static bool need_update_message_reactions(const MessageReactions *old_reactions, + const MessageReactions *new_reactions); + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +} // namespace td diff --git a/td/telegram/MessageReaction.hpp b/td/telegram/MessageReaction.hpp new file mode 100644 index 000000000..108632a5f --- /dev/null +++ b/td/telegram/MessageReaction.hpp @@ -0,0 +1,86 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 +// +// 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/MessageReaction.h" + +#include "td/utils/common.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +template +void MessageReaction::store(StorerT &storer) const { + CHECK(!is_empty()); + bool has_recent_chooser_user_ids = !recent_chooser_user_ids_.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(is_chosen_); + STORE_FLAG(has_recent_chooser_user_ids); + END_STORE_FLAGS(); + td::store(reaction_, storer); + td::store(choose_count_, storer); + if (has_recent_chooser_user_ids) { + td::store(recent_chooser_user_ids_, storer); + } +} + +template +void MessageReaction::parse(ParserT &parser) { + bool has_recent_chooser_user_ids; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(is_chosen_); + PARSE_FLAG(has_recent_chooser_user_ids); + END_PARSE_FLAGS(); + td::parse(reaction_, parser); + td::parse(choose_count_, parser); + if (has_recent_chooser_user_ids) { + td::parse(recent_chooser_user_ids_, parser); + } + CHECK(!is_empty()); +} + +template +void MessageReactions::store(StorerT &storer) const { + bool has_reactions = !reactions_.empty(); + bool has_old_chosen_reaction = !old_chosen_reaction_.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(is_min_); + STORE_FLAG(need_polling_); + STORE_FLAG(can_see_all_choosers_); + STORE_FLAG(has_pending_reaction_); + STORE_FLAG(has_reactions); + STORE_FLAG(has_old_chosen_reaction); + END_STORE_FLAGS(); + if (has_reactions) { + td::store(reactions_, storer); + } + if (has_old_chosen_reaction) { + td::store(old_chosen_reaction_, storer); + } +} + +template +void MessageReactions::parse(ParserT &parser) { + bool has_reactions; + bool has_old_chosen_reaction; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(is_min_); + PARSE_FLAG(need_polling_); + PARSE_FLAG(can_see_all_choosers_); + PARSE_FLAG(has_pending_reaction_); + PARSE_FLAG(has_reactions); + PARSE_FLAG(has_old_chosen_reaction); + END_PARSE_FLAGS(); + if (has_reactions) { + td::parse(reactions_, parser); + } + if (has_old_chosen_reaction) { + td::parse(old_chosen_reaction_, parser); + } +} + +} // namespace td diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index ce3f7ac6f..00232a695 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -33,6 +33,8 @@ #include "td/telegram/MessageContent.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageEntity.hpp" +#include "td/telegram/MessageReaction.h" +#include "td/telegram/MessageReaction.hpp" #include "td/telegram/MessageReplyInfo.hpp" #include "td/telegram/MessagesDb.h" #include "td/telegram/MessageSender.h" @@ -1919,7 +1921,7 @@ class GetMessagesViewsQuery final : public Td::ResultHandler { auto view_count = (flags & telegram_api::messageViews::VIEWS_MASK) != 0 ? info->views_ : 0; auto forward_count = (flags & telegram_api::messageViews::FORWARDS_MASK) != 0 ? info->forwards_ : 0; td_->messages_manager_->on_update_message_interaction_info(full_message_id, view_count, forward_count, true, - std::move(info->replies_)); + std::move(info->replies_), false, nullptr); } } @@ -4891,6 +4893,7 @@ void MessagesManager::Message::store(StorerT &storer) const { bool has_max_reply_media_timestamp = max_reply_media_timestamp >= 0; bool are_message_media_timestamp_entities_found = true; bool has_flags3 = true; + bool has_reactions = reactions != nullptr; BEGIN_STORE_FLAGS(); STORE_FLAG(is_channel_post); STORE_FLAG(is_outgoing); @@ -4961,6 +4964,7 @@ void MessagesManager::Message::store(StorerT &storer) const { BEGIN_STORE_FLAGS(); STORE_FLAG(noforwards); STORE_FLAG(has_explicit_sender); + STORE_FLAG(has_reactions); END_STORE_FLAGS(); } @@ -5079,6 +5083,9 @@ void MessagesManager::Message::store(StorerT &storer) const { if (has_max_reply_media_timestamp) { store(max_reply_media_timestamp, storer); } + if (has_reactions) { + store(reactions, storer); + } } // do not forget to resolve message dependencies @@ -5122,6 +5129,7 @@ void MessagesManager::Message::parse(ParserT &parser) { bool has_ttl_period = false; bool has_max_reply_media_timestamp = false; bool has_flags3 = false; + bool has_reactions = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_channel_post); PARSE_FLAG(is_outgoing); @@ -5192,6 +5200,7 @@ void MessagesManager::Message::parse(ParserT &parser) { BEGIN_PARSE_FLAGS(); PARSE_FLAG(noforwards); PARSE_FLAG(has_explicit_sender); + PARSE_FLAG(has_reactions); END_PARSE_FLAGS(); } @@ -5317,6 +5326,9 @@ void MessagesManager::Message::parse(ParserT &parser) { if (has_max_reply_media_timestamp) { parse(max_reply_media_timestamp, parser); } + if (has_reactions) { + parse(reactions, parser); + } CHECK(content != nullptr); is_content_secret |= @@ -6787,7 +6799,7 @@ void MessagesManager::on_update_message_view_count(FullMessageId full_message_id LOG(ERROR) << "Receive " << view_count << " views in updateChannelMessageViews for " << full_message_id; return; } - update_message_interaction_info(full_message_id, view_count, -1, false, nullptr); + update_message_interaction_info(full_message_id, view_count, -1, false, nullptr, false, nullptr); } void MessagesManager::on_update_message_forward_count(FullMessageId full_message_id, int32 forward_count) { @@ -6795,17 +6807,20 @@ void MessagesManager::on_update_message_forward_count(FullMessageId full_message LOG(ERROR) << "Receive " << forward_count << " forwards in updateChannelMessageForwards for " << full_message_id; return; } - update_message_interaction_info(full_message_id, -1, forward_count, false, nullptr); + update_message_interaction_info(full_message_id, -1, forward_count, false, nullptr, false, nullptr); } void MessagesManager::on_update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count, bool has_reply_info, - tl_object_ptr &&reply_info) { + tl_object_ptr &&reply_info, + bool has_reactions, + tl_object_ptr &&reactions) { if (view_count < 0 || forward_count < 0) { LOG(ERROR) << "Receive " << view_count << "/" << forward_count << " interaction counters for " << full_message_id; return; } - update_message_interaction_info(full_message_id, view_count, forward_count, has_reply_info, std::move(reply_info)); + update_message_interaction_info(full_message_id, view_count, forward_count, has_reply_info, std::move(reply_info), + has_reactions, std::move(reactions)); } void MessagesManager::on_pending_message_views_timeout(DialogId dialog_id) { @@ -6835,7 +6850,9 @@ void MessagesManager::on_pending_message_views_timeout(DialogId dialog_id) { void MessagesManager::update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count, bool has_reply_info, - tl_object_ptr &&reply_info) { + tl_object_ptr &&reply_info, + bool has_reactions, + tl_object_ptr &&reactions) { auto dialog_id = full_message_id.get_dialog_id(); Dialog *d = get_dialog_force(dialog_id, "update_message_interaction_info"); if (d == nullptr) { @@ -6863,9 +6880,11 @@ void MessagesManager::update_message_interaction_info(FullMessageId full_message if (new_reply_info.is_empty() && !is_empty_reply_info) { has_reply_info = false; } + auto new_reactions = MessageReactions::get_message_reactions(td_, std::move(reactions), td_->auth_manager_->is_bot()); if (update_message_interaction_info(dialog_id, m, view_count, forward_count, has_reply_info, - std::move(new_reply_info), "update_message_interaction_info")) { + std::move(new_reply_info), has_reactions, std::move(new_reactions), + "update_message_interaction_info")) { on_message_changed(d, m, true, "update_message_interaction_info"); } } @@ -6916,6 +6935,11 @@ bool MessagesManager::is_visible_message_reply_info(DialogId dialog_id, const Me return is_active_message_reply_info(dialog_id, m->reply_info); } +bool MessagesManager::is_visible_message_reactions(DialogId dialog_id, const Message *m) const { + // TODO hide message reactions if reactions are disabled in the chat + return true; +} + void MessagesManager::on_message_reply_info_changed(DialogId dialog_id, const Message *m) const { if (td_->auth_manager_->is_bot()) { return; @@ -6929,7 +6953,9 @@ void MessagesManager::on_message_reply_info_changed(DialogId dialog_id, const Me td_api::object_ptr MessagesManager::get_message_interaction_info_object( DialogId dialog_id, const Message *m) const { bool is_visible_reply_info = is_visible_message_reply_info(dialog_id, m); - if (m->view_count == 0 && m->forward_count == 0 && !is_visible_reply_info) { + bool has_reactions = + is_visible_message_reactions(dialog_id, m) && m->reactions != nullptr && !m->reactions->reactions_.empty(); + if (m->view_count == 0 && m->forward_count == 0 && !is_visible_reply_info && !has_reactions) { return nullptr; } if (m->message_id.is_scheduled() && (m->forward_info == nullptr || is_broadcast_channel(dialog_id))) { @@ -6945,12 +6971,21 @@ td_api::object_ptr MessagesManager::get_message_ CHECK(reply_info != nullptr); } - return td_api::make_object(m->view_count, m->forward_count, std::move(reply_info)); + vector> reactions; + if (has_reactions) { + reactions = transform(m->reactions->reactions_, [td = td_](const MessageReaction &reaction) { + return reaction.get_message_reaction_object(td); + }); + } + + return td_api::make_object(m->view_count, m->forward_count, std::move(reply_info), + std::move(reactions)); } bool MessagesManager::update_message_interaction_info(DialogId dialog_id, Message *m, int32 view_count, int32 forward_count, bool has_reply_info, - MessageReplyInfo &&reply_info, const char *source) { + MessageReplyInfo &&reply_info, bool has_reactions, + unique_ptr &&reactions, const char *source) { CHECK(m != nullptr); m->interaction_info_update_date = G()->unix_time(); // doesn't force message saving if (m->message_id.is_valid_scheduled()) { @@ -6968,7 +7003,13 @@ bool MessagesManager::update_message_interaction_info(DialogId dialog_id, Messag } } } - if (view_count > m->view_count || forward_count > m->forward_count || need_update_reply_info) { + if (has_reactions && reactions != nullptr && m->reactions != nullptr) { + reactions->update_from(*m->reactions); + } + bool need_update_reactions = + has_reactions && MessageReactions::need_update_message_reactions(m->reactions.get(), reactions.get()); + if (view_count > m->view_count || forward_count > m->forward_count || need_update_reply_info || + need_update_reactions) { LOG(DEBUG) << "Update interaction info of " << FullMessageId{dialog_id, m->message_id} << " from " << m->view_count << '/' << m->forward_count << "/" << m->reply_info << " to " << view_count << '/' << forward_count << "/" << reply_info; @@ -6995,6 +7036,10 @@ bool MessagesManager::update_message_interaction_info(DialogId dialog_id, Messag } need_update |= is_visible_message_reply_info(dialog_id, m); } + if (need_update_reactions) { + m->reactions = std::move(reactions); + need_update |= is_visible_message_reactions(dialog_id, m); + } if (need_update) { send_update_message_interaction_info(dialog_id, m); } @@ -13702,6 +13747,9 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( if (message->flags_ & MESSAGE_FLAG_HAS_REPLY_INFO) { message_info.reply_info = std::move(message->replies_); } + if (message->flags_ & MESSAGE_FLAG_HAS_REACTIONS) { + message_info.reactions = std::move(message->reactions_); + } if (message->flags_ & MESSAGE_FLAG_HAS_EDIT_DATE) { message_info.edit_date = message->edit_date_; } @@ -13959,6 +14007,8 @@ std::pair> MessagesManager::creat if (top_thread_message_id.is_valid() && dialog_type != DialogType::Channel) { top_thread_message_id = MessageId(); } + auto reactions = + MessageReactions::get_message_reactions(td_, std::move(message_info.reactions), td_->auth_manager_->is_bot()); bool has_forward_info = message_info.forward_header != nullptr; @@ -14003,6 +14053,7 @@ std::pair> MessagesManager::creat message->view_count = view_count; message->forward_count = forward_count; message->reply_info = std::move(reply_info); + message->reactions = std::move(reactions); message->legacy_layer = (is_legacy ? MTPROTO_LAYER : 0); message->content = std::move(message_info.content); message->reply_markup = get_reply_markup(std::move(message_info.reply_markup), td_->auth_manager_->is_bot(), false, @@ -24373,6 +24424,12 @@ void MessagesManager::add_message_dependencies(Dependencies &dependencies, const // it will be created in get_message_reply_info_object if needed add_dialog_dependencies(dependencies, recent_replier_dialog_id); } + if (m->reactions != nullptr) { + for (const auto &reaction : m->reactions->reactions_) { + const auto &user_ids = reaction.get_recent_chooser_user_ids(); + dependencies.user_ids.insert(user_ids.begin(), user_ids.end()); + } + } add_message_content_dependencies(dependencies, m->content.get()); add_reply_markup_dependencies(dependencies, m->reply_markup.get()); } @@ -34731,7 +34788,8 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr need_send_update = true; } if (update_message_interaction_info(dialog_id, old_message, new_message->view_count, new_message->forward_count, true, - std::move(new_message->reply_info), "update_message")) { + std::move(new_message->reply_info), true, std::move(new_message->reactions), + "update_message")) { need_send_update = true; } if (old_message->noforwards != new_message->noforwards) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 8551e6b99..140fd43b8 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -91,6 +91,7 @@ class DialogFilter; class DraftMessage; struct InputMessageContent; class MessageContent; +struct MessageReactions; class MultiSequenceDispatcher; class Td; @@ -115,6 +116,7 @@ class MessagesManager final : public Actor { static constexpr int32 MESSAGE_FLAG_HAS_MEDIA_ALBUM_ID = 1 << 17; static constexpr int32 MESSAGE_FLAG_IS_FROM_SCHEDULED = 1 << 18; static constexpr int32 MESSAGE_FLAG_IS_LEGACY = 1 << 19; + static constexpr int32 MESSAGE_FLAG_HAS_REACTIONS = 1 << 20; static constexpr int32 MESSAGE_FLAG_HIDE_EDIT_DATE = 1 << 21; static constexpr int32 MESSAGE_FLAG_IS_RESTRICTED = 1 << 22; static constexpr int32 MESSAGE_FLAG_HAS_REPLY_INFO = 1 << 23; @@ -335,8 +337,9 @@ class MessagesManager final : public Actor { void on_update_message_forward_count(FullMessageId full_message_id, int32 forward_count); void on_update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count, - bool has_reply_info, - tl_object_ptr &&reply_info); + bool has_reply_info, tl_object_ptr &&reply_info, + bool has_reactions, + tl_object_ptr &&reactions); void on_update_live_location_viewed(FullMessageId full_message_id); @@ -996,6 +999,7 @@ class MessagesManager final : public Actor { int32 view_count = 0; int32 forward_count = 0; tl_object_ptr reply_info; + tl_object_ptr reactions; int32 flags = 0; int32 edit_date = 0; vector restriction_reasons; @@ -1128,6 +1132,7 @@ class MessagesManager final : public Actor { int32 view_count = 0; int32 forward_count = 0; MessageReplyInfo reply_info; + unique_ptr reactions; unique_ptr thread_draft_message; int32 interaction_info_update_date = 0; @@ -2083,12 +2088,15 @@ class MessagesManager final : public Actor { void on_pending_message_views_timeout(DialogId dialog_id); void update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count, - bool has_reply_info, tl_object_ptr &&reply_info); + bool has_reply_info, tl_object_ptr &&reply_info, + bool has_reactions, tl_object_ptr &&reactions); bool is_active_message_reply_info(DialogId dialog_id, const MessageReplyInfo &info) const; bool is_visible_message_reply_info(DialogId dialog_id, const Message *m) const; + bool is_visible_message_reactions(DialogId dialog_id, const Message *m) const; + void on_message_reply_info_changed(DialogId dialog_id, const Message *m) const; Result get_top_thread_full_message_id(DialogId dialog_id, const Message *m) const; @@ -2097,7 +2105,8 @@ class MessagesManager final : public Actor { const Message *m) const; bool update_message_interaction_info(DialogId dialog_id, Message *m, int32 view_count, int32 forward_count, - bool has_reply_info, MessageReplyInfo &&reply_info, const char *source); + bool has_reply_info, MessageReplyInfo &&reply_info, bool has_reactions, + unique_ptr &&reactions, const char *source); bool update_message_contains_unread_mention(Dialog *d, Message *m, bool contains_unread_mention, const char *source);