// ==UserScript==
// @name DKTD Cooldown Display
// @version 0.13.3
// @description Item cooldown display for the Twitch game "don't kick the dog"
// @author Grabz
// @match https://www.twitch.tv/dontkickthedog
// @match https://www.twitch.tv/dontkickthedog/*
// @match https://www.twitch.tv/popout/dontkickthedog
// @match https://www.twitch.tv/popout/dontkickthedog/*
// @grant none
// @namespace https://greasyfork.org/users/722243
// ==/UserScript==
(() => {
'use strict';
console.log('DKTD Cooldown Display - Script found');
class TimerBasic {
/**
*
* @param {number} order
* @param {string} type
* @param {number} milliseconds
* @param {string} icon
* @param {boolean} title
*/
constructor(order, type, milliseconds, icon, title) {
this.order = order;
this.type = type;
this.milliseconds = milliseconds;
this.icon = icon;
this.title = title;
this.elem = null;
this.lastUpdate = Date.now();
this.markedForRemoval = false;
}
/**
* @returns {{ now: number, text: string, lastUpdate: number }}
*/
refresh() {
//Rebuild the element if it is gone from DOM
if(isDetached(this.elem)) {
this.buildTimerElem();
}
//Build data
const data = {
now: Date.now(),
text: '',
lastUpdate: this.lastUpdate
}
//Reduce time
this.milliseconds -= data.now - this.lastUpdate;
this.lastUpdate = data.now;
data.text = getFormattedTime(Math.floor(this.milliseconds / 1000));
//Exit if the cooldown has reached 0
if(this.milliseconds < 0) {
this.markedForRemoval = true;
}
return data;
}
buildTimerElem() {
let parent = elements.custom[`holder_${this.type}`];
if(isDetached(parent))
return false;
let elem = document.createElement('div');
elem.innerHTML = '<span>' + this.icon + '</span><span data-dktd="counter"></span>';
elem.style.paddingRight = '5px';
this.elem = elem;
parent.appendChild(elem);
return true;
}
}
class TimerIdle extends TimerBasic {
constructor(order, type, milliseconds, icon, title) {
super(order, type, milliseconds, icon, title);
this.notifierSent = false;
this.idleTimeMs = null;
}
refresh() {
const data = super.refresh();
//Persist
this.markedForRemoval = false;
if(this.milliseconds <= 0) {
data.text = 'IDLE';
}
//Play repeat notifier
if(this.idleTimeMs != null) {
this.idleTimeMs -= data.now - data.lastUpdate;
if(this.idleTimeMs <= 0) {
this.idleTimeMs = settings.value_notify_idle_keep_notifying * 1000;
if(settings.toggle_notify_idle && settings.toggle_notify_idle_keep_notifying)
audio[settings.audio_notify_idle].audio.play();
}
}
//Play idle notifier
if(this.milliseconds <= 1000 * 30 && !this.notifierSent) {
this.notifierSent = true;
if(settings.toggle_notify_idle)
audio[settings.audio_notify_idle].audio.play();
this.idleTimeMs = settings.value_notify_idle_keep_notifying * 1000;
}
return data;
}
}
function TimerManager() {
const timers = [];
this.addTimer = function (timer) {
timers.push(timer);
timers.sort((a, b) => a.order - b.order);
this.refresh();
};
this.deleteTimers = function (type) {
for(let i = 0; i < timers.length; i++) {
let timer = timers[i];
if(timer.type === type) {
timer.elem.remove();
timers.splice(i, 1);
i--;
}
}
};
this.refresh = function () {
let title = '';
for (let i = 0; i < timers.length; i++) {
const timer = timers[i];
const data = timer.refresh();
//Exit if the timer is to be removed
if (timer.markedForRemoval) {
if(!isDetached(timer.elem))
timer.elem.remove();
timers.splice(i, 1);
i--;
continue;
}
//Update counter
if(!isDetached(timer.elem))
timer.elem.querySelector('[data-dktd=counter]').textContent = data.text;
if(timer.title)
title += `${data.text} `;
}
document.title = `${title} ${default_page_title}`;
};
}
const holder_html = `
<div class='dktd-wrapper'>
<div class='dktd-container-buttons'>
<button class='dktd-options-button' data-dktd='options_button'>⚙️ DKTD
<div class='dktd-options-panel' data-dktd='options_panel'>
<div class='dktd-options-header'>
<div class='dktd-options-title'>DKTD Control Panel</div>
<div class='dktd-options-close' data-dktd='options_panel_header_close'>X</div>
</div>
<div class='dktd-options-content'>
<div class='dktd-options-row'>
<div style="width:100%;text-align:middle">Volume (<span data-dktd="text_volume">0</span>%)</div>
<input type="range" min="0" max="100" value="0" data-dktd="slider_volume" style="width:100%">
</div>
<hr class='dktd-options-separator'></hr>
<div class='dktd-options-row' style='justify-content: space-between'>
<label class='dktd-label' title="Play a sound when you're about to be logged out."><input class='dktd-label-input' data-dktd='toggle_notify_idle' type='checkbox'>Idle Notify</label>
<select class='dktd-audio' data-dktd='audio_notify_idle'></select>
</div>
<div class='dktd-options-row'>
<label class='dktd-label' title="Continue to notify after the first time about being idle."><input class='dktd-label-input' data-dktd='toggle_notify_idle_keep_notifying' type='checkbox'>Keep Notifying</label>
</div>
<div class='dktd-options-row'>
Every <input class='dktd-options-inline-input' data-dktd='value_notify_idle_keep_notifying' type='text' style='width: 30px;'> seconds
</div>
<hr class='dktd-options-separator'></hr>
<div class='dktd-options-row' style='justify-content: space-between'>
<label class='dktd-label' title="Play a sound whenever the dog is leaving."><input class='dktd-label-input' data-dktd='toggle_notify_leave' type='checkbox'>Dog Leave Notify</label>
<select class='dktd-audio' data-dktd='audio_notify_leave'></select>
</div>
<div class='dktd-options-row' style='justify-content: space-between'>
<label class='dktd-label' title="Play a sound whenever the dog's bone levels up."><input class='dktd-label-input' data-dktd='toggle_notify_bone' type='checkbox'>Bone Level Notify</label>
<select class='dktd-audio' data-dktd='audio_notify_bone'></select>
</div>
<div class='dktd-options-row' style='justify-content: space-between'>
<label class='dktd-label' title="Play a sound whenever Golden Dog is activated."><input class='dktd-label-input' data-dktd='toggle_notify_golden' type='checkbox'>Golden Dog Notify</label>
<select class='dktd-audio' data-dktd='audio_notify_golden'></select>
</div>
<div class='dktd-options-row' style='justify-content: space-between'>
<label class='dktd-label' title="Play a sound whenever a Warp is being claimed."><input class='dktd-label-input' data-dktd='toggle_notify_warp' type='checkbox'>Warp Notify</label>
<select class='dktd-audio' data-dktd='audio_notify_warp'></select>
</div>
</div>
</div>
</button>
</div>
<div class='dktd-container-idle'>
<span data-dktd='holder_idle'></span>
<span data-dktd='holder_sorry'></span>
</div>
<div class='dktd-holder-item' data-dktd='holder_item'></div>
</div>
`;
const addon_css = `
.dktd-wrapper { display: flex; flex-flow: column nowrap; width: 100%; flex: 1 0 65px; }
.dktd-options-button { position: relative; border-radius: 2px; font-weight: bold; height: 24px; padding-left: 4px; padding-right: 4px; background-color: #a27a2c; color: #e3e3e3; }
.dktd-options-panel { display: none; position: absolute; width: 32rem; bottom: 30px; left: 0; z-index: 10; cursor: default; padding: 8px; background-color: var(--color-background-base) !important; border-radius: 0px 0px 3px 3px; }
html.tw-root--theme-dark .dktd-options-panel { color: #e3e3e3; box-shadow: #1c1c1c 0px 4px 8px 0px; }
html.tw-root--theme-light .dktd-options-panel { color: #1c1c1c; box-shadow: #e3e3e3 0px 4px 8px 0px; }
.dktd-options-header { display: flex; flex-flow: row nowrap; align-items: center; padding-bottom: 6px; font-size: var(--font-size-5) !important; font-weight: bold; }
.dktd-options-content { display: flex; flex-flow: column nowrap; font-weight: normal; }
.dktd-options-row { display: flex; flex-flow: row nowrap; }
.dktd-options-separator { border-top: 1px solid #bbb; margin: 8px; }
.dktd-options-bottompad { margin-bottom: 8px; }
.dktd-options-inline-input { width: 40px; padding: 0; margin: 0; border: 1px solid gray; border-radius: 2px; margin-left: 2px; margin-right: 2px; padding-left: 2px; padding-right: 2px; }
.dktd-options-title { flex: 1 0 auto; text-align: center; }
.dktd-options-close { flex: 0 0 21px; text-align: center; cursor: pointer; }
.dktd-label { flex: 0 0 auto; cursor: pointer; }
.dktd-label-input { cursor: inherit; vertical-align: middle; margin-right: 4px; }
.dktd-audio { flex: 0 1 50%; cursor: pointer; padding: 0; margin: 0; border: 1px solid gray; border-radius: 0; }
.dktd-holder-item, .dktd-container-idle, .dktd-container-buttons { display: flex; flex-flow: row nowrap; justify-content: flex-start; }
`;
const elements = {
twitch: {
container: [null, '[data-test-selector="chat-input-buttons-container"]'], //Main parent
chat: [null, '[data-test-selector="chat-scrollable-area__message-container"]'], //Chat holder
chat_send: [null, '[data-a-target="chat-send-button"]'], //Chat message send button
chat_input: [null, '[data-a-target="chat-input"]'] //Chat message input field
},
custom: {
holder: null, //Timers holder
holder_item: null, //Item timers holder
holder_idle: null, //Idle timer holder
holder_sorry: null, //Probation timer holder
options_panel: null,
options_panel_header_close: null,
text_volume: null,
slider_volume: null,
toggle_notify_idle: null,
audio_notify_idle: null,
toggle_notify_idle_keep_notifying: null,
value_notify_idle_keep_notifying: null,
toggle_notify_bone: null,
audio_notify_bone: null,
toggle_notify_golden: null,
audio_notify_golden: null,
toggle_notify_leave: null,
audio_notify_leave: null,
toggle_notify_warp: null,
audio_notify_warp: null,
}
}
var settings = {
slider_volume: 70, //0 - 100
toggle_notify_idle: false,
audio_notify_idle: 'Bell',
toggle_notify_bone: false,
audio_notify_bone: 'Warm Soft Synth',
toggle_notify_golden: false,
audio_notify_golden: 'Soft Chimes 1',
toggle_notify_leave: false,
audio_notify_leave: 'Short Tone',
toggle_notify_warp: false,
audio_notify_warp: 'Soft Chimes 2',
toggle_notify_idle_keep_notifying: false,
value_notify_idle_keep_notifying: 30,
}
const audio = {
//Attribution: https://freesound.org/people/InspectorJ/sounds/411089/
'Bell': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/bell.mp3'), type: 'short'},
//Attribution: https://www.zapsplat.com/music/ui-notification-tone-glassy-chime-good-for-pop-up-or-other-element-3/
'Glassy Chime': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/glassy_chime.mp3'), type: 'short'},
//Attribution: https://www.zapsplat.com/music/ui-notification-tone-glassy-click-good-for-pop-up-or-other-element-4/
'Glassy Click': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/glassy_click.mp3'), type: 'short'},
//Attribution: https://www.zapsplat.com/music/notification-or-alert-tone-short-musical-mallet-sound-positive-4/
'Mallet Short': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/mallet_musical_short.mp3'), type: 'medium'},
//Attribution: https://www.zapsplat.com/music/notification-or-alert-tone-soft-musical-chimes-positive-success-1/
'Soft Chimes 1': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/musical_soft_chimes.mp3'), type: 'medium'},
//Attribution: https://www.zapsplat.com/music/notification-or-alert-tone-soft-musical-chimes-positive-success-3/
'Soft Chimes 2': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/musical_soft_chimes_2.mp3'), type: 'medium'},
//Attribution: https://www.zapsplat.com/music/alert-tone-simple-and-basic-warm-soft-synth-complete-success-1/
'Warm Soft Synth': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/warm_soft_synth.mp3'), type: 'medium'},
//Attribution: https://www.zapsplat.com/music/game-sound-bright-digital-high-pitched-negative-tone-descend/
'Medium Tone': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/high_pitched_negative_descend.mp3'), type: 'bad'},
//Attribution: https://www.zapsplat.com/music/game-sound-bright-digital-short-negative-tone-2/
'Short Tone': {audio: new Audio('https://raw.githubusercontent.com/grabz-dev/stuff/main/audio/alert/short_negative.mp3'), type: 'bad'},
}
const default_page_title = 'dontkickthedog - Twitch';
const timers = new TimerManager();
const observer = new MutationObserver(observerCallback);
//Start this off as true. This is because the script can only load when navigating to www.twitch.tv/dontkickthedog
var global_in_dktd = true;
//Load the script
if(document.readyState !== 'loading') load();
else document.addEventListener('DOMContentLoaded', () => load());
function load() {
const style = document.createElement("style")
style.innerText = addon_css;
document.head.appendChild(style);
timers.addTimer(new TimerIdle(0, 'idle', 0, '🕑', true));
//Start timer loop
loop();
console.log('DKTD Cooldown Display - Script loaded');
}
function loop() {
//Repeat loop
setTimeout(loop, 1000);
//Detect whether we are in the DKTD page or not
const in_dktd = window.location.pathname.toLowerCase().indexOf('dontkickthedog') > -1;
//If we've already processed the page switch event below, just exit
if(!in_dktd && in_dktd === global_in_dktd) return;
//Refresh timers
//The in_dktd check prevents a race condition that makes TimerManager sometimes change the title
//after the page's already been switched
if(in_dktd) timers.refresh();
//Check if all required Twitch elements are still in the DOM
//If not, find all required Twitch elements
//Twitch frequently rebuilds the chat window in various circumstances
//so we have to expect any and all of the Twitch elements to eventually be detached
//If the chat is popped out, the loop will simply never progress past this check
for(let key of Object.keys(elements.twitch)) {
if(isDetached(elements.twitch[key][0])) {
elements.twitch[key][0] = document.querySelector(elements.twitch[key][1]);
if(elements.twitch[key][0] == null) {
return;
}
}
}
//Handle page switch event
if(in_dktd !== global_in_dktd) {
//Handle a scenario where the user switches away from the DKTD page
if(!in_dktd && global_in_dktd) {
if(elements.custom.holder != null && !isDetached(elements.custom.holder))
elements.custom.holder.remove();
}
//Finally, make sure these match at the end
global_in_dktd = in_dktd;
//Exit, so the custom container isn't rebuilt
return;
}
//Rebuild our custom container and all elements within if it is detached
if(isDetached(elements.custom.holder)) {
elements.custom.holder = document.createElement('div');
elements.twitch.container[0].prepend(elements.custom.holder);
elements.custom.holder.innerHTML = holder_html;
//Create use specific containers and add them to our container
elements.custom.holder_item = elements.custom.holder.querySelector('[data-dktd=holder_item]');
elements.custom.holder_idle = elements.custom.holder.querySelector('[data-dktd=holder_idle]');
elements.custom.holder_sorry = elements.custom.holder.querySelector('[data-dktd=holder_sorry]');
elements.custom.text_volume = elements.custom.holder.querySelector('[data-dktd=text_volume]');
elements.custom.slider_volume = elements.custom.holder.querySelector('[data-dktd=slider_volume]');
elements.custom.toggle_notify_idle = elements.custom.holder.querySelector('[data-dktd=toggle_notify_idle]');
elements.custom.audio_notify_idle = elements.custom.holder.querySelector('[data-dktd=audio_notify_idle]');
elements.custom.toggle_notify_idle_keep_notifying = elements.custom.holder.querySelector('[data-dktd=toggle_notify_idle_keep_notifying]');
elements.custom.value_notify_idle_keep_notifying = elements.custom.holder.querySelector('[data-dktd=value_notify_idle_keep_notifying]');
elements.custom.toggle_notify_bone = elements.custom.holder.querySelector('[data-dktd=toggle_notify_bone]');
elements.custom.audio_notify_bone = elements.custom.holder.querySelector('[data-dktd=audio_notify_bone]');
elements.custom.toggle_notify_golden = elements.custom.holder.querySelector('[data-dktd=toggle_notify_golden]');
elements.custom.audio_notify_golden = elements.custom.holder.querySelector('[data-dktd=audio_notify_golden]');
elements.custom.toggle_notify_leave = elements.custom.holder.querySelector('[data-dktd=toggle_notify_leave]');
elements.custom.audio_notify_leave = elements.custom.holder.querySelector('[data-dktd=audio_notify_leave]');
elements.custom.toggle_notify_warp = elements.custom.holder.querySelector('[data-dktd=toggle_notify_warp]');
elements.custom.audio_notify_warp = elements.custom.holder.querySelector('[data-dktd=audio_notify_warp]');
elements.custom.options_button = elements.custom.holder.querySelector('[data-dktd=options_button]');
elements.custom.options_panel = elements.custom.holder.querySelector('[data-dktd=options_panel]');
elements.custom.options_panel_header_close = elements.custom.holder.querySelector('[data-dktd=options_panel_header_close]');
//Fill audio select elements and add associated events
for(let obj of [
{
elem: elements.custom.audio_notify_idle,
type: 'short',
setting: 'audio_notify_idle'
}, {
elem: elements.custom.audio_notify_bone,
type: 'medium',
setting: 'audio_notify_bone'
}, {
elem: elements.custom.audio_notify_golden,
type: 'medium',
setting: 'audio_notify_golden'
}, {
elem: elements.custom.audio_notify_leave,
type: 'bad',
setting: 'audio_notify_leave'
}, {
elem: elements.custom.audio_notify_warp,
type: 'medium',
setting: 'audio_notify_warp'
}]) {
let elem = obj.elem;
let type = obj.type;
for(let keyval of Object.entries(audio)) {
if(keyval[1].type !== type) continue;
let option = document.createElement('option');
option.value = keyval[0];
option.textContent = keyval[0];
elem.appendChild(option);
}
elem.addEventListener('change', e => {
settings[obj.setting] = e.target.value;
saveSettings();
audio[e.target.value].audio.play();
});
}
//Style holder
elements.custom.holder.style.display = 'flex';
elements.custom.holder.style.flexFlow = 'column nowrap';
elements.custom.holder.style.width = '100%';
//Load settings
Object.assign(settings, JSON.parse(localStorage.getItem('dktd:settings')));
//Old version compatibility
if(typeof localStorage.getItem('dktd:toggle_notify') === 'string') {
settings.toggle_notify_idle = !!localStorage.getItem('dktd:toggle_notify');
localStorage.removeItem('dktd:toggle_notify');
}
saveSettings();
//Load state
elements.custom.slider_volume.value = settings.slider_volume;
onVolumeChanged();
elements.custom.toggle_notify_idle.checked = !!settings.toggle_notify_idle;
elements.custom.toggle_notify_bone.checked = !!settings.toggle_notify_bone;
elements.custom.toggle_notify_golden.checked = !!settings.toggle_notify_golden;
elements.custom.toggle_notify_leave.checked = !!settings.toggle_notify_leave;
elements.custom.toggle_notify_warp.checked = !!settings.toggle_notify_warp;
elements.custom.audio_notify_idle.value = settings.audio_notify_idle;
elements.custom.audio_notify_bone.value = settings.audio_notify_bone;
elements.custom.audio_notify_golden.value = settings.audio_notify_golden;
elements.custom.audio_notify_leave.value = settings.audio_notify_leave;
elements.custom.audio_notify_warp.value = settings.audio_notify_warp;
elements.custom.toggle_notify_idle_keep_notifying.checked = !!settings.toggle_notify_idle_keep_notifying;
elements.custom.value_notify_idle_keep_notifying.value = +settings.value_notify_idle_keep_notifying;
//Initialize observer
observer.disconnect();
observer.observe(elements.twitch.chat[0], { childList: true });
//Add event listeners
elements.twitch.chat_send[0].addEventListener('click', e => {
if(elements.twitch.chat_input[0].textContent.length > 0)
messageTyped();
});
elements.twitch.chat_input[0].addEventListener('keydown', e => {
if(elements.twitch.chat_input[0].textContent.length > 0 && e.code === 'Enter')
messageTyped();
});
elements.custom.slider_volume.addEventListener('input', onVolumeChanged);
elements.custom.slider_volume.addEventListener('change', onVolumeChanged);
elements.custom.toggle_notify_idle.addEventListener('input', e => {
settings.toggle_notify_idle = e.target.checked;
saveSettings();
});
elements.custom.toggle_notify_idle_keep_notifying.addEventListener('input', e => {
settings.toggle_notify_idle_keep_notifying = e.target.checked;
saveSettings();
});
elements.custom.value_notify_idle_keep_notifying.addEventListener('input', e => {
e.target.value = e.target.value.replace(/\D/g, '');
e.target.value = e.target.value.substring(0, 3);
settings.value_notify_idle_keep_notifying = +e.target.value;
saveSettings();
});
elements.custom.toggle_notify_bone.addEventListener('input', e => {
settings.toggle_notify_bone = e.target.checked;
saveSettings();
});
elements.custom.toggle_notify_golden.addEventListener('input', e => {
settings.toggle_notify_golden = e.target.checked;
saveSettings();
});
elements.custom.toggle_notify_leave.addEventListener('input', e => {
settings.toggle_notify_leave = e.target.checked;
saveSettings();
});
elements.custom.toggle_notify_warp.addEventListener('input', e => {
settings.toggle_notify_warp = e.target.checked;
saveSettings();
});
elements.custom.options_button.addEventListener('click', e => {
if(e.target === e.currentTarget) {
const elem = elements.custom.options_panel;
elem.style.display = elem.style.display === 'none' || elem.style.display === '' ? 'initial' : 'none';
}
});
elements.custom.options_panel_header_close.addEventListener('click', e => {
elements.custom.options_panel.style.display = 'none';
});
}
}
function observerCallback(mutations, observer) {
for(const mutation of mutations) {
for(const node of mutation.addedNodes) {
let str = node.textContent;
//Exit if dktdbot haven't typed the message
{
let ffz_check = node.getAttribute('data-user') === 'dktdbot';
let vanilla_elem = node.querySelector('[data-a-target="chat-message-username"]');
let vanilla_check = vanilla_elem ? vanilla_elem.getAttribute('data-a-user') === 'dktdbot' : false;
if(!(ffz_check || vanilla_check)) continue;
}
//Play audio if dog bone leveled up
if(settings.toggle_notify_bone && str.indexOf('The Dog has reached') > -1) {
audio[settings.audio_notify_bone].audio.play();
}
//Play audio if golden dog started
if(settings.toggle_notify_golden && str.indexOf('Golden Dog!') > -1) {
audio[settings.audio_notify_golden].audio.play();
}
//Play audio if dog is leaving
if(settings.toggle_notify_leave) {
if(str.toLowerCase().indexOf('the dog leaves in') > -1 || str.toLowerCase().indexOf('the dog has left') > -1) {
audio[settings.audio_notify_leave].audio.play();
}
}
//Play audio if warp
if(settings.toggle_notify_warp && str.indexOf('A player has started claiming a Stronghold Portal') > -1) {
audio[settings.audio_notify_warp].audio.play();
}
//Exit if the logged in user was not mentioned
{
let ffz_check = node.querySelector('.ffz--mention-me') == null;
let vanilla_check = node.querySelector('.mention-fragment--recipient') == null;
if(ffz_check && vanilla_check) continue;
}
//Exit if the message does not contain the proper keywords
//Check for `use` command
//Reference messages:
//"used (ADA) [1⭐Rapid Cop] ...and 1 other Items. You have 2 Items on 🕒Cooldown. (50s) (115s)"
//"You have 1 Items on 🕒Cooldown. (28s)"
if(str.indexOf('Items on') > -1 && str.indexOf('Cooldown') > -1) {
//Split the item use message so we only keep the cooldowns part
//e.g. (120s) (143s) (68s) (69s) (60s)
str = node.textContent.split('Cooldown')[1];
//Split into an array
let arr = str.split('(');
arr.splice(0, 1);
//Reset the cooldown timers
timers.deleteTimers('item');
//Build new cooldown timers
for(let i = 0; i < arr.length; i++) {
let cooldown = +(arr[i].replaceAll(',', '').match(/\d+/g));
timers.addTimer(new TimerBasic(2, 'item', cooldown * 1000, '⚔', true));
}
}
//Check for `sorry` command
//Reference message: "is now on Probation, and cannot KICK for 2 minutes."
else if(str.indexOf('is now on Probation') > -1) {
//Refresh timer
timers.deleteTimers('sorry');
timers.addTimer(new TimerBasic(1, 'sorry', 1000 * 60 * 2, '👮', true));
}
//Check for `riot` command
//Reference message: "You are on Probation for 51s"
else if(str.indexOf('You are on Probation for') > -1) {
//Cut string to just the time
str = str.split('Probation for')[1];
//Get the remaining time
let cooldown = +(arr[i].match(/\d+/g));
//Refresh timer
timers.deleteTimers('sorry');
timers.addTimer(new TimerBasic(1, 'sorry', cooldown * 1000, '👮', true));
}
}
}
}
function messageTyped() {
timers.deleteTimers('idle');
timers.addTimer(new TimerIdle(0, 'idle', 1000 * 60 * 10, '🕑', true));
}
function saveSettings() {
localStorage.setItem('dktd:settings', JSON.stringify(settings));
}
function getFormattedTime(seconds) {
let hours = Math.floor(seconds / 60 / 60);
seconds = seconds - hours * 60 * 60;
let minutes = Math.floor(seconds / 60);
seconds = seconds - minutes * 60;
if(hours != 0)
return `${hours}:${minutes < 10 ? `0${minutes}` : minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
else if(minutes != 0)
return `${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
else
return `${seconds}`;
}
function isDetached(elem) {
return elem == null || !elem.isConnected;
}
function onVolumeChanged() {
settings.slider_volume = elements.custom.slider_volume.value;
saveSettings();
elements.custom.text_volume.textContent = settings.slider_volume;
audio["Glassy Chime"].audio.play();
for(let obj of Object.values(audio)) {
obj.audio.volume = settings.slider_volume / 100;
}
}
})();