Skip to content

Instantly share code, notes, and snippets.

@Kurotaku-sama
Last active June 25, 2024 13:24
Show Gist options
  • Save Kurotaku-sama/0a7f8373992756116940f31716e04a01 to your computer and use it in GitHub Desktop.
Save Kurotaku-sama/0a7f8373992756116940f31716e04a01 to your computer and use it in GitHub Desktop.
A script for some improvements for StreamElements
// ==UserScript==
// @name StreamElements improvements
// @name:de StreamElements Verbesserungen
// @namespace https://kurotaku.de
// @version 1.5.4
// @description A script for some improvements for StreamElements
// @description:de Ein Skript für einige Verbesserungen für StreamElements
// @author Kurotaku
// @license MIT
// @match https://streamelements.com/*/store
// @icon https://cdn.streamelements.com/static/logo/logo_red.png
// @updateURL https://gist.github.com/Kurotaku-sama/0a7f8373992756116940f31716e04a01/raw/StreamElements%2520improvements.user.js
// @downloadURL https://gist.github.com/Kurotaku-sama/0a7f8373992756116940f31716e04a01/raw/StreamElements%2520improvements.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==
let sold_out_items = [];
let subscriber_only_items = [];
(function() {
init_gm_config(); // Initialize the configuration
wait_for_element('.side-bar .usr-stats').then(async () => {
insert_new_sidebar_container(); // Insert a new sidebar for the custom buttons
insert_gm_config_button(); // Add the button to the filter container to open the configuration menu
if(GM_config.get("script_enabled")) { // Check if the script is disabled in config
insert_controls();
for(let i = 0; i < 120; i++) // While the page items are still loading (2 minute limit than abort the functions)
if(document.querySelector(".loading-card"))
if(i === 120)
return; // Abort
else
await Sleep(1000); // Wait and try again
if(GM_config.get("hide_items_from_list_by_default"))
toggle_hidden_itemlist();
if(GM_config.get("gray_out_hidden_items")) // If items should be grayed out that are in hidden list
gray_out_hidden_items();
check_sold_out_items();
if(GM_config.get("hide_sold_out_items_by_default")) // Hide items when out of stock
toggle_sold_out_items();
check_subscriber_only_items();
if(GM_config.get("hide_subscriber_items_by_default")) // Hide items when out of stock
toggle_subscriber_only_items();
if(GM_config.get("gray_out_sold_out_items")) // If items should be grayed out that are out of stock
gray_out_sold_out_items();
if(GM_config.get("sort_by_price") || GM_config.get("sort_by_price_descending"))
sort_by_price();
if(GM_config.get("magnifying_glass_buttons"))
magnifying_glass_buttons();
if(GM_config.get("hide_item_buttons"))
hide_item_buttons();
}
});
})();
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': 'StreamElements improvements',
'fields':
{
'script_enabled':
{
'label': 'Enable/Disable all improvements',
'type': 'checkbox',
'default': true
},
'items_to_hide':
{
'label': 'Hide items by name<br>(each item name must be written in a new line)',
'type': 'textarea',
'default': ''
},
'hide_items_from_list_by_default':
{
'label': 'Hide items by default',
'type': 'checkbox',
'default': true
},
'hide_sold_out_items_by_default':
{
'label': 'Hide sold out items by default',
'type': 'checkbox',
'default': false
},
'hide_subscriber_items_by_default':
{
'label': 'Hide subscriber only items by default',
'type': 'checkbox',
'default': false
},
'gray_out_hidden_items':
{
'label': 'Gray out hidden items',
'type': 'checkbox',
'default': true
},
'gray_out_sold_out_items':
{
'label': 'Gray out sold out items',
'type': 'checkbox',
'default': true
},
'sort_by_price':
{
'label': 'Sort items always by price',
'type': 'checkbox',
'default': false
},
'sort_by_price_descending':
{
'label': 'Sort items always by price (descending)',
'type': 'checkbox',
'default': false
},
'magnifying_glass_buttons':
{
'label': 'Button in top left corner to search on Steam for this item!',
'type': 'checkbox',
'default': true
},
'hide_item_buttons':
{
'label': 'Button in top right corner for qick hide<br>(Warning: you still must save in this popup, to keep the changes saved!)',
'type': 'checkbox',
'default': true
},
},
'events': {
'init': () => {GM_config.set("items_to_hide", get_prepared_items_to_hide())},
'save': () => {location.reload()},
},
'frame': frame,
'css': '#configuration {background-color: #020923; color:#fff; height:auto !important; width:auto !important; padding:20px !important;max-height: 600px !important;max-width:500px !important; border: 3px solid #fff !important} #configuration .config_header {font-size:17pt; font-weight:bold} #configuration .config_var {margin-top:10px; display: flex;} #configuration_buttons_holder {text-align: center;} #configuration label {color:#fff !important} #configuration #configuration_resetLink {color:#fff;} #configuration input[type="text"] {display:block; color: #fff; width:100%} #configuration input[type="checkbox"] {order:-1; margin: 0 10px 0 0} #configuration textarea {height: 150px; width: 100%; resize: none;background-color: transparent; color:#fff} #configuration #configuration_items_to_hide_var {display : block;}'
});
}
function insert_new_sidebar_container() {
let sidebar = document.querySelector(".side-bar .usr-stats");
let custom_container = "<div id='custom_container' class='usr-stats'></div>";
sidebar.insertAdjacentHTML('afterend', custom_container);
}
function insert_gm_config_button() {
let sidebar = document.querySelector("#custom_container");
let button = "<a id='gm_config_button' class='md-stroked md-button'>StreamElements improvements config</a>";
sidebar.insertAdjacentHTML('beforeend', button);
document.querySelector("#gm_config_button").addEventListener("click",() => GM_config.open());
}
// Custom controls under the config button
function insert_controls() {
let sidebar = document.querySelector("#custom_container");
let hide_items_slider = `<div class="slidercontainer"><label class="switch"><input id="toggle-hide-itemlist" type="checkbox" ${GM_config.get("hide_items_from_list_by_default") ? 'checked' : ''}><span class="slider round"></span></label>Show/Hide hidden items</div>`;
sidebar.insertAdjacentHTML('beforeend', hide_items_slider);
document.getElementById("toggle-hide-itemlist").addEventListener ("click", toggle_hidden_itemlist, false);
let sold_out_slider = `<div class="slidercontainer"><label class="switch"><input id="toggle-sold-out" type="checkbox" ${GM_config.get("hide_sold_out_items_by_default") ? 'checked' : ''}><span class="slider round"></span></label>Show/Hide sold out items</div>`;
sidebar.insertAdjacentHTML('beforeend', sold_out_slider);
document.getElementById("toggle-sold-out").addEventListener ("click", toggle_sold_out_items, false);
let subscriber_only_slider = `<div class="slidercontainer"><label class="switch"><input id="toggle-subscriber-only" type="checkbox" ${GM_config.get("hide_subscriber_items_by_default") ? 'checked' : ''}><span class="slider round"></span></label>Show/Hide subscriber items</div>`;
sidebar.insertAdjacentHTML('beforeend', subscriber_only_slider);
document.getElementById("toggle-subscriber-only").addEventListener ("click", toggle_subscriber_only_items, false);
let button_itemlist = "<a id='get-itemlist' class='md-stroked md-button md-dark-theme'>Get itemlist</a>";
sidebar.insertAdjacentHTML('beforeend', button_itemlist);
document.getElementById("get-itemlist").addEventListener ("click", get_itemlist, false);
let button_export_as_table = "<a id='get-item-table' class='md-stroked md-button md-dark-theme'>Show items as table</a>";
sidebar.insertAdjacentHTML('beforeend', button_export_as_table);
document.getElementById("get-item-table").addEventListener ("click", get_item_table, false);
let button_open_redemption_history = "<a id='redemption_history' class='md-stroked md-button md-dark-theme'>Show redemption history</a>";
sidebar.insertAdjacentHTML('beforeend', button_open_redemption_history);
document.getElementById("redemption_history").addEventListener ("click", show_redemption_history, false);
}
function sort_by_price() {
let descending = GM_config.get("sort_by_price_descending");
let list = document.querySelector(".public-store-items"); // Get container with the items in store
let nodesToSort = list.querySelectorAll(".stream-store-list-item"); // Get all items in store
Array.prototype.map.call(nodesToSort, function(node) {
return {
node: node,
relevantText: node.querySelector('.item-cost').textContent.replace(/\D/g, '') // Remove all symbols except numbers
};
}).sort(function(a, b) {
// Check if descending is enabled, if it is than a and b must be switched
return descending ? b.relevantText.localeCompare(a.relevantText, undefined, {'numeric': true}) : a.relevantText.localeCompare(b.relevantText, undefined, {'numeric': true})
}).forEach(function(item) {
list.appendChild(item.node);
});
}
function get_itemlist() { // Get list with all items in store
let item_list = "";
document.querySelectorAll(".item-title").forEach(function(el){item_list += `${el.getAttribute("title").trim()}\n`});
console.clear();
if(item_list !== "") {
console.log(item_list);
alert("Item list displayed in the console (F12 on most browsers)\nCopy the list and paste it to the config textarea.\nRemove the items from the list that you still want to see!");
}
else
alert("There are no items shown currently");
}
function get_item_table() {
document.getElementById("get-item-table").remove(); // Remove the Button to prevent users from clicking it again
let store_items_container = document.querySelector(".public-store-items");
let items = document.querySelectorAll(".stream-store-list-item"); // Get all items in store
let table = "<table id='item-table' class='item-table'>";
table += "<tr><th>Name</th><th>Description</th><th>Price</th><th>Stock</th></tr>"; // Cell headlines
items.forEach(function(item) {
// Get all informations from cards
let title = item.querySelector(".item-title").innerText.trim();
let description = item.querySelector(".clamp-description-text").innerText.trim();
let stock = item.querySelector(".item-quantity-left > span") ? item.querySelector(".item-quantity-left > span").innerText.trim().replace("items left", '').replace("item left", '') : "Unlimited";
let cost = item.querySelector(".item-cost").textContent.replace(/\D/g, '');
// Add line to table
table += `<tr><td>${title}</td><td>${description}</td><td class='table-item-cost'>${cost}</td><td class='table-item-stock'>${stock}</td></tr>`;
})
table += "</table>";
// Remove Filters
document.querySelectorAll(".filter-items-input").forEach((filter) => {filter.remove()});
// Download table button
let button_download_as_csv = "<a id='download-item-table' class='md-stroked md-button md-dark-theme'>Download table as CSV</a>";
store_items_container.style.display = "unset"; // Change from grid to unset to allow 100 % width
store_items_container.innerHTML = button_download_as_csv + table;
document.getElementById("download-item-table").addEventListener ("click", function() {download_table_as_csv("item-table")}, false);
}
function show_redemption_history() {
window.open("https://streamelements.com/dashboard/account/redemptions", "_blank").focus();
}
function toggle_hidden_itemlist() {
let hidden_items = get_hidden_items_array();
let items = document.querySelectorAll(".stream-store-list-item"); // Get all items in store
items.forEach(function(item) {
let title = get_item_title(item);
if(hidden_items.includes(title))
item.classList.toggle("hide-item");
})
}
function toggle_sold_out_items() {
sold_out_items.forEach(function(item) {
item.classList.toggle("sold-out-item");
});
}
function toggle_subscriber_only_items() {
subscriber_only_items.forEach(function(item) {
item.classList.toggle("hide-subscriber-item");
});
}
function gray_out_hidden_items() {
let hidden_items = get_hidden_items_array();
let items = document.querySelectorAll(".stream-store-list-item"); // Get all items in store
items.forEach(function(item) {
let title = get_item_title(item);
if(hidden_items.includes(title))
item.classList.add("gray-out-item");
});
}
function gray_out_sold_out_items() {
sold_out_items.forEach(function(item) {
item.classList.add("gray-out-item");
});
}
function magnifying_glass_buttons() {
let items = document.querySelectorAll(".stream-store-list-item");
let magnifying_glass_button = `<div class="magnifying-glass-container"><div class="ico ico-mglass"></div></div>`;
items.forEach((item) => {
item.insertAdjacentHTML('beforeend', magnifying_glass_button);
item.querySelector(".magnifying-glass-container").addEventListener("click", search_on_steam, false);
});
}
function search_on_steam(event) {
let item = event.target; // Get the item
while(item.tagName !== "MD-CARD") // Go up till you reach the item
item = item.parentNode;
let title = get_item_title(item);
window.open(`https://store.steampowered.com/search/?term=${title}`, "_blank");
}
function hide_item_buttons() {
let items = document.querySelectorAll(".stream-store-list-item");
let eye_button = `<div class="eye-slash-container"><div class="eye slash"><div></div><div></div></div></div>`;
items.forEach((item) => {
let hidden_items = get_hidden_items_array();
let title = get_item_title(item);
if(!hidden_items.includes(title)) {
item.insertAdjacentHTML('beforeend', eye_button);
item.querySelector(".eye-slash-container").addEventListener("click", add_to_hidden, false);
}
});
}
function add_to_hidden(event) {
let item = event.target; // Get the item
while(item.tagName !== "MD-CARD") // Go up till you reach the item
item = item.parentNode;
let add_to_hidden_button = item.querySelector(".eye-slash-container");
add_to_hidden_button.remove(); // Remove the button
let title = get_item_title(item);
GM_config.set("items_to_hide", get_prepared_items_to_hide(title)); // Override old list
if(GM_config.get("hide_items_from_list_by_default"))
item.classList.add("gray-out-item");
let hide_itemlist_enabled = document.querySelector("#toggle-hide-itemlist").checked;
if(hide_itemlist_enabled)
item.classList.add("hide-item");
}
function get_item_title(item) {
return item.querySelector(".item-title").getAttribute("title").trim();
}
function get_prepared_items_to_hide(new_title = null) {
let items_to_hide = GM_config.get("items_to_hide");
if(new_title) // Add new item title if exist
items_to_hide += `\n${new_title.trim()}`;
items_to_hide = items_to_hide.replace(/^\s*$(?:\r\n?|\n)/gm, ""); // Remove blank lines
items_to_hide = trim_spaces(items_to_hide); // Remove Spaces
items_to_hide = sort_alphabetically(items_to_hide); // Sort alphabetical
return items_to_hide;
}
function check_sold_out_items() {
let items = document.querySelectorAll(".stream-store-list-item");
items.forEach(function(item) {
let quantity_text = item.querySelector(".item-quantity-left > span")?.innerHTML;
if(quantity_text === "Sold out")
sold_out_items.push(item);
})
}
function check_subscriber_only_items() {
let items = document.querySelectorAll(".stream-store-list-item");
items.forEach(function(item) {
if(item.querySelector(".item-subscriber-only"))
subscriber_only_items.push(item);
})
}
function get_hidden_items_array() {
let hidden_items = GM_config.get("items_to_hide").split("\n");
hidden_items = hidden_items.map(function (el) {return el.trim();}); // Trim away the spaces
hidden_items = hidden_items.filter((el) => el !== ""); // Remove all empty lines (shouldn't happen anymore, but safe is safe)
return hidden_items;
}
// Add custom styles
let styles = `
/* Switch */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/** Eye Slash **/
.eye-slash-container {
position: absolute;
right: 0;
padding: 10px;
height: 40px;
width: 40px;
}
.eye-slash-container:hover {
cursor: pointer;
}
.eye{
width:1.25em;
height:0.75em;
position:absolute;
display:inline-block;
--background:#aaa;
--color:currentColor;
}
.eye div{
overflow:hidden;
height:50%;
position:relative;
margin-bottom:-1px;
}
.eye div:before{
content:'';
background:currentColor;
position:absolute;
left:0;
right:0;
height:300%;
border-radius:100%;
}
.eye div:last-child:before{
bottom:0;
}
.eye:before{
content:'';
position:absolute;
top:50%;
left:50%;
transform:translate(-50%, -50%);
width:0.35em;
height:0.35em;
background:var(--color);
border:0.1em solid var(--background);
border-radius:100%;
z-index:1;
}
.eye:after{
content:'';
position:absolute;
top:-0.15em;
left:calc(33.333% - 0.15em);
transform:rotate(45deg) scaleX(0);
transform-origin:left center;
width:90%;
height:0.1em;
background:var(--color);
border-top:0.1em solid var(--background);
z-index:2;
transition:transform 0.25s;
}
.eye.slash:after{
transform:rotate(45deg) scaleX(1);
}
/**Magnifying glass**/
.magnifying-glass-container {
position: absolute;
padding: 10px;
height: 40px;
width: 40px;
}
.magnifying-glass-container:hover {
cursor: pointer;
}
.ico-mglass {
position: absolute;
display:inline-block;
background: transparent;
border-radius: 30px;
height: 12px;
width: 12px;
border: 2px solid #fff;
}
.ico-mglass:after {
content: "";
height: 2px;
width: 8px;
background: #fff;
position: absolute;
top: 9px;
left: 6px;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
}
.userpages-wrap {max-width: unset;}
.hide-item,.sold-out-item,.hide-subscriber-item {display: none;}
.gray-out-item {filter: saturate(0%);}
.sort-buttons-container {display: grid; grid-template-columns: 50% 50%;}
.slidercontainer {letter-spacing: 1.2px;padding-left: 16px;padding-right: 16px;font-size: 12px;line-height: 36px;user-select: none;text-transform: uppercase;font-weight: 600; margin-top: 15px;}
.slidercontainer > .switch {margin-right: 10px;}
.item-table td, .item-table th {border: 2px solid #fff;padding: 10px;}
.table-item-cost,.table-item-stock {text-align:center;}`;
// 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