From c2bc02928bfdf1c643e03c151396150d38166e22 Mon Sep 17 00:00:00 2001 From: AlexeyZavar Date: Wed, 10 Dec 2025 22:45:24 +0300 Subject: [PATCH] feat: support premium emoji for non-premium users --- Telegram/SourceFiles/api/api_editing.cpp | 7 ++- Telegram/SourceFiles/api/api_sending.cpp | 7 ++- Telegram/SourceFiles/apiwrap.cpp | 15 +++--- .../ayu/utils/telegram_helpers.cpp | 51 +++++++++++++++++++ .../SourceFiles/ayu/utils/telegram_helpers.h | 2 + Telegram/SourceFiles/data/data_histories.cpp | 8 ++- .../data/stickers/data_custom_emoji.cpp | 4 ++ Telegram/SourceFiles/history/history_item.cpp | 33 ++++++++++-- .../history/history_item_helpers.cpp | 10 +++- .../SourceFiles/storage/localimageloader.cpp | 7 ++- Telegram/lib_ui | 2 +- 11 files changed, 126 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp index c3afa0b728..a36089d958 100644 --- a/Telegram/SourceFiles/api/api_editing.cpp +++ b/Telegram/SourceFiles/api/api_editing.cpp @@ -28,6 +28,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_response.h" #include "boxes/abstract_box.h" // Ui::show(). +// AyuGram includes +#include "ayu/utils/telegram_helpers.h" + + namespace Api { namespace { @@ -262,9 +266,10 @@ mtpRequestId EditMessage( const auto api = &session->api(); const auto text = textWithEntities.text; + const auto textNormalized = reverseLocalPremiumEmoji(textWithEntities, item->history()); const auto sentEntities = EntitiesToMTP( session, - textWithEntities.entities, + textNormalized.entities, ConvertOption::SkipLocal); const auto media = item->media(); diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index a77cc1beb4..79d88b99ee 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -34,6 +34,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "apiwrap.h" +// AyuGram includes +#include "ayu/utils/telegram_helpers.h" + + namespace Api { namespace { @@ -197,9 +201,10 @@ void SendExistingMedia( TextUtilities::ConvertTextTagsToEntities(message.textWithTags.tags) }; TextUtilities::Trim(caption); + const auto captionNormalized = reverseLocalPremiumEmoji(caption, history); auto sentEntities = EntitiesToMTP( session, - caption.entities, + captionNormalized.entities, ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_SendMedia::Flag::f_entities; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 10b077e301..ebce7c7a71 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -4126,9 +4126,10 @@ void ApiWrap::sendMessage( sendFlags |= MTPmessages_SendMessage::Flag::f_silent; mediaFlags |= MTPmessages_SendMedia::Flag::f_silent; } + const auto sendingNormalized = reverseLocalPremiumEmoji(sending, history); const auto sentEntities = Api::EntitiesToMTP( _session, - sending.entities, + sendingNormalized.entities, Api::ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_SendMessage::Flag::f_entities; @@ -4199,8 +4200,6 @@ void ApiWrap::sendMessage( draftMonoforumPeerId, Api::UnixtimeFromMsgId(response.outerMsgId)); } - - AyuWorker::markAsOnline(_session); }; const auto fail = [=]( const MTP::Error &error, @@ -4558,9 +4557,10 @@ void ApiWrap::sendMediaWithRandomId( auto caption = item->originalText(); TextUtilities::Trim(caption); + const auto captionNormalized = reverseLocalPremiumEmoji(caption, history); auto sentEntities = Api::EntitiesToMTP( _session, - caption.entities, + captionNormalized.entities, Api::ConvertOption::SkipLocal); const auto updateRecentStickers = Api::HasAttachedStickers(media); @@ -4622,8 +4622,6 @@ void ApiWrap::sendMediaWithRandomId( if (updateRecentStickers) { requestRecentStickers(std::nullopt, true); } - - AyuWorker::markAsOnline(_session); }, [=](const MTP::Error &error, const MTP::Response &response) { if (done) done(false); sendMessageFail(error, peer, randomId, itemId); @@ -4651,9 +4649,10 @@ void ApiWrap::sendMultiPaidMedia( auto caption = item->originalText(); TextUtilities::Trim(caption); + const auto captionNormalized = reverseLocalPremiumEmoji(caption, history); auto sentEntities = Api::EntitiesToMTP( _session, - caption.entities, + captionNormalized.entities, Api::ConvertOption::SkipLocal); const auto starsPaid = std::min( peer->starsPerMessageChecked(), @@ -4845,8 +4844,6 @@ void ApiWrap::sendAlbumIfReady(not_null album) { MTP_long(starsPaid) ), [=](const MTPUpdates &result, const MTP::Response &response) { _sendingAlbums.remove(groupId); - - AyuWorker::markAsOnline(_session); }, [=](const MTP::Error &error, const MTP::Response &response) { if (const auto album = _sendingAlbums.take(groupId)) { for (const auto &item : (*album)->items) { diff --git a/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp b/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp index 129c51e7ca..cf57c9fa0f 100644 --- a/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp +++ b/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp @@ -48,11 +48,14 @@ #include "data/data_chat.h" #include "data/data_poll.h" #include "data/data_saved_sublist.h" +#include "data/stickers/data_custom_emoji.h" +#include "data/stickers/data_stickers.h" #include "lang/lang_keys.h" #include "lang/lang_text_entity.h" #include "main/main_domain.h" #include "styles/style_ayu_styles.h" #include "ui/text/text_utilities.h" +#include "ui/text/text_entity.h" #include "ui/toast/toast.h" #include "unicode/regex.h" @@ -921,6 +924,54 @@ bool mediaDownloadable(const Data::Media *media) { return true; } +TextWithEntities reverseLocalPremiumEmoji(const TextWithEntities &text, not_null history, bool isForQuote) { + if (text.empty()) { + return text; + } + + const auto channel = history->peer->asChannel(); + const auto hasCustomEmoji = channel && channel->mgInfo && channel->mgInfo->emojiSet.id; + const auto sets = hasCustomEmoji && channel + ? &channel->owner().stickers().sets() + : nullptr; + const auto set = sets + ? sets->find(channel->mgInfo->emojiSet.id) + : decltype(sets->cend()){}; + const auto emojiAllowed = [=](const EntityInText& entity) + { + if (!sets || set == sets->cend()) { + return false; + } + const auto emojiId = Data::ParseCustomEmojiData(entity.data()); + if (!emojiId) { + return false; + } + const auto &emojiMap = set->second->emoji; + for (const auto &[emoji, documents] : emojiMap) { + for (const auto &document : documents) { + if (document->id == emojiId) { + return true; + } + } + } + return false; + }; + + auto result = text; + for (auto &entity : result.entities) { + if (entity.type() == EntityType::CustomEmoji && entity.isLocal()) { + if (isForQuote || !history->peer->isSelf() && !(history->owner().session().user()->flags() & UserDataFlag::Premium) && !emojiAllowed(entity)) { + entity = EntityInText( + EntityType::CustomUrl, + entity.offset(), + entity.length(), + u"tg://emoji?id="_q + entity.data()); + } + } + } + return result; +} + void resolveAllChats(const std::map &peers) { auto session = currentSession(); diff --git a/Telegram/SourceFiles/ayu/utils/telegram_helpers.h b/Telegram/SourceFiles/ayu/utils/telegram_helpers.h index 456c399d0a..a74e3dd0f8 100644 --- a/Telegram/SourceFiles/ayu/utils/telegram_helpers.h +++ b/Telegram/SourceFiles/ayu/utils/telegram_helpers.h @@ -98,6 +98,8 @@ ID getUserIdFromPackId(uint64 id); TextWithTags extractText(not_null item); bool mediaDownloadable(const Data::Media* media); +TextWithEntities reverseLocalPremiumEmoji(const TextWithEntities &text, not_null history, bool isForQuote = false); + void resolveAllChats(const std::map &peers); not_null currentSession(); diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index ef3ed9aa58..12caa53aca 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -33,6 +33,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL // AyuGram includes #include "ayu/ayu_settings.h" +#include "ayu/ayu_worker.h" +#include "ayu/utils/telegram_helpers.h" namespace Data { @@ -78,15 +80,16 @@ MTPInputReplyTo ReplyToForMTP( const auto external = replyTo.messageId && (replyTo.messageId.peer != history->peer->id || replyingToTopicId != replyToTopicId); + const auto textNormalized = reverseLocalPremiumEmoji(replyTo.quote, history); const auto quoteEntities = Api::EntitiesToMTP( &history->session(), - replyTo.quote.entities, + textNormalized.entities, Api::ConvertOption::SkipLocal); using Flag = MTPDinputReplyToMessage::Flag; return MTP_inputReplyToMessage( MTP_flags((replyTo.topicRootId ? Flag::f_top_msg_id : Flag()) | (external ? Flag::f_reply_to_peer_id : Flag()) - | (replyTo.quote.text.isEmpty() + | (textNormalized.text.isEmpty() ? Flag() : (Flag::f_quote_text | Flag::f_quote_offset)) | (replyToMonoforumPeerId @@ -1139,6 +1142,7 @@ int Histories::sendPreparedMessage( const MTPUpdates &result, const MTP::Response &response) { api->applyUpdates(result, randomId); + AyuWorker::markAsOnline(&history->owner().session()); done(result, response); finish(); }).fail([=]( diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 840249d8ef..ae87079000 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -1040,6 +1040,10 @@ TextWithEntities SingleCustomEmoji(not_null document) { bool AllowEmojiWithoutPremium( not_null peer, DocumentData *exactEmoji) { + if (true) { // AyuGram: allow all premium emojis (via tg://emoji?id=...) + return true; + } + if (peer->isSelf()) { return true; } else if (!exactEmoji) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index c3f4fc81b5..b40e597691 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -76,6 +76,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ayu/ayu_state.h" #include "ayu/features/message_shot/message_shot.h" #include "ayu/utils/telegram_helpers.h" +#include "ui/emoji_config.h" namespace { @@ -3916,7 +3917,33 @@ FullReplyTo HistoryItem::replyTo() const { } void HistoryItem::setText(const TextWithEntities &textWithEntities) { - for (const auto &entity : textWithEntities.entities) { + auto text = textWithEntities; + + static const auto kEmojiLinkRegex = QRegularExpression( + QStringLiteral("^tg://emoji\\?id=(\\d+)$")); + for (auto &entity : text.entities) { + if (entity.type() == EntityType::CustomUrl) { + const auto match = kEmojiLinkRegex.match(entity.data()); + if (match.hasMatch()) { + const auto entityText = text.text.mid( + entity.offset(), + entity.length()); + int emojiLength = 0; + const auto emoji = Ui::Emoji::Find(entityText, &emojiLength); + if (emoji && emojiLength == entityText.size()) { + const auto emojiId = match.captured(1); + entity = EntityInText( + EntityType::CustomEmoji, + entity.offset(), + entity.length(), + emojiId); + entity.setLocal(); + } + } + } + } + + for (const auto &entity : text.entities) { auto type = entity.type(); if (type == EntityType::Url || type == EntityType::CustomUrl @@ -3927,9 +3954,9 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) { break; } } - setTextValue((_media && _media->consumeMessageText(textWithEntities)) + setTextValue((_media && _media->consumeMessageText(text)) ? TextWithEntities() - : std::move(textWithEntities)); + : std::move(text)); } void HistoryItem::setTextValue(TextWithEntities text, bool force) { diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 5877b64139..763deda338 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL // AyuGram includes #include "ayu/ayu_settings.h" +#include "ayu/utils/telegram_helpers.h" namespace { @@ -645,6 +646,10 @@ bool LookupReplyIsTopicPost(HistoryItem *replyTo) { TextWithEntities DropDisallowedCustomEmoji( not_null to, TextWithEntities text) { + if (true) { // AyuGram: allow all premium emojis (via tg://emoji?id=...) + return text; + } + if (to->session().premium() || to->isSelf()) { return text; } @@ -891,15 +896,16 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) { ? PeerId() : replyTo.messageId.peer; const auto replyToTop = LookupReplyToTop(action.history, replyTo); + const auto quoteNormalized = reverseLocalPremiumEmoji(replyTo.quote, action.history, true); auto quoteEntities = Api::EntitiesToMTP( &action.history->session(), - replyTo.quote.entities, + quoteNormalized.entities, Api::ConvertOption::SkipLocal); return MTP_messageReplyHeader( MTP_flags(Flag::f_reply_to_msg_id | (replyToTop ? Flag::f_reply_to_top_id : Flag()) | (externalPeerId ? Flag::f_reply_to_peer_id : Flag()) - | (replyTo.quote.empty() + | (quoteNormalized.empty() ? Flag() : (Flag::f_quote | Flag::f_quote_text diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 642f1f56fd..09880b7cf8 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -42,6 +42,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +// AyuGram includes +#include "ayu/utils/telegram_helpers.h" + + namespace { constexpr auto kThumbnailQuality = 95; @@ -149,9 +153,10 @@ struct PreparedFileThumbnail { uint64 randomId) { auto caption = item->originalText(); TextUtilities::Trim(caption); + const auto captionNormalized = reverseLocalPremiumEmoji(caption, item->history()); auto sentEntities = Api::EntitiesToMTP( &item->history()->session(), - caption.entities, + captionNormalized.entities, Api::ConvertOption::SkipLocal); const auto flags = !sentEntities.v.isEmpty() ? MTPDinputSingleMedia::Flag::f_entities diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 5bc7b6fb5b..7f7c2518c0 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 5bc7b6fb5ba36a2735ae4d158cdfa81f04782f19 +Subproject commit 7f7c2518c0970120922ba9abe6400ee78450525f