From 8b21c0f80f81e0189b5d98bd4d430e2831e70098 Mon Sep 17 00:00:00 2001 From: BP602 Date: Tue, 23 Sep 2025 09:55:46 +0200 Subject: [PATCH 1/2] fix(7tv): use correct user ID for cosmetic subscriptions - Change streamerData.id to streamerData.user_id for 7TV cosmetic subscriptions - Handle both entitlement.create and entitlement.delete events - Add removeUserStyle method for proper cosmetic removal - This fixes 7TV badges and paints not appearing due to wrong subscription IDs --- src/renderer/src/providers/ChatProvider.jsx | 6 +++ .../src/providers/CosmeticsProvider.jsx | 43 +++++++++++++++++++ utils/services/connectionManager.js | 4 +- utils/services/seventv/sharedStvWebSocket.js | 3 +- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/providers/ChatProvider.jsx b/src/renderer/src/providers/ChatProvider.jsx index 5b48457..f27fc14 100644 --- a/src/renderer/src/providers/ChatProvider.jsx +++ b/src/renderer/src/providers/ChatProvider.jsx @@ -1908,6 +1908,12 @@ const useChatStore = create((set, get) => ({ useCosmeticsStore?.getState()?.addUserStyle(transformedUsername, body); break; } + case "entitlement.delete": { + const username = body?.object?.user?.connections?.find((c) => c.platform === "KICK")?.username; + const transformedUsername = username?.replaceAll("-", "_").toLowerCase(); + useCosmeticsStore?.getState()?.removeUserStyle(transformedUsername, body); + break; + } default: break; } diff --git a/src/renderer/src/providers/CosmeticsProvider.jsx b/src/renderer/src/providers/CosmeticsProvider.jsx index 9f064fa..ca8a7ab 100644 --- a/src/renderer/src/providers/CosmeticsProvider.jsx +++ b/src/renderer/src/providers/CosmeticsProvider.jsx @@ -35,6 +35,49 @@ const useCosmeticsStore = create((set, get) => ({ }); }, + removeUserStyle: (username, body) => { + const transformedUsername = username.toLowerCase(); + const refId = body?.object?.ref_id; + const kind = body?.object?.kind; + + if (!refId || !kind) return; + + set((state) => { + const currentStyle = state.userStyles[transformedUsername]; + if (!currentStyle) return state; + + // Remove by kind and ref_id + let updatedStyle = { ...currentStyle }; + let hasChanges = false; + + if (kind === "BADGE" && currentStyle.badgeId === refId) { + updatedStyle.badgeId = null; + hasChanges = true; + } else if (kind === "PAINT" && currentStyle.paintId === refId) { + updatedStyle.paintId = null; + hasChanges = true; + } + + if (!hasChanges) return state; + + // If both badge and paint are removed, remove the entire user style entry + if (!updatedStyle.badgeId && !updatedStyle.paintId) { + const { [transformedUsername]: removed, ...restUserStyles } = state.userStyles; + return { userStyles: restUserStyles }; + } + + return { + userStyles: { + ...state.userStyles, + [transformedUsername]: { + ...updatedStyle, + updatedAt: new Date().toISOString(), + }, + }, + }; + }); + }, + getUserStyle: (username) => { if (!username) return null; const transformedUsername = username.toLowerCase(); diff --git a/utils/services/connectionManager.js b/utils/services/connectionManager.js index ff70288..3284d52 100644 --- a/utils/services/connectionManager.js +++ b/utils/services/connectionManager.js @@ -306,7 +306,7 @@ class ConnectionManager { span.addEvent('7tv_websocket_add_start'); this.stvWebSocket.addChatroom( chatroom.id, - chatroom.streamerData.id, // Use the Kick channel ID for cosmetic/entitlement subscriptions + chatroom.streamerData.user_id, // Use the correct Kick user ID for cosmetic/entitlement subscriptions stvId, stvEmoteSetId ); @@ -586,7 +586,7 @@ class ConnectionManager { this.stvWebSocket.addChatroom( chatroom.id, - chatroom.streamerData?.id, + chatroom.streamerData?.user_id, stvId, stvEmoteSetId, ); diff --git a/utils/services/seventv/sharedStvWebSocket.js b/utils/services/seventv/sharedStvWebSocket.js index 387ba23..e0ede3d 100644 --- a/utils/services/seventv/sharedStvWebSocket.js +++ b/utils/services/seventv/sharedStvWebSocket.js @@ -615,12 +615,13 @@ class SharedStvWebSocket extends EventTarget { break; case "entitlement.create": + case "entitlement.delete": if (body.kind === 10) { this.dispatchEvent( new CustomEvent("message", { detail: { body, - type: "entitlement.create", + type: type, // Use the actual event type (create or delete) chatroomId, }, }), From 94e78523a46c82c2a8875d15d9de91adcf28a624 Mon Sep 17 00:00:00 2001 From: BP602 Date: Tue, 23 Sep 2025 11:38:01 +0200 Subject: [PATCH 2/2] fix(cosmetics): guard against missing usernames in removeUserStyle Add null check before calling toLowerCase() on username parameter to prevent TypeError when 7TV entitlement.delete events lack Kick connection data. --- src/renderer/src/providers/CosmeticsProvider.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/providers/CosmeticsProvider.jsx b/src/renderer/src/providers/CosmeticsProvider.jsx index ca8a7ab..609a576 100644 --- a/src/renderer/src/providers/CosmeticsProvider.jsx +++ b/src/renderer/src/providers/CosmeticsProvider.jsx @@ -36,6 +36,7 @@ const useCosmeticsStore = create((set, get) => ({ }, removeUserStyle: (username, body) => { + if (!username) return; const transformedUsername = username.toLowerCase(); const refId = body?.object?.ref_id; const kind = body?.object?.kind;