import type { Api as GramJs } from '../../../lib/gramjs'; import type { ApiChannelStatistics, ApiGroupStatistics, ApiMessageStatistics, ApiMessagePublicForward, StatisticsGraph, StatisticsOverviewItem, StatisticsOverviewPercentage, StatisticsOverviewPeriod, } from '../../types'; import { buildAvatarHash } from './chats'; import { buildApiPeerId } from './peers'; export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiChannelStatistics { return { // Graphs growthGraph: buildGraph(stats.growthGraph), followersGraph: buildGraph(stats.followersGraph), muteGraph: buildGraph(stats.muteGraph), topHoursGraph: buildGraph(stats.topHoursGraph), // Async graphs languagesGraph: (stats.languagesGraph as GramJs.StatsGraphAsync).token, viewsBySourceGraph: (stats.viewsBySourceGraph as GramJs.StatsGraphAsync).token, newFollowersBySourceGraph: (stats.newFollowersBySourceGraph as GramJs.StatsGraphAsync).token, interactionsGraph: (stats.interactionsGraph as GramJs.StatsGraphAsync).token, // Statistics overview followers: buildStatisticsOverview(stats.followers), viewsPerPost: buildStatisticsOverview(stats.viewsPerPost), sharesPerPost: buildStatisticsOverview(stats.sharesPerPost), enabledNotifications: buildStatisticsPercentage(stats.enabledNotifications), // Recent posts recentTopMessages: stats.recentMessageInteractions, }; } export function buildGroupStatistics(stats: GramJs.stats.MegagroupStats): ApiGroupStatistics { return { // Graphs growthGraph: buildGraph(stats.growthGraph), membersGraph: buildGraph(stats.membersGraph), topHoursGraph: buildGraph(stats.topHoursGraph), // Async graphs languagesGraph: (stats.languagesGraph as GramJs.StatsGraphAsync).token, messagesGraph: (stats.messagesGraph as GramJs.StatsGraphAsync).token, actionsGraph: (stats.actionsGraph as GramJs.StatsGraphAsync).token, // Statistics overview period: getOverviewPeriod(stats.period), members: buildStatisticsOverview(stats.members), viewers: buildStatisticsOverview(stats.viewers), messages: buildStatisticsOverview(stats.messages), posters: buildStatisticsOverview(stats.posters), }; } export function buildMessageStatistics(stats: GramJs.stats.MessageStats): ApiMessageStatistics { return { viewsGraph: buildGraph(stats.viewsGraph), }; } export function buildMessagePublicForwards( result: GramJs.messages.TypeMessages, ): ApiMessagePublicForward[] | undefined { if (!result || !('messages' in result)) { return undefined; } return result.messages.map((message) => { const peerId = buildApiPeerId((message.peerId as GramJs.PeerChannel).channelId, 'channel'); const channel = result.chats.find((p) => buildApiPeerId(p.id, 'channel') === peerId); return { messageId: message.id, views: (message as GramJs.Message).views, title: (channel as GramJs.Channel).title, chat: { id: peerId, type: 'chatTypeChannel', username: (channel as GramJs.Channel).username, avatarHash: buildAvatarHash((channel as GramJs.Channel).photo), }, }; }); } export function buildGraph( result: GramJs.TypeStatsGraph, isPercentage?: boolean, ): StatisticsGraph | undefined { if ((result as GramJs.StatsGraphError).error) { return undefined; } const data = JSON.parse((result as GramJs.StatsGraph).json.data); const [x, ...y] = data.columns; const hasSecondYAxis = data.y_scaled; return { type: isPercentage ? 'area' : data.types.y0, zoomToken: (result as GramJs.StatsGraph).zoomToken, labelFormatter: data.xTickFormatter, tooltipFormatter: data.xTooltipFormatter, labels: x.slice(1), hideCaption: !data.subchart.show, hasSecondYAxis, isStacked: data.stacked && !hasSecondYAxis, isPercentage, datasets: y.map((item: any) => { const key = item[0]; return { name: data.names[key], color: extractColor(data.colors[key]), values: item.slice(1), }; }), ...calculateMinimapRange(data.subchart.defaultZoom, x.slice(1)), }; } function extractColor(color: string): string { return color.substring(color.indexOf('#')); } function calculateMinimapRange(range: Array, values: Array) { const [min, max] = range; let minIndex = 0; let maxIndex = values.length - 1; values.forEach((item, index) => { if (!minIndex && item >= min) { minIndex = index; } if (!maxIndex && item >= max) { maxIndex = index; } }); const begin = Math.max(0, minIndex / (values.length - 1)); const end = Math.min(1, maxIndex / (values.length - 1)); return { minimapRange: { begin, end }, labelFromIndex: minIndex, labelToIndex: maxIndex }; } function buildStatisticsOverview({ current, previous }: GramJs.StatsAbsValueAndPrev): StatisticsOverviewItem { const change = current - previous; return { current, change, ...(previous && { percentage: (change ? ((Math.abs(change) / previous) * 100) : 0).toFixed(2) }), }; } function buildStatisticsPercentage(data: GramJs.StatsPercentValue): StatisticsOverviewPercentage { return { percentage: ((data.part / data.total) * 100).toFixed(2), }; } function getOverviewPeriod(data: GramJs.StatsDateRangeDays): StatisticsOverviewPeriod { return { maxDate: data.maxDate, minDate: data.minDate, }; }