diff --git a/README.md b/README.md index 70a6928..5ed3665 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,24 @@ -

Improved Twitch Subathon Countdown

-

This is a Twitch Subathon Countdown originally by JayexDesigns.

-

The CSS Code is updated by Johnnycyan. The JS Files are updated by leabdd and danilotitato to add functionalities.

-
+# Improved Twitch Subathon Countdown +This is a Twitch Subathon Countdown originally by JayexDesigns. -

Description

-

A subathon timer that increases when someone subscribes, donates money, donates bits or purchases streamloots chests.

-

Added is a Timer that gets saved (in localStorage) and can be reset.

-

When you open the index.html you get to chose between "Reset" or "Start".

-
+The CSS Code is updated by Johnnycyan. -

Shortcuts

-

To pause the Timer: CTRL+ALT+P

-

To enable/disable Happy Hour: CTRL+ALT+H

-

To enable/disable Random Hour: CTRL+ALT+R

-

Shortcuts can be changed in config.js

-
+The JS Files have been re-written by SugoiDogo. -

Preview

-

-
+## Description +A subathon timer that increases when someone subscribes, donates to your charity, donates bits, follows or raids. -

Usage

-

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:

+## Usage +Go to the [config page](https://sugoidogo.github.io/twitch-subathon-countdown/config.html), authorize the timer, and configure to your liking. -

Twitch

-

To get your Subs and Bits you just have to add your Channel Name.

+## Support +See [my github profile](https://github.com/sugoidogo) -

Streamlabs Token

-

To get the token you'll have to go to the API settings tab of your Streamlabs dashboard 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.

- - -

StreamElements Token

-

To get the token you'll have to go to your StreamElements channels dashboard, then click on show secrets, copy the Overlay token and paste the code on the "streamelements_token" variable in the "config.js" file.

- - -

Streamloots Token

-

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.

- - -

Initial Time

-

You can set the initial time of the timer by setting the hours, minutes and seconds to be added when the timer is created.

- -

Random Happy Hour

-

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.

-

You have to set the Happy Hour time values manually.

-

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.

-

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.

-

In the Config you can set a specific Date and Time. At that a Happy Hour will happen. scheduleHappy needs to be turned on.

- -

Bulk

-

This enables that multiple Subs get added to the timer at once.

-

For Example when a user gifts 10 Subs.

- -

Full control on what to use

-

You can enable and disable if you want to add time for Subscriptions, Bits, Donations or Chests.

- -

Other Values

-

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.

- -

Add To OBS

-

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.

- -

Known Problems

-Only Streamelements Token in config.js does not add time for Subscriptions. Emulate with the Overlay Editor does work.

+## 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. \ No newline at end of file diff --git a/auth_streamelements.png b/auth_streamelements.png deleted file mode 100644 index 2a53a16..0000000 Binary files a/auth_streamelements.png and /dev/null differ diff --git a/auth_streamlabs.png b/auth_streamlabs.png deleted file mode 100644 index 1241813..0000000 Binary files a/auth_streamlabs.png and /dev/null differ diff --git a/auth_streamloots.png b/auth_streamloots.png deleted file mode 100644 index 22e2d56..0000000 Binary files a/auth_streamloots.png and /dev/null differ diff --git a/config.js b/config.js deleted file mode 100644 index c2cdb2e..0000000 --- a/config.js +++ /dev/null @@ -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] diff --git a/css/style.css b/css/style.css index 144cb1d..bdd42b3 100644 --- a/css/style.css +++ b/css/style.css @@ -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%; + } +} \ No newline at end of file diff --git a/index.html b/index.html index 2b092e7..f895255 100644 --- a/index.html +++ b/index.html @@ -11,41 +11,11 @@ Subathon Timer -
-
- -
-
- -
-

00:00:00

-
-

Happy Hour Activated!

