Full Reset
This commit is contained in:
1017
js/app-mockup.js
1017
js/app-mockup.js
File diff suppressed because it is too large
Load Diff
718
js/app.js
718
js/app.js
@@ -1,718 +0,0 @@
|
||||
/* ----------------------- */
|
||||
/* OPTIONS */
|
||||
/* ----------------------- */
|
||||
|
||||
const streamerBotServerAddress = getURLParam("streamerBotServerAddress", "127.0.0.1");
|
||||
const streamerBotServerPort = getURLParam("streamerBotServerPort", "8080");
|
||||
|
||||
const ttsSpeakerBotChat = getURLParam("ttsSpeakerBotChat", false);
|
||||
const ttsSpeakerBotEvents = getURLParam("ttsSpeakerBotEvents", false);
|
||||
|
||||
let streamerBotConnected = false;
|
||||
const chatThreshhold = 50;
|
||||
|
||||
const chatContainer = document.querySelector('#chat');
|
||||
const chatFontSize = getURLParam("chatFontSize", 1);
|
||||
const chatBackground = getURLParam("chatBackground", "#121212");
|
||||
const chatBackgroundOpacity = getURLParam("chatBackgroundOpacity", 1);
|
||||
const chatScrollBar = getURLParam("chatScrollBar", false);
|
||||
const chatBiggerEmotes = getURLParam("chatBiggerEmotes", false);
|
||||
const chatField = getURLParam("chatField", false);
|
||||
const chatModeration = getURLParam("chatModeration", false);
|
||||
|
||||
const currentLang = lang[getURLParam("language", 'ptbr')];
|
||||
const eventsMockup = getURLParam("eventsMockup", true);
|
||||
const chatHorizontal = getURLParam("chatHorizontal", false);
|
||||
const showPlatform = getURLParam("showPlatform", false);
|
||||
const showAvatar = getURLParam("showAvatar", false);
|
||||
const showTimestamps = getURLParam("showTimestamps", false);
|
||||
const ampmTimeStamps = getURLParam("ampmTimeStamps", false);
|
||||
const showBadges = getURLParam("showBadges", true);
|
||||
const showPlatformStatistics = getURLParam("showPlatformStatistics", false);
|
||||
const hideAfter = getURLParam("hideAfter", 0);
|
||||
const ignoreChatters = getURLParam("ignoreChatters", "");
|
||||
const excludeCommands = getURLParam("excludeCommands", true);
|
||||
|
||||
const userColors = new Map();
|
||||
|
||||
const ignoreUserList = ignoreChatters.split(',').map(item => item.trim().toLowerCase()) || [];
|
||||
|
||||
chatContainer.style.zoom = chatFontSize;
|
||||
if (chatScrollBar == false) { chatContainer.classList.add('noscrollbar'); }
|
||||
if (chatField == true) {
|
||||
const chatfieldelement = document.getElementById("chat-input");
|
||||
chatfieldelement.classList.add('enabled');
|
||||
chatfieldelement.querySelector('form input[type=text]').setAttribute('placeholder', currentLang.chatsendmessage);
|
||||
}
|
||||
|
||||
/* ----------------------- */
|
||||
/* START */
|
||||
/* ----------------------- */
|
||||
|
||||
document.body.style.backgroundColor = hexToRGBA(chatBackground,chatBackgroundOpacity);
|
||||
|
||||
if (showPlatformStatistics == false) { document.querySelector('#statistics').style.display = 'none'; }
|
||||
if (chatHorizontal == true) { chatContainer.parentElement.classList.add('horizontal'); }
|
||||
|
||||
/* ----------------------- */
|
||||
/* STREAMER.BOT CONNECTION */
|
||||
/* ----------------------- */
|
||||
|
||||
const streamerBotClient = new StreamerbotClient({
|
||||
host: streamerBotServerAddress,
|
||||
port: streamerBotServerPort,
|
||||
onConnect: (data) => {
|
||||
console.debug( currentLang.streamerbotconnected );
|
||||
console.debug(data);
|
||||
streamerBotConnected = true;
|
||||
notifySuccess({
|
||||
title: currentLang.streamerbotconnected,
|
||||
text: ``
|
||||
});
|
||||
if (eventsMockup == true) {
|
||||
chatContainer.innerHTML = '';
|
||||
stopMockupSystem();
|
||||
}
|
||||
},
|
||||
onDisconnect: () => {
|
||||
console.error(currentLang.streamerbotdisconnected);
|
||||
streamerBotConnected = false;
|
||||
if (eventsMockup == true) {
|
||||
startMockupSystem();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/* ----------------------- */
|
||||
/* UTILITIES */
|
||||
/* ----------------------- */
|
||||
|
||||
|
||||
|
||||
async function addMessageToChat(userID, messageID, platform, data) {
|
||||
|
||||
if (ttsSpeakerBotChat == true) { ttsSpeakerBotSays(data.userName, currentLang.ttschat, data.message); }
|
||||
|
||||
let html = DOMPurify.sanitize(`
|
||||
<div id="${messageID}" data-user="${userID}" class="${platform} ${data.classes} ${chatBiggerEmotes == true ? 'bigger-emojis' : ''} message" style="" >
|
||||
<div class="animate__animated ${chatHorizontal == true ? 'animate__fadeIn' : 'animate__fadeInUp'} animate__faster">
|
||||
|
||||
${data.classes.includes("first-message") ? '<span class="first-chatter">✨</span>' : '' }
|
||||
|
||||
${!data.shared ? '' : data.shared}
|
||||
|
||||
${showTimestamps == true ? '<span class="time">'+whatTimeIsIt()+'</span>' : ''}
|
||||
|
||||
${showPlatform == true ? '<span class="platform"><img src="images/logo-'+platform+'.svg" ></span>' : '' }
|
||||
|
||||
${showAvatar == true ? (data.avatar ? '<span class="avatar"><img src="'+data.avatar+'"></span>' : '') : ''}
|
||||
|
||||
${showBadges == true ? '<span class="badges">'+data.badges+'</span>' : ''}
|
||||
|
||||
<span style="color: ${data.color}" class="user">${data.userName}:</span>
|
||||
|
||||
${!data.reply ? '' : data.reply}
|
||||
|
||||
<span class="text">${data.message}</span>
|
||||
</div>
|
||||
|
||||
${chatModeration == true && platform == 'twitch' ? `[CHATMODERATIONSNIPPETTWITCH]` : ''}
|
||||
${chatModeration == true && platform == 'youtube' ? `[CHATMODERATIONSNIPPETYOUTUBE]` : ''}
|
||||
|
||||
</div>
|
||||
`);
|
||||
|
||||
|
||||
let chatmodtwitch = `<span class="chatmoderation twitch">
|
||||
<button onclick="executeModCommand(event, '/deletemessage ${messageID}')" title="Remove Message"><i class="fa-solid fa-trash-can"></i></button>
|
||||
<button onclick="executeModCommand(event, '/timeout ${userID}')" title="Timeout User"><i class="fa-solid fa-stopwatch"></i></button>
|
||||
<button onclick="executeModCommand(event, '/ban ${userID}')" title="Ban User"><i class="fa-solid fa-gavel"></i></button>
|
||||
</span>`;
|
||||
|
||||
let chatmodyoutube = `<span class="chatmoderation youtube">
|
||||
<button onclick="executeModCommand(event, '/yt/timeout ${userID}')" title="Timeout User"><i class="fa-solid fa-stopwatch"></i></button>
|
||||
<button onclick="executeModCommand(event, '/yt/ban ${userID}')" title="Ban User"><i class="fa-solid fa-gavel"></i></button>
|
||||
</span>`;
|
||||
|
||||
html = html.replace('[CHATMODERATIONSNIPPETTWITCH]', chatmodtwitch);
|
||||
html = html.replace('[CHATMODERATIONSNIPPETYOUTUBE]', chatmodyoutube);
|
||||
|
||||
chatContainer.insertAdjacentHTML('afterbegin', html);
|
||||
|
||||
const messageElement = document.getElementById(messageID);
|
||||
|
||||
if (chatHorizontal == true) {
|
||||
await adjustMessagesHorizontal(messageElement);
|
||||
}
|
||||
|
||||
if (hideAfter > 0) {
|
||||
setTimeout(function () {
|
||||
messageElement.style.opacity = 0;
|
||||
setTimeout(function () {
|
||||
messageElement.remove();
|
||||
}, 1000);
|
||||
}, Math.floor(hideAfter * 1000));
|
||||
}
|
||||
|
||||
removeExtraChatMessages();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function addEventToChat(userID, messageID, platform, data) {
|
||||
|
||||
if (ttsSpeakerBotEvents == true) { ttsSpeakerBotSays(data.userName, '', data.message); }
|
||||
|
||||
const html = DOMPurify.sanitize(`
|
||||
<div id="${messageID}" data-user="${userID}" class="${platform} ${data.classes} message event" style="">
|
||||
<div class="animate__animated ${chatHorizontal == true ? 'animate__fadeIn' : 'animate__fadeInUp'} animate__faster">
|
||||
${!data.reply ? '' : data.reply}
|
||||
|
||||
${showPlatform == true ? '<span class="platform"><img src="images/logo-'+platform+'.svg" ></span>' : '' }
|
||||
|
||||
<span class="info">
|
||||
<span style="color: ${data.color}" class="user">${data.userName}</span>
|
||||
<span class="text">${data.message}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
chatContainer.insertAdjacentHTML('afterbegin', html);
|
||||
|
||||
const messageElement = document.getElementById(messageID);
|
||||
|
||||
if (chatHorizontal == true) {
|
||||
await adjustMessagesHorizontal(messageElement);
|
||||
}
|
||||
|
||||
if (hideAfter > 0) {
|
||||
setTimeout(function () {
|
||||
messageElement.style.opacity = 0;
|
||||
setTimeout(function () {
|
||||
messageElement.remove();
|
||||
}, 1000);
|
||||
}, Math.floor(hideAfter * 1000));
|
||||
}
|
||||
|
||||
removeExtraChatMessages();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function adjustMessagesHorizontal(messageElement) {
|
||||
const container = messageElement.querySelector('.animate__animated');
|
||||
if (!container) return;
|
||||
|
||||
const images = container.querySelectorAll("img.emote");
|
||||
|
||||
function waitImageLoad(img) {
|
||||
return new Promise((resolve) => {
|
||||
if (img.complete) {
|
||||
adjustEmotesWidthInMessage(img);
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
img.addEventListener("load", () => {
|
||||
adjustEmotesWidthInMessage(img);
|
||||
resolve();
|
||||
});
|
||||
img.addEventListener("error", () => resolve());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(Array.from(images).map(img => waitImageLoad(img)));
|
||||
|
||||
const messageElementWidth = Math.floor(container.offsetWidth + 10) + 'px';
|
||||
|
||||
Object.assign(messageElement.style, {
|
||||
width: messageElementWidth,
|
||||
overflow: "visible"
|
||||
});
|
||||
}
|
||||
|
||||
function adjustEmotesWidthInMessage(img) {
|
||||
const width = img.offsetWidth;
|
||||
img.style.width = width + "px";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const whatTimeIsIt = () => {
|
||||
const now = new Date();
|
||||
const hours24 = now.getHours();
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
const ampm = hours24 >= 12 ? 'PM' : 'AM';
|
||||
const hours12 = (hours24 % 12) || 12;
|
||||
|
||||
if (ampmTimeStamps == true) { return `${hours12}:${minutes} ${ampm}`; }
|
||||
else { return `${hours24}:${minutes}`; }
|
||||
};
|
||||
|
||||
|
||||
function removeExtraChatMessages() {
|
||||
const chatMessages = chatContainer.querySelectorAll('div.message');
|
||||
const total = chatMessages.length;
|
||||
|
||||
if (total >= chatThreshhold) {
|
||||
const toRemove = Math.floor(total * 0.25); // 25% do total
|
||||
for (let i = 0; i < toRemove; i++) {
|
||||
const last = chatContainer.lastElementChild;
|
||||
if (last) chatContainer.removeChild(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to format large numbers (e.g., 1000 => '1K')
|
||||
function formatNumber(num) {
|
||||
if (num >= 1000000) {
|
||||
let numStr = (num / 1000000).toFixed(1);
|
||||
if (numStr.endsWith('.0')) {
|
||||
numStr = numStr.slice(0, -2);
|
||||
}
|
||||
return numStr + 'M';
|
||||
}
|
||||
else if (num >= 1000) {
|
||||
let numStr = (num / 1000).toFixed(1);
|
||||
if (numStr.endsWith('.0')) {
|
||||
numStr = numStr.slice(0, -2);
|
||||
}
|
||||
return numStr + 'K';
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
|
||||
function formatCurrency(amount, currencyCode) {
|
||||
return new Intl.NumberFormat(undefined, {
|
||||
style: 'currency',
|
||||
currency: currencyCode,
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 2
|
||||
}).format(amount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function createRandomColor(platform, username) {
|
||||
if (userColors.get(platform).has(username)) {
|
||||
return userColors.get(platform).get(username);
|
||||
}
|
||||
else {
|
||||
const randomColor = "hsl(" + Math.random() * 360 + ", 100%, 75%)";
|
||||
userColors.get(platform).set(username, randomColor);
|
||||
return randomColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createRandomString(length) {
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getURLParam(param, defaultValue) {
|
||||
const chatQueryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(chatQueryString);
|
||||
const paramVar = urlParams.get(param);
|
||||
|
||||
switch (paramVar) {
|
||||
case 'true':
|
||||
return true;
|
||||
|
||||
case 'false':
|
||||
return false;
|
||||
|
||||
case null:
|
||||
case undefined:
|
||||
return defaultValue;
|
||||
|
||||
default:
|
||||
return paramVar;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const pushNotify = (data) => {
|
||||
|
||||
const SimpleNotify = {
|
||||
effect: 'fade',
|
||||
speed: 500,
|
||||
customClass: 'toasty',
|
||||
customIcon: '',
|
||||
showIcon: true,
|
||||
showCloseButton: true,
|
||||
autoclose: true,
|
||||
autotimeout: 5000,
|
||||
notificationsGap: null,
|
||||
notificationsPadding: null,
|
||||
type: 'outline',
|
||||
position: 'x-center bottom',
|
||||
customWrapper: '',
|
||||
};
|
||||
|
||||
const mergedData = {
|
||||
...SimpleNotify,
|
||||
...data
|
||||
}
|
||||
|
||||
new Notify (mergedData);
|
||||
}
|
||||
|
||||
const notifyError = (err) => {
|
||||
err.status = 'error';
|
||||
pushNotify(err);
|
||||
}
|
||||
|
||||
const notifyInfo = (info) => {
|
||||
info.status = 'info';
|
||||
pushNotify(info);
|
||||
}
|
||||
|
||||
const notifyWarning = (warn) => {
|
||||
warn.status = 'warning';
|
||||
pushNotify(warn);
|
||||
}
|
||||
|
||||
|
||||
const notifySuccess = (success) => {
|
||||
success.status = 'success';
|
||||
pushNotify(success);
|
||||
}
|
||||
|
||||
|
||||
function escapeRegex(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
|
||||
async function ttsSpeakerBotSays(user, action, message) {
|
||||
|
||||
if (streamerBotConnected == false) return;
|
||||
|
||||
const ttstext = await cleanStringOfHTMLButEmotes(message);
|
||||
const ttsmessage = user+' '+action+' '+ttstext;
|
||||
|
||||
streamerBotClient.doAction(
|
||||
{ name : "TTS Event" },
|
||||
{
|
||||
"ttsmessage": ttsmessage,
|
||||
}
|
||||
).then( (ttsstuff) => {
|
||||
console.debug('Sending TTS to Streamer.Bot', ttsstuff);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function cleanStringOfHTMLButEmotes(string) {
|
||||
// Cria um elemento DOM temporário
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = string;
|
||||
|
||||
// Substitui <img class="emote" alt="..."> por texto do alt
|
||||
const emotes = container.querySelectorAll('img.emote[alt]');
|
||||
emotes.forEach(img => {
|
||||
const altText = img.getAttribute('alt');
|
||||
const textNode = document.createTextNode(altText);
|
||||
img.replaceWith(textNode);
|
||||
});
|
||||
|
||||
// Remove todo o restante do HTML
|
||||
return container.textContent || "";
|
||||
}
|
||||
|
||||
|
||||
function stripStringFromHtml(html) {
|
||||
let doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
return doc.body.textContent || "";
|
||||
}
|
||||
|
||||
|
||||
function hexToRGBA(hexadecimal,opacity) {
|
||||
const hex = hexadecimal;
|
||||
const alpha = parseFloat(opacity);
|
||||
|
||||
// Converter hex para RGB
|
||||
const r = parseInt(hex.substr(1, 2), 16);
|
||||
const g = parseInt(hex.substr(3, 2), 16);
|
||||
const b = parseInt(hex.substr(5, 2), 16);
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const chatInputSend = document.getElementById("chat-input-send");
|
||||
const chatInputForm = document.querySelector("#chat-input form");
|
||||
const chatInput = chatInputForm.querySelector("input[type=text]")
|
||||
const chatSettings = document.getElementById("chat-input-settings");
|
||||
|
||||
chatInputForm.addEventListener("submit", function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var chatSendPlatforms = [];
|
||||
|
||||
const chatOnTwitch = document.querySelector('#chat-input #twitch input[type=checkbox]').checked;
|
||||
const chatOnYoutube = document.querySelector('#chat-input #youtube input[type=checkbox]').checked;
|
||||
const chatOnTiktok = document.querySelector('#chat-input #tiktok input[type=checkbox]').checked;
|
||||
const chatOnKick = document.querySelector('#chat-input #kick input[type=checkbox]').checked;
|
||||
|
||||
if (chatOnTwitch == true) {
|
||||
chatSendPlatforms.push('twitch');
|
||||
}
|
||||
|
||||
if (chatOnYoutube == true) {
|
||||
chatSendPlatforms.push('youtube');
|
||||
}
|
||||
|
||||
if (chatOnTiktok == true) {
|
||||
chatSendPlatforms.push('tiktok');
|
||||
}
|
||||
|
||||
if (chatOnKick == true) {
|
||||
chatSendPlatforms.push('kick');
|
||||
}
|
||||
|
||||
chatSendPlatforms = chatSendPlatforms.join(',')
|
||||
|
||||
const chatInput = chatInputForm.querySelector("input[type=text]")
|
||||
const chatInputText = chatInput.value;
|
||||
|
||||
// Sends Message to Twitch and YouTube
|
||||
streamerBotClient.doAction(
|
||||
{ name : "ChatRD Messages and Commands" },
|
||||
{
|
||||
"type": "chat",
|
||||
"platforms": chatSendPlatforms,
|
||||
"message": chatInputText,
|
||||
}
|
||||
).then( (sendchatstuff) => {
|
||||
console.debug('Sending Chat to Streamer.Bot', sendchatstuff);
|
||||
});
|
||||
|
||||
// Sends Message to Kick that are not commands
|
||||
if (chatSendPlatforms.includes('kick')) {
|
||||
if (!chatInputText.startsWith('/')) {
|
||||
streamerBotClient.doAction(
|
||||
{ name : "ChatRD Kick Messages" },
|
||||
{
|
||||
"message": chatInputText,
|
||||
}
|
||||
).then( (sendchatstuff) => {
|
||||
console.debug('Sending Kick Chat to Streamer.Bot', sendchatstuff);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sends Message to TikTok that are not commands
|
||||
if (chatSendPlatforms.includes('tiktok')) {
|
||||
if (!chatInputText.startsWith('/')) {
|
||||
streamerBotClient.doAction(
|
||||
{ name : "ChatRD TikTok Messages" },
|
||||
{
|
||||
"ttkmessage": chatInputText,
|
||||
}
|
||||
).then( (sendchatstuff) => {
|
||||
console.debug('Sending TikTok Chat to Streamer.Bot', sendchatstuff);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
chatInput.value = '';
|
||||
});
|
||||
|
||||
chatInputSend.addEventListener("click", function () {
|
||||
chatInputForm.requestSubmit();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
async function executeModCommand(event, command) {
|
||||
event.preventDefault();
|
||||
|
||||
if (streamerBotConnected == true) {
|
||||
chatInput.value = command;
|
||||
|
||||
chatInputForm.requestSubmit();
|
||||
}
|
||||
else {
|
||||
|
||||
notifyError({
|
||||
title: currentLang.streamerbotdisconnected,
|
||||
text: ``
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let chatcommands = {
|
||||
"Twitch" : [
|
||||
{ "name" : "/me", "usage" : "[message]" },
|
||||
{ "name" : "/announce", "usage" : "[message]" },
|
||||
{ "name" : "/clear", "usage" : "" },
|
||||
{ "name" : "/slow", "usage" : "[duration]" },
|
||||
{ "name" : "/slowoff", "usage" : "" },
|
||||
{ "name" : "/subscribers", "usage" : "" },
|
||||
{ "name" : "/subscribersoff", "usage" : "" },
|
||||
{ "name" : "/commercial", "usage" : "[duration]" },
|
||||
{ "name" : "/timeout", "usage" : "[user] [duration] [reason]" },
|
||||
{ "name" : "/untimeout", "usage" : "[user]" },
|
||||
{ "name" : "/ban", "usage" : "[user] [duration] [reason]" },
|
||||
{ "name" : "/unban", "usage" : "[user]" },
|
||||
{ "name" : "/mod", "usage" : "[user]" },
|
||||
{ "name" : "/unmod", "usage" : "[user]" },
|
||||
{ "name" : "/vip", "usage" : "[user]" },
|
||||
{ "name" : "/unvip", "usage" : "[user]" },
|
||||
{ "name" : "/shoutout", "usage" : "[user]" },
|
||||
{ "name" : "/raid", "usage" : "[user]" },
|
||||
{ "name" : "/unraid", "usage" : "" }
|
||||
],
|
||||
"YouTube" : [
|
||||
{ "name" : "/yt/title", "usage" : "[title]" },
|
||||
{ "name" : "/yt/description", "usage" : "[description]" },
|
||||
{ "name" : "/yt/timeout", "usage" : "[user] [duration]" },
|
||||
{ "name" : "/yt/ban", "usage" : "[user]" }
|
||||
]
|
||||
};
|
||||
|
||||
let chatcurrentFocus = -1;
|
||||
|
||||
const chatcommandslist = document.getElementById('chat-autocomplete-list');
|
||||
|
||||
chatInput.addEventListener('input', function () {
|
||||
const value = this.value.trim();
|
||||
chatcommandslist.innerHTML = '';
|
||||
chatcurrentFocus = -1;
|
||||
if (!value.startsWith('/')) return;
|
||||
Object.entries(chatcommands).forEach(([groupName, commands]) => {
|
||||
|
||||
const filtered = commands.filter(cmd => cmd.name.startsWith(value));
|
||||
|
||||
if (filtered.length === 0) return;
|
||||
|
||||
const groupTitle = document.createElement('div');
|
||||
groupTitle.textContent = groupName;
|
||||
chatcommandslist.appendChild(groupTitle);
|
||||
filtered.forEach(cmd => {
|
||||
const item = document.createElement('div');
|
||||
item.classList.add('autocomplete-item');
|
||||
item.innerHTML = `<strong>${cmd.name}</strong><small> ${cmd.usage}</small>`;
|
||||
item.addEventListener('click', () => {
|
||||
chatInput.value = cmd.name + ' ';
|
||||
chatcommandslist.innerHTML = '';
|
||||
});
|
||||
chatcommandslist.appendChild(item);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
chatInput.addEventListener('keydown', function (e) {
|
||||
const items = chatcommandslist.querySelectorAll('.autocomplete-item');
|
||||
|
||||
if (items.length === 0) return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
chatcurrentFocus++;
|
||||
highlightItem(items);
|
||||
}
|
||||
else if (e.key === 'ArrowUp') {
|
||||
chatcurrentFocus--;
|
||||
highlightItem(items);
|
||||
}
|
||||
|
||||
else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (chatcurrentFocus > -1 && items[chatcurrentFocus]) {
|
||||
items[chatcurrentFocus].click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function highlightItem(items) {
|
||||
if (!items) return;
|
||||
|
||||
items.forEach(item => item.classList.remove('active'));
|
||||
|
||||
if (chatcurrentFocus >= items.length) chatcurrentFocus = 0;
|
||||
if (chatcurrentFocus < 0) chatcurrentFocus = items.length - 1;
|
||||
|
||||
items[chatcurrentFocus].classList.add('active');
|
||||
items[chatcurrentFocus].scrollIntoView({ block: "nearest" });
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target !== chatcommandslist) {
|
||||
chatcommandslist.innerHTML = '';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
const settingsPanel = document.querySelector('#chat-input .settings');
|
||||
|
||||
chatSettings.addEventListener('click', function () {
|
||||
settingsPanel.classList.toggle('active');
|
||||
chatSettings.classList.toggle('active');
|
||||
});
|
||||
|
||||
|
||||
const checkboxNames = ["chatOnTwitch", "chatOnYouTube", "chatOnTiktok", "chatOnKick"];
|
||||
|
||||
// Restore checkbox states from localStorage
|
||||
checkboxNames.forEach(name => {
|
||||
const checkbox = document.querySelector(`input[name="${name}"]`);
|
||||
const savedValue = localStorage.getItem(name);
|
||||
if (checkbox && savedValue !== null) {
|
||||
checkbox.checked = savedValue === "true";
|
||||
}
|
||||
});
|
||||
|
||||
// Save state to localStorage on change
|
||||
checkboxNames.forEach(name => {
|
||||
const checkbox = document.querySelector(`input[name="${name}"]`);
|
||||
if (checkbox) {
|
||||
checkbox.addEventListener("change", function () {
|
||||
localStorage.setItem(name, checkbox.checked);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
0
js/chatrd.js
Normal file
0
js/chatrd.js
Normal file
@@ -1,322 +0,0 @@
|
||||
const showFourthwallDonations = getURLParam("showFourthwallDonations", true);
|
||||
|
||||
const showFourthwallOrders = getURLParam("showFourthwallOrders", true);
|
||||
const showFourthwallShowImage = getURLParam("showFourthwallShowImage", true);
|
||||
const showFourthwallBigImage = getURLParam("showFourthwallBigImage", true);
|
||||
|
||||
const showFourthwallSubscriptions = getURLParam("showFourthwallSubscriptions", true);
|
||||
|
||||
const showFourthwallGiftPurchase = getURLParam("showFourthwallGiftPurchase", true);
|
||||
const showFourthwallShowGiftImage = getURLParam("showFourthwallShowGiftImage", true);
|
||||
const showFourthwallBigGiftImage = getURLParam("showFourthwallBigGiftImage", true);
|
||||
|
||||
const showFourthwallGiftDraw = getURLParam("showFourthwallGiftDraw", true);
|
||||
const fourthWallGiftDrawCommand = getURLParam("fourthWallGiftDrawCommand", "!enter");
|
||||
|
||||
const fourthwallMessageHandlers = {
|
||||
'Fourthwall.Donation': (response) => {
|
||||
console.debug('Fourthwall Donation', response.data);
|
||||
fourthwallDonationMessage(response.data);
|
||||
},
|
||||
'Fourthwall.OrderPlaced': (response) => {
|
||||
console.debug('Fourthwall Order', response.data);
|
||||
fourthwallOrderMessage(response.data);
|
||||
},
|
||||
'Fourthwall.SubscriptionPurchased': (response) => {
|
||||
console.debug('Fourthwall Sub', response.data);
|
||||
fourthwallSubMessage(response.data);
|
||||
},
|
||||
'Fourthwall.GiftPurchase': (response) => {
|
||||
console.debug('Fourthwall Gift', response.data);
|
||||
fourthwallGiftMessage(response.data);
|
||||
},
|
||||
'Fourthwall.GiftDrawStarted': (response) => {
|
||||
console.debug('Fourthwall Gift Draw Start', response.data);
|
||||
fourthwallGiftDrawStartMessage(response.data);
|
||||
},
|
||||
'Fourthwall.GiftDrawEnded': (response) => {
|
||||
console.debug('Fourthwall Gift Draw Start', response.data);
|
||||
fourthwallGiftDrawEndMessage(response.data);
|
||||
},
|
||||
};
|
||||
|
||||
for (const [event, handler] of Object.entries(fourthwallMessageHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
async function fourthwallDonationMessage(data) {
|
||||
|
||||
if (showFourthwallDonations == false) return;
|
||||
|
||||
const {
|
||||
username : userName,
|
||||
amount,
|
||||
currency,
|
||||
message: text
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = formatCurrency(amount,currency);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.fourthwall.donation({
|
||||
money: money,
|
||||
message: text
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'fourthwall', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function fourthwallOrderMessage(data) {
|
||||
|
||||
if (showFourthwallOrders == false) return;
|
||||
|
||||
const username = data.username;
|
||||
const total = data.total;
|
||||
const currency = data.currency;
|
||||
const item = data.variants[0].name;
|
||||
const itemsQuantity = data.variants.length;
|
||||
const text = stripStringFromHtml(data.statmessageus);
|
||||
const imageUrl = data.variants[0].image;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var userName = '';
|
||||
if (username == undefined) { userName = currentLang.fourthwall.someone(); }
|
||||
else { userName = username; }
|
||||
|
||||
var money = '';
|
||||
if (total == 0) { money = 0; }
|
||||
else { money = formatCurrency(total,currency); }
|
||||
|
||||
var fourthWallImage = '';
|
||||
if (showFourthwallShowImage == true) {
|
||||
fourthWallImage = imageUrl
|
||||
}
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.fourthwall.order({
|
||||
money: money,
|
||||
firstItem: item,
|
||||
items: itemsQuantity,
|
||||
message: text,
|
||||
image: fourthWallImage
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = ['order'];
|
||||
if (showFourthwallBigImage == true) {
|
||||
classes.push('giantimage');
|
||||
}
|
||||
|
||||
const messageData = {
|
||||
classes: classes.join(' '),
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'fourthwall', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function fourthwallSubMessage(data) {
|
||||
|
||||
if (showFourthwallSubscriptions == false) return;
|
||||
|
||||
const {
|
||||
nickname : userName,
|
||||
amount,
|
||||
currency
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = formatCurrency(amount,currency);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.fourthwall.donation({
|
||||
money: money
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'fourthwall', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function fourthwallGiftMessage(data) {
|
||||
|
||||
if (showFourthwallGiftPurchase == false) return;
|
||||
|
||||
const userName = data.username;
|
||||
const total = data.total;
|
||||
const currency = data.currency;
|
||||
const gifts = data.gifts.length;
|
||||
const item = data.offer.name;
|
||||
const imageUrl = data.offer.imageUrl;
|
||||
const text = stripStringFromHtml(data.statmessageus);
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = '';
|
||||
if (total == 0) { money = 0; }
|
||||
else { money = formatCurrency(total,currency); }
|
||||
|
||||
var fourthWallImage = '';
|
||||
if (showFourthwallShowGiftImage == true) {
|
||||
fourthWallImage = imageUrl
|
||||
}
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.fourthwall.gift({
|
||||
money: money,
|
||||
firstItem: item,
|
||||
items: gifts,
|
||||
message: text,
|
||||
image: fourthWallImage
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = ['order'];
|
||||
if (showFourthwallBigGiftImage == true) {
|
||||
classes.push('giantimage');
|
||||
}
|
||||
|
||||
const messageData = {
|
||||
classes: classes.join(' '),
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'fourthwall', messageData);
|
||||
}
|
||||
|
||||
async function fourthwallGiftDrawStartMessage(data) {
|
||||
|
||||
if (showFourthwallGiftDraw == false) return;
|
||||
|
||||
const {
|
||||
offer: {
|
||||
name: itemName
|
||||
},
|
||||
durationSeconds
|
||||
} = data;
|
||||
|
||||
var userName = '';
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.fourthwall.drawstart({
|
||||
gift: itemName,
|
||||
command: fourthWallGiftDrawCommand,
|
||||
time: durationSeconds
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'fourthwall', messageData);
|
||||
}
|
||||
|
||||
async function fourthwallGiftDrawEndMessage(data) {
|
||||
|
||||
if (showFourthwallGiftDraw == false) return;
|
||||
|
||||
const {
|
||||
gifts
|
||||
} = data;
|
||||
|
||||
var userName = '';
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.fourthwall.drawend({
|
||||
winners: await getWinnersList(gifts)
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'fourthwall', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function getWinnersList(gifts) {
|
||||
const winners = gifts.map(gift => gift.winner).filter(Boolean); // Remove null/undefined
|
||||
|
||||
const numWinners = winners.length;
|
||||
|
||||
if (numWinners === 0) { return ""; }
|
||||
if (numWinners === 1) { return winners[0]; }
|
||||
if (numWinners === 2) { return `${winners[0]} and ${winners[1]}`; }
|
||||
|
||||
// For 3 or more, use the Oxford comma style: A, B, and C
|
||||
const allButLast = winners.slice(0, -1).join(", ");
|
||||
const lastWinner = winners[winners.length - 1];
|
||||
return `${allButLast}, and ${lastWinner}`;
|
||||
}
|
||||
@@ -1,569 +0,0 @@
|
||||
const kickUserName = getURLParam("kickUserName", false);
|
||||
|
||||
const showKickMessages = getURLParam("showKickMessages", true);
|
||||
const showKickFollows = getURLParam("showKickFollows", true);
|
||||
const showKickSubs = getURLParam("showKickSubs", true);
|
||||
const showKickGiftedSubs = getURLParam("showKickGiftedSubs", true);
|
||||
const showKickMassGiftedSubs = getURLParam("showKickMassGiftedSubs", true);
|
||||
const showKickRaids = getURLParam("showKickRaids", true);
|
||||
const showKickViewers = getURLParam("showKickViewers", true);
|
||||
|
||||
const kickAvatars = new Map();
|
||||
const kick7TVEmojis = new Map();
|
||||
|
||||
if (showKickViewers == false) { document.querySelector('#statistics #kick').style.display = 'none'; }
|
||||
|
||||
if (kickUserName) {
|
||||
|
||||
const kickWebSocket = new WebSocket(
|
||||
`wss://ws-us2.pusher.com/app/32cbd69e4b950bf97679?protocol=7&client=js&version=8.4.0-rc2&flash=false`
|
||||
);
|
||||
|
||||
kickWebSocket.onerror = (error) => {
|
||||
console.error("Kick WebSocket Error: " + error);
|
||||
};
|
||||
|
||||
kickWebSocket.onopen = () => {
|
||||
|
||||
kickGetUserInfo(kickUserName)
|
||||
.then((userInfo) => {
|
||||
console.log('Got Kick User Info', userInfo);
|
||||
|
||||
(async () => {
|
||||
const kick7TVEmotes = await get7TVEmotes(userInfo.user_id);
|
||||
if (kick7TVEmotes != null) {
|
||||
console.debug("Getting all Kick's 7TV Emojis + Globals", kick7TVEmotes);
|
||||
|
||||
kick7TVEmotes.forEach(emote => {
|
||||
kick7TVEmojis.set(emote.name, emote.url);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
kickWebSocket.send(
|
||||
JSON.stringify({
|
||||
event: "pusher:subscribe",
|
||||
data: {
|
||||
auth: null,
|
||||
channel: `chatrooms.${userInfo.chatroom.id}.v2`
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
kickWebSocket.send(
|
||||
JSON.stringify({
|
||||
event: "pusher:subscribe",
|
||||
data: {
|
||||
auth: null,
|
||||
channel: `channel.${userInfo.chatroom.channel_id}`
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
setInterval(() => {
|
||||
kickWebSocket.send(
|
||||
JSON.stringify({
|
||||
event: "pusher:ping",
|
||||
data: {},
|
||||
})
|
||||
);
|
||||
}, 60000);
|
||||
|
||||
kickUpdateStatistics(userInfo);
|
||||
setInterval(() => {
|
||||
kickGetUserInfo(kickUserName)
|
||||
.then((data) =>{
|
||||
kickUpdateStatistics(data);
|
||||
});
|
||||
}, 15000);
|
||||
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
kickWebSocket.onmessage = async ({ data }) => {
|
||||
const parsed = JSON.parse(data);
|
||||
const json = JSON.parse(parsed.data);
|
||||
|
||||
if (!parsed.event.includes("pusher")) {
|
||||
|
||||
switch (parsed.event) {
|
||||
case "App\\Events\\ChatMessageEvent":
|
||||
console.debug('Kick Chat', json);
|
||||
kickChatMessage(json);
|
||||
break;
|
||||
|
||||
case "App\\Events\\MessageDeletedEvent":
|
||||
kickChatMessageDeleted(json);
|
||||
break;
|
||||
|
||||
case "App\\Events\\UserBannedEvent":
|
||||
kickUserBanned(json);
|
||||
break;
|
||||
|
||||
case "App\\Events\\ChatroomClearEvent":
|
||||
kickChatClearMessages()
|
||||
break;
|
||||
|
||||
default:
|
||||
console.debug('Kick Event From WebSocket', parsed.event, json);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
else {
|
||||
console.debug('Kick User not set in ChatRD');
|
||||
}
|
||||
|
||||
|
||||
|
||||
streamerBotClient.on('General.Custom', (response) => {
|
||||
|
||||
|
||||
if (response.data.platform === 'Kick') {
|
||||
|
||||
const data = response.data.data;
|
||||
|
||||
switch (data.triggerCustomCodeEventName) {
|
||||
case "kickFollow" :
|
||||
console.debug('Kick Follow', data);
|
||||
kickFollowMessage(data);
|
||||
break;
|
||||
case "kickSub" :
|
||||
console.debug('Kick Sub', data);
|
||||
kickSubMessage(data);
|
||||
break;
|
||||
case "kickGift" :
|
||||
console.debug('Kick Sub Gift', data);
|
||||
kickGiftMessage(data);
|
||||
break;
|
||||
case "kickGifts" :
|
||||
console.debug('Kick Mass Sub Gift', data);
|
||||
kickGiftSubsMessage(data);
|
||||
break;
|
||||
case "kickIncomingRaid" :
|
||||
console.debug('Kick Raid', data);
|
||||
kickRaidMessage(data);
|
||||
break;
|
||||
default:
|
||||
//console.debug('Kick Event', response.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
async function kickChatMessage(data) {
|
||||
|
||||
if (showKickMessages == false) return;
|
||||
if (ignoreUserList.includes(data.sender.username.toLowerCase())) return;
|
||||
if (data.content.startsWith("!") && excludeCommands == true) return;
|
||||
|
||||
const {
|
||||
id: messageId,
|
||||
type,
|
||||
content: text,
|
||||
sender: {
|
||||
id: userID,
|
||||
slug: userSlug,
|
||||
username: userName,
|
||||
identity: {
|
||||
color,
|
||||
badges: badgesToParse
|
||||
}
|
||||
}
|
||||
|
||||
} = data;
|
||||
|
||||
const [avatar, message, badges] = await Promise.all([
|
||||
getKickAvatar(userSlug),
|
||||
getKickEmotes(text),
|
||||
getKickBadges(badgesToParse),
|
||||
]);
|
||||
|
||||
const replyHTML = (type == "reply" ?
|
||||
`<div class="reply"><i class="fa-solid fa-arrow-turn-up"></i> <strong>${data.metadata.original_sender.username}:</strong> ${await getKickEmotes(data.metadata.original_message.content)}</div>` : '');
|
||||
|
||||
const messageData = {
|
||||
classes: '',
|
||||
avatar,
|
||||
badges,
|
||||
userName,
|
||||
color,
|
||||
message,
|
||||
shared: '',
|
||||
reply: replyHTML,
|
||||
};
|
||||
|
||||
addMessageToChat(userSlug, messageId, 'kick', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function kickFollowMessage(data) {
|
||||
|
||||
if (showKickFollows == false) return;
|
||||
|
||||
const {
|
||||
userName : userID,
|
||||
user : userName
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kick.follow(),
|
||||
]);
|
||||
|
||||
const classes = 'follow';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kick', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function kickSubMessage(data) {
|
||||
|
||||
if (showKickSubs == false) return;
|
||||
|
||||
const {
|
||||
userName: userID,
|
||||
user: userName,
|
||||
tier,
|
||||
cumulative
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kick.sub({
|
||||
months : cumulative,
|
||||
tier : tier
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'kick', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function kickGiftMessage(data) {
|
||||
|
||||
if (showKickSubs == false || showKickGiftedSubs == false) return;
|
||||
|
||||
const {
|
||||
user: userName,
|
||||
userName: userID,
|
||||
recipientUser: recipientName,
|
||||
tier,
|
||||
totalSubsGifted
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kick.gifted({
|
||||
gifted : recipientName,
|
||||
tier : tier,
|
||||
total : totalSubsGifted
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kick', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function kickGiftSubsMessage(data) {
|
||||
|
||||
if (showKickSubs == false || showKickMassGiftedSubs == false) return;
|
||||
|
||||
const {
|
||||
user: userName,
|
||||
userName: userID,
|
||||
tier,
|
||||
gifts
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kick.giftedbomb({
|
||||
count : gifts,
|
||||
tier : tier
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'kick', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function kickRaidMessage(data) {
|
||||
|
||||
if (showKickRaids == false) return;
|
||||
|
||||
const {
|
||||
user: userName,
|
||||
viewers
|
||||
} = data;
|
||||
|
||||
const userID = userName.toLowerCase();
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kick.raid({ viewers : viewers })
|
||||
]);
|
||||
|
||||
const classes = 'raid';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kick', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function kickChatMessageDeleted(data) {
|
||||
document.getElementById(data.message.id)?.remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function kickUserBanned(data) {
|
||||
chatContainer.querySelectorAll(`[data-user="${data.user.slug}"]:not(.event)`).forEach(element => {
|
||||
element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function kickChatClearMessages() {
|
||||
chatContainer.querySelectorAll(`.kick:not(.event)`).forEach(element => {
|
||||
element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function kickUpdateStatistics(data) {
|
||||
|
||||
if (showPlatformStatistics == false || showKickViewers == false) return;
|
||||
|
||||
if (data.livestream == null) { }
|
||||
else {
|
||||
const viewers = DOMPurify.sanitize(data.livestream.viewer_count);
|
||||
document.querySelector('#statistics #kick .viewers span').textContent = formatNumber(viewers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function kickGetUserInfo(user) {
|
||||
const response = await fetch( `https://kick.com/api/v2/channels/${user}` );
|
||||
|
||||
if (response.status === 404) {
|
||||
console.error("Kick user was not found!");
|
||||
return 404;
|
||||
}
|
||||
else {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function getKickAvatar(user) {
|
||||
if (showAvatar == true) {
|
||||
if (kickAvatars.has(user)) {
|
||||
console.debug(`Kick avatar found for ${user}!`);
|
||||
return kickAvatars.get(user);
|
||||
}
|
||||
else {
|
||||
console.debug(`Kick avatar not found for ${user}! Trying to get it...`);
|
||||
|
||||
const response = await kickGetUserInfo(user);
|
||||
var newavatar = '';
|
||||
|
||||
if (response == 404) {
|
||||
newavatar = 'https://kick.com/img/default-profile-pictures/default2.jpeg';
|
||||
}
|
||||
else {
|
||||
if (response.user.profile_pic == null) {
|
||||
newavatar = 'https://kick.com/img/default-profile-pictures/default2.jpeg';
|
||||
}
|
||||
else {
|
||||
newavatar = response.user.profile_pic;
|
||||
newavatar = newavatar.replace(/fullsize(?=\.webp)/, "medium");
|
||||
}
|
||||
kickAvatars.set(user, newavatar);
|
||||
}
|
||||
|
||||
return newavatar;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function getKickEmotes(text) {
|
||||
var message = await parseKickEmojis(text);
|
||||
message = await parseKick7TVEmotes(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
async function parseKickEmojis(content) {
|
||||
const message = content;
|
||||
const messagewithemotes = message.replace(/\[emote:(\d+):([^\]]+)\]/g, (_, id, name) => {
|
||||
return `<img src="https://files.kick.com/emotes/${id}/fullsize" alt="${name}" class="emote" >`;
|
||||
});
|
||||
|
||||
return messagewithemotes;
|
||||
}
|
||||
|
||||
async function parseKick7TVEmotes(text) {
|
||||
const words = text.split(/\s+/);
|
||||
const parsedWords = words.map(word => {
|
||||
const clean = word.replace(/[^\w]/g, ''); // remove pontuação para comparar
|
||||
if (kick7TVEmojis.has(clean)) {
|
||||
const url = kick7TVEmojis.get(clean);
|
||||
return word.replace(clean, `<img src="${url}" alt="${clean}" class="emote" />`);
|
||||
}
|
||||
return word;
|
||||
});
|
||||
|
||||
return parsedWords.join(' ');
|
||||
}
|
||||
|
||||
async function get7TVEmotes(userid) {
|
||||
const userSet = await fetch(`https://7tv.io/v3/users/kick/${userid}`);
|
||||
|
||||
if (userSet.status === 404) {
|
||||
console.debug("7TV Profile based on this Kick user was not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
const userEmojis = await userSet.json();
|
||||
|
||||
const gettingAllKick7TVEmotes = userEmojis?.emote_set?.emotes?.map(emote => ({
|
||||
name: emote.name,
|
||||
id: emote.id,
|
||||
url: `https://cdn.7tv.app/emote/${emote.id}/1x.webp`
|
||||
})) || [];
|
||||
|
||||
const globalSet = await fetch(`https://7tv.io/v3/emote-sets/global`);
|
||||
const globalEmojis = await globalSet.json();
|
||||
|
||||
const gettingAllGlobal7TVEmotes = globalEmojis?.emotes?.map(emote => ({
|
||||
name: emote.name,
|
||||
id: emote.id,
|
||||
url: `https://cdn.7tv.app/emote/${emote.id}/1x.webp`
|
||||
})) || [];
|
||||
|
||||
const SevenTVEmotesFusion = [...gettingAllKick7TVEmotes, ...gettingAllGlobal7TVEmotes];
|
||||
return SevenTVEmotesFusion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function getKickBadges(badges) {
|
||||
const badgesArray = [];
|
||||
|
||||
badges.forEach(badge => {
|
||||
switch (badge.type) {
|
||||
case "broadcaster":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" class="size-[calc(1em*(18/13))]"><defs><linearGradient id="HostBadgeA" x1="16" x2="16" y1="-197.5" y2="118.7" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="HostBadgeB" x1="16" x2="16" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="HostBadgeC" x1="16" x2="16" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="HostBadgeD" x1="16" x2="16" y1="-197.5" y2="118.7" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="HostBadgeE" x1="16" x2="16" y1="-74.7" y2="-74.7" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="HostBadgeF" x1="27.2" x2="27.2" y1="-.5" y2="31.1" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient></defs><path fill="url(#HostBadgeA)" d="M9.6 19.2H6.4v3.2h3.2v-3.2Z"></path><path fill="url(#HostBadgeB)" d="M12.8 19.2h6.4V16h3.2V3.2h-3.2V0h-6.4v3.2H9.6V16h3.2v3.2Z"></path><path fill="url(#HostBadgeC)" d="M6.4 12.8H3.2v6.4h3.2v-6.4Z"></path><path fill="url(#HostBadgeD)" d="M25.6 19.2h-3.2v3.2h3.2v-3.2Z"></path><path fill="url(#HostBadgeE)" d="M9.6 22.4v3.2h3.2v3.2H9.6V32h12.8v-3.2h-3.2v-3.2h3.2v-3.2H9.6Z"></path><path fill="url(#HostBadgeF)" d="M25.6 12.8v6.4h3.2v-6.4h-3.2Z"></path></svg>');
|
||||
break;
|
||||
case "sidekick":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" class="size-[calc(1em*(18/13))]"><path fill="url(#SidekickBadgeA)" d="M0 5.5v11.3h2.3V20h2.3v3.2h2.2v3.2h6.9v-3.2h4.6v3.2H25v-3.2h2.3V20h2.3v-3.2H32V5.5h-9.2v3.2h-4.6V12h-4.5V8.7H9V5.5H0Zm13.7 13.7H7V16H4.6V9.6h2.3v3.2h4.5V16h2.3v3.2ZM27.4 16h-2.2v3.2h-6.9V16h2.3v-3.2h4.6V9.6h2.2V16Z"></path><defs><linearGradient id="SidekickBadgeA" x1="18.8" x2="11.7" y1="-2.7" y2="32.7" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6A4A"></stop><stop offset="1" stop-color="#C70C00"></stop></linearGradient></defs></svg>');
|
||||
break;
|
||||
case "moderator":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" class="size-[calc(1em*(18/13))]"><path fill="#00C7FF" d="M23.5 2.5v3h-3v3h-3v3h-3v3h-3v-3h-6v6h3v3h-3v3h-3v6h6v-3h3v-3h3v3h6v-6h-3v-3h3v-3h3v-3h3v-3h3v-6h-6Z"></path></svg>');
|
||||
break;
|
||||
case "vip":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" class="size-[calc(1em*(18/13))]"><path fill="url(#VIPBadgeA)" d="M27.8 4.8V7h-2.5v4.5h-2.1v2.3h-2.3V9.3h-2.4V2.6h-5v6.7H11v4.5H8.8v-2.3H6.7V7H4.2V4.8H0v24.6h32V4.8h-4.2Z"></path><defs><linearGradient id="VIPBadgeA" x1="16" x2="16" y1="-1" y2="35.1" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="1" stop-color="#FF9500"></stop></linearGradient></defs></svg>');
|
||||
break;
|
||||
case "sub_gifter":
|
||||
badgesArray.push('<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_31_3197)"><path d="M22.34 9.5L26 4H18L16 7L14 4H6L9.66 9.5H4V15.1H28V9.5H22.34Z" fill="#2EFAD1"></path><path d="M26.08 19.1001H5.90002V28.5001H26.08V19.1001Z" fill="#2EFAD1"></path><path d="M26.08 15.1001H5.90002V19.1001H26.08V15.1001Z" fill="#00A18D"></path></g><defs><clipPath id="clip0_31_3197"><rect width="24" height="24.5" fill="white" transform="translate(4 4)"></rect></clipPath></defs></svg>');
|
||||
break;
|
||||
case "og":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" class="size-[calc(1em*(18/13))]"><g clip-path="url(#OGBadgeA)"><path fill="url(#OGBadgeB)" d="M32 32H18.3v-1.6h-1.5v-16h1.5v-1.6H32v6.4h-9v9.6h3v-3.2h-1.6v-3.2H32V32Z"></path><path fill="url(#OGBadgeC)" d="M13.6 17.6v1.6h-12v-1.6H0v-16h1.5V0h12.2v1.6h1.5v16h-1.6Zm-4.5-4.8V3.2H6v9.6h3Z"></path><path fill="#00FFF2" d="M13.6 30.4V32h-12v-1.6H0V17.6h1.5V16h12.2v1.6h1.5v12.8h-1.6Zm-4.5-1.6v-9.6H6v9.6h3ZM32 16H18.3v-1.6h-1.5V1.6h1.5V0H32v3.2h-9v9.6h3V9.6h-1.6V6.4H32V16Z"></path></g><defs><linearGradient id="OGBadgeB" x1="16" x2="16" y1="32" y2="2.5" gradientUnits="userSpaceOnUse"><stop stop-color="#00FFF2"></stop><stop offset="1" stop-color="#006399"></stop></linearGradient><linearGradient id="OGBadgeC" x1="15.5" x2="16.1" y1=".4" y2="31.7" gradientUnits="userSpaceOnUse"><stop stop-color="#00FFF2"></stop><stop offset="1" stop-color="#006399"></stop></linearGradient><clipPath id="OGBadgeA"><path fill="#fff" d="M0 0h32v32H0z"></path></clipPath></defs></svg>');
|
||||
break;
|
||||
case "founder":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" class="size-[calc(1em*(18/13))]"><g clip-path="url(#FounderBadgeA)"><path fill="url(#FounderBadgeB)" fill-rule="evenodd" d="M29.3 8V5.4h-2.7V2.7H24V0H8v2.7H5.4v2.7H2.6V8H0v16h2.6v2.6h2.8v2.7H8V32h16v-2.7h2.6v-2.7h2.7V24H32V8h-2.7Zm-9.5 17.7h-6.5V12.8H9v-2.4h2V8.2h2v-2h7v19.5Z" clip-rule="evenodd"></path></g><defs><linearGradient id="FounderBadgeB" x1="15.7" x2="16.3" y1="-4.5" y2="36.7" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="1" stop-color="#FF9500"></stop></linearGradient><clipPath id="FounderBadgeA"><path fill="#fff" d="M0 0h32v32H0z"></path></clipPath></defs></svg>');
|
||||
break;
|
||||
case "subscriber":
|
||||
badgesArray.push('<i class="fa-solid fa-star sub"></i>');
|
||||
break;
|
||||
case "verified":
|
||||
badgesArray.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="size-[calc(1em*(18/13))]"><path fill="#1EFF00" d="M16 6.83512L13.735 4.93512L13.22 2.02512H10.265L8 0.120117L5.735 2.02012H2.78L2.265 4.93012L0 6.83512L1.48 9.39512L0.965 12.3051L3.745 13.3151L5.225 15.8751L8.005 14.8651L10.785 15.8751L12.265 13.3151L15.045 12.3051L14.53 9.39512L16.01 6.83512H16ZM6.495 12.4051L2.79 8.69512L4.205 7.28012L6.495 9.57512L11.29 4.78012L12.705 6.19512L6.5 12.4001L6.495 12.4051Z"></path></svg>');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return badgesArray.join(' ');
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
const showKofiSubscriptions = getURLParam("showKofiSubscriptions", true);
|
||||
const showKofiDonations = getURLParam("showKofiDonations", true);
|
||||
const showKofiOrders = getURLParam("showKofiOrders", true);
|
||||
|
||||
const kofiMessageHandlers = {
|
||||
'Kofi.Donation': (response) => {
|
||||
console.debug('Kofi Donation', response.data);
|
||||
kofiDonationMessage(response.data);
|
||||
},
|
||||
'Kofi.Subscription': (response) => {
|
||||
console.debug('Kofi Sub', response.data);
|
||||
kofiSubMessage(response.data);
|
||||
},
|
||||
'Kofi.Resubscription': (response) => {
|
||||
console.debug('Kofi Resub', response.data);
|
||||
kofiReSubMessage(response.data);
|
||||
},
|
||||
'Kofi.ShopOrder': (response) => {
|
||||
console.debug('Kofi Order', response.data);
|
||||
kofiOrderMessage(response.data);
|
||||
},
|
||||
};
|
||||
|
||||
for (const [event, handler] of Object.entries(kofiMessageHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
async function kofiDonationMessage(data) {
|
||||
|
||||
if (showKofiDonations == false) return;
|
||||
|
||||
const {
|
||||
from : userName,
|
||||
amount,
|
||||
currency,
|
||||
message: text
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = formatCurrency(amount,currency);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kofi.donation({
|
||||
money: money,
|
||||
message: text
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kofi', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function kofiSubMessage(data) {
|
||||
|
||||
if (showKofiSubscriptions == false) return;
|
||||
|
||||
const {
|
||||
from : userName,
|
||||
amount,
|
||||
currency,
|
||||
message: text
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = formatCurrency(amount,currency);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kofi.sub({
|
||||
money: money,
|
||||
message: text
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kofi', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function kofiReSubMessage(data) {
|
||||
|
||||
if (showKofiSubscriptions == false) return;
|
||||
|
||||
const {
|
||||
from : userName,
|
||||
amount,
|
||||
currency,
|
||||
tier,
|
||||
message: text
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = formatCurrency(amount,currency);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kofi.resub({
|
||||
money: money,
|
||||
tier: tier,
|
||||
message: text
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kofi', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function kofiOrderMessage(data) {
|
||||
|
||||
if (showKofiOrders == false) return;
|
||||
const {
|
||||
from : userName,
|
||||
amount,
|
||||
currency,
|
||||
items
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
var money = '';
|
||||
|
||||
if (amount == 0) { money = 0; }
|
||||
else { money = formatCurrency(amount,currency); }
|
||||
|
||||
var itemsQuantity = items.length
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.kofi.order({
|
||||
money: money,
|
||||
items: itemsQuantity
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'kofi', messageData);
|
||||
}
|
||||
0
js/lang.js
Normal file
0
js/lang.js
Normal file
166
js/lang/en.js
166
js/lang/en.js
@@ -1,166 +0,0 @@
|
||||
const en = {
|
||||
streamerbotconnected: 'Streamer.bot Online!',
|
||||
streamerbotdisconnected: 'Streamer.bot Disconnected!',
|
||||
|
||||
ttschat: 'said',
|
||||
|
||||
chatsendmessage: 'Send Message',
|
||||
|
||||
twitch : {
|
||||
firstMessage : () => `First chatter`,
|
||||
follow : () => ` followed the channel`,
|
||||
announcement : () => ` <div class="reply">📢 <strong>Announcement</strong></div>`,
|
||||
channelpoints : ({ title }) => ` <div class="reply"><i class="fa-solid fa-wand-magic-sparkles"></i> <strong>Channel Points - ${title}</strong></div>`,
|
||||
bits : ({ bits, message }) => ` cheered <i class="fa-regular fa-gem fall-and-bounce"></i> <strong>${bits} bits</strong>${message ? '<br>'+message : ''}`,
|
||||
|
||||
sub : ({ months, isPrime, tier }) => ` subscribed for
|
||||
${isPrime == true ? '<i class="fa-solid fa-crown"></i>' : '<i class="fa-solid fa-star"></i>'}
|
||||
<strong>${months || 1 } ${months == 1 ? 'month' : 'months'}
|
||||
(${isPrime == true ? 'Prime' : 'Tier '+tier.toString().charAt(0)})</strong>`,
|
||||
|
||||
resub : ({ months, isPrime, tier, message }) => ` subscribed for
|
||||
${isPrime == true ? '<i class="fa-solid fa-crown"></i>' : '<i class="fa-solid fa-star"></i>'}
|
||||
<strong>${months || 1 } ${months == 1 ? 'month' : 'months'}
|
||||
(${isPrime == true ? 'Prime' : 'Tier '+tier.toString().charAt(0)})</strong>
|
||||
${message ? '<br>'+message : '' }`,
|
||||
|
||||
gifted : ({ gifted, months, tier }) => ` gifted
|
||||
<strong>${months || 1 } ${months == 1 ? 'month' : 'months'}
|
||||
of Tier ${tier.toString().charAt(0)} ${months == 1 ? 'sub' : 'subs'}</strong>
|
||||
to <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
giftedbomb : ({ count, total, tier }) => ` gifted <i class="fa-solid fa-gift"></i> <strong>${count} subs (Tier ${tier.toString().charAt(0)})</strong> to the Community, <strong>${total || 1} ${total == 1 ? 'gift' : 'gifts'} in total</strong>`,
|
||||
|
||||
raid : ({ viewers }) => ` raided the channel with <i class="fa-solid fa-users"></i> <strong>${viewers} viewers</strong>`
|
||||
|
||||
},
|
||||
|
||||
|
||||
youtube : {
|
||||
superchat : ({ money, message }) => ` superchatted <i class="fa-solid fa-comments-dollar"></i> <strong>${money}</strong>
|
||||
${message ? '<br>'+message : ''}
|
||||
`,
|
||||
|
||||
supersticker : ({ money, sticker }) => `
|
||||
${sticker ? '<br>': ''}
|
||||
sent a supersticker of <i class="fa-solid fa-comments-dollar"></i> <strong>${money}</strong>
|
||||
${sticker ? '</span></span><span class="sticker"><img src="'+sticker+'"></span>': ''}
|
||||
`,
|
||||
|
||||
member : ({ months, tier, message }) => ` became a member for
|
||||
<i class="fa-solid fa-star"></i>
|
||||
<strong>${months || 1 } ${months && months > 1 ? 'months' : 'month'}
|
||||
(Tier ${tier})</strong>
|
||||
${message ? '<br>'+message : ''}`,
|
||||
|
||||
giftedmembers : ({ total, tier }) => ` gifted <i class="fa-solid fa-gift"></i> <strong>${total} ${total == 1 ? 'membership' : 'memberships'} (Tier ${tier}) to the channel</strong>`,
|
||||
|
||||
giftedtrainmembers : ({ gifted, tier }) => ` gifted a membership
|
||||
<strong>(${tier})</strong>
|
||||
to <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
},
|
||||
|
||||
|
||||
streamlabs : {
|
||||
tip : ({ money, message }) => ` donated 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
|
||||
streamelements : {
|
||||
tip : ({ money, message }) => ` donated 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
|
||||
tiktok : {
|
||||
follow : () => ` followed the channel`,
|
||||
likes : (likes) => `sent <strong><i class="fa-solid fa-heart"></i> <em class="likecount" style="font-style: normal;">${likes}</em> likes</strong>`,
|
||||
sub : ({ months }) => ` subscribed for <i class="fa-solid fa-star"></i> <strong>${months || 1 } ${(months && months > 1) ? 'months' : 'month'}</strong>`,
|
||||
gift : ({ gift, count, coins }) => ` gifted <strong>${gift} x${count}</strong> (🪙 <strong>${coins} ${(coins && coins > 1) ? 'coins' : 'coin'})</strong>`,
|
||||
|
||||
},
|
||||
|
||||
kick : {
|
||||
follow : () => ` followed the channel`,
|
||||
|
||||
sub : ({ months, tier }) => ` subscribed for
|
||||
<strong>${months || 1 } ${months == 1 ? 'month' : 'months'}
|
||||
(Tier ${tier})</strong>`,
|
||||
|
||||
gifted : ({ gifted, tier, total }) => ` gifted
|
||||
<strong>${total || 1 } ${total == 1 ? 'sub' : 'subs'}
|
||||
(Tier ${tier})</strong>
|
||||
to <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
giftedbomb : ({ count, tier }) => ` gifted <i class="fa-solid fa-gift"></i> <strong>${count} subs (Tier ${tier})</strong> to the Community`,
|
||||
|
||||
raid : ({ viewers }) => ` raided the channel with <i class="fa-solid fa-users"></i> <strong>${viewers} viewers</strong>`
|
||||
|
||||
},
|
||||
|
||||
patreon: {
|
||||
membership: ({ money }) => ` pledged a membership ($${money})`
|
||||
|
||||
},
|
||||
|
||||
tipeeestream : {
|
||||
tip : ({ money, message }) => ` donated 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
kofi : {
|
||||
donation : ({ money, message }) => ` donated 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
sub : ({ money, tier, message }) => ` subscribed <strong>(${money}) ${tier ? '(Tier '+tier+')' : ''}</strong>${message ? '<br>'+message : ''}`,
|
||||
resub : ({ money, tier, message }) => ` resubscribed <strong>${money} ${tier ? '(Tier '+tier+')' : ''}</strong>${message ? '<br>'+message : ''}`,
|
||||
order : ({ money, items }) => ` ordered <strong>${items} ${items == 1 ? 'item' : 'items'} (${money == 0 ? 'Free' : money})`,
|
||||
},
|
||||
|
||||
fourthwall : {
|
||||
someone : () => `Someone`,
|
||||
|
||||
donation : ({ money, message }) => ` donated 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
sub : ({ money }) => ` subscribed <strong>(${money})</strong>`,
|
||||
|
||||
order : ({
|
||||
money,
|
||||
firstItem,
|
||||
items,
|
||||
message,
|
||||
image,
|
||||
|
||||
}) => `
|
||||
${image ? '<br>': ''}
|
||||
ordered <strong>${firstItem}</strong> ${items > 1 ? 'and <strong>'+(items - 1)+' other '+((items - 1) == 1 ? 'item' : 'items')+'</strong>' : ''}
|
||||
(${money == 0 ? 'Free' : money})
|
||||
${message.trim() ? '<br>'+message : ''}
|
||||
${image ? '</span></span><span class="image"><img src="'+image+'"></span>': ''}
|
||||
`,
|
||||
|
||||
gift : ({
|
||||
money,
|
||||
firstItem,
|
||||
items,
|
||||
message,
|
||||
image,
|
||||
|
||||
}) => `
|
||||
${image ? '<br>': ''}
|
||||
gifted <strong>${items}x ${firstItem}</strong>
|
||||
(${money == 0 ? 'Free' : money})
|
||||
${message.trim() ? '<br>'+message : ''}
|
||||
${image ? '</span></span><span class="image"><img src="'+image+'"></span>': ''}
|
||||
`,
|
||||
|
||||
|
||||
|
||||
drawstart : ({ gift, command, time }) => `
|
||||
<strong><i class="fa-solid fa-gift"></i> Giveaway started!</strong>
|
||||
<br>Type <strong>${command}</strong> to have a chance to win <strong>${gift}</strong>. You have <strong>${time} seconds!</strong>`,
|
||||
|
||||
drawend : ({ winners }) => `
|
||||
<strong>🎉 Giveaway Ended!</strong>
|
||||
<br>Congratulations <strong>${winners}</strong>`,
|
||||
|
||||
|
||||
|
||||
},
|
||||
}
|
||||
156
js/lang/es.js
156
js/lang/es.js
@@ -1,156 +0,0 @@
|
||||
const es = {
|
||||
streamerbotconnected: '¡Streamer.bot en línea!',
|
||||
streamerbotdisconnected: '¡Streamer.bot desconectado!',
|
||||
ttschat: 'dijo',
|
||||
|
||||
chatsendmessage: 'Enviar mensaje',
|
||||
|
||||
twitch : {
|
||||
firstMessage : () => `Primeira mensaje`,
|
||||
follow : () => ` siguió el canal`,
|
||||
announcement : () => ` <div class="reply">📢 <strong>Anuncio</strong></div>`,
|
||||
channelpoints : ({ title }) => ` <div class="reply"><i class="fa-solid fa-wand-magic-sparkles"></i> <strong>Puntos del canal - ${title}</strong></div>`,
|
||||
bits : ({ bits, message }) => ` envió <i class="fa-regular fa-gem fall-and-bounce"></i> <strong>${bits} bits</strong>${message ? '<br>'+message : ''}`,
|
||||
|
||||
sub : ({ months, isPrime, tier }) => ` se suscribió por
|
||||
${isPrime == true ? '<i class="fa-solid fa-crown"></i>' : '<i class="fa-solid fa-star"></i>'}
|
||||
<strong>${months || 1 } ${months == 1 ? 'mes' : 'meses'}
|
||||
(${isPrime == true ? 'Prime' : 'Tier '+tier.toString().charAt(0)})</strong>`,
|
||||
|
||||
resub : ({ months, isPrime, tier, message }) => ` se volvió a suscribir por
|
||||
${isPrime == true ? '<i class="fa-solid fa-crown"></i>' : '<i class="fa-solid fa-star"></i>'}
|
||||
<strong>${months || 1 } ${months == 1 ? 'mes' : 'meses'}
|
||||
(${isPrime == true ? 'Prime' : 'Tier '+tier.toString().charAt(0)})</strong>
|
||||
${message ? '<br>'+message : '' }`,
|
||||
|
||||
gifted : ({ gifted, months, tier }) => ` regaló
|
||||
<strong>${months || 1 } ${months == 1 ? 'mes' : 'meses'}
|
||||
de Tier ${tier.toString().charAt(0)} ${months == 1 ? 'suscripción' : 'suscripciones'}</strong>
|
||||
a <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
giftedbomb : ({ count, total, tier }) => ` regaló <i class="fa-solid fa-gift"></i> <strong>${count} suscripciones (Tier ${tier.toString().charAt(0)})</strong> a la comunidad, <strong>${total || 1} ${total == 1 ? 'regalo' : 'regalos'} en total</strong>`,
|
||||
|
||||
raid : ({ viewers }) => ` hizo una raid al canal con <i class="fa-solid fa-users"></i> <strong>${viewers} espectadores</strong>`
|
||||
},
|
||||
|
||||
youtube : {
|
||||
superchat : ({ money, message }) => ` envió un superchat <i class="fa-solid fa-comments-dollar"></i> <strong>${money}</strong>
|
||||
${message ? '<br>'+message : ''}
|
||||
`,
|
||||
|
||||
supersticker : ({ money, sticker }) => `
|
||||
${sticker ? '<br>': ''}
|
||||
envió un supersticker de <i class="fa-solid fa-comments-dollar"></i> <strong>${money}</strong>
|
||||
${sticker ? '</span></span><span class="sticker"><img src="'+sticker+'"></span>': ''}
|
||||
`,
|
||||
|
||||
member : ({ months, tier, message }) => ` se hizo miembro por
|
||||
<i class="fa-solid fa-star"></i>
|
||||
<strong>${months || 1 } ${months && months > 1 ? 'meses' : 'mes'}
|
||||
(Tier ${tier})</strong>
|
||||
${message ? '<br>'+message : ''}`,
|
||||
|
||||
giftedmembers : ({ total, tier }) => ` regaló <i class="fa-solid fa-gift"></i> <strong>${total} ${total == 1 ? 'membresía' : 'membresías'} (Tier ${tier}) al canal</strong>`,
|
||||
|
||||
giftedtrainmembers : ({ gifted, tier }) => ` regaló una membresía
|
||||
<strong>(Tier ${tier})</strong>
|
||||
a <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
},
|
||||
|
||||
streamlabs : {
|
||||
tip : ({ money, message }) => ` donó 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
streamelements : {
|
||||
tip : ({ money, message }) => ` donó 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
tiktok : {
|
||||
follow : () => ` siguió el canal`,
|
||||
likes : (likes) => `envió <strong><i class="fa-solid fa-heart"></i> <em class="likecount" style="font-style: normal;">${likes}</em> likes</strong>`,
|
||||
sub : ({ months }) => ` se suscribió por <i class="fa-solid fa-star"></i> <strong>${months || 1 } ${(months && months > 1) ? 'meses' : 'mes'}</strong>`,
|
||||
gift : ({ gift, count, coins }) => ` regaló <strong>${gift} x${count}</strong> (🪙 <strong>${coins} ${(coins && coins > 1) ? 'monedas' : 'moneda'})</strong>`,
|
||||
},
|
||||
|
||||
kick : {
|
||||
follow : () => ` siguió el canal`,
|
||||
|
||||
sub : ({ months, tier }) => ` se suscribió por
|
||||
<strong>${months || 1 } ${months == 1 ? 'mes' : 'meses'}
|
||||
(Tier ${tier})</strong>`,
|
||||
|
||||
gifted : ({ gifted, tier, total }) => ` regaló
|
||||
<strong>${total || 1 } ${total == 1 ? 'suscripción' : 'suscripciones'}
|
||||
(Tier ${tier})</strong>
|
||||
a <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
giftedbomb : ({ count, tier }) => ` regaló <i class="fa-solid fa-gift"></i> <strong>${count} suscripciones (Tier ${tier})</strong> a la comunidad`,
|
||||
|
||||
raid : ({ viewers }) => ` hizo una raid al canal con <i class="fa-solid fa-users"></i> <strong>${viewers} espectadores</strong>`
|
||||
|
||||
},
|
||||
|
||||
patreon: {
|
||||
membership: ({ money }) => ` apoyó con una membresía ($${money})`
|
||||
|
||||
},
|
||||
|
||||
tipeeestream : {
|
||||
tip : ({ money, message }) => ` donó 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
kofi : {
|
||||
donation : ({ money, message }) => ` donó 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
sub : ({ money, tier, message }) => ` se suscribió <strong>(${money}) ${tier ? '(Tier '+tier+')' : ''}</strong>${message ? '<br>'+message : ''}`,
|
||||
resub : ({ money, tier, message }) => ` renovó la suscripción <strong>${money} ${tier ? '(Tier '+tier+')' : ''}</strong>${message ? '<br>'+message : ''}`,
|
||||
order : ({ money, items }) => ` compró <strong>${items} ${items == 1 ? 'artículo' : 'artículos'} (${money == 0 ? 'Gratis' : money})`,
|
||||
},
|
||||
|
||||
fourthwall : {
|
||||
someone : () => `Alguien`,
|
||||
|
||||
donation : ({ money, message }) => ` donó 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
sub : ({ money }) => ` se suscribió <strong>(${money})</strong>`,
|
||||
|
||||
order : ({
|
||||
money,
|
||||
firstItem,
|
||||
items,
|
||||
message,
|
||||
image,
|
||||
|
||||
}) => `
|
||||
${image ? '<br>': ''}
|
||||
compró <strong>${firstItem}</strong> ${items > 1 ? 'y <strong>'+(items - 1)+' '+((items - 1) == 1 ? 'artículo' : 'artículos')+'</strong>' : ''}
|
||||
(${money == 0 ? 'Gratis' : money})
|
||||
${message.trim() ? '<br>'+message : ''}
|
||||
${image ? '</span></span><span class="image"><img src="'+image+'"></span>': ''}
|
||||
`,
|
||||
|
||||
gift : ({
|
||||
money,
|
||||
firstItem,
|
||||
items,
|
||||
message,
|
||||
image,
|
||||
|
||||
}) => `
|
||||
${image ? '<br>': ''}
|
||||
regaló <strong>${items}x ${firstItem}</strong>
|
||||
(${money == 0 ? 'Gratis' : money})
|
||||
${message.trim() ? '<br>'+message : ''}
|
||||
${image ? '</span></span><span class="image"><img src="'+image+'"></span>': ''}
|
||||
`,
|
||||
|
||||
|
||||
|
||||
drawstart : ({ gift, command, time }) => `
|
||||
<strong><i class="fa-solid fa-gift"></i> ¡Sorteo iniciado!</strong>
|
||||
<br>Escribe <strong>${command}</strong> para tener la oportunidad de ganar <strong>${gift}</strong>. ¡Tienes <strong>${time} segundos</strong>!`,
|
||||
|
||||
drawend : ({ winners }) => `
|
||||
<strong>🎉 ¡Sorteo finalizado!</strong>
|
||||
<br>Felicitaciones <strong>${winners}</strong>`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
const lang = {
|
||||
ptbr : ptbr,
|
||||
en : en,
|
||||
es: es
|
||||
}
|
||||
162
js/lang/ptbr.js
162
js/lang/ptbr.js
@@ -1,162 +0,0 @@
|
||||
const ptbr = {
|
||||
streamerbotconnected: 'Streamer.bot Conectado!',
|
||||
streamerbotdisconnected: 'Streamer.bot Desconectado!',
|
||||
ttschat: 'disse',
|
||||
|
||||
chatsendmessage: 'Enviar mensagem',
|
||||
|
||||
twitch : {
|
||||
firstMessage : () => `Primeira mensagem`,
|
||||
follow : () => ` seguiu o canal`,
|
||||
announcement : () => ` <div class="reply">📢 <strong>Anúncio</strong></div>`,
|
||||
channelpoints : ({ title }) => ` <div class="reply"><i class="fa-solid fa-wand-magic-sparkles"></i> <strong>Pontos do Canal - ${title}</strong></div>`,
|
||||
bits : ({ bits, message }) => ` doou <i class="fa-regular fa-gem fall-and-bounce"></i> <strong>${bits} bits</strong>${message ? '<br>'+message : ''}`,
|
||||
|
||||
sub : ({ months, isPrime, tier }) => ` se inscreveu por
|
||||
${isPrime == true ? '<i class="fa-solid fa-crown"></i>' : '<i class="fa-solid fa-star"></i>'}
|
||||
<strong>${months || 1 } ${months == 1 ? 'mês' : 'meses'}
|
||||
(${isPrime == true ? 'Prime' : 'Tier '+tier.toString().charAt(0)})</strong>`,
|
||||
|
||||
resub : ({ months, isPrime, tier, message }) => ` se inscreveu por
|
||||
${isPrime == true ? '<i class="fa-solid fa-crown"></i>' : '<i class="fa-solid fa-star"></i>'}
|
||||
<strong>${months || 1 } ${months == 1 ? 'mês' : 'meses'}
|
||||
(${isPrime == true ? 'Prime' : 'Tier '+tier.toString().charAt(0)})</strong>
|
||||
${message ? '<br>'+message : '' }`,
|
||||
|
||||
gifted : ({ gifted, months, tier }) => ` doou
|
||||
<strong>${months || 1 } ${months == 1 ? 'mês' : 'meses'}
|
||||
de Tier ${tier.toString().charAt(0)}</strong>
|
||||
para <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
giftedbomb : ({ count, total, tier }) => ` doou <i class="fa-solid fa-gift"></i> <strong>${count} inscrições (Tier ${tier.toString().charAt(0)})</strong> para a Comunidade, totalizando <strong>${total || 1} ${total == 1 ? 'doação' : 'doações'}</strong>`,
|
||||
|
||||
raid : ({ viewers }) => ` raidou o canal com <i class="fa-solid fa-users"></i> <strong>${viewers} pessoas</strong>`
|
||||
|
||||
},
|
||||
|
||||
|
||||
youtube : {
|
||||
superchat : ({ money, message }) => ` fez um superchat de <i class="fa-solid fa-comments-dollar"></i> <strong>${money}</strong>
|
||||
${message ? '<br>'+message : ''}
|
||||
`,
|
||||
|
||||
supersticker : ({ money, sticker }) => `
|
||||
${sticker ? '<br>': ''}
|
||||
enviou um super sticker de <i class="fa-solid fa-comments-dollar"></i> <strong>${money}</strong>
|
||||
${sticker ? '</span></span><span class="sticker"><img src="'+sticker+'"></span>': ''}
|
||||
`,
|
||||
|
||||
member : ({ months, tier, message }) => ` se inscreveu por
|
||||
<i class="fa-solid fa-star"></i>
|
||||
<strong>${months || 1 } ${months && months > 1 ? 'meses' : 'mês'}
|
||||
(Tier ${tier})</strong>
|
||||
${message ? '<br>'+message : ''}`,
|
||||
|
||||
giftedmembers : ({ total, tier }) => ` doou <i class="fa-solid fa-gift"></i> <strong>${total} ${total == 1 ? 'inscrição' : 'inscrições'} (Tier ${tier}) para o canal</strong>`,
|
||||
|
||||
giftedtrainmembers : ({ gifted, tier }) => ` doou uma assinatura
|
||||
<strong>(${tier})</strong>
|
||||
para <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
},
|
||||
|
||||
|
||||
streamlabs : {
|
||||
tip : ({ money, message }) => ` doou 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
|
||||
streamelements : {
|
||||
tip : ({ money, message }) => ` doou 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
|
||||
tiktok : {
|
||||
follow : () => ` seguiu o canal`,
|
||||
likes : (likes) => `mandou <strong><i class="fa-solid fa-heart"></i> <em class="likecount" style="font-style: normal;">${likes}</em> likes</strong>`,
|
||||
sub : ({ months }) => ` se inscreveu por <i class="fa-solid fa-star"></i> <strong>${months || 1 } ${(months && months > 1) ? 'meses' : 'mês'}</strong>`,
|
||||
gift : ({ gift, count, coins }) => ` doou <strong>${gift} x${count}</strong> (🪙 <strong>${coins} ${(coins && coins > 1) ? 'moedas' : 'moeda'})</strong>`,
|
||||
|
||||
},
|
||||
|
||||
kick : {
|
||||
follow : () => ` seguiu o canal`,
|
||||
|
||||
sub : ({ months, tier }) => ` se inscreveu por
|
||||
<strong>${months || 1 } ${months == 1 ? 'mês' : 'meses'}
|
||||
(Tier ${tier})</strong>`,
|
||||
|
||||
gifted : ({ gifted, tier, total }) => ` doou
|
||||
<strong>${total || 1 } ${total == 1 ? 'inscrição' : 'inscrições'}
|
||||
(Tier ${tier})</strong>
|
||||
para <i class="fa-solid fa-gift"></i> <strong>${gifted}</strong>`,
|
||||
|
||||
giftedbomb : ({ count, tier }) => ` doou <i class="fa-solid fa-gift"></i> <strong>${count} inscrições (Tier ${tier})</strong> para a Comunidade`,
|
||||
|
||||
raid : ({ viewers }) => ` raidou o canal com <i class="fa-solid fa-users"></i> <strong>${viewers} pessoas</strong>`
|
||||
|
||||
},
|
||||
|
||||
patreon: {
|
||||
membership: ({ money }) => ` apoiou com uma assinatura ($${money})`
|
||||
|
||||
},
|
||||
|
||||
tipeeestream : {
|
||||
tip : ({ money, message }) => ` doou 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
},
|
||||
|
||||
kofi : {
|
||||
donation : ({ money, message }) => ` doou 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
sub : ({ money, tier, message }) => ` se inscreveu <strong>(${money}) ${tier ? '(Tier '+tier+')' : ''}</strong>${message ? '<br>'+message : ''}`,
|
||||
resub : ({ money, tier, message }) => ` se reninscreveu <strong>${money} ${tier ? '(Tier '+tier+')' : ''}</strong>${message ? '<br>'+message : ''}`,
|
||||
order : ({ money, items }) => ` comprou <strong>${items} ${items == 1 ? 'item' : 'itens'} (${money == 0 ? 'Grátis' : money})`,
|
||||
},
|
||||
|
||||
fourthwall : {
|
||||
someone : () => `Alguém`,
|
||||
|
||||
donation : ({ money, message }) => ` doou 🪙 <strong>${money}</strong>${message ? '<br>'+message : ''}`,
|
||||
sub : ({ money }) => ` se inscreveu <strong>(${money})</strong>`,
|
||||
|
||||
order : ({
|
||||
money,
|
||||
firstItem,
|
||||
items,
|
||||
message,
|
||||
image,
|
||||
|
||||
}) => `
|
||||
${image ? '<br>': ''}
|
||||
comprou <strong>${firstItem}</strong> ${items > 1 ? 'e <strong>'+(items - 1)+' '+((items - 1) == 1 ? 'item' : 'items')+'</strong>' : ''}
|
||||
(${money == 0 ? 'Grátis' : money})
|
||||
${message.trim() ? '<br>'+message : ''}
|
||||
${image ? '</span></span><span class="image"><img src="'+image+'"></span>': ''}
|
||||
`,
|
||||
|
||||
gift : ({
|
||||
money,
|
||||
firstItem,
|
||||
items,
|
||||
message,
|
||||
image,
|
||||
|
||||
}) => `
|
||||
${image ? '<br>': ''}
|
||||
presenteou <strong>${items}x ${firstItem}</strong>
|
||||
(${money == 0 ? 'Grátis' : money})
|
||||
${message.trim() ? '<br>'+message : ''}
|
||||
${image ? '</span></span><span class="image"><img src="'+image+'"></span>': ''}
|
||||
`,
|
||||
|
||||
|
||||
|
||||
drawstart : ({ gift, command, time }) => `
|
||||
<strong><i class="fa-solid fa-gift"></i> Sorteio iniciado!</strong>
|
||||
<br>Digite <strong>${command}</strong> para concorrer a <strong>${gift}</strong>. Você tem <strong>${time} segundos!</strong>`,
|
||||
|
||||
drawend : ({ winners }) => `
|
||||
<strong>🎉 Sorteio Encerrado!</strong>
|
||||
<br>Parabéns <strong>${winners}</strong>`,
|
||||
},
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
const showPatreonMemberships = getURLParam("showPatreonMemberships", true);
|
||||
|
||||
const patreonHandlers = {
|
||||
'Patreon.PledgeCreated': (response) => {
|
||||
console.debug('Patreon Membership', response.data);
|
||||
patreonMemberships(response.data);
|
||||
},
|
||||
};
|
||||
for (const [event, handler] of Object.entries(patreonHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function patreonMemberships(data) {
|
||||
|
||||
if (showPatreonMemberships == false) return;
|
||||
|
||||
const {
|
||||
attributes: {
|
||||
full_name: userName,
|
||||
will_pay_amount_cents: money
|
||||
}
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.patreon.membership({
|
||||
money : (money / 100).toFixed(2)
|
||||
})
|
||||
]);
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'patreon', messageData);
|
||||
}
|
||||
25
js/sb.js
Normal file
25
js/sb.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/* ----------------------- */
|
||||
/* STREAMER.BOT CONNECTION */
|
||||
/* ----------------------- */
|
||||
|
||||
const streamerBotServerAddress = "127.0.0.1";
|
||||
const streamerBotServerPort = "8080";
|
||||
|
||||
const streamerBotClient = new StreamerbotClient({
|
||||
host: streamerBotServerAddress,
|
||||
port: streamerBotServerPort,
|
||||
|
||||
onConnect: (data) => {
|
||||
console.log(`Streamer.bot successfully connected!`)
|
||||
console.debug(data);
|
||||
|
||||
notifySuccess({
|
||||
title: 'Connected to Streamer.bot',
|
||||
text: ``
|
||||
});
|
||||
},
|
||||
|
||||
/*onDisconnect: () => {
|
||||
console.error(`Streamer.bot disconnected`);
|
||||
}*/
|
||||
});
|
||||
452
js/settings.js
452
js/settings.js
@@ -1,452 +0,0 @@
|
||||
let streamerBotClient;
|
||||
let streamerBotConnected = false;
|
||||
|
||||
async function saveSettingsToLocalStorage() {
|
||||
const checkboxes = document.querySelectorAll("input[type=checkbox]:not(.avoid)");
|
||||
const textfields = document.querySelectorAll("input[type=text]:not(.avoid)");
|
||||
const numberfields = document.querySelectorAll("input[type=number]:not(.avoid)");
|
||||
const colorfields = document.querySelectorAll("input[type=color]:not(.avoid)");
|
||||
const selects = document.querySelectorAll("select:not(.avoid)");
|
||||
|
||||
const hiddenField = document.querySelector("textarea[name=youTubeCustomEmotes]:not(.avoid)");
|
||||
|
||||
const ranges = document.querySelectorAll("input[type=range]:not(.avoid)");
|
||||
|
||||
const settings = {};
|
||||
|
||||
checkboxes.forEach((checkbox) => {
|
||||
settings[checkbox.name] = checkbox.checked;
|
||||
});
|
||||
ranges.forEach((range) => {
|
||||
settings[range.name] = range.value;
|
||||
});
|
||||
textfields.forEach((textfield) => {
|
||||
settings[textfield.name] = textfield.value;
|
||||
});
|
||||
numberfields.forEach((numberfield) => {
|
||||
settings[numberfield.name] = numberfield.value;
|
||||
});
|
||||
colorfields.forEach((colorfield) => {
|
||||
settings[colorfield.name] = colorfield.value;
|
||||
});
|
||||
selects.forEach((select) => {
|
||||
settings[select.name] = select.value;
|
||||
});
|
||||
|
||||
localStorage.setItem("chatWidgetSettings", JSON.stringify(settings));
|
||||
|
||||
if (streamerBotConnected == true) {
|
||||
streamerBotClient.doAction(
|
||||
{ name : "YouTube Custom Emotes" },
|
||||
{
|
||||
"chatrdytcustomemotes": JSON.stringify(hiddenField.value.trim()),
|
||||
}
|
||||
).then( (setglobals) => {
|
||||
console.debug('Saving YouTube Emotes from Streamer.Bot', setglobals);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function loadSettingsFromLocalStorage() {
|
||||
const saved = localStorage.getItem("chatWidgetSettings");
|
||||
if (!saved) return;
|
||||
|
||||
const settings = JSON.parse(saved);
|
||||
console.log(settings);
|
||||
|
||||
Object.keys(settings).forEach((key) => {
|
||||
const input = document.querySelector(`[name="${key}"]`);
|
||||
if (input) {
|
||||
if (input.type === "checkbox") {
|
||||
input.checked = settings[key];
|
||||
}
|
||||
else {
|
||||
input.value = settings[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.querySelector('#font-value').textContent = Math.floor(document.querySelector('#font-slider').value * 100) + '%';
|
||||
|
||||
|
||||
var streamerBotServerAddress = document.querySelector('input[type=text][name=streamerBotServerAddress]').value;
|
||||
var streamerBotServerPort = document.querySelector('input[type=text][name=streamerBotServerPort]').value;
|
||||
|
||||
streamerBotClient = new StreamerbotClient({
|
||||
host: streamerBotServerAddress,
|
||||
port: streamerBotServerPort,
|
||||
onConnect: (data) => {
|
||||
streamerBotConnected = true;
|
||||
|
||||
document.querySelector('#memberemotesbstatus').classList.remove('offline');
|
||||
document.querySelector('#memberemotesbstatus').classList.add('online');
|
||||
document.querySelector('#memberemotesbstatus span').textContent = 'Streamer.Bot is Online!';
|
||||
|
||||
streamerBotClient.getGlobals().then( (getglobals) => {
|
||||
const settings = JSON.parse(getglobals.variables.chatrdytcustomemotes.value);
|
||||
console.debug('Getting YouTube Emotes from Streamer.Bot', settings);
|
||||
const textarea = document.querySelector("textarea[name=youTubeCustomEmotes]");
|
||||
textarea.value = settings;
|
||||
|
||||
populateEmoteList();
|
||||
});
|
||||
|
||||
},
|
||||
onDisconnect: () => {
|
||||
console.error('Streamer.bot Disconnected!');
|
||||
|
||||
streamerBotConnected = false;
|
||||
|
||||
document.querySelector('#memberemotesbstatus').classList.remove('online');
|
||||
document.querySelector('#memberemotesbstatus').classList.add('offline');
|
||||
document.querySelector('#memberemotesbstatus span').textContent = 'Streamer.Bot is Offline!';
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function pushChangeEvents() {
|
||||
const checkboxes = document.querySelectorAll("input[type=checkbox]:not(.avoid)");
|
||||
const textfields = document.querySelectorAll("input[type=text]:not(.avoid)");
|
||||
const numberfields = document.querySelectorAll("input[type=number]:not(.avoid)");
|
||||
const colorfields = document.querySelectorAll("input[type=color]:not(.avoid)");
|
||||
const selects = document.querySelectorAll("select:not(.avoid)");
|
||||
|
||||
const ranges = document.querySelectorAll("input[type=range]:not(.avoid)");
|
||||
|
||||
checkboxes.forEach((checkbox) => {
|
||||
checkbox.addEventListener('change', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
textfields.forEach((textfield) => {
|
||||
textfield.addEventListener('input', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
numberfields.forEach((numberfield) => {
|
||||
numberfield.addEventListener('input', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
colorfields.forEach((colorfield) => {
|
||||
colorfield.addEventListener('change', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
selects.forEach((select) => {
|
||||
select.addEventListener('change', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
textfields.forEach((textfield) => {
|
||||
textfield.addEventListener('input', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
|
||||
ranges.forEach((range) => {
|
||||
range.addEventListener('change', () => {
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector('#font-slider').addEventListener('input', function () {
|
||||
document.querySelector('#font-value').textContent = Math.floor(this.value * 100) + '%';
|
||||
});
|
||||
|
||||
document.querySelector('#bg-opacity-slider').addEventListener('input', function () {
|
||||
document.querySelector('#bg-opacity-value').textContent = this.value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function generateUrl() {
|
||||
document.getElementById("outputUrl").value = '';
|
||||
|
||||
|
||||
var baseUrl = 'https://vortisrd.github.io/chatrd/chat.html';
|
||||
|
||||
const checkboxes = document.querySelectorAll("input[type=checkbox]:not(.avoid)");
|
||||
const textfields = document.querySelectorAll("input[type=text]:not(.avoid)");
|
||||
const numberfields = document.querySelectorAll("input[type=number]:not(.avoid)");
|
||||
const colorfields = document.querySelectorAll("input[type=color]:not(.avoid)");
|
||||
const selects = document.querySelectorAll("select:not(.avoid)");
|
||||
|
||||
const ranges = document.querySelectorAll("input[type=range]:not(.avoid)");
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
selects.forEach((select) => {
|
||||
params.set(select.name, select.value);
|
||||
});
|
||||
ranges.forEach((range) => {
|
||||
params.set(range.name, range.value);
|
||||
});
|
||||
checkboxes.forEach((checkbox) => {
|
||||
params.set(checkbox.name, checkbox.checked);
|
||||
});
|
||||
colorfields.forEach((colorfield) => {
|
||||
params.set(colorfield.name, colorfield.value);
|
||||
});
|
||||
textfields.forEach((textfield) => {
|
||||
params.set(textfield.name, textfield.value);
|
||||
});
|
||||
numberfields.forEach((numberfield) => {
|
||||
params.set(numberfield.name, numberfield.value);
|
||||
});
|
||||
|
||||
document.getElementById("outputUrl").value = baseUrl + '?' + params.toString();
|
||||
document.querySelector('#chat-preview iframe').src = 'chat.html?'+params.toString();
|
||||
}
|
||||
|
||||
async function copyUrl() {
|
||||
|
||||
const output = document.getElementById("outputUrl")
|
||||
const value = output.value;
|
||||
|
||||
const button = document.querySelector('.url-bar button');
|
||||
const buttonDefaulText = 'Copy URL';
|
||||
|
||||
navigator.clipboard.writeText(value)
|
||||
.then(() => {
|
||||
|
||||
button.textContent = 'ChatRD URL Copied!';
|
||||
button.style.backgroundColor = "#00dd63";
|
||||
|
||||
setTimeout(() => {
|
||||
button.textContent = buttonDefaulText;
|
||||
button.removeAttribute('style');
|
||||
}, 3000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Failed to copy: ", err);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function setupAddEmoteModal() {
|
||||
const modal = document.getElementById("addEmoteModal");
|
||||
const nameInput = document.getElementById("newEmoteName");
|
||||
const urlInput = document.getElementById("newEmoteURL");
|
||||
const confirmBtn = document.getElementById("confirmAddEmote");
|
||||
const cancelBtn = document.getElementById("cancelAddEmote");
|
||||
const addButton = document.querySelector("#youtube .emote-item:last-child .add");
|
||||
const textarea = document.querySelector("textarea[name=youTubeCustomEmotes]");
|
||||
|
||||
if (!modal || !addButton || !textarea) return;
|
||||
|
||||
// Show modal
|
||||
addButton.onclick = () => {
|
||||
if (streamerBotConnected == true) {
|
||||
nameInput.value = "";
|
||||
urlInput.value = "";
|
||||
modal.classList.remove("hidden");
|
||||
nameInput.focus();
|
||||
}
|
||||
else {
|
||||
alert("Streamer.bot is Offline!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Cancel
|
||||
cancelBtn.onclick = () => {
|
||||
modal.classList.add("hidden");
|
||||
};
|
||||
|
||||
// Confirm
|
||||
confirmBtn.onclick = () => {
|
||||
const name = nameInput.value.trim();
|
||||
const url = urlInput.value.trim();
|
||||
|
||||
if (!name || !url) {
|
||||
alert("Both fields are required.");
|
||||
return;
|
||||
}
|
||||
|
||||
let emotes;
|
||||
try {
|
||||
emotes = JSON.parse(textarea.value);
|
||||
} catch (err) {
|
||||
console.error("Invalid JSON", err);
|
||||
alert("Emote data is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (emotes[name]) {
|
||||
alert(`Emote "${name}" already exists.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add and update
|
||||
emotes[name] = url;
|
||||
textarea.value = JSON.stringify(emotes, null, 4);
|
||||
modal.classList.add("hidden");
|
||||
populateEmoteList();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function populateEmoteList() {
|
||||
const textarea = document.querySelector("textarea[name=youTubeCustomEmotes]");
|
||||
const emoteList = document.querySelector("#youtube .emote-list");
|
||||
|
||||
if (!textarea || !emoteList) return;
|
||||
|
||||
const addButtonSpan = emoteList.querySelector(".emote-item:last-child");
|
||||
|
||||
// Remove all emote items except the add button
|
||||
emoteList.querySelectorAll(".emote-item").forEach(item => {
|
||||
if (item !== addButtonSpan) {
|
||||
item.remove();
|
||||
}
|
||||
});
|
||||
|
||||
let emotes;
|
||||
try {
|
||||
emotes = JSON.parse(textarea.value);
|
||||
} catch (e) {
|
||||
console.error("Invalid JSON in YouTube Emotes textarea", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Recreate each emote item
|
||||
for (const [emoteName, emoteUrl] of Object.entries(emotes)) {
|
||||
const span = document.createElement("span");
|
||||
span.classList.add("emote-item");
|
||||
span.innerHTML = `
|
||||
<img data-emote="${emoteName}" src="${emoteUrl}" alt="">
|
||||
<em>${emoteName}</em>
|
||||
<button class="delete"><i class="fa-solid fa-trash-can"></i></button>
|
||||
`;
|
||||
|
||||
// Add delete handler directly to the button
|
||||
const deleteBtn = span.querySelector(".delete");
|
||||
deleteBtn.addEventListener("click", () => {
|
||||
if (confirm(`Are you sure you want to delete '${emoteName}'?`)) {
|
||||
delete emotes[emoteName];
|
||||
textarea.value = JSON.stringify(emotes, null, 4);
|
||||
populateEmoteList(); // Re-render everything
|
||||
}
|
||||
});
|
||||
|
||||
emoteList.insertBefore(span, addButtonSpan);
|
||||
}
|
||||
|
||||
setupAddEmoteModal();
|
||||
generateUrl();
|
||||
saveSettingsToLocalStorage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
const accordionButtons = document.querySelectorAll("button.accordion");
|
||||
|
||||
accordionButtons.forEach(button => {
|
||||
button.addEventListener("click", () => {
|
||||
const targetId = button.getAttribute("data-target");
|
||||
const target = document.getElementById(targetId);
|
||||
const icon = button.querySelector("i");
|
||||
|
||||
if (!target || !target.classList.contains("accordion-container")) return;
|
||||
|
||||
const isOpen = target.classList.contains("open");
|
||||
|
||||
// Fecha todos os outros accordions
|
||||
document.querySelectorAll(".accordion-container.open").forEach(container => {
|
||||
if (container !== target) {
|
||||
container.classList.remove("open");
|
||||
container.style.maxHeight = null;
|
||||
|
||||
const otherButton = document.querySelector(`button.accordion[data-target="${container.id}"]`);
|
||||
if (otherButton) {
|
||||
const otherIcon = otherButton.querySelector("i");
|
||||
if (otherIcon) otherIcon.className = "fa-solid fa-chevron-down";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Alterna o atual
|
||||
if (!isOpen) {
|
||||
target.classList.add("open");
|
||||
target.style.maxHeight = target.scrollHeight + "px";
|
||||
if (icon) icon.className = "fa-solid fa-chevron-up";
|
||||
|
||||
// Espera a animação terminar para scrollar
|
||||
target.addEventListener("transitionend", function handler(e) {
|
||||
if (e.propertyName === "max-height") {
|
||||
target.removeEventListener("transitionend", handler);
|
||||
|
||||
const offset = target.getBoundingClientRect().top + window.scrollY - 60;
|
||||
window.scrollTo({
|
||||
top: offset,
|
||||
behavior: "smooth"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
else {
|
||||
target.classList.remove("open");
|
||||
target.style.maxHeight = null;
|
||||
if (icon) icon.className = "fa-solid fa-chevron-down";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
loadSettingsFromLocalStorage();
|
||||
generateUrl();
|
||||
pushChangeEvents();
|
||||
populateEmoteList();
|
||||
|
||||
|
||||
document.querySelectorAll('.nav-bar a').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Remove todas as classes dos links dentro da nav-bar
|
||||
document.querySelectorAll('.nav-bar a').forEach(link => {
|
||||
link.classList.remove('active');
|
||||
});
|
||||
|
||||
this.classList.add('active');
|
||||
|
||||
const targetId = this.getAttribute('href');
|
||||
const targetElement = document.querySelector(targetId);
|
||||
|
||||
if (targetElement) {
|
||||
|
||||
const offset = 60; // ajusta 20px acima
|
||||
const y = targetElement.getBoundingClientRect().top + window.scrollY - offset;
|
||||
|
||||
window.scrollTo({
|
||||
top: y,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
const showStreamElementsTips = getURLParam("showStreamElementsTips", true);
|
||||
|
||||
const streamElementsHandlers = {
|
||||
'StreamElements.Tip': (response) => {
|
||||
console.debug('StreamElements Event', response.data);
|
||||
streamElementsEventMessage(response.data);
|
||||
},
|
||||
};
|
||||
for (const [event, handler] of Object.entries(streamElementsHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function streamElementsEventMessage(data) {
|
||||
|
||||
if (showStreamElementsTips == false) return;
|
||||
|
||||
const {
|
||||
username: userName,
|
||||
amount: moneyFromUser,
|
||||
currency: currencyFromUser,
|
||||
message: messageFromUser,
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.streamlabs.tip({
|
||||
money : formatCurrency(moneyFromUser,currencyFromUser),
|
||||
message : messageFromUser
|
||||
})
|
||||
]);
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'streamelements', messageData);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
const showStreamlabsDonations = getURLParam("showStreamlabsDonations", true);
|
||||
|
||||
const streamLabsHandlers = {
|
||||
'Streamlabs.Donation': (response) => {
|
||||
console.debug('StreamLabs Event', response.data);
|
||||
streamLabsEventMessage(response.data);
|
||||
},
|
||||
};
|
||||
for (const [event, handler] of Object.entries(streamLabsHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
async function streamLabsEventMessage(data) {
|
||||
|
||||
if (showStreamlabsDonations == false) return;
|
||||
|
||||
const {
|
||||
from: userName,
|
||||
formattedAmount: moneyFromUser,
|
||||
currency: currencyFromUser,
|
||||
message: messageFromUser,
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.streamlabs.tip({
|
||||
money : formatCurrency(moneyFromUser,currencyFromUser),
|
||||
message : messageFromUser
|
||||
})
|
||||
]);
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'streamlabs', messageData);
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
/* ----------------------------------------------------------------------------------------- */
|
||||
/* TikFinity >< Streamer.Bot */
|
||||
/* ----------------------------------------------------------------------------------------- */
|
||||
/* In Streamer.bot, go into Servers/Clients, then Websocket clients, */
|
||||
/* and add the server info for TikFinity Desktop App. */
|
||||
/* ----------------------------------------------------------------------------------------- */
|
||||
/* If it's also running on the same computer, the address will be: ws://127.0.0.1:21213/ */
|
||||
/* ----------------------------------------------------------------------------------------- */
|
||||
|
||||
const showTikTokMessages = getURLParam("showTikTokMessages", true);
|
||||
const showTikTokFollows = getURLParam("showTikTokFollows", true);
|
||||
const showTikTokLikes = getURLParam("showTikTokLikes", true);
|
||||
const showTikTokGifts = getURLParam("showTikTokGifts", true);
|
||||
const showTikTokSubs = getURLParam("showTikTokSubs", true);
|
||||
const showTikTokStatistics = getURLParam("showTikTokStatistics", true);
|
||||
|
||||
userColors.set('tiktok', new Map());
|
||||
|
||||
if (showTikTokStatistics == false) { document.querySelector('#statistics #tiktok').style.display = 'none'; }
|
||||
|
||||
|
||||
|
||||
streamerBotClient.on('General.Custom', (response) => {
|
||||
if (response.data.platform === 'TikTok') {
|
||||
|
||||
let json = response.data;
|
||||
let jsonData = json.data.data;
|
||||
|
||||
switch (json.data.event) {
|
||||
case 'roomUser' :
|
||||
tiktokUpdateStatistics(jsonData, 'viewers');
|
||||
break;
|
||||
case 'like' :
|
||||
tiktokUpdateStatistics(jsonData, 'likes');
|
||||
//console.log('TikTok Likes', jsonData);
|
||||
tiktokLikesMessage(jsonData);
|
||||
break;
|
||||
case 'chat' :
|
||||
console.log('TikTok Chat', jsonData);
|
||||
tiktokChatMessage(jsonData);
|
||||
break;
|
||||
case 'follow' :
|
||||
console.log('TikTok Follow', jsonData);
|
||||
tiktokFollowMessage(jsonData);
|
||||
break;
|
||||
case 'subscribe' :
|
||||
console.log('TikTok Sub', jsonData);
|
||||
tiktokSubMessage(jsonData);
|
||||
break;
|
||||
case 'gift' :
|
||||
console.log('TikTok Gift', jsonData);
|
||||
tiktokGiftMessage(jsonData);
|
||||
break;
|
||||
default:
|
||||
//console.debug(json);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
async function tiktokChatMessage(data) {
|
||||
|
||||
if (showTikTokMessages == false) return;
|
||||
if (ignoreUserList.includes(data.nickname.toLowerCase())) return;
|
||||
if (data.comment.startsWith("!") && excludeCommands == true) return;
|
||||
|
||||
|
||||
const {
|
||||
userId: userID,
|
||||
msgId: messageID,
|
||||
profilePictureUrl: avatar,
|
||||
comment: message,
|
||||
emotes,
|
||||
nickname: userName,
|
||||
isSubscriber,
|
||||
isModerator,
|
||||
} = data;
|
||||
|
||||
const badgesHTML = [
|
||||
isSubscriber && '<i class="fa-solid fa-star"></i>',
|
||||
isModerator && '<i class="fa-solid fa-user-gear"></i>',
|
||||
].filter(Boolean).join('');
|
||||
|
||||
const classes = [
|
||||
isSubscriber && 'sub',
|
||||
isModerator && 'mod',
|
||||
].filter(Boolean);
|
||||
|
||||
var fullmessage = message;
|
||||
|
||||
if (emotes.length > 0) {
|
||||
emotes.forEach(emote => {
|
||||
var emotetoadd = ` <img src="${emote.emoteImageUrl}" class="emote" data-emote-id="${emote.emoteId}"> `;
|
||||
var position = emote.placeInComment;
|
||||
fullmessage = [fullmessage.slice(0, position), emotetoadd, fullmessage.slice(position)].join('');
|
||||
});
|
||||
}
|
||||
|
||||
const messageData = {
|
||||
classes: classes.join(' '),
|
||||
avatar,
|
||||
badges: badgesHTML,
|
||||
userName,
|
||||
color: await createRandomColor('tiktok', userID),
|
||||
message: fullmessage,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addMessageToChat(userID, messageID, 'tiktok', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function tiktokFollowMessage(data) {
|
||||
|
||||
if (showTikTokFollows == false) return;
|
||||
|
||||
const {
|
||||
userId: userID,
|
||||
msgId: messageID,
|
||||
profilePictureUrl: avatar,
|
||||
nickname: userName,
|
||||
} = data;
|
||||
|
||||
const message = currentLang.tiktok.follow();
|
||||
const classes = 'follow'
|
||||
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'tiktok', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function tiktokLikesMessage(data) {
|
||||
|
||||
if (showTikTokLikes == false) return;
|
||||
|
||||
const {
|
||||
userId: userID,
|
||||
msgId: messageID,
|
||||
profilePictureUrl: avatar,
|
||||
nickname: userName,
|
||||
likeCount: likesSent
|
||||
} = data;
|
||||
|
||||
var likeCountTotal = parseInt(likesSent);
|
||||
|
||||
// Search for Previous Likes from the Same User
|
||||
const previousLikeContainer = chatContainer.querySelector(`div.message.likes[data-user="${userID}"]`);
|
||||
|
||||
// If found, fetches the previous likes, deletes the element
|
||||
// and then creates a new count with a sum of the like count
|
||||
if (previousLikeContainer) {
|
||||
const likeCountElem = previousLikeContainer.querySelector('.likecount');
|
||||
if (likeCountElem) {
|
||||
var likeCountPrev = parseInt(likeCountElem.textContent);
|
||||
likeCountTotal = Math.floor(likeCountPrev + likeCountTotal);
|
||||
previousLikeContainer.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const message = currentLang.tiktok.likes(likeCountTotal)
|
||||
const classes = 'likes'
|
||||
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'tiktok', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function tiktokSubMessage(data) {
|
||||
|
||||
if (showTikTokSubs == false) return;
|
||||
|
||||
const {
|
||||
userId: userID,
|
||||
msgId: messageID,
|
||||
profilePictureUrl: avatar,
|
||||
nickname: userName,
|
||||
} = data;
|
||||
|
||||
const message = currentLang.tiktok.sub({
|
||||
months : data.subMonth
|
||||
});
|
||||
|
||||
const classes = 'sub'
|
||||
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'tiktok', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function tiktokGiftMessage(data) {
|
||||
|
||||
if (showTikTokGifts == false) return;
|
||||
if (data.giftType === 1 && !data.repeatEnd) {}
|
||||
else {
|
||||
const {
|
||||
userId: userID,
|
||||
msgId: messageID,
|
||||
profilePictureUrl: avatar,
|
||||
nickname: userName,
|
||||
} = data;
|
||||
|
||||
var coins = Math.floor(data.repeatCount*data.diamondCount);
|
||||
|
||||
const message = currentLang.tiktok.gift({
|
||||
gift : data.giftName,
|
||||
count : data.repeatCount,
|
||||
coins : coins
|
||||
});
|
||||
|
||||
const classes = 'gift'
|
||||
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'tiktok', messageData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function tiktokUpdateStatistics(data, type) {
|
||||
|
||||
if (showPlatformStatistics == false || showTikTokStatistics == false) return;
|
||||
|
||||
if (type == 'viewers') {
|
||||
const viewers = DOMPurify.sanitize(data.viewerCount);
|
||||
document.querySelector('#statistics #tiktok .viewers span').textContent = formatNumber(viewers);
|
||||
}
|
||||
|
||||
if (type == 'likes') {
|
||||
const likes = DOMPurify.sanitize(data.totalLikeCount);
|
||||
document.querySelector('#statistics #tiktok .likes span').textContent = formatNumber(likes);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
const showTipeeeDonations = getURLParam("showTipeeeDonations", true);
|
||||
|
||||
const tipeeeHandlers = {
|
||||
'TipeeeStream.Donation': (response) => {
|
||||
console.debug('TipeeeStream Donation', response.data);
|
||||
tipeeeStreamDonation(response.data);
|
||||
},
|
||||
};
|
||||
for (const [event, handler] of Object.entries(tipeeeHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function tipeeeStreamDonation(data) {
|
||||
|
||||
if (showTipeeeDonations == false) return;
|
||||
|
||||
const {
|
||||
user: userName,
|
||||
amount,
|
||||
currency,
|
||||
message: text
|
||||
} = data;
|
||||
|
||||
const userID = createRandomString(40);
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.tipeeestream.tip({
|
||||
money : formatCurrency(amount,currency),
|
||||
message: text
|
||||
})
|
||||
]);
|
||||
const classes = '';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'tipeeestream', messageData);
|
||||
}
|
||||
@@ -1,640 +0,0 @@
|
||||
const showTwitchMessages = getURLParam("showTwitchMessages", true);
|
||||
const showTwitchFollows = getURLParam("showTwitchFollows", true);
|
||||
const showTwitchBits = getURLParam("showTwitchBits", true);
|
||||
const showTwitchAnnouncements = getURLParam("showTwitchAnnouncements", true);
|
||||
const showTwitchSubs = getURLParam("showTwitchSubs", true);
|
||||
const showTwitchGiftedSubs = getURLParam("showTwitchGiftedSubs", true);
|
||||
const showTwitchGiftedSubsUserTrain = getURLParam("showTwitchGiftedSubsUserTrain", true);
|
||||
const showTwitchMassGiftedSubs = getURLParam("showTwitchMassGiftedSubs", true);
|
||||
const showTwitchRewardRedemptions = getURLParam("showTwitchRewardRedemptions", true);
|
||||
const showTwitchRaids = getURLParam("showTwitchRaids", true);
|
||||
const showTwitchSharedChat = getURLParam("showTwitchSharedChat", true);
|
||||
const showTwitchPronouns = getURLParam("showTwitchPronouns", false);
|
||||
const showTwitchViewers = getURLParam("showTwitchViewers", true);
|
||||
|
||||
const avatars = new Map();
|
||||
const pronouns = new Map();
|
||||
|
||||
if (showTwitchViewers == false) { document.querySelector('#statistics #twitch').style.display = 'none'; }
|
||||
|
||||
const twitchMessageHandlers = {
|
||||
'Twitch.ChatMessage': (response) => {
|
||||
console.debug('Twitch Chat', response.data);
|
||||
twitchChatMessage(response.data);
|
||||
},
|
||||
'Twitch.Follow': (response) => {
|
||||
console.debug('Twitch Follow', response.data);
|
||||
twitchFollowMessage(response.data);
|
||||
},
|
||||
'Twitch.Announcement': (response) => {
|
||||
console.debug('Twitch Announcements', response.data);
|
||||
twitchAnnouncementMessage(response.data);
|
||||
},
|
||||
'Twitch.Cheer': (response) => {
|
||||
console.debug('Twitch Cheer/Bits', response.data);
|
||||
twitchBitsMessage(response.data);
|
||||
},
|
||||
'Twitch.AutomaticRewardRedemption': (response) => {
|
||||
console.debug('Twitch Auto Reward Redemption', response.data);
|
||||
twitchChatMessageGiantEmote(response.data);
|
||||
},
|
||||
'Twitch.RewardRedemption': (response) => {
|
||||
console.debug('Twitch Reward Redemption', response.data);
|
||||
twitchRewardRedemption(response.data);
|
||||
},
|
||||
'Twitch.Sub': (response) => {
|
||||
console.debug('Twitch Sub', response.data);
|
||||
twitchSubMessage(response.data);
|
||||
},
|
||||
'Twitch.ReSub': (response) => {
|
||||
console.debug('Twitch Resub', response.data);
|
||||
twitchReSubMessage(response.data);
|
||||
},
|
||||
'Twitch.GiftSub': (response) => {
|
||||
console.debug('Twitch Gift Sub', response.data);
|
||||
twitchGiftMessage(response.data);
|
||||
|
||||
},
|
||||
'Twitch.GiftBomb': (response) => {
|
||||
console.debug('Twitch Gift Bomb', response.data);
|
||||
twitchGiftSubsMessage(response.data);
|
||||
},
|
||||
'Twitch.Raid': (response) => {
|
||||
console.debug('Twitch Raid', response.data);
|
||||
twitchRaidMessage(response.data);
|
||||
},
|
||||
'Twitch.ChatMessageDeleted': (response) => {
|
||||
console.debug('Twitch Chat Deleted', response.data);
|
||||
twitchChatMessageDeleted(response.data);
|
||||
},
|
||||
'Twitch.UserBanned': (response) => {
|
||||
console.debug('Twitch Ban', response.data);
|
||||
twitchUserBanned(response.data);
|
||||
},
|
||||
'Twitch.UserTimedOut': (response) => {
|
||||
console.debug('Twitch Timeout', response.data);
|
||||
twitchUserBanned(response.data);
|
||||
},
|
||||
'Twitch.ViewerCountUpdate': (response) => {
|
||||
console.debug('Twitch View Count Update', response.data);
|
||||
twitchUpdateStatistics(response.data);
|
||||
},
|
||||
'Twitch.ChatCleared': (response) => {
|
||||
console.debug('Twitch Chat Clear', response.data);
|
||||
twitchChatClearMessages();
|
||||
}
|
||||
};
|
||||
|
||||
for (const [event, handler] of Object.entries(twitchMessageHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
async function twitchChatMessage(data) {
|
||||
|
||||
if (showTwitchMessages == false) return;
|
||||
if (ignoreUserList.includes(data.message.username.toLowerCase())) return;
|
||||
if (data.message.message.startsWith("!") && excludeCommands == true) return;
|
||||
|
||||
const {
|
||||
message: {
|
||||
username: userID,
|
||||
color,
|
||||
displayName: fullUserName,
|
||||
message : text,
|
||||
firstMessage,
|
||||
isReply,
|
||||
reply: replyData,
|
||||
},
|
||||
isSharedChat,
|
||||
messageId,
|
||||
} = data;
|
||||
|
||||
const [avatar, message, badges] = await Promise.all([
|
||||
getTwitchAvatar(userID),
|
||||
getTwitchEmotes(data),
|
||||
getTwitchBadges(data),
|
||||
]);
|
||||
|
||||
const classes = firstMessage ? ['first-message'] : [];
|
||||
|
||||
if (data.user.role == 4) {
|
||||
classes.push('streamer');
|
||||
}
|
||||
|
||||
const replyHTML = isReply ?
|
||||
`<div class="reply"><i class="fa-solid fa-arrow-turn-up"></i> <strong>${replyData.userName}:</strong> ${replyData.msgBody}</div>` :
|
||||
'';
|
||||
|
||||
var sharedChat = '';
|
||||
if (isSharedChat) {
|
||||
if (showTwitchSharedChat == true) {
|
||||
if (!data.sharedChat.primarySource)
|
||||
{
|
||||
var sharedChat = `<div class="shared">
|
||||
<span><i class="fa-solid fa-comment-dots"></i> <strong>${data.sharedChat.sourceRoom.name}</strong></span>
|
||||
<i class="fa-solid fa-arrow-turn-down"></i>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
else if (!data.sharedChat.primarySource && showTwitchSharedChat == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let userName = fullUserName;
|
||||
|
||||
if (showTwitchPronouns == true) {
|
||||
let userPronouns = await getUserPronouns('twitch', data.message.username);
|
||||
userName = userPronouns + fullUserName;
|
||||
}
|
||||
|
||||
const messageData = {
|
||||
classes: classes.join(' '),
|
||||
avatar,
|
||||
badges,
|
||||
userName,
|
||||
color,
|
||||
message,
|
||||
shared: sharedChat,
|
||||
reply: replyHTML,
|
||||
};
|
||||
|
||||
addMessageToChat(userID, messageId, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function twitchChatMessageGiantEmote(data) {
|
||||
|
||||
if (showTwitchMessages == false) return;
|
||||
|
||||
const {
|
||||
user_login: userID,
|
||||
gigantified_emote: { id: emoteGigantify }
|
||||
} = data;
|
||||
const userMessages = chatContainer.querySelectorAll(`.twitch.message[data-user="${userID}"]`);
|
||||
|
||||
if (userMessages.length === 0) return;
|
||||
|
||||
const firstMessage = userMessages[0];
|
||||
const emoteImages = firstMessage.querySelectorAll(`img[data-emote-id="${emoteGigantify}"]`);
|
||||
|
||||
if (emoteImages.length === 0) return;
|
||||
|
||||
emoteImages.forEach(img => {
|
||||
img.classList.add("gigantify");
|
||||
if (img.src.endsWith("2.0")) {
|
||||
img.src = img.src.replace("2.0", "3.0");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function twitchFollowMessage(data) {
|
||||
|
||||
if (showTwitchFollows == false) return;
|
||||
|
||||
const {
|
||||
user_id : userID,
|
||||
user_name : userName
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.follow(),
|
||||
]);
|
||||
|
||||
const classes = 'follow';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function twitchBitsMessage(data) {
|
||||
|
||||
if (showTwitchBits == false) return;
|
||||
|
||||
const {
|
||||
messageId : messageID,
|
||||
user : {
|
||||
id : userID,
|
||||
name : userName
|
||||
}
|
||||
} = data;
|
||||
|
||||
data.message.message = data.message.message.replace(/\bCheer\d+\b/g, '').replace(/\s+/g, ' ').trim();
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.bits({
|
||||
bits: data.message.bits,
|
||||
message : await getTwitchEmotes(data)
|
||||
}),
|
||||
]);
|
||||
|
||||
const classes = 'bits';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function twitchAnnouncementMessage(data) {
|
||||
|
||||
if (showTwitchAnnouncements == false) return;
|
||||
|
||||
const {
|
||||
messageId : messageID,
|
||||
user : {
|
||||
id : userID,
|
||||
name : userName
|
||||
}
|
||||
} = data;
|
||||
|
||||
|
||||
data.message = {
|
||||
message: await getTwitchAnnouncementEmotes(data)
|
||||
};
|
||||
|
||||
|
||||
const replyHTML = currentLang.twitch.announcement();
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
` ${data.message.message}`
|
||||
]);
|
||||
|
||||
const classes = 'announcement';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: replyHTML,
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function twitchRewardRedemption(data) {
|
||||
|
||||
if (showTwitchRewardRedemptions == false) return;
|
||||
|
||||
const {
|
||||
user_id : userID,
|
||||
user_name : userName,
|
||||
} = data;
|
||||
const messageID = createRandomString(40);
|
||||
const replyHTML = currentLang.twitch.channelpoints({ title : data.reward.title });
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
` ${data.user_input}`
|
||||
]);
|
||||
const classes = 'rewards-redemption';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: replyHTML,
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function twitchSubMessage(data) {
|
||||
|
||||
if (showTwitchSubs == false) return;
|
||||
|
||||
const {
|
||||
user : {
|
||||
id : userID,
|
||||
name : userName
|
||||
}
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.sub({
|
||||
months : data.duration_months,
|
||||
isPrime : data.isPrime,
|
||||
tier : data.sub_tier
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function twitchReSubMessage(data) {
|
||||
|
||||
if (showTwitchSubs == false) return;
|
||||
|
||||
const {
|
||||
user : {
|
||||
id : userID,
|
||||
name : userName
|
||||
},
|
||||
text
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
const messagetext = await getTwitchEmotesOnParts(data);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.resub({
|
||||
months : data.cumulativeMonths,
|
||||
isPrime : data.isPrime,
|
||||
tier : data.subTier,
|
||||
message : messagetext
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function twitchGiftMessage(data) {
|
||||
|
||||
if (data.fromCommunitySubGift === false) {
|
||||
if (showTwitchSubs == false || showTwitchGiftedSubs == false) return;
|
||||
}
|
||||
else {
|
||||
if (showTwitchSubs == false || showTwitchGiftedSubsUserTrain == false) return;
|
||||
}
|
||||
|
||||
|
||||
const {
|
||||
user : {
|
||||
id : userID,
|
||||
name : userName
|
||||
}
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.gifted({
|
||||
gifted : data.recipient.name,
|
||||
months : data.durationMonths,
|
||||
tier : data.subTier,
|
||||
total : data.cumlativeTotal
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function twitchGiftSubsMessage(data) {
|
||||
|
||||
if (showTwitchSubs == false || showTwitchMassGiftedSubs == false) return;
|
||||
|
||||
const {
|
||||
user : {
|
||||
id : userID,
|
||||
name : userName
|
||||
}
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.giftedbomb({
|
||||
count : data.total,
|
||||
tier : data.sub_tier,
|
||||
total : data.cumulative_total
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'sub';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function twitchRaidMessage(data) {
|
||||
|
||||
if (showTwitchRaids == false) return;
|
||||
|
||||
const {
|
||||
from_broadcaster_user_login: userID,
|
||||
from_broadcaster_user_name: userName
|
||||
} = data;
|
||||
|
||||
const messageID = createRandomString(40);
|
||||
const [avatar, message] = await Promise.all([
|
||||
'',
|
||||
currentLang.twitch.raid({ viewers : data.viewers })
|
||||
]);
|
||||
|
||||
const classes = 'raid';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
};
|
||||
|
||||
addEventToChat(userID, messageID, 'twitch', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function twitchChatMessageDeleted(data) {
|
||||
document.getElementById(data.messageId)?.remove();
|
||||
}
|
||||
|
||||
|
||||
async function twitchUserBanned(data) {
|
||||
chatContainer.querySelectorAll(`[data-user="${data.user_login}"]`).forEach(element => {
|
||||
element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function twitchChatClearMessages() {
|
||||
chatContainer.querySelectorAll(`.twitch:not(.event)`).forEach(element => {
|
||||
element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function twitchUpdateStatistics(data) {
|
||||
if (showPlatformStatistics == false || showTwitchViewers == false) return;
|
||||
|
||||
const viewers = DOMPurify.sanitize(data.viewerCount);
|
||||
document.querySelector('#statistics #twitch .viewers span').textContent = formatNumber(viewers);
|
||||
}
|
||||
|
||||
|
||||
async function getTwitchEmotes(data) {
|
||||
const message = data.message.message;
|
||||
const emotes = data.emotes;
|
||||
const words = message.split(" ");
|
||||
emotes.sort((a, b) => b.startIndex - a.startIndex);
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
emotes.forEach(emote => {
|
||||
if (words[i] === emote.name) {
|
||||
words[i] = `<img src="${emote.imageUrl}" data-emote-id="${emote.id}" alt="${emote.name}" class="emote">`;
|
||||
}
|
||||
});
|
||||
}
|
||||
return words.join(" ");
|
||||
}
|
||||
|
||||
|
||||
async function getTwitchAnnouncementEmotes(data) {
|
||||
const message = data.text;
|
||||
const emotes = data.parts;
|
||||
const words = message.split(" ");
|
||||
emotes.sort((a, b) => b.startIndex - a.startIndex);
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
emotes.forEach(emote => {
|
||||
if (words[i] === emote.text) {
|
||||
words[i] = `<img src="${emote.imageUrl}" alt="${emote.text}" class="emote">`;
|
||||
}
|
||||
});
|
||||
}
|
||||
return words.join(" ");
|
||||
}
|
||||
|
||||
async function getTwitchEmotesOnParts(data) {
|
||||
const parts = data?.parts;
|
||||
const message = data.text;
|
||||
|
||||
if (!Array.isArray(parts)) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return parts.map(part => {
|
||||
if (part.type === 'text') {
|
||||
return message;
|
||||
} else if (part.type === 'emote') {
|
||||
return `<img src="${part.imageUrl}" alt="${part.text}" class="emote">`;
|
||||
}
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function getTwitchBadges(data) {
|
||||
const badges = data.message.badges;
|
||||
var htmlBadges = '';
|
||||
badges.forEach((badge) => {
|
||||
htmlBadges += `<img src="${badge.imageUrl}" class="badge">`;
|
||||
});
|
||||
return htmlBadges;
|
||||
}
|
||||
|
||||
|
||||
async function getTwitchAvatar(user) {
|
||||
if (showAvatar == true) {
|
||||
if (avatars.has(user)) {
|
||||
console.debug(`Twitch avatar found for ${user}!`);
|
||||
return avatars.get(user);
|
||||
}
|
||||
else {
|
||||
console.debug(`Twitch avatar not found for ${user}! Getting it from DECAPI!`);
|
||||
var decapi = await fetch('https://decapi.me/twitch/avatar/' + user);
|
||||
var newavatar = await decapi.text()
|
||||
|
||||
if (!newavatar) { newavatar = 'https://static-cdn.jtvnw.net/user-default-pictures-uv/cdd517fe-def4-11e9-948e-784f43822e80-profile_image-300x300.png'; }
|
||||
|
||||
avatars.set(user, newavatar);
|
||||
return newavatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getUserPronouns(platform, username) {
|
||||
if (pronouns.has(username)) {
|
||||
console.debug(`Pronouns found for ${username}. Getting it from Map...`)
|
||||
return pronouns.get(username);
|
||||
}
|
||||
else {
|
||||
console.debug(`Pronouns not found for ${username}. Retrieving...`)
|
||||
|
||||
const response = await streamerBotClient.getUserPronouns(platform, username);
|
||||
const pronoun = response.pronoun.userFound ? `<em class="pronouns">${response.pronoun.pronounSubject}/${response.pronoun.pronounObject}</em>` : '';
|
||||
|
||||
pronouns.set(username, pronoun);
|
||||
|
||||
return pronoun;
|
||||
}
|
||||
}
|
||||
@@ -1,455 +0,0 @@
|
||||
const showYouTubeMessages = getURLParam("showYouTubeMessages", true);
|
||||
const showYouTubeSuperChats = getURLParam("showYouTubeSuperChats", true);
|
||||
const showYouTubeSuperStickers = getURLParam("showYouTubeSuperStickers", true);
|
||||
const showYouTubeSuperStickerGif = getURLParam("showYouTubeSuperStickerGif", true);
|
||||
const showYouTubeSuperStickerFullSize = getURLParam("showYouTubeSuperStickerFullSize", false);
|
||||
const showYouTubeMemberships = getURLParam("showYouTubeMemberships", true);
|
||||
const showYouTubeGiftMemberships = getURLParam("showYouTubeGiftMemberships", true);
|
||||
const showYouTubeMembershipsTrain = getURLParam("showYouTubeMembershipsTrain", true);
|
||||
const showYouTubeStatistics = getURLParam("showYouTubeStatistics", true);
|
||||
|
||||
let youTubeCustomEmotes = [];
|
||||
|
||||
let youTubeBTTVEmotes = [];
|
||||
|
||||
userColors.set('youtube', new Map());
|
||||
|
||||
if (showYouTubeStatistics == false) { document.querySelector('#statistics #youtube').style.display = 'none'; }
|
||||
|
||||
const youtubeMessageHandlers = {
|
||||
'YouTube.Message': (response) => {
|
||||
console.debug('YouTube Chat', response.data);
|
||||
youTubeChatMessage(response.data);
|
||||
},
|
||||
'YouTube.UserBanned': (response) => {
|
||||
console.debug('YouTube Timeout/Hide/Ban', response.data);
|
||||
youTubeUserBanned(response.data);
|
||||
},
|
||||
'YouTube.SuperChat': (response) => {
|
||||
console.debug('YouTube SuperChat', response.data);
|
||||
youTubeSuperChatMessage(response.data);
|
||||
},
|
||||
'YouTube.SuperSticker': (response) => {
|
||||
console.debug('YouTube Super Sticker', response.data);
|
||||
youTubeSuperStickerMessage(response.data);
|
||||
},
|
||||
'YouTube.NewSponsor': (response) => {
|
||||
console.debug('YouTube New Member', response.data);
|
||||
youTubeNewSponsorMessage(response.data);
|
||||
},
|
||||
'YouTube.MemberMileStone': (response) => {
|
||||
console.debug('YouTube Member Milestone', response.data);
|
||||
youTubeNewSponsorMessage(response.data);
|
||||
},
|
||||
'YouTube.MembershipGift': (response) => {
|
||||
console.debug('YouTube Gifted Membership', response.data);
|
||||
youTubeGiftedMembersMessage(response.data);
|
||||
},
|
||||
'YouTube.GiftMembershipReceived': (response) => {
|
||||
console.debug('YouTube Gifted Membership Bomb', response.data);
|
||||
YouTubeGiftReceivedMessage(response.data);
|
||||
},
|
||||
'YouTube.StatisticsUpdated': (response) => {
|
||||
console.debug(response.data);
|
||||
youTubeUpdateStatistics(response.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (const [event, handler] of Object.entries(youtubeMessageHandlers)) {
|
||||
streamerBotClient.on(event, handler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function youTubeChatMessage(data) {
|
||||
|
||||
if (showYouTubeMessages == false) return;
|
||||
if (ignoreUserList.includes(data.user.name.toLowerCase())) return;
|
||||
if (data.message.startsWith("!") && excludeCommands == true) return;
|
||||
|
||||
if (streamerBotConnected == true) {
|
||||
if (youTubeCustomEmotes.length == 0) {
|
||||
streamerBotClient.getGlobals().then( (getglobals) => {
|
||||
youTubeCustomEmotes = JSON.parse(JSON.parse(getglobals.variables.chatrdytcustomemotes.value));
|
||||
console.debug('Getting YouTube Emotes from Streamer.Bot', youTubeCustomEmotes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
user: {
|
||||
id: userID,
|
||||
profileImageUrl: avatar,
|
||||
name: userName,
|
||||
isVerified,
|
||||
isSponsor,
|
||||
isModerator,
|
||||
isOwner,
|
||||
},
|
||||
eventId: messageID,
|
||||
} = data;
|
||||
|
||||
var messageHTML = await getYouTubeEmotes(data);
|
||||
|
||||
const badgesHTML = [
|
||||
isVerified && '<i class="fa-solid fa-check"></i>',
|
||||
isSponsor && '<i class="fa-solid fa-star"></i>',
|
||||
isModerator && '<i class="fa-solid fa-wrench"></i>',
|
||||
isOwner && '<i class="fa-solid fa-video"></i>',
|
||||
].filter(Boolean).join('');
|
||||
|
||||
const classes = [
|
||||
isSponsor && 'sub',
|
||||
isModerator && 'mod',
|
||||
isOwner && 'owner',
|
||||
].filter(Boolean);
|
||||
|
||||
const messageData = {
|
||||
classes: classes.join(' '),
|
||||
avatar,
|
||||
badges: badgesHTML,
|
||||
userName,
|
||||
color: await createRandomColor('youtube', userID),
|
||||
message : messageHTML,
|
||||
reply: '',
|
||||
};
|
||||
addMessageToChat(userID, messageID, 'youtube', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function youTubeUserBanned(data) {
|
||||
chatContainer.querySelectorAll(`[data-user="${data.bannedUser.id}"]:not(.event)`).forEach(element => {
|
||||
element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function youTubeSuperChatMessage(data) {
|
||||
|
||||
if (showYouTubeSuperChats == false) return;
|
||||
|
||||
const {
|
||||
user: {
|
||||
id: userID,
|
||||
name: userName,
|
||||
},
|
||||
eventId: messageID,
|
||||
amount,
|
||||
message : textmessage
|
||||
} = data;
|
||||
|
||||
var money = amount;
|
||||
var messagewithemotes = await getYouTubeEmotes(textmessage);
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
``,
|
||||
currentLang.youtube.superchat({
|
||||
money : money,
|
||||
message : messagewithemotes
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'superchat';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
}
|
||||
addEventToChat(userID, messageID, 'youtube', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function youTubeSuperStickerMessage(data) {
|
||||
|
||||
if (showYouTubeSuperStickers == false) return;
|
||||
|
||||
const {
|
||||
user: {
|
||||
id: userID,
|
||||
name: userName,
|
||||
},
|
||||
eventId: messageID,
|
||||
amount
|
||||
} = data;
|
||||
|
||||
var money = amount;
|
||||
var youtubeStickerUrl = '';
|
||||
|
||||
if (showYouTubeSuperStickerGif == true) {
|
||||
youtubeStickerUrl = await getYouTubeStickerImage(data);
|
||||
}
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
``,
|
||||
currentLang.youtube.supersticker({
|
||||
money : money,
|
||||
sticker : youtubeStickerUrl
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = ['supersticker'];
|
||||
if (showYouTubeSuperStickerFullSize == true) {
|
||||
classes.push('giantsupersticker');
|
||||
}
|
||||
|
||||
const messageData = {
|
||||
classes: classes.join(' '),
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
}
|
||||
addEventToChat(userID, messageID, 'youtube', messageData);
|
||||
}
|
||||
|
||||
async function youTubeNewSponsorMessage(data) {
|
||||
|
||||
if (showYouTubeMemberships == false) return;
|
||||
|
||||
const {
|
||||
user: {
|
||||
id: userID,
|
||||
name: userName,
|
||||
},
|
||||
eventId: messageID,
|
||||
levelName,
|
||||
months,
|
||||
message: messagetext,
|
||||
} = data;
|
||||
|
||||
var messagewithemotes = '';
|
||||
|
||||
if (messagetext) {
|
||||
messagewithemotes = await getYouTubeEmotes(messagetext);
|
||||
}
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
``,
|
||||
currentLang.youtube.member({
|
||||
months : months,
|
||||
tier : levelName,
|
||||
message: messagewithemotes
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'member';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
}
|
||||
addEventToChat(userID, messageID, 'youtube', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function youTubeGiftedMembersMessage(data) {
|
||||
|
||||
if (showYouTubeGiftMemberships == false) return;
|
||||
|
||||
const {
|
||||
user: {
|
||||
id: userID,
|
||||
name: userName,
|
||||
},
|
||||
eventId: messageID,
|
||||
tier,
|
||||
count
|
||||
} = data;
|
||||
const [avatar, message] = await Promise.all([
|
||||
``,
|
||||
currentLang.youtube.giftedmembers({
|
||||
total : count,
|
||||
tier : tier
|
||||
})
|
||||
]);
|
||||
const classes = 'giftedmembers';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
}
|
||||
addEventToChat(userID, messageID, 'youtube', messageData);
|
||||
}
|
||||
|
||||
async function YouTubeGiftReceivedMessage(data) {
|
||||
|
||||
if (showYouTubeMembershipsTrain == false) return;
|
||||
|
||||
const {
|
||||
user: {
|
||||
id: userID,
|
||||
name: userName,
|
||||
},
|
||||
gifter: {
|
||||
id : gifterUserId,
|
||||
name: gifterUserName
|
||||
},
|
||||
eventId: messageID,
|
||||
tier
|
||||
} = data;
|
||||
|
||||
const [avatar, message] = await Promise.all([
|
||||
``,
|
||||
currentLang.youtube.giftedmembers({
|
||||
gifted : gifterUserName,
|
||||
tier : tier
|
||||
})
|
||||
]);
|
||||
|
||||
const classes = 'giftedtrainmembers';
|
||||
const messageData = {
|
||||
classes: classes,
|
||||
avatar,
|
||||
badges: '',
|
||||
userName,
|
||||
color: '#FFF',
|
||||
message,
|
||||
reply: '',
|
||||
}
|
||||
addEventToChat(gifterUserId, messageID, 'youtube', messageData);
|
||||
}
|
||||
|
||||
|
||||
async function youTubeUpdateStatistics(data) {
|
||||
|
||||
if (showYouTubeStatistics == false) return;
|
||||
|
||||
const viewers = DOMPurify.sanitize(data.concurrentViewers);
|
||||
const likes = DOMPurify.sanitize(data.likeCount);
|
||||
document.querySelector('#statistics #youtube .viewers span').textContent = formatNumber(viewers);
|
||||
document.querySelector('#statistics #youtube .likes span').textContent = formatNumber(likes);
|
||||
}
|
||||
|
||||
|
||||
async function getYouTubeEmotes(data) {
|
||||
let message = data.message;
|
||||
const channelId = data.broadcast?.channelId;
|
||||
if (!channelId) return message;
|
||||
|
||||
// Carrega os emotes do canal BTTV se ainda não estiverem carregados
|
||||
if (youTubeBTTVEmotes.length === 0) {
|
||||
try {
|
||||
const res = await fetch(`https://api.betterttv.net/3/cached/users/youtube/${channelId}`);
|
||||
const emoteData = await res.json();
|
||||
console.debug('Getting YouTube BTTV Channel Emojis', `https://api.betterttv.net/3/cached/users/youtube/${channelId}`, emoteData);
|
||||
youTubeBTTVEmotes = [
|
||||
...(emoteData.sharedEmotes || []),
|
||||
...(emoteData.channelEmotes || [])
|
||||
];
|
||||
} catch (err) {
|
||||
console.warn("Failed to load BTTV emotes:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Cria o mapa de emotes
|
||||
const emoteMap = new Map();
|
||||
|
||||
// BTTV emotes
|
||||
for (const emote of youTubeBTTVEmotes) {
|
||||
const imageUrl = `https://cdn.betterttv.net/emote/${emote.id}/1x`;
|
||||
const emoteElement = `<img src="${imageUrl}" class="emote" alt="${emote.code}">`;
|
||||
emoteMap.set(emote.code, { html: emoteElement, raw: emote.code });
|
||||
}
|
||||
|
||||
// YouTube emotes (ex: :hand-pink-waving:)
|
||||
if (data.emotes) {
|
||||
for (const emote of data.emotes) {
|
||||
const emoteElement = `<img src="${emote.imageUrl}" class="emote" alt="${emote.name}">`;
|
||||
emoteMap.set(emote.name, { html: emoteElement, raw: emote.name });
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Member Emotes (também com dois-pontos)
|
||||
if (data.user.isSponsor === true || data.user.isOwner === true) {
|
||||
for (const [name, url] of Object.entries(youTubeCustomEmotes)) {
|
||||
const emoteElement = `<img src="${url}" class="emote" alt="${name}">`;
|
||||
emoteMap.set(`:${name}:`, { html: emoteElement, raw: `:${name}:` });
|
||||
}
|
||||
}
|
||||
|
||||
// Usa DOMParser para substituir apenas nós de texto
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(`<div>${message}</div>`, 'text/html');
|
||||
const container = doc.body.firstChild;
|
||||
|
||||
function escapeRegex(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function replaceEmotesInText(text) {
|
||||
// Ordena por tamanho decrescente pra evitar conflitos entre nomes parecidos
|
||||
const sorted = Array.from(emoteMap.values()).sort((a, b) => b.raw.length - a.raw.length);
|
||||
|
||||
for (const { raw, html } of sorted) {
|
||||
const escaped = escapeRegex(raw);
|
||||
|
||||
// Emotes com dois-pontos: :emote: → permitem colados
|
||||
const isDelimited = raw.startsWith(':') && raw.endsWith(':');
|
||||
const regex = isDelimited
|
||||
? new RegExp(escaped, 'g')
|
||||
: new RegExp(`(?<!\\S)${escaped}(?!\\S)`, 'g');
|
||||
|
||||
text = text.replace(regex, html);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function walk(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
const replaced = replaceEmotesInText(node.nodeValue);
|
||||
if (replaced !== node.nodeValue) {
|
||||
const span = doc.createElement('span');
|
||||
span.innerHTML = replaced;
|
||||
node.replaceWith(...span.childNodes);
|
||||
}
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
for (const child of Array.from(node.childNodes)) {
|
||||
walk(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(container);
|
||||
|
||||
return container.innerHTML;
|
||||
}
|
||||
|
||||
|
||||
// ChatGPT created this. :)
|
||||
async function getYouTubeStickerImage(data) {
|
||||
const stack = [data];
|
||||
|
||||
while (stack.length) {
|
||||
const current = stack.pop();
|
||||
|
||||
if (current && typeof current === 'object') {
|
||||
if ('imageUrl' in current && typeof current.imageUrl === 'string') {
|
||||
return current.imageUrl;
|
||||
}
|
||||
|
||||
for (const key in current) {
|
||||
if (Object.hasOwn(current, key)) {
|
||||
stack.push(current[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user