convert to sugoi web services

This commit is contained in:
josephsmendoza
2023-07-14 19:05:49 -07:00
parent a88a096af8
commit aca8b21945
17 changed files with 370 additions and 1131 deletions
+18 -59
View File
@@ -1,65 +1,24 @@
<h1 align="center">Improved Twitch Subathon Countdown</h1>
<p align="center">This is a Twitch Subathon Countdown originally by JayexDesigns.</p>
<p align="center">The CSS Code is updated by Johnnycyan. The JS Files are updated by leabdd and danilotitato to add functionalities.</p>
<br/>
# Improved Twitch Subathon Countdown
This is a Twitch Subathon Countdown originally by JayexDesigns.
<h2>Description</h2>
<p>A subathon timer that increases when someone subscribes, donates money, donates bits or purchases streamloots chests.</p>
<p>Added is a Timer that gets saved (in localStorage) and can be reset.</p>
<p>When you open the index.html you get to chose between "Reset" or "Start".</p>
<br/>
The CSS Code is updated by Johnnycyan.
<h2>Shortcuts</h2>
<p>To pause the Timer: CTRL+ALT+P</p>
<p>To enable/disable Happy Hour: CTRL+ALT+H</p>
<p>To enable/disable Random Hour: CTRL+ALT+R</p>
<p>Shortcuts can be changed in config.js</p>
<br/>
The JS Files have been re-written by SugoiDogo.
<h2>Preview</h2>
<h3 align="center"><img width="90%" src="./preview.webp"></h3>
<br/>
## Description
A subathon timer that increases when someone subscribes, donates to your charity, donates bits, follows or raids.
<h2>Usage</h2>
<p>After cloning this repository you'll have to edit the "config.js" file with any plain text editor, then change the value of the variables according to what you want:</p>
## Usage
Go to the [config page](https://sugoidogo.github.io/twitch-subathon-countdown/config.html), authorize the timer, and configure to your liking.
<h3>Twitch</h3>
<p>To get your Subs and Bits you just have to add your Channel Name.</p>
## Support
See [my github profile](https://github.com/sugoidogo)
<h3>Streamlabs Token</h3>
<p>To get the token you'll have to go to the API settings tab of your <a href="https://streamlabs.com/dashboard#/settings/api-settings">Streamlabs dashboard</a> then click on API Tokens, copy your socket API token and finally paste the code on the "streamlabs_token" variable in the "config.js" file.</p>
<img src="./auth_streamlabs.png">
<h3>StreamElements Token</h3>
<p>To get the token you'll have to go to your <a href="https://streamelements.com/dashboard/account/channels">StreamElements channels dashboard</a>, then click on show secrets, copy the Overlay token and paste the code on the "streamelements_token" variable in the "config.js" file.</p>
<img src="./auth_streamelements.png">
<h3>Streamloots Token</h3>
<p>To get the token you'll have to go to your Streamloots creator dashboard, then to alerts & widgets, then go to alerts, then copy the last part of the alerts url and finally paste the code on the "streamloots_token" variable in the "config.js" file.</p>
<img src="./auth_streamloots.png">
<h3>Initial Time</h3>
<p>You can set the initial time of the timer by setting the hours, minutes and seconds to be added when the timer is created.</p>
<h3>Random Happy Hour</h3>
<p>If this is true, you can turn enable Happy or Random Hour with the Shortcut. If it is false, the feature is disabled and cant be enabled.</p>
<p>You have to set the Happy Hour time values manually.</p>
<p>Random Hour has a bell curve which can be edited in the config. It multiplies the normal time by a random amount for each sub.</p>
<p>If enabled randHappy it can randomly activate the Happy Hour. It will last for one hour, but can be turned off manually with the Shortkey.</p>
<p>In the Config you can set a specific Date and Time. At that a Happy Hour will happen. scheduleHappy needs to be turned on.</p>
<h3>Bulk</h3>
<p>This enables that multiple Subs get added to the timer at once.</p>
<p>For Example when a user gifts 10 Subs.</p>
<h3>Full control on what to use</h3>
<p>You can enable and disable if you want to add time for Subscriptions, Bits, Donations or Chests.</p>
<h3>Other Values</h3>
<p>Change the other values to set the amount of seconds that will be added for the subscriptions, donations... And the minimum donation amounts to trigger the countdown increase.</p>
<h3>Add To OBS</h3>
<p>To add it to OBS you just have to drag the "index.html" file to the sources section or add a browser source that points to the "index.html" file.</p>
<h2>Known Problems</h2>
<p2>Only Streamelements Token in config.js does not add time for Subscriptions. Emulate with the Overlay Editor does work.</p>
## Missing Features
The original version of this overlay included support for events from StreamElements, StreamLabs, and Streamloots,
and the version this is forked from included additional features by leabdd and danilotitato,
such as Random Happy Hour and Bulk Events.
This fork is an almost total re-write of the core JS code behind the timer,
so those features will also have to be re-written to be included in this fork.
Future versions of this timer will include a plug-in loader,
allowing additional features and event sources to be added without additional forking.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

-80
View File
@@ -1,80 +0,0 @@
// Happy Hour
var happy_hour = true // set to true or false, to enable/disable the happy hours function
var random_hour = true
var randHappy = false // only works if happy_hour = true
var scheduleHappy = false // only works if happy_hour = true
// needs scheduleHappy to be true
var scheduleHappyDay = 5 // Weekdays from 0 to 6 from Sunday to Saturday | Default 5 is Friday
var scheduleHappyHour = 18 // Hours in 24 format UTC Time | Default 18 is 20:00 CEST
// Bulk (if you want multiple subs to add to a big sum or each alone)
var bulk_enabled = false
//Shortcuts
var pauseShort = "ctrl+alt+p"
var happyHourShort = "ctrl+alt+h"
var randomHourShort = "ctrl+alt+r"
// Login Data
var twitch_channel_name = ""
var streamlabs_token = ""
var streamelements_token = ""
var streamloots_token = ""
// Initial Counter Config
var initialHoursConfig = 2
var initialMinutesConfig = 0
var initialSecondsConfig = 0
// Sync Time, amount of seconds it waits after the timer hit 0 to stop it. Useful for last Second Subs.
var syncTime = 2 // Default 2 seconds.
// Enable/Disable Subs, Bits, Donations, Chests
var subEnable = true
var bitEnable = false
var donationEnable = false
var chestEnable = false
// General Twitch, Streamlabs And StreamElements Config
var seconds_added_per_sub_prime = 60
var seconds_added_per_sub_tier1 = 60
var seconds_added_per_sub_tier2 = 180
var seconds_added_per_sub_tier3 = 480
var seconds_added_per_resub_prime = 60
var seconds_added_per_resub_tier1 = 60
var seconds_added_per_resub_tier2 = 180
var seconds_added_per_resub_tier3 = 480
var seconds_added_per_giftsub_tier1 = 60
var seconds_added_per_giftsub_tier2 = 180
var seconds_added_per_giftsub_tier3 = 480
var min_amount_of_bits = 50
var seconds_added_per_bits = 30
// Streamlabs And StreamElements Config
var min_donation_amount = 1
var seconds_added_per_donation = 30
// Streamloots Config
var min_amount_of_chests = 1
var seconds_added_per_chests = 30
// General Twitch, Streamlabs And StreamElements Config for Happy Hour
var factor_t1 = 1.5
var factor_t2 = 1.75
var factor_t3 = 2.0
var factor_bits = 1.5
var factor_donations = 1.5
// General Twitch, Streamlabs And StreamElements Config for Random Hour
// [min, max, skew]
var range_t1 = [0.1, 3, 1]
var range_t2 = [0.1, 3, 2]
var range_t3 = [0.1, 3, 3]
var range_bits = [0.1, 3, 1]
var range_donations = [0.1, 3, 1]
+21 -171
View File
@@ -2,7 +2,8 @@
font-family: 'Ubuntu', sans-serif !important;
}
body, body * {
body,
body * {
padding: 0;
margin: 0;
font-family: 'Manrope', sans-serif;
@@ -13,16 +14,12 @@ body {
height: 100vh;
}
:root {
--background-color: #A020F0;
--shadow-color: #00000090;
--text-color: #FFFFFF;
}
#container {
position: absolute;
top: 50%;
@@ -30,63 +27,17 @@ body {
transform: translate(-50%, -50%);
/*background-color: var(--background-color);*/
background-image:
-webkit-linear-gradient(-45deg,
transparent 33%, rgba(0, 0, 0, .1) 33%,
rgba(0,0, 0, .1) 66%, transparent 66%),
-webkit-linear-gradient(top,
rgba(255, 255, 255, .25),
rgba(0, 0, 0, .25)),
-webkit-linear-gradient(left, #0074cc, #a700cc);
-webkit-linear-gradient(-45deg,
transparent 33%, rgba(0, 0, 0, .1) 33%,
rgba(0, 0, 0, .1) 66%, transparent 66%),
-webkit-linear-gradient(top,
rgba(255, 255, 255, .25),
rgba(0, 0, 0, .25)),
-webkit-linear-gradient(left, #0074cc, #a700cc);
box-shadow: 0 0 1.25rem 0.5rem var(--shadow-color);
width: 500px;
height: 180px;
border-radius: 30px;
visibility: hidden;
}
#SpecialHourText {
opacity: 0;
font-size: 2.5rem;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
/* #HappyHourDeactive {
visibility: hidden;
font-size: 3rem;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
#HappyHourError {
visibility: hidden;
font-size: 3rem;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
} */
#SpecialHourHTML {
top:-200px;
position: relative;
}
#startPage {
position: absolute;
align-items: center;
top: 75%;
left: 50%;
transform: translate(-50%, -50%);
/*background-color: var(--background-color);*/
width: 150px;
height: 50px;
border-radius: 30px;
background:none;
}
#textDiv {
@@ -96,79 +47,6 @@ body {
height: 100%;
}
.button {
border-radius: 30px;
box-shadow: 0 0 1.25rem 0.5rem var(--shadow-color);
justify-content: center;
align-items: center;
display: flex;
height: 100%;
width: 150px;
height: 50px;
top: 50%;
left: 50%;
color:white;
/*background-image:
-webkit-linear-gradient(-45deg,
transparent 33%, rgba(0, 0, 0, .1) 33%,
rgba(0,0, 0, .1) 66%, transparent 66%),
-webkit-linear-gradient(top,
rgba(255, 255, 255, .25),
rgba(0, 0, 0, .25)),
-webkit-linear-gradient(left, #0074cc, #a700cc);*/
background:none;
}
#firstButtonDiv, #secondButtonDiv {
background-image:
-webkit-linear-gradient(-45deg,
transparent 33%, rgba(0, 0, 0, .1) 33%,
rgba(0,0, 0, .1) 66%, transparent 66%),
-webkit-linear-gradient(top,
rgba(255, 255, 255, .25),
rgba(0, 0, 0, .25)),
-webkit-linear-gradient(left, #0074cc, #a700cc);
width: 150px;
height: 50px;
display: inline-block;
}
#firstButtonDiv:hover, #secondButtonDiv:hover {
background-image:
-webkit-linear-gradient(-45deg,
transparent 33%, rgba(0, 0, 0, .1) 33%,
rgba(0,0, 0, .1) 66%, transparent 66%),
-webkit-linear-gradient(top,
rgba(255, 255, 255, .25),
rgba(0, 0, 0, .25)),
-webkit-linear-gradient(left, #0061ac, #8e00ad)!important;
}
#firstButtonDiv, #secondButtonDiv {
border-radius: 30px;
justify-content: center;
align-items: center;
display: flex;
height: 100%;
width: 150px;
height: 50px;
top: 50%;
left: 50%;
color:white;
background-image:
-webkit-linear-gradient(-45deg,
transparent 33%, rgba(0, 0, 0, .1) 33%,
rgba(0,0, 0, .1) 66%, transparent 66%),
-webkit-linear-gradient(top,
rgba(255, 255, 255, .25),
rgba(0, 0, 0, .25)),
-webkit-linear-gradient(left, #0074cc, #a700cc);
}
#secondButtonDiv {
margin-top:10px;
}
p {
color: var(--text-color);
font-size: 5.8rem;
@@ -176,54 +54,26 @@ p {
text-shadow: 0px 0px 10px var(--shadow-color);
}
img {
position: absolute;
top: 0;
bottom: 0;
}
canvas {
width: 500px;
height: 180px;
border-radius: 20px;
position: absolute;
top: 0;
bottom: 0;
z-index: 2;
}
.addedTime {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0;
display: none;
z-index: 5;
font-weight: 400;
font-size: 3.25rem;
/*color: var(--background-color);*/
color:white;
transition: top 1s ease-out, opacity .5s ease-out;
color: white;
animation: slidefadeout 2s ease-out;
opacity: 0;
}
.gold {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0;
display: none;
z-index: 5;
font-weight: 400;
font-size: 3.25rem;
/*color: var(--background-color);*/
color:gold;
background: linear-gradient(to right, #BF953F, #FCF6BA, #B38728, #FBF5B7, #AA771C);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
transition: top 1s ease-out, opacity .5s ease-out;
text-shadow:none !important;
@keyframes slidefadeout {
0% {
opacity: 1;
top: 50%;
}
100% {
opacity: 0;
top: 25%;
}
}
-30
View File
@@ -11,41 +11,11 @@
<title>Subathon Timer</title>
</head>
<body>
<div id="startPage">
<div id="firstButtonDiv">
<button id="resetBtn" class="button">Reset</button>
</div>
<div id="secondButtonDiv">
<button id="startBtn" class="button">Start</button>
</div>
</div>
<div id="container" class="timer">
<div id="textDiv">
<p id="timeText">00:00:00</p>
</div>
<div id="SpecialHourHTML">
<p id="SpecialHourText">Happy Hour Activated!</p>
<!-- <p id="SpecialHourDeactive">Happy Hour Deactivated :(</p>
<p id="SpecialHourError">Happy Hour not available D:</p> -->
</div>
<canvas id="canvas"></canvas>
</div>
<script src="js/lib/mousetrap.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js' type='text/javascript'></script>
<script src='js/lib/tmi.min.js'></script>
<script src="js/lib/generalFunc.js"></script>
<script src="js/lib/timeFunc.js"></script>
<script src="config.js"></script>
<script src="js/script.js"></script>
<script src="js/api/twitch.js"></script>
<!-- <script src="js/api/streamlabs.js"></script> -->
<script src="js/api/streamelements.js"></script>
<!-- <script src="js/api/streamloots.js"></script> -->
</body>
</html>
-128
View File
@@ -1,128 +0,0 @@
if (streamelements_token !== "") {
var streamElementsSocket;
const streamElementsConnect = () => {
streamElementsSocket = undefined;
streamElementsSocket = io('https://realtime.streamelements.com', {
transports: ['websocket']
});
streamElementsSocket.on('connect', onConnect);
streamElementsSocket.on('disconnect', onDisconnect);
streamElementsSocket.on('authenticated', onAuthenticated);
streamElementsSocket.on('unauthorized', console.error);
streamElementsSocket.on('event:test', onEvent);
streamElementsSocket.on('event', onEvent);
streamElementsSocket.on('event:update', onEvent);
streamElementsSocket.on('event:reset', onEvent);
function onConnect() {
logMessage("StreamElements", "Socket Connected");
streamElementsSocket.emit('authenticate', {method: 'apikey', token: streamelements_token});
}
function onDisconnect() {
logMessage("StreamElements", "Socket Disconnected");
streamElementsConnect();
}
function onAuthenticated(data) {
const {
channelId
} = data;
logMessage("StreamElements", `Channel ${channelId} Connected`);
}
function onEvent(data) {
logObject("StreamElements", data);
if (!countdownEnded) {
let factor_t1_local = (happy_hour_active ? factor_t1 : 1) * (random_hour_active ? randomBellSkew(...range_t1): 1);
let factor_t2_local = (happy_hour_active ? factor_t2 : 1) * (random_hour_active ? randomBellSkew(...range_t2): 1);
let factor_t3_local = (happy_hour_active ? factor_t3 : 1) * (random_hour_active ? randomBellSkew(...range_t3): 1);
let factor_bits_local = (happy_hour_active ? factor_bits : 1) * (random_hour_active ? randomBellSkew(...range_bits): 1);
let factor_donations_local = (happy_hour_active ? factor_donations : 1) * (random_hour_active ? randomBellSkew(...range_donations): 1);
if ((data['listener'] === "subscriber-latest") && subEnable) {
if (data['event']['gifted'] || data['event']['bulkGifted']) {
let amount;
if (data['event']['gifted']) amount = 1;
else amount = data['event']['amount'];
if (data['event']['tier'] == "prime" || data['event']['tier'] == "1000") {
addTime(endingTime, seconds_added_per_giftsub_tier1 * amount * factor_t1_local);
logMessage("StreamElements", `Added ${seconds_added_per_giftsub_tier1 * amount * factor_t1_local} Seconds Because ${data['event']['name']} Gifted ${amount} Tier 1 Subs`);
}
else if (data['event']['tier'] == "2000") {
addTime(endingTime, seconds_added_per_giftsub_tier2 * amount * factor_t2_local);
logMessage("StreamElements", `Added ${seconds_added_per_giftsub_tier2 * amount * factor_t2_local} Seconds Because ${data['event']['name']} Gifted ${amount} Tier 2 Subs`);
}
else if (data['event']['tier'] == "3000") {
addTime(endingTime, seconds_added_per_giftsub_tier3 * amount * factor_t3_local);
logMessage("StreamElements", `Added ${seconds_added_per_giftsub_tier3 * amount * factor_t3_local} Seconds Because ${data['event']['name']} Gifted ${amount} Tier 3 Subs`);
}
}
else if (data['event']['amount'] != "1") {
if (data['event']['tier'] == "prime") {
addTime(endingTime, seconds_added_per_resub_prime * factor_t1_local);
logMessage("StreamElements", `Added ${seconds_added_per_resub_prime * factor_t1_local} Seconds Because ${data['event']['name']} ReSubscribed With Prime`);
}
else if (data['event']['tier'] == "1000") {
addTime(endingTime, seconds_added_per_resub_tier1 * factor_t1_local);
logMessage("StreamElements", `Added ${seconds_added_per_resub_tier1 * factor_t1_local} Seconds Because ${data['event']['name']} ReSubscribed With Tier 1`);
}
else if (data['event']['tier'] == "2000") {
addTime(endingTime, seconds_added_per_resub_tier2 * factor_t2_local);
logMessage("StreamElements", `Added ${seconds_added_per_resub_tier2 * factor_t2_local} Seconds Because ${data['event']['name']} ReSubscribed With Tier 2`);
}
else if (data['event']['tier'] == "3000") {
addTime(endingTime, seconds_added_per_resub_tier3 * factor_t3_local);
logMessage("StreamElements", `Added ${seconds_added_per_resub_tier3 * factor_t3_local} Seconds Because ${data['event']['name']} ReSubscribed With Tier 3`);
}
}
else {
if (data['event']['tier'] == "prime") {
addTime(endingTime, seconds_added_per_sub_prime * factor_t1_local);
logMessage("StreamElements", `Added ${seconds_added_per_sub_prime * factor_t1_local} Seconds Because ${data['event']['name']} Subscribed With Prime`);
}
else if (data['event']['tier'] == "1000") {
addTime(endingTime, seconds_added_per_sub_tier1 * factor_t1_local);
logMessage("StreamElements", `Added ${seconds_added_per_sub_tier1 * factor_t1_local} Seconds Because ${data['event']['name']} Subscribed With Tier 1`);
}
else if (data['event']['tier'] == "2000") {
addTime(endingTime, seconds_added_per_sub_tier2 * factor_t2_local);
logMessage("StreamElements", `Added ${seconds_added_per_sub_tier2 * factor_t2_local} Seconds Because ${data['event']['name']} Subscribed With Tier 2`);
}
else if (data['event']['tier'] == "3000") {
addTime(endingTime, seconds_added_per_sub_tier3 * factor_t3_local);
logMessage("StreamElements", `Added ${seconds_added_per_sub_tier3 * factor_t3_local} Seconds Because ${data['event']['name']} Subscribed With Tier 3`);
}
}
if (!users.includes(data['event']['name'])) {
users.push(data['event']['name']);
}
}
else if ((data['listener'] === "cheer-latest") && bitEnable) {
if (data['event']['amount'] >= min_amount_of_bits) {
let times = Math.floor(data['event']['amount']/min_amount_of_bits);
addTime(endingTime, seconds_added_per_bits * times * factor_bits_local);
logMessage("StreamElements", `Added ${seconds_added_per_bits * times * factor_bits_local} Seconds Because ${data['event']['name']} Donated ${data['event']['amount']} Bits`);
if (!users.includes(data['event']['name'])) {
users.push(data['event']['name']);
}
}
}
else if ((data['listener'] === "tip-latest") && donationEnable) {
if (data['event']['amount'] >= min_donation_amount) {
let times = Math.floor(data['event']['amount']/min_donation_amount);
addTime(endingTime, seconds_added_per_donation * times * factor_donations_local);
logMessage("StreamElements", `Added ${seconds_added_per_donation * times * factor_donations_local} Seconds Because ${data['event']['name']} Donated ${data['event']['amount']}$`);
if (!users.includes(data['event']['name'])) {
users.push(data['event']['name']);
}
}
}
}
}
}
streamElementsConnect();
}
-118
View File
@@ -1,118 +0,0 @@
if (streamlabs_token !== "") {
socket = io(`https://sockets.streamlabs.com?token=${streamlabs_token}`, {transports: ['websocket']});
socket.on("connect", () => {
logMessage("Streamlabs", "Socket Connected");
});
socket.on("event", (event) => {
logObject("Streamlabs", event);
let factor_t1_local = (happy_hour_active ? factor_t1 : 1) * (random_hour_active ? randomBellSkew(...range_t1): 1);
let factor_t2_local = (happy_hour_active ? factor_t2 : 1) * (random_hour_active ? randomBellSkew(...range_t2): 1);
let factor_t3_local = (happy_hour_active ? factor_t3 : 1) * (random_hour_active ? randomBellSkew(...range_t3): 1);
let factor_bits_local = (happy_hour_active ? factor_bits : 1) * (random_hour_active ? randomBellSkew(...range_bits): 1);
let factor_donations_local = (happy_hour_active ? factor_donations : 1) * (random_hour_active ? randomBellSkew(...range_donations): 1);
if (event.type == "subscription" && !event.message[0].gifter) {
if (!countdownEnded && subEnable) {
if (event.message[0].sub_plan == "1000") {
addTime(endingTime, seconds_added_per_sub_tier1 * factor_t1_local);
logMessage("Streamlabs", `Added ${seconds_added_per_sub_tier1 * factor_t1_local} Seconds Because ${event.message[0].name} Subscribed With Tier 1`);
}
else if (event.message[0].sub_plan == "2000") {
addTime(endingTime, seconds_added_per_sub_tier2 * factor_t2_local);
logMessage("Streamlabs", `Added ${seconds_added_per_sub_tier2 * factor_t2_local} Seconds Because ${event.message[0].name} Subscribed With Tier 2`);
}
else if (event.message[0].sub_plan == "3000") {
addTime(endingTime, seconds_added_per_sub_tier3 * factor_t3_local);
logMessage("Streamlabs", `Added ${seconds_added_per_sub_tier3 * factor_t3_local} Seconds Because ${event.message[0].name} Subscribed With Tier 3`);
}
else {
addTime(endingTime, seconds_added_per_sub_prime * factor_t1_local);
logMessage("Streamlabs", `Added ${seconds_added_per_sub_prime * factor_t1_local} Seconds Because ${event.message[0].name} Subscribed With Prime`);
}
if (!users.includes(event.message[0].name)) {
users.push(event.message[0].name);
}
}
}
else if (event.type == "resub" && !event.message[0].gifter) {
if (!countdownEnded && subEnable) {
if (event.message[0].sub_plan == "1000") {
addTime(endingTime, seconds_added_per_resub_tier1 * factor_t1_local);
logMessage("Streamlabs", `Added ${seconds_added_per_resub_tier1 * factor_t1_local} Seconds Because ${event.message[0].name} ReSubscribed With Tier 1`);
}
else if (event.message[0].sub_plan == "2000") {
addTime(endingTime, seconds_added_per_resub_tier2 * factor_t2_local);
logMessage("Streamlabs", `Added ${seconds_added_per_resub_tier2 * factor_t2_local} Seconds Because ${event.message[0].name} ReSubscribed With Tier 2`);
}
else if (event.message[0].sub_plan == "3000") {
addTime(endingTime, seconds_added_per_resub_tier3 * factor_t3_local);
logMessage("Streamlabs", `Added ${seconds_added_per_resub_tier3 * factor_t3_local} Seconds Because ${event.message[0].name} ReSubscribed With Tier 3`);
}
else {
addTime(endingTime, seconds_added_per_resub_prime * factor_t1_local);
logMessage("Streamlabs", `Added ${seconds_added_per_resub_prime * factor_t1_local} Seconds Because ${event.message[0].name} ReSubscribed With Prime`);
}
if (!users.includes(event.message[0].name)) {
users.push(event.message[0].name);
}
}
}
else if (event.message[0].gifter) {
if (!countdownEnded && subEnable) {
if (event.message[0].sub_plan == "1000") {
addTime(endingTime, seconds_added_per_giftsub_tier1 * factor_t1_local);
logMessage("Streamlabs", `Added ${seconds_added_per_giftsub_tier1 * factor_t1_local} Seconds Because ${event.message[0].gifter} Gifted A Tier 1 Sub`);
}
else if (event.message[0].sub_plan == "2000") {
addTime(endingTime, seconds_added_per_giftsub_tier2 * factor_t2_local);
logMessage("Streamlabs", `Added ${seconds_added_per_giftsub_tier2 * factor_t2_local} Seconds Because ${event.message[0].gifter} Gifted A Tier 2 Sub`);
}
else if (event.message[0].sub_plan == "3000") {
addTime(endingTime, seconds_added_per_giftsub_tier3 * factor_t3_local);
logMessage("Streamlabs", `Added ${seconds_added_per_giftsub_tier3 * factor_t3_local} Seconds Because ${event.message[0].gifter} Gifted A Tier 3 Sub`);
}
if (!users.includes(event.message[0].name)) {
users.push(event.message[0].name);
}
}
}
else if (event.type == "donation") {
if (!countdownEnded && donationEnable) {
let dono = parseInt(event.message[0].amount);
let currency = event.message[0].currency;
if (dono >= min_donation_amount) {
let times = Math.floor(dono/min_donation_amount);
addTime(endingTime, seconds_added_per_donation * times * factor_donations_local);
logMessage("Streamlabs", `Added ${seconds_added_per_donation * times} Seconds Because ${event.message[0].name} Donated ${dono} ${currency}`);
if (!users.includes(event.message[0].name)) {
users.push(event.message[0].name);
}
}
}
}
else if (event.type == "bits") {
if (!countdownEnded && donationEnable) {
let bits = parseInt(event.message[0].amount);
if (bits >= min_amount_of_bits) {
let times = Math.floor(bits/min_amount_of_bits);
addTime(endingTime, seconds_added_per_bits * times);
logMessage("Streamlabs", `Added ${seconds_added_per_bits * times * factor_bits_local} Seconds Because ${event.message[0].name} Donated ${bits} Bits`);
if (!users.includes(event.message[0].name)) {
users.push(event.message[0].name);
}
}
}
}
});
socket.on("disconnect", () => {
logMessage("Streamlabs", "Socket Disconnected");
socket.connect();
});
}
-63
View File
@@ -1,63 +0,0 @@
if (streamloots_token !== "") {
evtSourceStreamLoots = new EventSource(`https://widgets.streamloots.com/alerts/${streamloots_token}/media-stream`);
logMessage("Streamloots", "Event Source Created");
evtSourceStreamLoots.onmessage = async (event) => {
data = await JSON.parse(event.data);
logObject("Streamloots", data);
if (data.data.type == "purchase") {
if (!countdownEnded && chestEnable) {
if(!happy_hour_active){
let chests;
if (typeof(data.data.fields[0].value) == "number") {
chests = data.data.fields[0].value;
if (chests >= min_amount_of_chests) {
let times = Math.floor(chests/min_amount_of_chests);
addTime(endingTime, seconds_added_per_chests * times);
logMessage("Streamloots", `Added ${seconds_added_per_chests * times} Seconds Because ${data.data.fields[1].value} Bought ${chests} Chests`);
if (!users.includes(data.data.fields[1].value)) {
users.push(data.data.fields[1].value);
}
}
}
else {
chests = data.data.fields[1].value;
if (chests >= min_amount_of_chests) {
let times = Math.floor(chests/min_amount_of_chests);
addTime(endingTime, seconds_added_per_chests * times);
logMessage("Streamloots", `Added ${seconds_added_per_chests * times} Seconds Because ${data.data.fields[0].value} Gifted ${chests} Chests`);
if (!users.includes(data.data.fields[0].value)) {
users.push(data.data.fields[0].value);
}
}
}
}
if(happy_hour_active){
let chests;
if (typeof(data.data.fields[0].value) == "number") {
chests = data.data.fields[0].value;
if (chests >= min_amount_of_chests) {
let times = Math.floor(chests/min_amount_of_chests);
addTime(endingTime, seconds_added_per_chests_happy * times);
logMessage("Streamloots", `Added ${seconds_added_per_chests_happy * times} Seconds Because ${data.data.fields[1].value} Bought ${chests} Chests`);
if (!users.includes(data.data.fields[1].value)) {
users.push(data.data.fields[1].value);
}
}
}
else {
chests = data.data.fields[1].value;
if (chests >= min_amount_of_chests) {
let times = Math.floor(chests/min_amount_of_chests);
addTime(endingTime, seconds_added_per_chests_happy * times);
logMessage("Streamloots", `Added ${seconds_added_per_chests_happy * times} Seconds Because ${data.data.fields[0].value} Gifted ${chests} Chests`);
if (!users.includes(data.data.fields[0].value)) {
users.push(data.data.fields[0].value);
}
}
}
}
}
}
}
}
-117
View File
@@ -1,117 +0,0 @@
if (twitch_channel_name !== "") {
const client = new tmi.client({
connection: {
reconnect: true,
secure: true,
},
channels: [twitch_channel_name],
});
client.connect();
logMessage("Twitch", `Client Connected`);
client.on('subscription', (channel, username, methods, message, userstate) => {
if (!countdownEnded && subEnable) {
let factor_t1_local = (happy_hour_active ? factor_t1 : 1) * (random_hour_active ? randomBellSkew(...range_t1): 1);
let factor_t2_local = (happy_hour_active ? factor_t2 : 1) * (random_hour_active ? randomBellSkew(...range_t2): 1);
let factor_t3_local = (happy_hour_active ? factor_t3 : 1) * (random_hour_active ? randomBellSkew(...range_t3): 1);
switch (methods['plan']) {
case "Prime":
addTime(endingTime, seconds_added_per_sub_prime * factor_t1_local);
logMessage("Twitch", `Added ${seconds_added_per_sub_prime * factor_t1_local} Seconds Because ${username} Subscribed With Prime`);
break;
case "1000":
addTime(endingTime, seconds_added_per_sub_tier1 * factor_t1_local);
logMessage("Twitch", `Added ${seconds_added_per_sub_tier1 * factor_t1_local} Seconds Because ${username} Subscribed With Tier 1`);
break;
case "2000":
addTime(endingTime, seconds_added_per_sub_tier2 * factor_t2_local);
logMessage("Twitch", `Added ${seconds_added_per_sub_tier2 * factor_t2_local} Seconds Because ${username} Subscribed With Tier 2`);
break;
case "3000":
addTime(endingTime, seconds_added_per_sub_tier3 * factor_t3_local);
logMessage("Twitch", `Added ${seconds_added_per_sub_tier3 * factor_t3_local} Seconds Because ${username} Subscribed With Tier 3`);
break;
}
if (!users.includes(username)) {
users.push(username);
}
}
});
client.on('resub', (channel, username, months, message, userstate, methods) => {
if (!countdownEnded && subEnable) {
let factor_t1_local = (happy_hour_active ? factor_t1 : 1) * (random_hour_active ? randomBellSkew(...range_t1): 1);
let factor_t2_local = (happy_hour_active ? factor_t2 : 1) * (random_hour_active ? randomBellSkew(...range_t2): 1);
let factor_t3_local = (happy_hour_active ? factor_t3 : 1) * (random_hour_active ? randomBellSkew(...range_t3): 1);
switch (methods['plan']) {
case "Prime":
addTime(endingTime, seconds_added_per_resub_prime * factor_t1_local);
logMessage("Twitch", `Added ${seconds_added_per_resub_prime * factor_t1_local} Seconds Because ${username} ReSubscribed With Prime`);
break;
case "1000":
addTime(endingTime, seconds_added_per_resub_tier1 * factor_t1_local);
logMessage("Twitch", `Added ${seconds_added_per_resub_tier1 * factor_t1_local} Seconds Because ${username} ReSubscribed With Tier 1`);
break;
case "2000":
addTime(endingTime, seconds_added_per_resub_tier2 * factor_t2_local);
logMessage("Twitch", `Added ${seconds_added_per_resub_tier2 * factor_t2_local} Seconds Because ${username} ReSubscribed With Tier 2`);
break;
case "3000":
addTime(endingTime, seconds_added_per_resub_tier3 * factor_t3_local);
logMessage("Twitch", `Added ${seconds_added_per_resub_tier3 * factor_t3_local} Seconds Because ${username} ReSubscribed With Tier 3`);
break;
}
if (!users.includes(username)) {
users.push(username);
}
}
});
client.on('subgift', (channel, username, months, recipient, methods, userstate) => {
if (!countdownEnded && subEnable) {
let factor_t1_local = (happy_hour_active ? factor_t1 : 1) * (random_hour_active ? randomBellSkew(...range_t1): 1);
let factor_t2_local = (happy_hour_active ? factor_t2 : 1) * (random_hour_active ? randomBellSkew(...range_t2): 1);
let factor_t3_local = (happy_hour_active ? factor_t3 : 1) * (random_hour_active ? randomBellSkew(...range_t3): 1);
switch (methods['plan']) {
case "Prime":
addTime(endingTime, seconds_added_per_giftsub_prime * factor_t1_local);
logMessage("Twitch", `Added ${seconds_added_per_giftsub_prime * factor_t1_local} Seconds Because ${username} gifted Sub With Prime`);
break;
case "1000":
addTime(endingTime, seconds_added_per_giftsub_tier1 * factor_t1_local);
logMessage("Twitch", `Added ${seconds_added_per_giftsub_tier1 * factor_t1_local} Seconds Because ${username} gifted Sub With Tier 1`);
break;
case "2000":
addTime(endingTime, seconds_added_per_giftsub_tier2 * factor_t2_local);
logMessage("Twitch", `Added ${seconds_added_per_giftsub_tier2 * factor_t2_local} Seconds Because ${username} gifted Sub With Tier 2`);
break;
case "3000":
addTime(endingTime, seconds_added_per_giftsub_tier3 * factor_t3_local);
logMessage("Twitch", `Added ${seconds_added_per_giftsub_tier3 * factor_t3_local} Seconds Because ${username} gifted Sub With Tier 3`);
break;
}
if (!users.includes(username)) {
users.push(username);
}
}
});
client.on('cheer', (channel, userstate, message) => {
if (!countdownEnded && bitEnable) {
let factor_bits_local = (happy_hour_active ? factor_bits : 1) * (random_hour_active ? randomBellSkew(...range_bits): 1);
if (userstate.bits >= min_amount_of_bits) {
let times = Math.floor(userstate.bits/min_amount_of_bits);
addTime(endingTime, seconds_added_per_bits * times * factor_bits_local);
logMessage("Twitch", `Added ${seconds_added_per_bits * times} Seconds Because ${userstate['display-name']} Donated ${userstate.bits} Bits`);
if (!users.includes(userstate['display-name'])) {
users.push(userstate['display-name']);
}
}
}
});
}
-36
View File
@@ -1,36 +0,0 @@
const sleep = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
};
const randomInRange = (min, max) => {
return Math.floor(Math.random() * (max - min)) + min;
};
const randomBellSkew = (min, max, skew) => {
let u = 0, v = 0;
while(u === 0) u = Math.random() //Converting [0,1) to (0,1)
while(v === 0) v = Math.random()
let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v )
num = num / 10.0 + 0.5 // Translate to 0 -> 1
if (num > 1 || num < 0)
num = randomBellSkew(min, max, skew) // resample between 0 and 1 if out of range
else{
num = Math.pow(num, skew) // Skew
num *= max - min // Stretch to fill range
num += min // offset to min
}
return num
}
const logMessage = (module, message) => {
console.log(`[${module}]: ${message}`);
};
const logObject = (module, object) => {
console.log(`[${module}]: `, object);
};
-11
View File
@@ -1,11 +0,0 @@
/* mousetrap v1.6.5 craig.is/killing/mice */
(function(q,u,c){function v(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent("on"+b,g)}function z(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return n[a.which]?n[a.which]:r[a.which]?r[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||
"meta"==a}function A(a,b){var g,d=[];var e=a;"+"===e?e=["+"]:(e=e.replace(/\+{2}/g,"+plus"),e=e.split("+"));for(g=0;g<e.length;++g){var m=e[g];B[m]&&(m=B[m]);b&&"keypress"!=b&&C[m]&&(m=C[m],d.push("shift"));w(m)&&d.push(m)}e=m;g=b;if(!g){if(!p){p={};for(var c in n)95<c&&112>c||n.hasOwnProperty(c)&&(p[n[c]]=c)}g=p[e]?"keydown":"keypress"}"keypress"==g&&d.length&&(g="keydown");return{key:m,modifiers:d,action:g}}function D(a,b){return null===a||a===u?!1:a===b?!0:D(a.parentNode,b)}function d(a){function b(a){a=
a||{};var b=!1,l;for(l in p)a[l]?b=!0:p[l]=0;b||(x=!1)}function g(a,b,t,f,g,d){var l,E=[],h=t.type;if(!k._callbacks[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(l=0;l<k._callbacks[a].length;++l){var c=k._callbacks[a][l];if((f||!c.seq||p[c.seq]==c.level)&&h==c.action){var e;(e="keypress"==h&&!t.metaKey&&!t.ctrlKey)||(e=c.modifiers,e=b.sort().join(",")===e.sort().join(","));e&&(e=f&&c.seq==f&&c.level==d,(!f&&c.combo==g||e)&&k._callbacks[a].splice(l,1),E.push(c))}}return E}function c(a,b,c,f){k.stopCallback(b,
b.target||b.srcElement,c,f)||!1!==a(b,c)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function e(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=z(a);b&&("keyup"==a.type&&y===b?y=!1:k.handleKey(b,F(a),a))}function m(a,g,t,f){function h(c){return function(){x=c;++p[a];clearTimeout(q);q=setTimeout(b,1E3)}}function l(g){c(t,g,a);"keyup"!==f&&(y=z(g));setTimeout(b,10)}for(var d=p[a]=0;d<g.length;++d){var e=d+1===g.length?l:h(f||
A(g[d+1]).action);n(g[d],e,f,a,d)}}function n(a,b,c,f,d){k._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var e=a.split(" ");1<e.length?m(a,e,b,c):(c=A(a,c),k._callbacks[c.key]=k._callbacks[c.key]||[],g(c.key,c.modifiers,{type:c.action},f,a,d),k._callbacks[c.key][f?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:f,level:d,combo:a}))}var k=this;a=a||u;if(!(k instanceof d))return new d(a);k.target=a;k._callbacks={};k._directMap={};var p={},q,y=!1,r=!1,x=!1;k._handleKey=function(a,
d,e){var f=g(a,d,e),h;d={};var k=0,l=!1;for(h=0;h<f.length;++h)f[h].seq&&(k=Math.max(k,f[h].level));for(h=0;h<f.length;++h)f[h].seq?f[h].level==k&&(l=!0,d[f[h].seq]=1,c(f[h].callback,e,f[h].combo,f[h].seq)):l||c(f[h].callback,e,f[h].combo);f="keypress"==e.type&&r;e.type!=x||w(a)||f||b(d);r=l&&"keydown"==e.type};k._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)n(a[d],b,c)};v(a,"keypress",e);v(a,"keydown",e);v(a,"keyup",e)}if(q){var n={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",
18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},r={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},C={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},B={option:"alt",command:"meta","return":"enter",
escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p;for(c=1;20>c;++c)n[111+c]="f"+c;for(c=0;9>=c;++c)n[c+96]=c.toString();d.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};d.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};d.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};d.prototype.reset=function(){this._callbacks={};
this._directMap={};return this};d.prototype.stopCallback=function(a,b){if(-1<(" "+b.className+" ").indexOf(" mousetrap ")||D(b,this.target))return!1;if("composedPath"in a&&"function"===typeof a.composedPath){var c=a.composedPath()[0];c!==a.target&&(b=c)}return"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};d.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};d.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(n[b]=a[b]);p=null};
d.init=function(){var a=d(u),b;for(b in a)"_"!==b.charAt(0)&&(d[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};d.init();q.Mousetrap=d;"undefined"!==typeof module&&module.exports&&(module.exports=d);"function"===typeof define&&define.amd&&define(function(){return d})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
-52
View File
@@ -1,52 +0,0 @@
timeFunc = {
addHours(time, h) {
time.setTime(time.getTime() + (h * 60 * 60 * 1000));
return time;
},
addMinutes(time, m) {
time.setTime(time.getTime() + (m * 60 * 1000));
return time;
},
addSeconds (time, s) {
time.setTime(time.getTime() + (s * 1000));
return time;
},
getHours(time) {
let hours;
if (typeof time !== 'number') {
hours = time.getHours().toString();
}
else {
hours = Math.floor(time / (1000 * 60 * 60)).toString();
}
hours = (hours.length < 2) ? `0${hours}` : hours;
return hours;
},
getMinutes(time) {
let minutes;
if (typeof time !== 'number') {
minutes = time.getMinutes().toString();
}
else {
minutes = Math.floor((time % (1000 * 60 * 60)) / (1000 * 60)).toString();
}
minutes = (minutes.length < 2) ? `0${minutes}` : minutes;
return minutes;
},
getSeconds(time) {
let seconds;
if (typeof time !== 'number') {
seconds = time.getSeconds().toString();
}
else {
seconds = Math.floor((time % (1000 * 60)) / 1000).toString();
}
seconds = (seconds.length < 2) ? `0${seconds}` : seconds;
return seconds;
}
};
-1
View File
File diff suppressed because one or more lines are too long
+330 -264
View File
@@ -1,296 +1,362 @@
const timeText = document.getElementById("timeText");
const client_id='ib2n7v7mur7ab2mcxv7rjju2ctsyoi'
const message_ids=[]
const timer=document.querySelector('#timeText')
let tba,tokens,pubsub,ping_tid,pong_tid,eventsub,sse,time_started,time_passed,time_total,config,localforage
/** @type WebSocket */
let irc
var initialHours;
var initialMinutes;
var initialSeconds;
var paused = false;
var happy_hour_active = false;
var random_hour_active = false;
let countdownEnded = false;
let countdownUpdater = null;
var initialHoursLocal = window.localStorage.getItem('initialHours')
if (initialHoursLocal !== null) {
initialHours = initialHoursLocal;
logMessage("Core", "Found initialHours in localStorage.")
} else {
initialHours = initialHoursConfig;
}
var initialMinutesLocal = window.localStorage.getItem('initialMinutes')
if (initialMinutesLocal !== null) {
initialMinutes = initialMinutesLocal;
logMessage("Core", "Found initialMinutes in localStorage.")
} else {
initialMinutes = initialMinutesConfig;
}
var initialSecondsLocal = window.localStorage.getItem('initialSeconds')
if (initialSecondsLocal !== null) {
initialSeconds = initialSecondsLocal;
logMessage("Core", "Found initialSeconds in localStorage.")
} else {
initialSeconds = initialSecondsConfig;
window.onanimationend=function(event){
event.target.remove()
}
resetBtn.addEventListener("click", function(){
window.onload=async function(){
// init localforage
localforage=(await import('https://cdn.jsdelivr.net/npm/localforage/+esm')).default
localforage=localforage.createInstance({name:'sugoi-subathon-countdown'})
// init timer display
time_started=await localforage.getItem('time_started')
time_passed=await localforage.getItem('time_passed')
time_total=await localforage.getItem('time_total')
updateTime()
// init twitch api tokens
tba=await import('https://tba.sugoidogo.com/tba.mjs')
tokens=await tba.get_tokens(client_id)
// load user config
load_config()
// init event sources
init_irc()
init_pubsub()
init_eventsub()
}
if (happy_hour_active) specialHourHandler('Happy');
if (random_hour_active) specialHourHandler('Random');
countdownEnded = false;
function handle_event(event_name,event_amount=1){
if(!config[event_name+'-time-enabled']){
return false
}
add_time(parseReadableTimeIntoMilliseconds(config[event_name+'-time'])*event_amount)
return true
}
initialHours = initialHoursConfig;
initialMinutes = initialMinutesConfig;
initialSeconds = initialSecondsConfig;
function reset(){
localforage.removeItem('time_started')
localforage.removeItem('time_passed')
time_started=null
time_passed=null
time_total=parseReadableTimeIntoMilliseconds(config['start-time'])
localforage.setItem('time_total',time_total)
}
let timeNow = new Date(Date.now());
endingTime = timeFunc.addHours(timeNow, initialHours);
endingTime = timeFunc.addMinutes(timeNow, initialMinutes);
endingTime = timeFunc.addSeconds(timeNow, initialSeconds);
window.localStorage.removeItem('initialHours');
window.localStorage.removeItem('initialMinutes');
window.localStorage.removeItem('initialSeconds');
logMessage("Core", "Timer Reset.");
});
startBtn.addEventListener("click", function(){
if (paused){
var initialHoursLocal = window.localStorage.getItem('initialHours')
if (initialHoursLocal !== null) {
initialHours = initialHoursLocal;
logMessage("Core", "Found initialHours in localStorage.")
} else {
initialHours = initialHoursConfig;
function load_config(){
return fetch('https://ts.sugoidogo.com/config.json',{headers:tokens.auth_headers})
.then(response=>response.json())
.then(json=>{
config=json
if(!time_started && !time_passed){
reset()
}
var initialMinutesLocal = window.localStorage.getItem('initialMinutes')
if (initialMinutesLocal !== null) {
initialMinutes = initialMinutesLocal;
logMessage("Core", "Found initialMinutes in localStorage.")
} else {
initialMinutes = initialMinutesConfig;
}
var initialSecondsLocal = window.localStorage.getItem('initialSeconds')
if (initialSecondsLocal !== null) {
initialSeconds = initialSecondsLocal;
logMessage("Core", "Found initialSeconds in localStorage.")
} else {
initialSeconds = initialSecondsConfig;
})
}
function add_time(time){
if(config['max-time-enabled']){
const new_time=time_total+time
const max_time=parseReadableTimeIntoMilliseconds(config['max-time'])
if(new_time>max_time){
time=max_time-time_total
}
}
let timeNow = new Date(Date.now());
document.getElementById("startPage").style.visibility = "hidden";
document.getElementById("container").style.visibility = "visible";
endingTime = timeFunc.addHours(timeNow, initialHours);
endingTime = timeFunc.addMinutes(timeNow, initialMinutes);
endingTime = timeFunc.addSeconds(timeNow, initialSeconds);
paused = false;
countdownUpdater = setInterval(() => {
getNextTime();
}, 1);
});
Mousetrap.bind(pauseShort, function(e) {
paused = true;
logMessage("Core", "Timer was paused");
document.getElementById("startPage").style.visibility = "visible";
document.getElementById("container").style.visibility = "hidden";
clearInterval(countdownUpdater);
});
Mousetrap.bind(happyHourShort, async function(e){
specialHourHandler('Happy');
});
Mousetrap.bind(randomHourShort, async function(e){
specialHourHandler('Random');
});
async function specialHourHandler(type){
if ((type === 'Happy' && happy_hour) || (type === 'Random' && random_hour)){
specialHourFunc(type)
let addedTime=document.createElement('p')
let timeString=parseMillisecondsIntoReadableTime(time)
if(time>0){
timeString='+'+timeString
}
else {
logMessage("Core", `${type} Hour is not available`)
document.getElementById("SpecialHourText").innerHTML = `${type} Hour error`;
document.getElementById("SpecialHourText").animate({opacity: [ 0, 1 ], easing: [ 'ease-in', 'ease-out' ],}, 500);
document.getElementById("SpecialHourText").style.opacity = "1";
document.getElementById("SpecialHourHTML").animate({top: [ "-200px", "-250px" ], easing: [ 'ease-in', 'ease-out' ],}, 500);
document.getElementById("SpecialHourHTML").style.top = "-250px";
await sleep(5000)
document.getElementById("SpecialHourText").animate({opacity: [ 1, 0 ], easing: [ 'ease-in', 'ease-out' ],}, 500);
document.getElementById("SpecialHourText").style.opacity = "0";
if(time==0){
timeString='Timer Maxed!'
}
addedTime.innerHTML=timeString
addedTime.className='addedTime'
document.body.appendChild(addedTime);
time_total+=time
localforage.setItem('time_total',time_total)
}
async function specialHourFunc(type){
let activate = ((type === 'Happy' && !happy_hour_active) || (type === 'Random' && !random_hour_active));
let toggleText = activate ? 'Activated' : 'Deactivated';
logMessage("Core", `${type} Hour ${toggleText}`);
if (type === 'Happy') happy_hour_active = activate;
if (type === 'Random') random_hour_active = activate;
let animation = (happy_hour_active || random_hour_active) ? ', url(https://drive.google.com/uc?id=1oduFlPg84O1DliM5FsLvJJsnwZ4m1Vpm)' : '';
document.getElementById("SpecialHourText").innerHTML = `${type} Hour ${toggleText}!`;
document.getElementById("SpecialHourText").animate({opacity: [ 0, 1 ], easing: [ 'ease-in', 'ease-out' ],}, 500);
document.getElementById("SpecialHourText").style.opacity = "1";
document.getElementById("SpecialHourHTML").animate({top: [ "-200px", "-250px" ], easing: [ 'ease-in', 'ease-out' ],}, 500);
document.getElementById("SpecialHourHTML").style.top = "-250px";
document.getElementById("container").style.backgroundImage = `-webkit-linear-gradient(-45deg, transparent 33%, rgba(0, 0, 0, .1) 33%, rgba(0,0, 0, .1) 66%, transparent 66%), -webkit-linear-gradient(top, rgba(255, 255, 255, .25), rgba(0, 0, 0, .25))${animation}, -webkit-linear-gradient(left, #0074cc, #a700cc)`;
await sleep(activate ? 5000 : 10000);
document.getElementById("SpecialHourText").animate({opacity: [ 1, 0 ], easing: [ 'ease-in', 'ease-out' ],}, 500);
document.getElementById("SpecialHourText").style.opacity = "0";
function start(){
time_started=Date.now()
localforage.setItem('time_started',time_started)
}
let users = [];
let time;
let endingTime = new Date(Date.now());
endingTime = timeFunc.addHours(endingTime, initialHours);
endingTime = timeFunc.addMinutes(endingTime, initialMinutes);
endingTime = timeFunc.addSeconds(endingTime, initialSeconds);
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
function pause(){
time_passed+=Date.now()-time_started
time_started=null
localforage.removeItem('time_started')
localforage.setItem('time_passed',time_passed)
}
var randomHappyBool = false
var scheduleHappyBool = false
const getNextTime = () => {
let currentTime = new Date(Date.now());
let differenceTime = endingTime - currentTime;
time = `${timeFunc.getHours(differenceTime)}:${timeFunc.getMinutes(differenceTime)}:${timeFunc.getSeconds(differenceTime)}`;
if (differenceTime <= 0) {
time = "00:00:00";
if (differenceTime <= (syncTime * 1000 * -1)) {
clearInterval(countdownUpdater);
countdownEnded = true;
logMessage("Core", "Timer Ended");
}
// https://stackoverflow.com/a/33909506
function parseMillisecondsIntoReadableTime(milliseconds){
let negative=false
if(milliseconds<0){
milliseconds=Math.abs(milliseconds)
negative=true
}
if (!paused){
window.localStorage.setItem('initialHours', timeFunc.getHours(differenceTime));
window.localStorage.setItem('initialMinutes', timeFunc.getMinutes(differenceTime));
window.localStorage.setItem('initialSeconds', timeFunc.getSeconds(differenceTime));
if (randHappy && happy_hour && !randomHappyBool){
randomHappyBool = true
setTimeout(randomHappy,1000)
}
if (scheduleHappy && happy_hour && !scheduleHappyBool){
scheduleHappyBool = true
scheduleHappyFunc()
}
//Get hours from milliseconds
var hours = milliseconds / (1000*60*60);
var absoluteHours = Math.floor(hours);
var h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours;
//Get remainder from hours and convert to minutes
var minutes = (hours - absoluteHours) * 60;
var absoluteMinutes = Math.floor(minutes);
var m = absoluteMinutes > 9 ? absoluteMinutes : '0' + absoluteMinutes;
//Get remainder from minutes and convert to seconds
var seconds = (minutes - absoluteMinutes) * 60;
var absoluteSeconds = Math.floor(seconds);
var s = absoluteSeconds > 9 ? absoluteSeconds : '0' + absoluteSeconds;
let time = h + ':' + m + ':' + s;
if(negative){
time='-'+time
}
timeText.innerText = time;
};
function randomHappy(){
if (!happy_hour_active){
if ((getRandomInt(0,10000) == 127)){
logMessage("RandomHappy","It's not rigged!")
specialHourFunc()
setTimeout(specialHourFunc, 3600000)
}
setTimeout(randomHappy,1000)
}
return time
}
function scheduleHappyFunc(){
let now = new Date()
if (now.getDay() == scheduleHappyDay){
if (now.getUTCHours() == scheduleHappyHour){
if (now.getUTCMinutes() == 00){
logMessage("Schedule","It's time!")
specialHourFunc()
setTimeout(specialHourFunc, 3600000)
setTimeout(scheduleHappyFunc, 36000000)
function parseReadableTimeIntoMilliseconds(readableTime){
let negative=false
if(readableTime.startsWith('-')){
negative=true
readableTime=readableTime.substring(1)
}
const [seconds,minutes,hours]=readableTime.split(':').reverse()
let time=((hours||0)*1000*60*60)+((minutes||0)*1000*60)+((seconds||0)*1000)
if(negative){
time=0-time
}
return time
}
function updateTime(){
let time_remaining=time_total
if(time_passed){
time_remaining-=time_passed
}
if(time_started){
time_remaining-=Date.now()-time_started
}
timer.innerHTML=parseMillisecondsIntoReadableTime(time_remaining)
requestAnimationFrame(updateTime)
}
function init_pubsub(){
if(pong_tid){
clearTimeout(pong_tid)
pong_tid=null
}
if(ping_tid){
clearTimeout(ping_tid)
ping_tid=null
}
if(pubsub){
pubsub.close()
}
pubsub=new WebSocket('wss://pubsub-edge.twitch.tv')
pubsub.onopen=function(){
pubsub.send(JSON.stringify({
"type":"LISTEN",
"data":{
"auth_token":tokens.access_token,
"topics":[
"channel-bits-events-v2."+tokens.user_id,
"channel-subscribe-events-v1."+tokens.user_id
]
}
}))
}
pubsub.onmessage=function(event){
let message=JSON.parse(event.data)
console.debug(message)
switch(message.type){
case 'RESPONSE':{
if(message.error){
throw message.error
}
pubsub_ping()
break
}
case 'PONG':{
clearTimeout(pong_tid)
pong_tid=null
break
}
case 'RECONNECT':{
init_pubsub()
break
}
case 'AUTH_REVOKED':{
location.reload()
break
}
case 'MESSAGE':{
message=message.data.message
if('sub_plan' in message){
handle_event('sub'+message.sub_plan)
break
}
if('bits_used' in message){
handle_bits('bit',message.bits_used)
break
}
}
}
}
}
var firstSub = true;
var endingTimeBeforeCounter;
var addedTimeCounter;
var timeoutID;
const addTime = async (time, s) => {
let addedTime = Math.floor(s);
if (!bulk_enabled) {
endingTimeBeforeCounter = time;
addedTimeCounter = addedTime;
addTimeInternal();
return;
}
if (firstSub) {
firstSub = false;
endingTimeBeforeCounter = time;
addedTimeCounter = addedTime;
} else {
addedTimeCounter += addedTime;
window.clearTimeout(timeoutID);
}
timeoutID = window.setTimeout(addTimeInternal, 1000);
};
const addTimeInternal = async () => {
let time = endingTimeBeforeCounter;
let s = addedTimeCounter;
addedTimeCounter = 0;
firstSub = true;
let addedTime = document.createElement("p");
happy_hour_active ? addedTime.classList = "gold" : addedTime.classList = "addedTime";
addedTime.innerText = `+${s}s`;
document.body.appendChild(addedTime);
addedTime.style.display = "block";
await sleep(50);
addedTime.style.left = `${randomInRange(35, 65)}%`;
addedTime.style.top = `${randomInRange(15, 40)}%`;
addedTime.style.opacity = "1";
while (s > 0){
timeStep = s > 60 ? s/30 : 2
endingTime = timeFunc.addSeconds(time, timeStep)
await sleep(50);
s -= timeStep
}
await sleep(200);
addedTime.style.opacity = "0";
await sleep(200);
addedTime.remove();
function pubsub_ping(){
pubsub.send(JSON.stringify({'type':'PING'}))
const time=Math.floor(Math.random() * (5*60*1000))
ping_tid=setTimeout(pubsub_ping,time)
pong_tid=setTimeout(init_pubsub,20000)
}
const testAddTime = (times, delay, s) => {
let addTimeInterval = setInterval(async () => {
if (times > 0) {
await sleep(randomInRange(50, delay-50));
addTime(endingTime, s);
--times;
function init_eventsub(){
eventsub=new WebSocket('wss://eventsub.wss.twitch.tv/ws')
eventsub.onmessage=function(event){
let message=JSON.parse(event.data)
console.debug(message)
if(message.metadata.message_id in message_ids){
return
}else{
message_ids.push(message.metadata.message_id)
}
else {
clearInterval(addTimeInterval);
switch(message.metadata.message_type){
case 'session_welcome':{
let session_id=message.payload.session.id
const headers={'content-type':'application/json'}
Object.assign(headers,tokens.auth_headers)
const url=new URL('https://api.twitch.tv/helix/eventsub/subscriptions')
subscriptions=[
{
"type": "channel.follow",
"version": "2",
"condition": {
"broadcaster_user_id": tokens.user_id,
"moderator_user_id": tokens.user_id
},
"transport": {
"method": "websocket",
"session_id": session_id,
}
},
{
"type": "channel.raid",
"version": "1",
"condition": {
"to_broadcaster_user_id": tokens.user_id
},
"transport": {
"method": "websocket",
"session_id": session_id,
}
},
{
"type": "channel.charity_campaign.donate",
"version": "1",
"condition": {
"broadcaster_user_id": tokens.user_id
},
"transport": {
"method": "websocket",
"session_id": session_id,
}
}
]
for(const subscription of subscriptions){
fetch(url,{
headers:headers,
method:"POST",
body:JSON.stringify(subscription)
})
}
break
}
case 'notification':{
switch(message.metadata.subscription_type){
case 'channel.follow':{
handle_event('follow')
break
}
case 'channel.raid':{
handle_event('raid')
break
}
case 'channel.charity_campaign.donate':{
handle_event('charity',message.event.amount.value)
}
}
}
}
}, delay);
};
}
}
function ircSend(message){
console.debug('< '+message)
irc.send(message)
}
async function init_irc(){
if(irc){
irc.close()
}
irc=new WebSocket('wss://irc-ws.chat.twitch.tv:443')
irc.onclose=init_irc
irc.onopen=function(){
ircSend('CAP REQ twitch.tv/tags')
ircSend('PASS oauth:'+tokens.access_token)
ircSend('NICK '+tokens.login)
ircSend('JOIN #'+tokens.login)
}
irc.onmessage=function(event){
console.log(event)
for(let data of event.data.split('\r\n')){
console.debug('> '+data)
/** @type string */
data=data.split(':')
const tags=data.shift()
const type=data.shift()
const message=data.join(':')
console.debug(tags,type,message)
if(!message){
continue
}
if(!(tags.includes('broadcaster') || tags.includes('moderator'))){
continue
}
const command=message.split(' ')
console.debug(command)
if(command.shift()!='!subathon'){
continue
}
switch(command.shift()){
case 'start':{
start()
break
}
case 'pause':{
pause()
break
}
case 'reset':{
reset()
break
}
case 'add':{
add_time(parseReadableTimeIntoMilliseconds(command.shift()))
break
}
}
}
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 MiB