Add updateAnimatedEmojiMessageClicked.

This commit is contained in:
levlam 2021-09-17 15:51:42 +03:00
parent f0c2f8f124
commit 5de53e09eb
9 changed files with 256 additions and 11 deletions

View File

@ -977,7 +977,7 @@ chatActionBarReportUnrelatedLocation = ChatActionBar;
//@description The chat is a recently created group chat, to which new members can be invited
chatActionBarInviteMembers = ChatActionBar;
//@description The chat is a private or secret chat, which can be reported using the method reportChat, or the other user can be blocked using the method blockUser, or the other user can be added to the contact list using the method addContact
//@description The chat is a private or secret chat, which can be reported using the method reportChat, or the other user can be blocked using the method toggleMessageSenderIsBlocked, or the other user can be added to the contact list using the method addContact
//@can_unarchive If true, the chat was automatically archived and can be moved back to the main chat list using addChatToList simultaneously with setting chat notification settings to default using setChatNotificationSettings
//@distance If non-negative, the current user was found by the peer through searchChatsNearby and this is the distance between the users
chatActionBarReportAddBlock can_unarchive:Bool distance:int32 = ChatActionBar;
@ -2316,6 +2316,10 @@ diceStickersRegular sticker:sticker = DiceStickers;
diceStickersSlotMachine background:sticker lever:sticker left_reel:sticker center_reel:sticker right_reel:sticker = DiceStickers;
//@description Describes a click on an animated emoji by the other party, resulted in a big animation @start_delay Animation start delay @sticker The played animated sticker
animatedEmojiClick start_delay:double sticker:sticker = AnimatedEmojiClick;
//@description Represents the result of an ImportContacts request @user_ids User identifiers of the imported contacts in the same order as they were specified in the request; 0 if the contact is not yet a registered user
//@importer_count The number of users that imported the corresponding contact; 0 for already registered users or if unavailable
importedContacts user_ids:vector<int53> importer_count:vector<int32> = ImportedContacts;
@ -3835,6 +3839,9 @@ updateUsersNearby users_nearby:vector<chatNearby> = Update;
//@description The list of supported dice emojis has changed @emojis The new list of supported dice emojis
updateDiceEmojis emojis:vector<string> = Update;
//@description Some animated emoji message was clicked @chat_id Chat identifier @message_id Message identifier @clicks Description of new number of unread mention messages left in the chat
updateAnimatedEmojiMessageClicked chat_id:int53 message_id:int53 clicks:vector<animatedEmojiClick> = Update;
//@description The parameters of animation search through GetOption("animation_search_bot_username") bot has changed @provider Name of the animation search provider @emojis The new list of emojis suggested for searching
updateAnimationSearchParameters provider:string emojis:vector<string> = Update;

View File

