Compare commits

..

3 Commits

Author SHA1 Message Date
5c9c53b117 Исправлен чат 2025-08-17 09:10:24 +03:00
a9f9a242ec Фиксы 2025-08-15 13:23:12 +03:00
5de5982e9e rena chat gen 1 2025-08-13 21:58:06 +03:00
6 changed files with 958 additions and 87 deletions

View File

@ -0,0 +1,15 @@
<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>
</body>
</message>
</script>

View 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', '');
if (cleanedHTML.length >= 90) {
$messageElement.addClass('long');
}
$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);
}

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,24 @@
<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>
<heart />
<role />
</top>
<bottom>
<content>{message}</content>
<flag />
</body>
</bottom>
</message>
</script>
<script type="text/template" id="chatlist_event">
<event id="{messageId}">
<main>
<content>{content}</content>
</main>
</event>
</script>

View File

@ -137,23 +137,23 @@ async function commandUSERNOTICE(details) {
case 'communitypayforward':
case 'giftpaidupgrade':
case 'primepaidupgrade':
content = 'подписался'
content = `${author} оформил сабку`
type = 'sub';
break;
case 'subgift':
case 'anonsubgift':
if (subMysteries.includes(details.tags['msg-param-origin-id'])) return
content = `подарил подписку ${recipient}`;
content = `${author} подарил сабку ${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} сабок!`;
content = `${author} подарил ${mysteryGiftCount} сабок!`;
type = 'gift';
break;
case 'raid':
content = `притопал с ${viewerCount} зрителями!`;
content = `${author} привел на стрим ${viewerCount} котят!`;
type = 'raid';
break;
default:

View File

@ -1,12 +1,11 @@
/* 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');
@import url('https://fonts.googleapis.com/css2?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);
@ -18,9 +17,9 @@
/* 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);
--color-2: #AF99E6;
--color-3: linear-gradient(75deg, #FEEDFE, #FFFFFF);
--color-4: linear-gradient(75deg, #FEEDFE, #D3CBFE);
}
/* Main styling */
@ -45,11 +44,6 @@ content {
font-family: var(--font-family-1);
}
.font-2 {
opacity: 0;
font-family: var(--font-family-2);
}
.emote {
position: relative;
display: inline-block;
@ -112,6 +106,24 @@ content {
}
}
@keyframes beat {
0% {
transform: scale(1);
}
25% {
transform: scale(1.2);
}
50% {
transform: scale(0.9);
}
75% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
#chatbox {
position: absolute;
display: flex;
@ -127,6 +139,7 @@ content {
#chatbox message,
#chatbox event {
max-height: var(--max-height);
font-family: var(--font-family-1);
width: 100%;
display: flex;
flex-direction: column;
@ -149,106 +162,168 @@ content {
}
message {
padding: 40px 15px 15px;
padding-top: 75px;
}
message top {
position: relative;
display: inline-flex;
margin-bottom: 5px;
margin-left: 40px;
gap: 5px;
filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.4));
align-items: center;
gap: 25px;
margin-left: 30px;
margin-bottom: 15px;
}
message top::before {
position: absolute;
content: '';
left: 50%;
transform: translateX(-50%);
top: -25px;
height: 30px;
aspect-ratio: 296 / 18;
height: 80px;
top: 0;
left: -40px;
z-index: -1;
aspect-ratio: 192 / 191;
background-repeat: no-repeat;
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");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='191' fill='none'%3E%3Cpath fill='%23FFE9A9' d='M39.622 125.229c1.236-12.115 2.708-18.617 4.57-30.476l71.647-60.209 11.352-1.423 10.074 4.369s-9.021-1.763-13.305 1.215c-4.058 2.82-4.938 6.375-5.812 11.238-1.624 9.05.347 13.123 1.254 22.204 1.247 12.483 5.149 19.654 7.515 31.436 1.819 9.06-.84 23.297-.84 23.297s-13.318-1.75-20.427-6.133c-9.889-6.096-14.45-12.742-25.537-16.889-8.15-3.05-13.866-4.984-22.216-1.713-10.378 4.066-18.275 23.084-18.275 23.084Z'/%3E%3Cpath fill='%23fff' d='M87.3 76.625s9.204-6.323 13.767-11.706c3.441-4.058 6.823-11.46 6.823-11.46s7.595-.177 11.517 3.206c7.884 6.803 10.676 26.503 10.676 26.503s-13.741-6.985-22.172-8.296c-8.545-1.328-20.61 1.753-20.61 1.753Z'/%3E%3Cpath fill='%23FFF1C9' d='m71.487 85.795 1.39-13.028 28.685-24.937 6.689 5.337s-2.466 9.54-8.553 15.216c-6.088 5.676-11.396 8.007-16.36 14.395-1.665 2.142-.986 6.153-.986 6.153-4.025-2.032-6.438-2.696-10.865-3.136Z'/%3E%3Cpath fill='%23fff' d='M35.975 100.8c1.646-7.956 12.812-15.103 12.812-15.103-7.342-3.075-12.017-3.163-20.914-1.573 0 0 .553-18.798 8.702-26.094 10.366-9.282 28.563-5.174 28.563-5.174s.988-20.165 8.348-25.796c7.746-5.926 15.906-.944 21.114-3.34-2.123 8.882-2.033 13.644.02 21.412 0 0 7.339-9.516 13.837-12.358 6.629-2.9 10.601-3.02 17.574-1.088 4.762 1.318 11.238 5.81 11.238 5.81s-7.089-2.55-11.655-1.902c-3.74.53-5.889 1.45-8.955 3.656-5.535 3.982-8.542 15.228-8.542 15.228s-4.092 7.47-13.337 10.765c-4.7 1.675-10.062-1.404-10.062-1.404s-1.953 4.553-4.312 6.457c-2.36 1.905-7.222 2.853-7.222 2.853 1.785 4.847 1.913 7.989.266 12.868 0 0-8.697.25-13.438 2.526-4.73 2.27-6.983 4.502-10.453 8.436-7.736 8.774-9.94 28.249-9.94 28.249s-5.709-14.449-3.644-24.428Z'/%3E%3Cpath fill='%23FFDD8C' d='m80.453 46.392-2.4-.399s.713 11.226 3.666 17.712c2.938 6.454 8.062 12.666 8.062 12.666s.347-.302 1.099-.887c.751-.586 1.473-1.124 1.473-1.124s-4.211-5.046-7.246-11.637c-2.911-6.324-4.654-16.331-4.654-16.331ZM54.738 65.275l.617-2.229s12.63 4.193 18.247 8.543c5.591 4.33 10.765 9.997 10.765 9.997s-.707.882-1.18 1.5c-.474.62-.826 2.353-.826 2.353s-4.797-6.328-10.498-10.783c-5.47-4.273-17.125-9.38-17.125-9.38Z'/%3E%3Cpath fill='%23C3DB76' d='M68.986 120.514c6.036-6.871 10.209-9.895 19.254-11.248 20.217-3.024 42.398 30.699 42.398 30.699s-14.937-12.458-30.137-14.18c-18.136-2.055-41.39 15.409-41.304 15.901 0 0 4.065-14.656 9.789-21.172Z'/%3E%3Cpath fill='%239ABD2E' d='M63.742 114.376c6.022-6.054 18.181-3.416 18.181-3.416s-9.154 5.002-13.357 10.141c-5.519 6.75-9.36 20.58-9.36 20.58s-2.311-20.422 4.535-27.305Z'/%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;
color: var(--color-2);
background-image: var(--color-3);
padding: 10px 20px;
border-radius: 35px;
font-weight: 600;
filter: drop-shadow(0 0 3px rgba(0, 0, 0, .2));
}
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 top heart {
filter: drop-shadow(0 0 15px var(--color-1));
width: 25px;
animation: beat 1s infinite;
aspect-ratio: 35 / 32;
background-repeat: no-repeat;
background-size: contain;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='35' height='32' fill='none'%3E%3Cpath fill='%23856EC7' fill-rule='evenodd' d='M34.745 8.47C33.748 3.355 30.185.06 25.595.06c-2.568 0-5.347 1.078-8.064 3.11C14.78 1.078 12.003 0 9.405 0 4.816 0 1.219 3.326.254 8.5c-1.45 7.669 3.201 17.863 16.822 23.406a1.2 1.2 0 0 0 .454.094c.15 0 .301-.032.453-.094C31.574 26.24 36.195 16.077 34.745 8.47Z' clip-rule='evenodd'/%3E%3C/svg%3E");
}
message top role {
opacity: 0;
border: 1px solid var(--color-1);
padding: 10px 20px;
border-radius: 25px;
box-sizing: border-box;
width: 55px;
height: 40px;
background-repeat: no-repeat;
background-position: center;
background-size: 45%;
filter: drop-shadow(0 0 5px var(--color-1));
}
message.moderator top role,
message.vip top role
{
opacity: 1;
}
message.vip top role {
background-image: url("data:image/svg+xml,%3Csvg width='40' height='33' viewBox='0 0 40 33' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.97743 0.270996H32.0252C32.2914 0.270959 32.5539 0.334605 32.7911 0.456732C33.0283 0.578859 33.2335 0.756 33.3901 0.973677L39.8398 9.93798C39.9545 10.0975 40.0109 10.2922 39.9995 10.4891C39.9882 10.6859 39.9097 10.8727 39.7774 11.0176L20.6205 31.9974C20.5415 32.0837 20.4457 32.1525 20.3392 32.1996C20.2326 32.2467 20.1176 32.271 20.0013 32.271C19.885 32.271 19.77 32.2467 19.6635 32.1996C19.5569 32.1525 19.4611 32.0837 19.3822 31.9974L0.225246 11.0193C0.0925583 10.8743 0.0137951 10.6872 0.00241011 10.4899C-0.00897489 10.2927 0.0477246 10.0976 0.162824 9.93798L6.61257 0.973677C6.7691 0.756 6.97436 0.578859 7.21157 0.456732C7.44879 0.334605 7.71122 0.270959 7.97743 0.270996Z' fill='white'/%3E%3C/svg%3E%0A");
}
message.moderator top role {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='42' height='42' fill='none'%3E%3Cpath fill='%23fff' d='M11.577 20.824A2.223 2.223 0 0 0 8.53 24.06l.854.801-7.736 8.23a2.275 2.275 0 0 0 .1 3.215l4.35 4.09a2.276 2.276 0 0 0 3.216-.1l7.733-8.226.852.8a2.222 2.222 0 1 0 3.044-3.24l-9.367-8.807ZM25.098.463 13.903 18.434l9.287 8.732 17.248-12.282-.46-14.88-14.88.459Z'/%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");
padding-left: 40px;
}
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;
width: 30px;
left: 0;
bottom: -60px;
aspect-ratio: 40 / 73;
filter: drop-shadow(0 0 3px var(--color-1));
background-repeat: no-repeat;
background-size: cover;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='73' fill='none'%3E%3Cpath fill='%23fff' d='M37.56 31.713c-.16-.472.172-1.07.861-1.69a.724.724 0 0 0 .094-.964 7.8 7.8 0 0 0-9.814-2.392.723.723 0 0 1-1.035-.772 7.8 7.8 0 0 0-5.55-8.867.727.727 0 0 0-.875.44c-.377.968-.866 1.552-1.402 1.552-.524 0-1.004-.56-1.378-1.491a.722.722 0 0 0-.892-.414 7.801 7.801 0 0 0-5.262 8.775.723.723 0 0 1-1.035.773A7.8 7.8 0 0 0 1.038 29.72a.727.727 0 0 0 .209.956c.865.619 1.321 1.251 1.184 1.777-.134.508-.802.832-1.804.956a.722.722 0 0 0-.625.77 7.8 7.8 0 0 0 6.715 7.183c.578.08.828.774.434 1.205a7.8 7.8 0 0 0 .14 10.69.728.728 0 0 0 .834.153c.55-.263 1.016-.313 1.334-.1.307.204.441.627.423 1.203a.717.717 0 0 0 .48.7 7.8 7.8 0 0 0 9.016-3.035l.007-.01a.727.727 0 0 1 1.213 0l.007.01a7.8 7.8 0 0 0 9.943 2.645.726.726 0 0 0 .389-.772c-.117-.696-.026-1.22.307-1.476.306-.235.78-.215 1.353.017a.718.718 0 0 0 .852-.249 7.8 7.8 0 0 0-.617-9.776.723.723 0 0 1 .434-1.205 7.8 7.8 0 0 0 6.723-8.14.729.729 0 0 0-.67-.691c-.95-.067-1.596-.339-1.76-.819Zm-17.564 5.55-.005.023-.004-.024.028-.031-.019.031Z'/%3E%3Cpath fill='%23FFE8A9' d='M27.599 34.944a2.54 2.54 0 0 0-3.11-1.797l-1.967.527v-2.037a2.54 2.54 0 0 0-5.079 0v2.052l-1.985-.521a2.54 2.54 0 0 0-1.29 4.912l1.68.441-1.144 1.667a2.54 2.54 0 1 0 4.189 2.872l1.042-1.52 1.011 1.542a2.537 2.537 0 0 0 3.516.73 2.54 2.54 0 0 0 .731-3.515l-1.16-1.77 1.769-.473a2.54 2.54 0 0 0 1.797-3.11Z'/%3E%3Ccircle cx='20' cy='68.637' r='4' fill='%23fff'/%3E%3Ccircle cx='20' cy='4' r='4' fill='%23fff'/%3E%3C/svg%3E");
}
message flag {
message bottom content {
color: var(--color-1);
}
event {
max-width: fit-content;
padding-top: 75px;
}
event main {
position: relative;
}
event main::before {
position: absolute;
content: '';
left: 0;
top: -45px;
width: 100px;
aspect-ratio: 149 / 229;
background-repeat: no-repeat;
background-size: cover;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='149' height='229' fill='none'%3E%3Cpath stroke='%23FCBF58' stroke-linecap='round' stroke-width='6' d='M84.022 119.783S19.393 208.728 5.04 172.245c-13.218-33.598 78.987-52.462 78.987-52.462M81.158 130.731s-15.163 26.145-20.87 46.496c-4.98 17.758-9.15 45.763-9.15 45.763'/%3E%3Cpath stroke='%23FCBF58' stroke-linecap='round' stroke-width='6' d='M84.783 118.394s-9.321 29.702-12.322 53.243c-2.619 20.542-5.89 54.159-5.89 54.159'/%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-width='4' d='M146.718 56.418s-15.307 13.592-28.454 19.09c-11.472 4.8-28.31 11.993-28.31 11.993'/%3E%3Cpath fill='%23fff' d='M138.252 4.918s4.478 26.592 1.276 43.935c-3.202 17.344-15.394 21.552-25.381 35.393 0 0-6.983-7.52-9.358-13.515-3.254-8.214-4.642-11.614-2.381-20.143 4.98-18.783 35.844-45.67 35.844-45.67Z'/%3E%3Cpath fill='%23fff' d='M61.191 68.138s17.822-4.301 32.316-4.866c5.472-.213 9.072 2.652 13.662 6.374 3.902 3.163 8.495 8.678 8.495 8.678s-9.934 8.055-16.628 8.617c-5.5.461-8.797-.924-13.766-3.202-10.177-4.666-24.079-15.6-24.079-15.6Z'/%3E%3C/svg%3E");
}
event main::after {
position: absolute;
content: '';
top: -15px;
right: -15px;
width: 125px;
aspect-ratio: 1;
z-index: -1;
filter: drop-shadow(0 0 3px var(--color-1));
background-repeat: no-repeat;
background-size: cover;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='178' height='178' fill='none'%3E%3Ccircle cx='88.957' cy='89' r='86' stroke='%23fff' stroke-dasharray='0.1 20 0.1 20' stroke-linecap='round' stroke-width='5'/%3E%3C/svg%3E");
}
event main content {
position: relative;
color: var(--color-2);
background-image: var(--color-4);
padding: 35px 50px;
margin-left: 40px;
border-radius: 50px;
font-weight: 600;
}
event main content::before {
position: absolute;
content: '';
top: 0;
right: 25px;
left: 50%;
width: 40px;
aspect-ratio: 26 / 43;
aspect-ratio: 50 / 62;
z-index: 100;
transform: translate(-50%, -50%);
filter: drop-shadow(0 0 3px var(--color-1));
background-repeat: no-repeat;
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");
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='50' height='62' fill='none'%3E%3Cpath fill='%23fff' d='m25.453 0 1.154 9.172c1.37 10.903 9.793 19.609 20.698 21.394l2.648.434-2.648.434C36.4 33.219 27.977 41.925 26.607 52.828L25.453 62 24.3 52.828C22.929 41.925 14.506 33.219 3.6 31.434L.953 31l2.648-.434C14.506 28.781 22.93 20.075 24.3 9.172L25.453 0Z'/%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");
event main content::after {
position: absolute;
content: '';
width: 30px;
left: -30px;
bottom: -60px;
aspect-ratio: 40 / 73;
filter: drop-shadow(0 0 3px var(--color-1));
background-repeat: no-repeat;
background-size: cover;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='73' fill='none'%3E%3Cpath fill='%23fff' d='M37.56 31.713c-.16-.472.172-1.07.861-1.69a.724.724 0 0 0 .094-.964 7.8 7.8 0 0 0-9.814-2.392.723.723 0 0 1-1.035-.772 7.8 7.8 0 0 0-5.55-8.867.727.727 0 0 0-.875.44c-.377.968-.866 1.552-1.402 1.552-.524 0-1.004-.56-1.378-1.491a.722.722 0 0 0-.892-.414 7.801 7.801 0 0 0-5.262 8.775.723.723 0 0 1-1.035.773A7.8 7.8 0 0 0 1.038 29.72a.727.727 0 0 0 .209.956c.865.619 1.321 1.251 1.184 1.777-.134.508-.802.832-1.804.956a.722.722 0 0 0-.625.77 7.8 7.8 0 0 0 6.715 7.183c.578.08.828.774.434 1.205a7.8 7.8 0 0 0 .14 10.69.728.728 0 0 0 .834.153c.55-.263 1.016-.313 1.334-.1.307.204.441.627.423 1.203a.717.717 0 0 0 .48.7 7.8 7.8 0 0 0 9.016-3.035l.007-.01a.727.727 0 0 1 1.213 0l.007.01a7.8 7.8 0 0 0 9.943 2.645.726.726 0 0 0 .389-.772c-.117-.696-.026-1.22.307-1.476.306-.235.78-.215 1.353.017a.718.718 0 0 0 .852-.249 7.8 7.8 0 0 0-.617-9.776.723.723 0 0 1 .434-1.205 7.8 7.8 0 0 0 6.723-8.14.729.729 0 0 0-.67-.691c-.95-.067-1.596-.339-1.76-.819Zm-17.564 5.55-.005.023-.004-.024.028-.031-.019.031Z'/%3E%3Cpath fill='%23FFE8A9' d='M27.599 34.944a2.54 2.54 0 0 0-3.11-1.797l-1.967.527v-2.037a2.54 2.54 0 0 0-5.079 0v2.052l-1.985-.521a2.54 2.54 0 0 0-1.29 4.912l1.68.441-1.144 1.667a2.54 2.54 0 1 0 4.189 2.872l1.042-1.52 1.011 1.542a2.537 2.537 0 0 0 3.516.73 2.54 2.54 0 0 0 .731-3.515l-1.16-1.77 1.769-.473a2.54 2.54 0 0 0 1.797-3.11Z'/%3E%3Ccircle cx='20' cy='68.637' r='4' fill='%23fff'/%3E%3Ccircle cx='20' cy='4' r='4' fill='%23fff'/%3E%3C/svg%3E");
}