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;
}