Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Kurotaku-sama/bf8fef7e64b6954d6fad35f3682acc5e to your computer and use it in GitHub Desktop.
Save Kurotaku-sama/bf8fef7e64b6954d6fad35f3682acc5e to your computer and use it in GitHub Desktop.
Adds buttons to send commands for the onstream games in the twitch chat
// ==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);
@MAX1MDEV
Copy link

MAX1MDEV commented May 4, 2024

after 3 uses the script stopped working, buttons are not pressed

@Kurotaku-sama
Copy link
Author

after 3 uses the script stopped working, buttons are not pressed

You need to wait a couple of seconds before you send the same command multiple times or commands to fast, otherwise twitch prevents spam in the IRC Chat.
I've already integrated a bypass into the script that generate a custom id for the commands, to prevent the anti-spam to occur, but GodFather told me to remove this feature.

@MAX1MDEV
Copy link

MAX1MDEV commented May 5, 2024

after 3 uses the script stopped working, buttons are not pressed

You need to wait a couple of seconds before you send the same command multiple times or commands to fast, otherwise twitch prevents spam in the IRC Chat. I've already integrated a bypass into the script that generate a custom id for the commands, to prevent the anti-spam to occur, but GodFather told me to remove this feature.

still doesn't work :(

@Kurotaku-sama
Copy link
Author

after 3 uses the script stopped working, buttons are not pressed

You need to wait a couple of seconds before you send the same command multiple times or commands to fast, otherwise twitch prevents spam in the IRC Chat. I've already integrated a bypass into the script that generate a custom id for the commands, to prevent the anti-spam to occur, but GodFather told me to remove this feature.

still doesn't work :(

Sry than i cant rly help you bcs I can't reproduce it, what you could try is in line 188:
sendMessageToTwitchChat(suffix + cmd); // + ' - ' + makeid(5));
turn it into this:
sendMessageToTwitchChat(suffix + cmd + ' - ' + makeid(5));

Tell me if it worked than please, if it works I will make this as an separate option in the settings.

@MAX1MDEV
Copy link

MAX1MDEV commented May 6, 2024

two minute please

@MAX1MDEV
Copy link

MAX1MDEV commented May 6, 2024

after 3 uses the script stopped working, buttons are not pressed

You need to wait a couple of seconds before you send the same command multiple times or commands to fast, otherwise twitch prevents spam in the IRC Chat. I've already integrated a bypass into the script that generate a custom id for the commands, to prevent the anti-spam to occur, but GodFather told me to remove this feature.

still doesn't work :(

Sry than i cant rly help you bcs I can't reproduce it, what you could try is in line 188: sendMessageToTwitchChat(suffix + cmd); // + ' - ' + makeid(5)); turn it into this: sendMessageToTwitchChat(suffix + cmd + ' - ' + makeid(5));

Tell me if it worked than please, if it works I will make this as an separate option in the settings.

now he's sending messages like this: !hitsquad - slqyh

@Kurotaku-sama
Copy link
Author

Kurotaku-sama commented May 6, 2024

Yea that is a feature I integrated but GF wanted me to disable it.
The purpose of the code behind is that you dont send every time the same message so you don't get shadow banned for a specific time.
So tell me if this works now without any issues, than I will update the script to make this an optional feature in the Settings.

@MAX1MDEV
Copy link

MAX1MDEV commented May 6, 2024

Yea that is a feature I integrated but GF wanted me to disable it. The purpose of the code behind is that you dont send every time the same message so you don't get shadow banned for a specific time. So tell me if this works now without any issues, than I will update the script to make this an optional feature in the Settings.

yes, now always ok, ty <3

@Kurotaku-sama
Copy link
Author

Yea that is a feature I integrated but GF wanted me to disable it. The purpose of the code behind is that you dont send every time the same message so you don't get shadow banned for a specific time. So tell me if this works now without any issues, than I will update the script to make this an optional feature in the Settings.

yes, now always ok, ty <3

Okay I've updated now the script, in the settings of the script is now a new option to enable this shadowban bypass, since I never know when the addons are updating the scripts I recommend doing it manual for now .... I also was shadowbanned the whole day somehow.

@ruzzen
Copy link

ruzzen commented May 7, 2024

I've a trouble with sending commands, after pressing any button it adds "}" in the end, like "!hitsquad}"

@Kurotaku-sama
Copy link
Author

I've a trouble with sending commands, after pressing any button it adds "}" in the end, like "!hitsquad}"

Yea I fixed it a couple of hours ago right after I saw it, sorry for that.
Just reinstall to get the latest release instantly instead of waiting.
Sorry for the inconvenience

@MAX1MDEV
Copy link

MAX1MDEV commented May 8, 2024

work!!!

@MAX1MDEV
Copy link

MAX1MDEV commented May 8, 2024

always ok!!

@Kurotaku-sama
Copy link
Author

Kurotaku-sama commented May 8, 2024

work!!!

I will change sth in a couple of days, so the commands are upper and lower case shufled instead of an ID, if you still get shadowbanned for that tell me on Discord than please. (Name is just Kurotaku there)

@MAX1MDEV
Copy link

i add you, but check your code tomorrow (i'm busy)

@Kurotaku-sama
Copy link
Author

i add you, but check your code tomorrow (i'm busy)

Write me your username or write me a PM instead of adding me, recently I get so extremly much random friendinvites that I had to disable friend requests.

@Kurotaku-sama
Copy link
Author

I am really sorry somehow the auto update for all my userscripts didn't worked for quiet a while now.
Everyone who is using any of my userscripts must reinstall the userscripts, I fixed them all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment