diff --git a/multichat-overlay/icons/badges/kick-bot.svg b/multichat-overlay/icons/badges/kick-bot.svg
new file mode 100644
index 0000000..3d323d2
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-bot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-broadcaster.svg b/multichat-overlay/icons/badges/kick-broadcaster.svg
new file mode 100644
index 0000000..83900f4
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-broadcaster.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-founder.svg b/multichat-overlay/icons/badges/kick-founder.svg
new file mode 100644
index 0000000..6bdc644
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-founder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-moderator.svg b/multichat-overlay/icons/badges/kick-moderator.svg
new file mode 100644
index 0000000..791f54b
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-moderator.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-og.svg b/multichat-overlay/icons/badges/kick-og.svg
new file mode 100644
index 0000000..cd5fb3d
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-og.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-sidekick.svg b/multichat-overlay/icons/badges/kick-sidekick.svg
new file mode 100644
index 0000000..1eca43f
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-sidekick.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-staff.svg b/multichat-overlay/icons/badges/kick-staff.svg
new file mode 100644
index 0000000..95a0063
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-staff.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-sub_gifter.svg b/multichat-overlay/icons/badges/kick-sub_gifter.svg
new file mode 100644
index 0000000..9950dfb
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-sub_gifter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-subscriber.svg b/multichat-overlay/icons/badges/kick-subscriber.svg
new file mode 100644
index 0000000..128b91c
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-subscriber.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-verified.svg b/multichat-overlay/icons/badges/kick-verified.svg
new file mode 100644
index 0000000..d3c68e1
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-verified.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/icons/badges/kick-vip.svg b/multichat-overlay/icons/badges/kick-vip.svg
new file mode 100644
index 0000000..609dff0
--- /dev/null
+++ b/multichat-overlay/icons/badges/kick-vip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/multichat-overlay/script.js b/multichat-overlay/script.js
index 7d58fba..e89ced7 100644
--- a/multichat-overlay/script.js
+++ b/multichat-overlay/script.js
@@ -15,6 +15,8 @@ const pronounMap = new Map();
/////////////////
const youtubeRegex = /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:[&?#].*)?$/;
+const kickPusherWsUrl = 'wss://ws-us2.pusher.com/app/32cbd69e4b950bf97679?protocol=7&client=js&version=7.6.0&flash=false';
+let kickSubBadges = [];
/////////////
// OPTIONS //
@@ -53,8 +55,10 @@ const showTwitchChannelPointRedemptions = GetBooleanParam("showTwitchChannelPoin
const showTwitchRaids = GetBooleanParam("showTwitchRaids", true);
const showTwitchSharedChat = GetIntParam("showTwitchSharedChat", 2);
+const enableKickSupport = GetBooleanParam("enableKickSupport", false);
+const kickUsername = urlParams.get("kickUsername") || "";
const showKickMessages = GetBooleanParam("showKickMessages", true);
-const showKickFollows = GetBooleanParam("showKickFollows", false);
+//const showKickFollows = GetBooleanParam("showKickFollows", false);
const showKickSubs = GetBooleanParam("showKickSubs", true);
const showKickChannelPointRedemptions = GetBooleanParam("showKickChannelPointRedemptions", true);
const showKickHosts = GetBooleanParam("showKickHosts", true);
@@ -294,50 +298,145 @@ client.on('Fourthwall.GiftDrawEnded', (response) => {
FourthwallGiftDrawEnded(response.data);
})
-client.on('Custom.CodeEvent', (response) => {
- console.debug(response.data);
- CustomCodeEvent(response.data);
-})
+// client.on('Custom.CodeEvent', (response) => {
+// console.debug(response.data);
+// CustomCodeEvent(response.data);
+// })
-function CustomCodeEvent(data) {
- const eventName = data.eventName;
- const eventArgs = data.args;
+// function CustomCodeEvent(data) {
+// const eventName = data.eventName;
+// const eventArgs = data.args;
- switch (eventName) {
- case "kickChatMessage":
- KickChatMessage(eventArgs);
- break;
- case "kickFollow":
- KickFollow(eventArgs);
- break;
- case "kickSub":
- KickSub(eventArgs);
- break;
- case "kickGift":
- KickGift(eventArgs);
- break;
- case "kickGifts":
- KickGifts(eventArgs);
- break;
- case "kickRewardRedeemed":
- KickRewardRedeemed(eventArgs);
- break;
- case "kickIncomingRaid":
- KickIncomingRaid(eventArgs);
- break;
- case "kickChatMessageDeleted":
- KickChatMessageDeleted(eventArgs);
- break;
- case "kickBan":
- KickBan(eventArgs);
- break;
- case "kickTO":
- KickBan(eventArgs);
- break;
-
+// switch (eventName) {
+// case "kickChatMessage":
+// KickChatMessage(eventArgs);
+// break;
+// case "kickFollow":
+// KickFollow(eventArgs);
+// break;
+// case "kickSub":
+// KickSub(eventArgs);
+// break;
+// case "kickGift":
+// KickGift(eventArgs);
+// break;
+// case "kickGifts":
+// KickGifts(eventArgs);
+// break;
+// case "kickRewardRedeemed":
+// KickRewardRedeemed(eventArgs);
+// break;
+// case "kickIncomingRaid":
+// KickIncomingRaid(eventArgs);
+// break;
+// case "kickChatMessageDeleted":
+// KickChatMessageDeleted(eventArgs);
+// break;
+// case "kickBan":
+// KickBan(eventArgs);
+// break;
+// case "kickTO":
+// KickBan(eventArgs);
+// break;
+
+// }
+// }
+
+
+
+///////////////////////////
+// KICK PUSHER WEBSOCKET //
+///////////////////////////
+
+// Connect and handle Pusher WebSocket
+async function KickConnect() {
+ if (!enableKickSupport)
+ return;
+
+ // Channel to subscribe to (you'll need the correct channel name here)
+ const chatroomId = await GetKickChatroomId(kickUsername);
+
+ // Cache subscriber badges
+ kickSubBadges = await GetKickSubBadges(kickUsername);
+
+ const websocket = new WebSocket(kickPusherWsUrl);
+
+ websocket.onopen = function () {
+ console.log(`Kick successfully conntected to ${kickUsername}.`);
+ }
+
+ websocket.onmessage = function (response) {
+ try {
+ let data = JSON.parse(response.data);
+
+ console.debug(data);
+
+ // When connection is established, subscribe to a channel
+ if (data.event === 'pusher:connection_established') {
+ const socketData = JSON.parse(data.data);
+ console.log(`[Pusher] Socket established with ID: ${socketData.socket_id}`);
+
+ // Now subscribe to a channel
+ let subscribeMessage = {
+ event: 'pusher:subscribe',
+ data: {
+ channel: `chatroom_${chatroomId}`
+ }
+ };
+
+ websocket.send(JSON.stringify(subscribeMessage));
+
+ // Now subscribe to a channel
+ subscribeMessage = {
+ event: 'pusher:subscribe',
+ data: {
+ channel: `chatrooms.${chatroomId}.v2`
+ }
+ };
+
+ websocket.send(JSON.stringify(subscribeMessage));
+ console.log(`[Pusher] Sent subscription request to channel: ${chatroomId}`);
+ }
+
+ // Event handlers
+ const eventArgs = JSON.parse(data.data);
+ const event = data.event.split('\\').pop();
+ switch (event) {
+ case 'ChatMessageEvent':
+ KickChatMessage(eventArgs);
+ break;
+ //// 'Follows' unsupported by pusher
+ // case 'FollowEvent':
+ // break;
+ case 'SubscriptionEvent':
+ KickSubscription(eventArgs);
+ break;
+ case 'GiftedSubscriptionsEvent':
+ KickGiftedSubscriptions(eventArgs);
+ break;
+ case 'RewardRedeemedEvent':
+ KickRewardRedeemed(eventArgs);
+ break;
+ case 'StreamHostEvent':
+ KickStreamHost(eventArgs);
+ break;
+ case 'MessageDeletedEvent':
+ KickMessageDeleted(eventArgs);
+ break;
+ case 'UserBannedEvent':
+ KickUserBanned(eventArgs);
+ break;
+ }
+ }
+ catch (error) {
+ console.error(error);
+ }
}
}
+// Try connect when window is loaded
+window.addEventListener('load', KickConnect);
+
//////////////////////
@@ -346,7 +445,7 @@ function CustomCodeEvent(data) {
let tikfinityWebsocket = null;
-function tikfinityConnect() {
+function TikfinityConnect() {
if (tikfinityWebsocket) return; // Already connected
tikfinityWebsocket = new WebSocket("ws://localhost:21213/");
@@ -358,13 +457,13 @@ function tikfinityConnect() {
tikfinityWebsocket.onclose = function () {
console.error(`TikFinity disconnected...`)
tikfinityWebsocket = null;
- setTimeout(tikfinityConnect, 1000); // Schedule a reconnect attempt
+ setTimeout(TikfinityConnect, 1000); // Schedule a reconnect attempt
}
tikfinityWebsocket.onerror = function () {
console.error(`TikFinity failed for some reason...`)
tikfinityWebsocket = null;
- setTimeout(tikfinityConnect, 1000); // Schedule a reconnect attempt
+ setTimeout(TikfinityConnect, 1000); // Schedule a reconnect attempt
}
tikfinityWebsocket.onmessage = function (response) {
@@ -390,7 +489,7 @@ function tikfinityConnect() {
}
// Try connect when window is loaded
-window.addEventListener('load', tikfinityConnect);
+window.addEventListener('load', TikfinityConnect);
@@ -432,10 +531,9 @@ async function TwitchChatMessage(data) {
const pronounsDiv = instance.querySelector("#pronouns");
const usernameDiv = instance.querySelector("#username");
const messageDiv = instance.querySelector("#message");
-
+
// Render bubbles
- if (useChatBubbles)
- {
+ if (useChatBubbles) {
const opacity255 = Math.round(parseFloat(bubbleOpacity) * 255);
let hexOpacity = opacity255.toString(16);
if (hexOpacity.length < 2) {
@@ -588,7 +686,7 @@ async function TwitchChatMessage(data) {
if (groupConsecutiveMessages && messageList.children.length > 0 && scrollDirection != 2) {
const lastPlatform = messageList.lastChild.dataset.platform;
const lastUserId = messageList.lastChild.dataset.userId;
- if (lastPlatform == "twitch" && lastUserId == data.user.id){
+ if (lastPlatform == "twitch" && lastUserId == data.user.id) {
userInfoDiv.style.display = "none";
avatarDiv.innerHTML = '';
}
@@ -618,8 +716,7 @@ async function TwitchChatMessage(data) {
}
// Render YouTube links
- if (youtubeRegex.test(message))
- {
+ if (youtubeRegex.test(message)) {
const videoId = ExtractYouTubeVideoId(message);
const videoData = await GetYouTubeVideoData(videoId);
@@ -1124,10 +1221,9 @@ async function YouTubeMessage(data) {
const badgeListDiv = instance.querySelector("#badgeList");
const usernameDiv = instance.querySelector("#username");
const messageDiv = instance.querySelector("#message");
-
+
// Render bubbles
- if (useChatBubbles)
- {
+ if (useChatBubbles) {
const opacity255 = Math.round(parseFloat(bubbleOpacity) * 255);
let hexOpacity = opacity255.toString(16);
if (hexOpacity.length < 2) {
@@ -1258,8 +1354,7 @@ async function YouTubeMessage(data) {
}
// Render YouTube links
- if (youtubeRegex.test(data.message))
- {
+ if (youtubeRegex.test(data.message)) {
const videoId = ExtractYouTubeVideoId(data.message);
const videoData = await GetYouTubeVideoData(videoId);
@@ -1991,11 +2086,11 @@ async function KickChatMessage(data) {
return;
// Don't post messages starting with "!"
- if (data.message.startsWith("!") && excludeCommands)
+ if (data.content.startsWith("!") && excludeCommands)
return;
// Don't post messages from users from the ignore list
- if (ignoreUserList.includes(data.user.toLowerCase()))
+ if (ignoreUserList.includes(data.sender.username.toLowerCase()))
return;
// Get a reference to the template
@@ -2020,10 +2115,9 @@ async function KickChatMessage(data) {
const pronounsDiv = instance.querySelector("#pronouns");
const usernameDiv = instance.querySelector("#username");
const messageDiv = instance.querySelector("#message");
-
+
// Render bubbles
- if (useChatBubbles)
- {
+ if (useChatBubbles) {
const opacity255 = Math.round(parseFloat(bubbleOpacity) * 255);
let hexOpacity = opacity255.toString(16);
if (hexOpacity.length < 2) {
@@ -2033,18 +2127,18 @@ async function KickChatMessage(data) {
messageContainerDiv.classList.add("bubble");
}
- // Set First Time Chatter
- const firstMessage = data.firstMessage;
- if (firstMessage && showMessage) {
- firstMessageDiv.style.display = 'block';
- messageContainerDiv.classList.add("highlightMessage");
- }
+ // // Set First Time Chatter
+ // const firstMessage = data.firstMessage;
+ // if (firstMessage && showMessage) {
+ // firstMessageDiv.style.display = 'block';
+ // messageContainerDiv.classList.add("highlightMessage");
+ // }
// Set Reply Message
- const isReply = data.isReply;
+ const isReply = data.type == 'reply';
if (isReply && showMessage) {
- const replyUser = data["reply.userName"];
- const replyMsg = data["reply.message"];
+ const replyUser = data.metadata.original_sender.username;
+ const replyMsg = data.metadata.original_message.content;
replyDiv.style.display = 'block';
replyUserDiv.innerText = replyUser;
@@ -2059,14 +2153,18 @@ async function KickChatMessage(data) {
// Set the username info
if (showUsername) {
- usernameDiv.innerText = data.user;
- usernameDiv.style.color = data.color;
+ usernameDiv.innerText = data.sender.username;
+ usernameDiv.style.color = data.sender.identity.color;
}
// Set the message data
- let message = data.message;
- const messageColor = data.color;
- const role = data.role;
+ let message = data.content;
+
+ // Highlight mentions
+ const mentionRgx = new RegExp(`(^|\\s)@${kickUsername}(\\s|$)`, 'i');
+ const mention = mentionRgx.test(message);
+ if (mention && showMessage)
+ messageContainerDiv.classList.add("highlightMessage");
// Set furry mode
if (furryMode)
@@ -2077,10 +2175,6 @@ async function KickChatMessage(data) {
messageDiv.innerText = message;
}
- // Set the "action" color
- if (data.isAction)
- messageDiv.style.color = messageColor;
-
// Remove the line break
if (inlineChat) {
instance.querySelector("#colon-separator").style.display = `inline`;
@@ -2094,52 +2188,15 @@ async function KickChatMessage(data) {
platformDiv.innerHTML = platformElements;
}
- // // Render badges
- // if (showBadges) {
- // badgeListDiv.innerHTML = "";
- // for (i in data.message.badges) {
- // const badge = new Image();
- // badge.src = data.message.badges[i].imageUrl;
- // badge.classList.add("badge");
- // badgeListDiv.appendChild(badge);
- // }
- // }
-
// Render badges
- // if (data.user.isOwner && showBadges) {
- // const badge = new Image();
- // badge.src = `icons/badges/youtube-broadcaster.svg`;
- // badge.style.filter = `invert(100%)`;
- // badge.style.opacity = 0.8;
- // badge.classList.add("badge");
- // badgeListDiv.appendChild(badge);
- // }
-
- if (data.isModerator && showBadges) {
- const badge = new Image();
- badge.src = `icons/badges/youtube-moderator.svg`;
- badge.style.filter = `invert(100%)`;
- badge.style.opacity = 0.8;
- badge.classList.add("badge");
- badgeListDiv.appendChild(badge);
- }
-
- if (data.isSubscribed && showBadges) {
- const badge = new Image();
- badge.src = `icons/badges/youtube-member.svg`;
- badge.style.filter = `invert(100%)`;
- badge.style.opacity = 0.8;
- badge.classList.add("badge");
- badgeListDiv.appendChild(badge);
- }
-
- if (data.isVip && showBadges) {
- const badge = new Image();
- badge.src = `icons/badges/youtube-verified.svg`;
- badge.style.filter = `invert(100%)`;
- badge.style.opacity = 0.8;
- badge.classList.add("badge");
- badgeListDiv.appendChild(badge);
+ if (showBadges) {
+ badgeListDiv.innerHTML = "";
+ for (i in data.sender.identity.badges) {
+ const badge = new Image();
+ badge.src = GetKickBadgeURL(data.sender.identity.badges[i]);
+ badge.classList.add("badge");
+ badgeListDiv.appendChild(badge);
+ }
}
// Render emotes
@@ -2155,7 +2212,7 @@ async function KickChatMessage(data) {
// Render avatars
if (showAvatar) {
- const username = data.userName;
+ const username = data.sender.slug;
const avatarURL = await GetAvatar(username, 'kick');
const avatar = new Image();
avatar.src = avatarURL;
@@ -2169,7 +2226,7 @@ async function KickChatMessage(data) {
if (groupConsecutiveMessages && messageList.children.length > 0 && scrollDirection != 2) {
const lastPlatform = messageList.lastChild.dataset.platform;
const lastUserId = messageList.lastChild.dataset.userId;
- if (lastPlatform == "kick" && lastUserId == data.userId)
+ if (lastPlatform == "kick" && lastUserId == data.sender.id)
userInfoDiv.style.display = "none";
}
@@ -2183,7 +2240,7 @@ async function KickChatMessage(data) {
messageDiv.innerHTML = '';
messageDiv.appendChild(image);
- AddMessageItem(instance, data.msgId, 'kick', data.userId);
+ AddMessageItem(instance, data.id, 'kick', data.sender.id);
};
const urlObj = new URL(message);
@@ -2193,12 +2250,11 @@ async function KickChatMessage(data) {
image.src = "https://external-content.duckduckgo.com/iu/?u=" + urlObj.toString();
}
else {
- AddMessageItem(instance, data.msgId, 'kick', data.userId);
+ AddMessageItem(instance, data.id, 'kick', data.sender.id);
}
// Render YouTube links
- if (youtubeRegex.test(message))
- {
+ if (youtubeRegex.test(message)) {
const videoId = ExtractYouTubeVideoId(message);
const videoData = await GetYouTubeVideoData(videoId);
@@ -2206,35 +2262,35 @@ async function KickChatMessage(data) {
}
}
-async function KickFollow(data) {
- if (!showKickFollows)
- return;
+// async function KickFollow(data) {
+// if (!showKickFollows)
+// return;
- // Get a reference to the template
- const template = document.getElementById('cardTemplate');
+// // Get a reference to the template
+// const template = document.getElementById('cardTemplate');
- // Create a new instance of the template
- const instance = template.content.cloneNode(true);
+// // Create a new instance of the template
+// const instance = template.content.cloneNode(true);
- // Get divs
- const cardDiv = instance.querySelector("#card");
- const headerDiv = instance.querySelector("#header");
- const avatarDiv = instance.querySelector("#avatar");
- const iconDiv = instance.querySelector("#icon");
- const titleDiv = instance.querySelector("#title");
- const contentDiv = instance.querySelector("#contentDiv");
+// // Get divs
+// const cardDiv = instance.querySelector("#card");
+// const headerDiv = instance.querySelector("#header");
+// const avatarDiv = instance.querySelector("#avatar");
+// const iconDiv = instance.querySelector("#icon");
+// const titleDiv = instance.querySelector("#title");
+// const contentDiv = instance.querySelector("#contentDiv");
- // Set the card background colors
- cardDiv.classList.add('kick');
+// // Set the card background colors
+// cardDiv.classList.add('kick');
- // Set the text
- let username = data.user;
- titleDiv.innerText = `${username} followed`;
+// // Set the text
+// let username = data.user;
+// titleDiv.innerText = `${username} followed`;
- AddMessageItem(instance, data.messageId);
-}
+// AddMessageItem(instance, data.messageId);
+// }
-async function KickSub(data) {
+async function KickSubscription(data) {
if (!showKickSubs)
return;
@@ -2250,34 +2306,30 @@ async function KickSub(data) {
const avatarDiv = instance.querySelector("#avatar");
const iconDiv = instance.querySelector("#icon");
const titleDiv = instance.querySelector("#title");
- const contentDiv = instance.querySelector("#contentDiv");
+ const contentDiv = instance.querySelector("#content");
// Set the card background colors
cardDiv.classList.add('kick');
- // // Set the card header
- // for (i in data.user.badges) {
- // if (data.user.badges[i].name == "subscriber") {
- // const badge = new Image();
- // badge.src = data.user.badges[i].imageUrl;
- // badge.classList.add("badge");
- // iconDiv.appendChild(badge);
- // }
- // }
-
// Set the card header
const badge = new Image();
- badge.src = 'icons/platforms/kick.png';
+ badge.src = 'icons/platforms/kick.png'; //badge.src = CalculateKickSubBadge(data.months);
badge.classList.add("badge");
iconDiv.appendChild(badge);
// Set the text
- titleDiv.innerText = data.rawInput;
+ const username = data.username;
+ const months = data.months;
- AddMessageItem(instance, data.messageId);
+ if (months <= 1)
+ titleDiv.innerText = `${username} just subscribed for the first time!`;
+ else
+ titleDiv.innerText = `${username} resubscribed! (${months} months)`;
+
+ AddMessageItem(instance);
}
-async function KickGift(data) {
+async function KickGiftedSubscriptions(data) {
if (!showKickSubs)
return;
@@ -2305,12 +2357,19 @@ async function KickGift(data) {
iconDiv.appendChild(badge);
// Set the text
- titleDiv.innerText = `${data.rawInput}`;
+ const gifter = data.gifter_username;
+ const gifts = data.gifter_total;
+ titleDiv.innerText = `${gifter} gifted ${gifts} subscription${gifts === 1 ? '' : 's'} to the community!`;
- AddMessageItem(instance, data.messageId);
+ if (gifts > 1)
+ AddMessageItem(instance, data.messageId);
+
+ // Send individual notifications for every gifted user
+ for (const username of data.gifted_usernames)
+ KickGiftToUser(gifter, username);
}
-async function KickGifts(data) {
+async function KickGiftToUser(gifter, username) {
if (!showKickSubs)
return;
@@ -2338,9 +2397,7 @@ async function KickGifts(data) {
iconDiv.appendChild(badge);
// Set the text
- const username = data.user;
- const gifts = data.gifts;
- titleDiv.innerText = `${username} gifted ${gifts} subs to the community!`;
+ titleDiv.innerText = `${gifter} gifted a sub to ${username}`;
AddMessageItem(instance, data.messageId);
}
@@ -2368,7 +2425,7 @@ async function KickRewardRedeemed(data) {
// Render avatars
if (showAvatar) {
- const username = data.user;
+ const username = data.username;
const avatarURL = await GetAvatar(username, 'kick');
const avatar = new Image();
avatar.src = avatarURL;
@@ -2377,9 +2434,9 @@ async function KickRewardRedeemed(data) {
}
// Set the text
- let username = data.user;
- const rewardName = data.rewardTitle;
- const userInput = data.rewardUserInput;
+ const username = data.username;
+ const rewardName = data.reward_title;
+ const userInput = data.user_input;
titleDiv.innerHTML = `${username} redeemed ${rewardName}`;
contentDiv.innerText = `${userInput}`;
@@ -2387,7 +2444,7 @@ async function KickRewardRedeemed(data) {
AddMessageItem(instance, data.redeemId);
}
-async function KickIncomingRaid(data) {
+async function KickStreamHost(data) {
if (!showKickHosts)
return;
@@ -2410,7 +2467,7 @@ async function KickIncomingRaid(data) {
// Render avatars
if (showAvatar) {
- const username = data.user;
+ const username = data.host_username;
const avatarURL = await GetAvatar(username, 'kick');
const avatar = new Image();
avatar.src = avatarURL;
@@ -2418,10 +2475,9 @@ async function KickIncomingRaid(data) {
avatarDiv.appendChild(avatar);
}
-
// Set the text
- const username = data.user;
- const viewers = data.viewers;
+ const username = data.host_username;
+ const viewers = data.number_viewers;
titleDiv.innerText = `${username} is raiding`;
contentDiv.innerText = `with a party of ${viewers}`;
@@ -2429,40 +2485,43 @@ async function KickIncomingRaid(data) {
AddMessageItem(instance, data.messageId);
}
-function KickChatMessageDeleted(data) {
+function KickMessageDeleted(data) {
const messageList = document.getElementById("messageList");
// Maintain a list of chat messages to delete
const messagesToRemove = [];
// ID of the message to remove
- const messageId = data.msgId;
+ const messageId = data.message.id;
- // Find the items to remove
- for (let i = 0; i < messageList.children.length; i++) {
- if (messageList.children[i].id === messageId) {
- messagesToRemove.push(messageList.children[i]);
+ // Add a 200ms to ensure the automod doesn't delete the message before it's been added to the overlay
+ setTimeout(() => {
+ // Find the items to remove
+ for (let i = 0; i < messageList.children.length; i++) {
+ if (messageList.children[i].id === messageId) {
+ messagesToRemove.push(messageList.children[i]);
+ }
}
- }
- // Remove the items
- messagesToRemove.forEach(item => {
- item.style.opacity = 0;
- item.style.height = 0;
- setTimeout(function () {
- messageList.removeChild(item);
- }, 1000);
- });
+ // Remove the items
+ messagesToRemove.forEach(item => {
+ item.style.opacity = 0;
+ item.style.height = 0;
+ setTimeout(function () {
+ messageList.removeChild(item);
+ }, 1000);
+ });
+ }, 500);
}
-function KickBan(data) {
+function KickUserBanned(data) {
const messageList = document.getElementById("messageList");
// Maintain a list of chat messages to delete
const messagesToRemove = [];
// ID of the message to remove
- const userId = data.userId;
+ const userId = data.user.id;
// Find the items to remove
for (let i = 0; i < messageList.children.length; i++) {
@@ -2510,10 +2569,9 @@ async function TikTokChat(data) {
const pronounsDiv = instance.querySelector("#pronouns");
const usernameDiv = instance.querySelector("#username");
const messageDiv = instance.querySelector("#message");
-
+
// Render bubbles
- if (useChatBubbles)
- {
+ if (useChatBubbles) {
const opacity255 = Math.round(parseFloat(bubbleOpacity) * 255);
let hexOpacity = opacity255.toString(16);
if (hexOpacity.length < 2) {
@@ -2597,7 +2655,7 @@ async function TikTokChat(data) {
if (groupConsecutiveMessages && messageList.children.length > 0 && scrollDirection != 2) {
const lastPlatform = messageList.lastChild.dataset.platform;
const lastUserId = messageList.lastChild.dataset.userId;
- if (lastPlatform == "tiktok" && lastUserId == data.userId){
+ if (lastPlatform == "tiktok" && lastUserId == data.userId) {
userInfoDiv.style.display = "none";
avatarDiv.innerHTML = '';
}
@@ -2606,8 +2664,7 @@ async function TikTokChat(data) {
AddMessageItem(instance, data.msgId, 'tiktok', data.userId);
// Render YouTube links
- if (youtubeRegex.test(message))
- {
+ if (youtubeRegex.test(message)) {
const videoId = ExtractYouTubeVideoId(message);
const videoData = await GetYouTubeVideoData(videoId);
@@ -2831,8 +2888,8 @@ function IsImageUrl(url) {
}
function ExtractYouTubeVideoId(url) {
- const match = url.match(youtubeRegex);
- return match ? match[1] : null;
+ const match = url.match(youtubeRegex);
+ return match ? match[1] : null;
}
function AddMessageItem(element, elementID, platform, userId) {
@@ -2948,13 +3005,14 @@ function GetPermissionLevel(data, platform) {
else
return 10;
case 'kick':
- if (data.role >= 4)
+ if (data.sender.identity.badges.some(item => item.type === 'broadcaster'))
return 40;
- else if (data.role >= 3)
+ else if (data.sender.identity.badges.some(item => item.type === 'moderator'))
return 30;
- else if (data.role >= 2)
+ else if (data.sender.identity.badges.some(item => item.type === 'vip') ||
+ data.sender.identity.badges.some(item => item.type === 'og'))
return 20;
- else if (data.role >= 2 || data.isSubscribed)
+ else if (data.sender.identity.badges.some(item => item.type === 'subscriber'))
return 15;
else
return 10;
@@ -3030,6 +3088,54 @@ 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}`;
+
+ 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;
+ }
+}
+
+async function GetKickSubBadges(username) {
+ const response = await fetch(`https://kick.com/api/v2/channels/${username}`);
+ const data = await response.json();
+
+ return data.subscriber_badges || [];
+}
+
+function GetKickBadgeURL(data) {
+ switch (data.type) {
+ case 'subscriber':
+ return CalculateKickSubBadge(data.count);
+ default:
+ return `icons/badges/kick-${data.type}.svg`;
+ }
+}
+
+function CalculateKickSubBadge(months) {
+ if (!Array.isArray(kickSubBadges)) return null;
+
+ // Filter for eligible badges, then get the one with the highest 'months'
+ const badge = kickSubBadges
+ .filter(b => b.months <= months)
+ .sort((a, b) => b.months - a.months)[0];
+
+ return badge?.badge_image?.src || `icons/badges/kick-subscriber.svg`;
+}
+
///////////////////////////////////
@@ -3057,32 +3163,23 @@ function SetConnectionStatus(connected) {
}
async function GetYouTubeVideoData(videoId) {
- const url = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
+ const url = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
- try {
- const response = await fetch(url);
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
- const data = await response.json();
+ const data = await response.json();
- return {
- title: data.title,
- author: data.author_name,
- thumbnail: data.thumbnail_url,
- };
- } catch (error) {
- console.error('Error fetching YouTube video data:', error);
- return null;
- }
-}
-
-async function SemenLover()
-{
- let data = await GetYouTubeVideoData('dQw4w9WgXcQ');
-
- console.log(data);
-}
-
-SemenLover()
\ No newline at end of file
+ return {
+ title: data.title,
+ author: data.author_name,
+ thumbnail: data.thumbnail_url,
+ };
+ } catch (error) {
+ console.error('Error fetching YouTube video data:', error);
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/multichat-overlay/settings/settings.json b/multichat-overlay/settings/settings.json
index e4f8777..e00a97a 100644
--- a/multichat-overlay/settings/settings.json
+++ b/multichat-overlay/settings/settings.json
@@ -304,20 +304,30 @@
"defaultValue": 2,
"group": "Which Twitch messages do you want to see?"
},
+ {
+ "id": "enableKickSupport",
+ "label": "Enable Kick Support",
+ "description": "",
+ "type": "checkbox",
+ "defaultValue": false,
+ "group": "Which Kick messages do you want to see?"
+ },
+ {
+ "id": "kickUsername",
+ "label": "Kick Username",
+ "description": "",
+ "type": "text",
+ "defaultValue": "",
+ "showIf": "enableKickSupport",
+ "group": "Which Kick messages do you want to see?"
+ },
{
"id": "showKickMessages",
"label": "Chat Messages",
"description": "",
"type": "checkbox",
"defaultValue": true,
- "group": "Which Kick messages do you want to see?"
- },
- {
- "id": "showKickFollows",
- "label": "New Followers",
- "description": "",
- "type": "checkbox",
- "defaultValue": false,
+ "showIf": "enableKickSupport",
"group": "Which Kick messages do you want to see?"
},
{
@@ -326,6 +336,7 @@
"description": "",
"type": "checkbox",
"defaultValue": true,
+ "showIf": "enableKickSupport",
"group": "Which Kick messages do you want to see?"
},
{
@@ -334,6 +345,7 @@
"description": "",
"type": "checkbox",
"defaultValue": true,
+ "showIf": "enableKickSupport",
"group": "Which Kick messages do you want to see?"
},
{
@@ -342,6 +354,7 @@
"description": "",
"type": "checkbox",
"defaultValue": true,
+ "showIf": "enableKickSupport",
"group": "Which Kick messages do you want to see?"
},
{