mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-11 11:47:09 +02:00
795 lines
24 KiB
C++
795 lines
24 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "api/api_statistics.h"
|
|
|
|
#include "api/api_statistics_data_deserialize.h"
|
|
#include "apiwrap.h"
|
|
#include "base/unixtime.h"
|
|
#include "data/data_channel.h"
|
|
#include "data/data_session.h"
|
|
#include "data/data_stories.h"
|
|
#include "data/data_story.h"
|
|
#include "history/history.h"
|
|
#include "main/main_session.h"
|
|
|
|
namespace Api {
|
|
namespace {
|
|
|
|
[[nodiscard]] Data::StatisticalValue StatisticalValueFromTL(
|
|
const MTPStatsAbsValueAndPrev &tl) {
|
|
const auto current = tl.data().vcurrent().v;
|
|
const auto previous = tl.data().vprevious().v;
|
|
return Data::StatisticalValue{
|
|
.value = current,
|
|
.previousValue = previous,
|
|
.growthRatePercentage = previous
|
|
? std::abs((current - previous) / float64(previous) * 100.)
|
|
: 0,
|
|
};
|
|
}
|
|
|
|
[[nodiscard]] Data::ChannelStatistics ChannelStatisticsFromTL(
|
|
const MTPDstats_broadcastStats &data) {
|
|
const auto &tlUnmuted = data.venabled_notifications().data();
|
|
const auto unmuted = (!tlUnmuted.vtotal().v)
|
|
? 0.
|
|
: std::clamp(
|
|
tlUnmuted.vpart().v / tlUnmuted.vtotal().v * 100.,
|
|
0.,
|
|
100.);
|
|
using Recent = MTPPostInteractionCounters;
|
|
auto recentMessages = ranges::views::all(
|
|
data.vrecent_posts_interactions().v
|
|
) | ranges::views::transform([&](const Recent &tl) {
|
|
return tl.match([&](const MTPDpostInteractionCountersStory &data) {
|
|
return Data::StatisticsMessageInteractionInfo{
|
|
.storyId = data.vstory_id().v,
|
|
.viewsCount = data.vviews().v,
|
|
.forwardsCount = data.vforwards().v,
|
|
.reactionsCount = data.vreactions().v,
|
|
};
|
|
}, [&](const MTPDpostInteractionCountersMessage &data) {
|
|
return Data::StatisticsMessageInteractionInfo{
|
|
.messageId = data.vmsg_id().v,
|
|
.viewsCount = data.vviews().v,
|
|
.forwardsCount = data.vforwards().v,
|
|
.reactionsCount = data.vreactions().v,
|
|
};
|
|
});
|
|
}) | ranges::to_vector;
|
|
|
|
return {
|
|
.startDate = data.vperiod().data().vmin_date().v,
|
|
.endDate = data.vperiod().data().vmax_date().v,
|
|
|
|
.memberCount = StatisticalValueFromTL(data.vfollowers()),
|
|
.meanViewCount = StatisticalValueFromTL(data.vviews_per_post()),
|
|
.meanShareCount = StatisticalValueFromTL(data.vshares_per_post()),
|
|
.meanReactionCount = StatisticalValueFromTL(
|
|
data.vreactions_per_post()),
|
|
|
|
.meanStoryViewCount = StatisticalValueFromTL(
|
|
data.vviews_per_story()),
|
|
.meanStoryShareCount = StatisticalValueFromTL(
|
|
data.vshares_per_story()),
|
|
.meanStoryReactionCount = StatisticalValueFromTL(
|
|
data.vreactions_per_story()),
|
|
|
|
.enabledNotificationsPercentage = unmuted,
|
|
|
|
.memberCountGraph = StatisticalGraphFromTL(
|
|
data.vgrowth_graph()),
|
|
|
|
.joinGraph = StatisticalGraphFromTL(
|
|
data.vfollowers_graph()),
|
|
|
|
.muteGraph = StatisticalGraphFromTL(
|
|
data.vmute_graph()),
|
|
|
|
.viewCountByHourGraph = StatisticalGraphFromTL(
|
|
data.vtop_hours_graph()),
|
|
|
|
.viewCountBySourceGraph = StatisticalGraphFromTL(
|
|
data.vviews_by_source_graph()),
|
|
|
|
.joinBySourceGraph = StatisticalGraphFromTL(
|
|
data.vnew_followers_by_source_graph()),
|
|
|
|
.languageGraph = StatisticalGraphFromTL(
|
|
data.vlanguages_graph()),
|
|
|
|
.messageInteractionGraph = StatisticalGraphFromTL(
|
|
data.vinteractions_graph()),
|
|
|
|
.instantViewInteractionGraph = StatisticalGraphFromTL(
|
|
data.viv_interactions_graph()),
|
|
|
|
.reactionsByEmotionGraph = StatisticalGraphFromTL(
|
|
data.vreactions_by_emotion_graph()),
|
|
|
|
.storyInteractionsGraph = StatisticalGraphFromTL(
|
|
data.vstory_interactions_graph()),
|
|
|
|
.storyReactionsByEmotionGraph = StatisticalGraphFromTL(
|
|
data.vstory_reactions_by_emotion_graph()),
|
|
|
|
.recentMessageInteractions = std::move(recentMessages),
|
|
};
|
|
}
|
|
|
|
[[nodiscard]] Data::SupergroupStatistics SupergroupStatisticsFromTL(
|
|
const MTPDstats_megagroupStats &data) {
|
|
using Senders = MTPStatsGroupTopPoster;
|
|
using Administrators = MTPStatsGroupTopAdmin;
|
|
using Inviters = MTPStatsGroupTopInviter;
|
|
|
|
auto topSenders = ranges::views::all(
|
|
data.vtop_posters().v
|
|
) | ranges::views::transform([&](const Senders &tl) {
|
|
return Data::StatisticsMessageSenderInfo{
|
|
.userId = UserId(tl.data().vuser_id().v),
|
|
.sentMessageCount = tl.data().vmessages().v,
|
|
.averageCharacterCount = tl.data().vavg_chars().v,
|
|
};
|
|
}) | ranges::to_vector;
|
|
auto topAdministrators = ranges::views::all(
|
|
data.vtop_admins().v
|
|
) | ranges::views::transform([&](const Administrators &tl) {
|
|
return Data::StatisticsAdministratorActionsInfo{
|
|
.userId = UserId(tl.data().vuser_id().v),
|
|
.deletedMessageCount = tl.data().vdeleted().v,
|
|
.bannedUserCount = tl.data().vkicked().v,
|
|
.restrictedUserCount = tl.data().vbanned().v,
|
|
};
|
|
}) | ranges::to_vector;
|
|
auto topInviters = ranges::views::all(
|
|
data.vtop_inviters().v
|
|
) | ranges::views::transform([&](const Inviters &tl) {
|
|
return Data::StatisticsInviterInfo{
|
|
.userId = UserId(tl.data().vuser_id().v),
|
|
.addedMemberCount = tl.data().vinvitations().v,
|
|
};
|
|
}) | ranges::to_vector;
|
|
|
|
return {
|
|
.startDate = data.vperiod().data().vmin_date().v,
|
|
.endDate = data.vperiod().data().vmax_date().v,
|
|
|
|
.memberCount = StatisticalValueFromTL(data.vmembers()),
|
|
.messageCount = StatisticalValueFromTL(data.vmessages()),
|
|
.viewerCount = StatisticalValueFromTL(data.vviewers()),
|
|
.senderCount = StatisticalValueFromTL(data.vposters()),
|
|
|
|
.memberCountGraph = StatisticalGraphFromTL(
|
|
data.vgrowth_graph()),
|
|
|
|
.joinGraph = StatisticalGraphFromTL(
|
|
data.vmembers_graph()),
|
|
|
|
.joinBySourceGraph = StatisticalGraphFromTL(
|
|
data.vnew_members_by_source_graph()),
|
|
|
|
.languageGraph = StatisticalGraphFromTL(
|
|
data.vlanguages_graph()),
|
|
|
|
.messageContentGraph = StatisticalGraphFromTL(
|
|
data.vmessages_graph()),
|
|
|
|
.actionGraph = StatisticalGraphFromTL(
|
|
data.vactions_graph()),
|
|
|
|
.dayGraph = StatisticalGraphFromTL(
|
|
data.vtop_hours_graph()),
|
|
|
|
.weekGraph = StatisticalGraphFromTL(
|
|
data.vweekdays_graph()),
|
|
|
|
.topSenders = std::move(topSenders),
|
|
.topAdministrators = std::move(topAdministrators),
|
|
.topInviters = std::move(topInviters),
|
|
};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Statistics::Statistics(not_null<ChannelData*> channel)
|
|
: StatisticsRequestSender(channel) {
|
|
}
|
|
|
|
rpl::producer<rpl::no_value, QString> Statistics::request() {
|
|
return [=](auto consumer) {
|
|
auto lifetime = rpl::lifetime();
|
|
|
|
if (!channel()->isMegagroup()) {
|
|
makeRequest(MTPstats_GetBroadcastStats(
|
|
MTP_flags(MTPstats_GetBroadcastStats::Flags(0)),
|
|
channel()->inputChannel
|
|
)).done([=](const MTPstats_BroadcastStats &result) {
|
|
_channelStats = ChannelStatisticsFromTL(result.data());
|
|
consumer.put_done();
|
|
}).fail([=](const MTP::Error &error) {
|
|
consumer.put_error_copy(error.type());
|
|
}).send();
|
|
} else {
|
|
makeRequest(MTPstats_GetMegagroupStats(
|
|
MTP_flags(MTPstats_GetMegagroupStats::Flags(0)),
|
|
channel()->inputChannel
|
|
)).done([=](const MTPstats_MegagroupStats &result) {
|
|
const auto &data = result.data();
|
|
_supergroupStats = SupergroupStatisticsFromTL(data);
|
|
channel()->owner().processUsers(data.vusers());
|
|
consumer.put_done();
|
|
}).fail([=](const MTP::Error &error) {
|
|
consumer.put_error_copy(error.type());
|
|
}).send();
|
|
}
|
|
|
|
return lifetime;
|
|
};
|
|
}
|
|
|
|
Statistics::GraphResult Statistics::requestZoom(
|
|
const QString &token,
|
|
float64 x) {
|
|
return [=](auto consumer) {
|
|
auto lifetime = rpl::lifetime();
|
|
const auto wasEmpty = _zoomDeque.empty();
|
|
_zoomDeque.push_back([=] {
|
|
makeRequest(MTPstats_LoadAsyncGraph(
|
|
MTP_flags(x
|
|
? MTPstats_LoadAsyncGraph::Flag::f_x
|
|
: MTPstats_LoadAsyncGraph::Flag(0)),
|
|
MTP_string(token),
|
|
MTP_long(x)
|
|
)).done([=](const MTPStatsGraph &result) {
|
|
consumer.put_next(StatisticalGraphFromTL(result));
|
|
consumer.put_done();
|
|
if (!_zoomDeque.empty()) {
|
|
_zoomDeque.pop_front();
|
|
if (!_zoomDeque.empty()) {
|
|
_zoomDeque.front()();
|
|
}
|
|
}
|
|
}).fail([=](const MTP::Error &error) {
|
|
consumer.put_error_copy(error.type());
|
|
}).send();
|
|
});
|
|
if (wasEmpty) {
|
|
_zoomDeque.front()();
|
|
}
|
|
|
|
return lifetime;
|
|
};
|
|
}
|
|
|
|
Data::ChannelStatistics Statistics::channelStats() const {
|
|
return _channelStats;
|
|
}
|
|
|
|
Data::SupergroupStatistics Statistics::supergroupStats() const {
|
|
return _supergroupStats;
|
|
}
|
|
|
|
PublicForwards::PublicForwards(
|
|
not_null<ChannelData*> channel,
|
|
Data::RecentPostId fullId)
|
|
: StatisticsRequestSender(channel)
|
|
, _fullId(fullId) {
|
|
}
|
|
|
|
void PublicForwards::request(
|
|
const Data::PublicForwardsSlice::OffsetToken &token,
|
|
Fn<void(Data::PublicForwardsSlice)> done) {
|
|
if (_requestId) {
|
|
return;
|
|
}
|
|
const auto channel = StatisticsRequestSender::channel();
|
|
const auto processResult = [=](const MTPstats_PublicForwards &tl) {
|
|
using Messages = QVector<Data::RecentPostId>;
|
|
_requestId = 0;
|
|
|
|
const auto &data = tl.data();
|
|
auto &owner = channel->owner();
|
|
|
|
owner.processUsers(data.vusers());
|
|
owner.processChats(data.vchats());
|
|
|
|
const auto nextToken = data.vnext_offset()
|
|
? qs(*data.vnext_offset())
|
|
: Data::PublicForwardsSlice::OffsetToken();
|
|
|
|
const auto fullCount = data.vcount().v;
|
|
|
|
auto recentList = Messages(data.vforwards().v.size());
|
|
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 = owner.peerLoaded(peerId)) {
|
|
if (!lastDate) {
|
|
return;
|
|
}
|
|
owner.addNewMessage(
|
|
message,
|
|
MessageFlags(),
|
|
NewMessageType::Existing);
|
|
recentList.push_back({ .messageId = { peerId, msgId } });
|
|
}
|
|
}, [&](const MTPDpublicForwardStory &data) {
|
|
const auto story = owner.stories().applySingle(
|
|
peerFromMTP(data.vpeer()),
|
|
data.vstory());
|
|
if (story) {
|
|
recentList.push_back({ .storyId = story->fullId() });
|
|
}
|
|
});
|
|
}
|
|
|
|
const auto allLoaded = nextToken.isEmpty() || (nextToken == token);
|
|
_lastTotal = std::max(_lastTotal, fullCount);
|
|
done({
|
|
.list = std::move(recentList),
|
|
.total = _lastTotal,
|
|
.allLoaded = allLoaded,
|
|
.token = nextToken,
|
|
});
|
|
};
|
|
|
|
constexpr auto kLimit = tl::make_int(100);
|
|
if (_fullId.messageId) {
|
|
_requestId = makeRequest(MTPstats_GetMessagePublicForwards(
|
|
channel->inputChannel,
|
|
MTP_int(_fullId.messageId.msg),
|
|
MTP_string(token),
|
|
kLimit
|
|
)).done(processResult).fail([=] { _requestId = 0; }).send();
|
|
} else if (_fullId.storyId) {
|
|
_requestId = makeRequest(MTPstats_GetStoryPublicForwards(
|
|
channel->input,
|
|
MTP_int(_fullId.storyId.story),
|
|
MTP_string(token),
|
|
kLimit
|
|
)).done(processResult).fail([=] { _requestId = 0; }).send();
|
|
}
|
|
}
|
|
|
|
MessageStatistics::MessageStatistics(
|
|
not_null<ChannelData*> channel,
|
|
FullMsgId fullId)
|
|
: StatisticsRequestSender(channel)
|
|
, _publicForwards(channel, { .messageId = fullId })
|
|
, _fullId(fullId) {
|
|
}
|
|
|
|
MessageStatistics::MessageStatistics(
|
|
not_null<ChannelData*> channel,
|
|
FullStoryId storyId)
|
|
: StatisticsRequestSender(channel)
|
|
, _publicForwards(channel, { .storyId = storyId })
|
|
, _storyId(storyId) {
|
|
}
|
|
|
|
Data::PublicForwardsSlice MessageStatistics::firstSlice() const {
|
|
return _firstSlice;
|
|
}
|
|
|
|
void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
|
|
if (channel()->isMegagroup()) {
|
|
return;
|
|
}
|
|
const auto requestFirstPublicForwards = [=](
|
|
const Data::StatisticalGraph &messageGraph,
|
|
const Data::StatisticalGraph &reactionsGraph,
|
|
const Data::StatisticsMessageInteractionInfo &info) {
|
|
const auto callback = [=](Data::PublicForwardsSlice slice) {
|
|
const auto total = slice.total;
|
|
_firstSlice = std::move(slice);
|
|
done({
|
|
.messageInteractionGraph = messageGraph,
|
|
.reactionsByEmotionGraph = reactionsGraph,
|
|
.publicForwards = total,
|
|
.privateForwards = info.forwardsCount - total,
|
|
.views = info.viewsCount,
|
|
.reactions = info.reactionsCount,
|
|
});
|
|
};
|
|
_publicForwards.request({}, callback);
|
|
};
|
|
|
|
const auto requestPrivateForwards = [=](
|
|
const Data::StatisticalGraph &messageGraph,
|
|
const Data::StatisticalGraph &reactionsGraph) {
|
|
api().request(MTPchannels_GetMessages(
|
|
channel()->inputChannel,
|
|
MTP_vector<MTPInputMessage>(
|
|
1,
|
|
MTP_inputMessageID(MTP_int(_fullId.msg))))
|
|
).done([=](const MTPmessages_Messages &result) {
|
|
const auto process = [&](const MTPVector<MTPMessage> &messages) {
|
|
const auto &message = messages.v.front();
|
|
return message.match([&](const MTPDmessage &data) {
|
|
auto reactionsCount = 0;
|
|
if (const auto tlReactions = data.vreactions()) {
|
|
const auto &tlCounts = tlReactions->data().vresults();
|
|
for (const auto &tlCount : tlCounts.v) {
|
|
reactionsCount += tlCount.data().vcount().v;
|
|
}
|
|
}
|
|
return Data::StatisticsMessageInteractionInfo{
|
|
.messageId = IdFromMessage(message),
|
|
.viewsCount = data.vviews()
|
|
? data.vviews()->v
|
|
: 0,
|
|
.forwardsCount = data.vforwards()
|
|
? data.vforwards()->v
|
|
: 0,
|
|
.reactionsCount = reactionsCount,
|
|
};
|
|
}, [](const MTPDmessageEmpty &) {
|
|
return Data::StatisticsMessageInteractionInfo();
|
|
}, [](const MTPDmessageService &) {
|
|
return Data::StatisticsMessageInteractionInfo();
|
|
});
|
|
};
|
|
|
|
auto info = result.match([&](const MTPDmessages_messages &data) {
|
|
return process(data.vmessages());
|
|
}, [&](const MTPDmessages_messagesSlice &data) {
|
|
return process(data.vmessages());
|
|
}, [&](const MTPDmessages_channelMessages &data) {
|
|
return process(data.vmessages());
|
|
}, [](const MTPDmessages_messagesNotModified &) {
|
|
return Data::StatisticsMessageInteractionInfo();
|
|
});
|
|
|
|
requestFirstPublicForwards(
|
|
messageGraph,
|
|
reactionsGraph,
|
|
std::move(info));
|
|
}).fail([=](const MTP::Error &error) {
|
|
requestFirstPublicForwards(messageGraph, reactionsGraph, {});
|
|
}).send();
|
|
};
|
|
|
|
const auto requestStoryPrivateForwards = [=](
|
|
const Data::StatisticalGraph &messageGraph,
|
|
const Data::StatisticalGraph &reactionsGraph) {
|
|
api().request(MTPstories_GetStoriesByID(
|
|
channel()->input,
|
|
MTP_vector<MTPint>(1, MTP_int(_storyId.story)))
|
|
).done([=](const MTPstories_Stories &result) {
|
|
const auto &storyItem = result.data().vstories().v.front();
|
|
auto info = storyItem.match([&](const MTPDstoryItem &data) {
|
|
if (!data.vviews()) {
|
|
return Data::StatisticsMessageInteractionInfo();
|
|
}
|
|
const auto &tlViews = data.vviews()->data();
|
|
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();
|
|
};
|
|
|
|
if (_storyId) {
|
|
makeRequest(MTPstats_GetStoryStats(
|
|
MTP_flags(MTPstats_GetStoryStats::Flags(0)),
|
|
channel()->input,
|
|
MTP_int(_storyId.story)
|
|
)).done([=](const MTPstats_StoryStats &result) {
|
|
const auto &data = result.data();
|
|
requestStoryPrivateForwards(
|
|
StatisticalGraphFromTL(data.vviews_graph()),
|
|
StatisticalGraphFromTL(data.vreactions_by_emotion_graph()));
|
|
}).fail([=](const MTP::Error &error) {
|
|
requestStoryPrivateForwards({}, {});
|
|
}).send();
|
|
} else {
|
|
makeRequest(MTPstats_GetMessageStats(
|
|
MTP_flags(MTPstats_GetMessageStats::Flags(0)),
|
|
channel()->inputChannel,
|
|
MTP_int(_fullId.msg.bare)
|
|
)).done([=](const MTPstats_MessageStats &result) {
|
|
const auto &data = result.data();
|
|
requestPrivateForwards(
|
|
StatisticalGraphFromTL(data.vviews_graph()),
|
|
StatisticalGraphFromTL(data.vreactions_by_emotion_graph()));
|
|
}).fail([=](const MTP::Error &error) {
|
|
requestPrivateForwards({}, {});
|
|
}).send();
|
|
}
|
|
}
|
|
|
|
Boosts::Boosts(not_null<PeerData*> peer)
|
|
: _peer(peer)
|
|
, _api(&peer->session().api().instance()) {
|
|
}
|
|
|
|
rpl::producer<rpl::no_value, QString> Boosts::request() {
|
|
return [=](auto consumer) {
|
|
auto lifetime = rpl::lifetime();
|
|
const auto channel = _peer->asChannel();
|
|
if (!channel) {
|
|
return lifetime;
|
|
}
|
|
|
|
_api.request(MTPpremium_GetBoostsStatus(
|
|
_peer->input
|
|
)).done([=](const MTPpremium_BoostsStatus &result) {
|
|
const auto &data = result.data();
|
|
channel->updateLevelHint(data.vlevel().v);
|
|
const auto hasPremium = !!data.vpremium_audience();
|
|
const auto premiumMemberCount = hasPremium
|
|
? std::max(0, int(data.vpremium_audience()->data().vpart().v))
|
|
: 0;
|
|
const auto participantCount = hasPremium
|
|
? std::max(
|
|
int(data.vpremium_audience()->data().vtotal().v),
|
|
premiumMemberCount)
|
|
: 0;
|
|
const auto premiumMemberPercentage = (participantCount > 0)
|
|
? (100. * premiumMemberCount / participantCount)
|
|
: 0;
|
|
|
|
const auto slots = data.vmy_boost_slots();
|
|
_boostStatus.overview = Data::BoostsOverview{
|
|
.group = channel->isMegagroup(),
|
|
.mine = slots ? int(slots->v.size()) : 0,
|
|
.level = std::max(data.vlevel().v, 0),
|
|
.boostCount = std::max(
|
|
data.vboosts().v,
|
|
data.vcurrent_level_boosts().v),
|
|
.currentLevelBoostCount = data.vcurrent_level_boosts().v,
|
|
.nextLevelBoostCount = data.vnext_level_boosts()
|
|
? data.vnext_level_boosts()->v
|
|
: 0,
|
|
.premiumMemberCount = premiumMemberCount,
|
|
.premiumMemberPercentage = premiumMemberPercentage,
|
|
};
|
|
_boostStatus.link = qs(data.vboost_url());
|
|
|
|
if (data.vprepaid_giveaways()) {
|
|
_boostStatus.prepaidGiveaway = ranges::views::all(
|
|
data.vprepaid_giveaways()->v
|
|
) | ranges::views::transform([](const MTPPrepaidGiveaway &r) {
|
|
return Data::BoostPrepaidGiveaway{
|
|
.months = r.data().vmonths().v,
|
|
.id = r.data().vid().v,
|
|
.quantity = r.data().vquantity().v,
|
|
.date = QDateTime::fromSecsSinceEpoch(
|
|
r.data().vdate().v),
|
|
};
|
|
}) | ranges::to_vector;
|
|
}
|
|
|
|
using namespace Data;
|
|
requestBoosts({ .gifts = false }, [=](BoostsListSlice &&slice) {
|
|
_boostStatus.firstSliceBoosts = std::move(slice);
|
|
requestBoosts({ .gifts = true }, [=](BoostsListSlice &&s) {
|
|
_boostStatus.firstSliceGifts = std::move(s);
|
|
consumer.put_done();
|
|
});
|
|
});
|
|
}).fail([=](const MTP::Error &error) {
|
|
consumer.put_error_copy(error.type());
|
|
}).send();
|
|
|
|
return lifetime;
|
|
};
|
|
}
|
|
|
|
void Boosts::requestBoosts(
|
|
const Data::BoostsListSlice::OffsetToken &token,
|
|
Fn<void(Data::BoostsListSlice)> done) {
|
|
if (_requestId) {
|
|
return;
|
|
}
|
|
constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice);
|
|
constexpr auto kTlLimit = tl::make_int(kLimit);
|
|
const auto gifts = token.gifts;
|
|
_requestId = _api.request(MTPpremium_GetBoostsList(
|
|
gifts
|
|
? MTP_flags(MTPpremium_GetBoostsList::Flag::f_gifts)
|
|
: MTP_flags(0),
|
|
_peer->input,
|
|
MTP_string(token.next),
|
|
token.next.isEmpty() ? kTlFirstSlice : kTlLimit
|
|
)).done([=](const MTPpremium_BoostsList &result) {
|
|
_requestId = 0;
|
|
|
|
const auto &data = result.data();
|
|
_peer->owner().processUsers(data.vusers());
|
|
|
|
auto list = std::vector<Data::Boost>();
|
|
list.reserve(data.vboosts().v.size());
|
|
constexpr auto kMonthsDivider = int(30 * 86400);
|
|
for (const auto &boost : data.vboosts().v) {
|
|
const auto &data = boost.data();
|
|
const auto path = data.vused_gift_slug()
|
|
? (u"giftcode/"_q + qs(data.vused_gift_slug()->v))
|
|
: QString();
|
|
auto giftCodeLink = !path.isEmpty()
|
|
? Data::GiftCodeLink{
|
|
_peer->session().createInternalLink(path),
|
|
_peer->session().createInternalLinkFull(path),
|
|
qs(data.vused_gift_slug()->v),
|
|
}
|
|
: Data::GiftCodeLink();
|
|
list.push_back({
|
|
data.is_gift(),
|
|
data.is_giveaway(),
|
|
data.is_unclaimed(),
|
|
qs(data.vid()),
|
|
data.vuser_id().value_or_empty(),
|
|
data.vgiveaway_msg_id()
|
|
? FullMsgId{ _peer->id, data.vgiveaway_msg_id()->v }
|
|
: FullMsgId(),
|
|
QDateTime::fromSecsSinceEpoch(data.vdate().v),
|
|
QDateTime::fromSecsSinceEpoch(data.vexpires().v),
|
|
(data.vexpires().v - data.vdate().v) / kMonthsDivider,
|
|
std::move(giftCodeLink),
|
|
data.vmultiplier().value_or_empty(),
|
|
});
|
|
}
|
|
done(Data::BoostsListSlice{
|
|
.list = std::move(list),
|
|
.multipliedTotal = data.vcount().v,
|
|
.allLoaded = (data.vcount().v == data.vboosts().v.size()),
|
|
.token = Data::BoostsListSlice::OffsetToken{
|
|
.next = data.vnext_offset()
|
|
? qs(*data.vnext_offset())
|
|
: QString(),
|
|
.gifts = gifts,
|
|
},
|
|
});
|
|
}).fail([=] {
|
|
_requestId = 0;
|
|
}).send();
|
|
}
|
|
|
|
Data::BoostStatus Boosts::boostStatus() const {
|
|
return _boostStatus;
|
|
}
|
|
|
|
ChannelEarnStatistics::ChannelEarnStatistics(not_null<ChannelData*> channel)
|
|
: StatisticsRequestSender(channel) {
|
|
}
|
|
|
|
rpl::producer<rpl::no_value, QString> ChannelEarnStatistics::request() {
|
|
return [=](auto consumer) {
|
|
auto lifetime = rpl::lifetime();
|
|
|
|
makeRequest(MTPstats_GetBroadcastRevenueStats(
|
|
MTP_flags(0),
|
|
channel()->inputChannel
|
|
)).done([=](const MTPstats_BroadcastRevenueStats &result) {
|
|
const auto &data = result.data();
|
|
const auto &balances = data.vbalances().data();
|
|
_data = Data::EarnStatistics{
|
|
.topHoursGraph = StatisticalGraphFromTL(
|
|
data.vtop_hours_graph()),
|
|
.revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()),
|
|
.currentBalance = balances.vcurrent_balance().v,
|
|
.availableBalance = balances.vavailable_balance().v,
|
|
.overallRevenue = balances.voverall_revenue().v,
|
|
.usdRate = data.vusd_rate().v,
|
|
};
|
|
|
|
requestHistory({}, [=](Data::EarnHistorySlice &&slice) {
|
|
_data.firstHistorySlice = std::move(slice);
|
|
|
|
api().request(
|
|
MTPchannels_GetFullChannel(channel()->inputChannel)
|
|
).done([=](const MTPmessages_ChatFull &result) {
|
|
result.data().vfull_chat().match([&](
|
|
const MTPDchannelFull &d) {
|
|
_data.switchedOff = d.is_restricted_sponsored();
|
|
}, [](const auto &) {
|
|
});
|
|
consumer.put_done();
|
|
}).fail([=](const MTP::Error &error) {
|
|
consumer.put_error_copy(error.type());
|
|
}).send();
|
|
});
|
|
}).fail([=](const MTP::Error &error) {
|
|
consumer.put_error_copy(error.type());
|
|
}).send();
|
|
|
|
return lifetime;
|
|
};
|
|
}
|
|
|
|
void ChannelEarnStatistics::requestHistory(
|
|
const Data::EarnHistorySlice::OffsetToken &token,
|
|
Fn<void(Data::EarnHistorySlice)> done) {
|
|
if (_requestId) {
|
|
return;
|
|
}
|
|
constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice);
|
|
constexpr auto kTlLimit = tl::make_int(kLimit);
|
|
_requestId = api().request(MTPstats_GetBroadcastRevenueTransactions(
|
|
channel()->inputChannel,
|
|
MTP_int(token),
|
|
(!token) ? kTlFirstSlice : kTlLimit
|
|
)).done([=](const MTPstats_BroadcastRevenueTransactions &result) {
|
|
_requestId = 0;
|
|
|
|
const auto &tlTransactions = result.data().vtransactions().v;
|
|
|
|
auto list = std::vector<Data::EarnHistoryEntry>();
|
|
list.reserve(tlTransactions.size());
|
|
for (const auto &tlTransaction : tlTransactions) {
|
|
list.push_back(tlTransaction.match([&](
|
|
const MTPDbroadcastRevenueTransactionProceeds &d) {
|
|
return Data::EarnHistoryEntry{
|
|
.type = Data::EarnHistoryEntry::Type::In,
|
|
.amount = d.vamount().v,
|
|
.date = base::unixtime::parse(d.vfrom_date().v),
|
|
.dateTo = base::unixtime::parse(d.vto_date().v),
|
|
};
|
|
}, [&](const MTPDbroadcastRevenueTransactionWithdrawal &d) {
|
|
return Data::EarnHistoryEntry{
|
|
.type = Data::EarnHistoryEntry::Type::Out,
|
|
.status = d.is_pending()
|
|
? Data::EarnHistoryEntry::Status::Pending
|
|
: d.is_failed()
|
|
? Data::EarnHistoryEntry::Status::Failed
|
|
: Data::EarnHistoryEntry::Status::Success,
|
|
.amount = (std::numeric_limits<Data::EarnInt>::max()
|
|
- d.vamount().v
|
|
+ 1),
|
|
.date = base::unixtime::parse(d.vdate().v),
|
|
// .provider = qs(d.vprovider()),
|
|
.successDate = d.vtransaction_date()
|
|
? base::unixtime::parse(d.vtransaction_date()->v)
|
|
: QDateTime(),
|
|
.successLink = d.vtransaction_url()
|
|
? qs(*d.vtransaction_url())
|
|
: QString(),
|
|
};
|
|
}, [&](const MTPDbroadcastRevenueTransactionRefund &d) {
|
|
return Data::EarnHistoryEntry{
|
|
.type = Data::EarnHistoryEntry::Type::Return,
|
|
.amount = d.vamount().v,
|
|
.date = base::unixtime::parse(d.vdate().v),
|
|
// .provider = qs(d.vprovider()),
|
|
};
|
|
}));
|
|
}
|
|
const auto nextToken = token + tlTransactions.size();
|
|
done(Data::EarnHistorySlice{
|
|
.list = std::move(list),
|
|
.total = result.data().vcount().v,
|
|
.allLoaded = (result.data().vcount().v == nextToken),
|
|
.token = Data::EarnHistorySlice::OffsetToken(nextToken),
|
|
});
|
|
}).fail([=] {
|
|
done({});
|
|
_requestId = 0;
|
|
}).send();
|
|
}
|
|
|
|
Data::EarnStatistics ChannelEarnStatistics::data() const {
|
|
return _data;
|
|
}
|
|
|
|
} // namespace Api
|