|
// ==UserScript== |
|
// @name Twitch hitsquadgodfather command buttons |
|
// @namespace https://kurotaku.de |
|
// @version 1.5.8 |
|
// @description Adds buttons to send commands for the onstream games in the Twitch chat |
|
// @author Kurotaku |
|
// @license MIT |
|
// @match https://www.twitch.tv/hitsquadgodfather* |
|
// @match https://www.twitch.tv/hitsquadbrawlers* |
|
// @match https://www.twitch.tv/hitsquadplays* |
|
// @match https://www.twitch.tv/*/hitsquadgodfather/chat* |
|
// @match https://www.twitch.tv/*/hitsquadbrawlers/chat* |
|
// @match https://www.twitch.tv/*/hitsquadplays/chat* |
|
// @icon https://static.twitchcdn.net/assets/favicon-32-e29e246c157142c94346.png |
|
// @updateURL https://gist.github.com/Kurotaku-sama/bf8fef7e64b6954d6fad35f3682acc5e/raw/Twitch%2520hitsquadgodfather%2520command%2520buttons.user.js |
|
// @downloadURL https://gist.github.com/Kurotaku-sama/bf8fef7e64b6954d6fad35f3682acc5e/raw/Twitch%2520hitsquadgodfather%2520command%2520buttons.user.js |
|
// @require https://gist.github.com/Kurotaku-sama/9ebeb659500f6eee2f780344948e1e8f/raw/kuros_library.user.js |
|
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js |
|
// @grant GM_getValue |
|
// @grant GM_setValue |
|
// @grant GM_registerMenuCommand |
|
// ==/UserScript== |
|
|
|
|
|
const twitch_host = 'irc-ws.chat.twitch.tv'; |
|
const twitch_port = 443; |
|
let twitch_channel; // Variable to store channel name later |
|
let socket; |
|
let timer; |
|
let reconnect_interval = 5000; |
|
|
|
|
|
(async function() { |
|
await init_gm_config(); // Initialize the configuration |
|
|
|
if(GM_config.get("script_enabled")) |
|
if(GM_config.get("auth_username") != "" && GM_config.get("auth_oauth") != "") { |
|
wait_for_element('.chat-input').then(async () => { |
|
|
|
if(GM_config.get("voucher_buttons")) |
|
add_voucher_buttons(); |
|
|
|
twitch_channel = document.title.split(" ")[0]; // Get channel name here bcs in the first view milliseconds the document title is only "Twitch" |
|
if(twitch_channel === "Twitch") // Fallback for sites like Multistre.am |
|
twitch_channel = "hitsquadgodfather"; |
|
|
|
connect_to_twitch(); |
|
add_command_buttons(); |
|
}); |
|
} |
|
else // Warning if people haven't set up the oauth token correctly |
|
wait_for_element('.chat-input').then(async () => { |
|
let html = `<div id="actions" class="k-buttongroups"><label class="k-buttongroup-label">Hello thanks for installing my Twitch hitsquadgodfather command buttons userscript.<br><br>Click on the Tampermonkey extension and click on settings underneath the script and insert your username + oauth token in order to be able to connect to the chat with this script.</label></div>`; |
|
document.querySelector(".chat-input").insertAdjacentHTML('beforebegin', html); |
|
}); |
|
})(); |
|
|
|
|
|
function init_gm_config() { |
|
GM_registerMenuCommand('Settings', () => { |
|
GM_config.open(); |
|
}); |
|
let frame = document.createElement('div'); |
|
document.body.appendChild(frame); |
|
GM_config.init( |
|
{ |
|
'id': 'configuration', |
|
'title': 'Twitch hitsquadgodfather config', |
|
'fields': |
|
{ |
|
'script_enabled': |
|
{ |
|
'label': 'Enable/Disable the script', |
|
'type': 'checkbox', |
|
'default': true |
|
}, |
|
'auth_username': |
|
{ |
|
'label': 'Username', |
|
'type': 'textbox' |
|
}, |
|
'auth_oauth': |
|
{ |
|
'label': 'Oauth (required for IRC connection to chat!)<br>Can be generated here: https://twitchapps.com/tmi/', |
|
'type': 'textbox' |
|
}, |
|
'voucher_buttons': { |
|
'label': 'Voucher redemption buttons', |
|
'type': 'checkbox', |
|
'default': true |
|
}, |
|
// 'voucher_buttons_point_check': { |
|
// 'label': 'Point check for vouchers:<br>Disable this if you can\'t redeem vouchers even when you have the amount of required points.', |
|
// 'type': 'checkbox', |
|
// 'default': true |
|
// }, |
|
'buttons_general': |
|
{ |
|
'label': 'General buttons', |
|
'type': 'checkbox', |
|
'default': true |
|
}, |
|
'buttons_trivia': |
|
{ |
|
'label': 'Trivia buttons', |
|
'type': 'checkbox', |
|
'default': true |
|
}, |
|
'showdown_buttons': { |
|
'label': 'Showdown buttons', |
|
'type': 'checkbox', |
|
'default': true |
|
}, |
|
'bypass_shadowban': { |
|
'label': 'Bypass Shadowban:<br>Enable this only, if you get shadowbanned after a couple of commands.<br>(Shadowban is when your messages are temporarily not appear)', |
|
'type': 'checkbox', |
|
'default': false |
|
}, |
|
}, |
|
'events': { |
|
'save': () => {location.reload()}, |
|
}, |
|
'frame': frame, |
|
'css': '#configuration {color: black; height:auto !important; width:auto !important; padding:20px !important;max-height: 600px !important;max-width:500px !important; border: 3px solid #000 !important} #configuration .section_header {background: unset; color:unset;} #configuration .config_header {font-size:17pt; font-weight:bold} #configuration .config_var {margin-top:10px; display: flex;} .config_var :nth-child(2) {order:-1; margin-right:10px;} #configuration_buttons_holder {text-align: center;} #configuration #configuration_resetLink {color:#fff;}' |
|
}); |
|
} |
|
|
|
|
|
function connect_to_twitch() { |
|
socket = new WebSocket(`wss://${twitch_host}:${twitch_port}`); |
|
socket.onopen = () => { |
|
console.log('Twitch connection started.'); |
|
|
|
socket.send(`PASS ${GM_config.get("auth_oauth").includes("oauth:") ? GM_config.get("auth_oauth") : "oauth:" + GM_config.get("auth_oauth")}`); |
|
socket.send(`NICK ${GM_config.get("auth_username")}`); |
|
socket.send(`JOIN #${twitch_channel}`); |
|
|
|
timer = setInterval(function() { |
|
socket.send('PING :tmi.twitch.tv'); |
|
}, 5 * 60 * 1000); // Send all 5 minutes a ping |
|
}; |
|
|
|
socket.onclose = function() { |
|
console.log('Twitch connection closed.'); |
|
|
|
// Stoppe den Timer, wenn die Verbindung geschlossen wurde |
|
clearInterval(timer); |
|
|
|
// Versuche, die Verbindung automatisch neu zu öffnen |
|
setTimeout(function() { |
|
connect_to_twitch(); |
|
}, reconnect_interval); |
|
}; |
|
} |
|
|
|
|
|
function close_target() { |
|
switch_panel(null); |
|
} |
|
|
|
|
|
function switch_panel(event) { |
|
document.querySelector("#actions").classList.toggle("hidden"); |
|
document.querySelector("#targets").classList.toggle("hidden"); |
|
if(event) |
|
document.querySelector("#targets").setAttribute("data-action", event.target.getAttribute("cmd")); |
|
} |
|
|
|
|
|
function show_role(event) { |
|
let roles = document.querySelectorAll(".k-role"); |
|
roles.forEach(el => { |
|
if(el.getAttribute("data-role") === event.target.getAttribute("data-role")) |
|
el.classList.remove("hidden") |
|
else |
|
el.classList.add("hidden") |
|
}); |
|
} |
|
|
|
|
|
function send_command(event) { |
|
let cmd = ""; |
|
if(event.target.parentNode.parentNode.getAttribute("data-action")) { |
|
cmd = event.target.parentNode.parentNode.getAttribute("data-action"); // Add action attack or devine in case its from the switched panel |
|
// Remove the data and go back to main panel |
|
event.target.parentNode.parentNode.setAttribute("data-action", ""); |
|
switch_panel(null); |
|
} |
|
cmd += event.target.getAttribute("cmd"); |
|
|
|
let suffix = "!"; |
|
if(cmd.trim() !== "" && cmd !== null) |
|
if(GM_config.get("bypass_shadowban")) |
|
sendMessageToTwitchChat(`${suffix}${randomize_case(cmd)}`); |
|
else |
|
sendMessageToTwitchChat(`${suffix}${cmd}`); |
|
else |
|
alert("Please contact script creator, this button doesn't seem to work correctly"); |
|
} |
|
|
|
|
|
function sendMessageToTwitchChat(message) { |
|
socket.send(`PRIVMSG #${twitch_channel} :${message}`); |
|
|
|
socket.onerror = (error) => { |
|
console.error('Error:', error); |
|
}; |
|
} |
|
|
|
|
|
function add_command_buttons() { |
|
let buttongroups = ""; |
|
if(GM_config.get("buttons_general")) |
|
buttongroups += `<label class="k-buttongroup-label">General</label> |
|
<div class="k-buttongroup"> |
|
<button cmd="hitsquad" class="actionbutton">Hitsquad</button> |
|
<button cmd="strikes" class="actionbutton">Strikes</button> |
|
</div>`; |
|
if(GM_config.get("buttons_trivia")) |
|
buttongroups += `<label class="k-buttongroup-label">Trivia</label> |
|
<div class="k-buttongroup"> |
|
<button cmd="answer1" class="actionbutton">1</button> |
|
<button cmd="answer2" class="actionbutton">2</button> |
|
<button cmd="answer3" class="actionbutton">3</button> |
|
<button cmd="answer4" class="actionbutton">4</button> |
|
<button cmd="triviapoints" class="actionbutton">Points</button> |
|
</div>`; |
|
if(GM_config.get("showdown_buttons")) { |
|
// Selection |
|
buttongroups += `<label class="k-buttongroup-label">Showdown</label> |
|
<div class="k-buttongroup"> |
|
<button cmd="wizard" class="actionbutton">Wizard</button> |
|
<button cmd="knight" class="actionbutton">Knight</button> |
|
<button cmd="cleric" class="actionbutton">Cleric</button> |
|
<button cmd="experience" class="actionbutton">Experience</button> |
|
</div>`; |
|
// Labels to show roles |
|
buttongroups += `<div class="k-labelgroup"> |
|
<label class="k-selection-label" data-role="wizard">Wizard</label> |
|
<label class="k-selection-label" data-role="knight">Knight</label> |
|
<label class="k-selection-label" data-role="cleric">Cleric</label> |
|
<label class="k-selection-label" data-role="close">Close</label> |
|
</div>`; // Wizard |
|
buttongroups += `<div class="k-buttongroup k-role hidden" data-role="wizard"> |
|
<button cmd="attack" class="targetbutton">Attack</button> |
|
<button cmd="flames" class="actionbutton">Flames</button> |
|
<button cmd="shield" class="actionbutton">Shield</button> |
|
<button cmd="moan" class="actionbutton">Moan</button></div>`; |
|
// Knight |
|
buttongroups += `<div class="k-buttongroup k-role hidden" data-role="knight"> |
|
<button cmd="attack" class="targetbutton">Attack</button> |
|
<button cmd="frenzy" class="actionbutton">Frenzy</button> |
|
<button cmd="rally" class="actionbutton">Rally</button> |
|
<button cmd="moan" class="actionbutton">moan</button></div>`; |
|
// Cleric |
|
buttongroups += `<div class="k-buttongroup k-role hidden" data-role="cleric"> |
|
<button cmd="attack" class="targetbutton">Attack</button> |
|
<button cmd="divine" class="targetbutton">Divine</button> |
|
<button cmd="heal" class="actionbutton">Heal</button> |
|
<button cmd="moan" class="actionbutton">Moan</button> |
|
</div>`; |
|
} |
|
|
|
let html = `<div id="actions" class="k-buttongroups">${buttongroups}</div>`; |
|
html += `<div id="targets" class="k-buttongroups hidden" data-action=""> |
|
<label class="k-buttongroup-label targets">Targets</label> |
|
<div class="k-buttongroup"> |
|
<button cmd="1" class="actionbutton">1</button> |
|
<button cmd="2" class="actionbutton">2</button> |
|
<button cmd="3" class="actionbutton">3</button> |
|
<button cmd="4" class="actionbutton">4</button> |
|
<button cmd="5" class="actionbutton">5</button> |
|
<button class="closebutton">Close</button> |
|
</div>`; |
|
|
|
document.querySelector(".chat-input").insertAdjacentHTML('beforebegin', html); |
|
|
|
if(document.querySelector(".k-buttongroup .closebutton")) |
|
document.querySelector(".k-buttongroup .closebutton").addEventListener("click", close_target, false); |
|
|
|
let actionbuttons = document.querySelectorAll(".k-buttongroup .actionbutton"); |
|
actionbuttons.forEach(el => {el.addEventListener("click", send_command, false)}); |
|
let targetbuttons = document.querySelectorAll(".k-buttongroup .targetbutton"); |
|
targetbuttons.forEach(el => {el.addEventListener("click", switch_panel, false)}); |
|
let selection_labels = document.querySelectorAll(".k-selection-label"); |
|
selection_labels.forEach(el => {el.addEventListener("click", show_role, false)}); |
|
} |
|
|
|
|
|
async function add_voucher_buttons() { |
|
wait_for_element('.chat-input__buttons-container').then(async () => { |
|
let html = `<div class="k-buttongroups"><div class="k-buttongroup"> |
|
<button id="add_1k" voucher="1,000 Clams Voucher" cost="5000" class="actionbutton">+1k Clams</button> |
|
<button id="add_2k" voucher="2,000 Clams Voucher" cost="10000" class="actionbutton">+2k Clams</button> |
|
</div></div>` |
|
document.querySelector(".chat-input").insertAdjacentHTML('afterend', html); |
|
document.querySelector("#add_1k").addEventListener("click", add_clams, false); |
|
document.querySelector("#add_2k").addEventListener("click", add_clams, false); |
|
}); |
|
} |
|
|
|
|
|
async function add_clams(trigger) { |
|
let voucher = trigger.target.attributes.voucher.value; |
|
let cost = parseInt(trigger.target.attributes.cost.value); |
|
let storebutton = document.querySelector(".community-points-summary button"); |
|
// let points = parseInt(storebutton.querySelector(`div[data-test-selector="balance-string"] > span`).innerHTML.toLowerCase() |
|
// .replace(".", "").replace(",", "").replace(" ", "") |
|
// .replace("k", "000").replace("万", "000").replace("萬", "000").replace("만", "000").replace("тыс", "000").replace("tis", "000") |
|
// .replace("mio", "000000").replace("mil", "000000").replace("mill", "000000").replace("tr", "000000") |
|
// .replace("млн", "000000").replace("εκ", "000000").replace("mn", "000000").replace("mln", "000000").replace("m", "000000")); |
|
// if(GM_config.get("voucher_buttons_point_check") == false || points >= cost) { // Check if you have enough points (when not disabled by the point check) |
|
storebutton.click(); |
|
wait_for_element('.rewards-list').then(async () => { // Wait till rewards list is showing |
|
let rewards = document.querySelector(".rewards-list"); |
|
let reward = rewards.querySelector(`img[alt="${voucher}"]`); |
|
if(reward) { // Open the voucher buy menu |
|
reward.click(); |
|
wait_for_element('.reward-center-body button.ScCoreButton-sc-ocjdkq-0').then(async () => { // Wait till voucher item is showing |
|
let reward_redeem_button = document.querySelector(".reward-center-body button.ScCoreButton-sc-ocjdkq-0"); |
|
if(reward_redeem_button.disabled == false) |
|
reward_redeem_button.click(); |
|
else { |
|
storebutton.click(); |
|
alert("Error: Reward not available, maybe you reached maximum amount of claims for this stream or you don't have enough channel points!"); |
|
} |
|
}); |
|
} |
|
else |
|
alert("Error: Reward not found maybe they are disabled at the moment, if not than please contact script creator via Discord"); |
|
}); |
|
// } |
|
// else |
|
// alert("You don't have enough channel points for this Action!<br>If you are sure that you have enough points and this is a mistake, go into the userscript settings and disable \"Point check for vouchers\""); |
|
} |
|
|
|
|
|
// Add custom styles |
|
let styles = ` |
|
.actionbutton { |
|
min-width:40px; |
|
|
|
|
|
background-color: var(--color-background-button-primary-default); |
|
color: var(--color-text-button-primary); |
|
|
|
display: inline-flex; |
|
position: relative; |
|
-moz-box-align: center; |
|
align-items: center; |
|
-moz-box-pack: center; |
|
justify-content: center; |
|
vertical-align: middle; |
|
overflow: hidden; |
|
text-decoration: none; |
|
text-decoration-color: currentcolor; |
|
white-space: nowrap; |
|
user-select: none; |
|
font-weight: var(--font-weight-semibold); |
|
font-size: var(--button-text-default); |
|
height: var(--button-size-default); |
|
border-radius: var(--input-border-radius-default); |
|
} |
|
|
|
.k-buttongroups { |
|
padding-left: 20px; |
|
padding-right: 20px;} |
|
|
|
.k-buttongroup { |
|
display: flex; |
|
flex-wrap: wrap; |
|
grid-column-gap: 10px;} |
|
|
|
.k-buttongroup-label {} |
|
|
|
.k-buttongroup > button { |
|
min-width:50px; |
|
margin-bottom:5px;} |
|
|
|
.k-labelgroup { |
|
margin-top: 10px; |
|
font-size: 20px; |
|
justify-content: space-between; |
|
display: flex;} |
|
.hidden { display:none;} |
|
`; |
|
// Insert custom Styles |
|
let style_sheet = document.createElement("style"); |
|
style_sheet.innerText = styles; |
|
document.head.appendChild(style_sheet); |
after 3 uses the script stopped working, buttons are not pressed