Перенос чатов из другого репозитория
18
StreamLabsChats/altrumi/generation 7/index.html
Normal file
@ -0,0 +1,18 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<author>{from}</author>
|
||||
<content>{message}</content>
|
||||
</message>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="chatlist_event">
|
||||
<event id="{messageId}">
|
||||
<main>
|
||||
<content>{author} {content}</content>
|
||||
</main>
|
||||
</event>
|
||||
</script>
|
||||
521
StreamLabsChats/altrumi/generation 7/script.js
Normal file
@ -0,0 +1,521 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = 0;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = 'подписался'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `подарил подписку ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
subMysteries.push(details.tags['msg-param-origin-id'])
|
||||
content = `подарил ${mysteryGiftCount} сабок!`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `притопал с ${viewerCount} зрителями!`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
parity++;
|
||||
if (parity == 4) {
|
||||
parity = 0
|
||||
}
|
||||
|
||||
if (parity % 2 == 0) {
|
||||
$messageElement.attr('parity', `even-${Math.abs(Math.floor((parity-1) / 2))}`);
|
||||
} else {
|
||||
$messageElement.attr('parity', `odd-${Math.floor(parity / 2)}`);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.appendTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
|
||||
let $children = $parentElement.children();
|
||||
}
|
||||
257
StreamLabsChats/altrumi/generation 7/style.css
Normal file
24
StreamLabsChats/rechumoe/generation 1/index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<top>
|
||||
<dot></dot>
|
||||
<dot></dot>
|
||||
<author>{from}</author>
|
||||
♡
|
||||
<role></role>
|
||||
</top>
|
||||
<content>{message}</content>
|
||||
</message>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="chatlist_event">
|
||||
<event id="{messageId}">
|
||||
<main>
|
||||
<content>{author} {content} ♡</content>
|
||||
</main>
|
||||
</event>
|
||||
</script>
|
||||
514
StreamLabsChats/rechumoe/generation 1/script.js
Normal file
@ -0,0 +1,514 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = 'подписался'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `подарил подписку ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
subMysteries.push(details.tags['msg-param-origin-id'])
|
||||
content = `подарил ${mysteryGiftCount} сабок!`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `притопал с ${viewerCount} зрителями!`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.prependTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
|
||||
let $children = $parentElement.children();
|
||||
$children.slice(3).attr({deleted: true});
|
||||
$children.slice(10).remove();
|
||||
}
|
||||
296
StreamLabsChats/rechumoe/generation 1/style.css
Normal file
23
StreamLabsChats/rechumoe/generation 2/index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<top>
|
||||
<rechumoe></rechumoe>
|
||||
<author>{from}</author>
|
||||
</top>
|
||||
<content>{message}</content>
|
||||
</message>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="chatlist_event">
|
||||
<event id="{messageId}">
|
||||
<main>
|
||||
<gif-1></gif-1>
|
||||
<gif-2></gif-2>
|
||||
<content>{author} {content} ♡</content>
|
||||
</main>
|
||||
</event>
|
||||
</script>
|
||||
514
StreamLabsChats/rechumoe/generation 2/script.js
Normal file
@ -0,0 +1,514 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = '<br> подписался'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `подарил подписку <br> ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
subMysteries.push(details.tags['msg-param-origin-id'])
|
||||
content = `подарил <br> ${mysteryGiftCount} сабок!`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `притопал <br> с ${viewerCount} зрителями!`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.prependTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
|
||||
let $children = $parentElement.children();
|
||||
$children.slice(3).attr({deleted: true});
|
||||
$children.slice(10).remove();
|
||||
}
|
||||
326
StreamLabsChats/rechumoe/generation 2/style.css
Normal file
21
StreamLabsChats/reira/generation 7/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<mid>
|
||||
<author>{from}</author>
|
||||
<content>{message}</content>
|
||||
</mid>
|
||||
</message>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="chatlist_event">
|
||||
<event id="{messageId}">
|
||||
<main>
|
||||
<author>{author}</author>
|
||||
<content>{content}</content>
|
||||
</main>
|
||||
</event>
|
||||
</script>
|
||||
510
StreamLabsChats/reira/generation 7/script.js
Normal file
@ -0,0 +1,510 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = '<br>subed'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `gifted a sub to<br> ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
subMysteries.push(details.tags['msg-param-origin-id'])
|
||||
content = `gifted<br> ${mysteryGiftCount} subs`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `raided with<br> ${viewerCount} viewers`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.appendTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
}
|
||||
270
StreamLabsChats/reira/generation 7/style.css
Normal file
13
StreamLabsChats/sadkawaai/generation 1/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
<a class="font-2">2</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<top>
|
||||
<author>{from}</author>
|
||||
</top>
|
||||
<content>{message}</content>
|
||||
</message>
|
||||
</script>
|
||||
510
StreamLabsChats/sadkawaai/generation 1/script.js
Normal file
@ -0,0 +1,510 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = 'подписался'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `подарил подписку ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
subMysteries.push(details.tags['msg-param-origin-id'])
|
||||
content = `подарил ${mysteryGiftCount} сабок!`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `притопал с ${viewerCount} зрителями!`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.appendTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
}
|
||||
190
StreamLabsChats/sadkawaai/generation 1/style.css
Normal file
@ -0,0 +1,190 @@
|
||||
/* Imports */
|
||||
@import url("https://fonts.googleapis.com/css2?family=Comfortaa:wght@300..700&family=Fredoka:wght@300..700&display=swap");
|
||||
|
||||
/* Variables */
|
||||
:root {
|
||||
/* Fonts */
|
||||
--font-size: 35px;
|
||||
--font-family-1: "Comfortaa", sans-serif;
|
||||
--font-family-2: "Fredoka", sans-serif;
|
||||
|
||||
/* Emotes */
|
||||
--emote-size: calc(var(--font-size) * 2);
|
||||
--emote-size-xl: calc(var(--font-size) * 3);
|
||||
--emote-size-xxl: calc(var(--font-size) * 8);
|
||||
|
||||
/* Badges */
|
||||
--badge-size: 25px;
|
||||
|
||||
/* Colors */
|
||||
--color-1: #ffffff;
|
||||
--color-2: #ffc5d3;
|
||||
}
|
||||
|
||||
/* Main styling */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(to top,
|
||||
rgba(0, 0, 0, 1) 0%,
|
||||
rgba(0, 0, 0, 1) 75%,
|
||||
rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
|
||||
content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.font-1 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
.font-2 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-2);
|
||||
}
|
||||
|
||||
.emote {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.emote.emote-left {
|
||||
margin-left: -5.5px;
|
||||
}
|
||||
|
||||
.emote.emote-right {
|
||||
margin-right: -5.5px;
|
||||
}
|
||||
|
||||
.emote img {
|
||||
height: var(--emote-size);
|
||||
}
|
||||
|
||||
.emote img.zero-width {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
[large-emotes="true"] .emote img {
|
||||
height: var(--emote-size-xl);
|
||||
}
|
||||
|
||||
[gigaemote="true"] .emote img {
|
||||
height: var(--emote-size-xxl);
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes disappear {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes deleted {
|
||||
to {
|
||||
transform: translateX(-200%);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px 5px 0 5px;
|
||||
font-size: var(--font-size);
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
#chatbox message,
|
||||
#chatbox event {
|
||||
max-height: var(--max-height);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
word-break: break-word;
|
||||
transform-origin: bottom left;
|
||||
animation: appear 700ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
#chatbox[disappear="true"] message,
|
||||
#chatbox[disappear="true"] event {
|
||||
animation:
|
||||
appear 700ms ease-in-out forwards,
|
||||
disappear 700ms linear 30s forwards;
|
||||
}
|
||||
|
||||
#chatbox message[deleted="true"],
|
||||
#chatbox event[deleted="true"] {
|
||||
animation: deleted 700ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
message {
|
||||
padding: 30px 15px 15px;
|
||||
}
|
||||
|
||||
message top {
|
||||
display: inline-flex;
|
||||
margin-bottom: 5px;
|
||||
gap: 5px;
|
||||
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.4));
|
||||
}
|
||||
|
||||
message top author {
|
||||
position: relative;
|
||||
color: var(--color-1);
|
||||
background-color: var(--color-2);
|
||||
font-family: var(--font-family-2);
|
||||
font-weight: bolder;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 0.7);
|
||||
padding: 3px 30px 5px;
|
||||
margin-inline: 7.5px;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
message content {
|
||||
position: relative;
|
||||
color: var(--color-1);
|
||||
margin-top: 5px;
|
||||
margin-left: 65px;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
message content::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: -65px;
|
||||
width: 35px;
|
||||
aspect-ratio: 25 / 31;
|
||||
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.4));
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 25 31'%3E%3Cpath fill='%23FFAFCC' d='M24.86 15.25c-.25-4.52-2.24-8.08-6.35-9.7l-.5.8c1.54 1.28 2.87 2.6 3.24 4.67.08.5-.29.87-.74.75-1.54-.29-3.03-.7-4.52-1.16a13 13 0 0 1 .95 4.4c0 .32-.29.65-.62.61a8.54 8.54 0 0 1-5.73-2.73 14.2 14.2 0 0 1-2.28 3.15c-.54.54-1.57 1.74-2.4 1.7-.83-.05-1.5-1.16-1.74-1.83a3.89 3.89 0 0 1 0-2.56c-.38.12-.7.24-1.04.41-1.12 3.81.54 8.04 3.4 10.9 3.11 3.1 9.7 7.45 14.27 5.34 4.64-2.24 4.35-10.48 4.06-14.75ZM9.6 20.47c-.79 0-.79-1.24 0-1.24s.79 1.24 0 1.24Zm3.86-4.68c.78 0 .78 1.24 0 1.24-.8 0-.8-1.24 0-1.24Zm.29 4.01c.78 0 .78 1.25 0 1.25-.83 0-.83-1.25 0-1.25Zm1.03 5.68c-.79 0-.79-1.24 0-1.24s.79 1.24 0 1.24Zm3.53-8.12c.78 0 .78 1.24 0 1.24-.79 0-.79-1.24 0-1.24Zm.45 9.53c-.78 0-.78-1.24 0-1.24.8 0 .8 1.24 0 1.24Zm1.37-13.96c.79 0 .79 1.24 0 1.24s-.79-1.24 0-1.24Zm.3 9.82c-.8 0-.8-1.25 0-1.25.78 0 .78 1.25 0 1.25Z'/%3E%3Cpath fill='%23FFAFCC' d='M5.37 12.63A5.44 5.44 0 0 0 5 16.15c.16.69.7.6 1.04.26.29-.26.5-.52.78-.77a14 14 0 0 0 2.74-4.03c.21-.48.83-.35 1.08 0a7.18 7.18 0 0 0 4.65 2.95 11.73 11.73 0 0 0-1.37-4.54c-.25-.47.17-1.16.7-.95a47.8 47.8 0 0 0 4.82 1.42c-.58-1.5-1.74-2.53-3.03-3.56-.2-.17-.25-.56-.08-.77.58-.9 1.12-1.85 1.66-2.79.04-.17.13-.34.17-.51.16-.56-.46-.99-.92-.73-2.53 1.46-5.72.77-7.47 3.64 0 0 0 .05-.04.09a28.8 28.8 0 0 0-1.7-3.65c-.87-1.54-1.79-.47-3.2-.34-.58.04-.79.64-.46 1.12 1 1.28 2.04 2.57 3.04 3.9a6.4 6.4 0 0 0-4.07 1.2C1.84 9.21.93 10.96.06 12.64c-.2.42.16 1.15.7.94l.67-.26c.95-.56 2-.94 3.07-1.29.54-.3 1.08.13.87.6Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
16
StreamLabsChats/sadkawaai/generation 2/index.html
Normal file
@ -0,0 +1,16 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
<a class="font-2">2</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<top>
|
||||
<author>{from}</author>
|
||||
</top>
|
||||
<bottom>
|
||||
<content>{message}</content>
|
||||
<flag />
|
||||
</body>
|
||||
</message>
|
||||
</script>
|
||||
510
StreamLabsChats/sadkawaai/generation 2/script.js
Normal file
@ -0,0 +1,510 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = 'подписался'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `подарил подписку ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
subMysteries.push(details.tags['msg-param-origin-id'])
|
||||
content = `подарил ${mysteryGiftCount} сабок!`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `притопал с ${viewerCount} зрителями!`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.appendTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
}
|
||||
254
StreamLabsChats/sadkawaai/generation 2/style.css
Normal file
@ -0,0 +1,254 @@
|
||||
/* Imports */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
|
||||
|
||||
/* Variables */
|
||||
:root {
|
||||
/* Fonts */
|
||||
--font-size: 35px;
|
||||
--font-family-1: "Montserrat", sans-serif;
|
||||
--font-family-2: "Merriweather", sans-serif;
|
||||
|
||||
/* Emotes */
|
||||
--emote-size: calc(var(--font-size) * 2);
|
||||
--emote-size-xl: calc(var(--font-size) * 3);
|
||||
--emote-size-xxl: calc(var(--font-size) * 8);
|
||||
|
||||
/* Badges */
|
||||
--badge-size: 25px;
|
||||
|
||||
/* Colors */
|
||||
--color-1: #FFFFFF;
|
||||
--color-2: linear-gradient(75deg, #812027, #2A0B0C);
|
||||
--color-3: linear-gradient(75deg, #BD4867, #3B0F1B);
|
||||
--color-4: linear-gradient(75deg, #A64C77, #501732);
|
||||
}
|
||||
|
||||
/* Main styling */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(to top,
|
||||
rgba(0, 0, 0, 1) 0%,
|
||||
rgba(0, 0, 0, 1) 75%,
|
||||
rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
|
||||
content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.font-1 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
.font-2 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-2);
|
||||
}
|
||||
|
||||
.emote {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.emote.emote-left {
|
||||
margin-left: -5.5px;
|
||||
}
|
||||
|
||||
.emote.emote-right {
|
||||
margin-right: -5.5px;
|
||||
}
|
||||
|
||||
.emote img {
|
||||
height: var(--emote-size);
|
||||
}
|
||||
|
||||
.emote img.zero-width {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
[large-emotes="true"] .emote img {
|
||||
height: var(--emote-size-xl);
|
||||
}
|
||||
|
||||
[gigaemote="true"] .emote img {
|
||||
height: var(--emote-size-xxl);
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
transform: translateY(200%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes disappear {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes deleted {
|
||||
to {
|
||||
transform: translateX(-200%);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px 5px 0 5px;
|
||||
font-size: var(--font-size);
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
#chatbox message,
|
||||
#chatbox event {
|
||||
max-height: var(--max-height);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
word-break: break-word;
|
||||
transform-origin: bottom;
|
||||
animation: appear 700ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
#chatbox[disappear="true"] message,
|
||||
#chatbox[disappear="true"] event {
|
||||
animation:
|
||||
appear 700ms ease-in-out forwards,
|
||||
disappear 700ms linear 30s forwards;
|
||||
}
|
||||
|
||||
#chatbox message[deleted="true"],
|
||||
#chatbox event[deleted="true"] {
|
||||
animation: deleted 700ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
message {
|
||||
padding: 40px 15px 15px;
|
||||
}
|
||||
|
||||
message top {
|
||||
display: inline-flex;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 40px;
|
||||
gap: 5px;
|
||||
filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.4));
|
||||
}
|
||||
|
||||
message top::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: -25px;
|
||||
height: 30px;
|
||||
aspect-ratio: 296 / 18;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='296' height='18' fill='none'%3E%3Ccircle cx='175.5' cy='9.5' r='1.5' fill='%23fff'/%3E%3Ccircle cx='120.5' cy='9.5' r='1.5' fill='%23fff'/%3E%3Cpath fill='%23fff' d='M147.746 17.902a.514.514 0 0 0 .287.098.481.481 0 0 0 .318-.13c5.263-3.278 8.453-6.718 9.41-10.224.765-2.726-.351-5.582-2.711-6.978-3.157-1.85-6.028.616-7.05 1.688-1.02-1.072-3.86-3.538-7.05-1.688-2.36 1.396-3.476 4.253-2.711 6.978 1.022 3.505 4.211 6.946 9.507 10.256ZM103 9.5a.5.5 0 0 0 0-1v1ZM0 9v.5h103v-1H0V9ZM193 8a.5.5 0 0 0 0 1V8Zm0 .5V9h103V8H193v.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message top author {
|
||||
position: relative;
|
||||
color: var(--color-1);
|
||||
font-family: var(--font-family-1);
|
||||
font-weight: bolder;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 0.7);
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
message top author::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 50%;
|
||||
left: -40px;
|
||||
transform: translateY(-50%);
|
||||
width: 30px;
|
||||
aspect-ratio: 23 / 21;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='23' height='21' fill='none'%3E%3Cpath fill='%23fff' d='M20.063 11.037c-.894-.42-1.967-.945-1.099.184 1.201 1.602 2.147 3.254 1.968 4.777-.154 1.339-1.15 1.68-2.223 1.575-.665-.08-1.201-.656-1.585-1.155-1.712-2.23-4.702-2.782-6.9-.866-.87.76-1.739 2.126-3.017 2.126-1.328 0-2.095-1.575-1.635-2.756.818-2.152 3.195-3.15 4.856-4.462 2.045-1.601 3.323-4.042 2.94-6.22-.384-2.258-1.969-3.99-4.32-4.2-2.146-.21-2.862.42-4.55 1.627-.868.158-2.3-.341-3.628 1.575C.103 4.344.205 5.998.052 6.234-.102 6.39 0 7.598 1.534 7.467c1.533-.131 4.499-2.94 4.499-2.94.996-.787 1.738-1.6 2.99-1.023 1.2.552 1.2 2.074.485 3.046-.307.42-1.355 1.364-1.763 1.68-.562.393-1.124.76-1.688 1.154-1.124.814-2.172 1.785-2.913 2.966-1.456 2.336-1.304 5.354.716 7.297.996.946 2.351 1.444 3.706 1.34 1.355-.105 2.556-.815 3.578-1.707.613-.552 1.252-1.601 2.147-1.601.894 0 1.405 1.05 1.993 1.601 1.457 1.34 3.579 1.81 5.368.867 3.706-1.944 2.657-7.167-.588-9.11Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message bottom {
|
||||
position: relative;
|
||||
color: var(--color-1);
|
||||
background: var(--color-2);
|
||||
font-family: var(--font-family-2);
|
||||
padding: 40px 30px;
|
||||
border-radius: 0 25px 25px 25px;
|
||||
}
|
||||
|
||||
message.moderator bottom {
|
||||
background: var(--color-3) !important;
|
||||
}
|
||||
|
||||
message.vip bottom,
|
||||
message.subscriber bottom {
|
||||
background: var(--color-4);
|
||||
}
|
||||
|
||||
message bottom::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: 1px;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 25px;
|
||||
aspect-ratio: 16 / 53;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='53' fill='none'%3E%3Cpath fill='%23fff' d='M16 26.5 8 16 0 26.5 8 37l8-10.5Z'/%3E%3Ccircle cx='8' cy='43.5' r='1.5' fill='%23fff'/%3E%3Ccircle cx='8' cy='1.5' r='1.5' fill='%23fff'/%3E%3Ccircle cx='8' cy='51.5' r='1.5' fill='%23fff'/%3E%3Ccircle cx='8' cy='9.5' r='1.5' fill='%23fff'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message bottom::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -2px;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
message flag {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 0;
|
||||
right: 25px;
|
||||
width: 40px;
|
||||
aspect-ratio: 26 / 43;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='43' fill='none'%3E%3Cpath fill='url(%23a)' fill-opacity='.8' d='M26 41.48c0 .366-.148.683-.398.851-.25.166-.534.14-.764-.07L13.907 32.145c-.543-.504-1.271-.504-1.814 0L1.162 42.26c-.229.211-.515.24-.764.07-.249-.168-.398-.485-.398-.85V.513C0-1.425 1.211-3 2.7-3h20.596c1.49 0 2.7 1.577 2.7 3.514V41.48H26Z'/%3E%3Cdefs%3E%3ClinearGradient id='a' x1='13' x2='13' y1='-3' y2='42.441' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23B24252'/%3E%3Cstop offset='1' stop-color='%237E2026'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message.moderator flag {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='43' fill='none'%3E%3Cpath fill='url(%23a)' fill-opacity='.8' d='M26 41.48c0 .366-.148.683-.398.851-.25.166-.534.14-.764-.07L13.907 32.145c-.543-.504-1.271-.504-1.814 0L1.162 42.26c-.229.211-.515.24-.764.07-.249-.168-.398-.485-.398-.85V.513C0-1.425 1.211-3 2.7-3h20.596c1.49 0 2.7 1.577 2.7 3.514V41.48H26Z'/%3E%3Cdefs%3E%3ClinearGradient id='a' x1='13' x2='13' y1='-3' y2='42.441' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23E25E82'/%3E%3Cstop offset='1' stop-color='%23B74563'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E")!important;
|
||||
}
|
||||
|
||||
message.vip flag,
|
||||
message.subscriber flag
|
||||
{
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='26' height='43' viewBox='0 0 26 43' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M26 41.4803C26 41.8458 25.8519 42.1631 25.6017 42.331C25.3528 42.4971 25.0681 42.4705 24.8383 42.2612L13.907 32.1446C13.3645 31.6412 12.6355 31.6412 12.093 32.1446L1.16168 42.2612C0.933176 42.4722 0.647223 42.5004 0.398291 42.331C0.149359 42.1631 0 41.8458 0 41.4803V0.513963C0 -1.42495 1.21147 -3 2.69996 -3H23.2962C24.786 -3 25.9962 -1.42329 25.9962 0.513963V41.4803H26Z' fill='url(%23paint0_linear_2116_38)' fill-opacity='0.8'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_2116_38' x1='13' y1='-3' x2='13' y2='42.4411' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23D787AD'/%3E%3Cstop offset='1' stop-color='%23A6547B'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E%0A");
|
||||
}
|
||||
17
StreamLabsChats/sauyon/generation 1/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
<a class="font-2">2</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<author>{from}</author>
|
||||
<content>{message}</content>
|
||||
</message>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="chatlist_event">
|
||||
<event id="{messageId}">
|
||||
<content>{author} {content}</content>
|
||||
</event>
|
||||
</script>
|
||||
510
StreamLabsChats/sauyon/generation 1/script.js
Normal file
@ -0,0 +1,510 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = 'subscribed'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `gifted a sub to ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `gifted a sub to<br> ${recipient}`;
|
||||
content = `gifted ${mysteryGiftCount} subs`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `raided with ${viewerCount} viewers`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.appendTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
}
|
||||
190
StreamLabsChats/sauyon/generation 1/style.css
Normal file
@ -0,0 +1,190 @@
|
||||
/* Imports */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&family=Nunito:ital@0;1&display=swap');
|
||||
|
||||
/* Variables */
|
||||
:root {
|
||||
/* Fonts */
|
||||
--font-size: 35px;
|
||||
--font-family-1: "Fredoka", serif;
|
||||
--font-family-2: "Nunito", serif;
|
||||
|
||||
/* Emotes */
|
||||
--emote-size: calc(var(--font-size) * 2);
|
||||
--emote-size-xl: calc(var(--font-size) * 4);
|
||||
--emote-size-xxl: calc(var(--font-size) * 8);
|
||||
|
||||
/* Badges */
|
||||
--badge-size: 25px;
|
||||
|
||||
/* Colors */
|
||||
--color-1: #FFFFFF;
|
||||
--color-2: #E2CBA9;
|
||||
}
|
||||
|
||||
/* Main styling */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(to top,
|
||||
rgba(0, 0, 0, 1) 0%,
|
||||
rgba(0, 0, 0, 1) 75%,
|
||||
rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
|
||||
content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.font-1 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
.font-2 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-2);
|
||||
}
|
||||
|
||||
.emote {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.emote.emote-left {
|
||||
margin-left: -5.5px;
|
||||
}
|
||||
|
||||
.emote.emote-right {
|
||||
margin-right: -5.5px;
|
||||
}
|
||||
|
||||
.emote img {
|
||||
height: var(--emote-size);
|
||||
}
|
||||
|
||||
.emote img.zero-width {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
[large-emotes="true"] .emote img {
|
||||
height: var(--emote-size-xl);
|
||||
}
|
||||
|
||||
[gigaemote="true"] .emote img {
|
||||
height: var(--emote-size-xxl);
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes disappear {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes deleted {
|
||||
to {
|
||||
transform: translateX(-200%);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px 5px 0 5px;
|
||||
font-size: var(--font-size);
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
#chatbox message,
|
||||
#chatbox event {
|
||||
max-height: var(--max-height);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
word-break: break-word;
|
||||
transform-origin: bottom;
|
||||
animation: appear 700ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
#chatbox[disappear="true"] message,
|
||||
#chatbox[disappear="true"] event {
|
||||
animation:
|
||||
appear 700ms ease-in-out forwards,
|
||||
disappear 700ms linear 30s forwards;
|
||||
}
|
||||
|
||||
#chatbox message[deleted="true"],
|
||||
#chatbox event[deleted="true"] {
|
||||
animation: deleted 700ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
message {
|
||||
padding: 10px;
|
||||
color: var(--color-1);
|
||||
}
|
||||
|
||||
message author {
|
||||
position: relative;
|
||||
font-family: var(--font-family-1);
|
||||
margin-left: 40px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
message author::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: -45px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 30px;
|
||||
aspect-ratio: 13 / 15;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 13 15'%3E%3Cpath fill='%23E2CBA9' d='M12.8932 2.52946C12.5796.764687 11.0684-.218957 9.32914.0414197 7.67541.272865 6.47788 1.57475 5.73655 3.02128 3.91175.735756.632797 1.25651.0910575 4.46782-.222581 6.29045.290646 8.14201 1.37412 9.58855 2.82827 11.498 4.79564 13.176 6.47788 14.8539c.31364.2893.71281.1158.85538-.2314 1.5967-3.8189 6.44384-7.52199 5.55994-12.09304Zm-1.8818 4.19495C9.81385 9.18352 7.90351 11.1797 6.70598 13.6099 4.85266 11.903 2.4576 10.1093 1.43115 7.85271.832385 6.55083.632797 4.84391 1.3171 3.5131c.94091-1.90942 3.1649-1.01257 4.04879.40503v.02893c-.05703.49183.76984.78113 1.02645.28931.79835-1.5044 2.16696-3.73206 4.21986-3.00879 2.1955.78113 1.1405 3.99243.3992 5.49683Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message content {
|
||||
font-family: var(--font-family-2);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
event {
|
||||
margin: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
event content {
|
||||
font-family: var(--font-family-1);
|
||||
font-weight: 600;
|
||||
padding: 5px 75px;
|
||||
border: 5px solid var(--color-2);
|
||||
border-radius: 50px;
|
||||
color: var(--color-2);
|
||||
}
|
||||
17
StreamLabsChats/sonya/generation 1/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<div id="chatbox" class="sl__chat__layout">
|
||||
<a class="font-1">1</a>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="chatlist_item">
|
||||
<message id="{messageId}">
|
||||
<author>{from}</author>
|
||||
<content>{message}</content>
|
||||
</message>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="chatlist_event">
|
||||
<event id="{messageId}">
|
||||
<author>{author}</author>
|
||||
<content>{content}</content>
|
||||
</event>
|
||||
</script>
|
||||
510
StreamLabsChats/sonya/generation 1/script.js
Normal file
@ -0,0 +1,510 @@
|
||||
let $chatBox = $(".sl__chat__layout");
|
||||
|
||||
let usersPfp = {};
|
||||
let parity = true;
|
||||
|
||||
let emoteSetId;
|
||||
let websocketEmotesConnected = false;
|
||||
let globalEmotes = [];
|
||||
let channelEmotes = [];
|
||||
let subMysteries = [];
|
||||
|
||||
let messageProcessingList = [
|
||||
detachMessage,
|
||||
loadGlobalEmotes,
|
||||
loadChannelEmotes,
|
||||
processTags,
|
||||
getProfilePicture,
|
||||
cleanText,
|
||||
userPings,
|
||||
upscaleBadges,
|
||||
upscaleEmotes,
|
||||
process7TVEmotes,
|
||||
fixEmotesPadding,
|
||||
largeEmotes,
|
||||
parityParse,
|
||||
preloadImages,
|
||||
animate,
|
||||
];
|
||||
|
||||
$(document).on("onEventReceived", function (event) {
|
||||
let details = event.detail;
|
||||
let command = details.command;
|
||||
|
||||
if (
|
||||
[
|
||||
"",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"NOTICE",
|
||||
"PART",
|
||||
"PASS",
|
||||
"PING",
|
||||
"PONG",
|
||||
"CAP",
|
||||
"GLOBALUSERSTATE",
|
||||
"HOSTTARGET",
|
||||
"RECONNECT",
|
||||
"ROOMSTATE",
|
||||
"USERSTATE",
|
||||
"WHISPER",
|
||||
].includes(command)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "PRIVMSG":
|
||||
commandPRIVMSG(details);
|
||||
break;
|
||||
|
||||
case "USERNOTICE":
|
||||
commandUSERNOTICE(details);
|
||||
break;
|
||||
|
||||
case "CLEARCHAT":
|
||||
commandClearCHAT(details);
|
||||
break;
|
||||
|
||||
case "CLEARMSG":
|
||||
commandClearMSG(details);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Event ${command} not handled.`, details);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
async function nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
if (functions[0] != undefined)
|
||||
await functions[0](
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
async function commandPRIVMSG(details) {
|
||||
let $messageElement = $(`#${details.messageId}`);
|
||||
let $parentElement = $messageElement.parent() || $chatBox;
|
||||
|
||||
if (details.body == '!event' && details.tags['user-id'] == details.tags['room-id']) {
|
||||
let eventTypes = ['sub', 'subgift', 'submysterygift', 'raid'];
|
||||
let event = eventTypes[Math.floor(Math.random()*eventTypes.length)];
|
||||
details.tags['msg-id'] = event;
|
||||
return commandUSERNOTICE(details);
|
||||
}
|
||||
|
||||
if (!$messageElement.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextFunction(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
messageProcessingList
|
||||
);
|
||||
}
|
||||
|
||||
async function commandUSERNOTICE(details) {
|
||||
let $eventElement = $('#chatlist_event')
|
||||
let eventType = details.tags['msg-id']
|
||||
let author = details.tags['display-name']
|
||||
let content;
|
||||
let type;
|
||||
|
||||
let displayName = details.tags['display-name']
|
||||
let recipient = details.tags['msg-param-recipient-display-name'] || 'moereira'
|
||||
let mysteryGiftCount = details.tags['msg-param-mass-gift-count'] || 100
|
||||
let viewerCount = details.tags['msg-param-viewerCount'] || 100
|
||||
|
||||
if (!$eventElement.length) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (eventType) {
|
||||
case 'sub':
|
||||
case 'resub':
|
||||
case 'standardpayforward':
|
||||
case 'communitypayforward':
|
||||
case 'giftpaidupgrade':
|
||||
case 'primepaidupgrade':
|
||||
content = 'subscribed'
|
||||
type = 'sub';
|
||||
break;
|
||||
case 'subgift':
|
||||
case 'anonsubgift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `gifted a sub to ${recipient}`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'submysterygift':
|
||||
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
|
||||
content = `gifted a sub to<br> ${recipient}`;
|
||||
content = `gifted ${mysteryGiftCount} subs`;
|
||||
type = 'gift';
|
||||
break;
|
||||
case 'raid':
|
||||
content = `raided with ${viewerCount} viewers`;
|
||||
type = 'raid';
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
$eventElement = $eventElement.html()
|
||||
$eventElement = $eventElement.replace('{messageId}', details.messageId)
|
||||
$eventElement = $eventElement.replace('{author}', author)
|
||||
$eventElement = $eventElement.replace('{content}', content)
|
||||
$eventElement = $($eventElement)
|
||||
|
||||
$eventElement.addClass(type)
|
||||
$eventElement.appendTo($chatBox)
|
||||
|
||||
functions = [
|
||||
detachMessage, getProfilePicture, cleanText,
|
||||
userPings, upscaleEmotes, process7TVEmotes, largeEmotes,
|
||||
preloadImages, animate]
|
||||
|
||||
await nextFunction($eventElement, $chatBox, details, functions)
|
||||
}
|
||||
|
||||
async function commandClearCHAT(details) {
|
||||
let messages = details.body
|
||||
? $(`[userId="${details.tags["target-user-id"]}"]`)
|
||||
: $("message");
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages.eq(i);
|
||||
setTimeout(() => {
|
||||
message.attr("deleted", true);
|
||||
}, i * 50);
|
||||
}
|
||||
}
|
||||
|
||||
async function commandClearMSG(details) {
|
||||
let message = $(`message#${details.messageId}`);
|
||||
message.attr("deleted", true);
|
||||
}
|
||||
|
||||
async function detachMessage(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
$messageElement.detach();
|
||||
$messageElement.hide();
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function loadGlobalEmotes($messageElement, $parentElement, details, functions) {
|
||||
if (globalEmotes.length == 0) {
|
||||
fetch('https://7tv.io/v3/emote-sets/global')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Global emotes: Error: (${response.status})`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(`Global emotes: Success (${data.emotes.length})`);
|
||||
globalEmotes = data.emotes;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
|
||||
async function loadChannelEmotes($messageElement, $parentElement, details, functions) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
|
||||
let tags = details.tags;
|
||||
let roomId = tags['room-id'];
|
||||
|
||||
if (!emoteSetId) {
|
||||
try {
|
||||
let response = await fetch(`https://7tv.io/v3/users/twitch/${roomId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Channel emotes: Error: (${response.status})`);
|
||||
}
|
||||
let data = await response.json();
|
||||
console.log(`Channel emotes: Success (${data.emote_set.emotes.length})`);
|
||||
channelEmotes = data.emote_set.emotes;
|
||||
emoteSetId = data.emote_set.id;
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!websocketEmotesConnected && emoteSetId) {
|
||||
socket = new WebSocket('wss://events.7tv.io/v3');
|
||||
websocketEmotesConnected = true;
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('Connected to 7TV WebSocket');
|
||||
socket.send(JSON.stringify({
|
||||
'op': 35,
|
||||
'd': {
|
||||
'type': 'emote_set.update',
|
||||
'condition': {
|
||||
'object_id': emoteSetId
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
switch (data.op) {
|
||||
case 0:
|
||||
let eventData = data.d.body;
|
||||
|
||||
if (eventData.hasOwnProperty('pulled')) {
|
||||
let emoteId = eventData.pulled[0].old_value.id;
|
||||
channelEmotes = channelEmotes.filter(obj =>
|
||||
obj.id !== emoteId);
|
||||
};
|
||||
if (eventData.hasOwnProperty('pushed')) {
|
||||
let emoteData = eventData.pushed[0].value;
|
||||
channelEmotes.push(emoteData);
|
||||
};
|
||||
if (eventData.hasOwnProperty('updated')) {
|
||||
let emoteId = eventData.updated[0].old_value.id;
|
||||
let emoteData = eventData.updated[0].value;
|
||||
|
||||
let emoteIndex = channelEmotes.findIndex(
|
||||
obj => obj.id === emoteId);
|
||||
|
||||
if (emoteIndex !== -1) {
|
||||
channelEmotes[emoteIndex] = emoteData;
|
||||
};
|
||||
};
|
||||
|
||||
break;
|
||||
case 4:
|
||||
socket.close();
|
||||
websocketEmotesConnected = false;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket closed:', event.code, event.reason);
|
||||
websocketEmotesConnected = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function processTags(
|
||||
$messageElement,
|
||||
$parentElement,
|
||||
details,
|
||||
functions
|
||||
) {
|
||||
let tags = details.tags;
|
||||
let info = tags["badge-info"];
|
||||
let badges = tags.badges.split(",");
|
||||
let msgId = tags["msg-id"];
|
||||
|
||||
if (info) {
|
||||
let badge = info.replace("/", "-");
|
||||
$messageElement.addClass(badge);
|
||||
}
|
||||
|
||||
badges.forEach((badge) => {
|
||||
let name = badge.split("/")[0];
|
||||
if (name) {
|
||||
$messageElement.addClass(name);
|
||||
}
|
||||
});
|
||||
|
||||
$messageElement.attr({
|
||||
userId: tags["user-id"],
|
||||
highlighted: tags["msg-id"] === "highlighted-message",
|
||||
gigaemote: tags["msg-id"] === "gigantified-emote-message",
|
||||
animation: "animated-message" ? tags["animation-id"] : "",
|
||||
"first-msg": tags["first-msg"] === true && msgId !== "user-intro",
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions);
|
||||
}
|
||||
|
||||
async function getProfilePicture($messageElement, $parentElement, details, functions) {
|
||||
let $profilePictureElement = $messageElement.find('pfp').eq(0);
|
||||
let userId = details.tags['user-id'];
|
||||
|
||||
if ($profilePictureElement.length != 0 && !(userId in usersPfp)) {
|
||||
try { let response = await fetch('https' + '://st' + 'reaml' + 'abs.c' + 'om/ap' + 'i/v5/' + 'helix' + '/user' + 's?bro' + 'adcas' + 'ter_i' + 'd=' + userId); if (!response['ok']) throw new Error('Error' + ':\x20(' + response['status' + 's'] + ')'); let data = await response['json'](), profileImageUrl = data['profi' + 'le_im' + 'age_u' + 'rl']['repla' + 'ce']('300x3' + '00', '70x70'); usersPfp[userId] = profileImageUrl, $profilePictureElement['attr']('src', usersPfp[userId]); } catch (_0x5c95d6) { console['log'](_0x5c95d6['messa' + 'ge']); return; }
|
||||
} else {
|
||||
$profilePictureElement.attr('src', usersPfp[userId]);
|
||||
}
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function cleanText($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let specialChar = new TextDecoder().decode(new Uint8Array([243, 160, 128, 128]));
|
||||
let cleanedHTML = $contentElement.html().replaceAll(specialChar, '').replaceAll('\n', '');
|
||||
|
||||
$contentElement.html(cleanedHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function userPings($messageElement, $parentElement, details, functions) {
|
||||
let $content = $messageElement.find('content');
|
||||
let contentHTML = $content.html();
|
||||
let pings = $content.text().match(/@\w+/g) || [];
|
||||
|
||||
pings.forEach(user => {
|
||||
contentHTML = contentHTML.replace(
|
||||
new RegExp(user, 'g'),
|
||||
`<user-ping>${user}</user-ping>`
|
||||
);
|
||||
});
|
||||
|
||||
$content.html(contentHTML);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleBadges($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.badge').each(function () {
|
||||
let $badge = $(this);
|
||||
let $badgeImage = $badge.children('img').eq(0);
|
||||
|
||||
if ($badgeImage.attr('src').includes('jtvnw')) {
|
||||
$badgeImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function upscaleEmotes($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.find('.emote').each(function () {
|
||||
let $emote = $(this);
|
||||
let $emoteImage = $emote.children('img').eq(0);
|
||||
|
||||
if ($emoteImage.attr('src').includes('jtvnw')) {
|
||||
$emoteImage.attr('src', function (index, oldSrc) {
|
||||
return oldSrc.replace('1.0', '4.0');
|
||||
});
|
||||
}
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function process7TVEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $headElement = $contentElement.find('head').eq(0);
|
||||
let $content = ` ${$contentElement.html()} `;
|
||||
let emotes = globalEmotes.concat(channelEmotes);
|
||||
|
||||
if ($headElement[0] != undefined) {
|
||||
$content = $content.replace($headElement[0].outerHTML,
|
||||
` ${$headElement[0].outerHTML} `);
|
||||
}
|
||||
|
||||
emotes.forEach(emote => {
|
||||
let temp_text = '';
|
||||
let emoteSrc = `https://${emote.data.host.url}/4x.avif`;
|
||||
let emoteClasses = 'emote';
|
||||
|
||||
if (emote.flags == 1) {
|
||||
emoteClasses = 'emote zero-width';
|
||||
};
|
||||
|
||||
while (($content = (temp_text = $content).replace(
|
||||
` ${emote.name} `,
|
||||
` <span class="${emoteClasses}"><img src="${emoteSrc}"></span> `
|
||||
)) !== temp_text) { }
|
||||
});
|
||||
|
||||
$contentElement.html($content);
|
||||
$contentElement.find('.zero-width').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.children[0].classList.add('zero-width');
|
||||
this.previousElementSibling.innerHTML += this.innerHTML;
|
||||
this.remove();
|
||||
};
|
||||
});
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function fixEmotesPadding($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
|
||||
$contentElement.find('.emote').each(function () {
|
||||
let previousSibling = this.previousSibling.nodeValue;
|
||||
let nextSibling = this.nextSibling.nodeValue;
|
||||
|
||||
if (previousSibling && previousSibling.trim() === '') {
|
||||
this.classList.add('emote-left');
|
||||
};
|
||||
|
||||
if (nextSibling && nextSibling.trim() === '') {
|
||||
this.classList.add('emote-right');
|
||||
};
|
||||
});
|
||||
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function largeEmotes($messageElement, $parentElement, details, functions) {
|
||||
let $contentElement = $messageElement.find('content').eq(0);
|
||||
let $content = $contentElement.clone().find('head').remove().end().text().replace(/\s+/g, '');
|
||||
let emoteCount = $messageElement.find('.emote').length;
|
||||
|
||||
$messageElement.attr('large-emotes', !$content && emoteCount <= 3);
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function parityParse($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.attr('parity', parity ? 'even' : 'odd');
|
||||
parity = !parity;
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
}
|
||||
|
||||
async function preloadImages($messageElement, $parentElement, details, functions) {
|
||||
let $images = $messageElement.find('img');
|
||||
let imagesCount = $images.length;
|
||||
|
||||
if (imagesCount > 0) {
|
||||
let imagesLoaded = 0;
|
||||
$images.on('load', function () {
|
||||
if (++imagesLoaded === imagesCount) {
|
||||
nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
await nextFunction($messageElement, $parentElement, details, functions)
|
||||
};
|
||||
};
|
||||
|
||||
async function animate($messageElement, $parentElement, details, functions) {
|
||||
$messageElement.appendTo($parentElement).slideDown(700, function () {
|
||||
$messageElement.get(0).style.setProperty('--max-height',
|
||||
`${$messageElement.outerHeight()}px`);
|
||||
}).animate({}, 700);
|
||||
}
|
||||
1
StreamLabsChats/sonya/generation 1/static/bow.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="34" fill="none"><mask id="a" width="14" height="28" x="1" y="5" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#ABAFBC" d="M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z"/><path fill="#A8ACB9" d="M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z"/><g opacity=".62" style="mix-blend-mode:plus-lighter"><path fill="#ABAFBC" d="M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z"/><path fill="#A8ACB9" d="M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z"/></g><g opacity=".83"><path fill="#A8ACB9" d="M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z"/><path fill="#A8ACB9" d="M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z" opacity=".65" style="mix-blend-mode:plus-lighter"/></g></mask><g mask="url(#a)"><path fill="#AAAEBC" d="M-1.395 2.367 15.238-.714l6.001 32.393-16.632 3.082z"/></g><path fill="url(#b)" d="M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z"/><path fill="url(#c)" d="M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z"/><g opacity=".62" style="mix-blend-mode:plus-lighter"><path fill="url(#d)" d="M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z"/><path fill="url(#e)" d="M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z"/></g><g opacity=".83"><path fill="url(#f)" d="M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z"/><path fill="url(#g)" d="M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z" opacity=".65" style="mix-blend-mode:plus-lighter"/></g><defs><linearGradient id="b" x1="4.306" x2="8.305" y1="10.845" y2="32.426" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1F5"/><stop offset="1" stop-color="#FBDFE2"/></linearGradient><linearGradient id="c" x1="6.924" x2="9.012" y1="9.263" y2="20.529" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF2F6"/><stop offset="1" stop-color="#FADBDE"/></linearGradient><linearGradient id="d" x1="4.306" x2="8.305" y1="10.845" y2="32.426" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1F5"/><stop offset="1" stop-color="#FBDFE2"/></linearGradient><linearGradient id="e" x1="6.924" x2="9.012" y1="9.263" y2="20.529" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF2F6"/><stop offset="1" stop-color="#FADBDE"/></linearGradient><linearGradient id="f" x1="4.873" x2="14.082" y1="6.695" y2="13.513" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF2F6"/><stop offset="1" stop-color="#FADBDE"/></linearGradient><linearGradient id="g" x1="4.873" x2="14.082" y1="6.695" y2="13.513" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF2F6"/><stop offset="1" stop-color="#FADBDE"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
1
StreamLabsChats/sonya/generation 1/static/dots.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2" height="15" fill="none"><circle cx="1.002" cy="1.633" r="1" fill="#fff"/><circle cx="1.002" cy="13.633" r="1" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 180 B |
1
StreamLabsChats/sonya/generation 1/static/petals.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="51" height="9" fill="none"><path fill="url(#a)" d="M4.54 8.013C2.032 8.013 0 6.255 0 4.085S2.032.156 4.54.156c1.424 0 3.314 1.267 4.681 2.362a1.986 1.986 0 0 1 0 3.133C7.854 6.746 5.964 8.013 4.54 8.013Z"/><path fill="url(#b)" d="M24.54 8.013c-2.508 0-4.54-1.758-4.54-3.928S22.032.156 24.54.156c1.424 0 3.314 1.267 4.681 2.362a1.986 1.986 0 0 1 0 3.133c-1.367 1.095-3.258 2.362-4.681 2.362Z"/><path fill="url(#c)" d="M44.54 8.013c-2.508 0-4.54-1.758-4.54-3.928S42.032.156 44.54.156c1.424 0 3.314 1.267 4.681 2.362a1.986 1.986 0 0 1 0 3.133c-1.367 1.095-3.257 2.362-4.681 2.362Z"/><defs><linearGradient id="a" x1="0" x2="11" y1="4.085" y2="4.085" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient><linearGradient id="b" x1="20" x2="31" y1="4.085" y2="4.085" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient><linearGradient id="c" x1="40" x2="51" y1="4.085" y2="4.085" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="31" height="32" fill="none"><path fill="#fff" d="M14.923 11.178a2 2 0 0 1 3.154 0l.514.66c.232.296.541.52.895.649l.786.286a2 2 0 0 1 .975 3l-.469.692a2 2 0 0 0-.341 1.052l-.029.836a2 2 0 0 1-2.551 1.853l-.804-.23a1.999 1.999 0 0 0-1.106 0l-.804.23a2 2 0 0 1-2.551-1.853l-.029-.837a2 2 0 0 0-.341-1.05l-.469-.694a2 2 0 0 1 .975-3l.786-.285c.354-.128.663-.353.895-.65l.514-.66Z"/><path stroke="#fff" stroke-dasharray="0.1 6" stroke-linecap="round" d="M14.15 1.27a15 15 0 1 0 13.684 24.103"/></svg>
|
||||
|
After Width: | Height: | Size: 543 B |
1
StreamLabsChats/sonya/generation 1/static/star.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="15" fill="none"><path fill="url(#a)" d="M10.752 4.884c0 1.743-1.223 3.156-2.732 3.156-1.51 0-2.732-1.413-2.733-3.157 0-.973.852-2.26 1.604-3.207a1.426 1.426 0 0 1 2.257 0c.752.947 1.604 2.234 1.604 3.208Z"/><path fill="url(#b)" d="M5.408 4.76c1.744 0 3.158 1.223 3.158 2.731 0 1.51-1.413 2.732-3.157 2.732-.974 0-2.26-.852-3.207-1.604a1.426 1.426 0 0 1 0-2.256c.946-.753 2.233-1.604 3.206-1.604Z"/><path fill="url(#c)" d="M5.287 9.554c0-1.744 1.223-3.157 2.732-3.157 1.51 0 2.732 1.414 2.733 3.157 0 .974-.852 2.26-1.604 3.207a1.426 1.426 0 0 1-2.257 0c-.752-.947-1.604-2.234-1.604-3.207Z"/><path fill="url(#d)" d="M11.177 4.758c-1.744 0-3.157 1.223-3.157 2.732 0 1.509 1.413 2.732 3.157 2.732.973 0 2.26-.85 3.207-1.603a1.426 1.426 0 0 0 0-2.257c-.947-.752-2.234-1.603-3.207-1.604Z"/><defs><linearGradient id="a" x1="8.019" x2="8.019" y1=".391" y2="8.04" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient><linearGradient id="b" x1="4.741" x2="4.74" y1="4.759" y2="10.223" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient><linearGradient id="c" x1="8.019" x2="8.019" y1="6.397" y2="14.047" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient><linearGradient id="d" x1="11.845" x2="11.845" y1="10.222" y2="4.759" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF1FA"/><stop offset="1" stop-color="#fff"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
StreamLabsChats/sonya/generation 1/static/sword.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none"><path fill="url(#a)" d="M7.143 10.511a.735.735 0 0 1-.798 1.201.733.733 0 0 1-.238-.164L5.07 10.51l-2.074 2.074a.734.734 0 0 1-1.037 1.037L.922 12.585a.733.733 0 0 1 1.037-1.037l2.074-2.074-1.037-1.037a.733.733 0 0 1 1.037-1.036l3.11 3.11Zm.52-.518L4.55 6.882 10.773.66l2.593.519.518 2.592-6.221 6.222Z"/><defs><linearGradient id="a" x1="7.295" x2="7.295" y1=".66" y2="13.837" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#FFF2F6"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 578 B |
9
StreamLabsChats/sonya/generation 1/static/vip.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.1641 0.785156C11.262 0.785158 11.3586 0.807536 11.4467 0.850622C11.5348 0.893708 11.6122 0.956388 11.6731 1.03399L11.7212 1.10508L13.6712 4.39653C13.7351 4.50446 13.7671 4.62866 13.7634 4.75443C13.7597 4.8802 13.7205 5.00226 13.6504 5.10616L13.5984 5.17397L8.0538 11.4494C7.95084 11.5558 7.82795 11.6403 7.69234 11.6979C7.55672 11.7556 7.41111 11.7852 7.26403 11.7852C7.00402 11.7852 6.75377 11.693 6.54251 11.5106L6.45476 11.4277L0.929626 5.17397C0.846504 5.08004 0.791777 4.9639 0.771957 4.83938C0.752136 4.71486 0.768056 4.58719 0.817823 4.47157L0.856824 4.39587L2.81532 1.08994L2.85692 1.0307C2.88675 0.99328 2.92053 0.959285 2.95767 0.929321L3.01553 0.887191L3.07273 0.854277L3.10523 0.839136L3.14423 0.822679L3.21508 0.801614L3.28788 0.788448L3.36393 0.785156H11.1641Z" fill="url(#paint0_linear_1587_171)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1587_171" x1="7.26367" y1="0.785156" x2="7.26367" y2="11.7852" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFEEF9"/>
|
||||
<stop offset="1" stop-color="white"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
276
StreamLabsChats/sonya/generation 1/style.css
Normal file
@ -0,0 +1,276 @@
|
||||
/* Imports */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap');
|
||||
|
||||
/* Variables */
|
||||
:root {
|
||||
/* Fonts */
|
||||
--font-size: 35px;
|
||||
--font-family-1: "Nunito", serif;
|
||||
|
||||
/* Emotes */
|
||||
--emote-size: calc(var(--font-size) * 2);
|
||||
--emote-size-xl: calc(var(--font-size) * 4);
|
||||
--emote-size-xxl: calc(var(--font-size) * 8);
|
||||
|
||||
/* Badges */
|
||||
--badge-size: 25px;
|
||||
}
|
||||
|
||||
/* Main styling */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(to top,
|
||||
rgba(0, 0, 0, 1) 0%,
|
||||
rgba(0, 0, 0, 1) 75%,
|
||||
rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
|
||||
content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.font-1 {
|
||||
opacity: 0;
|
||||
font-family: var(--font-family-1);
|
||||
}
|
||||
|
||||
.emote {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.emote.emote-left {
|
||||
margin-left: -5.5px;
|
||||
}
|
||||
|
||||
.emote.emote-right {
|
||||
margin-right: -5.5px;
|
||||
}
|
||||
|
||||
.emote img {
|
||||
height: var(--emote-size);
|
||||
}
|
||||
|
||||
.emote img.zero-width {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
[large-emotes="true"] .emote img {
|
||||
height: var(--emote-size-xl);
|
||||
}
|
||||
|
||||
[gigaemote="true"] .emote img {
|
||||
height: var(--emote-size-xxl);
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(200%)
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes disappear {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0)
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-200%)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes deleted {
|
||||
to {
|
||||
transform: translateX(-200%);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px 5px 0 5px;
|
||||
font-size: var(--font-size);
|
||||
font-family: var(--font-family-1);
|
||||
/* background: gray; */
|
||||
}
|
||||
|
||||
#chatbox message,
|
||||
#chatbox event {
|
||||
max-height: var(--max-height);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
word-break: break-word;
|
||||
transform-origin: bottom;
|
||||
margin-top: 30px;
|
||||
animation: appear 600ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
#chatbox[disappear="true"] message,
|
||||
#chatbox[disappear="true"] event {
|
||||
animation:
|
||||
appear 600ms ease-in-out forwards,
|
||||
disappear 600ms linear 30s forwards;
|
||||
}
|
||||
|
||||
#chatbox message[deleted="true"],
|
||||
#chatbox event[deleted="true"] {
|
||||
animation: deleted 600ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
message {
|
||||
padding: 10px 30px 15px 10px;
|
||||
font-family: var(--font-family-1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
message author {
|
||||
position: relative;
|
||||
margin-left: 40px;
|
||||
font-weight: 600;
|
||||
text-transform: lowercase;
|
||||
letter-spacing: .2rem;
|
||||
font-weight: 600;
|
||||
filter: drop-shadow(0 0 2px white);
|
||||
}
|
||||
|
||||
message author::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: -35px;
|
||||
top: 5px;
|
||||
width: 35px;
|
||||
transform: translateX(-25%);
|
||||
aspect-ratio: 16 / 15;
|
||||
filter: drop-shadow(0 0 1px white);
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='15' fill='none'%3E%3Cpath fill='url(%23a)' d='M10.752 4.884c0 1.743-1.223 3.156-2.732 3.156-1.51 0-2.732-1.413-2.733-3.157 0-.973.852-2.26 1.604-3.207a1.426 1.426 0 0 1 2.257 0c.752.947 1.604 2.234 1.604 3.208Z'/%3E%3Cpath fill='url(%23b)' d='M5.408 4.76c1.744 0 3.158 1.223 3.158 2.731 0 1.51-1.413 2.732-3.157 2.732-.974 0-2.26-.852-3.207-1.604a1.426 1.426 0 0 1 0-2.256c.946-.753 2.233-1.604 3.206-1.604Z'/%3E%3Cpath fill='url(%23c)' d='M5.287 9.554c0-1.744 1.223-3.157 2.732-3.157 1.51 0 2.732 1.414 2.733 3.157 0 .974-.852 2.26-1.604 3.207a1.426 1.426 0 0 1-2.257 0c-.752-.947-1.604-2.234-1.604-3.207Z'/%3E%3Cpath fill='url(%23d)' d='M11.177 4.758c-1.744 0-3.157 1.223-3.157 2.732 0 1.509 1.413 2.732 3.157 2.732.973 0 2.26-.85 3.207-1.603a1.426 1.426 0 0 0 0-2.257c-.947-.752-2.234-1.603-3.207-1.604Z'/%3E%3Cdefs%3E%3ClinearGradient id='a' x1='8.019' x2='8.019' y1='.391' y2='8.04' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' x1='4.741' x2='4.74' y1='4.759' y2='10.223' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' x1='8.019' x2='8.019' y1='6.397' y2='14.047' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient id='d' x1='11.845' x2='11.845' y1='10.222' y2='4.759' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message.vip author::before {
|
||||
aspect-ratio: 14 / 12;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='14' height='12' viewBox='0 0 14 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11.1641 0.785156C11.262 0.785158 11.3586 0.807536 11.4467 0.850622C11.5348 0.893708 11.6122 0.956388 11.6731 1.03399L11.7212 1.10508L13.6712 4.39653C13.7351 4.50446 13.7671 4.62866 13.7634 4.75443C13.7597 4.8802 13.7205 5.00226 13.6504 5.10616L13.5984 5.17397L8.0538 11.4494C7.95084 11.5558 7.82795 11.6403 7.69234 11.6979C7.55672 11.7556 7.41111 11.7852 7.26403 11.7852C7.00402 11.7852 6.75377 11.693 6.54251 11.5106L6.45476 11.4277L0.929626 5.17397C0.846504 5.08004 0.791777 4.9639 0.771957 4.83938C0.752136 4.71486 0.768056 4.58719 0.817823 4.47157L0.856824 4.39587L2.81532 1.08994L2.85692 1.0307C2.88675 0.99328 2.92053 0.959285 2.95767 0.929321L3.01553 0.887191L3.07273 0.854277L3.10523 0.839136L3.14423 0.822679L3.21508 0.801614L3.28788 0.788448L3.36393 0.785156H11.1641Z' fill='url(%23paint0_linear_1587_171)'/%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_1587_171' x1='7.26367' y1='0.785156' x2='7.26367' y2='11.7852' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFEEF9'/%3E%3Cstop offset='1' stop-color='white'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
message.moderator author::before {
|
||||
aspect-ratio: 14 / 14;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none'%3E%3Cpath fill='url(%23a)' d='M7.143 10.511a.735.735 0 0 1-.798 1.201.733.733 0 0 1-.238-.164L5.07 10.51l-2.074 2.074a.734.734 0 0 1-1.037 1.037L.922 12.585a.733.733 0 0 1 1.037-1.037l2.074-2.074-1.037-1.037a.733.733 0 0 1 1.037-1.036l3.11 3.11Zm.52-.518L4.55 6.882 10.773.66l2.593.519.518 2.592-6.221 6.222Z'/%3E%3Cdefs%3E%3ClinearGradient id='a' x1='7.295' x2='7.295' y1='.66' y2='13.837' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23fff'/%3E%3Cstop offset='1' stop-color='%23FFF2F6'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message content {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
font-weight: 500;
|
||||
background-color: rgba(168, 172, 181, .45);
|
||||
border: 2.5px solid #FFF1FA;
|
||||
border-radius: 10px;
|
||||
padding: 12px 20px;
|
||||
margin-left: 40px;
|
||||
margin-top: 5px;
|
||||
letter-spacing: .2rem;
|
||||
}
|
||||
|
||||
message content::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: -31.5px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 5px;
|
||||
aspect-ratio: 2 / 15;
|
||||
filter: drop-shadow(0 0 3px white);
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='15' fill='none'%3E%3Ccircle cx='1.002' cy='1.633' r='1' fill='%23fff'/%3E%3Ccircle cx='1.002' cy='13.633' r='1' fill='%23fff'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
message content::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
bottom: -20px;
|
||||
right: -45px;
|
||||
width: 45px;
|
||||
aspect-ratio: 22 / 34;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='34' fill='none'%3E%3Cmask id='a' width='14' height='28' x='1' y='5' maskUnits='userSpaceOnUse' style='mask-type:alpha'%3E%3Cpath fill='%23ABAFBC' d='M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z'/%3E%3Cpath fill='%23A8ACB9' d='M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z'/%3E%3Cg opacity='.62' style='mix-blend-mode:plus-lighter'%3E%3Cpath fill='%23ABAFBC' d='M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z'/%3E%3Cpath fill='%23A8ACB9' d='M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z'/%3E%3C/g%3E%3Cg opacity='.83'%3E%3Cpath fill='%23A8ACB9' d='M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z'/%3E%3Cpath fill='%23A8ACB9' d='M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z' opacity='.65' style='mix-blend-mode:plus-lighter'/%3E%3C/g%3E%3C/mask%3E%3Cg mask='url(%23a)'%3E%3Cpath fill='%23AAAEBC' d='M-1.395 2.367 15.238-.714l6.001 32.393-16.632 3.082z'/%3E%3C/g%3E%3Cpath fill='url(%23b)' d='M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z'/%3E%3Cpath fill='url(%23c)' d='M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z'/%3E%3Cg opacity='.62' style='mix-blend-mode:plus-lighter'%3E%3Cpath fill='url(%23d)' d='M7.81 31.795 2.675 15.236c-.13-.419.461-.662.663-.272l7.15 13.803a.36.36 0 0 1-.029.378l-2.015 2.756a.36.36 0 0 1-.634-.106Z'/%3E%3Cpath fill='url(%23e)' d='M12.012 18.566c6.3-6.177-5.09-8.144-10.005-8.323a.339.339 0 0 0-.354.312c-.44 5.405 4.044 14.203 10.359 8.011Z'/%3E%3C/g%3E%3Cg opacity='.83'%3E%3Cpath fill='url(%23f)' d='M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z'/%3E%3Cpath fill='url(%23g)' d='M14.938 10.032c.396-8.814-9.277-2.487-12.994.733a.339.339 0 0 0-.046.47c3.363 4.253 12.643 7.631 13.04-1.203Z' opacity='.65' style='mix-blend-mode:plus-lighter'/%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='b' x1='4.306' x2='8.305' y1='10.845' y2='32.426' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1F5'/%3E%3Cstop offset='1' stop-color='%23FBDFE2'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' x1='6.924' x2='9.012' y1='9.263' y2='20.529' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF2F6'/%3E%3Cstop offset='1' stop-color='%23FADBDE'/%3E%3C/linearGradient%3E%3ClinearGradient id='d' x1='4.306' x2='8.305' y1='10.845' y2='32.426' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1F5'/%3E%3Cstop offset='1' stop-color='%23FBDFE2'/%3E%3C/linearGradient%3E%3ClinearGradient id='e' x1='6.924' x2='9.012' y1='9.263' y2='20.529' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF2F6'/%3E%3Cstop offset='1' stop-color='%23FADBDE'/%3E%3C/linearGradient%3E%3ClinearGradient id='f' x1='4.873' x2='14.082' y1='6.695' y2='13.513' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF2F6'/%3E%3Cstop offset='1' stop-color='%23FADBDE'/%3E%3C/linearGradient%3E%3ClinearGradient id='g' x1='4.873' x2='14.082' y1='6.695' y2='13.513' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF2F6'/%3E%3Cstop offset='1' stop-color='%23FADBDE'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
event {
|
||||
color: white;
|
||||
flex-direction: row!important;
|
||||
padding: 25px 40px 25px 0;
|
||||
width: -webkit-fill-available!important;
|
||||
}
|
||||
|
||||
event author {
|
||||
position: relative;
|
||||
text-transform: uppercase;
|
||||
filter: drop-shadow(0 0 3px white);
|
||||
font-weight: 500;
|
||||
margin-inline: 100px 70px;
|
||||
letter-spacing: .3rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
event author::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: -100px;
|
||||
width: 80px;
|
||||
aspect-ratio: 31 / 32;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='31' height='32' fill='none'%3E%3Cpath fill='%23fff' d='M14.923 11.178a2 2 0 0 1 3.154 0l.514.66c.232.296.541.52.895.649l.786.286a2 2 0 0 1 .975 3l-.469.692a2 2 0 0 0-.341 1.052l-.029.836a2 2 0 0 1-2.551 1.853l-.804-.23a1.999 1.999 0 0 0-1.106 0l-.804.23a2 2 0 0 1-2.551-1.853l-.029-.837a2 2 0 0 0-.341-1.05l-.469-.694a2 2 0 0 1 .975-3l.786-.285c.354-.128.663-.353.895-.65l.514-.66Z'/%3E%3Cpath stroke='%23fff' stroke-dasharray='0.1 6' stroke-linecap='round' d='M14.15 1.27a15 15 0 1 0 13.684 24.103'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
event content {
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
event content::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -10px -25px;
|
||||
border: 2px solid white;
|
||||
border-radius: 15px;
|
||||
filter: drop-shadow(0 0 2px white) drop-shadow(0 0 3px white);
|
||||
}
|
||||
|
||||
event content::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 120px;
|
||||
top: 0;
|
||||
right: 50px;
|
||||
transform: translateY(-90%);
|
||||
aspect-ratio: 51 / 9;
|
||||
background-size: cover;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='51' height='9' fill='none'%3E%3Cpath fill='url(%23a)' d='M4.54 8.013C2.032 8.013 0 6.255 0 4.085S2.032.156 4.54.156c1.424 0 3.314 1.267 4.681 2.362a1.986 1.986 0 0 1 0 3.133C7.854 6.746 5.964 8.013 4.54 8.013Z'/%3E%3Cpath fill='url(%23b)' d='M24.54 8.013c-2.508 0-4.54-1.758-4.54-3.928S22.032.156 24.54.156c1.424 0 3.314 1.267 4.681 2.362a1.986 1.986 0 0 1 0 3.133c-1.367 1.095-3.258 2.362-4.681 2.362Z'/%3E%3Cpath fill='url(%23c)' d='M44.54 8.013c-2.508 0-4.54-1.758-4.54-3.928S42.032.156 44.54.156c1.424 0 3.314 1.267 4.681 2.362a1.986 1.986 0 0 1 0 3.133c-1.367 1.095-3.257 2.362-4.681 2.362Z'/%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' x2='11' y1='4.085' y2='4.085' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' x1='20' x2='31' y1='4.085' y2='4.085' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' x1='40' x2='51' y1='4.085' y2='4.085' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23FFF1FA'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E");
|
||||
}
|
||||