From a5b33f5b0ae672cbffc6b21edc41a231b3bbb202 Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Sun, 14 Jun 2026 13:50:12 -0300 Subject: [PATCH] Show Telegram subscribers on post metrics; render reactions as compact pills --- .../Social/Telegram/TelegramAnalytics.php | 20 +++++--- .../components/posts/PostPlatformMetrics.vue | 51 ++++++++++++++++--- .../Services/Social/TelegramAnalyticsTest.php | 30 ++++++++++- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/app/Services/Social/Telegram/TelegramAnalytics.php b/app/Services/Social/Telegram/TelegramAnalytics.php index 9e0cdd71..c21d8075 100644 --- a/app/Services/Social/Telegram/TelegramAnalytics.php +++ b/app/Services/Social/Telegram/TelegramAnalytics.php @@ -46,19 +46,25 @@ public function getMetrics(SocialAccount $account): array * Post-level metrics. Reaction counts are pushed by the webhook and stored * on the post platform's meta (the Bot API offers no post views to bots). * - * @return array + * @return array */ public function fetchPostMetrics(PostPlatform $postPlatform): array { + $account = $postPlatform->socialAccount; + $metrics = $account ? $this->getMetrics($account) : []; + $reactions = data_get($postPlatform->meta, 'reactions', []); - if (! is_array($reactions)) { - return []; + if (is_array($reactions)) { + foreach ($reactions as $reaction) { + $metrics[] = [ + 'label' => (string) data_get($reaction, 'type'), + 'value' => (int) data_get($reaction, 'count'), + 'kind' => 'reaction', + ]; + } } - return array_values(array_map(fn (array $reaction): array => [ - 'label' => (string) data_get($reaction, 'type'), - 'value' => (int) data_get($reaction, 'count'), - ], $reactions)); + return $metrics; } } diff --git a/resources/js/components/posts/PostPlatformMetrics.vue b/resources/js/components/posts/PostPlatformMetrics.vue index 9fd89b2f..54ea70ba 100644 --- a/resources/js/components/posts/PostPlatformMetrics.vue +++ b/resources/js/components/posts/PostPlatformMetrics.vue @@ -8,6 +8,7 @@ import { metrics as metricsRoute } from '@/routes/app/posts/platforms'; interface Metric { label: string; value: number; + kind?: string; } type MetricsResponse = Metric[] | { unsupported: true; reason: string }; @@ -22,10 +23,18 @@ const props = defineProps(); const loading = ref(true); const metrics = ref([]); +const stats = computed(() => + metrics.value.filter((m) => m.kind !== 'reaction'), +); +const reactions = computed(() => + metrics.value.filter((m) => m.kind === 'reaction'), +); + const hasMetrics = computed(() => metrics.value.length > 0); const formatNumber = (n: number): string => { - if (n >= 1_000_000) return (n / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M'; + if (n >= 1_000_000) + return (n / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M'; if (n >= 1_000) return (n / 1_000).toFixed(1).replace(/\.0$/, '') + 'K'; return n.toString(); }; @@ -35,7 +44,10 @@ const http = useHttp, MetricsResponse>({}); onMounted(async () => { try { const response = await http.get( - metricsRoute.url({ post: props.postId, postPlatform: props.postPlatformId }), + metricsRoute.url({ + post: props.postId, + postPlatform: props.postPlatformId, + }), ); if (Array.isArray(response)) { @@ -61,20 +73,45 @@ onMounted(async () => {
-
+
{{ $t('posts.show.metrics') }}
-
+
-

{{ metric.label }}

-

{{ formatNumber(metric.value) }}

+

+ {{ metric.label }} +

+

+ {{ formatNumber(metric.value) }} +

+ +
+ + {{ reaction.label }} + {{ + formatNumber(reaction.value) + }} + +
diff --git a/tests/Feature/Services/Social/TelegramAnalyticsTest.php b/tests/Feature/Services/Social/TelegramAnalyticsTest.php index e3fb8047..7ab63b7e 100644 --- a/tests/Feature/Services/Social/TelegramAnalyticsTest.php +++ b/tests/Feature/Services/Social/TelegramAnalyticsTest.php @@ -33,17 +33,43 @@ expect(app(TelegramAnalytics::class)->getMetrics($account))->toBe([]); }); -it('maps stored reactions to post metrics', function () { +it('maps stored reactions to post metrics, tagged as reactions', function () { + config(['trypost.platforms.telegram.bot_token' => '']); // skip the subscriber lookup + $postPlatform = PostPlatform::factory()->create([ 'platform' => Platform::Telegram, 'meta' => ['reactions' => [['type' => '👍', 'count' => 12], ['type' => '❤️', 'count' => 5]]], ]); expect(app(TelegramAnalytics::class)->fetchPostMetrics($postPlatform)) - ->toBe([['label' => '👍', 'value' => 12], ['label' => '❤️', 'value' => 5]]); + ->toBe([ + ['label' => '👍', 'value' => 12, 'kind' => 'reaction'], + ['label' => '❤️', 'value' => 5, 'kind' => 'reaction'], + ]); +}); + +it('includes the channel subscriber count alongside reactions', function () { + $account = SocialAccount::factory()->telegram()->create(); + $postPlatform = PostPlatform::factory()->create([ + 'social_account_id' => $account->id, + 'platform' => Platform::Telegram, + 'meta' => ['reactions' => [['type' => '👍', 'count' => 3]]], + ]); + + Http::fake([ + '*/botTESTTOKEN/getChatMemberCount*' => Http::response(['ok' => true, 'result' => 50], 200), + ]); + + expect(app(TelegramAnalytics::class)->fetchPostMetrics($postPlatform)) + ->toBe([ + ['label' => 'Subscribers', 'value' => 50], + ['label' => '👍', 'value' => 3, 'kind' => 'reaction'], + ]); }); it('returns no post metrics when there are no reactions yet', function () { + config(['trypost.platforms.telegram.bot_token' => '']); + $postPlatform = PostPlatform::factory()->create([ 'platform' => Platform::Telegram, 'meta' => [],