Instantly share code, notes, and snippets.
Last active
June 15, 2024 04:04
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save Kurotaku-sama/090f27bf20be879ddd5d0de88af3ac7f to your computer and use it in GitHub Desktop.
Adds buttons to send commands in the Twitch chat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Twitch staggerrilla command buttons | |
// @namespace https://kurotaku.de | |
// @version 1.0.9 | |
// @description Adds buttons to send commands in the Twitch chat | |
// @author Kurotaku | |
// @license MIT | |
// @match https://www.twitch.tv/staggerrilla* | |
// @icon https://static.twitchcdn.net/assets/favicon-32-e29e246c157142c94346.png | |
// @updateURL https://gist.github.com/Kurotaku-sama/090f27bf20be879ddd5d0de88af3ac7f/raw/Twitch%2520staggerrilla%2520command%2520buttons.user.js | |
// @downloadURL https://gist.github.com/Kurotaku-sama/090f27bf20be879ddd5d0de88af3ac7f/raw/Twitch%2520staggerrilla%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 = 'staggerrilla'; // 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(); | |
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 staggerrilla 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 staggerrilla 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/<br><b>Warning: If you already generated such a oauth token for another channel, copy it from that script and paste it here!</b>', | |
'type': 'textbox' | |
}, | |
'voucher_buttons': { | |
'label': 'Voucher redemption buttons', | |
'type': 'checkbox', | |
'default': true | |
}, | |
'buttons_general': | |
{ | |
'label': 'General 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 temporaril 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="fish" class="actionbutton">Fish</button> | |
<button cmd="bloop" class="actionbutton">Bloop</button> | |
<button cmd="bub" class="actionbutton">Bub</button> | |
<button cmd="bleep" class="actionbutton">Bleep</button> | |
<button cmd="insert 75" class="actionbutton">Insert 75</button> | |
</div>`; | |
let html = `<div id="actions" class="k-buttongroups">${buttongroups}</div>`; | |
document.querySelector(".chat-input").insertAdjacentHTML('beforebegin', html); | |
let actionbuttons = document.querySelectorAll(".k-buttongroup .actionbutton"); | |
actionbuttons.forEach(el => {el.addEventListener("click", send_command, 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_75k" voucher="75k Bubbers" cost="115000" class="actionbutton">+75k</button> | |
<button id="add_100k" voucher="100k Bubbers" cost="150000" class="actionbutton">+100k</button> | |
<button id="add_200k" voucher="300k" cost="300000" class="actionbutton">+200k</button> | |
</div></div>` | |
document.querySelector(".chat-input").insertAdjacentHTML('afterend', html); | |
document.querySelector("#add_75k").addEventListener("click", add_bubs, false); | |
document.querySelector("#add_100k").addEventListener("click", add_bubs, false); | |
document.querySelector("#add_200k").addEventListener("click", add_bubs, false); | |
}); | |
} | |
async function add_bubs(trigger) { | |
let voucher = trigger.target.attributes.voucher.value; | |
let cost = parseInt(trigger.target.attributes.cost.value); | |
let storebutton = document.querySelector(".community-points-summary button"); | |
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"); | |
}); | |
} | |
// 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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment