diff --git a/horizontal-chat/script.js b/horizontal-chat/script.js index f05da84..145a3aa 100644 --- a/horizontal-chat/script.js +++ b/horizontal-chat/script.js @@ -310,6 +310,60 @@ function CustomCodeEvent(data) { +////////////////////// +// TIKFINITY CLIENT // +////////////////////// + +let tikfinityWebsocket = null; + +function tikfinityConnect() { + if (tikfinityWebsocket) return; // Already connected + + tikfinityWebsocket = new WebSocket("ws://localhost:21213/"); + + tikfinityWebsocket.onopen = function () { + console.log(`TikFinity successfully connected...`) + } + + tikfinityWebsocket.onclose = function () { + console.error(`TikFinity disconnected...`) + tikfinityWebsocket = null; + 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 + } + + tikfinityWebsocket.onmessage = function (response) { + let payload = JSON.parse(response.data); + + let event = payload.event; + let data = payload.data; + + console.debug('Event: ' + event); + + switch (event) { + case 'chat': + TikTokChat(data); + break; + case 'gift': + TikTokGift(data); + break; + case 'subscribe': + TikTokSubscribe(data); + break; + } + } +} + +// Try connect when window is loaded +window.addEventListener('load', tikfinityConnect); + + + ///////////////////// // HORIZONTAL CHAT // ///////////////////// @@ -1312,6 +1366,129 @@ function KickBan(data) { }); } +async function TikTokChat(data) { + // Don't post messages starting with "!" + if (data.comment.startsWith("!") && excludeCommands) + return; + + // Don't post messages from users from the ignore list + if (ignoreUserList.includes(data.comment.toLowerCase())) + return; + + // Get a reference to the template + const template = document.getElementById('messageTemplate'); + + // Create a new instance of the template + const instance = template.content.cloneNode(true); + + // Get divs + const userInfoDiv = instance.querySelector("#userInfo"); + const avatarDiv = instance.querySelector("#avatar"); + const timestampDiv = instance.querySelector("#timestamp"); + const platformDiv = instance.querySelector("#platform"); + const badgeListDiv = instance.querySelector("#badgeList"); + const pronounsDiv = instance.querySelector("#pronouns"); + const usernameDiv = instance.querySelector("#username"); + const messageDiv = instance.querySelector("#message"); + + // Set timestamp + if (showTimestamps) { + timestampDiv.classList.add("timestamp"); + timestampDiv.innerText = GetCurrentTimeFormatted(); + } + + // Set the username info + if (showUsername) { + usernameDiv.innerText = data.nickname; + usernameDiv.style.color = '#9e9e9e'; + } + + // Set the message data + let message = data.comment; + + // Set furry mode + if (furryMode) + message = TranslateToFurry(message); + + // Set message text + if (showMessage) { + messageDiv.innerText = message; + } + + // Render platform + if (showPlatform) { + const platformElements = ``; + platformDiv.innerHTML = platformElements; + } + + // Render badges + if (showBadges) { + badgeListDiv.innerHTML = ""; + + if (data.isModerator) { + 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); + } + + for (i in data.userBadges) { + if (data.userBadges[i].type == 'image') { + const badge = new Image(); + badge.src = data.userBadges[i].url; + badge.classList.add("badge"); + badgeListDiv.appendChild(badge); + } + } + } + + // Render avatars + if (showAvatar) { + const avatar = new Image(); + avatar.src = data.profilePictureUrl; + avatar.classList.add("avatar"); + avatarDiv.appendChild(avatar); + } + + // Hide the header if the same username sends a message twice in a row + const messageList = document.getElementById("messageList"); + if (groupConsecutiveMessages && messageList.children.length > 0) { + const lastPlatform = messageList.lastChild.dataset.platform; + const lastUserId = messageList.lastChild.dataset.userId; + if (lastPlatform == "tiktok" && lastUserId == data.userId) + userInfoDiv.style.display = "none"; + } + + AddMessageItem(instance, data.msgId, 'tiktok', data.userId); +} + +async function TikTokGift(data) { + if (data.giftType === 1 && !data.repeatEnd) { + // Streak in progress => show only temporary + console.debug(`${data.uniqueId} is sending gift ${data.giftName} x${data.repeatCount}`); + return; + } + + // Streak ended or non-streakable gift => process the gift with final repeat_count + console.debug(`${data.uniqueId} has sent gift ${data.giftName} x${data.repeatCount}`); + + const giftImg = ``; + + const message = `${data.nickname} sent ${giftImg}x${data.repeatCount}`; + + ShowAlert(message, 'tiktok'); +} + +async function TikTokSubscribe(data) { + let username = data.nickname; + + const message = `${username} subscribed on TikTok`; + + ShowAlert(message, 'tiktok'); +} + ////////////////////// @@ -1521,7 +1698,7 @@ function ShowAlert(message, background = null, duration = animationDuration) { const alertBoxContent = document.querySelector("#alertBoxContent"); // Set the message text - alertBoxContent.innerHTML = message; + alertBoxContent.innerHTML = message; // Set the background alertBoxDiv.classList.add(background); diff --git a/horizontal-chat/style.css b/horizontal-chat/style.css index 4891d2c..baf5c59 100644 --- a/horizontal-chat/style.css +++ b/horizontal-chat/style.css @@ -280,6 +280,10 @@ li { background: linear-gradient(#466edb, #1c56f5) !important; } +.tiktok { + background: linear-gradient(#262626, #000000) !important; +} + .blank { background: #adadad00 !important; } diff --git a/multichat-overlay/script.js b/multichat-overlay/script.js index f904a1d..e199ca1 100644 --- a/multichat-overlay/script.js +++ b/multichat-overlay/script.js @@ -1181,6 +1181,10 @@ function YouTubeMessage(data) { avatar.classList.add("avatar"); avatarDiv.appendChild(avatar); } + else + { + avatarDiv.style.display = 'none'; + } // Hide the header if the same username sends a message twice in a row @@ -2094,6 +2098,10 @@ async function KickChatMessage(data) { avatar.classList.add("avatar"); avatarDiv.appendChild(avatar); } + else + { + avatarDiv.style.display = 'none'; + } // Hide the header if the same username sends a message twice in a row // EXCEPT when the scroll direction is set to reverse (scrollDirection == 2) @@ -2501,6 +2509,22 @@ function TikTokChat(data) { avatar.classList.add("avatar"); avatarDiv.appendChild(avatar); } + else + { + avatarDiv.style.display = 'none'; + } + + // Hide the header if the same username sends a message twice in a row + // EXCEPT when the scroll direction is set to reverse (scrollDirection == 2) + const messageList = document.getElementById("messageList"); + 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){ + userInfoDiv.style.display = "none"; + avatarDiv.innerHTML = ''; + } + } AddMessageItem(instance, data.msgId, 'tiktok', data.userId); } diff --git a/multistream-alerts/icons/platforms/kick.png b/multistream-alerts/icons/platforms/kick.png new file mode 100644 index 0000000..9cc9afa Binary files /dev/null and b/multistream-alerts/icons/platforms/kick.png differ diff --git a/multistream-alerts/icons/platforms/tiktok.png b/multistream-alerts/icons/platforms/tiktok.png new file mode 100644 index 0000000..c9e0da5 Binary files /dev/null and b/multistream-alerts/icons/platforms/tiktok.png differ diff --git a/multistream-alerts/script.js b/multistream-alerts/script.js index deedede..66fef18 100644 --- a/multistream-alerts/script.js +++ b/multistream-alerts/script.js @@ -318,6 +318,57 @@ function CustomCodeEvent(data) { +////////////////////// +// TIKFINITY CLIENT // +////////////////////// + +let tikfinityWebsocket = null; + +function tikfinityConnect() { + if (tikfinityWebsocket) return; // Already connected + + tikfinityWebsocket = new WebSocket("ws://localhost:21213/"); + + tikfinityWebsocket.onopen = function () { + console.log(`TikFinity successfully connected...`) + } + + tikfinityWebsocket.onclose = function () { + console.error(`TikFinity disconnected...`) + tikfinityWebsocket = null; + 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 + } + + tikfinityWebsocket.onmessage = function (response) { + let payload = JSON.parse(response.data); + + let event = payload.event; + let data = payload.data; + + console.debug('Event: ' + event); + + switch (event) { + case 'gift': + TikTokGift(data); + break; + case 'subscribe': + TikTokSubscribe(data); + break; + } + } +} + +// Try connect when window is loaded +window.addEventListener('load', tikfinityConnect); + + + /////////////////////// // MULTICHAT OVERLAY // /////////////////////// @@ -1268,6 +1319,49 @@ async function KickIncomingRaid(data) { ); } +async function TikTokGift(data) { + // Set the text + const username = data.nickname; + const tiktokIcon = ``; + const giftImg = ``; + + // Render avatars + const avatarURL = 'icons/platforms/tiktok.png'; + + UpdateAlertBox( + 'tiktok', + avatarURL, + `${username}`, + `sent ${giftImg}x${data.repeatCount}`, + '', + username, + '', + '', //twitchSubAction, + data + ); +} + +async function TikTokSubscribe(data) { + // Set the text + const username = data.nickname; + const tiktokIcon = ``; + + // Render avatars + const avatarURL = 'icons/platforms/tiktok.png'; + + UpdateAlertBox( + 'tiktok', + avatarURL, + `${username}`, + `subscribed on TikTok`, + '', + username, + '', + '', //twitchSubAction, + data + ); +} + ////////////////////// @@ -1596,4 +1690,80 @@ function SetConnectionStatus(connected) { statusContainer.style.transition = ""; statusContainer.style.opacity = 1; } -} \ No newline at end of file +} + +let data = { + "giftId": 5655, + "repeatCount": 1, + "repeatEnd": true, + "groupId": "1750261118899", + "userId": "6955276871237370885", + "secUid": "MS4wLjABAAAAWFtVMVXKfBB_hnuByyirANl3y8R4o8PC9clvNivtbD5JvdS6JtmCo0GR9int0mNU", + "uniqueId": "deejayamme", + "nickname": "Amme", + "profilePictureUrl": "https://p77-sign-va.tiktokcdn.com/tos-maliva-avt-0068/7332939462534447110~tplv-tiktokx-cropcenter:100:100.webp?dr=10399&refresh_token=b162ebd3&x-expires=1750431600&x-signature=9bDYBHHAzrlaBnA9%2BkBW37toUrI%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=no1a", + "followRole": 0, + "userBadges": [ + { + "type": "image", + "badgeSceneType": 6, + "displayType": 1, + "url": "https://p19-webcast.tiktokcdn.com/webcast-sg/new_top_gifter_version_2.png~tplv-obj.image" + }, + { + "type": "privilege", + "privilegeId": "7138381861675357988", + "level": 22, + "badgeSceneType": 8 + } + ], + "userSceneTypes": [ + 6, + 8, + 6 + ], + "userDetails": { + "createTime": "0", + "bioDescription": "", + "profilePictureUrls": [ + "https://p77-sign-va.tiktokcdn.com/tos-maliva-avt-0068/7332939462534447110~tplv-tiktokx-cropcenter:100:100.webp?dr=10399&refresh_token=b162ebd3&x-expires=1750431600&x-signature=9bDYBHHAzrlaBnA9%2BkBW37toUrI%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=no1a", + "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/7332939462534447110~tplv-tiktokx-cropcenter:100:100.webp?dr=10399&refresh_token=e43e92cc&x-expires=1750431600&x-signature=UD9nuFnspy%2B%2Bi0ouX2FfTf1EUX0%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=no1a", + "https://p77-sign-va.tiktokcdn.com/tos-maliva-avt-0068/7332939462534447110~tplv-tiktokx-cropcenter:100:100.jpeg?dr=10399&refresh_token=32267bb8&x-expires=1750431600&x-signature=rEkdnHYpCSdnuS4IFZ7Xgg6qb0A%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=no1a" + ] + }, + "followInfo": { + "followingCount": 9109, + "followerCount": 6426, + "followStatus": 0, + "pushStatus": 0 + }, + "isModerator": false, + "isNewGifter": false, + "isSubscriber": false, + "topGifterRank": 1, + "gifterLevel": 22, + "teamMemberLevel": 0, + "msgId": "7517314100029606678", + "createTime": "1750261122519", + "displayType": "webcast_aweme_gift_send_messageNew", + "label": "{0:user} sent {1:gift} × {2:string}", + "gift": { + "gift_id": 5655, + "repeat_count": 1, + "repeat_end": 1, + "gift_type": 1 + }, + "describe": "sent Rose", + "giftType": 1, + "diamondCount": 1, + "giftName": "Rose", + "giftPictureUrl": "https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png", + "timestamp": 1750261122520, + "receiverUserId": "7050235313499374598", + "originalName": "Rose", + "originalDescribe": "Sent Rose", + "tikfinityUserId": 1903738, + "tikfinityUsername": "nuttylmao" +} + +TikTokGift(data); \ No newline at end of file diff --git a/multistream-alerts/style.css b/multistream-alerts/style.css index 2b2a330..58a1cd1 100644 --- a/multistream-alerts/style.css +++ b/multistream-alerts/style.css @@ -335,6 +335,10 @@ html { background: linear-gradient(#466edbbf, #1c56f5bf) !important; } +.tiktok { + background: linear-gradient(#262626BF, #000000BF) !important; +} + .blank { background: #adadad00 !important; }