- -
-
- - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/js/api/streamelements.js b/js/api/streamelements.js deleted file mode 100644 index 40c8332..0000000 --- a/js/api/streamelements.js +++ /dev/null @@ -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(); -} \ No newline at end of file diff --git a/js/api/streamlabs.js b/js/api/streamlabs.js deleted file mode 100644 index b600fc0..0000000 --- a/js/api/streamlabs.js +++ /dev/null @@ -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(); - }); -} \ No newline at end of file diff --git a/js/api/streamloots.js b/js/api/streamloots.js deleted file mode 100644 index 3d412bd..0000000 --- a/js/api/streamloots.js +++ /dev/null @@ -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); - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/js/api/twitch.js b/js/api/twitch.js deleted file mode 100644 index 513e117..0000000 --- a/js/api/twitch.js +++ /dev/null @@ -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']); - } - } - } - }); -} \ No newline at end of file diff --git a/js/lib/generalFunc.js b/js/lib/generalFunc.js deleted file mode 100644 index bea1d6e..0000000 --- a/js/lib/generalFunc.js +++ /dev/null @@ -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); -}; - - diff --git a/js/lib/mousetrap.min.js b/js/lib/mousetrap.min.js deleted file mode 100644 index 185c42f..0000000 --- a/js/lib/mousetrap.min.js +++ /dev/null @@ -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;gc||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":".","?":"/","|":"\\"},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); diff --git a/js/lib/timeFunc.js b/js/lib/timeFunc.js deleted file mode 100644 index 45a83cd..0000000 --- a/js/lib/timeFunc.js +++ /dev/null @@ -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; - } -}; \ No newline at end of file diff --git a/js/lib/tmi.min.js b/js/lib/tmi.min.js deleted file mode 100644 index 8f5833e..0000000 --- a/js/lib/tmi.min.js +++ /dev/null @@ -1 +0,0 @@ -!function s(o,i,r){function a(t,e){if(!i[t]){if(!o[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(c)return c(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=i[t]={exports:{}},o[t][0].call(n.exports,function(e){return a(o[t][1][e]||e)},n,n.exports,s,o,i,r)}return i[t].exports}for(var c="function"==typeof require&&require,e=0;e: ").concat(m)),t.tags.hasOwnProperty("username")||(t.tags.username=M),t.tags["message-type"]="whisper";var J=H.channel(t.tags.username);this.emits(["whisper","message"],[[J,t.tags,m,!1]]);break;case"PRIVMSG":t.tags.username=t.prefix.split("!")[0],"jtv"===t.tags.username?(a=H.username(m.split(" ")[0]),c=m.includes("auto"),m.includes("hosting you for")?(J=H.extractNumber(m),this.emit("hosted",l,a,J,c)):m.includes("hosting you")&&this.emit("hosted",l,a,0,c)):(a=H.get(this.opts.options.messagesLogLevel,"info"),c=H.actionMessage(m),t.tags["message-type"]=c?"action":"chat",m=c?c[1]:m,t.tags.hasOwnProperty("bits")?this.emit("cheer",l,t.tags,m):(t.tags.hasOwnProperty("msg-id")?"highlighted-message"!==t.tags["msg-id"]&&"skip-subs-mode-message"!==t.tags["msg-id"]||(u=t.tags["msg-id"],this.emit("redeem",l,t.tags.username,u,t.tags,m)):t.tags.hasOwnProperty("custom-reward-id")&&(u=t.tags["custom-reward-id"],this.emit("redeem",l,t.tags.username,u,t.tags,m)),c?(this.log[a]("[".concat(l,"] *<").concat(t.tags.username,">: ").concat(m)),this.emits(["action","message"],[[l,t.tags,m,!1]])):(this.log[a]("[".concat(l,"] <").concat(t.tags.username,">: ").concat(m)),this.emits(["chat","message"],[[l,t.tags,m,!1]]))));break;default:this.log.warn("Could not parse message:\n".concat(JSON.stringify(t,null,4)))}}},n.prototype.connect=function(){var s=this;return new Promise(function(t,n){s.server=H.get(s.opts.connection.server,"irc-ws.chat.twitch.tv"),s.port=H.get(s.opts.connection.port,80),s.secure&&(s.port=443),443===s.port&&(s.secure=!0),s.reconnectTimer=s.reconnectTimer*s.reconnectDecay,s.reconnectTimer>=s.maxReconnectInterval&&(s.reconnectTimer=s.maxReconnectInterval),s._openConnection(),s.once("_promiseConnect",function(e){e?n(e):t([s.server,~~s.port])})})},n.prototype._openConnection=function(){this.ws=new r("".concat(this.secure?"wss":"ws","://").concat(this.server,":").concat(this.port,"/"),"irc"),this.ws.onmessage=this._onMessage.bind(this),this.ws.onerror=this._onError.bind(this),this.ws.onclose=this._onClose.bind(this),this.ws.onopen=this._onOpen.bind(this)},n.prototype._onOpen=function(){var t=this;H.isNull(this.ws)||1!==this.ws.readyState||(this.log.info("Connecting to ".concat(this.server," on port ").concat(this.port,"..")),this.emit("connecting",this.server,~~this.port),this.username=H.get(this.opts.identity.username,H.justinfan()),this._getToken().then(function(e){e=H.password(e);t.log.info("Sending authentication to server.."),t.emit("logon"),t.ws.send("CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership"),e?t.ws.send("PASS ".concat(e)):H.isJustinfan(t.username)&&t.ws.send("PASS SCHMOOPIIE"),t.ws.send("NICK ".concat(t.username))}).catch(function(e){t.emits(["_promiseConnect","disconnected"],[[e],["Could not get a token."]])}))},n.prototype._getToken=function(){var e,t=this.opts.identity.password;return"function"==typeof t?(e=t())instanceof Promise?e:Promise.resolve(e):Promise.resolve(t)},n.prototype._onMessage=function(e){var t=this;e.data.split("\r\n").forEach(function(e){H.isNull(e)||t.handleMessage(q.msg(e))})},n.prototype._onError=function(){var t=this;this.moderators={},this.userstate={},this.globaluserstate={},clearInterval(this.pingLoop),clearTimeout(this.pingTimeout),this.reason=H.isNull(this.ws)?"Connection closed.":"Unable to connect.",this.emits(["_promiseConnect","disconnected"],[[this.reason]]),this.reconnect&&this.reconnections===this.maxReconnectAttempts&&(this.emit("maxreconnect"),this.log.error("Maximum reconnection attempts reached.")),this.reconnect&&!this.reconnecting&&this.reconnections<=this.maxReconnectAttempts-1&&(this.reconnecting=!0,this.reconnections=this.reconnections+1,this.log.error("Reconnecting in ".concat(Math.round(this.reconnectTimer/1e3)," seconds..")),this.emit("reconnect"),setTimeout(function(){t.reconnecting=!1,t.connect().catch(function(e){return t.log.error(e)})},this.reconnectTimer)),this.ws=null},n.prototype._onClose=function(){var t=this;this.moderators={},this.userstate={},this.globaluserstate={},clearInterval(this.pingLoop),clearTimeout(this.pingTimeout),this.wasCloseCalled?(this.wasCloseCalled=!1,this.reason="Connection closed.",this.log.info(this.reason),this.emits(["_promiseConnect","_promiseDisconnect","disconnected"],[[this.reason],[null],[this.reason]])):(this.emits(["_promiseConnect","disconnected"],[[this.reason]]),this.reconnect&&this.reconnections===this.maxReconnectAttempts&&(this.emit("maxreconnect"),this.log.error("Maximum reconnection attempts reached.")),this.reconnect&&!this.reconnecting&&this.reconnections<=this.maxReconnectAttempts-1&&(this.reconnecting=!0,this.reconnections=this.reconnections+1,this.log.error("Could not connect to server. Reconnecting in ".concat(Math.round(this.reconnectTimer/1e3)," seconds..")),this.emit("reconnect"),setTimeout(function(){t.reconnecting=!1,t.connect().catch(function(e){return t.log.error(e)})},this.reconnectTimer))),this.ws=null},n.prototype._getPromiseDelay=function(){return this.currentLatency<=600?600:this.currentLatency+100},n.prototype._sendCommand=function(s,o,i,r){var a=this;return new Promise(function(e,t){if(H.isNull(a.ws)||1!==a.ws.readyState)return t("Not connected to server.");var n;"number"==typeof s&&H.promiseDelay(s).then(function(){return t("No response from Twitch.")}),H.isNull(o)?(a.log.info("Executing command: ".concat(i)),a.ws.send(i)):(n=H.channel(o),a.log.info("[".concat(n,"] Executing command: ").concat(i)),a.ws.send("PRIVMSG ".concat(n," :").concat(i))),"function"==typeof r?r(e,t):e()})},n.prototype._sendMessage=function(c,u,l,m){var h=this;return new Promise(function(e,t){if(H.isNull(h.ws)||1!==h.ws.readyState)return t("Not connected to server.");if(H.isJustinfan(h.getUsername()))return t("Cannot send anonymous messages.");var n,s=H.channel(u);h.userstate[s]||(h.userstate[s]={}),500<=l.length&&(n=H.splitLine(l,500),l=n[0],setTimeout(function(){h._sendMessage(c,u,n[1],function(){})},350)),h.ws.send("PRIVMSG ".concat(s," :").concat(l));var o={};Object.keys(h.emotesets).forEach(function(e){return h.emotesets[e].forEach(function(e){return(H.isRegex(e.code)?q.emoteRegex:q.emoteString)(l,e.code,e.id,o)})});var i=H.merge(h.userstate[s],q.emotes({emotes:q.transformEmotes(o)||null})),r=H.get(h.opts.options.messagesLogLevel,"info"),a=H.actionMessage(l);a?(i["message-type"]="action",h.log[r]("[".concat(s,"] *<").concat(h.getUsername(),">: ").concat(a[1])),h.emits(["action","message"],[[s,i,a[1],!0]])):(i["message-type"]="chat",h.log[r]("[".concat(s,"] <").concat(h.getUsername(),">: ").concat(l)),h.emits(["chat","message"],[[s,i,l,!0]])),"function"==typeof m?m(e,t):e()})},n.prototype._updateEmoteset=function(s){var o=this;this.emotes=s,this._getToken().then(function(e){return o.api({url:"/chat/emoticon_images?emotesets=".concat(s),headers:{Authorization:"OAuth ".concat(H.token(e)),"Client-ID":o.clientId}},function(e,t,n){return e?void setTimeout(function(){return o._updateEmoteset(s)},6e4):(o.emotesets=n.emoticon_sets||{},o.emit("emotesets",s,o.emotesets))})}).catch(function(){return setTimeout(function(){return o._updateEmoteset(s)},6e4)})},n.prototype.getUsername=function(){return this.username},n.prototype.getOptions=function(){return this.opts},n.prototype.getChannels=function(){return this.channels},n.prototype.isMod=function(e,t){e=H.channel(e);return this.moderators[e]||(this.moderators[e]=[]),this.moderators[e].includes(H.username(t))},n.prototype.readyState=function(){return H.isNull(this.ws)?"CLOSED":["CONNECTING","OPEN","CLOSING","CLOSED"][this.ws.readyState]},n.prototype._isConnected=function(){return null!==this.ws&&1===this.ws.readyState},n.prototype.disconnect=function(){var n=this;return new Promise(function(e,t){H.isNull(n.ws)||3===n.ws.readyState?(n.log.error("Cannot disconnect from server. Socket is not opened or connection is already closing."),t("Cannot disconnect from server. Socket is not opened or connection is already closing.")):(n.wasCloseCalled=!0,n.log.info("Disconnecting from server.."),n.ws.close(),n.once("_promiseDisconnect",function(){return e([n.server,~~n.port])}))})},void 0!==u&&u.exports&&(u.exports=n),"undefined"!=typeof window&&(window.tmi={},window.tmi.client=n,window.tmi.Client=n)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./api":2,"./commands":4,"./events":5,"./logger":6,"./parser":7,"./timer":8,"./utils":9,ws:10}],4:[function(e,t,n){"use strict";var u=e("./utils");function s(s,o){var e=this;return s=u.channel(s),o=u.get(o,30),this._sendCommand(this._getPromiseDelay(),s,"/followers ".concat(o),function(t,n){e.once("_promiseFollowers",function(e){e?n(e):t([s,~~o])})})}function o(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/followersoff",function(t,n){e.once("_promiseFollowersoff",function(e){e?n(e):t([s])})})}function i(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),null,"PART ".concat(s),function(t,n){e.once("_promisePart",function(e){e?n(e):t([s])})})}function r(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/r9kbeta",function(t,n){e.once("_promiseR9kbeta",function(e){e?n(e):t([s])})})}function a(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/r9kbetaoff",function(t,n){e.once("_promiseR9kbetaoff",function(e){e?n(e):t([s])})})}function c(s,o){var e=this;return s=u.channel(s),o=u.get(o,300),this._sendCommand(this._getPromiseDelay(),s,"/slow ".concat(o),function(t,n){e.once("_promiseSlow",function(e){e?n(e):t([s,~~o])})})}function l(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/slowoff",function(t,n){e.once("_promiseSlowoff",function(e){e?n(e):t([s])})})}t.exports={action:function(n,s){return n=u.channel(n),s="ACTION ".concat(s,""),this._sendMessage(this._getPromiseDelay(),n,s,function(e,t){e([n,s])})},ban:function(s,o,i){var e=this;return s=u.channel(s),o=u.username(o),i=u.get(i,""),this._sendCommand(this._getPromiseDelay(),s,"/ban ".concat(o," ").concat(i),function(t,n){e.once("_promiseBan",function(e){e?n(e):t([s,o,i])})})},clear:function(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/clear",function(t,n){e.once("_promiseClear",function(e){e?n(e):t([s])})})},color:function(e,s){var o=this;return s=u.get(s,e),this._sendCommand(this._getPromiseDelay(),"#tmijs","/color ".concat(s),function(t,n){o.once("_promiseColor",function(e){e?n(e):t([s])})})},commercial:function(s,o){var e=this;return s=u.channel(s),o=u.get(o,30),this._sendCommand(this._getPromiseDelay(),s,"/commercial ".concat(o),function(t,n){e.once("_promiseCommercial",function(e){e?n(e):t([s,~~o])})})},deletemessage:function(s,e){var o=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/delete ".concat(e),function(t,n){o.once("_promiseDeletemessage",function(e){e?n(e):t([s])})})},emoteonly:function(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/emoteonly",function(t,n){e.once("_promiseEmoteonly",function(e){e?n(e):t([s])})})},emoteonlyoff:function(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/emoteonlyoff",function(t,n){e.once("_promiseEmoteonlyoff",function(e){e?n(e):t([s])})})},followersonly:s,followersmode:s,followersonlyoff:o,followersmodeoff:o,host:function(o,i){var e=this;return o=u.channel(o),i=u.username(i),this._sendCommand(2e3,o,"/host ".concat(i),function(n,s){e.once("_promiseHost",function(e,t){e?s(e):n([o,i,~~t])})})},join:function(a){var c=this;return a=u.channel(a),this._sendCommand(null,null,"JOIN ".concat(a),function(s,o){var i="_promiseJoin",r=!1,e=function e(t,n){a===u.channel(n)&&(c.removeListener(i,e),r=!0,t?o(t):s([a]))};c.on(i,e);e=c._getPromiseDelay();u.promiseDelay(e).then(function(){r||c.emit(i,"No response from Twitch.",a)})})},mod:function(s,o){var e=this;return s=u.channel(s),o=u.username(o),this._sendCommand(this._getPromiseDelay(),s,"/mod ".concat(o),function(t,n){e.once("_promiseMod",function(e){e?n(e):t([s,o])})})},mods:function(o){var i=this;return o=u.channel(o),this._sendCommand(this._getPromiseDelay(),o,"/mods",function(n,s){i.once("_promiseMods",function(e,t){e?s(e):(t.forEach(function(e){i.moderators[o]||(i.moderators[o]=[]),i.moderators[o].includes(e)||i.moderators[o].push(e)}),n(t))})})},part:i,leave:i,ping:function(){var n=this;return this._sendCommand(this._getPromiseDelay(),null,"PING",function(t,e){n.latency=new Date,n.pingTimeout=setTimeout(function(){null!==n.ws&&(n.wasCloseCalled=!1,n.log.error("Ping timeout."),n.ws.close(),clearInterval(n.pingLoop),clearTimeout(n.pingTimeout))},u.get(n.opts.connection.timeout,9999)),n.once("_promisePing",function(e){return t([parseFloat(e)])})})},r9kbeta:r,r9kmode:r,r9kbetaoff:a,r9kmodeoff:a,raw:function(n){return this._sendCommand(this._getPromiseDelay(),null,n,function(e,t){e([n])})},say:function(n,s){return n=u.channel(n),s.startsWith(".")&&!s.startsWith("..")||s.startsWith("/")||s.startsWith("\\")?"me "===s.substr(1,3)?this.action(n,s.substr(4)):this._sendCommand(this._getPromiseDelay(),n,s,function(e,t){e([n,s])}):this._sendMessage(this._getPromiseDelay(),n,s,function(e,t){e([n,s])})},slow:c,slowmode:c,slowoff:l,slowmodeoff:l,subscribers:function(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/subscribers",function(t,n){e.once("_promiseSubscribers",function(e){e?n(e):t([s])})})},subscribersoff:function(s){var e=this;return s=u.channel(s),this._sendCommand(this._getPromiseDelay(),s,"/subscribersoff",function(t,n){e.once("_promiseSubscribersoff",function(e){e?n(e):t([s])})})},timeout:function(s,o,i,r){var e=this;return s=u.channel(s),o=u.username(o),u.isNull(i)||u.isInteger(i)||(r=i,i=300),i=u.get(i,300),r=u.get(r,""),this._sendCommand(this._getPromiseDelay(),s,"/timeout ".concat(o," ").concat(i," ").concat(r),function(t,n){e.once("_promiseTimeout",function(e){e?n(e):t([s,o,~~i,r])})})},unban:function(s,o){var e=this;return s=u.channel(s),o=u.username(o),this._sendCommand(this._getPromiseDelay(),s,"/unban ".concat(o),function(t,n){e.once("_promiseUnban",function(e){e?n(e):t([s,o])})})},unhost:function(s){var e=this;return s=u.channel(s),this._sendCommand(2e3,s,"/unhost",function(t,n){e.once("_promiseUnhost",function(e){e?n(e):t([s])})})},unmod:function(s,o){var e=this;return s=u.channel(s),o=u.username(o),this._sendCommand(this._getPromiseDelay(),s,"/unmod ".concat(o),function(t,n){e.once("_promiseUnmod",function(e){e?n(e):t([s,o])})})},unvip:function(s,o){var e=this;return s=u.channel(s),o=u.username(o),this._sendCommand(this._getPromiseDelay(),s,"/unvip ".concat(o),function(t,n){e.once("_promiseUnvip",function(e){e?n(e):t([s,o])})})},vip:function(s,o){var e=this;return s=u.channel(s),o=u.username(o),this._sendCommand(this._getPromiseDelay(),s,"/vip ".concat(o),function(t,n){e.once("_promiseVip",function(e){e?n(e):t([s,o])})})},vips:function(e){var t=this;return e=u.channel(e),this._sendCommand(this._getPromiseDelay(),e,"/vips",function(n,s){t.once("_promiseVips",function(e,t){e?s(e):n(t)})})},whisper:function(n,s){var o=this;return(n=u.username(n))===this.getUsername()?Promise.reject("Cannot send a whisper to the same account."):this._sendCommand(this._getPromiseDelay(),"#tmijs","/w ".concat(n," ").concat(s),function(e,t){o.once("_promiseWhisper",function(e){e&&t(e)})}).catch(function(e){if(e&&"string"==typeof e&&0!==e.indexOf("No response from Twitch."))throw e;var t=u.channel(n),e=u.merge({"message-type":"whisper","message-id":null,"thread-id":null,username:o.getUsername()},o.globaluserstate);return o.emits(["whisper","message"],[[t,e,s,!0],[t,e,s,!0]]),[n,s]})}}},{"./utils":9}],5:[function(e,t,n){"use strict";function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function c(e){return"function"==typeof e}function u(e){return"object"===s(e)&&null!==e}function a(e){return void 0===e}((t.exports=o).EventEmitter=o).prototype._events=void 0,o.prototype._maxListeners=void 0,o.defaultMaxListeners=10,o.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},o.prototype.emit=function(e){var t,n,s,o,i,r;if(this._events||(this._events={}),"error"===e&&(!this._events.error||u(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;throw TypeError('Uncaught, unspecified "error" event.')}if(a(n=this._events[e]))return!1;if(c(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:o=Array.prototype.slice.call(arguments,1),n.apply(this,o)}else if(u(n))for(o=Array.prototype.slice.call(arguments,1),s=(r=n.slice()).length,i=0;in&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},o.prototype.once=function(e,t){if(!c(t))throw TypeError("listener must be a function");var n=!1;if(this._events.hasOwnProperty(e)&&"_"===e.charAt(0)){var s,o=1,i=e;for(s in this._events)this._events.hasOwnProperty(s)&&s.startsWith(i)&&o++;e+=o}function r(){"_"!==e.charAt(0)||isNaN(e.substr(e.length-1))||(e=e.substring(0,e.length-1)),this.removeListener(e,r),n||(n=!0,t.apply(this,arguments))}return r.listener=t,this.on(e,r),this},o.prototype.removeListener=function(e,t){var n,s,o,i;if(!c(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(o=(n=this._events[e]).length,s=-1,n===t||c(n.listener)&&n.listener===t){if(delete this._events[e],this._events.hasOwnProperty(e+"2")&&"_"===e.charAt(0)){var r,a=e;for(r in this._events)this._events.hasOwnProperty(r)&&r.startsWith(a)&&(isNaN(parseInt(r.substr(r.length-1)))||(this._events[e+parseInt(r.substr(r.length-1)-1)]=this._events[r],delete this._events[r]));this._events[e]=this._events[e+"1"],delete this._events[e+"1"]}this._events.removeListener&&this.emit("removeListener",e,t)}else if(u(n)){for(i=o;0n?(t.command=e.slice(n),t):null;for(t.command=e.slice(n,s),n=s+1;32===e.charCodeAt(n);)n++;for(;ne.length)&&(t=e.length);for(var n=0,s=new Array(t);n").replace(/\\"\\;/g,'"').replace(/\\'\\;/g,"'")},unescapeIRC:function(e){return e&&e.includes("\\")?e.replace(a,function(e,t){return t in u?u[t]:t}):e},escapeIRC:function(e){return e&&e.replace(c,function(e,t){return t in l?"\\".concat(l[t]):t})},actionMessage:function(e){return e.match(i)},addWord:function(e,t){return e.length?e+" "+t:e+t},channel:function(e){var t=(e||"").toLowerCase();return"#"===t[0]?t:"#"+t},extractNumber:function(e){for(var t=e.split(" "),n=0;nresponse.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() - } - } - timeText.innerText = time; - -}; + //Get hours from milliseconds + var hours = milliseconds / (1000*60*60); + var absoluteHours = Math.floor(hours); + var h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours; -function randomHappy(){ - if (!happy_hour_active){ - if ((getRandomInt(0,10000) == 127)){ - logMessage("RandomHappy","It's not rigged!") - specialHourFunc() - setTimeout(specialHourFunc, 3600000) - } - setTimeout(randomHappy,1000) + //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 } + + 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 + } + } + } + } +} \ No newline at end of file diff --git a/preview.webp b/preview.webp deleted file mode 100644 index a4e130e..0000000 Binary files a/preview.webp and /dev/null differ