From 2542ec5d936f3ad1b61473613e36252dbc4a0886 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 28 Nov 2023 05:30:16 +0300 Subject: [PATCH] Added list of public forwards for story statistics. --- Telegram/SourceFiles/api/api_statistics.cpp | 126 +++++++++++++++--- Telegram/SourceFiles/api/api_statistics.h | 13 +- Telegram/SourceFiles/data/data_statistics.h | 17 ++- .../info/statistics/info_statistics_common.h | 16 +-- .../info_statistics_inner_widget.cpp | 12 +- .../info_statistics_list_controllers.cpp | 24 ++-- .../info_statistics_list_controllers.h | 3 +- .../info_statistics_recent_message.cpp | 2 +- 8 files changed, 157 insertions(+), 56 deletions(-) diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 14b2b56b2..6655c618e 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "data/data_channel.h" -#include "data/data_peer.h" #include "data/data_session.h" #include "history/history.h" #include "main/main_session.h" @@ -352,7 +351,7 @@ Data::SupergroupStatistics Statistics::supergroupStats() const { PublicForwards::PublicForwards( not_null channel, - FullMsgId fullId) + Data::RecentPostId fullId) : StatisticsRequestSender(channel) , _fullId(fullId) { } @@ -360,9 +359,20 @@ PublicForwards::PublicForwards( void PublicForwards::request( const Data::PublicForwardsSlice::OffsetToken &token, Fn done) { - if (_requestId) { - return; + if (!_requestId) { + if (_fullId.messageId) { + requestMessage(token, std::move(done)); + } else if (_fullId.storyId) { + requestStory(token, std::move(done)); + } } +} + +void PublicForwards::requestMessage( + const Data::PublicForwardsSlice::OffsetToken &token, + Fn done) { + Expects(_fullId.messageId); + const auto offsetPeer = channel()->owner().peer(token.fullId.peer); const auto tlOffsetPeer = offsetPeer ? offsetPeer->input @@ -370,13 +380,13 @@ void PublicForwards::request( constexpr auto kLimit = tl::make_int(100); _requestId = makeRequest(MTPstats_GetMessagePublicForwards( channel()->inputChannel, - MTP_int(_fullId.msg), + MTP_int(_fullId.messageId.msg), MTP_int(token.rate), tlOffsetPeer, MTP_int(token.fullId.msg), kLimit )).done([=, channel = channel()](const MTPmessages_Messages &result) { - using Messages = QVector; + using Messages = QVector; _requestId = 0; auto nextToken = Data::PublicForwardsSlice::OffsetToken(); @@ -393,7 +403,7 @@ void PublicForwards::request( MessageFlags(), NewMessageType::Existing); nextToken.fullId = { peerId, msgId }; - result.push_back(nextToken.fullId); + result.push_back({ .messageId = nextToken.fullId }); } } } @@ -448,11 +458,79 @@ void PublicForwards::request( }).send(); } +void PublicForwards::requestStory( + const Data::PublicForwardsSlice::OffsetToken &token, + Fn done) { + Expects(_fullId.storyId); + + constexpr auto kLimit = tl::make_int(100); + _requestId = makeRequest(MTPstats_GetStoryPublicForwards( + channel()->input, + MTP_int(_fullId.storyId.story), + MTP_string(token.storyOffset), + kLimit + )).done([=, channel = channel()]( + const MTPstats_PublicForwards &tlForwards) { + using Messages = QVector; + _requestId = 0; + + const auto &data = tlForwards.data(); + + channel->owner().processUsers(data.vusers()); + channel->owner().processChats(data.vchats()); + + const auto nextToken = Data::PublicForwardsSlice::OffsetToken({ + .storyOffset = data.vnext_offset().value_or_empty(), + }); + + const auto allLoaded = nextToken.storyOffset.isEmpty() + || (nextToken.storyOffset == token.storyOffset); + const auto fullCount = data.vcount().v; + + auto recentList = Messages(); + for (const auto &tlForward : data.vforwards().v) { + tlForward.match([&](const MTPDpublicForwardMessage &data) { + const auto &message = data.vmessage(); + const auto msgId = IdFromMessage(message); + const auto peerId = PeerFromMessage(message); + const auto lastDate = DateFromMessage(message); + if (const auto peer = channel->owner().peerLoaded(peerId)) { + if (!lastDate) { + return; + } + channel->owner().addNewMessage( + message, + MessageFlags(), + NewMessageType::Existing); + recentList.push_back({ .messageId = { peerId, msgId } }); + } + }, [&](const MTPDpublicForwardStory &data) { + data.vstory().match([&](const MTPDstoryItem &d) { + recentList.push_back({ + .storyId = { peerFromMTP(data.vpeer()), d.vid().v } + }); + }, [](const auto &) { + }); + }); + } + + _lastTotal = std::max(_lastTotal, fullCount); + done({ + .list = std::move(recentList), + .total = _lastTotal, + .allLoaded = allLoaded, + .token = nextToken, + }); + }).fail([=] { + _requestId = 0; + }).send(); +} + MessageStatistics::MessageStatistics( not_null channel, FullMsgId fullId) : StatisticsRequestSender(channel) -, _publicForwards(channel, fullId) +, _publicForwards(channel, { .messageId = fullId }) , _fullId(fullId) { } @@ -460,7 +538,7 @@ MessageStatistics::MessageStatistics( not_null channel, FullStoryId storyId) : StatisticsRequestSender(channel) -, _publicForwards(channel, {}) +, _publicForwards(channel, { .storyId = storyId }) , _storyId(storyId) { } @@ -476,7 +554,7 @@ void MessageStatistics::request(Fn done) { const Data::StatisticalGraph &messageGraph, const Data::StatisticalGraph &reactionsGraph, const Data::StatisticsMessageInteractionInfo &info) { - _publicForwards.request({}, [=](Data::PublicForwardsSlice slice) { + const auto callback = [=](Data::PublicForwardsSlice slice) { const auto total = slice.total; _firstSlice = std::move(slice); done({ @@ -487,7 +565,8 @@ void MessageStatistics::request(Fn done) { .views = info.viewsCount, .reactions = info.reactionsCount, }); - }); + }; + _publicForwards.request({}, callback); }; const auto requestPrivateForwards = [=]( @@ -553,22 +632,27 @@ void MessageStatistics::request(Fn done) { MTP_vector(1, MTP_int(_storyId.story))) ).done([=](const MTPstories_Stories &result) { const auto &storyItem = result.data().vstories().v.front(); - storyItem.match([&](const MTPDstoryItem &data) { + auto info = storyItem.match([&](const MTPDstoryItem &data) { if (!data.vviews()) { - return; + return Data::StatisticsMessageInteractionInfo(); } const auto &tlViews = data.vviews()->data(); - done({ - .messageInteractionGraph = messageGraph, - .reactionsByEmotionGraph = reactionsGraph, - .publicForwards = -1, - .privateForwards = tlViews.vforwards_count().value_or(0), - .views = tlViews.vviews_count().v, - .reactions = tlViews.vreactions_count().value_or(0), - }); + return Data::StatisticsMessageInteractionInfo{ + .storyId = data.vid().v, + .viewsCount = tlViews.vviews_count().v, + .forwardsCount = tlViews.vforwards_count().value_or(0), + .reactionsCount = tlViews.vreactions_count().value_or(0), + }; }, [](const auto &) { + return Data::StatisticsMessageInteractionInfo(); }); + + requestFirstPublicForwards( + messageGraph, + reactionsGraph, + std::move(info)); }).fail([=](const MTP::Error &error) { + requestFirstPublicForwards(messageGraph, reactionsGraph, {}); }).send(); }; diff --git a/Telegram/SourceFiles/api/api_statistics.h b/Telegram/SourceFiles/api/api_statistics.h index 4df9ef4fb..d1b5b6624 100644 --- a/Telegram/SourceFiles/api/api_statistics.h +++ b/Telegram/SourceFiles/api/api_statistics.h @@ -68,14 +68,23 @@ private: class PublicForwards final : public StatisticsRequestSender { public: - PublicForwards(not_null channel, FullMsgId fullId); + PublicForwards( + not_null channel, + Data::RecentPostId fullId); void request( const Data::PublicForwardsSlice::OffsetToken &token, Fn done); private: - const FullMsgId _fullId; + void requestMessage( + const Data::PublicForwardsSlice::OffsetToken &token, + Fn done); + void requestStory( + const Data::PublicForwardsSlice::OffsetToken &token, + Fn done); + + const Data::RecentPostId _fullId; mtpRequestId _requestId = 0; int _lastTotal = 0; diff --git a/Telegram/SourceFiles/data/data_statistics.h b/Telegram/SourceFiles/data/data_statistics.h index 354ae1902..4c0f327b6 100644 --- a/Telegram/SourceFiles/data/data_statistics.h +++ b/Telegram/SourceFiles/data/data_statistics.h @@ -133,12 +133,27 @@ struct AnyStatistics final { Data::StoryStatistics story; }; +struct RecentPostId final { + FullMsgId messageId; + FullStoryId storyId; + + [[nodiscard]] bool valid() const { + return messageId || storyId; + } + explicit operator bool() const { + return valid(); + } + friend inline auto operator<=>(RecentPostId, RecentPostId) = default; + friend inline bool operator==(RecentPostId, RecentPostId) = default; +}; + struct PublicForwardsSlice final { struct OffsetToken final { int rate = 0; FullMsgId fullId; + QString storyOffset; }; - QVector list; + QVector list; int total = 0; bool allLoaded = false; OffsetToken token; diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_common.h b/Telegram/SourceFiles/info/statistics/info_statistics_common.h index c2fb5ccc5..caf3c4838 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_common.h +++ b/Telegram/SourceFiles/info/statistics/info_statistics_common.h @@ -11,23 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Info::Statistics { -struct RecentPostId final { - FullMsgId messageId; - FullStoryId storyId; - - [[nodiscard]] bool valid() const { - return messageId || storyId; - } - explicit operator bool() const { - return valid(); - } - friend inline auto operator<=>(RecentPostId, RecentPostId) = default; - friend inline bool operator==(RecentPostId, RecentPostId) = default; -}; - struct SavedState final { Data::AnyStatistics stats; - base::flat_map recentPostPreviews; + base::flat_map recentPostPreviews; Data::PublicForwardsSlice publicForwardsFirstSlice; int recentPostsExpanded = 0; }; diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp index 99b1dbe1c..3fba3ab22 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_inner_widget.cpp @@ -641,7 +641,7 @@ void InnerWidget::load() { .message = _contextId ? data : Data::StoryStatistics(), .story = _storyId ? data : Data::StoryStatistics(), }; - if (_contextId) { + if (_contextId || _storyId) { _state.publicForwardsFirstSlice = api->firstSlice(); } fill(); @@ -726,13 +726,14 @@ void InnerWidget::fill() { descriptor.peer, tr::lng_stats_inviters_title()); } - } else if (message) { + } else if (_state.stats.message || _state.stats.story) { + using namespace Data; AddPublicForwards( _state.publicForwardsFirstSlice, inner, [=](FullMsgId id) { _showRequests.fire({ .history = id }); }, descriptor.peer, - _contextId); + RecentPostId{ .messageId = _contextId, .storyId = _storyId }); } } @@ -762,7 +763,7 @@ void InnerWidget::fillRecentPosts() { messageWrap, rpl::never(), st::statisticsRecentPostButton)); - const auto fullRecentId = RecentPostId{ + const auto fullRecentId = Data::RecentPostId{ .messageId = maybeItem ? maybeItem->fullId() : FullMsgId(), .storyId = maybeStory ? maybeStory->fullId() : FullStoryId(), }; @@ -886,7 +887,8 @@ void InnerWidget::restoreState(not_null memento) { _state = memento->state(); if (_state.stats.channel || _state.stats.supergroup - || _state.stats.message) { + || _state.stats.message + || _state.stats.story) { fill(); } else { load(); diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 0dd0d6f97..cdaffc183 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -91,11 +91,11 @@ void AddSubtitle( return resultText; } -struct Descriptor final { +struct PublicForwardsDescriptor final { Data::PublicForwardsSlice firstSlice; Fn showPeerHistory; not_null peer; - FullMsgId contextId; + Data::RecentPostId contextId; }; struct MembersDescriptor final { @@ -229,7 +229,7 @@ void MembersController::rowClicked(not_null row) { class PublicForwardsController final : public PeerListController { public: - explicit PublicForwardsController(Descriptor d); + explicit PublicForwardsController(PublicForwardsDescriptor d); Main::Session &session() const override; void prepare() override; @@ -251,7 +251,7 @@ private: }; -PublicForwardsController::PublicForwardsController(Descriptor d) +PublicForwardsController::PublicForwardsController(PublicForwardsDescriptor d) : _session(&d.peer->session()) , _showPeerHistory(std::move(d.showPeerHistory)) , _api(d.peer->asChannel(), d.contextId) @@ -282,8 +282,11 @@ void PublicForwardsController::applySlice( _apiToken = slice.token; for (const auto &item : slice.list) { - if (const auto peer = session().data().peerLoaded(item.peer)) { - appendRow(peer, item.msg); + // TODO support stories. + if (const auto fullId = item.messageId) { + if (const auto peer = session().data().peerLoaded(fullId.peer)) { + appendRow(peer, fullId.msg); + } } } delegate()->peerListRefreshRows(); @@ -617,23 +620,24 @@ void AddPublicForwards( not_null container, Fn showPeerHistory, not_null peer, - FullMsgId contextId) { + Data::RecentPostId contextId) { if (!peer->isChannel()) { return; } struct State final { - State(Descriptor d) : controller(std::move(d)) { + State(PublicForwardsDescriptor d) : controller(std::move(d)) { } PeerListContentDelegateSimple delegate; PublicForwardsController controller; }; - const auto state = container->lifetime().make_state(Descriptor{ + auto d = PublicForwardsDescriptor{ firstSlice, std::move(showPeerHistory), peer, contextId, - }); + }; + const auto state = container->lifetime().make_state(std::move(d)); if (const auto total = firstSlice.total; total > 0) { AddSubtitle( diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.h b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.h index de4bc5f2f..0696062c7 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.h +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.h @@ -17,6 +17,7 @@ namespace Data { struct Boost; struct BoostsListSlice; struct PublicForwardsSlice; +struct RecentPostId; struct SupergroupStatistics; } // namespace Data @@ -27,7 +28,7 @@ void AddPublicForwards( not_null container, Fn showPeerHistory, not_null peer, - FullMsgId contextId); + Data::RecentPostId contextId); void AddMembersList( Data::SupergroupStatistics data, diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp index e5b7e61ff..c09038bee 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_recent_message.cpp @@ -349,7 +349,7 @@ void MessagePreview::paintEvent(QPaintEvent *e) { void MessagePreview::saveState(SavedState &state) const { if (!_lifetimeDownload) { - const auto fullId = RecentPostId{ _messageId, _storyId }; + const auto fullId = Data::RecentPostId{ _messageId, _storyId }; state.recentPostPreviews[fullId] = _preview; } }