@ -7,8 +7,10 @@
#include "td/telegram/DialogAction.h"
#include "td/telegram/misc.h"
#include "td/telegram/ServerMessageId.h"
#include "td/utils/misc.h"
#include "td/utils/utf8.h"
namespace td {
@ -45,6 +47,16 @@ void DialogAction::init(Type type, string emoji) {
}
}
void DialogAction::init(Type type, int32 message_id, string emoji, string data) {
if (ServerMessageId(message_id).is_valid() && is_valid_emoji(emoji) && check_utf8(data)) {
type_ = type;
progress_ = message_id;
emoji_ = PSTRING() << emoji << '\xFF' << data;
} else {
init(Type::Cancel);
}
}
DialogAction::DialogAction(Type type, int32 progress) {
init(type, progress);
}
@ -53,6 +65,10 @@ DialogAction::DialogAction(Type type, string emoji) {
init(type, std::move(emoji));
}
DialogAction::DialogAction(Type type, int32 message_id, string emoji, string data) {
init(type, message_id, std::move(emoji), std::move(data));
}
DialogAction::DialogAction(tl_object_ptr<td_api::ChatAction> &&action) {
if (action == nullptr) {
return;
@ -128,7 +144,6 @@ DialogAction::DialogAction(tl_object_ptr<telegram_api::SendMessageAction> &&acti
init(Type::Cancel);
break;
case telegram_api::sendMessageTypingAction::ID:
case telegram_api::sendMessageEmojiInteraction::ID:
init(Type::Typing);
break;
case telegram_api::sendMessageRecordVideoAction::ID:
@ -190,6 +205,12 @@ DialogAction::DialogAction(tl_object_ptr<telegram_api::SendMessageAction> &&acti
init(Type::EnjoyingAnimations, std::move(emoji_interaction_seen_action->emoticon_));
break;
}
case telegram_api::sendMessageEmojiInteraction::ID: {
auto emoji_interaction_action = move_tl_object_as<telegram_api::sendMessageEmojiInteraction>(action);
init(Type::ClickingAnimatedEmoji, emoji_interaction_action->msg_id_,
std::move(emoji_interaction_action->emoticon_), std::move(emoji_interaction_action->interaction_->data_));
break;
}
default:
UNREACHABLE();
break;
@ -232,6 +253,12 @@ tl_object_ptr<telegram_api::SendMessageAction> DialogAction::get_input_send_mess
return make_tl_object<telegram_api::sendMessageChooseStickerAction>();
case Type::EnjoyingAnimations:
return make_tl_object<telegram_api::sendMessageEmojiInteractionSeen>(emoji_);
case Type::ClickingAnimatedEmoji: {
auto pos = emoji_.find('\xFF');
CHECK(pos < emoji_.size());
return make_tl_object<telegram_api::sendMessageEmojiInteraction>(
emoji_.substr(0, pos), progress_, make_tl_object<telegram_api::dataJSON>(emoji_.substr(pos + 1)));
}
default:
UNREACHABLE();
return nullptr;
@ -274,6 +301,7 @@ tl_object_ptr<secret_api::SendMessageAction> DialogAction::get_secret_input_send
return make_tl_object<secret_api::sendMessageTypingAction>();
case Type::EnjoyingAnimations:
return make_tl_object<secret_api::sendMessageTypingAction>();
case Type::ClickingAnimatedEmoji:
default:
UNREACHABLE();
return nullptr;
@ -314,6 +342,7 @@ tl_object_ptr<td_api::ChatAction> DialogAction::get_chat_action_object() const {
return td_api::make_object<td_api::chatActionEnjoyingAnimations>(emoji_);
case Type::ImportingMessages:
case Type::SpeakingInVoiceChat:
case Type::ClickingAnimatedEmoji:
default:
UNREACHABLE();
return td_api::make_object<td_api::chatActionCancel>();
@ -426,6 +455,18 @@ int32 DialogAction::get_importing_messages_action_progress() const {
return progress_;
}
DialogAction::ClickingAnimateEmojiInfo DialogAction::get_clicking_animated_emoji_action_info() const {
ClickingAnimateEmojiInfo result;
if (type_ == Type::ClickingAnimatedEmoji) {
auto pos = emoji_.find('\xFF');
CHECK(pos < emoji_.size());
result.message_id = progress_;
result.emoji = emoji_.substr(0, pos);
result.data = emoji_.substr(pos + 1);
}
return result;
}
StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &action) {
string_builder << "ChatAction";
const char *type = [action_type = action.type_] {
@ -464,17 +505,26 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &act
return "ChoosingSticker";
case DialogAction::Type::EnjoyingAnimations:
return "EnjoyingAnimations";
case DialogAction::Type::ClickingAnimatedEmoji:
return "ClickingAnimatedEmoji";
default:
UNREACHABLE();
return "Cancel";
}
}();
string_builder << type << "Action";
if (action.progress_ != 0) {
string_builder << '(' << action.progress_ << "%)";
}
if (!action.emoji_.empty()) {
string_builder << '(' << action.emoji_ << ')';
if (action.type_ == DialogAction::Type::ClickingAnimatedEmoji) {
auto pos = action.emoji_.find('\xFF');
CHECK(pos < action.emoji_.size());
string_builder << '(' << action.progress_ << ")(" << Slice(action.emoji_).substr(0, pos) << ")("
<< Slice(action.emoji_).substr(pos + 1) << ')';
} else {
if (action.progress_ != 0) {
string_builder << '(' << action.progress_ << "%)";
}
if (!action.emoji_.empty()) {
string_builder << '(' << action.emoji_ << ')';
}
}
return string_builder;
}

View File

@ -34,7 +34,8 @@ class DialogAction {
SpeakingInVoiceChat,
ImportingMessages,
ChoosingSticker,
EnjoyingAnimations
EnjoyingAnimations,
ClickingAnimatedEmoji
};
Type type_ = Type::Cancel;
int32 progress_ = 0;
@ -44,12 +45,16 @@ class DialogAction {
DialogAction(Type type, string emoji);
DialogAction(Type type, int32 message_id, string emoji, string data);
void init(Type type);
void init(Type type, int32 progress);
void init(Type type, string emoji);
void init(Type type, int32 message_id, string emoji, string data);
static bool is_valid_emoji(string &emoji);
public:
@ -75,6 +80,13 @@ class DialogAction {
int32 get_importing_messages_action_progress() const;
struct ClickingAnimateEmojiInfo {
int32 message_id;
string emoji;
string data;
};
ClickingAnimateEmojiInfo get_clicking_animated_emoji_action_info() const;
friend bool operator==(const DialogAction &lhs, const DialogAction &rhs) {
return lhs.type_ == rhs.type_ && lhs.progress_ == rhs.progress_ && lhs.emoji_ == rhs.emoji_;
}

View File

@ -5313,6 +5313,22 @@ void get_message_content_animated_emoji_click_sticker(const MessageContent *cont
td->stickers_manager_->get_animated_emoji_click_sticker(text.text, full_message_id, std::move(promise));
}
void on_message_content_animated_emoji_clicked(const MessageContent *content, FullMessageId full_message_id, Td *td,
string emoji, string data) {
if (content->get_type() != MessageContentType::Text) {
return;
}
auto &text = static_cast<const MessageText *>(content)->text;
if (!text.entities.empty() || remove_emoji_modifiers(text.text) != emoji) {
return;
}
auto error = td->stickers_manager_->on_animated_emoji_message_clicked(emoji, full_message_id, std::move(data));
if (error.is_error()) {
LOG(WARNING) << "Failed to process animated emoji click with data \"" << data << "\": " << error;
}
}
bool need_reget_message_content(const MessageContent *content) {
CHECK(content != nullptr);
switch (content->get_type()) {

View File

@ -228,6 +228,9 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
void get_message_content_animated_emoji_click_sticker(const MessageContent *content, FullMessageId full_message_id,
Td *td, Promise<td_api::object_ptr<td_api::sticker>> &&promise);
void on_message_content_animated_emoji_clicked(const MessageContent *content, FullMessageId full_message_id, Td *td,
string emoji, string data);
bool need_reget_message_content(const MessageContent *content);
bool need_delay_message_content_notification(const MessageContent *content, UserId my_user_id);

View File

@ -7099,6 +7099,19 @@ void MessagesManager::on_user_dialog_action(DialogId dialog_id, MessageId top_th
}
}
{
auto clicking_info = action.get_clicking_animated_emoji_action_info();
if (!clicking_info.data.empty()) {
auto message_id = MessageId(ServerMessageId(clicking_info.message_id));
CHECK(message_id.is_valid());
if (date > G()->unix_time() - 10) {
on_animated_emoji_message_clicked({dialog_id, message_id}, user_id, std::move(clicking_info.emoji),
std::move(clicking_info.data));
}
return;
}
}
if (!td_->contacts_manager_->have_min_user(user_id)) {
LOG(DEBUG) << "Ignore " << action << " of unknown " << user_id;
return;
@ -19724,6 +19737,20 @@ void MessagesManager::click_animated_emoji_message(FullMessageId full_message_id
get_message_content_animated_emoji_click_sticker(m->content.get(), full_message_id, td_, std::move(promise));
}
void MessagesManager::on_animated_emoji_message_clicked(FullMessageId full_message_id, UserId user_id, string emoji,
string data) {
CHECK(full_message_id.get_message_id().is_server());
auto *m = get_message_force(full_message_id, "on_animated_emoji_message_clicked");
if (m == nullptr) {
return;
}
if (full_message_id.get_dialog_id().get_type() != DialogType::User ||
full_message_id.get_dialog_id().get_user_id() != user_id) {
return;
}
on_message_content_animated_emoji_clicked(m->content.get(), full_message_id, td_, std::move(emoji), std::move(data));
}
void MessagesManager::open_dialog(Dialog *d) {
DialogId dialog_id = d->dialog_id;
if (!have_input_peer(dialog_id, AccessRights::Read)) {

View File

@ -2962,6 +2962,8 @@ class MessagesManager final : public Actor {
Status can_import_messages(DialogId dialog_id);
void on_animated_emoji_message_clicked(FullMessageId full_message_id, UserId user_id, string emoji, string data);
void add_sponsored_dialog(const Dialog *d, DialogSource source);
void save_sponsored_dialog();

View File

@ -56,6 +56,7 @@
#include "td/utils/utf8.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <type_traits>
#include <unordered_set>
@ -1362,13 +1363,19 @@ void StickersManager::on_load_special_sticker_set(const SpecialStickerSetType &t
CHECK(sticker_set->was_loaded);
if (type.type_ == SpecialStickerSetType::animated_emoji_click()) {
auto pending_requests = std::move(pending_get_animated_emoji_click_stickers_);
auto pending_get_requests = std::move(pending_get_animated_emoji_click_stickers_);
reset_to_empty(pending_get_animated_emoji_click_stickers_);
for (auto &pending_request : pending_requests) {
for (auto &pending_request : pending_get_requests) {
choose_animated_emoji_click_sticker(sticker_set, std::move(pending_request.message_text_),
pending_request.full_message_id_, pending_request.start_time_,
std::move(pending_request.promise_));
}
auto pending_click_requests = std::move(pending_on_animated_emoji_message_clicked_);
reset_to_empty(pending_on_animated_emoji_message_clicked_);
for (auto &pending_request : pending_click_requests) {
send_update_animated_emoji_clicked(sticker_set, pending_request.emoji_, pending_request.full_message_id_,
std::move(pending_request.clicks_));
}
return;
}
@ -4034,7 +4041,7 @@ void StickersManager::choose_animated_emoji_click_sticker(const StickerSet *stic
for (auto sticker_id : sticker_set->sticker_ids) {
auto s = get_sticker(sticker_id);
CHECK(s != nullptr);
if (s->alt == message_text) {
if (remove_emoji_modifiers(s->alt) == message_text) {
auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
if (it != sticker_set->sticker_emojis_map_.end()) {
for (auto &emoji : it->second) {
@ -4103,6 +4110,115 @@ void StickersManager::flush_pending_animated_emoji_clicks() {
}));
}
Status StickersManager::on_animated_emoji_message_clicked(const string &emoji, FullMessageId full_message_id,
string data) {
TRY_RESULT(value, json_decode(data));
if (value.type() != JsonValue::Type::Object) {
return Status::Error("Expected an object");
}
auto &object = value.get_object();
TRY_RESULT(version, get_json_object_int_field(object, "v", false));
if (version != 1) {
return Status::OK();
}
TRY_RESULT(array_value, get_json_object_field(object, "a", JsonValue::Type::Array, false));
auto &array = array_value.get_array();
if (array.size() > 20) {
return Status::Error("Click array is too big");
}
vector<std::pair<int, double>> clicks;
for (auto &click : array) {
if (click.type() != JsonValue::Type::Object) {
return Status::Error("Expected clicks as JSON objects");
}
auto &click_object = click.get_object();
TRY_RESULT(index, get_json_object_int_field(click_object, "i", false));
if (index <= 0 || index > 9) {
return Status::Error("Wrong index");
}
TRY_RESULT(start_time, get_json_object_double_field(click_object, "t", false));
if (!clicks.empty()) {
if (!std::isfinite(start_time)) {
return Status::Error("Receive invalid start time");
}
if (start_time < clicks.back().second) {
return Status::Error("Non-monotonic start time");
}
if (start_time > clicks.back().second + 3) {
return Status::Error("Too big delay between clicks");
}
} else {
start_time = 0.0;
}
clicks.emplace_back(static_cast<int>(index), start_time);
}
auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click());
if (special_sticker_set.id_.is_valid()) {
auto sticker_set = get_sticker_set(special_sticker_set.id_);
CHECK(sticker_set != nullptr);
if (sticker_set->was_loaded) {
send_update_animated_emoji_clicked(sticker_set, emoji, full_message_id, std::move(clicks));
return Status::OK();
}
}
LOG(INFO) << "Waiting for an emoji click sticker set needed in " << full_message_id;
if (!special_sticker_set.is_being_loaded_) {
load_special_sticker_set(special_sticker_set);
}
PendingOnAnimatedEmojiClicked pending_request;
pending_request.emoji_ = emoji;
pending_request.full_message_id_ = full_message_id;
pending_request.clicks_ = std::move(clicks);
pending_on_animated_emoji_message_clicked_.push_back(std::move(pending_request));
return Status::OK();
}
void StickersManager::send_update_animated_emoji_clicked(const StickerSet *sticker_set, const string &emoji,
FullMessageId full_message_id,
vector<std::pair<int, double>> clicks) {
if (td_->messages_manager_->is_message_edited_recently(full_message_id, 2)) {
// includes deleted full_message_id
return;
}
if (!td_->messages_manager_->have_input_peer(full_message_id.get_dialog_id(), AccessRights::Read)) {
return;
}
std::unordered_map<int, FileId> sticker_ids;
for (auto sticker_id : sticker_set->sticker_ids) {
auto s = get_sticker(sticker_id);
CHECK(s != nullptr);
if (remove_emoji_modifiers(s->alt) == emoji) {
auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
if (it != sticker_set->sticker_emojis_map_.end()) {
for (auto &sticker_emoji : it->second) {
auto number = get_emoji_number(sticker_emoji);
if (number > 0) {
sticker_ids[number] = sticker_id;
}
}
}
}
}
vector<td_api::object_ptr<td_api::animatedEmojiClick>> result;
for (auto &click : clicks) {
auto sticker_id = sticker_ids[click.first];
if (!sticker_id.is_valid()) {
LOG(INFO) << "Failed to find sticker for animation " << click.first;
return;
}
result.push_back(td_api::make_object<td_api::animatedEmojiClick>(click.second, get_sticker_object(sticker_id)));
}
send_closure(G()->td(), &Td::send_update,
td_api::make_object<td_api::updateAnimatedEmojiMessageClicked>(
full_message_id.get_dialog_id().get(), full_message_id.get_message_id().get(), std::move(result)));
}
void StickersManager::view_featured_sticker_sets(const vector<StickerSetId> &sticker_set_ids) {
for (auto sticker_set_id : sticker_set_ids) {
auto set = get_sticker_set(sticker_set_id);

View File

@ -72,6 +72,8 @@ class StickersManager final : public Actor {
void get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id,
Promise<td_api::object_ptr<td_api::sticker>> &&promise);
Status on_animated_emoji_message_clicked(const string &emoji, FullMessageId full_message_id, string data);
void create_sticker(FileId file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions,
tl_object_ptr<telegram_api::documentAttributeSticker> sticker, bool is_animated,
MultiPromiseActor *load_data_multipromise_ptr);
@ -399,6 +401,12 @@ class StickersManager final : public Actor {
Promise<td_api::object_ptr<td_api::sticker>> promise_;
};
struct PendingOnAnimatedEmojiClicked {
string emoji_;
FullMessageId full_message_id_;
vector<std::pair<int, double>> clicks_;
};
struct SpecialStickerSet {
StickerSetId id_;
int64 access_hash_ = 0;
@ -590,6 +598,9 @@ class StickersManager final : public Actor {
void flush_pending_animated_emoji_clicks();
void send_update_animated_emoji_clicked(const StickerSet *sticker_set, const string &emoji,
FullMessageId full_message_id, vector<std::pair<int, double>> clicks);
td_api::object_ptr<td_api::updateDiceEmojis> get_update_dice_emojis_object() const;
void start_up() final;
@ -743,6 +754,7 @@ class StickersManager final : public Actor {
std::unordered_map<int64, unique_ptr<PendingSetStickerSetThumbnail>> pending_set_sticker_set_thumbnails_;
vector<PendingGetAnimatedEmojiClickSticker> pending_get_animated_emoji_click_stickers_;
vector<PendingOnAnimatedEmojiClicked> pending_on_animated_emoji_message_clicked_;
string last_clicked_animated_emoji_;
FullMessageId last_clicked_animated_emoji_full_message_id_;