From 9990755ccc0c65c1a674ae8a36e7d6622b0a93ff Mon Sep 17 00:00:00 2001 From: nuttylmao Date: Wed, 17 Sep 2025 23:44:32 +1000 Subject: [PATCH 1/5] =?UTF-8?q?=E2=80=A2=20Added=20a=20new=20setting=20to?= =?UTF-8?q?=20update=20YouTube=20titles=20on=20start=20=E2=80=A2=20Reduced?= =?UTF-8?q?=20the=20number=20of=20calls=20to=20FetchBroadcast()=20to=20opt?= =?UTF-8?q?imize=20API=20quota=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- multistream-title-updater/contents/index.html | 15 +++- multistream-title-updater/contents/script.js | 88 ++++++++++++++++++- multistream-title-updater/contents/style.css | 7 ++ 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/multistream-title-updater/contents/index.html b/multistream-title-updater/contents/index.html index 0bb65b6..2f62705 100644 --- a/multistream-title-updater/contents/index.html +++ b/multistream-title-updater/contents/index.html @@ -25,6 +25,8 @@
@@ -59,6 +61,17 @@ +
+
+
Include YouTube
+
Update YouTube title as soon as broadcast starts
+
+ +
+ @@ -211,7 +224,7 @@
-
diff --git a/multistream-title-updater/contents/script.js b/multistream-title-updater/contents/script.js index 340c415..739f74c 100644 --- a/multistream-title-updater/contents/script.js +++ b/multistream-title-updater/contents/script.js @@ -8,6 +8,7 @@ const sbActionOpenUrl = '14da1d44-6e29-4582-92c3-2c59388be57e'; let currentBroadcastId = ''; let runningActionId = ''; +let youtubeTitleOnBroadcastStart = ''; /////////////////// @@ -21,6 +22,9 @@ const updateKickDialog = document.getElementById('update-kick-dialog'); const updateYouTubeDialog = document.getElementById('update-youtube-dialog'); const broadcastList = document.getElementById('broadcast-list'); const youtubeWarning = document.getElementById('youtube-warning'); +const includeYouTubeSettingWrapper = document.getElementById('include-youtube-setting-wrapper'); +const includeYouTubeSetting = document.getElementById('include-youtube-setting'); +const youtubeTitleOnBroadcastStartLabel = document.getElementById('youtube-title-on-broadcast-start-label'); @@ -37,6 +41,21 @@ window.parent.sbClient.on('General.Custom', (response) => { GeneralCustom(response.data); }) +window.parent.sbClient.on('YouTube.BroadcastStarted', (response) => { + console.debug(response.data); + YouTubeBroadcastStarted(response.data); +}) + +window.parent.sbClient.on('Twitch.StreamUpdate', (response) => { + console.debug(response.data); + FetchBroadcasts(); +}) + +window.parent.sbClient.on('YouTube.BroadcastUpdated', (response) => { + console.debug(response.data); + FetchBroadcasts(); +}) + /////////////////////////////// @@ -64,9 +83,17 @@ async function GeneralCustom(data) { // Only show the warning if there are 0 monitored broadcasts const ytBroadcastCount = data.broadcastList.filter(b => b.platform === "youtube").length; if (ytBroadcastCount <= 0) + { youtubeWarning.style.display = 'flex'; + includeYouTubeSettingWrapper.style.display = 'flex'; + includeYouTubeSetting.checked = true; + } else + { youtubeWarning.style.display = 'none'; + includeYouTubeSettingWrapper.style.display = 'none'; + includeYouTubeSetting.checked = false; + } // Set the URL to the livestreaming dashboard const broadcastButton = youtubeWarning.querySelector('#broadcast-dashboard-button'); @@ -83,7 +110,7 @@ async function GeneralCustom(data) { const childDivs = broadcastList.querySelectorAll(":scope > div"); childDivs.forEach(childDiv => { var result = data.broadcastList.find(obj => { - return obj.id === childDiv.id + return `id-${obj.id}` === childDiv.id; }) if (result == null) childDiv.remove(); @@ -93,17 +120,58 @@ async function GeneralCustom(data) { } } +function YouTubeBroadcastStarted(data) { + if (!includeYouTubeSetting.checked) + return; + + setTimeout(async () => { + await window.parent.sbClient.doAction( + action = { + id: sbActionUpdateStreamInfo + }, + args = { + platform: 'youtube', + title: youtubeTitleOnBroadcastStart, + broadcastId: data.id + } + ); + }, 1000); +} + async function FetchBroadcasts() { // Fetch from Streamer.bot const response = await window.parent.sbClient.doAction({ id: sbActionFetchBroadcasts}); runningActionId = response.args.runningActionId; } +async function FetchKickInfo() { + // Fetch from Streamer.bot + const broadcasterInfo = await window.parent.sbClient.getBroadcaster(); + + if (broadcasterInfo.platforms.kick) { + const userLogin = broadcasterInfo.platforms.kick.broadcasterLogin; + + let kickData = { + platform: 'kick', + id: 'kick', + streamUrl: `https://www.kick.com/${userLogin}`, + dashboardUrl: 'https://dashboard.kick.com/stream', + userLogin: userLogin + } + AddBroadcast(kickData); + } + else { + const kickBroadcastDiv = document.getElementById('id-kick'); + if (kickBroadcastDiv) + kickBroadcastDiv.remove(); + } +} + async function AddBroadcast(data) { // Get a reference to the template const template = document.getElementById('broadcast-template'); - const existingDiv = broadcastList.querySelector(`#${data.id}`); + const existingDiv = broadcastList.querySelector(`#id-${data.id}`); // Create a new instance of the template let instance; @@ -112,7 +180,7 @@ async function AddBroadcast(data) { instance = existingDiv; else { instance = template.content.firstElementChild.cloneNode(true); - instance.id = data.id; + instance.id = `id-${data.id}`; broadcastList.appendChild(instance); } @@ -284,6 +352,18 @@ async function UpdateAllSubmit() { } ); + // Set the YouTube title for next broadcast start + if (includeYouTubeSetting.checked) + { + youtubeTitleOnBroadcastStart = document.getElementById('all-title-input').value; + if (youtubeTitleOnBroadcastStart) + youtubeTitleOnBroadcastStartLabel.textContent = `Title will be set to '${youtubeTitleOnBroadcastStart}' when broadcast starts.`; + } + else { + youtubeTitleOnBroadcastStart = ''; + youtubeTitleOnBroadcastStartLabel.textContent = ''; + } + CloseUpdateAllDialog(); } @@ -543,4 +623,4 @@ ValidateYouTubeDialog(); // REFRESH BROADCAST LIST // //////////////////////////// -setInterval(FetchBroadcasts, 5000); \ No newline at end of file +setInterval(FetchKickInfo, 5000); \ No newline at end of file diff --git a/multistream-title-updater/contents/style.css b/multistream-title-updater/contents/style.css index 3e28841..e233b84 100644 --- a/multistream-title-updater/contents/style.css +++ b/multistream-title-updater/contents/style.css @@ -69,6 +69,13 @@ button { color: yellow; } +.setting { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.5em; +} + #kick-warning { display: none; } From 263a7cb07bf752d3f39fa2beca774c223f0ea414 Mon Sep 17 00:00:00 2001 From: nuttylmao Date: Thu, 18 Sep 2025 01:51:49 +1000 Subject: [PATCH 2/5] Fixed issue with Kick usernames that have underscores in them --- multistream-title-updater/config.json | 2 +- multistream-title-updater/contents/script.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/multistream-title-updater/config.json b/multistream-title-updater/config.json index a6578a3..38f853f 100644 --- a/multistream-title-updater/config.json +++ b/multistream-title-updater/config.json @@ -14,5 +14,5 @@ "name": "Multistream Title Updater | Open Link" } ], - "sbImportCode": "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" + "sbImportCode": "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" } \ No newline at end of file diff --git a/multistream-title-updater/contents/script.js b/multistream-title-updater/contents/script.js index 739f74c..8f6c522 100644 --- a/multistream-title-updater/contents/script.js +++ b/multistream-title-updater/contents/script.js @@ -154,7 +154,7 @@ async function FetchKickInfo() { let kickData = { platform: 'kick', id: 'kick', - streamUrl: `https://www.kick.com/${userLogin}`, + streamUrl: `https://www.kick.com/${userLogin.replaceAll('_', '-')}`, dashboardUrl: 'https://dashboard.kick.com/stream', userLogin: userLogin } From 798385ec33a0f4324e9a62b21a1ed5bd1986538b Mon Sep 17 00:00:00 2001 From: nuttylmao Date: Thu, 18 Sep 2025 05:28:18 +1000 Subject: [PATCH 3/5] Added support for Kick kicks --- horizontal-chat/icons/badges/kick-kicks.svg | 1 + horizontal-chat/script.js | 55 ++++++++----- horizontal-chat/settings/settings.json | 8 ++ horizontal-chat/style.css | 4 + multichat-overlay/icons/badges/kick-kicks.svg | 1 + multichat-overlay/index.html | 23 +++++- multichat-overlay/script.js | 77 ++++++++++++++----- multichat-overlay/settings/settings.json | 8 ++ multichat-overlay/style.css | 37 +++++++-- .../icons/badges/kick-kicks.svg | 1 + multistream-alerts/script.js | 71 ++++++++++++----- multistream-alerts/settings/settings.json | 17 ++++ 12 files changed, 240 insertions(+), 63 deletions(-) create mode 100644 horizontal-chat/icons/badges/kick-kicks.svg create mode 100644 multichat-overlay/icons/badges/kick-kicks.svg create mode 100644 multistream-alerts/icons/badges/kick-kicks.svg diff --git a/horizontal-chat/icons/badges/kick-kicks.svg b/horizontal-chat/icons/badges/kick-kicks.svg new file mode 100644 index 0000000..3f5a142 --- /dev/null +++ b/horizontal-chat/icons/badges/kick-kicks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/horizontal-chat/script.js b/horizontal-chat/script.js index 2cb44b7..f932541 100644 --- a/horizontal-chat/script.js +++ b/horizontal-chat/script.js @@ -58,6 +58,7 @@ const showKickMessages = GetBooleanParam("showKickMessages", true); const showKickSubs = GetBooleanParam("showKickSubs", true); const showKickChannelPointRedemptions = GetBooleanParam("showKickChannelPointRedemptions", true); const showKickHosts = GetBooleanParam("showKickHosts", true); +const showKickGifts = GetBooleanParam("showKickGifts", true); const showYouTubeMessages = GetBooleanParam("showYouTubeMessages", true); const showYouTubeSuperChats = GetBooleanParam("showYouTubeSuperChats", true); @@ -301,7 +302,9 @@ async function KickConnect() { return; // Channel to subscribe to (you'll need the correct channel name here) - const chatroomId = await GetKickChatroomId(kickUsername); + const kickIds = await GetKickIds(kickUsername); + const chatroomId = kickIds.chatroomId; + const channelId = kickIds.channelId; // Cache subscriber badges kickSubBadges = await GetKickSubBadges(kickUsername); @@ -334,6 +337,7 @@ async function KickConnect() { websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}` } })); websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}.v2` } })); websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `predictions-channel-${chatroomId}` } })); + websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `channel_${channelId}` } })); console.log(`[Pusher] Sent subscription request to channel: ${chatroomId}`); } @@ -365,6 +369,9 @@ async function KickConnect() { case 'UserBannedEvent': KickUserBanned(eventArgs); break; + case 'KicksGifted': + KickKicksGifted(eventArgs); + break; } } catch (error) { @@ -1425,6 +1432,18 @@ function KickUserBanned(data) { }); } +function KickKicksGifted(data) { + if (!showKickGifts) + return; + + const kicksImg = ``; + const giftImg = ``; + + const message = ` ${giftImg} ${data.sender.username} sent ${data.gift.name} ${kicksImg} ${data.gift.amount}`; + + ShowAlert(message, 'kick'); +} + async function TikTokChat(data) { if (!showTikTokMessages) return; @@ -1863,25 +1882,25 @@ function EscapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } -async function GetKickChatroomId(username) { - const url = `https://kick.com/api/v2/channels/${username}`; +async function GetKickIds(username) { + const url = `https://kick.com/api/v2/channels/${username}`; - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error ${response.status}`); - } + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error ${response.status}`); + } - const data = await response.json(); - if (data.chatroom && data.chatroom.id) { - return data.chatroom.id; - } else { - throw new Error("Chatroom ID not found in response."); - } - } catch (error) { - console.error("Failed to fetch chatroom ID:", error.message); - return null; - } + const data = await response.json(); + if (data.chatroom && data.chatroom.id) { + return { chatroomId: data.chatroom.id, channelId: data.chatroom.channel_id }; + } else { + throw new Error("Chatroom ID not found in response."); + } + } catch (error) { + console.error("Failed to fetch chatroom ID:", error.message); + return null; + } } async function GetKickSubBadges(username) { diff --git a/horizontal-chat/settings/settings.json b/horizontal-chat/settings/settings.json index 2891d09..0222140 100644 --- a/horizontal-chat/settings/settings.json +++ b/horizontal-chat/settings/settings.json @@ -255,6 +255,14 @@ "defaultValue": true, "group": "Which Kick messages do you want to see?" }, + { + "id": "showKickGifts", + "label": "Gifts", + "description": "", + "type": "checkbox", + "defaultValue": true, + "group": "Which Kick messages do you want to see?" + }, { "id": "enableTikTokSupport", "label": "Enable TikTok Support", diff --git a/horizontal-chat/style.css b/horizontal-chat/style.css index baf5c59..a7a0419 100644 --- a/horizontal-chat/style.css +++ b/horizontal-chat/style.css @@ -76,6 +76,10 @@ html { /* background-color: red; */ } +#alertBoxContent img { + height: 1.2em; +} + #background { position: absolute; bottom: 0; diff --git a/multichat-overlay/icons/badges/kick-kicks.svg b/multichat-overlay/icons/badges/kick-kicks.svg new file mode 100644 index 0000000..3f5a142 --- /dev/null +++ b/multichat-overlay/icons/badges/kick-kicks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/multichat-overlay/index.html b/multichat-overlay/index.html index de32944..00a06c4 100644 --- a/multichat-overlay/index.html +++ b/multichat-overlay/index.html @@ -66,7 +66,7 @@ + + \ No newline at end of file diff --git a/multichat-overlay/script.js b/multichat-overlay/script.js index 6779df8..035dfa7 100644 --- a/multichat-overlay/script.js +++ b/multichat-overlay/script.js @@ -61,6 +61,7 @@ const showKickMessages = GetBooleanParam("showKickMessages", true); const showKickSubs = GetBooleanParam("showKickSubs", true); const showKickChannelPointRedemptions = GetBooleanParam("showKickChannelPointRedemptions", true); const showKickHosts = GetBooleanParam("showKickHosts", true); +const showKickGifts = GetBooleanParam("showKickGifts", true); const showYouTubeMessages = GetBooleanParam("showYouTubeMessages", true); const showYouTubeSuperChats = GetBooleanParam("showYouTubeSuperChats", true); @@ -322,6 +323,11 @@ client.on('Fourthwall.GiftDrawEnded', (response) => { FourthwallGiftDrawEnded(response.data); }) +client.on('Fourthwall.GiftDrawEnded', (response) => { + console.debug(response.data); + FourthwallGiftDrawEnded(response.data); +}) + /////////////////////////// @@ -334,7 +340,9 @@ async function KickConnect() { return; // Channel to subscribe to (you'll need the correct channel name here) - const chatroomId = await GetKickChatroomId(kickUsername); + const kickIds = await GetKickIds(kickUsername); + const chatroomId = kickIds.chatroomId; + const channelId = kickIds.channelId; // Cache subscriber badges kickSubBadges = await GetKickSubBadges(kickUsername); @@ -367,6 +375,7 @@ async function KickConnect() { websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}` } })); websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}.v2` } })); websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `predictions-channel-${chatroomId}` } })); + websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `channel_${channelId}` } })); console.log(`[Pusher] Sent subscription request to channel: ${chatroomId}`); } @@ -398,6 +407,9 @@ async function KickConnect() { case 'UserBannedEvent': KickUserBanned(eventArgs); break; + case 'KicksGifted': + KickKicksGifted(eventArgs); + break; } } catch (error) { @@ -2518,6 +2530,35 @@ function KickMessageDeleted(data) { }, 500); } +async function KickKicksGifted(data) { + if (!showKickGifts) + return; + + // Get a reference to the template + const template = document.getElementById('kick-gift-template'); + + // Create a new instance of the template + const instance = template.content.cloneNode(true); + + // Get divs + const avatarImg = instance.querySelector('.kick-gift-avatar'); + const usernameSpan = instance.querySelector('#kick-gift-username'); + const giftNameSpan = instance.querySelector('#kick-gift-name'); + const stickerImg = instance.querySelector('.kick-gift-sticker'); + const amountDiv = instance.querySelector('#kick-gift-amount'); + const messageDiv = instance.querySelector('#kick-gift-message'); + + avatarImg.src = await GetAvatar(data.sender.username.replace('_', '-'), 'kick'); // Set the card header + usernameSpan.innerText = data.sender.username; // Set the username + usernameSpan.style.color = data.sender.username_color; + giftNameSpan.innerText = data.gift.name; // Set the gift name + stickerImg.src = `https://files.kick.com/kicks/gifts/${data.gift.gift_id.replace('_', '-')}.webp`; // Set the sticker image URL + amountDiv.innerText = data.gift.amount; // Set the number of gifts sent + messageDiv.innerText = data.message // Set the message + + AddMessageItem(instance, null, 'kick', data.senderId); +} + function KickUserBanned(data) { const messageList = document.getElementById("messageList"); @@ -3188,25 +3229,25 @@ function EscapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } -async function GetKickChatroomId(username) { - const url = `https://kick.com/api/v2/channels/${username}`; +async function GetKickIds(username) { + const url = `https://kick.com/api/v2/channels/${username}`; - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error ${response.status}`); - } + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error ${response.status}`); + } - const data = await response.json(); - if (data.chatroom && data.chatroom.id) { - return data.chatroom.id; - } else { - throw new Error("Chatroom ID not found in response."); - } - } catch (error) { - console.error("Failed to fetch chatroom ID:", error.message); - return null; - } + const data = await response.json(); + if (data.chatroom && data.chatroom.id) { + return { chatroomId: data.chatroom.id, channelId: data.chatroom.channel_id }; + } else { + throw new Error("Chatroom ID not found in response."); + } + } catch (error) { + console.error("Failed to fetch chatroom ID:", error.message); + return null; + } } async function GetKickSubBadges(username) { diff --git a/multichat-overlay/settings/settings.json b/multichat-overlay/settings/settings.json index d1383cc..dbf67a4 100644 --- a/multichat-overlay/settings/settings.json +++ b/multichat-overlay/settings/settings.json @@ -376,6 +376,14 @@ "defaultValue": true, "group": "Which Kick messages do you want to see?" }, + { + "id": "showKickGifts", + "label": "Gifts", + "description": "", + "type": "checkbox", + "defaultValue": true, + "group": "Which Kick messages do you want to see?" + }, { "id": "enableTikTokSupport", "label": "Enable TikTok Support", diff --git a/multichat-overlay/style.css b/multichat-overlay/style.css index cd048e3..2736e7d 100644 --- a/multichat-overlay/style.css +++ b/multichat-overlay/style.css @@ -224,17 +224,18 @@ li { transform: translate(0px, 0.25em); } +.kick-gift, .tiktok-gift { display: flex; flex-direction: row; align-items: center; gap: 1em; - padding: 0.5em 1em; + padding: 0.5em; border-radius: var(--border-radius); max-width: 100%; - background: linear-gradient(#262626BF, #000000BF) !important; } +.kick-gift-avatar, .tiktok-gift-avatar { height: 3em; margin: 1px; @@ -243,17 +244,37 @@ li { flex-shrink: 0; } +.kick-gift-contents, .tiktok-gift-message { flex-grow: 1; - white-space: nowrap; /* keeps text in a single line */ - overflow: hidden; /* hides overflowed content */ - text-overflow: ellipsis; /* adds the "..." at the end */ + /* white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; */ + gap: 1em; +} + +.kick-gift-contents { + /* text-align: center; */ +} + +#kick-gift-message { + font-size: 0.9em; +} + +.kick-gift-sticker, +.tiktok-gift-sticker { + flex-shrink: 0; + justify-content: flex-end; + + filter: drop-shadow(5px 5px 5px #00000060); } .tiktok-gift-sticker { height: 2.5em; - flex-shrink: 0; - justify-content: flex-end; +} + +.kick-gift-sticker { + height: 5em; } #tiktok-gift-repeat-count { @@ -292,7 +313,7 @@ li { } .kick { - background: linear-gradient(#53fc18, #005600) !important; + background: linear-gradient(#32b602, #005600) !important; } .streamlabs { diff --git a/multistream-alerts/icons/badges/kick-kicks.svg b/multistream-alerts/icons/badges/kick-kicks.svg new file mode 100644 index 0000000..3f5a142 --- /dev/null +++ b/multistream-alerts/icons/badges/kick-kicks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/multistream-alerts/script.js b/multistream-alerts/script.js index 30eacaf..ca29eae 100644 --- a/multistream-alerts/script.js +++ b/multistream-alerts/script.js @@ -77,6 +77,8 @@ const showKickChannelPointRedemptions = GetBooleanParam("showKickChannelPointRed const kickChannelPointRedemptionAction = urlParams.get("kickChannelPointRedemptionAction") || ""; const showKickHosts = GetBooleanParam("showKickHosts", true); const kickHostAction = urlParams.get("kickHostAction") || ""; +const showKickGifts = GetBooleanParam("showKickGifts", true); +const kickGiftAction = urlParams.get("kickGiftAction") || ""; // Which YouTube alerts do you want to see? const showYouTubeSuperChats = GetBooleanParam("showYouTubeSuperChats", true); @@ -315,7 +317,9 @@ async function KickConnect() { return; // Channel to subscribe to (you'll need the correct channel name here) - const chatroomId = await GetKickChatroomId(kickUsername); + const kickIds = await GetKickIds(kickUsername); + const chatroomId = kickIds.chatroomId; + const channelId = kickIds.channelId; // Cache subscriber badges kickSubBadges = await GetKickSubBadges(kickUsername); @@ -348,6 +352,7 @@ async function KickConnect() { websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}` } })); websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}.v2` } })); websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `predictions-channel-${chatroomId}` } })); + websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `channel_${channelId}` } })); console.log(`[Pusher] Sent subscription request to channel: ${chatroomId}`); } @@ -370,6 +375,9 @@ async function KickConnect() { case 'StreamHostEvent': KickStreamHost(eventArgs); break; + case 'KicksGifted': + KickKicksGifted(eventArgs); + break; } } catch (error) { @@ -1365,6 +1373,33 @@ async function KickStreamHost(data) { ); } +async function KickKicksGifted(data) { + if (!showKickGifts) + return; + + // Set the text + const username = data.sender.username; + const tiktokIcon = ``; + // const giftImg = ``; + const kickKicksIcon = ``; + + // Render avatars + // const avatarURL = await GetAvatar(data.sender.username.replace('_', '-'), 'kick'); + const avatarURL = `https://files.kick.com/kicks/gifts/${data.gift.gift_id.replace('_', '-')}.webp`; + + UpdateAlertBox( + 'kick', + avatarURL, + `${username}`, + `sent ${kickKicksIcon} ${data.gift.amount}`, + '', + username, + data.message, + kickGiftAction, + data + ); +} + async function TikTokGift(data) { if (!showTikTokGifts) return; @@ -1710,25 +1745,25 @@ function UpdateAlertBox(platform, avatarURL, headerText, descriptionText, attrib } -async function GetKickChatroomId(username) { - const url = `https://kick.com/api/v2/channels/${username}`; +async function GetKickIds(username) { + const url = `https://kick.com/api/v2/channels/${username}`; - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error ${response.status}`); - } + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error ${response.status}`); + } - const data = await response.json(); - if (data.chatroom && data.chatroom.id) { - return data.chatroom.id; - } else { - throw new Error("Chatroom ID not found in response."); - } - } catch (error) { - console.error("Failed to fetch chatroom ID:", error.message); - return null; - } + const data = await response.json(); + if (data.chatroom && data.chatroom.id) { + return { chatroomId: data.chatroom.id, channelId: data.chatroom.channel_id }; + } else { + throw new Error("Chatroom ID not found in response."); + } + } catch (error) { + console.error("Failed to fetch chatroom ID:", error.message); + return null; + } } async function GetKickSubBadges(username) { diff --git a/multistream-alerts/settings/settings.json b/multistream-alerts/settings/settings.json index 87cb412..89768e0 100644 --- a/multistream-alerts/settings/settings.json +++ b/multistream-alerts/settings/settings.json @@ -422,6 +422,23 @@ "showIf": "showKickHosts", "group": "Which Kick alerts do you want to see?" }, + { + "id": "showKickGifts", + "label": "Gifts", + "description": "", + "type": "checkbox", + "defaultValue": true, + "group": "Which Kick alerts do you want to see?" + }, + { + "id": "kickGiftAction", + "label": "", + "description": "Also run this Streamer.bot Action", + "type": "sb-actions", + "defaultValue": "", + "showIf": "showKickGifts", + "group": "Which Kick alerts do you want to see?" + }, { "id": "enableTikTokSupport", "label": "Enable TikTok Support", From 58213318d3ff78a10114c049c138eda4fe24105f Mon Sep 17 00:00:00 2001 From: nuttylmao Date: Mon, 22 Sep 2025 04:31:16 +1000 Subject: [PATCH 4/5] Move a tonne of reused functions to helpers.js --- .../core/settings-core}/index.html | 3 +- .../core/settings-core}/script.js | 39 --- .../core/settings-core}/style.css | 0 .common/utils/helpers.js | 222 +++++++++++++++ .templates/obs-dock/index.html | 2 +- clock-widget/settings/index.html | 2 +- clock-widget/settings/script.js | 2 +- horizontal-chat/index.html | 1 + horizontal-chat/script.js | 177 +----------- horizontal-chat/settings/index.html | 2 +- horizontal-chat/settings/script.js | 2 +- multichat-overlay/index.html | 1 + multichat-overlay/script.js | 260 +++--------------- multichat-overlay/settings/index.html | 2 +- multichat-overlay/settings/script.js | 2 +- multichat-overlay/style.css | 6 +- multistream-alerts/index.html | 1 + multistream-alerts/script.js | 121 +------- multistream-alerts/settings/index.html | 2 +- multistream-alerts/settings/script.js | 2 +- multistream-title-updater/index.html | 2 +- obs-info-hud/settings/index.html | 2 +- obs-info-hud/settings/script.js | 2 +- printer-bot/contents/index.html | 1 + printer-bot/contents/script.js | 40 --- printer-bot/index.html | 2 +- scumbag-sub-goal-widget/settings/index.html | 2 +- scumbag-sub-goal-widget/settings/script.js | 2 +- simple-twitch-metrics/settings/index.html | 2 +- simple-twitch-metrics/settings/script.js | 2 +- twitch-ad-countdown-timer/index.html | 1 + twitch-ad-countdown-timer/script.js | 67 +---- twitch-ad-countdown-timer/settings/index.html | 2 +- twitch-ad-countdown-timer/settings/script.js | 2 +- twitch-uptime-widget/settings/index.html | 2 +- twitch-uptime-widget/settings/script.js | 2 +- 36 files changed, 303 insertions(+), 679 deletions(-) rename {.utilities/settings-page-builder => .common/core/settings-core}/index.html (95%) rename {.utilities/settings-page-builder => .common/core/settings-core}/script.js (93%) rename {.utilities/settings-page-builder => .common/core/settings-core}/style.css (100%) create mode 100644 .common/utils/helpers.js diff --git a/.utilities/settings-page-builder/index.html b/.common/core/settings-core/index.html similarity index 95% rename from .utilities/settings-page-builder/index.html rename to .common/core/settings-core/index.html index 219f6b9..96f8b2f 100644 --- a/.utilities/settings-page-builder/index.html +++ b/.common/core/settings-core/index.html @@ -12,7 +12,7 @@