Merge pull request #8 from nuttylmao/beta

Moved early access widgets to stable
This commit is contained in:
nuttylmao
2025-09-01 04:16:21 +10:00
committed by GitHub
49 changed files with 3883 additions and 355 deletions
+17 -4
View File
@@ -227,10 +227,23 @@ function LoadJSON(settingsJson) {
function UpdateSettingItemVisibility() {
data.settings.forEach(setting => {
if (setting.showIf) {
if (!document.getElementById(setting.showIf).checked)
document.getElementById(`item-${setting.id}`).style.display = 'none'
else
document.getElementById(`item-${setting.id}`).style.display = 'flex'
const parentElement = document.getElementById(setting.showIf);
let shouldShow = true;
// Walk up the chain of showIf dependencies
let currentSetting = setting;
while (currentSetting.showIf) {
const parentInput = document.getElementById(currentSetting.showIf);
if (!parentInput || !parentInput.checked) {
shouldShow = false;
break;
}
// Find the parent setting object (to keep walking up)
currentSetting = data.settings.find(s => s.id === currentSetting.showIf) || {};
}
document.getElementById(`item-${setting.id}`).style.display = shouldShow ? 'flex' : 'none';
}
});
}
@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_209_29907)"><path d="M17.56 0H14.4533C13.717 0 13.12 0.597452 13.12 1.33445V4.4437C13.12 5.1807 13.717 5.77815 14.4533 5.77815H17.56C18.2964 5.77815 18.8933 5.1807 18.8933 4.4437V1.33445C18.8933 0.597452 18.2964 0 17.56 0Z" fill="url(#paint0_linear_209_29907)"></path><path d="M17.3333 5.77815H14.6667V8.44704H17.3333V5.77815Z" fill="url(#paint1_linear_209_29907)"></path><path d="M5.33333 14.8257C5.33333 14.8257 0 14.8257 0 20.1635C0 25.5013 5.33333 25.5013 5.33333 25.5013V14.8257Z" fill="url(#paint2_linear_209_29907)"></path><path d="M26.6667 14.8257C26.6667 14.8257 32 14.8257 32 20.1635C32 25.5013 26.6667 25.5013 26.6667 25.5013V14.8257Z" fill="url(#paint3_linear_209_29907)"></path><path d="M26.6667 10.8224H5.33333V28.1701H26.6667V10.8224Z" fill="#4FD8FF"></path><path d="M15.76 11.2761C20.4133 11.2761 24.0933 12.3036 25.24 13.2911C26.3867 14.8657 26.1733 24.4737 24.9067 26.0751C24.2533 26.849 21.0667 28.01 16.28 28.01C11.24 28.01 7.73333 26.7556 6.94667 25.9283C5.70667 24.367 5.65333 14.7189 6.84 13.211C7.58667 12.4637 10.6667 11.2761 15.76 11.2761ZM15.76 7.27273C10.9067 7.27273 6.12 8.28691 4.01333 10.382C1.25333 13.1309 1.34667 25.7948 4.01333 28.6372C6.08 30.8524 11.2133 32 16.28 32C21.3467 32 26.08 30.9058 27.9867 28.6372C30.48 25.648 30.8667 12.9975 27.9867 10.382C25.7467 8.34028 20.72 7.27273 15.76 7.27273Z" fill="url(#paint4_linear_209_29907)"></path><path d="M23 17.975C23 16.6852 21.9553 15.6397 20.6667 15.6397C19.378 15.6397 18.3333 16.6852 18.3333 17.975V21.3111C18.3333 22.6008 19.378 23.6464 20.6667 23.6464C21.9553 23.6464 23 22.6008 23 21.3111V17.975Z" fill="black"></path><path d="M13.6667 17.975C13.6667 16.6852 12.622 15.6397 11.3333 15.6397C10.0447 15.6397 9 16.6852 9 17.975V21.3111C9 22.6008 10.0447 23.6464 11.3333 23.6464C12.622 23.6464 13.6667 22.6008 13.6667 21.3111V17.975Z" fill="black"></path></g><defs><linearGradient id="paint0_linear_209_29907" x1="15.963" y1="0.886836" x2="15.963" y2="43.3072" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint1_linear_209_29907" x1="16" y1="-69.28" x2="16" y2="-69.28" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint2_linear_209_29907" x1="17.28" y1="-0.2" x2="16.8598" y2="31.7766" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint3_linear_209_29907" x1="14.72" y1="-0.2" x2="15.1402" y2="31.7766" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint4_linear_209_29907" x1="5.14015" y1="0.587156" x2="36.6592" y2="34.8544" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><clipPath id="clip0_209_29907"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><path d="M15.6773 22.1533C17.3698 22.1533 18.8182 21.5507 20.0233 20.3461C21.2282 19.1415 21.8307 17.6924 21.8307 16V6.15401C21.8307 4.46162 21.2286 3.01305 20.0233 1.80784C18.8182 0.602907 17.3698 0 15.6773 0C13.9849 0 12.5363 0.602907 11.3311 1.80784C10.1259 3.01285 9.52344 4.46162 9.52344 6.15401V16C9.52344 17.6923 10.1262 19.1415 11.3311 20.3461C12.5361 21.5507 13.9849 22.1533 15.6773 22.1533Z" fill="url(#paint0_linear_209_29909)"></path><path d="M15.6773 22.1533C17.3698 22.1533 18.8182 21.5507 20.0233 20.3461C21.2282 19.1415 21.8307 17.6924 21.8307 16V6.15401C21.8307 4.46162 21.2286 3.01305 20.0233 1.80784C18.8182 0.602907 17.3698 0 15.6773 0C13.9849 0 12.5363 0.602907 11.3311 1.80784C10.1259 3.01285 9.52344 4.46162 9.52344 6.15401V16C9.52344 17.6923 10.1262 19.1415 11.3311 20.3461C12.5361 21.5507 13.9849 22.1533 15.6773 22.1533Z" fill="white" fill-opacity="0.3"></path><path d="M26.3888 12.6731C26.1459 12.4295 25.8568 12.3076 25.5234 12.3076C25.1904 12.3076 24.902 12.4295 24.6581 12.6731C24.4147 12.9167 24.293 13.2051 24.293 13.5383V16C24.293 18.3718 23.4498 20.4006 21.7639 22.0864C20.0785 23.7723 18.0495 24.6153 15.6775 24.6153C13.3057 24.6153 11.2769 23.7723 9.59089 22.0864C7.90509 20.401 7.06226 18.3719 7.06226 16V13.5383C7.06226 13.2051 6.94041 12.9167 6.69692 12.6731C6.45329 12.4295 6.16514 12.3076 5.83159 12.3076C5.49804 12.3076 5.20956 12.4295 4.96606 12.6731C4.72237 12.9167 4.60059 13.2051 4.60059 13.5383V16C4.60059 18.8333 5.54627 21.2981 7.4371 23.3941C9.32799 25.4901 11.6645 26.6919 14.4467 26.9994V29.5381H9.52373C9.19038 29.5381 8.90196 29.6601 8.6584 29.9037C8.41477 30.1472 8.29293 30.4357 8.29293 30.7691C8.29293 31.1019 8.41477 31.391 8.6584 31.6344C8.90196 31.8778 9.19038 32 9.52373 32H21.831C22.1643 32 22.4531 31.8779 22.6963 31.6344C22.9402 31.391 23.0622 31.1019 23.0622 30.7691C23.0622 30.4358 22.9402 30.1472 22.6963 29.9037C22.4532 29.6601 22.1644 29.5381 21.831 29.5381H16.9086V26.9994C19.6904 26.6919 22.0267 25.4901 23.9178 23.3941C25.8089 21.2981 26.7548 18.8333 26.7548 16V13.5383C26.7548 13.2051 26.6327 12.9169 26.3888 12.6731Z" fill="url(#paint1_linear_209_29909)"></path><defs><linearGradient id="paint0_linear_209_29909" x1="5.22969e-08" y1="-5.22969e-08" x2="32" y2="32" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="paint1_linear_209_29909" x1="-5.88081e-07" y1="-8.98822e-07" x2="4.72839" y2="35.6202" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_209_29912)"><path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="url(#paint0_linear_209_29912)"></path><path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="url(#paint1_linear_209_29912)"></path><path d="M16 29.0375C23.2004 29.0375 29.0375 23.2004 29.0375 16C29.0375 8.79958 23.2004 2.96249 16 2.96249C8.79959 2.96249 2.9625 8.79958 2.9625 16C2.9625 23.2004 8.79959 29.0375 16 29.0375Z" fill="#FEB635"></path><path d="M16 29.0375C23.2004 29.0375 29.0375 23.2004 29.0375 16C29.0375 8.79958 23.2004 2.96249 16 2.96249C8.79959 2.96249 2.9625 8.79958 2.9625 16C2.9625 23.2004 8.79959 29.0375 16 29.0375Z" fill="url(#paint2_linear_209_29912)"></path><path d="M29.0375 16C29.0375 23.1875 23.1875 29.0375 16 29.0375C13.6563 29.0375 11.4625 28.4187 9.5625 27.3312C11.3125 28.2062 13.2875 28.7 15.375 28.7C22.5625 28.7 28.4125 22.85 28.4125 15.6625C28.4125 10.8188 25.75 6.58125 21.8125 4.3375C26.0938 6.475 29.0375 10.8938 29.0375 16ZM16.8875 3.575C19.4563 3.575 21.85 4.325 23.8625 5.60625C21.675 3.95625 18.95 2.96875 16 2.96875C8.8125 2.96875 2.9625 8.8125 2.9625 16.0063C2.9625 20.6437 5.4 24.7313 9.0625 27.0312C5.9 24.65 3.85 20.8687 3.85 16.6125C3.85 9.425 9.7 3.575 16.8875 3.575Z" fill="black" fill-opacity="0.05"></path><path d="M18.5966 9.45456V24H14.6477V13.0909H14.5625L11.3807 14.9943V11.6421L14.9602 9.45456H18.5966Z" fill="black" fill-opacity="0.8"></path><path d="M18.5966 9.45456V24H14.6477V13.0909H14.5625L11.3807 14.9943V11.6421L14.9602 9.45456H18.5966Z" fill="url(#paint3_linear_209_29912)" fill-opacity="0.5"></path><path d="M18.5966 9.45456V24H14.6477V13.0909H14.5625L11.3807 14.9943V11.6421L14.9602 9.45456H18.5966Z" stroke="black" stroke-opacity="0.1" stroke-width="0.350269"></path></g><defs><linearGradient id="paint0_linear_209_29912" x1="15.7467" y1="-4.46667" x2="16.2533" y2="36.6933" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><linearGradient id="paint1_linear_209_29912" x1="16" y1="0" x2="16" y2="32" gradientUnits="userSpaceOnUse"><stop stop-color="white" stop-opacity="0.3"></stop><stop offset="1" stop-color="white" stop-opacity="0.15"></stop></linearGradient><linearGradient id="paint2_linear_209_29912" x1="15.7936" y1="-0.677142" x2="16.2064" y2="32.8618" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><linearGradient id="paint3_linear_209_29912" x1="18.5966" y1="16.7273" x2="11.3807" y2="16.7273" gradientUnits="userSpaceOnUse"><stop stop-color="white" stop-opacity="0.1"></stop><stop offset="0.3" stop-color="white" stop-opacity="0.2"></stop><stop offset="0.65" stop-color="white" stop-opacity="0.05"></stop><stop offset="1" stop-color="white" stop-opacity="0.2"></stop></linearGradient><clipPath id="clip0_209_29912"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_817_50667)"><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 0 2 0H30ZM16.2197 2.99316C15.8292 2.60266 15.1962 2.60265 14.8057 2.99316L8.36328 9.43555C7.97294 9.82608 7.97284 10.4591 8.36328 10.8496L10.0918 12.5781C10.4823 12.9686 11.1153 12.9685 11.5059 12.5781L11.585 12.499L13.9414 14.8564L3.57129 25.2275C2.70357 26.0954 2.7035 27.5023 3.57129 28.3701C4.43911 29.2376 5.84612 29.2377 6.71387 28.3701L17.084 17.999L19.4414 20.3564L19.3633 20.4346C18.9728 20.8251 18.9728 21.4581 19.3633 21.8486L21.0918 23.5771C21.4823 23.9676 22.1154 23.9676 22.5059 23.5771L28.9482 17.1348C29.3386 16.7443 29.3386 16.1112 28.9482 15.7207L27.2197 13.9922C26.8293 13.6017 26.1962 13.6018 25.8057 13.9922L25.7266 14.0703L23.3701 11.7139C24.2377 10.8461 24.2376 9.4391 23.3701 8.57129C22.5023 7.7035 21.0954 7.70357 20.2275 8.57129L17.8701 6.21387L17.9482 6.13574C18.3388 5.74522 18.3388 5.11221 17.9482 4.72168L16.2197 2.99316Z" fill="url(#paint0_linear_817_50667)"></path><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 0 2 0H30ZM16.2197 2.99316C15.8292 2.60266 15.1962 2.60265 14.8057 2.99316L8.36328 9.43555C7.97294 9.82608 7.97284 10.4591 8.36328 10.8496L10.0918 12.5781C10.4823 12.9686 11.1153 12.9685 11.5059 12.5781L11.585 12.499L13.9414 14.8564L3.57129 25.2275C2.70357 26.0954 2.7035 27.5023 3.57129 28.3701C4.43911 29.2376 5.84612 29.2377 6.71387 28.3701L17.084 17.999L19.4414 20.3564L19.3633 20.4346C18.9728 20.8251 18.9728 21.4581 19.3633 21.8486L21.0918 23.5771C21.4823 23.9676 22.1154 23.9676 22.5059 23.5771L28.9482 17.1348C29.3386 16.7443 29.3386 16.1112 28.9482 15.7207L27.2197 13.9922C26.8293 13.6017 26.1962 13.6018 25.8057 13.9922L25.7266 14.0703L23.3701 11.7139C24.2377 10.8461 24.2376 9.4391 23.3701 8.57129C22.5023 7.7035 21.0954 7.70357 20.2275 8.57129L17.8701 6.21387L17.9482 6.13574C18.3388 5.74522 18.3388 5.11221 17.9482 4.72168L16.2197 2.99316Z" fill="url(#paint1_linear_817_50667)"></path><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 0 2 0H30ZM16.2197 2.99316C15.8292 2.60266 15.1962 2.60265 14.8057 2.99316L8.36328 9.43555C7.97294 9.82608 7.97284 10.4591 8.36328 10.8496L10.0918 12.5781C10.4823 12.9686 11.1153 12.9685 11.5059 12.5781L11.585 12.499L13.9414 14.8564L3.57129 25.2275C2.70357 26.0954 2.7035 27.5023 3.57129 28.3701C4.43911 29.2376 5.84612 29.2377 6.71387 28.3701L17.084 17.999L19.4414 20.3564L19.3633 20.4346C18.9728 20.8251 18.9728 21.4581 19.3633 21.8486L21.0918 23.5771C21.4823 23.9676 22.1154 23.9676 22.5059 23.5771L28.9482 17.1348C29.3386 16.7443 29.3386 16.1112 28.9482 15.7207L27.2197 13.9922C26.8293 13.6017 26.1962 13.6018 25.8057 13.9922L25.7266 14.0703L23.3701 11.7139C24.2377 10.8461 24.2376 9.4391 23.3701 8.57129C22.5023 7.7035 21.0954 7.70357 20.2275 8.57129L17.8701 6.21387L17.9482 6.13574C18.3388 5.74522 18.3388 5.11221 17.9482 4.72168L16.2197 2.99316Z" fill="url(#paint2_linear_817_50667)"></path></g><defs><linearGradient id="paint0_linear_817_50667" x1="18.8102" y1="-12.7222" x2="2.88536" y2="39.1063" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6A4A"></stop><stop offset="1" stop-color="#C70C00"></stop></linearGradient><linearGradient id="paint1_linear_817_50667" x1="15.7467" y1="-4.75575" x2="16.321" y2="39.0672" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><linearGradient id="paint2_linear_817_50667" x1="-14.9543" y1="46.9544" x2="32.0001" y2="-0.000509222" gradientUnits="userSpaceOnUse"><stop stop-color="#0095FF"></stop><stop offset="0.99" stop-color="#00C7FF"></stop></linearGradient><clipPath id="clip0_817_50667"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32">
<path fill="url(#SidekickBadgeA)" d="M0 5.5v11.3h2.3V20h2.3v3.2h2.2v3.2h6.9v-3.2h4.6v3.2H25v-3.2h2.3V20h2.3v-3.2H32V5.5h-9.2v3.2h-4.6V12h-4.5V8.7H9V5.5H0Zm13.7 13.7H7V16H4.6V9.6h2.3v3.2h4.5V16h2.3v3.2ZM27.4 16h-2.2v3.2h-6.9V16h2.3v-3.2h4.6V9.6h2.2V16Z"/>
<defs>
<linearGradient id="SidekickBadgeA" x1="18.8" x2="11.7" y1="-2.7" y2="32.7" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF6A4A"/>
<stop offset="1" stop-color="#C70C00"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 588 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.07324 1.33331H6.51991V4.29331H7.99991V2.81331H9.47991V1.33331H13.9266V5.77998H12.4466V7.25998H10.9599V8.73998H12.4466V10.22H13.9266V14.6666H9.47991V13.1866H7.99991V11.7066H6.51991V14.6666H2.07324V1.33331Z" fill="url(#badge-verified-gradient)"/>
<defs>
<linearGradient id="badge-verified-gradient" x1="33.791%" y1="97.416%" x2="65.541%" y2="4.5%" gradientUnits="objectBoundingBox">
<stop stop-color="#1EFF00"/>
<stop offset="0.99" stop-color="#00FF8C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 644 B

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_162_470)"><g clip-path="url(#clip1_162_470)"><path d="M22.34 9.5L26 4H18L16 7L14 4H6L9.66 9.5H4V15.1H28V9.5H22.34Z" fill="#53FC18"></path><path d="M26.0799 19.0996H5.8999V28.4996H26.0799V19.0996Z" fill="#53FC18"></path><path d="M26.0799 15.0996H5.8999V19.0996H26.0799V15.0996Z" fill="#32970E"></path></g></g><defs><clipPath id="clip0_162_470"><rect width="24" height="24.5" fill="white" transform="translate(4 4)"></rect></clipPath><clipPath id="clip1_162_470"><rect width="24" height="24.5" fill="white" transform="translate(4 4)"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 693 B

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_209_29946)"><path d="M17.0284 2.91378L16.2357 0.667951C16.1573 0.445558 15.8427 0.445558 15.7643 0.667951L14.9716 2.91378C12.9003 8.78263 8.78263 12.9003 2.91378 14.9716L0.667951 15.7643C0.445558 15.8427 0.445558 16.1573 0.667951 16.2357L2.91378 17.0284C8.78263 19.0998 12.9003 23.2174 14.9716 29.0862L15.7643 31.3321C15.8427 31.5544 16.1573 31.5544 16.2357 31.3321L17.0284 29.0862C19.0998 23.2174 23.2174 19.0998 29.0862 17.0284L31.3321 16.2357C31.5544 16.1573 31.5544 15.8427 31.3321 15.7643L29.0862 14.9716C23.2174 12.9003 19.0998 8.78263 17.0284 2.91378Z" fill="black"></path><path d="M17.0284 2.91378L16.2357 0.667951C16.1573 0.445558 15.8427 0.445558 15.7643 0.667951L14.9716 2.91378C12.9003 8.78263 8.78263 12.9003 2.91378 14.9716L0.667951 15.7643C0.445558 15.8427 0.445558 16.1573 0.667951 16.2357L2.91378 17.0284C8.78263 19.0998 12.9003 23.2174 14.9716 29.0862L15.7643 31.3321C15.8427 31.5544 16.1573 31.5544 16.2357 31.3321L17.0284 29.0862C19.0998 23.2174 23.2174 19.0998 29.0862 17.0284L31.3321 16.2357C31.5544 16.1573 31.5544 15.8427 31.3321 15.7643L29.0862 14.9716C23.2174 12.9003 19.0998 8.78263 17.0284 2.91378Z" fill="url(#paint0_radial_209_29946)"></path></g><defs><radialGradient id="paint0_radial_209_29946" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(16 16) rotate(90) scale(16)"><stop stop-color="#E1FF00"></stop><stop offset="1" stop-color="#2AA300"></stop></radialGradient><clipPath id="clip0_209_29946"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_614_6275)"><path d="M30.8598 19.2368C30.1977 18.2069 29.5356 17.2138 28.8736 16.1839C28.7264 15.9632 28.7264 15.8161 28.8736 15.5954C29.5356 14.6023 30.1609 13.6092 30.823 12.6161C31.5954 11.4391 31.1908 10.2989 29.8667 9.82069C28.7632 9.41609 27.6598 8.97471 26.5563 8.57012C26.3356 8.49656 26.2253 8.34943 26.2253 8.09196C26.1885 6.87816 26.1149 5.66437 26.0414 4.48736C25.9678 3.2 24.9747 2.46437 23.7241 2.7954C22.5471 3.08966 21.3701 3.42069 20.2299 3.75173C19.9724 3.82529 19.8253 3.75173 19.6414 3.56782C18.9057 2.61149 18.1333 1.69195 17.3977 0.772414C16.5885 -0.257472 15.3379 -0.257472 14.492 0.772414C13.7563 1.69195 12.9839 2.61149 12.2851 3.53103C12.1012 3.7885 11.9172 3.82529 11.623 3.75173C10.4828 3.42069 9.34253 3.12644 8.53334 2.90575C6.95173 2.53793 5.99541 3.16322 5.92184 4.48736C5.84828 5.70115 5.77472 6.91495 5.73794 8.16552C5.73794 8.42299 5.62759 8.53333 5.4069 8.64368C4.26667 9.08506 3.12644 9.52644 1.98621 9.96782C0.809203 10.446 0.441387 11.5862 1.14023 12.6529C1.8023 13.6828 2.46437 14.6759 3.12644 15.7057C3.27356 15.9264 3.27356 16.0736 3.12644 16.331C2.42759 17.3609 1.76552 18.3908 1.10345 19.4575C0.478165 20.4506 0.882759 21.6276 1.98621 22.069C3.12644 22.5104 4.30345 22.9517 5.44368 23.3931C5.70115 23.4667 5.77471 23.6138 5.77471 23.8713C5.81149 25.0483 5.95862 26.1885 5.95862 27.3655C5.95862 28.5425 6.9885 29.6092 8.42298 29.1678C9.56321 28.8 10.7034 28.5425 11.8437 28.2115C12.0644 28.1379 12.2115 28.1747 12.3586 28.3954C13.131 29.3517 13.8667 30.2713 14.6391 31.2276C15.485 32.2575 16.6988 32.2575 17.508 31.2276C18.2805 30.2713 19.0161 29.3517 19.7885 28.3954C19.9356 28.2115 20.046 28.1379 20.3034 28.2115C21.4804 28.5425 22.6575 28.8368 23.8345 29.1678C25.0483 29.4988 26.0781 28.7632 26.1149 27.5126C26.1885 26.2989 26.2621 25.0851 26.2988 23.8345C26.2988 23.5402 26.446 23.4299 26.6667 23.3563C27.7701 22.9517 28.9103 22.5104 30.0138 22.069C31.1908 21.4805 31.5586 20.3034 30.8598 19.2368ZM22.069 13.2046L14.7127 20.5609C14.5287 20.7448 14.2713 20.892 14.0138 20.9287C13.9402 20.9287 13.8299 20.9655 13.7563 20.9655C13.4253 20.9655 13.0575 20.8184 12.8 20.5609L9.78392 17.5448C9.26898 17.0299 9.26898 16.1839 9.78392 15.669C10.2989 15.154 11.1448 15.154 11.6598 15.669L13.7196 17.7287L20.1196 11.3287C20.6345 10.8138 21.4805 10.8138 21.9954 11.3287C22.5839 11.8437 22.5839 12.6897 22.069 13.2046Z" fill="url(#paint0_linear_614_6275)"></path></g><defs><linearGradient id="paint0_linear_614_6275" x1="8.14138" y1="32.3591" x2="24.4968" y2="0.904884" gradientUnits="userSpaceOnUse"><stop stop-color="#1EFF00"></stop><stop offset="0.99" stop-color="#00FF8C"></stop></linearGradient><clipPath id="clip0_614_6275"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_746_28171)"><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 4.10637e-08 2 0H30ZM15.9648 5C15.7748 5.00005 15.588 5.05204 15.4238 5.15039C15.2596 5.24878 15.124 5.39057 15.0303 5.56055L9.82812 15.0176L3.55078 11.8906C3.36913 11.7985 3.16534 11.7607 2.96387 11.7822C2.76241 11.8038 2.57048 11.8842 2.41113 12.0127C2.25235 12.1408 2.13185 12.3126 2.06348 12.5078C1.99511 12.7031 1.98143 12.9144 2.02441 13.1172L4.58301 25.127C4.63544 25.3782 4.77165 25.6034 4.96777 25.7627C5.16376 25.9217 5.40762 26.0056 5.65723 26H26.251C26.5009 26.0057 26.7453 25.9219 26.9414 25.7627C27.1376 25.6034 27.2737 25.3782 27.3262 25.127L29.9697 13.1172C30.0187 12.9103 30.0086 12.6932 29.9404 12.4922C29.8722 12.2912 29.7485 12.1151 29.585 11.9844C29.4215 11.8537 29.2249 11.7743 29.0186 11.7559C28.8122 11.7374 28.6049 11.7802 28.4219 11.8799L22.1025 15.0283L16.9004 5.56055C16.8066 5.39054 16.6701 5.24878 16.5059 5.15039C16.3416 5.05207 16.1549 5 15.9648 5Z" fill="url(#paint0_linear_746_28171)"></path><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 4.10637e-08 2 0H30ZM15.9648 5C15.7748 5.00005 15.588 5.05204 15.4238 5.15039C15.2596 5.24878 15.124 5.39057 15.0303 5.56055L9.82812 15.0176L3.55078 11.8906C3.36913 11.7985 3.16534 11.7607 2.96387 11.7822C2.76241 11.8038 2.57048 11.8842 2.41113 12.0127C2.25235 12.1408 2.13185 12.3126 2.06348 12.5078C1.99511 12.7031 1.98143 12.9144 2.02441 13.1172L4.58301 25.127C4.63544 25.3782 4.77165 25.6034 4.96777 25.7627C5.16376 25.9217 5.40762 26.0056 5.65723 26H26.251C26.5009 26.0057 26.7453 25.9219 26.9414 25.7627C27.1376 25.6034 27.2737 25.3782 27.3262 25.127L29.9697 13.1172C30.0187 12.9103 30.0086 12.6932 29.9404 12.4922C29.8722 12.2912 29.7485 12.1151 29.585 11.9844C29.4215 11.8537 29.2249 11.7743 29.0186 11.7559C28.8122 11.7374 28.6049 11.7802 28.4219 11.8799L22.1025 15.0283L16.9004 5.56055C16.8066 5.39054 16.6701 5.24878 16.5059 5.15039C16.3416 5.05207 16.1549 5 15.9648 5Z" fill="url(#paint1_linear_746_28171)"></path></g><defs><linearGradient id="paint0_linear_746_28171" x1="18.8102" y1="-12.7222" x2="2.88536" y2="39.1063" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6A4A"></stop><stop offset="1" stop-color="#C70C00"></stop></linearGradient><linearGradient id="paint1_linear_746_28171" x1="15.7467" y1="-4.75575" x2="16.321" y2="39.0672" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><clipPath id="clip0_746_28171"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+616 -14
View File
@@ -14,6 +14,15 @@ const animationDuration = 8000;
let widgetLocked = false; // Needed to lock animation from overlapping
let alertQueue = [];
/////////////////
// GLOBAL VARS //
/////////////////
const kickPusherWsUrl = 'wss://ws-us2.pusher.com/app/32cbd69e4b950bf97679?protocol=7&client=js&version=7.6.0&flash=false';
let kickSubBadges = [];
/////////////
// OPTIONS //
/////////////
@@ -37,16 +46,30 @@ const groupConsecutiveMessages = GetBooleanParam("groupConsecutiveMessages", tru
const showTwitchMessages = GetBooleanParam("showTwitchMessages", true);
const showTwitchAnnouncements = GetBooleanParam("showTwitchAnnouncements", true);
const showTwitchFollows = GetBooleanParam("showTwitchFollows", false);
const showTwitchSubs = GetBooleanParam("showTwitchSubs", true);
const showTwitchChannelPointRedemptions = GetBooleanParam("showTwitchChannelPointRedemptions", true);
const showTwitchRaids = GetBooleanParam("showTwitchRaids", true);
const showTwitchSharedChat = GetBooleanParam("showTwitchSharedChat", true);
let kickUsername = urlParams.get("kickUsername") || "";
const showKickMessages = GetBooleanParam("showKickMessages", true);
// const showKickFollows = GetBooleanParam("showKickFollows", false);
const showKickSubs = GetBooleanParam("showKickSubs", true);
const showKickChannelPointRedemptions = GetBooleanParam("showKickChannelPointRedemptions", true);
const showKickHosts = GetBooleanParam("showKickHosts", true);
const showYouTubeMessages = GetBooleanParam("showYouTubeMessages", true);
const showYouTubeSuperChats = GetBooleanParam("showYouTubeSuperChats", true);
const showYouTubeSuperStickers = GetBooleanParam("showYouTubeSuperStickers", true);
const showYouTubeMemberships = GetBooleanParam("showYouTubeMemberships", true);
const enableTikTokSupport = GetBooleanParam("enableTikTokSupport", false);
const showTikTokFollows = GetBooleanParam("showTikTokFollows", false);
const showTikTokMessages = GetBooleanParam("showTikTokMessages", false);
const showTikTokGifts = GetBooleanParam("showTikTokGifts", false);
const showTikTokSubs = GetBooleanParam("showTikTokSubs", false);
const showStreamlabsDonations = GetBooleanParam("showStreamlabsDonations", true)
const showStreamElementsTips = GetBooleanParam("showStreamElementsTips", true);
const showPatreonMemberships = GetBooleanParam("showPatreonMemberships", true);
@@ -58,6 +81,9 @@ const furryMode = GetBooleanParam("furryMode", false);
const animationSpeed = GetIntParam("animationSpeed", 0.5);
// Kick is stupid and turns underscores into dashes which fuck everything up, therefore do a find/replace to make it work good
kickUsername = kickUsername.replace(/_/g, "-");
// Set fonts for the widget
document.body.style.fontFamily = font;
document.body.style.fontSize = `${fontSize}px`;
@@ -113,6 +139,11 @@ client.on('Twitch.Announcement', (response) => {
TwitchAnnouncement(response.data);
})
client.on('Twitch.Follow', (response) => {
console.debug(response.data);
TwitchFollow(response.data);
})
client.on('Twitch.Sub', (response) => {
console.debug(response.data);
TwitchSub(response.data);
@@ -260,6 +291,153 @@ client.on('Fourthwall.GiftDrawEnded', (response) => {
///////////////////////////
// KICK PUSHER WEBSOCKET //
///////////////////////////
// Connect and handle Pusher WebSocket
async function KickConnect() {
if (!kickUsername)
return;
// Channel to subscribe to (you'll need the correct channel name here)
const chatroomId = await GetKickChatroomId(kickUsername);
// Cache subscriber badges
kickSubBadges = await GetKickSubBadges(kickUsername);
const websocket = new WebSocket(kickPusherWsUrl);
// Reconnect
websocket.onclose = function () {
console.log(`Reconnecting to ${kickUsername}...`);
setTimeout(connectPusher, 5000);
};
websocket.onopen = function () {
console.log(`Kick successfully conntected to ${kickUsername}.`);
}
websocket.onmessage = function (response) {
try {
let data = JSON.parse(response.data);
console.debug(data);
// When connection is established, subscribe to a channel
if (data.event === 'pusher:connection_established') {
const socketData = JSON.parse(data.data);
console.log(`[Pusher] Socket established with ID: ${socketData.socket_id}`);
// Now subscribe to a channel
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatroom_${chatroomId}` } }));
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}` } }));
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}.v2` } }));
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `predictions-channel-${chatroomId}` } }));
console.log(`[Pusher] Sent subscription request to channel: ${chatroomId}`);
}
// Event handlers
const eventArgs = JSON.parse(data.data);
const event = data.event.split('\\').pop();
switch (event) {
case 'ChatMessageEvent':
KickChatMessage(eventArgs);
break;
//// 'Follows' unsupported by pusher
// case 'FollowEvent':
// break;
case 'SubscriptionEvent':
KickSubscription(eventArgs);
break;
case 'GiftedSubscriptionsEvent':
KickGiftedSubscriptions(eventArgs);
break;
case 'RewardRedeemedEvent':
KickRewardRedeemed(eventArgs);
break;
case 'StreamHostEvent':
KickStreamHost(eventArgs);
break;
case 'MessageDeletedEvent':
KickMessageDeleted(eventArgs);
break;
case 'UserBannedEvent':
KickUserBanned(eventArgs);
break;
}
}
catch (error) {
console.error(error);
}
}
}
// Try connect when window is loaded
window.addEventListener('load', KickConnect);
//////////////////////
// TIKFINITY CLIENT //
//////////////////////
let tikfinityWebsocket = null;
function TikfinityConnect() {
if (!enableTikTokSupport)
return;
if (tikfinityWebsocket) return; // Already connected
tikfinityWebsocket = new WebSocket("ws://localhost:21213/");
tikfinityWebsocket.onopen = function () {
console.log(`TikFinity successfully connected...`)
}
tikfinityWebsocket.onclose = function () {
console.error(`TikFinity disconnected...`)
tikfinityWebsocket = null;
setTimeout(TikfinityConnect, 1000); // Schedule a reconnect attempt
}
tikfinityWebsocket.onerror = function () {
console.error(`TikFinity failed for some reason...`)
tikfinityWebsocket = null;
setTimeout(TikfinityConnect, 1000); // Schedule a reconnect attempt
}
tikfinityWebsocket.onmessage = function (response) {
let payload = JSON.parse(response.data);
let event = payload.event;
let data = payload.data;
console.debug('Event: ' + event);
switch (event) {
case 'chat':
TikTokChat(data);
break;
case 'follow':
TikTokFollow(data);
break;
case 'gift':
TikTokGift(data);
break;
case 'subscribe':
TikTokSubscribe(data);
break;
}
}
}
// Try connect when window is loaded
window.addEventListener('load', TikfinityConnect);
/////////////////////
// HORIZONTAL CHAT //
/////////////////////
@@ -393,7 +571,7 @@ async function TwitchChatMessage(data) {
// Render avatars
if (showAvatar) {
const username = data.message.username;
const avatarURL = await GetAvatar(username);
const avatarURL = await GetAvatar(username, 'twitch');
const avatar = new Image();
avatar.src = avatarURL;
avatar.classList.add("avatar");
@@ -461,6 +639,20 @@ async function TwitchAnnouncement(data) {
ShowAlert(message, background);
}
function TwitchFollow(data) {
if (!showTwitchFollows)
return;
// Set the text
let username = data.user_name;
if (data.user_name.toLowerCase() != data.user_login.toLowerCase())
username = `${data.user_name} (${data.user_login})`;
const message = `${username} followed`;
ShowAlert(message, 'twitch');
}
async function TwitchSub(data) {
if (!showTwitchSubs)
return;
@@ -601,7 +793,7 @@ function TwitchUserBanned(data) {
const messagesToRemove = [];
// ID of the message to remove
const userId = data.user_id;
const userId = data.targetUser.id;
// Find the items to remove
for (let i = 0; i < messageList.children.length; i++) {
@@ -975,7 +1167,7 @@ function FourthwallGiftPurchase(data) {
if (!showFourthwallAlerts)
return;
let user = data.username;
// let user = data.username;
const total = data.total;
const currency = data.currency;
const gifts = data.gifts.length;
@@ -984,7 +1176,8 @@ function FourthwallGiftPurchase(data) {
let message = "";
// If the user ordered more than one item, write how many items they ordered
message += `${user} gifted`;
// message += `${user} gifted`;
message += `Someone has gifted`;
// If there is more than one gifted item, display the number of gifts
if (gifts > 1)
@@ -1010,10 +1203,9 @@ function FourthwallGiftDrawStarted(data) {
const itemName = data.offer.name;
let message = "";
message += `${user} gifted`;
// If the user ordered more than one item, write how many items they ordered
message += `🎁 ${itemName} Giveaway! • Type !join in the next ${durationSeconds} seconds for your chance to win!`;
message += `🎁 ${itemName} Giveaway! • Type 'join' in the next ${durationSeconds} seconds for your chance to win!`;
ShowAlert(message, 'fourthwall');
}
@@ -1031,6 +1223,349 @@ function FourthwallGiftDrawEnded(data) {
ShowAlert(message, 'fourthwall');
}
async function KickChatMessage(data) {
if (!showKickMessages)
return;
// Don't post messages starting with "!"
if (data.content.startsWith("!") && excludeCommands)
return;
// Don't post messages from users from the ignore list
if (ignoreUserList.includes(data.sender.username.toLowerCase()))
return;
// Get a reference to the template
const template = document.getElementById('messageTemplate');
// Create a new instance of the template
const instance = template.content.cloneNode(true);
// Get divs
const userInfoDiv = instance.querySelector("#userInfo");
const avatarDiv = instance.querySelector("#avatar");
const timestampDiv = instance.querySelector("#timestamp");
const platformDiv = instance.querySelector("#platform");
const badgeListDiv = instance.querySelector("#badgeList");
const pronounsDiv = instance.querySelector("#pronouns");
const usernameDiv = instance.querySelector("#username");
const messageDiv = instance.querySelector("#message");
// Set timestamp
if (showTimestamps) {
timestampDiv.classList.add("timestamp");
timestampDiv.innerText = GetCurrentTimeFormatted();
}
// Set the username info
if (showUsername) {
usernameDiv.innerText = data.sender.username;
usernameDiv.style.color = data.sender.identity.color;
}
// Set the message data
let message = data.content;
// Set furry mode
if (furryMode)
message = TranslateToFurry(message);
// Set message text
if (showMessage) {
messageDiv.innerText = message;
}
// Render platform
if (showPlatform) {
const platformElements = `<img src="icons/platforms/kick.png" class="platform"/>`;
platformDiv.innerHTML = platformElements;
}
// Render badges
if (showBadges) {
badgeListDiv.innerHTML = "";
for (i in data.sender.identity.badges) {
const badge = new Image();
badge.src = GetKickBadgeURL(data.sender.identity.badges[i]);
badge.classList.add("badge");
badgeListDiv.appendChild(badge);
}
}
// Render emotes
function replaceEmotes(message) {
const emoteRegex = /\[emote:(\d+):([^\]]+)\]/g;
return message.replace(emoteRegex, (_, id, name) => {
const imgUrl = `https://files.kick.com/emotes/${id}/fullsize`;
return `<img src="${imgUrl}" alt="${name}" title="${name}" class="emote" />`;
});
}
messageDiv.innerHTML = replaceEmotes(message);
// Render avatars
if (showAvatar) {
const username = data.sender.slug;
const avatarURL = await GetAvatar(username, 'kick');
const avatar = new Image();
avatar.src = avatarURL;
avatar.classList.add("avatar");
avatarDiv.appendChild(avatar);
}
// Hide the header if the same username sends a message twice in a row
const messageList = document.getElementById("messageList");
if (groupConsecutiveMessages && messageList.children.length > 0) {
const lastPlatform = messageList.lastChild.dataset.platform;
const lastUserId = messageList.lastChild.dataset.userId;
if (lastPlatform == "kick" && lastUserId == data.sender.id)
userInfoDiv.style.display = "none";
}
AddMessageItem(instance, data.id, 'kick', data.sender.id);
}
function KickSubscription(data) {
if (!showKickSubs)
return;
// Set the text
const username = data.username;
const months = data.months;
let message = '';
if (months <= 1)
message = `${username} just subscribed for the first time!`;
else
message = `${username} resubscribed! (${months} months)`;
ShowAlert(message, 'kick');
}
function KickGiftedSubscriptions(data) {
if (!showKickSubs)
return;
// Set the text
const gifter = data.gifter_username;
const giftedUsers = data.gifted_usernames;
let message = '';
if (giftedUsers.length <= 1)
message = `${gifter} gifted a sub to ${giftedUsers[0]}`;
else
message = `${gifter} gifted ${giftedUsers.length} subscription${giftedUsers.length === 1 ? '' : 's'} to the community!`;
ShowAlert(message, 'kick');
}
function KickRewardRedeemed(data) {
if (!showKickChannelPointRedemptions)
return;
const username = data.username;
const rewardName = data.reward_title;
const message = `${username} redeemed ${rewardName}`;
ShowAlert(message, 'kick');
}
function KickStreamHost(data) {
if (!showKickHosts)
return;
const username = data.host_username;
const viewers = data.number_viewers;
const message = `${username} is raiding with a party of ${viewers}`;
ShowAlert(message, 'kick');
}
function KickMessageDeleted(data) {
const messageList = document.getElementById("messageList");
// Maintain a list of chat messages to delete
const messagesToRemove = [];
// ID of the message to remove
const messageId = data.message.id;
// Find the items to remove
for (let i = 0; i < messageList.children.length; i++) {
if (messageList.children[i].id === messageId) {
messagesToRemove.push(messageList.children[i]);
}
}
// Remove the items
messagesToRemove.forEach(item => {
messageList.removeChild(item);
});
}
function KickUserBanned(data) {
const messageList = document.getElementById("messageList");
// Maintain a list of chat messages to delete
const messagesToRemove = [];
// ID of the message to remove
const userId = data.user.id;
// Find the items to remove
for (let i = 0; i < messageList.children.length; i++) {
if (messageList.children[i].dataset.userId == userId) {
messagesToRemove.push(messageList.children[i]);
}
}
// Remove the items
messagesToRemove.forEach(item => {
messageList.removeChild(item);
});
}
async function TikTokChat(data) {
if (!showTikTokMessages)
return;
// Don't post messages starting with "!"
if (data.comment.startsWith("!") && excludeCommands)
return;
// Don't post messages from users from the ignore list
if (ignoreUserList.includes(data.comment.toLowerCase()))
return;
// Get a reference to the template
const template = document.getElementById('messageTemplate');
// Create a new instance of the template
const instance = template.content.cloneNode(true);
// Get divs
const userInfoDiv = instance.querySelector("#userInfo");
const avatarDiv = instance.querySelector("#avatar");
const timestampDiv = instance.querySelector("#timestamp");
const platformDiv = instance.querySelector("#platform");
const badgeListDiv = instance.querySelector("#badgeList");
const pronounsDiv = instance.querySelector("#pronouns");
const usernameDiv = instance.querySelector("#username");
const messageDiv = instance.querySelector("#message");
// Set timestamp
if (showTimestamps) {
timestampDiv.classList.add("timestamp");
timestampDiv.innerText = GetCurrentTimeFormatted();
}
// Set the username info
if (showUsername) {
usernameDiv.innerText = data.nickname;
usernameDiv.style.color = '#9e9e9e';
}
// Set the message data
let message = data.comment;
// Set furry mode
if (furryMode)
message = TranslateToFurry(message);
// Set message text
if (showMessage) {
messageDiv.innerText = message;
}
// Render platform
if (showPlatform) {
const platformElements = `<img src="icons/platforms/tiktok.png" class="platform"/>`;
platformDiv.innerHTML = platformElements;
}
// Render badges
if (showBadges) {
badgeListDiv.innerHTML = "";
if (data.isModerator) {
const badge = new Image();
badge.src = `icons/badges/youtube-moderator.svg`;
badge.style.filter = `invert(100%)`;
badge.style.opacity = 0.8;
badge.classList.add("badge");
badgeListDiv.appendChild(badge);
}
for (i in data.userBadges) {
if (data.userBadges[i].type == 'image') {
const badge = new Image();
badge.src = data.userBadges[i].url;
badge.classList.add("badge");
badgeListDiv.appendChild(badge);
}
}
}
// Render avatars
if (showAvatar) {
const avatar = new Image();
avatar.src = data.profilePictureUrl;
avatar.classList.add("avatar");
avatarDiv.appendChild(avatar);
}
// Hide the header if the same username sends a message twice in a row
const messageList = document.getElementById("messageList");
if (groupConsecutiveMessages && messageList.children.length > 0) {
const lastPlatform = messageList.lastChild.dataset.platform;
const lastUserId = messageList.lastChild.dataset.userId;
if (lastPlatform == "tiktok" && lastUserId == data.userId)
userInfoDiv.style.display = "none";
}
AddMessageItem(instance, data.msgId, 'tiktok', data.userId);
}
function TikTokFollow(data) {
if (!showTikTokFollows)
return;
const message = `${data.nickname} followed`;
ShowAlert(message, 'tiktok');
}
function TikTokGift(data) {
if (!showTikTokGifts)
return;
if (data.giftType === 1 && !data.repeatEnd) {
// Streak in progress => show only temporary
console.debug(`${data.uniqueId} is sending gift ${data.giftName} x${data.repeatCount}`);
return;
}
// Streak ended or non-streakable gift => process the gift with final repeat_count
console.debug(`${data.uniqueId} has sent gift ${data.giftName} x${data.repeatCount}`);
const giftImg = `<img src=${data.giftPictureUrl} class="platform"/>`;
const message = `${data.nickname} sent ${giftImg}x${data.repeatCount}`;
ShowAlert(message, 'tiktok');
}
function TikTokSubscribe(data) {
if (!showTikTokSubs)
return;
let username = data.nickname;
const message = `${username} subscribed on TikTok`;
ShowAlert(message, 'tiktok');
}
//////////////////////
@@ -1086,18 +1621,37 @@ function GetCurrentTimeFormatted() {
return formattedTime;
}
async function GetAvatar(username) {
if (avatarMap.has(username)) {
console.debug(`Avatar found for ${username}. Retrieving from hash map.`)
return avatarMap.get(username);
async function GetAvatar(username, platform) {
// First, check if the username is hashed already
if (avatarMap.has(`${username}-${platform}`)) {
console.debug(`Avatar found for ${username} (${platform}). Retrieving from hash map.`)
return avatarMap.get(`${username}-${platform}`);
}
else {
console.debug(`No avatar found for ${username}. Retrieving from Decapi.`)
// If code reaches this point, the username hasn't been hashed, so retrieve avatar
switch (platform) {
case 'twitch':
{
console.debug(`No avatar found for ${username} (${platform}). Retrieving from Decapi.`)
let response = await fetch('https://decapi.me/twitch/avatar/' + username);
let data = await response.text()
avatarMap.set(username, data);
let data = await response.text();
avatarMap.set(`${username}-${platform}`, data);
return data;
}
case 'kick':
{
console.debug(`No avatar found for ${username} (${platform}). Retrieving from Kick.`)
let response = await fetch('https://kick.com/api/v2/channels/' + username);
console.log('https://kick.com/api/v2/channels/' + username)
let data = await response.json();
let avatarURL = data.user.profile_pic;
if (!avatarURL)
avatarURL = 'https://kick.com/img/default-profile-pictures/default2.jpeg';
avatarMap.set(`${username}-${platform}`, avatarURL);
return avatarURL;
}
}
}
async function GetPronouns(platform, username) {
@@ -1309,6 +1863,54 @@ function EscapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
async function GetKickChatroomId(username) {
const url = `https://kick.com/api/v2/channels/${username}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const data = await response.json();
if (data.chatroom && data.chatroom.id) {
return data.chatroom.id;
} else {
throw new Error("Chatroom ID not found in response.");
}
} catch (error) {
console.error("Failed to fetch chatroom ID:", error.message);
return null;
}
}
async function GetKickSubBadges(username) {
const response = await fetch(`https://kick.com/api/v2/channels/${username}`);
const data = await response.json();
return data.subscriber_badges || [];
}
function GetKickBadgeURL(data) {
switch (data.type) {
case 'subscriber':
return CalculateKickSubBadge(data.count);
default:
return `icons/badges/kick-${data.type}.svg`;
}
}
function CalculateKickSubBadge(months) {
if (!Array.isArray(kickSubBadges)) return null;
// Filter for eligible badges, then get the one with the highest 'months'
const badge = kickSubBadges
.filter(b => b.months <= months)
.sort((a, b) => b.months - a.months)[0];
return badge?.badge_image?.src || `icons/badges/kick-subscriber.svg`;
}
///////////////////////////////////
+92
View File
@@ -143,6 +143,14 @@
"defaultValue": true,
"group": "Which Twitch messages do you want to see?"
},
{
"id": "showTwitchFollows",
"label": "New Followers",
"description": "",
"type": "checkbox",
"defaultValue": false,
"group": "Which Twitch messages do you want to see?"
},
{
"id": "showTwitchSubs",
"label": "New Subscribers",
@@ -207,6 +215,90 @@
"defaultValue": true,
"group": "Which YouTube messages do you want to see?"
},
{
"id": "kickUsername",
"label": "Kick Username",
"description": "",
"type": "text",
"defaultValue": "",
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickMessages",
"label": "Chat Messages",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickSubs",
"label": "New Subscribers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickChannelPointRedemptions",
"label": "Channel Point Redemptions",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickHosts",
"label": "Hosts",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "enableTikTokSupport",
"label": "Enable TikTok Support",
"description": "",
"type": "checkbox",
"defaultValue": false,
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokMessages",
"label": "Chat Messages",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokFollows",
"label": "New Followers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokGifts",
"label": "Gifts",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokSubs",
"label": "New Subscribers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showStreamlabsDonations",
"label": "Streamlabs Tips",
+8
View File
@@ -252,6 +252,10 @@ li {
background: linear-gradient(#FF6C60, #FF0707) !important;
}
.kick {
background: linear-gradient(#53fc18, #005600) !important;
}
.streamlabs {
background: linear-gradient(#73dabb, #397765) !important;
}
@@ -276,6 +280,10 @@ li {
background: linear-gradient(#466edb, #1c56f5) !important;
}
.tiktok {
background: linear-gradient(#262626, #000000) !important;
}
.blank {
background: #adadad00 !important;
}
@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_209_29907)"><path d="M17.56 0H14.4533C13.717 0 13.12 0.597452 13.12 1.33445V4.4437C13.12 5.1807 13.717 5.77815 14.4533 5.77815H17.56C18.2964 5.77815 18.8933 5.1807 18.8933 4.4437V1.33445C18.8933 0.597452 18.2964 0 17.56 0Z" fill="url(#paint0_linear_209_29907)"></path><path d="M17.3333 5.77815H14.6667V8.44704H17.3333V5.77815Z" fill="url(#paint1_linear_209_29907)"></path><path d="M5.33333 14.8257C5.33333 14.8257 0 14.8257 0 20.1635C0 25.5013 5.33333 25.5013 5.33333 25.5013V14.8257Z" fill="url(#paint2_linear_209_29907)"></path><path d="M26.6667 14.8257C26.6667 14.8257 32 14.8257 32 20.1635C32 25.5013 26.6667 25.5013 26.6667 25.5013V14.8257Z" fill="url(#paint3_linear_209_29907)"></path><path d="M26.6667 10.8224H5.33333V28.1701H26.6667V10.8224Z" fill="#4FD8FF"></path><path d="M15.76 11.2761C20.4133 11.2761 24.0933 12.3036 25.24 13.2911C26.3867 14.8657 26.1733 24.4737 24.9067 26.0751C24.2533 26.849 21.0667 28.01 16.28 28.01C11.24 28.01 7.73333 26.7556 6.94667 25.9283C5.70667 24.367 5.65333 14.7189 6.84 13.211C7.58667 12.4637 10.6667 11.2761 15.76 11.2761ZM15.76 7.27273C10.9067 7.27273 6.12 8.28691 4.01333 10.382C1.25333 13.1309 1.34667 25.7948 4.01333 28.6372C6.08 30.8524 11.2133 32 16.28 32C21.3467 32 26.08 30.9058 27.9867 28.6372C30.48 25.648 30.8667 12.9975 27.9867 10.382C25.7467 8.34028 20.72 7.27273 15.76 7.27273Z" fill="url(#paint4_linear_209_29907)"></path><path d="M23 17.975C23 16.6852 21.9553 15.6397 20.6667 15.6397C19.378 15.6397 18.3333 16.6852 18.3333 17.975V21.3111C18.3333 22.6008 19.378 23.6464 20.6667 23.6464C21.9553 23.6464 23 22.6008 23 21.3111V17.975Z" fill="black"></path><path d="M13.6667 17.975C13.6667 16.6852 12.622 15.6397 11.3333 15.6397C10.0447 15.6397 9 16.6852 9 17.975V21.3111C9 22.6008 10.0447 23.6464 11.3333 23.6464C12.622 23.6464 13.6667 22.6008 13.6667 21.3111V17.975Z" fill="black"></path></g><defs><linearGradient id="paint0_linear_209_29907" x1="15.963" y1="0.886836" x2="15.963" y2="43.3072" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint1_linear_209_29907" x1="16" y1="-69.28" x2="16" y2="-69.28" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint2_linear_209_29907" x1="17.28" y1="-0.2" x2="16.8598" y2="31.7766" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint3_linear_209_29907" x1="14.72" y1="-0.2" x2="15.1402" y2="31.7766" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><linearGradient id="paint4_linear_209_29907" x1="5.14015" y1="0.587156" x2="36.6592" y2="34.8544" gradientUnits="userSpaceOnUse"><stop stop-color="#00C7FF"></stop><stop offset="0.99" stop-color="#006399"></stop></linearGradient><clipPath id="clip0_209_29907"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><path d="M15.6773 22.1533C17.3698 22.1533 18.8182 21.5507 20.0233 20.3461C21.2282 19.1415 21.8307 17.6924 21.8307 16V6.15401C21.8307 4.46162 21.2286 3.01305 20.0233 1.80784C18.8182 0.602907 17.3698 0 15.6773 0C13.9849 0 12.5363 0.602907 11.3311 1.80784C10.1259 3.01285 9.52344 4.46162 9.52344 6.15401V16C9.52344 17.6923 10.1262 19.1415 11.3311 20.3461C12.5361 21.5507 13.9849 22.1533 15.6773 22.1533Z" fill="url(#paint0_linear_209_29909)"></path><path d="M15.6773 22.1533C17.3698 22.1533 18.8182 21.5507 20.0233 20.3461C21.2282 19.1415 21.8307 17.6924 21.8307 16V6.15401C21.8307 4.46162 21.2286 3.01305 20.0233 1.80784C18.8182 0.602907 17.3698 0 15.6773 0C13.9849 0 12.5363 0.602907 11.3311 1.80784C10.1259 3.01285 9.52344 4.46162 9.52344 6.15401V16C9.52344 17.6923 10.1262 19.1415 11.3311 20.3461C12.5361 21.5507 13.9849 22.1533 15.6773 22.1533Z" fill="white" fill-opacity="0.3"></path><path d="M26.3888 12.6731C26.1459 12.4295 25.8568 12.3076 25.5234 12.3076C25.1904 12.3076 24.902 12.4295 24.6581 12.6731C24.4147 12.9167 24.293 13.2051 24.293 13.5383V16C24.293 18.3718 23.4498 20.4006 21.7639 22.0864C20.0785 23.7723 18.0495 24.6153 15.6775 24.6153C13.3057 24.6153 11.2769 23.7723 9.59089 22.0864C7.90509 20.401 7.06226 18.3719 7.06226 16V13.5383C7.06226 13.2051 6.94041 12.9167 6.69692 12.6731C6.45329 12.4295 6.16514 12.3076 5.83159 12.3076C5.49804 12.3076 5.20956 12.4295 4.96606 12.6731C4.72237 12.9167 4.60059 13.2051 4.60059 13.5383V16C4.60059 18.8333 5.54627 21.2981 7.4371 23.3941C9.32799 25.4901 11.6645 26.6919 14.4467 26.9994V29.5381H9.52373C9.19038 29.5381 8.90196 29.6601 8.6584 29.9037C8.41477 30.1472 8.29293 30.4357 8.29293 30.7691C8.29293 31.1019 8.41477 31.391 8.6584 31.6344C8.90196 31.8778 9.19038 32 9.52373 32H21.831C22.1643 32 22.4531 31.8779 22.6963 31.6344C22.9402 31.391 23.0622 31.1019 23.0622 30.7691C23.0622 30.4358 22.9402 30.1472 22.6963 29.9037C22.4532 29.6601 22.1644 29.5381 21.831 29.5381H16.9086V26.9994C19.6904 26.6919 22.0267 25.4901 23.9178 23.3941C25.8089 21.2981 26.7548 18.8333 26.7548 16V13.5383C26.7548 13.2051 26.6327 12.9169 26.3888 12.6731Z" fill="url(#paint1_linear_209_29909)"></path><defs><linearGradient id="paint0_linear_209_29909" x1="5.22969e-08" y1="-5.22969e-08" x2="32" y2="32" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient><linearGradient id="paint1_linear_209_29909" x1="-5.88081e-07" y1="-8.98822e-07" x2="4.72839" y2="35.6202" gradientUnits="userSpaceOnUse"><stop stop-color="#FF1CD2"></stop><stop offset="1" stop-color="#B20DFF"></stop></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_209_29912)"><path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="url(#paint0_linear_209_29912)"></path><path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="url(#paint1_linear_209_29912)"></path><path d="M16 29.0375C23.2004 29.0375 29.0375 23.2004 29.0375 16C29.0375 8.79958 23.2004 2.96249 16 2.96249C8.79959 2.96249 2.9625 8.79958 2.9625 16C2.9625 23.2004 8.79959 29.0375 16 29.0375Z" fill="#FEB635"></path><path d="M16 29.0375C23.2004 29.0375 29.0375 23.2004 29.0375 16C29.0375 8.79958 23.2004 2.96249 16 2.96249C8.79959 2.96249 2.9625 8.79958 2.9625 16C2.9625 23.2004 8.79959 29.0375 16 29.0375Z" fill="url(#paint2_linear_209_29912)"></path><path d="M29.0375 16C29.0375 23.1875 23.1875 29.0375 16 29.0375C13.6563 29.0375 11.4625 28.4187 9.5625 27.3312C11.3125 28.2062 13.2875 28.7 15.375 28.7C22.5625 28.7 28.4125 22.85 28.4125 15.6625C28.4125 10.8188 25.75 6.58125 21.8125 4.3375C26.0938 6.475 29.0375 10.8938 29.0375 16ZM16.8875 3.575C19.4563 3.575 21.85 4.325 23.8625 5.60625C21.675 3.95625 18.95 2.96875 16 2.96875C8.8125 2.96875 2.9625 8.8125 2.9625 16.0063C2.9625 20.6437 5.4 24.7313 9.0625 27.0312C5.9 24.65 3.85 20.8687 3.85 16.6125C3.85 9.425 9.7 3.575 16.8875 3.575Z" fill="black" fill-opacity="0.05"></path><path d="M18.5966 9.45456V24H14.6477V13.0909H14.5625L11.3807 14.9943V11.6421L14.9602 9.45456H18.5966Z" fill="black" fill-opacity="0.8"></path><path d="M18.5966 9.45456V24H14.6477V13.0909H14.5625L11.3807 14.9943V11.6421L14.9602 9.45456H18.5966Z" fill="url(#paint3_linear_209_29912)" fill-opacity="0.5"></path><path d="M18.5966 9.45456V24H14.6477V13.0909H14.5625L11.3807 14.9943V11.6421L14.9602 9.45456H18.5966Z" stroke="black" stroke-opacity="0.1" stroke-width="0.350269"></path></g><defs><linearGradient id="paint0_linear_209_29912" x1="15.7467" y1="-4.46667" x2="16.2533" y2="36.6933" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><linearGradient id="paint1_linear_209_29912" x1="16" y1="0" x2="16" y2="32" gradientUnits="userSpaceOnUse"><stop stop-color="white" stop-opacity="0.3"></stop><stop offset="1" stop-color="white" stop-opacity="0.15"></stop></linearGradient><linearGradient id="paint2_linear_209_29912" x1="15.7936" y1="-0.677142" x2="16.2064" y2="32.8618" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><linearGradient id="paint3_linear_209_29912" x1="18.5966" y1="16.7273" x2="11.3807" y2="16.7273" gradientUnits="userSpaceOnUse"><stop stop-color="white" stop-opacity="0.1"></stop><stop offset="0.3" stop-color="white" stop-opacity="0.2"></stop><stop offset="0.65" stop-color="white" stop-opacity="0.05"></stop><stop offset="1" stop-color="white" stop-opacity="0.2"></stop></linearGradient><clipPath id="clip0_209_29912"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_817_50667)"><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 0 2 0H30ZM16.2197 2.99316C15.8292 2.60266 15.1962 2.60265 14.8057 2.99316L8.36328 9.43555C7.97294 9.82608 7.97284 10.4591 8.36328 10.8496L10.0918 12.5781C10.4823 12.9686 11.1153 12.9685 11.5059 12.5781L11.585 12.499L13.9414 14.8564L3.57129 25.2275C2.70357 26.0954 2.7035 27.5023 3.57129 28.3701C4.43911 29.2376 5.84612 29.2377 6.71387 28.3701L17.084 17.999L19.4414 20.3564L19.3633 20.4346C18.9728 20.8251 18.9728 21.4581 19.3633 21.8486L21.0918 23.5771C21.4823 23.9676 22.1154 23.9676 22.5059 23.5771L28.9482 17.1348C29.3386 16.7443 29.3386 16.1112 28.9482 15.7207L27.2197 13.9922C26.8293 13.6017 26.1962 13.6018 25.8057 13.9922L25.7266 14.0703L23.3701 11.7139C24.2377 10.8461 24.2376 9.4391 23.3701 8.57129C22.5023 7.7035 21.0954 7.70357 20.2275 8.57129L17.8701 6.21387L17.9482 6.13574C18.3388 5.74522 18.3388 5.11221 17.9482 4.72168L16.2197 2.99316Z" fill="url(#paint0_linear_817_50667)"></path><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 0 2 0H30ZM16.2197 2.99316C15.8292 2.60266 15.1962 2.60265 14.8057 2.99316L8.36328 9.43555C7.97294 9.82608 7.97284 10.4591 8.36328 10.8496L10.0918 12.5781C10.4823 12.9686 11.1153 12.9685 11.5059 12.5781L11.585 12.499L13.9414 14.8564L3.57129 25.2275C2.70357 26.0954 2.7035 27.5023 3.57129 28.3701C4.43911 29.2376 5.84612 29.2377 6.71387 28.3701L17.084 17.999L19.4414 20.3564L19.3633 20.4346C18.9728 20.8251 18.9728 21.4581 19.3633 21.8486L21.0918 23.5771C21.4823 23.9676 22.1154 23.9676 22.5059 23.5771L28.9482 17.1348C29.3386 16.7443 29.3386 16.1112 28.9482 15.7207L27.2197 13.9922C26.8293 13.6017 26.1962 13.6018 25.8057 13.9922L25.7266 14.0703L23.3701 11.7139C24.2377 10.8461 24.2376 9.4391 23.3701 8.57129C22.5023 7.7035 21.0954 7.70357 20.2275 8.57129L17.8701 6.21387L17.9482 6.13574C18.3388 5.74522 18.3388 5.11221 17.9482 4.72168L16.2197 2.99316Z" fill="url(#paint1_linear_817_50667)"></path><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 0 2 0H30ZM16.2197 2.99316C15.8292 2.60266 15.1962 2.60265 14.8057 2.99316L8.36328 9.43555C7.97294 9.82608 7.97284 10.4591 8.36328 10.8496L10.0918 12.5781C10.4823 12.9686 11.1153 12.9685 11.5059 12.5781L11.585 12.499L13.9414 14.8564L3.57129 25.2275C2.70357 26.0954 2.7035 27.5023 3.57129 28.3701C4.43911 29.2376 5.84612 29.2377 6.71387 28.3701L17.084 17.999L19.4414 20.3564L19.3633 20.4346C18.9728 20.8251 18.9728 21.4581 19.3633 21.8486L21.0918 23.5771C21.4823 23.9676 22.1154 23.9676 22.5059 23.5771L28.9482 17.1348C29.3386 16.7443 29.3386 16.1112 28.9482 15.7207L27.2197 13.9922C26.8293 13.6017 26.1962 13.6018 25.8057 13.9922L25.7266 14.0703L23.3701 11.7139C24.2377 10.8461 24.2376 9.4391 23.3701 8.57129C22.5023 7.7035 21.0954 7.70357 20.2275 8.57129L17.8701 6.21387L17.9482 6.13574C18.3388 5.74522 18.3388 5.11221 17.9482 4.72168L16.2197 2.99316Z" fill="url(#paint2_linear_817_50667)"></path></g><defs><linearGradient id="paint0_linear_817_50667" x1="18.8102" y1="-12.7222" x2="2.88536" y2="39.1063" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6A4A"></stop><stop offset="1" stop-color="#C70C00"></stop></linearGradient><linearGradient id="paint1_linear_817_50667" x1="15.7467" y1="-4.75575" x2="16.321" y2="39.0672" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><linearGradient id="paint2_linear_817_50667" x1="-14.9543" y1="46.9544" x2="32.0001" y2="-0.000509222" gradientUnits="userSpaceOnUse"><stop stop-color="#0095FF"></stop><stop offset="0.99" stop-color="#00C7FF"></stop></linearGradient><clipPath id="clip0_817_50667"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32">
<path fill="url(#SidekickBadgeA)" d="M0 5.5v11.3h2.3V20h2.3v3.2h2.2v3.2h6.9v-3.2h4.6v3.2H25v-3.2h2.3V20h2.3v-3.2H32V5.5h-9.2v3.2h-4.6V12h-4.5V8.7H9V5.5H0Zm13.7 13.7H7V16H4.6V9.6h2.3v3.2h4.5V16h2.3v3.2ZM27.4 16h-2.2v3.2h-6.9V16h2.3v-3.2h4.6V9.6h2.2V16Z"/>
<defs>
<linearGradient id="SidekickBadgeA" x1="18.8" x2="11.7" y1="-2.7" y2="32.7" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF6A4A"/>
<stop offset="1" stop-color="#C70C00"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 588 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.07324 1.33331H6.51991V4.29331H7.99991V2.81331H9.47991V1.33331H13.9266V5.77998H12.4466V7.25998H10.9599V8.73998H12.4466V10.22H13.9266V14.6666H9.47991V13.1866H7.99991V11.7066H6.51991V14.6666H2.07324V1.33331Z" fill="url(#badge-verified-gradient)"/>
<defs>
<linearGradient id="badge-verified-gradient" x1="33.791%" y1="97.416%" x2="65.541%" y2="4.5%" gradientUnits="objectBoundingBox">
<stop stop-color="#1EFF00"/>
<stop offset="0.99" stop-color="#00FF8C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 644 B

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_162_470)"><g clip-path="url(#clip1_162_470)"><path d="M22.34 9.5L26 4H18L16 7L14 4H6L9.66 9.5H4V15.1H28V9.5H22.34Z" fill="#53FC18"></path><path d="M26.0799 19.0996H5.8999V28.4996H26.0799V19.0996Z" fill="#53FC18"></path><path d="M26.0799 15.0996H5.8999V19.0996H26.0799V15.0996Z" fill="#32970E"></path></g></g><defs><clipPath id="clip0_162_470"><rect width="24" height="24.5" fill="white" transform="translate(4 4)"></rect></clipPath><clipPath id="clip1_162_470"><rect width="24" height="24.5" fill="white" transform="translate(4 4)"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 693 B

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_209_29946)"><path d="M17.0284 2.91378L16.2357 0.667951C16.1573 0.445558 15.8427 0.445558 15.7643 0.667951L14.9716 2.91378C12.9003 8.78263 8.78263 12.9003 2.91378 14.9716L0.667951 15.7643C0.445558 15.8427 0.445558 16.1573 0.667951 16.2357L2.91378 17.0284C8.78263 19.0998 12.9003 23.2174 14.9716 29.0862L15.7643 31.3321C15.8427 31.5544 16.1573 31.5544 16.2357 31.3321L17.0284 29.0862C19.0998 23.2174 23.2174 19.0998 29.0862 17.0284L31.3321 16.2357C31.5544 16.1573 31.5544 15.8427 31.3321 15.7643L29.0862 14.9716C23.2174 12.9003 19.0998 8.78263 17.0284 2.91378Z" fill="black"></path><path d="M17.0284 2.91378L16.2357 0.667951C16.1573 0.445558 15.8427 0.445558 15.7643 0.667951L14.9716 2.91378C12.9003 8.78263 8.78263 12.9003 2.91378 14.9716L0.667951 15.7643C0.445558 15.8427 0.445558 16.1573 0.667951 16.2357L2.91378 17.0284C8.78263 19.0998 12.9003 23.2174 14.9716 29.0862L15.7643 31.3321C15.8427 31.5544 16.1573 31.5544 16.2357 31.3321L17.0284 29.0862C19.0998 23.2174 23.2174 19.0998 29.0862 17.0284L31.3321 16.2357C31.5544 16.1573 31.5544 15.8427 31.3321 15.7643L29.0862 14.9716C23.2174 12.9003 19.0998 8.78263 17.0284 2.91378Z" fill="url(#paint0_radial_209_29946)"></path></g><defs><radialGradient id="paint0_radial_209_29946" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(16 16) rotate(90) scale(16)"><stop stop-color="#E1FF00"></stop><stop offset="1" stop-color="#2AA300"></stop></radialGradient><clipPath id="clip0_209_29946"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="size-[calc(1em*(18/13))]"><g clip-path="url(#clip0_614_6275)"><path d="M30.8598 19.2368C30.1977 18.2069 29.5356 17.2138 28.8736 16.1839C28.7264 15.9632 28.7264 15.8161 28.8736 15.5954C29.5356 14.6023 30.1609 13.6092 30.823 12.6161C31.5954 11.4391 31.1908 10.2989 29.8667 9.82069C28.7632 9.41609 27.6598 8.97471 26.5563 8.57012C26.3356 8.49656 26.2253 8.34943 26.2253 8.09196C26.1885 6.87816 26.1149 5.66437 26.0414 4.48736C25.9678 3.2 24.9747 2.46437 23.7241 2.7954C22.5471 3.08966 21.3701 3.42069 20.2299 3.75173C19.9724 3.82529 19.8253 3.75173 19.6414 3.56782C18.9057 2.61149 18.1333 1.69195 17.3977 0.772414C16.5885 -0.257472 15.3379 -0.257472 14.492 0.772414C13.7563 1.69195 12.9839 2.61149 12.2851 3.53103C12.1012 3.7885 11.9172 3.82529 11.623 3.75173C10.4828 3.42069 9.34253 3.12644 8.53334 2.90575C6.95173 2.53793 5.99541 3.16322 5.92184 4.48736C5.84828 5.70115 5.77472 6.91495 5.73794 8.16552C5.73794 8.42299 5.62759 8.53333 5.4069 8.64368C4.26667 9.08506 3.12644 9.52644 1.98621 9.96782C0.809203 10.446 0.441387 11.5862 1.14023 12.6529C1.8023 13.6828 2.46437 14.6759 3.12644 15.7057C3.27356 15.9264 3.27356 16.0736 3.12644 16.331C2.42759 17.3609 1.76552 18.3908 1.10345 19.4575C0.478165 20.4506 0.882759 21.6276 1.98621 22.069C3.12644 22.5104 4.30345 22.9517 5.44368 23.3931C5.70115 23.4667 5.77471 23.6138 5.77471 23.8713C5.81149 25.0483 5.95862 26.1885 5.95862 27.3655C5.95862 28.5425 6.9885 29.6092 8.42298 29.1678C9.56321 28.8 10.7034 28.5425 11.8437 28.2115C12.0644 28.1379 12.2115 28.1747 12.3586 28.3954C13.131 29.3517 13.8667 30.2713 14.6391 31.2276C15.485 32.2575 16.6988 32.2575 17.508 31.2276C18.2805 30.2713 19.0161 29.3517 19.7885 28.3954C19.9356 28.2115 20.046 28.1379 20.3034 28.2115C21.4804 28.5425 22.6575 28.8368 23.8345 29.1678C25.0483 29.4988 26.0781 28.7632 26.1149 27.5126C26.1885 26.2989 26.2621 25.0851 26.2988 23.8345C26.2988 23.5402 26.446 23.4299 26.6667 23.3563C27.7701 22.9517 28.9103 22.5104 30.0138 22.069C31.1908 21.4805 31.5586 20.3034 30.8598 19.2368ZM22.069 13.2046L14.7127 20.5609C14.5287 20.7448 14.2713 20.892 14.0138 20.9287C13.9402 20.9287 13.8299 20.9655 13.7563 20.9655C13.4253 20.9655 13.0575 20.8184 12.8 20.5609L9.78392 17.5448C9.26898 17.0299 9.26898 16.1839 9.78392 15.669C10.2989 15.154 11.1448 15.154 11.6598 15.669L13.7196 17.7287L20.1196 11.3287C20.6345 10.8138 21.4805 10.8138 21.9954 11.3287C22.5839 11.8437 22.5839 12.6897 22.069 13.2046Z" fill="url(#paint0_linear_614_6275)"></path></g><defs><linearGradient id="paint0_linear_614_6275" x1="8.14138" y1="32.3591" x2="24.4968" y2="0.904884" gradientUnits="userSpaceOnUse"><stop stop-color="#1EFF00"></stop><stop offset="0.99" stop-color="#00FF8C"></stop></linearGradient><clipPath id="clip0_614_6275"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_746_28171)"><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 4.10637e-08 2 0H30ZM15.9648 5C15.7748 5.00005 15.588 5.05204 15.4238 5.15039C15.2596 5.24878 15.124 5.39057 15.0303 5.56055L9.82812 15.0176L3.55078 11.8906C3.36913 11.7985 3.16534 11.7607 2.96387 11.7822C2.76241 11.8038 2.57048 11.8842 2.41113 12.0127C2.25235 12.1408 2.13185 12.3126 2.06348 12.5078C1.99511 12.7031 1.98143 12.9144 2.02441 13.1172L4.58301 25.127C4.63544 25.3782 4.77165 25.6034 4.96777 25.7627C5.16376 25.9217 5.40762 26.0056 5.65723 26H26.251C26.5009 26.0057 26.7453 25.9219 26.9414 25.7627C27.1376 25.6034 27.2737 25.3782 27.3262 25.127L29.9697 13.1172C30.0187 12.9103 30.0086 12.6932 29.9404 12.4922C29.8722 12.2912 29.7485 12.1151 29.585 11.9844C29.4215 11.8537 29.2249 11.7743 29.0186 11.7559C28.8122 11.7374 28.6049 11.7802 28.4219 11.8799L22.1025 15.0283L16.9004 5.56055C16.8066 5.39054 16.6701 5.24878 16.5059 5.15039C16.3416 5.05207 16.1549 5 15.9648 5Z" fill="url(#paint0_linear_746_28171)"></path><path d="M30 0C31.1046 0 32 0.895431 32 2V30C32 31.1046 31.1046 32 30 32H2C0.895431 32 0 31.1046 0 30V2C0 0.895431 0.895431 4.10637e-08 2 0H30ZM15.9648 5C15.7748 5.00005 15.588 5.05204 15.4238 5.15039C15.2596 5.24878 15.124 5.39057 15.0303 5.56055L9.82812 15.0176L3.55078 11.8906C3.36913 11.7985 3.16534 11.7607 2.96387 11.7822C2.76241 11.8038 2.57048 11.8842 2.41113 12.0127C2.25235 12.1408 2.13185 12.3126 2.06348 12.5078C1.99511 12.7031 1.98143 12.9144 2.02441 13.1172L4.58301 25.127C4.63544 25.3782 4.77165 25.6034 4.96777 25.7627C5.16376 25.9217 5.40762 26.0056 5.65723 26H26.251C26.5009 26.0057 26.7453 25.9219 26.9414 25.7627C27.1376 25.6034 27.2737 25.3782 27.3262 25.127L29.9697 13.1172C30.0187 12.9103 30.0086 12.6932 29.9404 12.4922C29.8722 12.2912 29.7485 12.1151 29.585 11.9844C29.4215 11.8537 29.2249 11.7743 29.0186 11.7559C28.8122 11.7374 28.6049 11.7802 28.4219 11.8799L22.1025 15.0283L16.9004 5.56055C16.8066 5.39054 16.6701 5.24878 16.5059 5.15039C16.3416 5.05207 16.1549 5 15.9648 5Z" fill="url(#paint1_linear_746_28171)"></path></g><defs><linearGradient id="paint0_linear_746_28171" x1="18.8102" y1="-12.7222" x2="2.88536" y2="39.1063" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6A4A"></stop><stop offset="1" stop-color="#C70C00"></stop></linearGradient><linearGradient id="paint1_linear_746_28171" x1="15.7467" y1="-4.75575" x2="16.321" y2="39.0672" gradientUnits="userSpaceOnUse"><stop stop-color="#FFC900"></stop><stop offset="0.99" stop-color="#FF9500"></stop></linearGradient><clipPath id="clip0_746_28171"><rect width="32" height="32" fill="white"></rect></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+27 -11
View File
@@ -1,6 +1,7 @@
<!DOCTYPE html>
<head>
<title>nutty | Multichat Overlay</title>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./style.css" />
@@ -20,7 +21,9 @@
<template id="messageTemplate">
<div id="messageContainer">
<label>
<div class="message-contents">
<div id="avatar"></div>
<div>
<div id="firstMessage">
<span>✨ First Time Chat</span>
<span> from viewer</span>
@@ -29,7 +32,6 @@
<span id="sharedChatChannel"></span>
</div>
<label id="userInfo">
<span id="avatar"></span>
<span id="timestamp"></span>
<span id="platform"></span>
<span id="badgeList"></span>
@@ -39,32 +41,46 @@
<br id="line-space">
</label>
<div id="reply">
<span>➥ Replying to <span id="replyUser"></span>: <span id="replyMsg"></span></span>
<div>➥ Replying to <span id="replyUser"></span>: <span id="replyMsg"></span></div>
</div>
<span id="message"></span>
</label>
</div>
</div>
</div>
</template>
<template id="cardTemplate">
<div id="card">
<div class="message-contents add-this-class-to-the-thing-to-make-all-the-text-centered-vertically-because-that-looks-much-better">
<div id="avatar"></div>
<div>
<label id="header">
<span id="avatar"></span>
<span id="icon"></span>
<span id="title"></span>
<br id="line-space">
</label>
<span id="content"></span>
</div>
</div>
</div>
</template>
<template id="stickerTemplate">
<div id="sticker">
<div id="youtubeSuperSticker">
<div style="width: 100%; text-align: center;">
<img id="stickerImg">
<template id="tiktok-gift-template">
<div class="tiktok-gift">
<img class="tiktok-gift-avatar"
src="https://static-cdn.jtvnw.net/jtv_user_pictures/f632e0c6-38e2-4065-a29e-6ba9b3e8cacf-profile_image-300x300.png" />
<div class="tiktok-gift-message">
<span id="tiktok-gift-username" style="font-size: 0.9em; font-weight: 700;">
</span>
<br />
<span style="font-size: 0.7em;">
sent <span id="tiktok-gift-name"></span>
</span>
</div>
<div id="stickerLabel"></div>
<img class="tiktok-gift-sticker"
src="https://p19-webcast.tiktokcdn.com/img/maliva/webcast-va/3851a489dea62b3a9fe99f61de5470ae~tplv-obj.png" />
<div id="tiktok-gift-repeat-count">
x13
</div>
</div>
</template>
+1242 -205
View File
File diff suppressed because it is too large Load Diff
+141 -3
View File
@@ -5,7 +5,7 @@
"label": "Show Platform",
"description": "",
"type": "checkbox",
"defaultValue": true,
"defaultValue": false,
"group": "Appearance"
},
{
@@ -71,7 +71,7 @@
"type": "number",
"min": 1,
"max": 120,
"defaultValue": 30,
"defaultValue": 20,
"group": "Appearance"
},
{
@@ -84,6 +84,35 @@
"step": ".1",
"group": "Appearance"
},
{
"id": "useChatBubbles",
"label": "Chat Bubbles",
"description": "",
"type": "checkbox",
"defaultValue": false,
"group": "Appearance"
},
{
"id": "bubbleColor",
"label": "Bubble Color",
"description": "",
"type": "color",
"defaultValue": "#1d1d1d",
"showIf": "useChatBubbles",
"group": "Appearance"
},
{
"id": "bubbleOpacity",
"label": "Bubble Opacity",
"description": "",
"type": "number",
"defaultValue": "0.90",
"min": 0,
"max": 1,
"step": ".01",
"showIf": "useChatBubbles",
"group": "Appearance"
},
{
"id": "background",
"label": "Background",
@@ -97,7 +126,7 @@
"label": "Background Opacity",
"description": "",
"type": "number",
"defaultValue": "0.85",
"defaultValue": "0",
"min": 0,
"max": 1,
"step": ".01",
@@ -197,6 +226,14 @@
"defaultValue": 20,
"group": "General"
},
{
"id": "showYouTubeLinkPreviews",
"label": "Show YouTube Link Previews",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "General"
},
{
"id": "showTwitchMessages",
"label": "Chat Messages",
@@ -213,6 +250,14 @@
"defaultValue": true,
"group": "Which Twitch messages do you want to see?"
},
{
"id": "showTwitchFollows",
"label": "New Followers",
"description": "",
"type": "checkbox",
"defaultValue": false,
"group": "Which Twitch messages do you want to see?"
},
{
"id": "showTwitchSubs",
"label": "New Subscribers",
@@ -291,6 +336,99 @@
"defaultValue": true,
"group": "Which YouTube messages do you want to see?"
},
{
"id": "kickUsername",
"label": "Kick Username",
"description": "",
"type": "text",
"defaultValue": "",
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickMessages",
"label": "Chat Messages",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickSubs",
"label": "New Subscribers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickChannelPointRedemptions",
"label": "Channel Point Redemptions",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "showKickHosts",
"label": "Hosts",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick messages do you want to see?"
},
{
"id": "enableTikTokSupport",
"label": "Enable TikTok Support",
"description": "",
"type": "checkbox",
"defaultValue": false,
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokMessages",
"label": "Chat Messages",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokFollows",
"label": "New Followers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokLikes",
"label": "Likes",
"description": "Self-updating Likes",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokGifts",
"label": "Gifts",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showTikTokSubs",
"label": "New Subscribers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok messages do you want to see?"
},
{
"id": "showStreamlabsDonations",
"label": "Streamlabs Tips",
+105 -18
View File
@@ -1,8 +1,8 @@
* {
margin: 0;
padding: 0;
--margin: 40px;
--border-radius: 20px;
--margin: 20px;
--border-radius: 0.5em;
/* --line-spacing: 1.7em; */
}
@@ -52,6 +52,7 @@ html {
.normalScrollDirection {
bottom: 0;
}
.reverseScrollDirection {
display: flex;
top: 0;
@@ -92,17 +93,47 @@ li {
font-size: 0.7em;
}
.highlightMessage {
background: #adadad46;
padding: 20px 40px;
.bubble {
background: var(--bubble-color) !important;
padding: 0.5em 1em;
border-radius: var(--border-radius);
}
.highlightMessage {
/* background: #adadad46 !important; */
background: #414141e5 !important;
box-shadow: inset 0 0 0 0.1em rgb(94, 94, 94);
padding: 0.5em 1em;
border-radius: var(--border-radius);
}
.message-contents {
display: flex;
flex-direction: row;
gap: 0.5em;
}
.add-this-class-to-the-thing-to-make-all-the-text-centered-vertically-because-that-looks-much-better {
align-items: center;
}
#avatar {
width: 2em;
}
#avatar:empty {
display: none;
}
.avatar {
width: 2em; /* Set width equal to height */
height: 2em;
margin: 1px;
transform: translate(0px, 0.25em);
border-radius: 50%
border-radius: 50%;
object-fit: cover; /* Crop to fill the circle */
overflow: hidden; /* Ensure no overflow outside the circle */
display: inline-block; /* Just in case */
}
.timestamp,
@@ -136,16 +167,22 @@ li {
}
.productImage {
height: 15em;
height: 3em;
border-radius: 0.25em;
transform: translate(0px, 0.25em);
}
.centerThatShitHomie {
.youtubeThumbnail {
width: 5em;
border-radius: 0.25em;
transform: translate(0px, 0.25em);
}
/* .centerThatShitHomie {
display: inline-block;
width: 100%;
text-align: center;
}
} */
#badgeList {
margin: 0px 5px;
@@ -163,8 +200,9 @@ li {
#reply {
display: none;
overflow: hidden;
white-space: nowrap;
white-space: normal; /* Allow line wrapping */
text-overflow: ellipsis;
word-break: break-word; /* Break long words if necessary */
font-size: 0.7em;
opacity: 0.5;
}
@@ -176,21 +214,53 @@ li {
#card {
background: #adadad46;
padding: 20px 40px;
padding: 0.5em 1em;
border-radius: var(--border-radius);
}
#youtubeSuperSticker {
display: block;
.youtube-super-sticker {
height: 2em;
margin: 1px;
transform: translate(0px, 0.25em);
}
#sticker-img {
height: 5em;
.tiktok-gift {
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
padding: 0.5em 1em;
border-radius: var(--border-radius);
max-width: 100%;
background: linear-gradient(#262626BF, #000000BF) !important;
}
#stickerLabel {
text-align: center;
font-weight: 700;
.tiktok-gift-avatar {
height: 3em;
margin: 1px;
border-radius: 50%;
justify-content: left;
flex-shrink: 0;
}
.tiktok-gift-message {
flex-grow: 1;
white-space: nowrap; /* keeps text in a single line */
overflow: hidden; /* hides overflowed content */
text-overflow: ellipsis; /* adds the "..." at the end */
}
.tiktok-gift-sticker {
height: 2.5em;
flex-shrink: 0;
justify-content: flex-end;
}
#tiktok-gift-repeat-count {
justify-content: flex-end;
font-size: 1.5em;
font-style: italic;
font-weight: 500;
}
/*****************/
@@ -221,6 +291,10 @@ li {
background: linear-gradient(#FF6C60BF, #FF0707BF) !important;
}
.kick {
background: linear-gradient(#53fc18BF, #005600BF) !important;
}
.streamlabs {
background: linear-gradient(#73dabbbf, #397765bf) !important;
}
@@ -245,6 +319,19 @@ li {
background: linear-gradient(#466edbbf, #1c56f5bf) !important;
}
.tiktok {
background: linear-gradient(#262626BF, #000000BF) !important;
}
.fourthwall {
background: linear-gradient(#466edbbf, #1c56f5bf) !important;
}
.thumbnail {
background: linear-gradient(#494949bf, #202020bf) !important;
}
.blank {
background: #adadad00 !important;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+451 -61
View File
@@ -24,6 +24,15 @@ const messageLabel = document.getElementById('message');
let widgetLocked = false; // Needed to lock animation from overlapping
let alertQueue = [];
/////////////////
// GLOBAL VARS //
/////////////////
const kickPusherWsUrl = 'wss://ws-us2.pusher.com/app/32cbd69e4b950bf97679?protocol=7&client=js&version=7.6.0&flash=false';
let kickSubBadges = [];
/////////////
// OPTIONS //
/////////////
@@ -60,6 +69,15 @@ const twitchCheerAction = urlParams.get("twitchCheerAction") || "";
const showTwitchRaids = GetBooleanParam("showTwitchRaids", true);
const twitchRaidAction = urlParams.get("twitchRaidAction") || "";
// Which Kick alerts do you want to see?
let kickUsername = urlParams.get("kickUsername") || "";
const showKickSubs = GetBooleanParam("showKickSubs", true);
const kickSubAction = urlParams.get("kickSubAction") || "";
const showKickChannelPointRedemptions = GetBooleanParam("showKickChannelPointRedemptions", true);
const kickChannelPointRedemptionAction = urlParams.get("kickChannelPointRedemptionAction") || "";
const showKickHosts = GetBooleanParam("showKickHosts", true);
const kickHostAction = urlParams.get("kickHostAction") || "";
// Which YouTube alerts do you want to see?
const showYouTubeSuperChats = GetBooleanParam("showYouTubeSuperChats", true);
const youtubeSuperChatAction = urlParams.get("youtubeSuperChatAction") || "";
@@ -68,6 +86,13 @@ const youtubeSuperStickerAction = urlParams.get("youtubeSuperStickerAction") ||
const showYouTubeMemberships = GetBooleanParam("showYouTubeMemberships", true);
const youtubeMembershipAction = urlParams.get("youtubeMembershipAction") || "";
// Which TikTok alerts do you want to see?
const enableTikTokSupport = GetBooleanParam("enableTikTokSupport", false);
const showTikTokGifts = GetBooleanParam("showTikTokGifts", true);
const tiktokGiftAction = urlParams.get("tiktokGiftAction") || "";
const showTikTokSubs = GetBooleanParam("showTikTokSubs", true);
const tiktokSubAction = urlParams.get("tiktokSubAction") || "";
// Which donation alerts do you want to see?
const showStreamlabsDonations = GetBooleanParam("showStreamlabsDonations", false);
const streamlabsDonationAction = urlParams.get("streamlabsDonationAction") || "";
@@ -82,6 +107,9 @@ const tipeeestreamDonationAction = urlParams.get("tipeeestreamDonationAction") |
const showFourthwallAlerts = GetBooleanParam("showFourthwallAlerts", false);
const fourthwallAlertAction = urlParams.get("fourthwallAlertAction") || "";
// Kick is stupid and turns underscores into dashes which fuck everything up, therefore do a find/replace to make it work good
kickUsername = kickUsername.replace(/_/g, "-");
// Set avatar visibility
if (!showAvatar) {
avatarElement.style.display = 'none';
@@ -102,7 +130,7 @@ if (useCustomBackground) {
hexOpacity = "0" + hexOpacity;
}
//document.body.style.background = `${background}${hexOpacity}`;
console.log(`${background}${hexOpacity}`);
//console.log(`${background}${hexOpacity}`);
document.documentElement.style.setProperty('--custom-background', `${background}${hexOpacity}`);
}
@@ -277,6 +305,138 @@ client.on('Fourthwall.GiftDrawEnded', (response) => {
///////////////////////////
// KICK PUSHER WEBSOCKET //
///////////////////////////
// Connect and handle Pusher WebSocket
async function KickConnect() {
if (!kickUsername)
return;
// Channel to subscribe to (you'll need the correct channel name here)
const chatroomId = await GetKickChatroomId(kickUsername);
// Cache subscriber badges
kickSubBadges = await GetKickSubBadges(kickUsername);
const websocket = new WebSocket(kickPusherWsUrl);
// Reconnect
websocket.onclose = function () {
console.log(`Reconnecting to ${kickUsername}...`);
setTimeout(connectPusher, 5000);
};
websocket.onopen = function () {
console.log(`Kick successfully conntected to ${kickUsername}.`);
}
websocket.onmessage = function (response) {
try {
let data = JSON.parse(response.data);
console.debug(data);
// When connection is established, subscribe to a channel
if (data.event === 'pusher:connection_established') {
const socketData = JSON.parse(data.data);
console.log(`[Pusher] Socket established with ID: ${socketData.socket_id}`);
// Now subscribe to a channel
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatroom_${chatroomId}` } }));
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}` } }));
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `chatrooms.${chatroomId}.v2` } }));
websocket.send(JSON.stringify({ event: 'pusher:subscribe', data: { channel: `predictions-channel-${chatroomId}` } }));
console.log(`[Pusher] Sent subscription request to channel: ${chatroomId}`);
}
// Event handlers
const eventArgs = JSON.parse(data.data);
const event = data.event.split('\\').pop();
switch (event) {
//// 'Follows' unsupported by pusher
// case 'FollowEvent':
// break;
case 'SubscriptionEvent':
KickSubscription(eventArgs);
break;
case 'GiftedSubscriptionsEvent':
KickGiftedSubscriptions(eventArgs);
break;
case 'RewardRedeemedEvent':
KickRewardRedeemed(eventArgs);
break;
case 'StreamHostEvent':
KickStreamHost(eventArgs);
break;
}
}
catch (error) {
console.error(error);
}
}
}
// Try connect when window is loaded
window.addEventListener('load', KickConnect);
//////////////////////
// TIKFINITY CLIENT //
//////////////////////
let tikfinityWebsocket = null;
function TikfinityConnect() {
if (!enableTikTokSupport)
return;
if (tikfinityWebsocket) return; // Already connected
tikfinityWebsocket = new WebSocket("ws://localhost:21213/");
tikfinityWebsocket.onopen = function () {
console.log(`TikFinity successfully connected...`)
}
tikfinityWebsocket.onclose = function () {
console.error(`TikFinity disconnected...`)
tikfinityWebsocket = null;
setTimeout(TikfinityConnect, 1000); // Schedule a reconnect attempt
}
tikfinityWebsocket.onerror = function () {
console.error(`TikFinity failed for some reason...`)
tikfinityWebsocket = null;
setTimeout(TikfinityConnect, 1000); // Schedule a reconnect attempt
}
tikfinityWebsocket.onmessage = function (response) {
let payload = JSON.parse(response.data);
let event = payload.event;
let data = payload.data;
console.debug('Event: ' + event);
switch (event) {
case 'gift':
TikTokGift(data);
break;
case 'subscribe':
TikTokSubscribe(data);
break;
}
}
}
// Try connect when window is loaded
window.addEventListener('load', TikfinityConnect);
///////////////////////
// MULTICHAT OVERLAY //
///////////////////////
@@ -289,7 +449,7 @@ async function TwitchFollow(data) {
const username = data.user_name;
// Render avatars
const avatarURL = await GetAvatar(username);
const avatarURL = await GetAvatar(username, 'twitch');
UpdateAlertBox(
'twitch',
@@ -314,7 +474,7 @@ async function TwitchCheer(data) {
let message = data.message.message;
// Render avatars
const avatarURL = await GetAvatar(username);
const avatarURL = await GetAvatar(username, 'twitch');
// Render emotes
for (i in data.emotes) {
@@ -369,7 +529,7 @@ async function TwitchSub(data) {
const isPrime = data.is_prime;
// Render avatars
const avatarURL = await GetAvatar(username);
const avatarURL = await GetAvatar(username, 'twitch');
if (!isPrime)
UpdateAlertBox(
@@ -409,7 +569,7 @@ async function TwitchResub(data) {
const message = data.text;
// Render avatars
const avatarURL = await GetAvatar(username);
const avatarURL = await GetAvatar(username, 'twitch');
if (!isPrime)
UpdateAlertBox(
@@ -453,7 +613,7 @@ async function TwitchGiftSub(data) {
return;
// Render avatars
const avatarURL = await GetAvatar(username);
const avatarURL = await GetAvatar(username, 'twitch');
let messageText = '';
if (cumlativeTotal > 0)
@@ -488,7 +648,7 @@ async function TwitchGiftBomb(data) {
const subTier = data.sub_tier.charAt(0);
// Render avatars
const avatarURL = await GetAvatar(login);
const avatarURL = await GetAvatar(login, 'twitch');
let message = ``;
if (totalGifts > 0)
@@ -518,7 +678,7 @@ async function TwitchRewardRedemption(data) {
const channelPointIcon = `<img src="icons/badges/twitch-channel-point.png" class="platform" style="height: 1em"/>`;
// Render avatars
const avatarURL = await GetAvatar(data.user_login);
const avatarURL = await GetAvatar(data.user_login, 'twitch');
UpdateAlertBox(
'twitch',
@@ -538,7 +698,7 @@ async function TwitchRaid(data) {
return;
// Render avatars
const avatarURL = await GetAvatar(data.from_broadcaster_user_login);
const avatarURL = await GetAvatar(data.from_broadcaster_user_login, 'twitch');
// Set the text
const username = data.from_broadcaster_user_login;
@@ -988,18 +1148,17 @@ function FourthwallSubscriptionPurchased(data) {
}
function FourthwallGiftPurchase(data) {
console.log(data);
if (!showFourthwallAlerts)
return;
// Set the text
let user = data.username;
// let user = data.username;
const total = data.total;
const currency = data.currency;
const gifts = data.gifts.length;
const itemName = data.offer.name;
const itemImageUrl = data.offer.imageUrl;
const message = DecodeHTMLString(data.statmessageus);
// const itemImageUrl = data.offer.imageUrl;
// const message = DecodeHTMLString(data.statmessageus);
let contents = '';
let attributesText = '';
@@ -1019,12 +1178,12 @@ function FourthwallGiftPurchase(data) {
UpdateAlertBox(
'fourthwall',
itemImageUrl,
`${user}`,
`gifted ${contents}`,
'', // itemImageUrl,
`An item has been gifted!`,// `${user}`,
`${contents}`, // `gifted ${contents}`,
attributesText,
user,
message,
'', // user,
'', // message,
fourthwallAlertAction,
data
);
@@ -1042,7 +1201,7 @@ function FourthwallGiftDrawStarted(data) {
'fourthwall',
'',
`<span style="font-size: 1.2em">🎁 ${itemName} Giveaway!</span>`,
`Type !join in the next ${durationSeconds} seconds for your chance to win!`,
`Type 'join' in the next ${durationSeconds} seconds for your chance to win!`,
'',
'',
'',
@@ -1055,13 +1214,9 @@ function FourthwallGiftDrawEnded(data) {
if (!showFourthwallAlerts)
return;
// Render avatars
if (showAvatar) {
avatar.src = '';
}
UpdateAlertBox(
'fourthwall',
'',
`<span style="font-size: 1.2em">🥳 GIVEAWAY ENDED 🥳</span>`,
`Congratulations ${GetWinnersList(data.gifts)}!`,
'',
@@ -1072,6 +1227,202 @@ function FourthwallGiftDrawEnded(data) {
);
}
// async function KickFollow(data) {
// if (!showKickFollows)
// return;
// // Set the text
// const username = data.user;
// // Render avatars
// const avatarURL = await GetAvatar(username, 'kick');
// UpdateAlertBox(
// 'kick',
// avatarURL,
// `${username}`,
// `followed`,
// '',
// username,
// '',
// kickFollowAction,
// data
// );
// }
async function KickSubscription(data) {
if (!showKickSubs)
return;
// Set the text
const username = data.username;
const months = data.months;
let description = '';
if (months <= 1)
description = `just subscribed for the first time!`;
else
description = `resubscribed!`;
const attribute = `${months} months`;
// Render avatars
const avatarURL = await GetAvatar(username, 'kick');
UpdateAlertBox(
'kick',
avatarURL,
`${username}`,
description,
attribute,
username,
'',
kickSubAction,
data
);
}
async function KickGiftedSubscriptions(data) {
if (!showKickSubs)
return;
// Set the text
const gifter = data.gifter_username;
const giftedUsers = data.gifted_usernames;
let description = '';
let attribute = '';
if (giftedUsers.length <= 1)
{
description = `gifted a sub to`;
attribute = `${giftedUsers[0]}`;
}
else
description = `gifted ${giftedUsers.length} subscription${giftedUsers.length === 1 ? '' : 's'} to the community!`;
// Render avatars
const avatarURL = await GetAvatar(gifter, 'kick');
UpdateAlertBox(
'kick',
avatarURL,
`${gifter}`,
description,
attribute,
gifter,
'',
kickSubAction,
data
);
}
async function KickRewardRedeemed(data) {
if (!showKickChannelPointRedemptions)
return;
const username = data.username;
const rewardName = data.reward_title;
const userInput = data.user_input;
// Render avatars
const avatarURL = await GetAvatar(username, 'kick');
UpdateAlertBox(
'kick',
avatarURL,
`${username} redeemed`,
`${rewardName}`,
'',
username,
userInput,
kickChannelPointRedemptionAction,
data
);
}
async function KickStreamHost(data) {
if (!showKickHosts)
return;
// Render avatars
const avatarURL = await GetAvatar(data.host_username, 'kick');
// Set the text
const username = data.host_username;
const viewers = data.number_viewers;
UpdateAlertBox(
'kick',
avatarURL,
`${username}`,
`is raiding with a party of ${viewers}`,
'',
username,
'',
kickHostAction,
data
);
}
async function TikTokGift(data) {
if (!showTikTokGifts)
return;
if (data.giftType === 1 && !data.repeatEnd) {
// Streak in progress => show only temporary
console.debug(`${data.uniqueId} is sending gift ${data.giftName} x${data.repeatCount}`);
return;
}
// Streak ended or non-streakable gift => process the gift with final repeat_count
console.debug(`${data.uniqueId} has sent gift ${data.giftName} x${data.repeatCount}`);
// Set the text
const username = data.nickname;
const tiktokIcon = `<img src="icons/platforms/tiktok.png" class="platform"/>`;
const giftImg = `<img src=${data.giftPictureUrl} style="height: 1em"/>`;
// Render avatars
const avatarURL = 'icons/platforms/tiktok.png';
UpdateAlertBox(
'tiktok',
avatarURL,
`${username}`,
`sent ${giftImg}x${data.repeatCount}`,
'',
username,
'',
tiktokGiftAction,
data
);
}
async function TikTokSubscribe(data) {
if (!showTikTokSubs)
return;
// Set the text
const username = data.nickname;
const tiktokIcon = `<img src="icons/platforms/tiktok.png" class="platform"/>`;
// Render avatars
const avatarURL = 'icons/platforms/tiktok.png';
UpdateAlertBox(
'tiktok',
avatarURL,
`${username}`,
`subscribed on TikTok`,
'',
username,
'',
tiktokSubAction,
data
);
}
//////////////////////
@@ -1116,18 +1467,37 @@ function GetIntParam(paramName, defaultValue) {
return intValue;
}
async function GetAvatar(username) {
if (avatarMap.has(username)) {
console.debug(`Avatar found for ${username}. Retrieving from hash map.`)
return avatarMap.get(username);
async function GetAvatar(username, platform) {
// First, check if the username is hashed already
if (avatarMap.has(`${username}-${platform}`)) {
console.debug(`Avatar found for ${username} (${platform}). Retrieving from hash map.`)
return avatarMap.get(`${username}-${platform}`);
}
else {
console.debug(`No avatar found for ${username}. Retrieving from Decapi.`)
// If code reaches this point, the username hasn't been hashed, so retrieve avatar
switch (platform) {
case 'twitch':
{
console.debug(`No avatar found for ${username} (${platform}). Retrieving from Decapi.`)
let response = await fetch('https://decapi.me/twitch/avatar/' + username);
let data = await response.text()
avatarMap.set(username, data);
let data = await response.text();
avatarMap.set(`${username}-${platform}`, data);
return data;
}
case 'kick':
{
console.debug(`No avatar found for ${username} (${platform}). Retrieving from Kick.`)
let response = await fetch('https://kick.com/api/v2/channels/' + username);
console.log('https://kick.com/api/v2/channels/' + username)
let data = await response.json();
let avatarURL = data.user.profile_pic;
if (!avatarURL)
avatarURL = 'https://kick.com/img/default-profile-pictures/default2.jpeg';
avatarMap.set(`${username}-${platform}`, avatarURL);
return avatarURL;
}
}
}
function DecodeHTMLString(html) {
@@ -1340,6 +1710,54 @@ function UpdateAlertBox(platform, avatarURL, headerText, descriptionText, attrib
}
async function GetKickChatroomId(username) {
const url = `https://kick.com/api/v2/channels/${username}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const data = await response.json();
if (data.chatroom && data.chatroom.id) {
return data.chatroom.id;
} else {
throw new Error("Chatroom ID not found in response.");
}
} catch (error) {
console.error("Failed to fetch chatroom ID:", error.message);
return null;
}
}
async function GetKickSubBadges(username) {
const response = await fetch(`https://kick.com/api/v2/channels/${username}`);
const data = await response.json();
return data.subscriber_badges || [];
}
function GetKickBadgeURL(data) {
switch (data.type) {
case 'subscriber':
return CalculateKickSubBadge(data.count);
default:
return `icons/badges/kick-${data.type}.svg`;
}
}
function CalculateKickSubBadge(months) {
if (!Array.isArray(kickSubBadges)) return null;
// Filter for eligible badges, then get the one with the highest 'months'
const badge = kickSubBadges
.filter(b => b.months <= months)
.sort((a, b) => b.months - a.months)[0];
return badge?.badge_image?.src || `icons/badges/kick-subscriber.svg`;
}
////////////////////
// TEST FUNCTIONS //
////////////////////
@@ -1348,7 +1766,7 @@ async function testWidget()
{
UpdateAlertBox(
'twitch',
await GetAvatar('nutty'),
await GetAvatar('nutty', 'twitch'),
`nutty`,
`subscribed with Tier 3`,
'',
@@ -1382,31 +1800,3 @@ function SetConnectionStatus(connected) {
statusContainer.style.opacity = 1;
}
}
// let data = {
// cumulative_total: 77,
// id: "6616253944106387595",
// isTest: false,
// messageId: "00e4258c-a880-4f11-a93c-4734ee92199f",
// recipients: [
// {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
// ],
// sub_tier: "1000", // Usually "1000" = Tier 1
// systemMessage: "thetrickster1973 is gifting 10 Tier 1 Subs to nutty's community! They've gifted a total of 77 in the channel!",
// total: 10,
// user: {
// badges: [{}, {}, {}],
// color: "#1E90FF",
// id: "52175891",
// login: "thetrickster1973",
// monthsSubscribed: 14,
// name: "thetrickster1973",
// role: 2, // 2 - VIP
// subscribed: true,
// type: "twitch"
// }
// };
// TwitchGiftBomb(data);
//testWidget();
+103
View File
@@ -363,6 +363,109 @@
"showIf": "showYouTubeMemberships",
"group": "Which YouTube alerts do you want to see?"
},
{
"id": "kickUsername",
"label": "Kick Username",
"description": "",
"type": "text",
"defaultValue": "",
"group": "Which Kick alerts do you want to see?"
},
{
"id": "showKickSubs",
"label": "New Subscribers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick alerts do you want to see?"
},
{
"id": "kickSubAction",
"label": "",
"description": "Also run this Streamer.bot Action",
"type": "sb-actions",
"defaultValue": "",
"showIf": "showKickSubs",
"group": "Which Kick alerts do you want to see?"
},
{
"id": "showKickChannelPointRedemptions",
"label": "Channel Point Redemptions",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick alerts do you want to see?"
},
{
"id": "kickChannelPointRedemptionAction",
"label": "",
"description": "Also run this Streamer.bot Action",
"type": "sb-actions",
"defaultValue": "",
"showIf": "showKickChannelPointRedemptions",
"group": "Which Kick alerts do you want to see?"
},
{
"id": "showKickHosts",
"label": "Hosts",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "Which Kick alerts do you want to see?"
},
{
"id": "kickHostAction",
"label": "",
"description": "Also run this Streamer.bot Action",
"type": "sb-actions",
"defaultValue": "",
"showIf": "showKickHosts",
"group": "Which Kick alerts do you want to see?"
},
{
"id": "enableTikTokSupport",
"label": "Enable TikTok Support",
"description": "",
"type": "checkbox",
"defaultValue": false,
"group": "Which TikTok alerts do you want to see?"
},
{
"id": "showTikTokGifts",
"label": "Gifts",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok alerts do you want to see?"
},
{
"id": "tiktokGiftAction",
"label": "",
"description": "Also run this Streamer.bot Action",
"type": "sb-actions",
"defaultValue": "",
"showIf": "showTikTokGifts",
"group": "Which TikTok alerts do you want to see?"
},
{
"id": "showTikTokSubs",
"label": "New Subscribers",
"description": "",
"type": "checkbox",
"defaultValue": true,
"showIf": "enableTikTokSupport",
"group": "Which TikTok alerts do you want to see?"
},
{
"id": "tiktokSubAction",
"label": "",
"description": "Also run this Streamer.bot Action",
"type": "sb-actions",
"defaultValue": "",
"showIf": "showTikTokSubs",
"group": "Which TikTok alerts do you want to see?"
},
{
"id": "showStreamlabsDonations",
"label": "Streamlabs Tips",
+8
View File
@@ -307,6 +307,10 @@ html {
background: linear-gradient(#FF6C60BF, #FF0707BF) !important;
}
.kick {
background: linear-gradient(#53fc18BF, #005600BF) !important;
}
.streamlabs {
background: linear-gradient(#73dabbbf, #397765bf) !important;
}
@@ -331,6 +335,10 @@ html {
background: linear-gradient(#466edbbf, #1c56f5bf) !important;
}
.tiktok {
background: linear-gradient(#262626BF, #000000BF) !important;
}
.blank {
background: #adadad00 !important;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

+49
View File
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./style.css" />
<script type="text/javascript" src="https://unpkg.com/@streamerbot/client/dist/streamerbot-client.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3/build/global/luxon.min.js"></script>
</head>
<body>
<div id="background">
<div id="connect-box">
<div class="field">
<label for="ip">IP Address</label>
<input type="text" id="ip" name="ip" placeholder="127.0.0.1" value="127.0.0.1">
</div>
<div class="field">
<label for="port">Port</label>
<input type="text" id="port" name="port" placeholder="8080" value="8080">
</div>
<button id="connect-button" onclick="Connect()">Connect</button>
</div>
</div>
</body>
<template id="receipt-template">
<!-- Header -->
<div id="header">
<img id="avatar">
<div>
<div id="title"></div>
<div id="subtitle"></div>
</div>
</div>
<!-- Contents -->
<div id="content">
</div>
<!-- Footer -->
<div id="footer">
<div id="date"></div>
<img id="icon" src="icons/platforms/twitch.png">
</div>
</template>
<script src="./script.js"></script>
+804
View File
@@ -0,0 +1,804 @@
///////////////////
// PAGE ELEMENTS //
///////////////////
const headerEl = document.getElementById('header');
const contentEl = document.getElementById('content');
const footerEl = document.getElementById('footer');
const avatarEl = document.getElementById('avatar');
const titleEl = document.getElementById('title');
const subtitleEl = document.getElementById('subtitle');
const dateEl = document.getElementById('date');
//////////////////////
// GLOBAL VARIABLES //
//////////////////////
const avatarMap = new Map();
/////////////////////////
// STREAMER.BOT CLIENT //
/////////////////////////
// Check local storage
if (localStorage.getItem('sbServerAddress') === null)
localStorage.setItem('sbServerAddress', '127.0.0.1');
if (localStorage.getItem('sbServerPort') === null)
localStorage.setItem('sbServerPort', '8080');
document.getElementById('ip').value = localStorage.getItem('sbServerAddress');
document.getElementById('port').value = localStorage.getItem('sbServerPort');
let sbServerAddress = document.getElementById('ip').value;
let sbServerPort = document.getElementById('port').value;
let client = new StreamerbotClient({
host: sbServerAddress,
port: sbServerPort,
onConnect: (data) => {
console.log(`Streamer.bot successfully connected to ${sbServerAddress}:${sbServerPort}`)
console.debug(data);
SetConnectionState(true);
},
onDisconnect: () => {
console.error(`Streamer.bot disconnected from ${sbServerAddress}:${sbServerPort}`)
SetConnectionState(false);
}
});
client.on('General.Custom', (response) => {
console.debug(response.data);
CustomEvent(response.data);
})
////////////////////
// STREAM PRINTER //
////////////////////
async function CustomEvent(data) {
if (data.actionName != 'Printer Bot | Events')
return;
// Get a reference to the template
const template = document.getElementById('receipt-template');
// Create a new instance of the template
const instance = template.content.cloneNode(true);
// Get divs
const headerEl = instance.querySelector('#header');
const contentEl = instance.querySelector('#content');
const footerEl = instance.querySelector('#footer');
const avatarEl = instance.querySelector('#avatar');
const titleEl = instance.querySelector('#title');
const subtitleEl = instance.querySelector('#subtitle');
const iconEl = instance.querySelector('#icon');
const dateEl = instance.querySelector('#date');
// Set the main contents
switch (data.__source) {
// Twitch events
case ('TwitchCheer'):
{
avatarEl.src = await GetAvatar(data.userName, 'twitch');
titleEl.innerText = `${data.bits} BITS`;
subtitleEl.innerText = `${data.user}`;
const messageEl = document.createElement('div');
messageEl.innerHTML = data.message;
// Render emotes
for (i in data.emotes) {
const emoteElement = `<img src="${data.emotes[i].imageUrl}" class="emote"/>`;
const emoteName = EscapeRegExp(data.emotes[i].name);
let regexPattern = emoteName;
// Check if the emote name consists only of word characters (alphanumeric and underscore)
if (/^\w+$/.test(emoteName)) {
regexPattern = `\\b${emoteName}\\b`;
}
else {
// For non-word emotes, ensure they are surrounded by non-word characters or boundaries
regexPattern = `(?<=^|[^\\w])${emoteName}(?=$|[^\\w])`;
}
const regex = new RegExp(regexPattern, 'g');
messageEl.innerHTML = messageEl.innerHTML.replace(regex, emoteElement);
}
// Render cheermotes
for (i in data.cheerEmotes) {
const bits = data.cheerEmotes[i].bits;
const imageUrl = data.cheerEmotes[i].imageUrl;
const name = data.cheerEmotes[i].name;
const cheerEmoteElement = `<img src="${imageUrl}" class="emote"/>`;
const bitsElements = `<span class="bits">${bits}</span>`
messageEl.innerHTML = messageEl.innerHTML.replace(new RegExp(`\\b${name}${bits}\\b`, 'i'), cheerEmoteElement + bitsElements);
}
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'twitch');
}
break;
case ('TwitchSub'):
{
avatarEl.src = await GetAvatar(data.userName, 'twitch');
titleEl.innerText = `${data.tier} subscriber`;
subtitleEl.innerText = `${data.user}`;
const messageEl = document.createElement('div');
messageEl.innerHTML = '<b>First time subscriber!</b>';
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'twitch');
}
break;
case ('TwitchReSub'):
{
avatarEl.src = await GetAvatar(data.userName, 'twitch');
titleEl.innerText = `${data.tier} subscriber`;
subtitleEl.innerText = `${data.user}`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data.cumulative} months${data.monthStreak > 1 ? ' (' + data.monthStreak + ' in a row!)' : ''}</b>`;
if (data.messageStripped)
messageEl.innerHTML += `<br><br><i>${data.messageStripped}</i>`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'twitch');
}
break;
case ('TwitchGiftSub'):
{
// Don't put profile pictures for subs that come from Gift Bombs to avoid rate limits
if (data.fromGiftBomb)
//avatarEl.style.display = 'none';
return;
else
avatarEl.src = await GetAvatar(data.recipientUserName, 'twitch');
titleEl.innerText = `Gifted Sub`;
const messageEl = document.createElement('div');
messageEl.innerHTML += `<b>${data.recipientUser}</b><br>received a ${data.tier} sub from<br>`;
if (data.anonymous)
messageEl.innerHTML += `a mysterious admirer...`;
else
messageEl.innerHTML += `<b>${data.user}</b>!`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'twitch');
}
break;
case ('TwitchGiftBomb'):
{
avatarEl.src = await GetAvatar(data.userName, 'twitch');
titleEl.innerHTML = `${data.gifts} × Gifted Subs`;
subtitleEl.innerHTML += `✧*・゚✧ ${data.tier.toUpperCase()} ✧・゚*✧`;
if (data.anonymous)
subtitleEl.innerHTML += `<br>From a mystery person...`;
else
subtitleEl.innerHTML += `<br>${data.user}`;
const messageEl = document.createElement('div');
if (data.totalGifts > 1) {
messageEl.innerHTML = `They've gifted <b>${data.totalGifts} subs</b> in total!</br></br>`;
}
// Get a list of all recipient users
Object.keys(data)
.filter(key => /^gift\.recipientUser\d+$/.test(key))
.forEach((key, index) => {
const username = data[key];
messageEl.innerHTML += `${username}</br>`;
});
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'twitch');
}
break;
case ('TwitchRaid'):
{
avatarEl.src = await GetAvatar(data.userName, 'twitch');
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data.user}</b><br>is raiding with a party of<br><b>${data.viewers} viewers!</b>`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'twitch');
}
break;
// YouTube Events
case ('YouTubeNewSponsor'):
{
if (data.userProfileUrl)
avatarEl.src = data.userProfileUrl;
else
avatarEl.style.display = 'none';
titleEl.innerText = `${data.levelName}`;
subtitleEl.innerText = `${data.user}`;
contentEl.style.display = 'none';
// Set the platform icon
SetPlatformIcon(iconEl, 'youtube');
}
break;
case ('YouTubeGiftMembershipReceived'):
{
if (data.gifterProfileUrl)
avatarEl.src = data.gifterProfileUrl;
else
avatarEl.style.display = 'none';
titleEl.innerText = `Gifted Membership`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data.user}</b><br>received a membership from<br><b>${data.gifterUser}</b>!`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'youtube');
}
break;
case ('YouTubeSuperChat'):
{
if (data.userProfileUrl)
avatarEl.src = data.userProfileUrl;
else
avatarEl.style.display = 'none';
titleEl.style.fontSize = '2em';
titleEl.innerText = `${data.amount}`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data.user}</b><br>sent a Super Chat!`;
if (data.message)
messageEl.innerHTML += `<br><br><i>${data.message}</i>`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'youtube');
}
break;
case ('YouTubeSuperSticker'):
{
if (data.stickerImageUrl)
avatarEl.src = data.stickerImageUrl;
else
avatarEl.style.display = 'none';
titleEl.style.fontSize = '2em';
titleEl.innerText = `${data.amount}`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data.user}</b><br>sent a Super Sticker!`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'youtube');
}
break;
break;
// Kick Events
case ('KickSubscription'):
case ('KickResubscription'):
{
avatarEl.src = ConvertWEBPToPNG(await GetAvatar(data.user, 'kick'));
titleEl.innerText = `Subscriber`;
subtitleEl.innerText = `${data.user}`;
const messageEl = document.createElement('div');
if (data.duration > 1)
messageEl.innerHTML = `<b>${data.duration} months</b>`;
else
messageEl.innerHTML = '<b>First time subscriber!</b>';
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'kick');
}
break;
case ('KickGiftSubscription'):
{
avatarEl.src = ConvertWEBPToPNG(await GetAvatar(data["recipient.userLogin"], 'kick'));
titleEl.innerText = `Gifted Sub`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data["recipient.userName"]}</b><br>received a sub from<br><b>${data.user}</b>!`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'kick');
}
break;
case ('KickMassGiftSubscription'):
{
// There is only one sub, so use the same template for a single gifted sub
if ('recipient.userName' in data) {
avatarEl.src = ConvertWEBPToPNG(await GetAvatar(data["recipient.userLogin"], 'kick'));
titleEl.innerText = `Gifted Sub`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data["recipient.userName"]}</b><br>received a sub from<br><b>${data.user}</b>!`;
contentEl.appendChild(messageEl);
}
else {
avatarEl.src = ConvertWEBPToPNG(await GetAvatar(data.user, 'kick'));
// Calculate how many subs were gived
let maxIndex = -1;
for (const key in data) {
const match = key.match(/^recipient\.(\d+)\./);
if (match) {
const index = parseInt(match[1], 10);
if (index > maxIndex) {
maxIndex = index;
}
}
}
const totalGifts = maxIndex + 1;
titleEl.innerHTML = `${totalGifts} × Gifted Subs`;
subtitleEl.innerText = `${data.user}`;
const messageEl = document.createElement('div');
// Loop through each recipient and include it in the receipt
const recipients = {};
// Reconstruct recipient objects
for (const key in data) {
const match = key.match(/^recipient\.(\d+)\.(.+)$/);
if (match) {
const index = match[1];
const field = match[2];
if (!recipients[index]) {
recipients[index] = {};
}
recipients[index][field] = data[key];
}
}
// Loop through and print userName
for (const index in recipients) {
messageEl.innerHTML += `${recipients[index].userName}<br>`;
}
contentEl.appendChild(messageEl);
}
// Set the platform icon
SetPlatformIcon(iconEl, 'kick');
}
break;
// StreamElements Events
case ('StreamElementsTip'):
{
const avatarURL = await GetAvatar(data.tipUsername, 'twitch');
if (IsValidUrl(avatarURL))
avatarEl.src = avatarURL;
else
avatarEl.style.display = 'none'
titleEl.style.fontSize = '2em';
titleEl.innerText = FormatCurrency(data.tipAmount, data.tipCurrency);
subtitleEl.innerText = `${data.tipUsername}`;
if (data.tipMessage) {
const messageEl = document.createElement('div');
messageEl.innerHTML = `<i>${data.tipMessage}</i>`;
contentEl.appendChild(messageEl);
}
else {
contentEl.style.display = 'none';
}
}
break;
// Streamlabs Events
case ('StreamlabsDonation'):
{
const avatarURL = await GetAvatar(data.donationFrom, 'twitch');
if (IsValidUrl(avatarURL))
avatarEl.src = avatarURL;
else
avatarEl.style.display = 'none'
titleEl.style.fontSize = '2em';
titleEl.innerText = data.donationFormattedAmount;
subtitleEl.innerText = `${data.donationFrom}`;
if (data.donationMessage) {
const messageEl = document.createElement('div');
messageEl.innerHTML = `<i>${data.donationMessage}</i>`;
contentEl.appendChild(messageEl);
}
else {
contentEl.style.display = 'none';
}
}
break;
// Fourthwall Events
case ('FourthwallDonation'):
{
const avatarURL = await GetAvatar(data["fw.username"], 'twitch');
if (IsValidUrl(avatarURL))
avatarEl.src = avatarURL;
else
avatarEl.style.display = 'none'
titleEl.style.fontSize = '2em';
titleEl.innerText = FormatCurrency(data["fw.amount"], data["fw.currency"]);
if (data["fw.username"])
subtitleEl.innerText = `${data["fw.username"]}`;
else if (data["fw.email"])
subtitleEl.innerText = `${data["fw.email"]}`;
if (data["fw.message"]) {
const messageEl = document.createElement('div');
messageEl.innerHTML = `<i>${data["fw.message"]}</i>`;
contentEl.appendChild(messageEl);
}
else {
contentEl.style.display = 'none';
}
}
break;
// case ('FourthwallGiftPurchase'):
// break;
case ('FourthwallOrderPlaced'):
{
// Only print non-free orders
if (data["fw.total"] <= 0)
return;
const avatarURL = await GetAvatar(data["fw.username"], 'twitch');
if (IsValidUrl(avatarURL))
avatarEl.src = avatarURL;
else
avatarEl.style.display = 'none'
titleEl.style.fontSize = '2em';
titleEl.innerText = FormatCurrency(data["fw.total"], data["fw.currency"]);
if (data["fw.username"])
subtitleEl.innerText = `${data["fw.username"]}`;
else if (data["fw.email"])
subtitleEl.innerText = `${data["fw.email"]}`;
// Compile a list of all items bought
const variants = [];
// Iterate through all keys in the data object
for (const key in data) {
const match = key.match(/^fw\.variants\[(\d+)\]\.(\w+)$/);
if (match) {
const index = Number(match[1]);
const field = match[2];
// Make sure the array slot exists
if (!variants[index]) {
variants[index] = {};
}
// Assign the field to the appropriate variant object
variants[index][field] = data[key];
}
}
// Print each item on the receipt
const messageEl = document.createElement('div');
variants.forEach((variant, i) => {
messageEl.innerHTML += `${variant.quantity} × ${variant.name}<br>`;
});
messageEl.style.textAlign = 'left';
// Check if they left a custom message
let customMessageEl = document.createElement('div');
const customMessage = data["fw.statmessageus"];
if (customMessage)
{
const txt = document.createElement("textarea");
txt.innerHTML = customMessage;
customMessageEl.innerHTML += `<br><i>${txt.value}</i>`;
}
// Add a cute thank you message because you're uwu like that
const thankYouEl = document.createElement('div');
thankYouEl.innerHTML += `<br><b>Thank you for your purchase!</b>`;
contentEl.appendChild(messageEl);
contentEl.appendChild(customMessageEl);
contentEl.appendChild(thankYouEl);
}
break;
case ('FourthwallSubscriptionPurchased'):
{
const avatarURL = await GetAvatar(data["fw.nickname"], 'twitch');
if (IsValidUrl(avatarURL))
avatarEl.src = avatarURL;
else
avatarEl.style.display = 'none'
titleEl.innerText = `New Member`;
subtitleEl.innerHTML = `${data["fw.nickname"]}`;
const messageEl = document.createElement('div');
messageEl.innerHTML = `Thanks for joining at the <b>${FormatCurrency(data["fw.amount"], data["fw.currency"])}</b> tier!`;
contentEl.appendChild(messageEl);
}
break;
// Custom Code Events
case ('CustomCodeEvent'):
{
switch (data.triggerCustomCodeEventName) {
case ('kickIncomingRaid'):
{
avatarEl.src = ConvertWEBPToPNG(await GetAvatar(data.user, 'kick'));
const messageEl = document.createElement('div');
messageEl.innerHTML = `<b>${data.user}</b><br>is hosting with a party of<br><b>${data.viewers} viewers!</b>`;
contentEl.appendChild(messageEl);
// Set the platform icon
SetPlatformIcon(iconEl, 'kick');
}
break;
}
}
break;
// Don't print any event not excplicitly listed above
default:
return;
}
// Set the timestamp
const { DateTime } = luxon;
const now = DateTime.local();
const formatted = now.toFormat("cccc, LLLL d',' yyyy HH:mm:ss");
// Add ordinal suffix manually
function addOrdinal(n) {
if (n >= 11 && n <= 13) return 'th';
switch (n % 10) {
case 1: return 'st';
case 2: return 'nd';
case 3: return 'rd';
default: return 'th';
}
}
const day = now.day;
const ordinal = addOrdinal(day);
const fullFormatted = now.toFormat(`cccc, LLLL '${day}${ordinal}' yyyy HH:mm:ss`);
dateEl.textContent = fullFormatted;
// Send it to the print routine!
const receiptHTML = await GetRenderedHTML(instance);
console.log(receiptHTML);
client.doAction({ name: 'Printer Bot | Print Routine' }, {
receiptHTML: receiptHTML,
isTest: data.isTest
});
}
//////////////////////
// HELPER FUNCTIONS //
//////////////////////
async function GetAvatar(username, platform) {
// First, check if the username is hashed already
if (avatarMap.has(`${username}-${platform}`)) {
console.debug(`Avatar found for ${username} (${platform}). Retrieving from hash map.`)
return avatarMap.get(`${username}-${platform}`);
}
// If code reaches this point, the username hasn't been hashed, so retrieve avatar
switch (platform) {
case 'twitch':
{
console.debug(`No avatar found for ${username} (${platform}). Retrieving from Decapi.`)
let response = await fetch('https://decapi.me/twitch/avatar/' + username);
let data = await response.text();
avatarMap.set(`${username}-${platform}`, data);
return data;
}
case 'kick':
{
console.debug(`No avatar found for ${username} (${platform}). Retrieving from Kick.`)
try {
let response = await fetch('https://kick.com/api/v2/channels/' + username);
console.log('https://kick.com/api/v2/channels/' + username)
let data = await response.json();
let avatarURL = data.user.profile_pic;
if (!avatarURL)
avatarURL = 'https://kick.com/img/default-profile-pictures/default2.jpeg';
avatarMap.set(`${username}-${platform}`, avatarURL);
return avatarURL;
}
catch (error) {
console.debug(error);
return 'https://kick.com/img/default-profile-pictures/default2.jpeg';
}
}
}
}
async function GetRenderedHTML(fragment) {
if (!(fragment instanceof DocumentFragment)) {
throw new Error('Argument must be a DocumentFragment');
}
// Filter out comment nodes from fragment content
const nodes = Array.from(fragment.childNodes).filter(
node => node.nodeType !== Node.COMMENT_NODE
);
const bodyContent = nodes
.map(node => node.outerHTML || node.textContent)
.join('');
// Get inline <style> contents
const inlineStyles = Array.from(document.querySelectorAll('style'))
.map(style => style.textContent)
.join('\n');
// Get all external stylesheet URLs
const linkHrefs = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
.map(link => link.href);
// Fetch external CSS contents
const externalCSSContents = await Promise.all(
linkHrefs.map(async href => {
try {
const res = await fetch(href);
if (!res.ok) throw new Error(`Failed to load CSS from ${href}`);
return await res.text();
} catch {
console.warn(`Could not fetch CSS from ${href}`);
return '';
}
})
);
// Combine all CSS into one string
const combinedCSS = inlineStyles + '\n' + externalCSSContents.join('\n');
// Build the full standalone HTML string
const fullHTML = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Exported Page</title>
<style>${combinedCSS}</style>
</head>
<body>
${bodyContent}
</body>
</html>`.trim();
return fullHTML;
}
function ConvertWEBPToPNG(URL) {
return `https://images.weserv.nl/?url=${URL}&output=png`;
}
function FormatCurrency(amount, currency) {
const isISOCode = /^[A-Z]{3}$/.test(currency);
if (isISOCode) {
try {
return new Intl.NumberFormat(undefined, {
style: 'currency',
currency: currency,
currencyDisplay: 'symbol',
}).format(amount);
} catch {
return `${amount.toFixed(2)} ${currency}`;
}
}
// Handle some common symbols that go before the number
const symbolsBefore = ['$', '€', '£', '¥', '₹'];
if (symbolsBefore.includes(currency)) {
return `${currency}${amount.toFixed(2)}`;
}
// Otherwise default to appending after
return `${amount.toFixed(2)} ${currency}`;
}
function IsValidUrl(string) {
try {
new URL(string);
return true;
} catch {
return false;
}
}
function EscapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
function SetPlatformIcon(el, platform) {
// Set the platform icon
let baseURL = window.location.href;
baseURL = baseURL.replace(/index\.html$/i, '');
el.src = `${baseURL}/icons/platforms/${platform}.png`;
}
///////////////////////////////////
// STREAMER.BOT WEBSOCKET STATUS //
///////////////////////////////////
function SetConnectionState(isConnected) {
if (isConnected) {
document.getElementById('ip').disabled = true;
document.getElementById('port').disabled = true;
document.getElementById('connect-button').style.backgroundColor = '#e43b3b';
document.getElementById('connect-button').innerText = 'Disconnect';
localStorage.setItem('sbServerAddress', document.getElementById('ip').value);
localStorage.setItem('sbServerPort', document.getElementById('port').value);
}
else {
document.getElementById('ip').disabled = false;
document.getElementById('port').disabled = false;
document.getElementById('connect-button').style.backgroundColor = '#3be477';
document.getElementById('connect-button').innerText = 'Connect';
}
}
function Connect() {
if (document.getElementById('ip').disabled)
client.disconnect();
else
{
client.options.host = document.getElementById('ip').value;
client.options.port = document.getElementById('port').value;
client.connect();
}
}
+127
View File
@@ -0,0 +1,127 @@
body {
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
}
#background {
color: white;
background-color: #282828;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
label {
font-weight: 600;
}
button {
font-size: 1em;
font-weight: 500;
background-color: #3be477;
color: white;
opacity: 0.8;
border-width: 0;
border-radius: 0.25em;
padding: 0.25em 0.5em;
width: 100%;
}
button:hover {
opacity: 1;
cursor: pointer;
}
button:disabled {
opacity: 0.5;
cursor: inherit;
}
input {
border-radius: 0.5em;
width: calc(100% - 20px);
margin: 10px 0px;
padding: 10px 10px;
background-color: #ffffff05;
border-width: 0px;
color: white;
font-size: 1em;
}
input:disabled {
opacity: 0.5;
}
textarea:focus,
input:focus {
outline: none;
}
#connect-box {
font-size: 24px;
background-color: #181818;
text-align: left;
display: flex;
flex-direction: column;
gap: 1em;
padding: 2em;
border-radius: 0.5em;
}
.field {
display: flex;
flex-direction: column;
}
#header {}
#avatar {
width: 6em;
height: 6em;
border-radius: 50%;
object-fit: cover;
}
#title {
font-weight: 900;
font-size: 1.5em;
text-transform: uppercase;
}
#subtitle {
font-weight: 700;
font-size: 1.2em;
}
#attribute {
padding: 0.5em 0em;
}
#content {
/* border-top: 1px solid black;
border-bottom: 1px solid black;
border-left: none;
border-right: none;
margin: 1em 0em; */
padding: 0.5em 0em;
}
#icon {
height: 1em;
}
#date {
margin: 0.5em 0em;
font-size: 0.7em;
text-transform: uppercase;
}
#footer {}
.emote {
height: 1em;
}