Add class ReactionType.

This commit is contained in:
levlam 2023-08-04 18:39:07 +03:00
parent e695289557
commit 5558825d61
17 changed files with 586 additions and 431 deletions

View File

@ -444,6 +444,7 @@ set(TDLIB_SOURCE
td/telegram/PremiumGiftOption.cpp
td/telegram/QueryCombiner.cpp
td/telegram/QueryMerger.cpp
td/telegram/ReactionType.cpp
td/telegram/RecentDialogList.cpp
td/telegram/ReplyMarkup.cpp
td/telegram/ReportReason.cpp
@ -734,6 +735,7 @@ set(TDLIB_SOURCE
td/telegram/PublicDialogType.h
td/telegram/QueryCombiner.h
td/telegram/QueryMerger.h
td/telegram/ReactionType.h
td/telegram/RecentDialogList.h
td/telegram/ReplyMarkup.h
td/telegram/ReportReason.h
@ -838,6 +840,7 @@ set(TDLIB_SOURCE
td/telegram/PollId.hpp
td/telegram/PollManager.hpp
td/telegram/PremiumGiftOption.hpp
td/telegram/ReactionType.hpp
td/telegram/ReplyMarkup.hpp
td/telegram/RequestedDialogType.hpp
td/telegram/ScopeNotificationSettings.hpp

View File

@ -28,10 +28,9 @@ ChatReactions::ChatReactions(telegram_api::object_ptr<telegram_api::ChatReaction
}
case telegram_api::chatReactionsSome::ID: {
auto chat_reactions = move_tl_object_as<telegram_api::chatReactionsSome>(chat_reactions_ptr);
reactions_ =
transform(chat_reactions->reactions_, [](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
return get_message_reaction_string(reaction);
});
reaction_types_ = transform(
chat_reactions->reactions_,
[](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) { return ReactionType(reaction); });
break;
}
default:
@ -51,9 +50,9 @@ ChatReactions::ChatReactions(td_api::object_ptr<td_api::ChatAvailableReactions>
break;
case td_api::chatAvailableReactionsSome::ID: {
auto chat_reactions = move_tl_object_as<td_api::chatAvailableReactionsSome>(chat_reactions_ptr);
reactions_ = transform(chat_reactions->reactions_, [](const td_api::object_ptr<td_api::ReactionType> &reaction) {
return get_message_reaction_string(reaction);
});
reaction_types_ =
transform(chat_reactions->reactions_,
[](const td_api::object_ptr<td_api::ReactionType> &reaction) { return ReactionType(reaction); });
break;
}
default:
@ -61,30 +60,33 @@ ChatReactions::ChatReactions(td_api::object_ptr<td_api::ChatAvailableReactions>
}
}
ChatReactions ChatReactions::get_active_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) const {
ChatReactions ChatReactions::get_active_reactions(
const FlatHashMap<ReactionType, size_t, ReactionTypeHash> &active_reaction_pos) const {
ChatReactions result = *this;
if (!reactions_.empty()) {
if (!reaction_types_.empty()) {
CHECK(!allow_all_);
CHECK(!allow_custom_);
td::remove_if(result.reactions_,
[&](const string &reaction) { return !is_active_reaction(reaction, active_reaction_pos); });
td::remove_if(result.reaction_types_, [&](const ReactionType &reaction_type) {
return !reaction_type.is_active_reaction(active_reaction_pos);
});
}
return result;
}
bool ChatReactions::is_allowed_reaction(const string &reaction) const {
bool ChatReactions::is_allowed_reaction_type(const ReactionType &reaction_type) const {
CHECK(!allow_all_);
if (allow_custom_ && is_custom_reaction(reaction)) {
if (allow_custom_ && reaction_type.is_custom_reaction()) {
return true;
}
return td::contains(reactions_, reaction);
return td::contains(reaction_types_, reaction_type);
}
td_api::object_ptr<td_api::ChatAvailableReactions> ChatReactions::get_chat_available_reactions_object() const {
if (allow_all_) {
return td_api::make_object<td_api::chatAvailableReactionsAll>();
}
return td_api::make_object<td_api::chatAvailableReactionsSome>(transform(reactions_, get_reaction_type_object));
return td_api::make_object<td_api::chatAvailableReactionsSome>(transform(
reaction_types_, [](const ReactionType &reaction_type) { return reaction_type.get_reaction_type_object(); }));
}
telegram_api::object_ptr<telegram_api::ChatReactions> ChatReactions::get_input_chat_reactions() const {
@ -95,15 +97,16 @@ telegram_api::object_ptr<telegram_api::ChatReactions> ChatReactions::get_input_c
}
return telegram_api::make_object<telegram_api::chatReactionsAll>(flags, allow_custom_);
}
if (!reactions_.empty()) {
return telegram_api::make_object<telegram_api::chatReactionsSome>(transform(reactions_, get_input_reaction));
if (!reaction_types_.empty()) {
return telegram_api::make_object<telegram_api::chatReactionsSome>(transform(
reaction_types_, [](const ReactionType &reaction_type) { return reaction_type.get_input_reaction(); }));
}
return telegram_api::make_object<telegram_api::chatReactionsNone>();
}
bool operator==(const ChatReactions &lhs, const ChatReactions &rhs) {
// don't compare allow_custom_
return lhs.reactions_ == rhs.reactions_ && lhs.allow_all_ == rhs.allow_all_;
return lhs.reaction_types_ == rhs.reaction_types_ && lhs.allow_all_ == rhs.allow_all_;
}
StringBuilder &operator<<(StringBuilder &string_builder, const ChatReactions &reactions) {
@ -113,7 +116,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const ChatReactions &re
}
return string_builder << "AllRegularReactions";
}
return string_builder << '[' << reactions.reactions_ << ']';
return string_builder << '[' << reactions.reaction_types_ << ']';
}
} // namespace td

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/ReactionType.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -17,13 +18,13 @@
namespace td {
struct ChatReactions {
vector<string> reactions_;
vector<ReactionType> reaction_types_;
bool allow_all_ = false; // implies empty reactions
bool allow_custom_ = false; // implies allow_all
ChatReactions() = default;
explicit ChatReactions(vector<string> &&reactions) : reactions_(std::move(reactions)) {
explicit ChatReactions(vector<ReactionType> &&reactions) : reaction_types_(std::move(reactions)) {
}
explicit ChatReactions(telegram_api::object_ptr<telegram_api::ChatReactions> &&chat_reactions_ptr);
@ -33,28 +34,29 @@ struct ChatReactions {
ChatReactions(bool allow_all, bool allow_custom) : allow_all_(allow_all), allow_custom_(allow_custom) {
}
ChatReactions get_active_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) const;
ChatReactions get_active_reactions(
const FlatHashMap<ReactionType, size_t, ReactionTypeHash> &active_reaction_pos) const;
bool is_allowed_reaction(const string &reaction) const;
bool is_allowed_reaction_type(const ReactionType &reaction) const;
telegram_api::object_ptr<telegram_api::ChatReactions> get_input_chat_reactions() const;
td_api::object_ptr<td_api::ChatAvailableReactions> get_chat_available_reactions_object() const;
bool empty() const {
return reactions_.empty() && !allow_all_;
return reaction_types_.empty() && !allow_all_;
}
template <class StorerT>
void store(StorerT &storer) const {
bool has_reactions = !reactions_.empty();
bool has_reactions = !reaction_types_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(allow_all_);
STORE_FLAG(allow_custom_);
STORE_FLAG(has_reactions);
END_STORE_FLAGS();
if (has_reactions) {
td::store(reactions_, storer);
td::store(reaction_types_, storer);
}
}
@ -67,7 +69,7 @@ struct ChatReactions {
PARSE_FLAG(has_reactions);
END_PARSE_FLAGS();
if (has_reactions) {
td::parse(reactions_, parser);
td::parse(reaction_types_, parser);
}
}
};

View File

@ -25,6 +25,7 @@
#include "td/telegram/net/PublicRsaKeyShared.h"
#include "td/telegram/net/Session.h"
#include "td/telegram/Premium.h"
#include "td/telegram/ReactionType.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
@ -1402,9 +1403,9 @@ void ConfigManager::process_config(tl_object_ptr<telegram_api::config> config) {
options.set_option_integer("notification_default_delay_ms", fix_timeout_ms(config->notify_default_delay_ms_));
if (is_from_main_dc && !options.have_option("default_reaction_need_sync")) {
auto reaction_str = get_message_reaction_string(config->reactions_default_);
if (!reaction_str.empty()) {
options.set_option_string("default_reaction", reaction_str);
ReactionType reaction_type(config->reactions_default_);
if (!reaction_type.is_empty()) {
options.set_option_string("default_reaction", reaction_type.get_string());
}
}

View File

@ -13,7 +13,6 @@
#include "td/telegram/Global.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/StickersManager.h"
@ -25,17 +24,11 @@
#include "td/actor/SleepActor.h"
#include "td/utils/algorithm.h"
#include "td/utils/as.h"
#include "td/utils/base64.h"
#include "td/utils/buffer.h"
#include "td/utils/crypto.h"
#include "td/utils/emoji.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include "td/utils/utf8.h"
#include <algorithm>
#include <utility>
@ -49,81 +42,6 @@ static size_t get_max_reaction_count() {
max(static_cast<int32>(1), static_cast<int32>(G()->get_option_integer(option_key, is_premium ? 3 : 1))));
}
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<int64>(r_decoded.ok().c_str());
}
static string get_custom_emoji_string(int64 custom_emoji_id) {
char s[8];
as<int64>(&s) = custom_emoji_id;
return PSTRING() << '#' << base64_encode(Slice(s, 8));
}
telegram_api::object_ptr<telegram_api::Reaction> get_input_reaction(const string &reaction) {
if (reaction.empty()) {
return telegram_api::make_object<telegram_api::reactionEmpty>();
}
if (is_custom_reaction(reaction)) {
return telegram_api::make_object<telegram_api::reactionCustomEmoji>(get_custom_emoji_id(reaction));
}
return telegram_api::make_object<telegram_api::reactionEmoji>(reaction);
}
string get_message_reaction_string(const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
if (reaction == nullptr) {
return string();
}
switch (reaction->get_id()) {
case telegram_api::reactionEmpty::ID:
return string();
case telegram_api::reactionEmoji::ID: {
const string &emoji = static_cast<const telegram_api::reactionEmoji *>(reaction.get())->emoticon_;
if (is_custom_reaction(emoji)) {
return string();
}
return emoji;
}
case telegram_api::reactionCustomEmoji::ID:
return get_custom_emoji_string(
static_cast<const telegram_api::reactionCustomEmoji *>(reaction.get())->document_id_);
default:
UNREACHABLE();
return string();
}
}
td_api::object_ptr<td_api::ReactionType> get_reaction_type_object(const string &reaction) {
CHECK(!reaction.empty());
if (is_custom_reaction(reaction)) {
return td_api::make_object<td_api::reactionTypeCustomEmoji>(get_custom_emoji_id(reaction));
}
return td_api::make_object<td_api::reactionTypeEmoji>(reaction);
}
string get_message_reaction_string(const td_api::object_ptr<td_api::ReactionType> &type) {
if (type == nullptr) {
return string();
}
switch (type->get_id()) {
case td_api::reactionTypeEmoji::ID: {
const string &emoji = static_cast<const td_api::reactionTypeEmoji *>(type.get())->emoji_;
if (!check_utf8(emoji) || is_custom_reaction(emoji)) {
return string();
}
return emoji;
}
case td_api::reactionTypeCustomEmoji::ID:
return get_custom_emoji_string(
static_cast<const td_api::reactionTypeCustomEmoji *>(type.get())->custom_emoji_id_);
default:
UNREACHABLE();
return string();
}
}
class GetMessagesReactionsQuery final : public Td::ResultHandler {
DialogId dialog_id_;
vector<MessageId> message_ids_;
@ -186,7 +104,7 @@ class SendReactionQuery final : public Td::ResultHandler {
explicit SendReactionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, vector<string> reactions, bool is_big, bool add_to_recent) {
void send(FullMessageId full_message_id, vector<ReactionType> reaction_types, bool is_big, bool add_to_recent) {
dialog_id_ = full_message_id.get_dialog_id();
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
@ -195,7 +113,7 @@ class SendReactionQuery final : public Td::ResultHandler {
}
int32 flags = 0;
if (!reactions.empty()) {
if (!reaction_types.empty()) {
flags |= telegram_api::messages_sendReaction::REACTION_MASK;
if (is_big) {
@ -208,9 +126,11 @@ class SendReactionQuery final : public Td::ResultHandler {
}
send_query(G()->net_query_creator().create(
telegram_api::messages_sendReaction(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
full_message_id.get_message_id().get_server_message_id().get(),
transform(reactions, get_input_reaction)),
telegram_api::messages_sendReaction(
flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
full_message_id.get_message_id().get_server_message_id().get(),
transform(reaction_types,
[](const ReactionType &reaction_type) { return reaction_type.get_input_reaction(); })),
{{dialog_id_}, {full_message_id}}));
}
@ -238,7 +158,7 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::addedReactions>> promise_;
DialogId dialog_id_;
MessageId message_id_;
string reaction_;
ReactionType reaction_type_;
string offset_;
public:
@ -246,10 +166,10 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
: promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, string reaction, string offset, int32 limit) {
void send(FullMessageId full_message_id, ReactionType reaction_type, string offset, int32 limit) {
dialog_id_ = full_message_id.get_dialog_id();
message_id_ = full_message_id.get_message_id();
reaction_ = std::move(reaction);
reaction_type_ = std::move(reaction_type);
offset_ = std::move(offset);
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
@ -258,7 +178,7 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
}
int32 flags = 0;
if (!reaction_.empty()) {
if (!reaction_type_.is_empty()) {
flags |= telegram_api::messages_getMessageReactionsList::REACTION_MASK;
}
if (!offset_.empty()) {
@ -268,7 +188,7 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
send_query(G()->net_query_creator().create(
telegram_api::messages_getMessageReactionsList(flags, std::move(input_peer),
message_id_.get_server_message_id().get(),
get_input_reaction(reaction_), offset_, limit),
reaction_type_.get_input_reaction(), offset_, limit),
{{full_message_id}}));
}
@ -292,29 +212,30 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
}
vector<td_api::object_ptr<td_api::addedReaction>> reactions;
FlatHashMap<string, vector<DialogId>> recent_reactions;
FlatHashMap<ReactionType, vector<DialogId>, ReactionTypeHash> recent_reaction_types;
for (const auto &reaction : ptr->reactions_) {
DialogId dialog_id(reaction->peer_id_);
auto reaction_str = get_message_reaction_string(reaction->reaction_);
if (!dialog_id.is_valid() || (reaction_.empty() ? reaction_str.empty() : reaction_ != reaction_str)) {
auto reaction_type = ReactionType(reaction->reaction_);
if (!dialog_id.is_valid() ||
(reaction_type_.is_empty() ? reaction_type.is_empty() : reaction_type_ != reaction_type)) {
LOG(ERROR) << "Receive unexpected " << to_string(reaction);
continue;
}
if (offset_.empty()) {
recent_reactions[reaction_str].push_back(dialog_id);
recent_reaction_types[reaction_type].push_back(dialog_id);
}
auto message_sender = get_min_message_sender_object(td_, dialog_id, "GetMessageReactionsListQuery");
if (message_sender != nullptr) {
reactions.push_back(td_api::make_object<td_api::addedReaction>(get_reaction_type_object(reaction_str),
reactions.push_back(td_api::make_object<td_api::addedReaction>(reaction_type.get_reaction_type_object(),
std::move(message_sender), reaction->date_));
}
}
if (offset_.empty()) {
td_->messages_manager_->on_get_message_reaction_list({dialog_id_, message_id_}, reaction_,
std::move(recent_reactions), total_count);
td_->messages_manager_->on_get_message_reaction_list({dialog_id_, message_id_}, reaction_type_,
std::move(recent_reaction_types), total_count);
}
promise_.set_value(
@ -328,13 +249,13 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
};
class SetDefaultReactionQuery final : public Td::ResultHandler {
string reaction_;
ReactionType reaction_type_;
public:
void send(const string &reaction) {
reaction_ = reaction;
void send(const ReactionType &reaction_type) {
reaction_type_ = reaction_type;
send_query(
G()->net_query_creator().create(telegram_api::messages_setDefaultReaction(get_input_reaction(reaction))));
G()->net_query_creator().create(telegram_api::messages_setDefaultReaction(reaction_type.get_input_reaction())));
}
void on_result(BufferSlice packet) final {
@ -348,7 +269,7 @@ class SetDefaultReactionQuery final : public Td::ResultHandler {
}
auto default_reaction = td_->option_manager_->get_option_string("default_reaction", "-");
if (default_reaction != reaction_) {
if (default_reaction != reaction_type_.get_string()) {
send_set_default_reaction_query(td_);
} else {
td_->option_manager_->set_option_empty("default_reaction_needs_sync");
@ -404,10 +325,10 @@ class ReportReactionQuery final : public Td::ResultHandler {
}
};
MessageReaction::MessageReaction(string reaction, int32 choose_count, bool is_chosen,
MessageReaction::MessageReaction(ReactionType reaction_type, int32 choose_count, bool is_chosen,
DialogId my_recent_chooser_dialog_id, vector<DialogId> &&recent_chooser_dialog_ids,
vector<std::pair<ChannelId, MinChannel>> &&recent_chooser_min_channels)
: reaction_(std::move(reaction))
: reaction_type_(std::move(reaction_type))
, choose_count_(choose_count)
, is_chosen_(is_chosen)
, my_recent_chooser_dialog_id_(my_recent_chooser_dialog_id)
@ -453,7 +374,7 @@ void MessageReaction::update_recent_chooser_dialog_ids(const MessageReaction &ol
return;
}
CHECK(is_chosen_ && old_reaction.is_chosen_);
CHECK(reaction_ == old_reaction.reaction_);
CHECK(reaction_type_ == old_reaction.reaction_type_);
CHECK(old_reaction.recent_chooser_dialog_ids_.size() == MAX_RECENT_CHOOSERS + 1);
for (size_t i = 0; i < MAX_RECENT_CHOOSERS; i++) {
if (recent_chooser_dialog_ids_[i] != old_reaction.recent_chooser_dialog_ids_[i]) {
@ -528,18 +449,18 @@ td_api::object_ptr<td_api::messageReaction> MessageReaction::get_message_reactio
}
}
}
return td_api::make_object<td_api::messageReaction>(get_reaction_type_object(reaction_), choose_count_, is_chosen_,
std::move(recent_choosers));
return td_api::make_object<td_api::messageReaction>(reaction_type_.get_reaction_type_object(), 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.my_recent_chooser_dialog_id_ == rhs.my_recent_chooser_dialog_id_ &&
return lhs.reaction_type_ == rhs.reaction_type_ && lhs.choose_count_ == rhs.choose_count_ &&
lhs.is_chosen_ == rhs.is_chosen_ && lhs.my_recent_chooser_dialog_id_ == rhs.my_recent_chooser_dialog_id_ &&
lhs.recent_chooser_dialog_ids_ == rhs.recent_chooser_dialog_ids_;
}
StringBuilder &operator<<(StringBuilder &string_builder, const MessageReaction &reaction) {
string_builder << '[' << reaction.reaction_ << (reaction.is_chosen_ ? " X " : " x ") << reaction.choose_count_;
string_builder << '[' << reaction.reaction_type_ << (reaction.is_chosen_ ? " X " : " x ") << reaction.choose_count_;
if (!reaction.recent_chooser_dialog_ids_.empty()) {
string_builder << " by " << reaction.recent_chooser_dialog_ids_;
if (reaction.my_recent_chooser_dialog_id_.is_valid()) {
@ -554,16 +475,17 @@ td_api::object_ptr<td_api::unreadReaction> UnreadMessageReaction::get_unread_rea
if (sender_id == nullptr) {
return nullptr;
}
return td_api::make_object<td_api::unreadReaction>(get_reaction_type_object(reaction_), std::move(sender_id),
return td_api::make_object<td_api::unreadReaction>(reaction_type_.get_reaction_type_object(), std::move(sender_id),
is_big_);
}
bool operator==(const UnreadMessageReaction &lhs, const UnreadMessageReaction &rhs) {
return lhs.reaction_ == rhs.reaction_ && lhs.sender_dialog_id_ == rhs.sender_dialog_id_ && lhs.is_big_ == rhs.is_big_;
return lhs.reaction_type_ == rhs.reaction_type_ && lhs.sender_dialog_id_ == rhs.sender_dialog_id_ &&
lhs.is_big_ == rhs.is_big_;
}
StringBuilder &operator<<(StringBuilder &string_builder, const UnreadMessageReaction &unread_reaction) {
return string_builder << '[' << unread_reaction.reaction_ << (unread_reaction.is_big_ ? " BY " : " by ")
return string_builder << '[' << unread_reaction.reaction_type_ << (unread_reaction.is_big_ ? " BY " : " by ")
<< unread_reaction.sender_dialog_id_ << ']';
}
@ -591,18 +513,18 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
}
}
FlatHashSet<string> reaction_strings;
vector<std::pair<int32, string>> chosen_reaction_order;
FlatHashSet<ReactionType, ReactionTypeHash> reaction_types;
vector<std::pair<int32, ReactionType>> chosen_reaction_order;
for (auto &reaction_count : reactions->results_) {
auto reaction_str = get_message_reaction_string(reaction_count->reaction_);
auto reaction_type = ReactionType(reaction_count->reaction_);
if (reaction_count->count_ <= 0 || reaction_count->count_ >= MessageReaction::MAX_CHOOSE_COUNT ||
reaction_str.empty()) {
LOG(ERROR) << "Receive reaction " << reaction_str << " with invalid count " << reaction_count->count_;
reaction_type.is_empty()) {
LOG(ERROR) << "Receive reaction " << reaction_type << " with invalid count " << reaction_count->count_;
continue;
}
if (!reaction_strings.insert(reaction_str).second) {
LOG(ERROR) << "Receive duplicate reaction " << reaction_str;
if (!reaction_types.insert(reaction_type).second) {
LOG(ERROR) << "Receive duplicate reaction " << reaction_type;
continue;
}
@ -611,15 +533,15 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
vector<DialogId> recent_chooser_dialog_ids;
vector<std::pair<ChannelId, MinChannel>> recent_chooser_min_channels;
for (auto &peer_reaction : reactions->recent_reactions_) {
auto peer_reaction_str = get_message_reaction_string(peer_reaction->reaction_);
if (peer_reaction_str == reaction_str) {
auto peer_reaction_type = ReactionType(peer_reaction->reaction_);
if (peer_reaction_type == reaction_type) {
DialogId dialog_id(peer_reaction->peer_id_);
if (!dialog_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << dialog_id << " as a recent chooser for reaction " << reaction_str;
LOG(ERROR) << "Receive invalid " << dialog_id << " as a recent chooser for reaction " << reaction_type;
continue;
}
if (!recent_choosers.insert(dialog_id).second) {
LOG(ERROR) << "Receive duplicate " << dialog_id << " as a recent chooser for reaction " << reaction_str;
LOG(ERROR) << "Receive duplicate " << dialog_id << " as a recent chooser for reaction " << reaction_type;
continue;
}
if (!td->messages_manager_->have_dialog_info(dialog_id)) {
@ -649,7 +571,7 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
my_recent_chooser_dialog_id = dialog_id;
}
if (peer_reaction->unread_) {
result->unread_reactions_.emplace_back(std::move(peer_reaction_str), dialog_id, peer_reaction->big_);
result->unread_reactions_.emplace_back(std::move(peer_reaction_type), dialog_id, peer_reaction->big_);
}
if (recent_chooser_dialog_ids.size() == MessageReaction::MAX_RECENT_CHOOSERS) {
break;
@ -659,32 +581,32 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
bool is_chosen = (reaction_count->flags_ & telegram_api::reactionCount::CHOSEN_ORDER_MASK) != 0;
if (is_chosen) {
chosen_reaction_order.emplace_back(reaction_count->chosen_order_, reaction_str);
chosen_reaction_order.emplace_back(reaction_count->chosen_order_, reaction_type);
}
result->reactions_.push_back({std::move(reaction_str), reaction_count->count_, is_chosen,
result->reactions_.push_back({std::move(reaction_type), reaction_count->count_, is_chosen,
my_recent_chooser_dialog_id, std::move(recent_chooser_dialog_ids),
std::move(recent_chooser_min_channels)});
}
if (chosen_reaction_order.size() > 1) {
std::sort(chosen_reaction_order.begin(), chosen_reaction_order.end());
result->chosen_reaction_order_ =
transform(chosen_reaction_order, [](const std::pair<int32, string> &order) { return order.second; });
transform(chosen_reaction_order, [](const std::pair<int32, ReactionType> &order) { return order.second; });
}
return result;
}
MessageReaction *MessageReactions::get_reaction(const string &reaction) {
MessageReaction *MessageReactions::get_reaction(const ReactionType &reaction_type) {
for (auto &added_reaction : reactions_) {
if (added_reaction.get_reaction() == reaction) {
if (added_reaction.get_reaction_type() == reaction_type) {
return &added_reaction;
}
}
return nullptr;
}
const MessageReaction *MessageReactions::get_reaction(const string &reaction) const {
const MessageReaction *MessageReactions::get_reaction(const ReactionType &reaction_type) const {
for (auto &added_reaction : reactions_) {
if (added_reaction.get_reaction() == reaction) {
if (added_reaction.get_reaction_type() == reaction_type) {
return &added_reaction;
}
}
@ -698,12 +620,12 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
chosen_reaction_order_ = old_reactions.chosen_reaction_order_;
for (const auto &old_reaction : old_reactions.reactions_) {
if (old_reaction.is_chosen()) {
auto *reaction = get_reaction(old_reaction.get_reaction());
auto *reaction = get_reaction(old_reaction.get_reaction_type());
if (reaction != nullptr) {
reaction->update_from(old_reaction);
}
} else {
td::remove(chosen_reaction_order_, old_reaction.get_reaction());
td::remove(chosen_reaction_order_, old_reaction.get_reaction_type());
}
}
unread_reactions_ = old_reactions.unread_reactions_;
@ -714,7 +636,7 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
for (const auto &old_reaction : old_reactions.reactions_) {
if (old_reaction.is_chosen() &&
old_reaction.get_recent_chooser_dialog_ids().size() == MessageReaction::MAX_RECENT_CHOOSERS + 1) {
auto *reaction = get_reaction(old_reaction.get_reaction());
auto *reaction = get_reaction(old_reaction.get_reaction_type());
if (reaction != nullptr && reaction->is_chosen()) {
reaction->update_recent_chooser_dialog_ids(old_reaction);
}
@ -722,11 +644,11 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
}
}
bool MessageReactions::add_reaction(const string &reaction, bool is_big, DialogId my_dialog_id,
bool MessageReactions::add_reaction(const ReactionType &reaction_type, bool is_big, DialogId my_dialog_id,
bool have_recent_choosers) {
vector<string> new_chosen_reaction_order = get_chosen_reactions();
vector<ReactionType> new_chosen_reaction_order = get_chosen_reaction_types();
auto added_reaction = get_reaction(reaction);
auto added_reaction = get_reaction(reaction_type);
if (added_reaction == nullptr) {
vector<DialogId> recent_chooser_dialog_ids;
DialogId my_recent_chooser_dialog_id;
@ -735,18 +657,18 @@ bool MessageReactions::add_reaction(const string &reaction, bool is_big, DialogI
my_recent_chooser_dialog_id = my_dialog_id;
}
reactions_.push_back(
{reaction, 1, true, my_recent_chooser_dialog_id, std::move(recent_chooser_dialog_ids), Auto()});
new_chosen_reaction_order.emplace_back(reaction);
{reaction_type, 1, true, my_recent_chooser_dialog_id, std::move(recent_chooser_dialog_ids), Auto()});
new_chosen_reaction_order.emplace_back(reaction_type);
} else if (!added_reaction->is_chosen()) {
added_reaction->set_as_chosen(my_dialog_id, have_recent_choosers);
new_chosen_reaction_order.emplace_back(reaction);
new_chosen_reaction_order.emplace_back(reaction_type);
} else if (!is_big) {
return false;
}
auto max_reaction_count = get_max_reaction_count();
while (new_chosen_reaction_order.size() > max_reaction_count) {
auto index = new_chosen_reaction_order[0] == reaction ? 1 : 0;
auto index = new_chosen_reaction_order[0] == reaction_type ? 1 : 0;
CHECK(static_cast<size_t>(index) < new_chosen_reaction_order.size());
bool is_removed = do_remove_reaction(new_chosen_reaction_order[index]);
CHECK(is_removed);
@ -765,10 +687,10 @@ bool MessageReactions::add_reaction(const string &reaction, bool is_big, DialogI
return true;
}
bool MessageReactions::remove_reaction(const string &reaction, DialogId my_dialog_id) {
if (do_remove_reaction(reaction)) {
bool MessageReactions::remove_reaction(const ReactionType &reaction_type, DialogId my_dialog_id) {
if (do_remove_reaction(reaction_type)) {
if (!chosen_reaction_order_.empty()) {
bool is_removed = td::remove(chosen_reaction_order_, reaction);
bool is_removed = td::remove(chosen_reaction_order_, reaction_type);
CHECK(is_removed);
// if the user isn't a Premium user, then max_reaction_count could be reduced from 3 to 1
@ -793,10 +715,10 @@ bool MessageReactions::remove_reaction(const string &reaction, DialogId my_dialo
return false;
}
bool MessageReactions::do_remove_reaction(const string &reaction) {
bool MessageReactions::do_remove_reaction(const ReactionType &reaction_type) {
for (auto it = reactions_.begin(); it != reactions_.end(); ++it) {
auto &message_reaction = *it;
if (message_reaction.get_reaction() == reaction) {
if (message_reaction.get_reaction_type() == reaction_type) {
if (message_reaction.is_chosen()) {
message_reaction.unset_as_chosen();
if (message_reaction.is_empty()) {
@ -810,21 +732,21 @@ bool MessageReactions::do_remove_reaction(const string &reaction) {
return false;
}
void MessageReactions::sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) {
void MessageReactions::sort_reactions(const FlatHashMap<ReactionType, size_t, ReactionTypeHash> &active_reaction_pos) {
std::sort(reactions_.begin(), reactions_.end(),
[&active_reaction_pos](const MessageReaction &lhs, const MessageReaction &rhs) {
if (lhs.get_choose_count() != rhs.get_choose_count()) {
return lhs.get_choose_count() > rhs.get_choose_count();
}
auto lhs_it = active_reaction_pos.find(lhs.get_reaction());
auto lhs_it = active_reaction_pos.find(lhs.get_reaction_type());
auto lhs_pos = lhs_it != active_reaction_pos.end() ? lhs_it->second : active_reaction_pos.size();
auto rhs_it = active_reaction_pos.find(rhs.get_reaction());
auto rhs_it = active_reaction_pos.find(rhs.get_reaction_type());
auto rhs_pos = rhs_it != active_reaction_pos.end() ? rhs_it->second : active_reaction_pos.size();
if (lhs_pos != rhs_pos) {
return lhs_pos < rhs_pos;
}
return lhs.get_reaction() < rhs.get_reaction();
return lhs.get_reaction_type() < rhs.get_reaction_type();
});
}
@ -856,22 +778,23 @@ void MessageReactions::fix_my_recent_chooser_dialog_id(DialogId my_dialog_id) {
}
}
vector<string> MessageReactions::get_chosen_reactions() const {
vector<ReactionType> MessageReactions::get_chosen_reaction_types() const {
if (!chosen_reaction_order_.empty()) {
return chosen_reaction_order_;
}
vector<string> reaction_order;
vector<ReactionType> reaction_order;
for (auto &reaction : reactions_) {
if (reaction.is_chosen()) {
reaction_order.push_back(reaction.get_reaction());
reaction_order.push_back(reaction.get_reaction_type());
}
}
return reaction_order;
}
bool MessageReactions::are_consistent_with_list(const string &reaction, FlatHashMap<string, vector<DialogId>> reactions,
int32 total_count) const {
bool MessageReactions::are_consistent_with_list(
const ReactionType &reaction_type, FlatHashMap<ReactionType, vector<DialogId>, ReactionTypeHash> reaction_types,
int32 total_count) const {
auto are_consistent = [](const vector<DialogId> &lhs, const vector<DialogId> &rhs) {
size_t i = 0;
size_t max_i = td::min(lhs.size(), rhs.size());
@ -881,27 +804,27 @@ bool MessageReactions::are_consistent_with_list(const string &reaction, FlatHash
return i == max_i;
};
if (reaction.empty()) {
if (reaction_type.is_empty()) {
// received list and total_count for all reactions
int32 old_total_count = 0;
for (const auto &message_reaction : reactions_) {
CHECK(!message_reaction.get_reaction().empty());
if (!are_consistent(reactions[message_reaction.get_reaction()],
CHECK(!message_reaction.get_reaction_type().is_empty());
if (!are_consistent(reaction_types[message_reaction.get_reaction_type()],
message_reaction.get_recent_chooser_dialog_ids())) {
return false;
}
old_total_count += message_reaction.get_choose_count();
reactions.erase(message_reaction.get_reaction());
reaction_types.erase(message_reaction.get_reaction_type());
}
return old_total_count == total_count && reactions.empty();
return old_total_count == total_count && reaction_types.empty();
}
// received list and total_count for a single reaction
const auto *message_reaction = get_reaction(reaction);
const auto *message_reaction = get_reaction(reaction_type);
if (message_reaction == nullptr) {
return reactions.count(reaction) == 0 && total_count == 0;
return reaction_types.count(reaction_type) == 0 && total_count == 0;
} else {
return are_consistent(reactions[reaction], message_reaction->get_recent_chooser_dialog_ids()) &&
return are_consistent(reaction_types[reaction_type], message_reaction->get_recent_chooser_dialog_ids()) &&
message_reaction->get_choose_count() == total_count;
}
}
@ -970,14 +893,6 @@ StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr<Messag
return string_builder << *reactions;
}
bool is_custom_reaction(const string &reaction) {
return reaction[0] == '#';
}
bool is_active_reaction(const string &reaction, const FlatHashMap<string, size_t> &active_reaction_pos) {
return !reaction.empty() && (is_custom_reaction(reaction) || active_reaction_pos.count(reaction) > 0);
}
void reload_message_reactions(Td *td, DialogId dialog_id, vector<MessageId> &&message_ids) {
if (!td->messages_manager_->have_input_peer(dialog_id, AccessRights::Read) ||
dialog_id.get_type() == DialogType::SecretChat || message_ids.empty()) {
@ -998,14 +913,14 @@ void reload_message_reactions(Td *td, DialogId dialog_id, vector<MessageId> &&me
td->create_handler<GetMessagesReactionsQuery>()->send(dialog_id, std::move(message_ids));
}
void send_message_reaction(Td *td, FullMessageId full_message_id, vector<string> reactions, bool is_big,
void send_message_reaction(Td *td, FullMessageId full_message_id, vector<ReactionType> reaction_types, bool is_big,
bool add_to_recent, Promise<Unit> &&promise) {
td->create_handler<SendReactionQuery>(std::move(promise))
->send(full_message_id, std::move(reactions), is_big, add_to_recent);
->send(full_message_id, std::move(reaction_types), is_big, add_to_recent);
}
void get_message_added_reactions(Td *td, FullMessageId full_message_id, string reaction, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::addedReactions>> &&promise) {
void get_message_added_reactions(Td *td, FullMessageId full_message_id, ReactionType reaction_type, string offset,
int32 limit, Promise<td_api::object_ptr<td_api::addedReactions>> &&promise) {
if (!td->messages_manager_->have_message_force(full_message_id, "get_message_added_reactions")) {
return promise.set_error(Status::Error(400, "Message not found"));
}
@ -1025,19 +940,19 @@ void get_message_added_reactions(Td *td, FullMessageId full_message_id, string r
}
td->create_handler<GetMessageReactionsListQuery>(std::move(promise))
->send(full_message_id, std::move(reaction), std::move(offset), limit);
->send(full_message_id, std::move(reaction_type), std::move(offset), limit);
}
void set_default_reaction(Td *td, string reaction, Promise<Unit> &&promise) {
if (reaction.empty()) {
void set_default_reaction(Td *td, ReactionType reaction_type, Promise<Unit> &&promise) {
if (reaction_type.is_empty()) {
return promise.set_error(Status::Error(400, "Default reaction must be non-empty"));
}
if (!is_custom_reaction(reaction) && !td->stickers_manager_->is_active_reaction(reaction)) {
if (!reaction_type.is_custom_reaction() && !td->stickers_manager_->is_active_reaction(reaction_type)) {
return promise.set_error(Status::Error(400, "Can't set incative reaction as default"));
}
if (td->option_manager_->get_option_string("default_reaction", "-") != reaction) {
td->option_manager_->set_option_string("default_reaction", reaction);
if (td->option_manager_->get_option_string("default_reaction", "-") != reaction_type.get_string()) {
td->option_manager_->set_option_string("default_reaction", reaction_type.get_string());
if (!td->option_manager_->get_option_boolean("default_reaction_needs_sync")) {
td->option_manager_->set_option_boolean("default_reaction_needs_sync", true);
send_set_default_reaction_query(td);
@ -1047,14 +962,8 @@ void set_default_reaction(Td *td, string reaction, Promise<Unit> &&promise) {
}
void send_set_default_reaction_query(Td *td) {
td->create_handler<SetDefaultReactionQuery>()->send(td->option_manager_->get_option_string("default_reaction"));
}
td_api::object_ptr<td_api::updateDefaultReactionType> get_update_default_reaction_type(const string &default_reaction) {
if (default_reaction.empty()) {
return nullptr;
}
return td_api::make_object<td_api::updateDefaultReactionType>(get_reaction_type_object(default_reaction));
td->create_handler<SetDefaultReactionQuery>()->send(
ReactionType(td->option_manager_->get_option_string("default_reaction")));
}
void report_message_reactions(Td *td, FullMessageId full_message_id, DialogId chooser_dialog_id,
@ -1088,38 +997,16 @@ void report_message_reactions(Td *td, FullMessageId full_message_id, DialogId ch
td->create_handler<ReportReactionQuery>(std::move(promise))->send(dialog_id, message_id, chooser_dialog_id);
}
vector<string> get_recent_reactions(Td *td) {
vector<ReactionType> get_recent_reactions(Td *td) {
return td->stickers_manager_->get_recent_reactions();
}
vector<string> get_top_reactions(Td *td) {
vector<ReactionType> get_top_reactions(Td *td) {
return td->stickers_manager_->get_top_reactions();
}
void add_recent_reaction(Td *td, const string &reaction) {
td->stickers_manager_->add_recent_reaction(reaction);
}
int64 get_reactions_hash(const vector<string> &reactions) {
vector<uint64> numbers;
for (auto &reaction : reactions) {
if (is_custom_reaction(reaction)) {
auto custom_emoji_id = static_cast<uint64>(get_custom_emoji_id(reaction));
numbers.push_back(custom_emoji_id >> 32);
numbers.push_back(custom_emoji_id & 0xFFFFFFFF);
} else {
auto emoji = remove_emoji_selectors(reaction);
unsigned char hash[16];
md5(emoji, {hash, sizeof(hash)});
auto get = [hash](int num) {
return static_cast<uint32>(hash[num]);
};
numbers.push_back(0);
numbers.push_back(static_cast<int32>((get(0) << 24) + (get(1) << 16) + (get(2) << 8) + get(3)));
}
}
return get_vector_hash(numbers);
void add_recent_reaction(Td *td, const ReactionType &reaction_type) {
td->stickers_manager_->add_recent_reaction(reaction_type);
}
} // namespace td

View File

@ -11,6 +11,7 @@
#include "td/telegram/FullMessageId.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/MinChannel.h"
#include "td/telegram/ReactionType.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
@ -33,7 +34,7 @@ class MessageReaction {
static constexpr size_t MAX_RECENT_CHOOSERS = 3;
string reaction_;
ReactionType reaction_type_;
int32 choose_count_ = 0;
bool is_chosen_ = false;
DialogId my_recent_chooser_dialog_id_;
@ -46,7 +47,7 @@ class MessageReaction {
friend struct MessageReactions;
MessageReaction(string reaction, int32 choose_count, bool is_chosen, DialogId my_recent_chooser_dialog_id,
MessageReaction(ReactionType reaction_type, int32 choose_count, bool is_chosen, DialogId my_recent_chooser_dialog_id,
vector<DialogId> &&recent_chooser_dialog_ids,
vector<std::pair<ChannelId, MinChannel>> &&recent_chooser_min_channels);
@ -94,8 +95,8 @@ class MessageReaction {
public:
MessageReaction() = default;
const string &get_reaction() const {
return reaction_;
const ReactionType &get_reaction_type() const {
return reaction_type_;
}
template <class StorerT>
@ -114,7 +115,7 @@ inline bool operator!=(const MessageReaction &lhs, const MessageReaction &rhs) {
StringBuilder &operator<<(StringBuilder &string_builder, const MessageReaction &reaction);
class UnreadMessageReaction {
string reaction_;
ReactionType reaction_type_;
DialogId sender_dialog_id_;
bool is_big_ = false;
@ -125,8 +126,8 @@ class UnreadMessageReaction {
public:
UnreadMessageReaction() = default;
UnreadMessageReaction(string reaction, DialogId sender_dialog_id, bool is_big)
: reaction_(std::move(reaction)), sender_dialog_id_(sender_dialog_id), is_big_(is_big) {
UnreadMessageReaction(ReactionType reaction_type, DialogId sender_dialog_id, bool is_big)
: reaction_type_(std::move(reaction_type)), sender_dialog_id_(sender_dialog_id), is_big_(is_big) {
}
td_api::object_ptr<td_api::unreadReaction> get_unread_reaction_object(Td *td) const;
@ -149,7 +150,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const UnreadMessageReac
struct MessageReactions {
vector<MessageReaction> reactions_;
vector<UnreadMessageReaction> unread_reactions_;
vector<string> chosen_reaction_order_;
vector<ReactionType> chosen_reaction_order_;
bool is_min_ = false;
bool need_polling_ = true;
bool can_get_added_reactions_ = false;
@ -160,25 +161,26 @@ struct MessageReactions {
tl_object_ptr<telegram_api::messageReactions> &&reactions,
bool is_bot);
MessageReaction *get_reaction(const string &reaction);
MessageReaction *get_reaction(const ReactionType &reaction_type);
const MessageReaction *get_reaction(const string &reaction) const;
const MessageReaction *get_reaction(const ReactionType &reaction_type) const;
void update_from(const MessageReactions &old_reactions);
bool add_reaction(const string &reaction, bool is_big, DialogId my_dialog_id, bool have_recent_choosers);
bool add_reaction(const ReactionType &reaction_type, bool is_big, DialogId my_dialog_id, bool have_recent_choosers);
bool remove_reaction(const string &reaction, DialogId my_dialog_id);
bool remove_reaction(const ReactionType &reaction_type, DialogId my_dialog_id);
void sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos);
void sort_reactions(const FlatHashMap<ReactionType, size_t, ReactionTypeHash> &active_reaction_pos);
void fix_chosen_reaction();
void fix_my_recent_chooser_dialog_id(DialogId my_dialog_id);
vector<string> get_chosen_reactions() const;
vector<ReactionType> get_chosen_reaction_types() const;
bool are_consistent_with_list(const string &reaction, FlatHashMap<string, vector<DialogId>> reactions,
bool are_consistent_with_list(const ReactionType &reaction_type,
FlatHashMap<ReactionType, vector<DialogId>, ReactionTypeHash> reaction_types,
int32 total_count) const;
vector<td_api::object_ptr<td_api::messageReaction>> get_message_reactions_object(Td *td, UserId my_user_id,
@ -201,48 +203,32 @@ struct MessageReactions {
void parse(ParserT &parser);
private:
bool do_remove_reaction(const string &reaction);
bool do_remove_reaction(const ReactionType &reaction_type);
};
StringBuilder &operator<<(StringBuilder &string_builder, const MessageReactions &reactions);
StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr<MessageReactions> &reactions);
telegram_api::object_ptr<telegram_api::Reaction> get_input_reaction(const string &reaction);
td_api::object_ptr<td_api::ReactionType> get_reaction_type_object(const string &reaction);
string get_message_reaction_string(const telegram_api::object_ptr<telegram_api::Reaction> &reaction);
string get_message_reaction_string(const td_api::object_ptr<td_api::ReactionType> &type);
bool is_custom_reaction(const string &reaction);
bool is_active_reaction(const string &reaction, const FlatHashMap<string, size_t> &active_reaction_pos);
void reload_message_reactions(Td *td, DialogId dialog_id, vector<MessageId> &&message_ids);
void send_message_reaction(Td *td, FullMessageId full_message_id, vector<string> reactions, bool is_big,
void send_message_reaction(Td *td, FullMessageId full_message_id, vector<ReactionType> reaction_types, bool is_big,
bool add_to_recent, Promise<Unit> &&promise);
void get_message_added_reactions(Td *td, FullMessageId full_message_id, string reaction, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::addedReactions>> &&promise);
void get_message_added_reactions(Td *td, FullMessageId full_message_id, ReactionType reaction_type, string offset,
int32 limit, Promise<td_api::object_ptr<td_api::addedReactions>> &&promise);
void set_default_reaction(Td *td, string reaction, Promise<Unit> &&promise);
void set_default_reaction(Td *td, ReactionType reaction_type, Promise<Unit> &&promise);
void send_set_default_reaction_query(Td *td);
td_api::object_ptr<td_api::updateDefaultReactionType> get_update_default_reaction_type(const string &default_reaction);
void report_message_reactions(Td *td, FullMessageId full_message_id, DialogId chooser_dialog_id,
Promise<Unit> &&promise);
vector<string> get_recent_reactions(Td *td);
vector<ReactionType> get_recent_reactions(Td *td);
vector<string> get_top_reactions(Td *td);
vector<ReactionType> get_top_reactions(Td *td);
void add_recent_reaction(Td *td, const string &reaction);
int64 get_reactions_hash(const vector<string> &reactions);
void add_recent_reaction(Td *td, const ReactionType &reaction_type);
} // namespace td

View File

@ -8,6 +8,7 @@
#include "td/telegram/MessageReaction.h"
#include "td/telegram/MinChannel.hpp"
#include "td/telegram/ReactionType.hpp"
#include "td/utils/algorithm.h"
#include "td/utils/common.h"
@ -27,7 +28,7 @@ void MessageReaction::store(StorerT &storer) const {
STORE_FLAG(has_recent_chooser_min_channels);
STORE_FLAG(has_my_recent_chooser_dialog_id);
END_STORE_FLAGS();
td::store(reaction_, storer);
td::store(reaction_type_, storer);
td::store(choose_count_, storer);
if (has_recent_chooser_dialog_ids) {
td::store(recent_chooser_dialog_ids_, storer);
@ -51,7 +52,7 @@ void MessageReaction::parse(ParserT &parser) {
PARSE_FLAG(has_recent_chooser_min_channels);
PARSE_FLAG(has_my_recent_chooser_dialog_id);
END_PARSE_FLAGS();
td::parse(reaction_, parser);
td::parse(reaction_type_, parser);
td::parse(choose_count_, parser);
if (has_recent_chooser_dialog_ids) {
td::parse(recent_chooser_dialog_ids_, parser);
@ -65,7 +66,7 @@ void MessageReaction::parse(ParserT &parser) {
CHECK(td::contains(recent_chooser_dialog_ids_, my_recent_chooser_dialog_id_));
}
CHECK(!is_empty());
CHECK(!reaction_.empty());
CHECK(!reaction_type_.is_empty());
}
template <class StorerT>
@ -73,7 +74,7 @@ void UnreadMessageReaction::store(StorerT &storer) const {
BEGIN_STORE_FLAGS();
STORE_FLAG(is_big_);
END_STORE_FLAGS();
td::store(reaction_, storer);
td::store(reaction_type_, storer);
td::store(sender_dialog_id_, storer);
}
@ -82,9 +83,9 @@ void UnreadMessageReaction::parse(ParserT &parser) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_big_);
END_PARSE_FLAGS();
td::parse(reaction_, parser);
td::parse(reaction_type_, parser);
td::parse(sender_dialog_id_, parser);
CHECK(!reaction_.empty());
CHECK(!reaction_type_.is_empty());
}
template <class StorerT>

View File

@ -5766,9 +5766,9 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
if (has_available_reactions) {
parse(available_reactions, parser);
} else if (has_legacy_available_reactions) {
vector<string> legacy_available_reactions;
parse(legacy_available_reactions, parser);
available_reactions = ChatReactions(std::move(legacy_available_reactions));
vector<ReactionType> legacy_available_reaction_types;
parse(legacy_available_reaction_types, parser);
available_reactions = ChatReactions(std::move(legacy_available_reaction_types));
}
if (has_available_reactions_generation) {
parse(available_reactions_generation, parser);
@ -6846,8 +6846,9 @@ void MessagesManager::update_message_reactions(FullMessageId full_message_id,
update_message_interaction_info(full_message_id, -1, -1, false, nullptr, true, std::move(reactions));
}
void MessagesManager::on_get_message_reaction_list(FullMessageId full_message_id, const string &reaction,
FlatHashMap<string, vector<DialogId>> reactions, int32 total_count) {
void MessagesManager::on_get_message_reaction_list(
FullMessageId full_message_id, const ReactionType &reaction_type,
FlatHashMap<ReactionType, vector<DialogId>, ReactionTypeHash> reaction_types, int32 total_count) {
const Message *m = get_message_force(full_message_id, "on_get_message_reaction_list");
if (m == nullptr || m->reactions == nullptr) {
return;
@ -6855,7 +6856,7 @@ void MessagesManager::on_get_message_reaction_list(FullMessageId full_message_id
// it's impossible to use received reactions to update message reactions, because there is no way to find,
// which reactions are chosen by the current user, so just reload message reactions for consistency
if (m->reactions->are_consistent_with_list(reaction, std::move(reactions), total_count)) {
if (m->reactions->are_consistent_with_list(reaction_type, std::move(reaction_types), total_count)) {
return;
}
@ -8454,19 +8455,19 @@ void MessagesManager::hide_dialog_message_reactions(Dialog *d) {
}
}
void MessagesManager::set_active_reactions(vector<string> active_reactions) {
if (active_reactions == active_reactions_) {
void MessagesManager::set_active_reactions(vector<ReactionType> active_reaction_types) {
if (active_reaction_types == active_reaction_types_) {
return;
}
LOG(INFO) << "Set active reactions to " << active_reactions;
bool is_changed = active_reactions != active_reactions_;
active_reactions_ = std::move(active_reactions);
LOG(INFO) << "Set active reactions to " << active_reaction_types;
bool is_changed = active_reaction_types != active_reaction_types_;
active_reaction_types_ = std::move(active_reaction_types);
auto old_active_reaction_pos_ = std::move(active_reaction_pos_);
active_reaction_pos_.clear();
for (size_t i = 0; i < active_reactions_.size(); i++) {
active_reaction_pos_[active_reactions_[i]] = i;
for (size_t i = 0; i < active_reaction_types_.size(); i++) {
active_reaction_pos_[active_reaction_types_[i]] = i;
}
if (td_->auth_manager_->is_bot()) {
@ -23787,22 +23788,22 @@ Result<td_api::object_ptr<td_api::availableReactions>> MessagesManager::get_mess
LOG(INFO) << "Have available reactions " << available_reactions << " to be sorted by top reactions " << top_reactions
<< " and recent reactions " << recent_reactions;
if (active_reactions.allow_custom_ && active_reactions.allow_all_) {
for (auto &reaction : recent_reactions) {
if (is_custom_reaction(reaction)) {
for (auto &reaction_type : recent_reactions) {
if (reaction_type.is_custom_reaction()) {
show_premium = true;
}
}
for (auto &reaction : top_reactions) {
if (is_custom_reaction(reaction)) {
for (auto &reaction_type : top_reactions) {
if (reaction_type.is_custom_reaction()) {
show_premium = true;
}
}
}
FlatHashSet<string> all_available_reactions;
for (const auto &reaction : available_reactions.reactions_) {
CHECK(!reaction.empty());
all_available_reactions.insert(reaction);
FlatHashSet<ReactionType, ReactionTypeHash> all_available_reaction_types;
for (const auto &reaction_type : available_reactions.reaction_types_) {
CHECK(!reaction_type.is_empty());
all_available_reaction_types.insert(reaction_type);
}
vector<td_api::object_ptr<td_api::availableReaction>> top_reaction_objects;
@ -23810,22 +23811,22 @@ Result<td_api::object_ptr<td_api::availableReactions>> MessagesManager::get_mess
vector<td_api::object_ptr<td_api::availableReaction>> popular_reaction_objects;
vector<td_api::object_ptr<td_api::availableReaction>> last_reaction_objects;
FlatHashSet<string> added_custom_reactions;
FlatHashSet<ReactionType, ReactionTypeHash> added_custom_reaction_types;
auto add_reactions = [&](vector<td_api::object_ptr<td_api::availableReaction>> &reaction_objects,
const vector<string> &reactions) {
for (auto &reaction : reactions) {
if (all_available_reactions.erase(reaction) != 0) {
const vector<ReactionType> &reaction_types) {
for (auto &reaction_type : reaction_types) {
if (all_available_reaction_types.erase(reaction_type) != 0) {
// add available reaction
if (is_custom_reaction(reaction)) {
added_custom_reactions.insert(reaction);
if (reaction_type.is_custom_reaction()) {
added_custom_reaction_types.insert(reaction_type);
}
reaction_objects.push_back(
td_api::make_object<td_api::availableReaction>(get_reaction_type_object(reaction), false));
} else if (is_custom_reaction(reaction) && available_reactions.allow_custom_ &&
added_custom_reactions.insert(reaction).second) {
td_api::make_object<td_api::availableReaction>(reaction_type.get_reaction_type_object(), false));
} else if (reaction_type.is_custom_reaction() && available_reactions.allow_custom_ &&
added_custom_reaction_types.insert(reaction_type).second) {
// add implicitly available custom reaction
reaction_objects.push_back(
td_api::make_object<td_api::availableReaction>(get_reaction_type_object(reaction), !is_premium));
td_api::make_object<td_api::availableReaction>(reaction_type.get_reaction_type_object(), !is_premium));
} else {
// skip the reaction
}
@ -23843,8 +23844,8 @@ Result<td_api::object_ptr<td_api::availableReactions>> MessagesManager::get_mess
} else {
add_reactions(top_reaction_objects, top_reactions);
}
add_reactions(last_reaction_objects, active_reactions_);
add_reactions(last_reaction_objects, available_reactions.reactions_);
add_reactions(last_reaction_objects, active_reaction_types_);
add_reactions(last_reaction_objects, available_reactions.reaction_types_);
if (show_premium) {
if (recent_reactions.empty()) {
@ -23864,7 +23865,7 @@ Result<td_api::object_ptr<td_api::availableReactions>> MessagesManager::get_mess
append(top_reaction_objects, std::move(last_reaction_objects));
}
CHECK(all_available_reactions.empty());
CHECK(all_available_reaction_types.empty());
return td_api::make_object<td_api::availableReactions>(
std::move(top_reaction_objects), std::move(recent_reaction_objects), std::move(popular_reaction_objects),
@ -23899,16 +23900,16 @@ ChatReactions MessagesManager::get_message_available_reactions(const Dialog *d,
}
if (active_reactions.allow_all_) {
active_reactions.reactions_ = active_reactions_;
active_reactions.reaction_types_ = active_reaction_types_;
active_reactions.allow_all_ = false;
}
if (m->reactions != nullptr) {
for (const auto &reaction : m->reactions->reactions_) {
// an already used reaction can be added if it is an active reaction
const string &reaction_str = reaction.get_reaction();
if (can_use_reactions && is_active_reaction(reaction_str, active_reaction_pos_) &&
!td::contains(active_reactions.reactions_, reaction_str)) {
active_reactions.reactions_.push_back(reaction_str);
const auto &reaction_type = reaction.get_reaction_type();
if (can_use_reactions && reaction_type.is_active_reaction(active_reaction_pos_) &&
!td::contains(active_reactions.reaction_types_, reaction_type)) {
active_reactions.reaction_types_.push_back(reaction_type);
}
}
}
@ -23918,7 +23919,7 @@ ChatReactions MessagesManager::get_message_available_reactions(const Dialog *d,
return active_reactions;
}
void MessagesManager::add_message_reaction(FullMessageId full_message_id, string reaction, bool is_big,
void MessagesManager::add_message_reaction(FullMessageId full_message_id, ReactionType reaction_type, bool is_big,
bool add_to_recent, Promise<Unit> &&promise) {
auto dialog_id = full_message_id.get_dialog_id();
Dialog *d = get_dialog_force(dialog_id, "add_message_reaction");
@ -23931,7 +23932,7 @@ void MessagesManager::add_message_reaction(FullMessageId full_message_id, string
return promise.set_error(Status::Error(400, "Message not found"));
}
if (!get_message_available_reactions(d, m, true).is_allowed_reaction(reaction)) {
if (!get_message_available_reactions(d, m, true).is_allowed_reaction_type(reaction_type)) {
return promise.set_error(Status::Error(400, "The reaction isn't available for the message"));
}
@ -23944,18 +23945,19 @@ void MessagesManager::add_message_reaction(FullMessageId full_message_id, string
auto my_dialog_id =
d->default_send_message_as_dialog_id.is_valid() ? d->default_send_message_as_dialog_id : get_my_dialog_id();
if (!m->reactions->add_reaction(reaction, is_big, my_dialog_id, have_recent_choosers)) {
if (!m->reactions->add_reaction(reaction_type, is_big, my_dialog_id, have_recent_choosers)) {
return promise.set_value(Unit());
}
if (add_to_recent) {
add_recent_reaction(td_, reaction);
add_recent_reaction(td_, reaction_type);
}
set_message_reactions(d, m, is_big, add_to_recent, std::move(promise));
}
void MessagesManager::remove_message_reaction(FullMessageId full_message_id, string reaction, Promise<Unit> &&promise) {
void MessagesManager::remove_message_reaction(FullMessageId full_message_id, ReactionType reaction_type,
Promise<Unit> &&promise) {
auto dialog_id = full_message_id.get_dialog_id();
Dialog *d = get_dialog_force(dialog_id, "remove_message_reaction");
if (d == nullptr) {
@ -23967,13 +23969,13 @@ void MessagesManager::remove_message_reaction(FullMessageId full_message_id, str
return promise.set_error(Status::Error(400, "Message not found"));
}
if (reaction.empty()) {
if (reaction_type.is_empty()) {
return promise.set_error(Status::Error(400, "Invalid reaction specified"));
}
auto my_dialog_id =
d->default_send_message_as_dialog_id.is_valid() ? d->default_send_message_as_dialog_id : get_my_dialog_id();
if (m->reactions == nullptr || !m->reactions->remove_reaction(reaction, my_dialog_id)) {
if (m->reactions == nullptr || !m->reactions->remove_reaction(reaction_type, my_dialog_id)) {
return promise.set_value(Unit());
}
@ -23999,7 +24001,7 @@ void MessagesManager::set_message_reactions(Dialog *d, Message *m, bool is_big,
send_closure(actor_id, &MessagesManager::on_set_message_reactions, full_message_id, std::move(result),
std::move(promise));
});
send_message_reaction(td_, full_message_id, m->reactions->get_chosen_reactions(), is_big, add_to_recent,
send_message_reaction(td_, full_message_id, m->reactions->get_chosen_reaction_types(), is_big, add_to_recent,
std::move(query_promise));
}
@ -33833,7 +33835,7 @@ void MessagesManager::set_dialog_available_reactions(
ChatReactions available_reactions(std::move(available_reactions_ptr), !is_broadcast_channel(dialog_id));
auto active_reactions = get_active_reactions(available_reactions);
if (active_reactions.reactions_.size() != available_reactions.reactions_.size()) {
if (active_reactions.reaction_types_.size() != available_reactions.reaction_types_.size()) {
return promise.set_error(Status::Error(400, "Invalid reactions specified"));
}
available_reactions = std::move(active_reactions);

View File

@ -54,6 +54,7 @@
#include "td/telegram/NotificationSettingsScope.h"
#include "td/telegram/OrderedMessage.h"
#include "td/telegram/Photo.h"
#include "td/telegram/ReactionType.h"
#include "td/telegram/RecentDialogList.h"
#include "td/telegram/ReplyMarkup.h"
#include "td/telegram/RestrictionReason.h"
@ -355,8 +356,9 @@ class MessagesManager final : public Actor {
void try_reload_message_reactions(DialogId dialog_id, bool is_finished);
void on_get_message_reaction_list(FullMessageId full_message_id, const string &reaction,
FlatHashMap<string, vector<DialogId>> reactions, int32 total_count);
void on_get_message_reaction_list(FullMessageId full_message_id, const ReactionType &reaction_type,
FlatHashMap<ReactionType, vector<DialogId>, ReactionTypeHash> reaction_types,
int32 total_count);
void on_update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count,
bool has_reply_info,
@ -566,7 +568,7 @@ class MessagesManager final : public Actor {
void set_dialog_description(DialogId dialog_id, const string &description, Promise<Unit> &&promise);
void set_active_reactions(vector<string> active_reactions);
void set_active_reactions(vector<ReactionType> active_reaction_types);
void set_dialog_available_reactions(DialogId dialog_id,
td_api::object_ptr<td_api::ChatAvailableReactions> &&available_reactions_ptr,
@ -856,10 +858,10 @@ class MessagesManager final : public Actor {
Result<td_api::object_ptr<td_api::availableReactions>> get_message_available_reactions(FullMessageId full_message_id,
int32 row_size);
void add_message_reaction(FullMessageId full_message_id, string reaction, bool is_big, bool add_to_recent,
void add_message_reaction(FullMessageId full_message_id, ReactionType reaction_type, bool is_big, bool add_to_recent,
Promise<Unit> &&promise);
void remove_message_reaction(FullMessageId full_message_id, string reaction, Promise<Unit> &&promise);
void remove_message_reaction(FullMessageId full_message_id, ReactionType reaction_type, Promise<Unit> &&promise);
void get_message_public_forwards(FullMessageId full_message_id, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::foundMessages>> &&promise);
@ -3697,8 +3699,8 @@ class MessagesManager final : public Actor {
FlatHashMap<FullMessageId, int32, FullMessageIdHash> pending_read_reactions_;
vector<string> active_reactions_;
FlatHashMap<string, size_t> active_reaction_pos_;
vector<ReactionType> active_reaction_types_;
FlatHashMap<ReactionType, size_t, ReactionTypeHash> active_reaction_pos_;
FlatHashMap<PendingGetHistoryQuery, vector<Promise<Unit>>, PendingGetHistoryQueryHash> get_history_queries_;

View File

@ -17,10 +17,10 @@
#include "td/telegram/Global.h"
#include "td/telegram/JsonValue.h"
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/MessageReaction.h"
#include "td/telegram/net/MtprotoHeader.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/NotificationManager.h"
#include "td/telegram/ReactionType.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StorageManager.h"
@ -315,7 +315,7 @@ bool OptionManager::is_internal_option(Slice name) {
td_api::object_ptr<td_api::Update> OptionManager::get_internal_option_update(Slice name) const {
if (name == "default_reaction") {
return get_update_default_reaction_type(get_option_string(name));
return ReactionType(get_option_string(name)).get_update_default_reaction_type();
}
if (name == "otherwise_relogin_days") {
auto days = narrow_cast<int32>(get_option_integer(name));

View File

@ -0,0 +1,154 @@
//
// 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<int64>(r_decoded.ok().c_str());
}
static string get_custom_emoji_string(int64 custom_emoji_id) {
char s[8];
as<int64>(&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<telegram_api::Reaction> &reaction) {
if (reaction == nullptr) {
return;
}
switch (reaction->get_id()) {
case telegram_api::reactionEmpty::ID:
break;
case telegram_api::reactionEmoji::ID:
reaction_ = static_cast<const telegram_api::reactionEmoji *>(reaction.get())->emoticon_;
if (is_custom_reaction()) {
reaction_ = string();
}
break;
case telegram_api::reactionCustomEmoji::ID:
reaction_ =
get_custom_emoji_string(static_cast<const telegram_api::reactionCustomEmoji *>(reaction.get())->document_id_);
break;
default:
UNREACHABLE();
break;
}
}
ReactionType::ReactionType(const td_api::object_ptr<td_api::ReactionType> &type) {
if (type == nullptr) {
return;
}
switch (type->get_id()) {
case td_api::reactionTypeEmoji::ID: {
const string &emoji = static_cast<const td_api::reactionTypeEmoji *>(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<const td_api::reactionTypeCustomEmoji *>(type.get())->custom_emoji_id_);
break;
default:
UNREACHABLE();
break;
}
}
telegram_api::object_ptr<telegram_api::Reaction> ReactionType::get_input_reaction() const {
if (is_empty()) {
return telegram_api::make_object<telegram_api::reactionEmpty>();
}
if (is_custom_reaction()) {
return telegram_api::make_object<telegram_api::reactionCustomEmoji>(get_custom_emoji_id(reaction_));
}
return telegram_api::make_object<telegram_api::reactionEmoji>(reaction_);
}
td_api::object_ptr<td_api::ReactionType> ReactionType::get_reaction_type_object() const {
CHECK(!is_empty());
if (is_custom_reaction()) {
return td_api::make_object<td_api::reactionTypeCustomEmoji>(get_custom_emoji_id(reaction_));
}
return td_api::make_object<td_api::reactionTypeEmoji>(reaction_);
}
td_api::object_ptr<td_api::updateDefaultReactionType> ReactionType::get_update_default_reaction_type() const {
if (is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::updateDefaultReactionType>(get_reaction_type_object());
}
bool ReactionType::is_custom_reaction() const {
return reaction_[0] == '#';
}
bool ReactionType::is_active_reaction(
const FlatHashMap<ReactionType, size_t, ReactionTypeHash> &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<ReactionType> &reaction_types) {
vector<uint64> numbers;
for (auto &reaction_type : reaction_types) {
if (reaction_type.is_custom_reaction()) {
auto custom_emoji_id = static_cast<uint64>(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<uint32>(hash[num]);
};
numbers.push_back(0);
numbers.push_back(static_cast<int32>((get(0) << 24) + (get(1) << 16) + (get(2) << 8) + get(3)));
}
}
return get_vector_hash(numbers);
}
} // namespace td

View File

@ -0,0 +1,85 @@
//
// 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)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/StringBuilder.h"
namespace td {
class Dependencies;
class Td;
class ReactionType {
string reaction_;
friend bool operator<(const ReactionType &lhs, const ReactionType &rhs);
friend bool operator==(const ReactionType &lhs, const ReactionType &rhs);
friend StringBuilder &operator<<(StringBuilder &string_builder, const ReactionType &reaction_type);
friend struct ReactionTypeHash;
public:
ReactionType() = default;
explicit ReactionType(string &&emoji);
explicit ReactionType(const telegram_api::object_ptr<telegram_api::Reaction> &reaction);
explicit ReactionType(const td_api::object_ptr<td_api::ReactionType> &type);
telegram_api::object_ptr<telegram_api::Reaction> get_input_reaction() const;
td_api::object_ptr<td_api::ReactionType> get_reaction_type_object() const;
td_api::object_ptr<td_api::updateDefaultReactionType> get_update_default_reaction_type() const;
bool is_custom_reaction() const;
bool is_active_reaction(const FlatHashMap<ReactionType, size_t, ReactionTypeHash> &active_reaction_pos) const;
bool is_empty() const {
return reaction_.empty();
}
const string &get_string() const {
return reaction_;
}
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
struct ReactionTypeHash {
uint32 operator()(const ReactionType &reaction_type) const {
return Hash<string>()(reaction_type.reaction_);
}
};
bool operator<(const ReactionType &lhs, const ReactionType &rhs);
bool operator==(const ReactionType &lhs, const ReactionType &rhs);
inline bool operator!=(const ReactionType &lhs, const ReactionType &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const ReactionType &reaction_type);
int64 get_reaction_types_hash(const vector<ReactionType> &reaction_types);
} // namespace td

View File

@ -0,0 +1,27 @@
//
// 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)
//
#pragma once
#include "td/telegram/ReactionType.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void ReactionType::store(StorerT &storer) const {
CHECK(!is_empty());
td::store(reaction_, storer);
}
template <class ParserT>
void ReactionType::parse(ParserT &parser) {
td::parse(reaction_, parser);
}
} // namespace td

View File

@ -1812,12 +1812,13 @@ void StickersManager::init() {
td_api::object_ptr<td_api::emojiReaction> StickersManager::get_emoji_reaction_object(const string &emoji) const {
for (auto &reaction : reactions_.reactions_) {
if (reaction.reaction_ == emoji) {
if (reaction.reaction_type_.get_string() == emoji) {
return td_api::make_object<td_api::emojiReaction>(
reaction.reaction_, reaction.title_, reaction.is_active_, get_sticker_object(reaction.static_icon_),
get_sticker_object(reaction.appear_animation_), get_sticker_object(reaction.select_animation_),
get_sticker_object(reaction.activate_animation_), get_sticker_object(reaction.effect_animation_),
get_sticker_object(reaction.around_animation_), get_sticker_object(reaction.center_animation_));
reaction.reaction_type_.get_string(), reaction.title_, reaction.is_active_,
get_sticker_object(reaction.static_icon_), get_sticker_object(reaction.appear_animation_),
get_sticker_object(reaction.select_animation_), get_sticker_object(reaction.activate_animation_),
get_sticker_object(reaction.effect_animation_), get_sticker_object(reaction.around_animation_),
get_sticker_object(reaction.center_animation_));
}
}
return nullptr;
@ -1833,47 +1834,47 @@ void StickersManager::get_emoji_reaction(const string &emoji,
promise.set_value(get_emoji_reaction_object(emoji));
}
vector<string> StickersManager::get_recent_reactions() {
vector<ReactionType> StickersManager::get_recent_reactions() {
load_recent_reactions();
return recent_reactions_.reactions_;
return recent_reactions_.reaction_types_;
}
vector<string> StickersManager::get_top_reactions() {
vector<ReactionType> StickersManager::get_top_reactions() {
load_top_reactions();
return top_reactions_.reactions_;
return top_reactions_.reaction_types_;
}
void StickersManager::add_recent_reaction(const string &reaction) {
void StickersManager::add_recent_reaction(const ReactionType &reaction_type) {
load_recent_reactions();
auto &reactions = recent_reactions_.reactions_;
if (!reactions.empty() && reactions[0] == reaction) {
auto &reactions = recent_reactions_.reaction_types_;
if (!reactions.empty() && reactions[0] == reaction_type) {
return;
}
auto it = std::find(reactions.begin(), reactions.end(), reaction);
auto it = std::find(reactions.begin(), reactions.end(), reaction_type);
if (it == reactions.end()) {
if (static_cast<int32>(reactions.size()) == MAX_RECENT_REACTIONS) {
reactions.back() = reaction;
reactions.back() = reaction_type;
} else {
reactions.push_back(reaction);
reactions.push_back(reaction_type);
}
it = reactions.end() - 1;
}
std::rotate(reactions.begin(), it, it + 1);
recent_reactions_.hash_ = get_reactions_hash(reactions);
recent_reactions_.hash_ = get_reaction_types_hash(reactions);
}
void StickersManager::clear_recent_reactions(Promise<Unit> &&promise) {
load_recent_reactions();
if (recent_reactions_.reactions_.empty()) {
if (recent_reactions_.reaction_types_.empty()) {
return promise.set_value(Unit());
}
recent_reactions_.hash_ = 0;
recent_reactions_.reactions_.clear();
recent_reactions_.reaction_types_.clear();
td_->create_handler<ClearRecentReactionsQuery>(std::move(promise))->send();
}
@ -4139,12 +4140,13 @@ void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &ty
td_api::object_ptr<td_api::updateActiveEmojiReactions> StickersManager::get_update_active_emoji_reactions_object()
const {
return td_api::make_object<td_api::updateActiveEmojiReactions>(vector<string>(active_reactions_));
return td_api::make_object<td_api::updateActiveEmojiReactions>(
transform(active_reaction_types_, [](const ReactionType &reaction_type) { return reaction_type.get_string(); }));
}
void StickersManager::save_active_reactions() {
LOG(INFO) << "Save " << active_reactions_.size() << " active reactions";
G()->td_db()->get_binlog_pmc()->set("active_reactions", log_event_store(active_reactions_).as_slice().str());
LOG(INFO) << "Save " << active_reaction_types_.size() << " active reactions";
G()->td_db()->get_binlog_pmc()->set("active_reactions", log_event_store(active_reaction_types_).as_slice().str());
}
void StickersManager::save_reactions() {
@ -4154,34 +4156,34 @@ void StickersManager::save_reactions() {
}
void StickersManager::save_recent_reactions() {
LOG(INFO) << "Save " << recent_reactions_.reactions_.size() << " recent reactions";
LOG(INFO) << "Save " << recent_reactions_.reaction_types_.size() << " recent reactions";
are_recent_reactions_loaded_from_database_ = true;
G()->td_db()->get_binlog_pmc()->set("recent_reactions", log_event_store(recent_reactions_).as_slice().str());
}
void StickersManager::save_top_reactions() {
LOG(INFO) << "Save " << top_reactions_.reactions_.size() << " top reactions";
LOG(INFO) << "Save " << top_reactions_.reaction_types_.size() << " top reactions";
are_top_reactions_loaded_from_database_ = true;
G()->td_db()->get_binlog_pmc()->set("top_reactions", log_event_store(top_reactions_).as_slice().str());
}
void StickersManager::load_active_reactions() {
LOG(INFO) << "Loading active reactions";
string active_reactions = G()->td_db()->get_binlog_pmc()->get("active_reactions");
if (active_reactions.empty()) {
string active_reaction_types = G()->td_db()->get_binlog_pmc()->get("active_reactions");
if (active_reaction_types.empty()) {
return reload_reactions();
}
auto status = log_event_parse(active_reactions_, active_reactions);
auto status = log_event_parse(active_reaction_types_, active_reaction_types);
if (status.is_error()) {
LOG(ERROR) << "Can't load active reactions: " << status;
active_reactions_ = {};
active_reaction_types_ = {};
return reload_reactions();
}
LOG(INFO) << "Successfully loaded " << active_reactions_.size() << " active reactions";
LOG(INFO) << "Successfully loaded " << active_reaction_types_.size() << " active reactions";
td_->messages_manager_->set_active_reactions(vector<string>(active_reactions_));
td_->messages_manager_->set_active_reactions(active_reaction_types_);
send_closure(G()->td(), &Td::send_update, get_update_active_emoji_reactions_object());
}
@ -4204,8 +4206,8 @@ void StickersManager::load_reactions() {
LOG(ERROR) << "Can't load available reactions: " << status;
return reload_reactions();
}
for (auto &reaction : new_reactions.reactions_) {
if (!reaction.is_valid()) {
for (auto &reaction_type : new_reactions.reactions_) {
if (!reaction_type.is_valid()) {
LOG(ERROR) << "Loaded invalid reaction";
return reload_reactions();
}
@ -4236,7 +4238,7 @@ void StickersManager::load_recent_reactions() {
return reload_recent_reactions();
}
LOG(INFO) << "Successfully loaded " << recent_reactions_.reactions_.size() << " recent reactions";
LOG(INFO) << "Successfully loaded " << recent_reactions_.reaction_types_.size() << " recent reactions";
}
void StickersManager::load_top_reactions() {
@ -4258,26 +4260,26 @@ void StickersManager::load_top_reactions() {
return reload_top_reactions();
}
LOG(INFO) << "Successfully loaded " << top_reactions_.reactions_.size() << " top reactions";
LOG(INFO) << "Successfully loaded " << top_reactions_.reaction_types_.size() << " top reactions";
}
void StickersManager::update_active_reactions() {
vector<string> active_reactions;
vector<ReactionType> active_reaction_types;
for (auto &reaction : reactions_.reactions_) {
if (reaction.is_active_) {
active_reactions.emplace_back(reaction.reaction_);
active_reaction_types.emplace_back(reaction.reaction_type_);
}
}
if (active_reactions == active_reactions_) {
if (active_reaction_types == active_reaction_types_) {
return;
}
active_reactions_ = active_reactions;
active_reaction_types_ = active_reaction_types;
save_active_reactions();
send_closure(G()->td(), &Td::send_update, get_update_active_emoji_reactions_object());
td_->messages_manager_->set_active_reactions(std::move(active_reactions));
td_->messages_manager_->set_active_reactions(std::move(active_reaction_types));
}
void StickersManager::on_get_available_reactions(
@ -4311,7 +4313,7 @@ void StickersManager::on_get_available_reactions(
Reaction reaction;
reaction.is_active_ = !available_reaction->inactive_;
reaction.is_premium_ = available_reaction->premium_;
reaction.reaction_ = std::move(available_reaction->reaction_);
reaction.reaction_type_ = ReactionType(std::move(available_reaction->reaction_));
reaction.title_ = std::move(available_reaction->title_);
reaction.static_icon_ =
on_get_sticker_document(std::move(available_reaction->static_icon_), StickerFormat::Webp).second;
@ -4329,11 +4331,11 @@ void StickersManager::on_get_available_reactions(
on_get_sticker_document(std::move(available_reaction->center_icon_), StickerFormat::Tgs).second;
if (!reaction.is_valid()) {
LOG(ERROR) << "Receive invalid reaction " << reaction.reaction_;
LOG(ERROR) << "Receive invalid reaction " << reaction.reaction_type_;
continue;
}
if (reaction.is_premium_) {
LOG(ERROR) << "Receive premium reaction " << reaction.reaction_;
LOG(ERROR) << "Receive premium reaction " << reaction.reaction_type_;
continue;
}
@ -4364,21 +4366,20 @@ void StickersManager::on_get_recent_reactions(tl_object_ptr<telegram_api::messag
CHECK(constructor_id == telegram_api::messages_reactions::ID);
auto reactions = move_tl_object_as<telegram_api::messages_reactions>(reactions_ptr);
auto new_reactions =
transform(reactions->reactions_, [](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
return get_message_reaction_string(reaction);
});
if (new_reactions == recent_reactions_.reactions_ && recent_reactions_.hash_ == reactions->hash_) {
auto new_reaction_types = transform(
reactions->reactions_,
[](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) { return ReactionType(reaction); });
if (new_reaction_types == recent_reactions_.reaction_types_ && recent_reactions_.hash_ == reactions->hash_) {
LOG(INFO) << "Top reactions are not modified";
return;
}
recent_reactions_.reactions_ = std::move(new_reactions);
recent_reactions_.reaction_types_ = std::move(new_reaction_types);
recent_reactions_.hash_ = reactions->hash_;
auto expected_hash = get_reactions_hash(recent_reactions_.reactions_);
auto expected_hash = get_reaction_types_hash(recent_reactions_.reaction_types_);
if (recent_reactions_.hash_ != expected_hash) {
LOG(ERROR) << "Receive hash " << recent_reactions_.hash_ << " instead of " << expected_hash << " for reactions "
<< recent_reactions_.reactions_;
<< recent_reactions_.reaction_types_;
}
save_recent_reactions();
@ -4401,15 +4402,14 @@ void StickersManager::on_get_top_reactions(tl_object_ptr<telegram_api::messages_
CHECK(constructor_id == telegram_api::messages_reactions::ID);
auto reactions = move_tl_object_as<telegram_api::messages_reactions>(reactions_ptr);
auto new_reactions =
transform(reactions->reactions_, [](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) {
return get_message_reaction_string(reaction);
});
if (new_reactions == top_reactions_.reactions_ && top_reactions_.hash_ == reactions->hash_) {
auto new_reaction_types = transform(
reactions->reactions_,
[](const telegram_api::object_ptr<telegram_api::Reaction> &reaction) { return ReactionType(reaction); });
if (new_reaction_types == top_reactions_.reaction_types_ && top_reactions_.hash_ == reactions->hash_) {
LOG(INFO) << "Top reactions are not modified";
return;
}
top_reactions_.reactions_ = std::move(new_reactions);
top_reactions_.reaction_types_ = std::move(new_reaction_types);
top_reactions_.hash_ = reactions->hash_;
save_top_reactions();
@ -7229,9 +7229,9 @@ void StickersManager::send_update_animated_emoji_clicked(FullMessageId full_mess
full_message_id.get_message_id().get(), get_sticker_object(sticker_id, false, true)));
}
bool StickersManager::is_active_reaction(const string &reaction) const {
bool StickersManager::is_active_reaction(const ReactionType &reaction_type) const {
for (auto &supported_reaction : reactions_.reactions_) {
if (supported_reaction.reaction_ == reaction) {
if (supported_reaction.reaction_type_ == reaction_type) {
return supported_reaction.is_active_;
}
}
@ -10412,7 +10412,7 @@ void StickersManager::get_current_state(vector<td_api::object_ptr<td_api::Update
return;
}
if (!active_reactions_.empty()) {
if (!active_reaction_types_.empty()) {
updates.push_back(get_update_active_emoji_reactions_object());
}
for (int32 type = 0; type < MAX_STICKER_TYPE; type++) {

View File

@ -16,6 +16,7 @@
#include "td/telegram/FullMessageId.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/ReactionType.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/SpecialStickerSetType.h"
#include "td/telegram/StickerFormat.h"
@ -146,7 +147,7 @@ class StickersManager final : public Actor {
Status on_animated_emoji_message_clicked(string &&emoji, FullMessageId full_message_id, string data);
bool is_active_reaction(const string &reaction) const;
bool is_active_reaction(const ReactionType &reaction_type) const;
void create_sticker(FileId file_id, FileId premium_animation_file_id, string minithumbnail, PhotoSize thumbnail,
Dimensions dimensions, tl_object_ptr<telegram_api::documentAttributeSticker> sticker,
@ -195,11 +196,11 @@ class StickersManager final : public Actor {
void get_emoji_reaction(const string &emoji, Promise<td_api::object_ptr<td_api::emojiReaction>> &&promise);
vector<string> get_recent_reactions();
vector<ReactionType> get_recent_reactions();
vector<string> get_top_reactions();
vector<ReactionType> get_top_reactions();
void add_recent_reaction(const string &reaction);
void add_recent_reaction(const ReactionType &reaction_type);
void clear_recent_reactions(Promise<Unit> &&promise);
@ -584,7 +585,7 @@ class StickersManager final : public Actor {
};
struct Reaction {
string reaction_;
ReactionType reaction_type_;
string title_;
bool is_active_ = false;
bool is_premium_ = false;
@ -598,7 +599,7 @@ class StickersManager final : public Actor {
bool is_valid() const {
return static_icon_.is_valid() && appear_animation_.is_valid() && select_animation_.is_valid() &&
activate_animation_.is_valid() && effect_animation_.is_valid() && !reaction_.empty();
activate_animation_.is_valid() && effect_animation_.is_valid() && !reaction_type_.is_empty();
}
template <class StorerT>
@ -623,7 +624,7 @@ class StickersManager final : public Actor {
struct ReactionList {
int64 hash_ = 0;
bool is_being_reloaded_ = false;
vector<string> reactions_;
vector<ReactionType> reaction_types_;
template <class StorerT>
void store(StorerT &storer) const;
@ -1178,7 +1179,7 @@ class StickersManager final : public Actor {
FlatHashMap<FileId, std::pair<UserId, Promise<Unit>>, FileIdHash> being_uploaded_files_;
Reactions reactions_;
vector<string> active_reactions_;
vector<ReactionType> active_reaction_types_;
ReactionList recent_reactions_;
ReactionList top_reactions_;

View File

@ -476,7 +476,7 @@ void StickersManager::Reaction::store(StorerT &storer) const {
STORE_FLAG(has_center_animation);
STORE_FLAG(is_premium_);
END_STORE_FLAGS();
td::store(reaction_, storer);
td::store(reaction_type_, storer);
td::store(title_, storer);
stickers_manager->store_sticker(static_icon_, false, storer, "Reaction");
stickers_manager->store_sticker(appear_animation_, false, storer, "Reaction");
@ -502,7 +502,7 @@ void StickersManager::Reaction::parse(ParserT &parser) {
PARSE_FLAG(has_center_animation);
PARSE_FLAG(is_premium_);
END_PARSE_FLAGS();
td::parse(reaction_, parser);
td::parse(reaction_type_, parser);
td::parse(title_, parser);
static_icon_ = stickers_manager->parse_sticker(false, parser);
appear_animation_ = stickers_manager->parse_sticker(false, parser);
@ -545,24 +545,24 @@ void StickersManager::Reactions::parse(ParserT &parser) {
template <class StorerT>
void StickersManager::ReactionList::store(StorerT &storer) const {
bool has_reactions = !reactions_.empty();
bool has_reaction_types = !reaction_types_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_reactions);
STORE_FLAG(has_reaction_types);
END_STORE_FLAGS();
if (has_reactions) {
td::store(reactions_, storer);
if (has_reaction_types) {
td::store(reaction_types_, storer);
td::store(hash_, storer);
}
}
template <class ParserT>
void StickersManager::ReactionList::parse(ParserT &parser) {
bool has_reactions;
bool has_reaction_types;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_reactions);
PARSE_FLAG(has_reaction_types);
END_PARSE_FLAGS();
if (has_reactions) {
td::parse(reactions_, parser);
if (has_reaction_types) {
td::parse(reaction_types_, parser);
td::parse(hash_, parser);
}
}

View File

@ -107,6 +107,7 @@
#include "td/telegram/Premium.h"
#include "td/telegram/PrivacyManager.h"
#include "td/telegram/PublicDialogType.h"
#include "td/telegram/ReactionType.h"
#include "td/telegram/ReportReason.h"
#include "td/telegram/RequestActor.h"
#include "td/telegram/ScopeNotificationSettings.h"
@ -5350,7 +5351,7 @@ void Td::on_request(uint64 id, td_api::addMessageReaction &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
messages_manager_->add_message_reaction({DialogId(request.chat_id_), MessageId(request.message_id_)},
get_message_reaction_string(request.reaction_type_), request.is_big_,
ReactionType(request.reaction_type_), request.is_big_,
request.update_recent_reactions_, std::move(promise));
}
@ -5358,7 +5359,7 @@ void Td::on_request(uint64 id, td_api::removeMessageReaction &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
messages_manager_->remove_message_reaction({DialogId(request.chat_id_), MessageId(request.message_id_)},
get_message_reaction_string(request.reaction_type_), std::move(promise));
ReactionType(request.reaction_type_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::getMessageAddedReactions &request) {
@ -5366,14 +5367,14 @@ void Td::on_request(uint64 id, td_api::getMessageAddedReactions &request) {
CLEAN_INPUT_STRING(request.offset_);
CREATE_REQUEST_PROMISE();
get_message_added_reactions(this, {DialogId(request.chat_id_), MessageId(request.message_id_)},
get_message_reaction_string(request.reaction_type_), std::move(request.offset_),
request.limit_, std::move(promise));
ReactionType(request.reaction_type_), std::move(request.offset_), request.limit_,
std::move(promise));
}
void Td::on_request(uint64 id, td_api::setDefaultReactionType &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
set_default_reaction(this, get_message_reaction_string(request.reaction_type_), std::move(promise));
set_default_reaction(this, ReactionType(request.reaction_type_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::getMessagePublicForwards &request) {