From f1dd130bc0a37ddfe4cf20da6d5e0bc750d4cb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Sun, 5 Oct 2025 19:13:06 +0300 Subject: [PATCH] feat: extract and display membership gift counts in chat messages --- backend/src/ingestion/youtubei.ts | 18 ++++++++++++++++-- client/app/dashboard/page.tsx | 8 +++++--- client/app/overlay/page.tsx | 10 +++++++--- shared/chat.ts | 1 + 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/backend/src/ingestion/youtubei.ts b/backend/src/ingestion/youtubei.ts index 8c04141..b3113b1 100644 --- a/backend/src/ingestion/youtubei.ts +++ b/backend/src/ingestion/youtubei.ts @@ -257,6 +257,19 @@ function normalizeAction(action: any): ChatMessage | null { (isPaid ? undefined : 'New member'); } + // Extract gift count for gift purchases + let giftCount: number | undefined; + if (isGiftPurchase) { + const text = resolveMessageText(item); + const headerText = item.header_primary_text?.toString() || item.header_subtext?.toString() || ''; + const combinedText = text + ' ' + headerText; + // Try to extract number from text like "Gifted 5 memberships" or "5 memberships" + const countMatch = combinedText.match(/(\d+)\s*(?:membership|memberships|member|members)/i); + if (countMatch) { + giftCount = parseInt(countMatch[1], 10); + } + } + return { id: String(item.id ?? item.timestamp_usec ?? Date.now()), author: String(item.author?.name ?? 'Unknown'), @@ -268,9 +281,10 @@ function normalizeAction(action: any): ChatMessage | null { isMember, isVerified, superChat: extractSuperChatInfo(item), - membershipGift: isMembership || isGiftReceived, + membershipGift: isMembership, membershipGiftPurchase: isGiftPurchase, - membershipLevel + membershipLevel, + giftCount }; } diff --git a/client/app/dashboard/page.tsx b/client/app/dashboard/page.tsx index c9bb170..db4814c 100644 --- a/client/app/dashboard/page.tsx +++ b/client/app/dashboard/page.tsx @@ -54,8 +54,8 @@ export default function DashboardPage() { }, [pollError, overlayStatus]); const superChats = useMemo(() => messages.filter(m => m.superChat), [messages]); - const newMembers = useMemo(() => messages.filter(m => m.membershipGiftPurchase), [messages]); - const regularMessages = useMemo(() => messages.filter(m => !m.superChat && !m.membershipGift), [messages]); + const newMembers = useMemo(() => messages.filter(m => m.membershipGift || m.membershipGiftPurchase), [messages]); + const regularMessages = useMemo(() => messages.filter(m => !m.superChat && !m.membershipGift && !m.membershipGiftPurchase), [messages]); return (
@@ -462,7 +462,9 @@ function MemberItem({ message, isSelected, onSelect }: ChatItemProps) {
{message.author} - {message.membershipLevel || 'New member'} + {message.membershipGiftPurchase && message.giftCount + ? `Gifted ${message.giftCount} membership${message.giftCount > 1 ? 's' : ''}` + : message.membershipLevel || 'New member'}
diff --git a/client/app/overlay/page.tsx b/client/app/overlay/page.tsx index cb33b06..5ed4197 100644 --- a/client/app/overlay/page.tsx +++ b/client/app/overlay/page.tsx @@ -89,12 +89,16 @@ export default function OverlayPage() { 💰 {message.superChat.currency}{message.superChat.amount} )} - {message.membershipGift && ( + {(message.membershipGift || message.membershipGiftPurchase) && (
- 🎁 New Member! + {message.membershipGiftPurchase && message.giftCount + ? `🎁 Gifted ${message.giftCount} Membership${message.giftCount > 1 ? 's' : ''}!` + : message.membershipGiftPurchase + ? '🎁 Gift Purchase' + : '🎁 New Member!'}
)} - {(!message.superChat && !message.membershipGift) && message.text && ( + {(!message.superChat && !message.membershipGift && !message.membershipGiftPurchase) && message.text && (

{message.text}

)} diff --git a/shared/chat.ts b/shared/chat.ts index 1b815ac..77e1cef 100644 --- a/shared/chat.ts +++ b/shared/chat.ts @@ -24,4 +24,5 @@ export type ChatMessage = { membershipGift?: boolean; membershipGiftPurchase?: boolean; membershipLevel?: string; // e.g., "New member", "Member (6 months)", etc. + giftCount?: number; // Number of memberships gifted };