// ==UserScript==
// @name TwitchModsDACH Bann-Hammer (by RaidHammer)
// @description A tool for moderating Twitch easier during hate raids
// @namespace TwitchModsDACH Bann-Hammer (by RaidHammer)
// @version 3.1.1.4
// @match *://www.twitch.tv/*
// @run-at document-idle
// @author TwitchModsDACH - The original code is from victornpb
// @homepageURL https://github.com/TwitchmodsDACH/Bann-Hammer
// @supportURL https://github.com/TwitchmodsDACH/Bann-Hammer/issues
// @contributionURL https://github.com/TwitchmodsDACH/Bann-Hammer
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
/* jshint esversion: 8 */
(function() {
'use strict';
function processStoredModChannels() {
const storedModChannels = JSON.parse(localStorage.getItem("myModChannels"));
}
processStoredModChannels
})();
(function (urlCount) {
// Load jQuery- and jQuery UI-Bibliothek for draggable window
var jqueryScript = document.createElement('script');
jqueryScript.src = 'https://code.jquery.com/jquery-3.6.0.min.js';
document.head.appendChild(jqueryScript);
var jqueryUIScript = document.createElement('script');
jqueryUIScript.src = 'https://code.jquery.com/ui/1.13.0/jquery-ui.min.js';
document.head.appendChild(jqueryUIScript);
// Globle required Variables
var myVersion = "3.1.1.4"
var text;
var banReason;
var urlBannlisten = "https://github.com/TwitchmodsDACH/Bannlisten"
var mdgBtnAdvertisingText = "➕ isds_advertising"
var mdgBtnFollowBotText = "➕ isds_follow_bots"
var mdgBtnTrollsText0 = "➕ isds_hate_trolls_0_g"
var mdgBtnTrollsText1 = "➕ isds_hate_trolls_h_m"
var mdgBtnTrollsText2 = "➕ isds_hate_trolls_n_z"
var mdgBtnSec = "➕ isds_security_list"
var mdgBtnUnbanText = "➕ isds_UNBAN"
var mdgBtnViewerBotsText = "➕ isds_viewer_bots"
var mdgBtnFlirtyMadText = "➕ isds_mad_tos"
var mdgBtnSpamBotsText = "➕ isds_spam_bots"
var mdgBtnStreamSniperText = "➕ isds_streamsniper"
var mdgBtnFakeScamText = "➕ isds_fake_scam"
var mdgBtnPornBotText = "➕ isds_porn_bots"
var replaceFooter = "none"
var isPaused = false;
var queueList = new Set();
var ignoredList = new Set();
var bannedList = new Set();
var LOGPREFIX = "[BANN-HAMMER]";
const delay = t => new Promise(r => setTimeout(r, t));
var themePrincess = "#FF1493"
var themeNormal = "#34AE0C"
var themeTextColor = themeNormal
var updateText = "keine neue Version verfügbar"
const urlParts = document.location.href.split("/");
var activeChannel;
if (urlParts[urlParts.length - 1] == "home" ) {
activeChannel = urlParts[urlParts.length - 2]
} else {
activeChannel = urlParts[urlParts.length - 1]
}
var TMDLocalStorageBanList = activeChannel + "_banlist"
var TMDLocalStorageUnBanList = activeChannel + "_unbanlist1"
var bannedUsersStore = JSON.parse(localStorage.getItem(TMDLocalStorageBanList)) || [];
var unbannedUsersStore = JSON.parse(localStorage.getItem(TMDLocalStorageUnBanList)) || [];
var modChannelList = new Set();
var TMDLocalStorageModChannels = "myModChannels"
var modChannelStore = JSON.parse(localStorage.getItem(TMDLocalStorageModChannels)) || [];
console.log(urlParts[urlParts.length - 2])
// This function is requried to disable CORS for importing the GitHub ban lists
// https://portswigger.net/web-security/cors
var corsDisable = {
"id": 1,
"enabled": true,
"name": "Allow All",
"match": "<all_urls>",
"action": "allow",
"responseHeaders": [{
"name": "Access-Control-Allow-Origin",
"value": "*"
}]
};
if (typeof GM_setValue === "function") {
GM_setValue("corsDisable", corsDisable);
} else {
// Fallback for Safari
localStorage.setItem("corsDisable", JSON.stringify(corsDisable));
}
if (typeof GM_addStyle == 'undefined') {
GM_addStyle = (css) => {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
}
}
if (typeof GM_setValue === "function") {
GM_setValue("corsDisable", JSON.stringify(corsDisable));
} else if (typeof localStorage !== "undefined") {
localStorage.setItem("corsDisable", JSON.stringify(corsDisable));
}
// Frontend
var html = /*html*/`
<div id="raidhammer" class="raidhammer">
<style>
.raidhammer {
z-index: 99999999;
position: absolute;
top: 250px;
left: 350px;
background-color: var(--color-background-base);
color: var(--color-text-base);
border: var(--border-width-default) solid var(--color-border-base);
box-shadow: var(--shadow-elevation-2);
padding: 5px;
min-width: 525px;
cursor: move;
}
.raidhammer .handle {
cursor: move;
user-select: none;
}
.raidhammer .svg {
color: "${themeTextColor}"
}
.raidhammer .header {
display: flex;
}
.raidhammer .logo {
font-weight: var(--font-weight-semibold);
min-height: 30px;
line-height: 30px;
--color: var(--color-text-link);
}
.raidhammer h6 {
color: var(--color-hinted-grey-7);
}
.raidhammer h6 button {
height: auto;
background: none;
}
.raidhammer .list {
padding: 8px;
min-height: 8em;
max-height: 350px;
overflow-y: auto;
background: var(--color-background-body);
}
.raidhammer .list span {
font-weight: var(--font-weight-semibold);
}
.raidhammer .empty {
padding: 2em;
text-align: center;
opacity: 0.85;
}
.raidhammer button {
padding: 0 .5em;
margin: 1px;
font-weight: var(--font-weight-semibold);
border-radius: var(--border-radius-medium);
font-size: var(--button-text-default);
height: var(--button-size-default);
background-color: var(--color-background-button-secondary-default);
color: var(--color-text-button-secondary);
min-width: 30px;
text-align: center;
}
.raidhammer button.ban {
var(--color-text-button-primary);
background: #f44336;
min-width: 60px;
}
.raidhammer button.banAll {
var(--color-text-button-primary);
background: #f44336;
min-width: 40px;
}
.raidhammer button.unbanAll {
var(--color-text-button-primary);
background: #34ae0c;
min-width: 40px;
}
.raidhammer button.unban {
var(--color-text-button-primary);
background: #34ae0c;
min-width: 60px;
}
.raidhammer .import {
background: var(--color-background-body);
border: var(--border-width-default) solid var(--color-border-base);
padding: 3px;
min-height: 20px
}
.raidhammer textarea {
background: var(--color-background-base);
color: var(--color-text-base);
padding: .5em;
font-size: 10pt;
width: 100%;
min-height: 8em;
}
.raidhammer .footer {
font-size: 7pt;
text-align: center;
}
</style>
<div class="header">
<span style="flex-grow: -1;"></span>
<span class="handle" style="flex-grow: -1;"></span>
<button class="princess"><img src="https://raw.githubusercontent.com/TwitchmodsDACH/Bann-Hammer/main/dokumentation/magicwand.png" title="Für die Prinzessinnen unter uns" width="20px" height="20px"></button>
<span style="flex-grow: 1;"></span>
<h5 id="header" class="logo">
<a href="https://github.com/TwitchmodsDACH/Bann-Hammer" target="_blank" style="color: ${themeTextColor};" titel="Zum Bann-Hammer Repository">Bann-Hammer
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="5 5 1280 1280" style="color: ${themeTextColor};fill: currentcolor;align:center;">
<path d="M517 1c-16 3-28 10-41 22l-10 10 161 160 161 161 2-2c6-4 17-19 21-25 10-19 12-44 4-64-6-14-5-13-120-129L576 17c-8-7-18-12-27-15-8-1-25-2-32-1zM249 250 77 422l161 161 161 161 74-74 74-75 18 19 18 18-2 4c-4 6-4 14-1 20a28808 28808 0 0 0 589 621c4 2 6 3 13 3 6 0 8-1 13-3 6-4 79-77 82-83 4-9 4-21-2-29l-97-93-235-223-211-200c-51-47-73-68-76-69-6-3-13-3-19 0l-5 3-18-18-18-18 74-74 74-74-161-161L422 77 249 250zM23 476a75 75 0 0 0-10 95c4 6 219 222 231 232 8 7 16 11 26 14 6 2 10 2 22 2s14 0 22-2l14-6c5-4 20-16 24-21l2-2-161-161L32 466l-9 10z"/>
</svg>
TwitchModsDACH Edition</a>
</h5><br>
<span style="flex-grow: 1;"></span>
<button class="closeBtn">_</button>
</div>
<div id="import" class="import" style="display:none;">
<textarea id="textfield" placeholder="Ein Benutzername pro Zeile"></textarea>
<div style="text-align:right;">
<input type="text" id="banReason" style="width:66%" placeholder="Gib einen Bann-Grund an" />
<button class="importBtn" title="Benutzer zur Liste hinzufügen" style="width:32%">➕ Hinzufügen</button>
</div>
<div style="align:center">
<button id="mdgBtnTrolls0" class="mdgBtnTrolls0" style="width:32%" title="Importiert die isds_hate_troll Liste 0 bis g">${mdgBtnTrollsText0}</button>
<button id="mdgBtnTrolls1" class="mdgBtnTrolls1" style="width:33%" title="Importiert die isds_hate_troll Liste h bis m">${mdgBtnTrollsText1}</button>
<button id="mdgBtnTrolls2" class="mdgBtnTrolls2" style="width:32%" title="Importiert die isds_hate_troll Liste n bis z">${mdgBtnTrollsText2}</button>
</div>
<div style="align:center">
<button id="mdgBtnSec" class="mdgBtnSec" style="width:32%" title="Importiert isds_security_ban Liste">${mdgBtnSec}</button>
<button id="mdgBtnViewerBots" class="mdgBtnViewerBots" style="width:33%" title="Importiert isds_viewerbot Liste">${mdgBtnViewerBotsText}</button>
<button id="tmdBtnStreamSniper" class="tmdBtnStreamSniper" style="width:32%" title="Importiert isds_streamsniper Liste">${mdgBtnStreamSniperText}</button>
</div>
<div style="align:center">
<button id="mdgBtnFlirtyMad" class="mdgBtnFlirtyMad" style="width:32%" title="Importiert isds_mad_tos Liste">${mdgBtnFlirtyMadText}</button>
<button id="mdgBtnFollowBot" class="mdgBtnFollowBot" style="width:33%" title="Importiert isds_follow_bots Liste">${mdgBtnFollowBotText}</button>
<button id="mdgBtnUnban" class="mdgBtnUnban" style="width:32%;color:#34ae0c" title="Importiert isds_unban Liste">${mdgBtnUnbanText}</button>
</div>
<div style="align:center">
<button id="mdgBtnAdvertising" class="mdgBtnAdvertising" style="width:32%" title="Importiert isds_advertising Liste">${mdgBtnAdvertisingText}</button>
<button id="mdgBtnSpamBots" class="mdgBtnSpamBots" style="width:33%" title="Importiert isds_spam_bots Liste">${mdgBtnSpamBotsText}</button>
<button id="isds" class="isds" style="width:32%" title="Webseite des Institut für Sicherheit und Daten-Analyse im Streaming">https://isds.tech</button>
</div>
<div style="align:center">
<button id="mdgBtnFakeScam" class="mdgBtnFakeScam" style="width:32%" title="Importiert isds_fake_scam Liste">${mdgBtnFakeScamText}</button>
<button id="mdgBtnPornBot" class="mdgBtnPornBot" style="width:33%" title="Importiert isds_spam_bots Liste">${mdgBtnPornBotText}</button>
</div>
</div>
<div class="body">
<div class="list"></div>
<div style="display: flex; margin: 5px;">
<span style="flex-grow: 2;"></span>
<div id="buttons" class="buttons">
<button class="back" title="Zurück">⬅</button>
<button class="MooBot" title="Öffnet Moobot" onclick="window.open('https://moo.bot/','_blank')"><img src="https://moo.bot/favicon.ico" height="17px" style = "position:relative; top:1px;"></button>
<button class="NightBot" title="Öffnet Nightbot" onclick="window.open('https://nightbot.tv/dashboard','_blank')"><img src="https://logodix.com/logo/1909538.png" height="17px" style = "position:relative; top:1px;"></button>
<button class="comanderRoot" title="Öffnet ComanderRoot" onclick="window.open('https://twitch-tools.rootonline.de','_blank')">🤖</button>
<button class="sLabs" title="Öffnet Streamlabs" onclick="window.open('https://streamlabs.com/dashboard','_blank')"><img src="https://cdn.streamlabs.com/static/imgs/streamlabs-logos/app-icon/streamlabs-app-icon.png" height="17px" style = "position:relative; top:1px;"></button>
<button class="sElements" title="Öffnet Streamelements" onclick="window.open('https://streamelements.com/dashboard','_blank')"><img src="https://avatars.githubusercontent.com/u/16977512?s=17&v=4" style="position:relative; top:1px;"></button>
<button class="chatstats" title="Öffnet SullyGnome Kanal-Statistiken für den aktuellen Kanal" onclick="window.open('https://sullygnome.com/channel/${activeChannel}','_blank')">📈</button>
<button class="modLogger" title="Öffnet ModLogger für den aktuellen Kanal" onclick="window.open('https://jvpeek.github.io/twitchmodlogger/?channel=${activeChannel}','_blank')">🗄</button>
<button class="chatDeepStats" title="Öffnet ChatStats für den aktuellen Kanal" onclick="window.open('https://echtkpvl.github.io/echt-twitch/chat-stats.html?channel=${activeChannel}','_blank')">🩻</button>
<button class="pause" id="pause" title="Pause/Play">⏸</button>
<button class="modChannels" title="Alle als Mod-Kanal hinzufügen">⚔</button>
<button class="ignoreAll" title="Liste leeren">🗑</button>
<button class="unbanAll" title="Alle auf der Liste entbannen">⚕</button>
<button class="banAll" title="Alle auf der Liste bannen">👹</button>
</div>
</div>
</div>
<div id="footer" class="footer">
<a href="https://github.com/TwitchmodsDACH/Bannlisten" target="_blank" style="color: ${themeTextColor};" id="replaceFooter" titel="Zur Bannliste">TwitchModsDACH Bannlisten</a> -
<a id="manoooo" href="https://github.com/TwitchmodsDACH/Bann-Hammer/raw/main/bannhammer.user.js" title="Aktuelle Bannhammer Version installieren">${updateText}</a> - ${myVersion}
</div>`;
// Append Bann-Hammer after page load
document.addEventListener('DOMContentLoaded', () => {
document.body.appendChild(raidhammer);
});
// Function PauseButton
function pauseBanAll() {
isPaused = !isPaused;
if (isPaused) {
var btn = document.getElementById("pause");
btn.value = 'pause';
btn.innerHTML = 'Pause';
} else {
var btn = document.getElementById("pause");
btn.value = 'unpause';
btn.innerHTML = 'Unpause';
}
}
// Function Modal
const d = document.createElement("div");
d.style.display = 'none';
d.innerHTML = html;
const textarea = d.querySelector("textarea");
// Function activate button
const activateBtn = document.createElement('button');
activateBtn.innerHTML = `
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 1280 1280" style="color: ${themeTextColor}; fill: currentcolor;">
<path d="M517 1c-16 3-28 10-41 22l-10 10 161 160 161 161 2-2c6-4 17-19 21-25 10-19 12-44 4-64-6-14-5-13-120-129L576 17c-8-7-18-12-27-15-8-1-25-2-32-1zM249 250 77 422l161 161 161 161 74-74 74-75 18 19 18 18-2 4c-4 6-4 14-1 20a28808 28808 0 0 0 589 621c4 2 6 3 13 3 6 0 8-1 13-3 6-4 79-77 82-83 4-9 4-21-2-29l-97-93-235-223-211-200c-51-47-73-68-76-69-6-3-13-3-19 0l-5 3-18-18-18-18 74-74 74-74-161-161L422 77 249 250zM23 476a75 75 0 0 0-10 95c4 6 219 222 231 232 8 7 16 11 26 14 6 2 10 2 22 2s14 0 22-2l14-6c5-4 20-16 24-21l2-2-161-161L32 466l-9 10z"/>
</svg>
`;
activateBtn.style.cssText = `
display: inline-flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
user-select: none;
height: var(--button-size-default);
width: var(--button-size-default);
border-radius: var(--border-radius-medium);
background-color: var(--color-background-button-text-default);
color: var(--color-fill-button-icon);
`;
activateBtn.setAttribute('id', 'hammer');
activateBtn.setAttribute('title', 'Bann-Hammer');
activateBtn.onclick = toggle;
let enabled;
let watchdogTimer;
function appendActivatorBtn() {
const modBtn = document.querySelector('[data-test-selector="mod-view-link"]');
if (modBtn) {
const twitchBar = modBtn.parentElement.parentElement.parentElement;
if (twitchBar && !twitchBar.contains(activateBtn)) {
console.log(LOGPREFIX, 'Mod tools available. Adding button...');
twitchBar.insertBefore(activateBtn, twitchBar.firstChild);
document.body.appendChild(d);
$('.raidhammer').draggable();
}
} else if (document.location.toString().includes('/moderator/')){
const chatBtn = document.querySelector('[data-a-target="chat-send-button"]');
const twitchBar = chatBtn.parentElement.parentElement.parentElement;
if (twitchBar && !twitchBar.contains(activateBtn)) {
console.log(LOGPREFIX, 'Mod tools available. Adding button...');
twitchBar.insertBefore(activateBtn, twitchBar.firstChild);
document.body.appendChild(d);
$('.raidhammer').draggable();
}
}
else {
if (enabled) {
console.log(LOGPREFIX, 'Mod tools not found. Stopped chatWatchdog!');
watchdogTimer = enabled = false;
hide();
}
}
}
setInterval(appendActivatorBtn, 5000);
// Eventhandler
d.querySelector(".ignoreAll").onclick = ignoreAll;
d.querySelector(".banAll").onclick = banAll;
d.querySelector(".closeBtn").onclick = hide;
d.querySelector(".modChannels").onclick = addModChannelsAll;
d.querySelector(".unbanAll").onclick = unbanAll;
d.querySelector(".back").onclick = toggleBack;
d.querySelector(".pause").onclick = togglePause;
d.querySelector(".princess").onclick = toggleTheme;
d.querySelector(".isds").onclick = isds;
d.querySelector(".import button.mdgBtnUnban").onclick = importMDGUnban;
d.querySelector(".import button.mdgBtnTrolls0").onclick = importMDGtrolls0;
d.querySelector(".import button.mdgBtnTrolls1").onclick = importMDGtrolls1;
d.querySelector(".import button.mdgBtnTrolls2").onclick = importMDGtrolls2;
d.querySelector(".import button.mdgBtnSec").onclick = importMDGsec;
d.querySelector(".import button.mdgBtnViewerBots").onclick = importMDGViewerBots;
d.querySelector(".import button.mdgBtnFlirtyMad").onclick = importMDGFlirtyMad;
d.querySelector(".import button.mdgBtnFollowBot").onclick = importMDGFollowBot;
d.querySelector(".import button.mdgBtnAdvertising").onclick = importMDGAdvertising;
d.querySelector(".import button.mdgBtnSpamBots").onclick = importMDGSpamBots;
d.querySelector(".import button.tmdBtnStreamSniper").onclick = importMDGStreamSniper;
d.querySelector(".import button.mdgBtnFakeScam").onclick = importMDGFakeScam;
d.querySelector(".import button.mdgBtnPornBot").onclick = importMDGPorn;
d.querySelector(".import button.importBtn").onclick = importList;
// delegated events
d.addEventListener('click', e => {
const target = e.target;
if (target.matches('.ignore')) ignoreItem(target.dataset.user);
if (target.matches('.ban')) banItem(target.dataset.user);
if (target.matches('.unban')) unbanItem(target.dataset.user);
if (target.matches('.accountage')) accountage(target.dataset.user);
if (target.matches('.toggleImport')) toggleImport();
if (target.matches('.start')) toggleImport();
if (target.matches('.removeModChannel')) removeModChannel(target.dataset.user);
if (target.matches('.addModChannels')) addModChannels(target.dataset.user);
});
function isds() {
window.open("https://isds.tech");
}
// Function toggleTheme
function toggleTheme() {
var dataHeader = document.getElementById('header').innerHTML;
var dataFooter = document.getElementById('footer').innerHTML;
var dataHammer = document.getElementById('hammer').innerHTML;
// Test actually color in use is our gree
if (dataHeader.match("#34AE0C") && dataFooter.match("#34AE0C") && dataHammer.match("#34AE0C")) {
console.log(LOGPREFIX, "huh? I'm a princess now!")
dataHeader = dataHeader.replace(/#34AE0C/g, themePrincess);
dataFooter = dataFooter.replace(/#34AE0C/g, themePrincess);
dataHammer = dataHammer.replace(/#34AE0C/g, themePrincess);
document.getElementById('header').innerHTML = dataHeader;
document.getElementById('footer').innerHTML = dataFooter;
document.getElementById('hammer').innerHTML = dataHammer;
} else {
console.log(LOGPREFIX, "Muh? I'm no longer a princess :-/")
dataHeader = dataHeader.replace(/#FF1493/g, themeNormal);
dataFooter = dataFooter.replace(/#FF1493/g, themeNormal);
dataHammer = dataHammer.replace(/#FF1493/g, themeNormal);
document.getElementById('header').innerHTML = dataHeader;
document.getElementById('footer').innerHTML = dataFooter;
document.getElementById('hammer').innerHTML = dataHammer;
const targetElement = document.getElementById("body");
}
}
// Function toggle pause/play
function togglePause() {
if (isPaused) {
isPaused = false;
var btn = document.getElementById("pause");
btn.value = 'pause';
btn.innerHTML = '⏸';
var queueList
} else {
isPaused = true;
var btn = document.getElementById("pause");
btn.value = 'play';
btn.innerHTML = '▶';
}
}
// Function show Bann-Hammer window
function show() {
console.log(LOGPREFIX, 'Show');
d.style.display = '';
$('.raidhammer').draggable();
renderList();
}
// Function hide Bann-Hammer window
function hide() {
console.log(LOGPREFIX, 'Hide');
d.style.display = 'none';
}
// Function checking new versions
function toggle() {
function checkVersion() {
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bann-Hammer/main/bannhammer.user.js")
.then((response) => response.text())
.then((text) => {
var regex = /@version\s+(\d.*)/;
var match = regex.exec(text);
var newVersion = match[1];
if (myVersion < newVersion) {
document.getElementById('manoooo').innerHTML = "🚨 Update verfügbar 🚨"
} else {
document.getElementById('manoooo').innerHTML = "keine neuen Updates"
}
});
}
if (d.style.display !== 'none') hide();
else show();
checkVersion();
}
// Function toogle import
function toggleImport() {
document.getElementById("textfield").value = "";
const importDiv = d.querySelector(".import");
const body = d.querySelector(".body");
if (importDiv.style.display !== 'none') {
importDiv.style.display = 'none';
body.style.display = '';
}
else {
importDiv.style.display = '';
body.style.display = 'none';
d.querySelector(".import textarea").focus();
}
}
// Function toggle back
function toggleBack() {
queueList.clear();
document.getElementById("textfield").value = "";
body = d.querySelector(".body");
insertText("")
importDiv = d.querySelector(".import");
body = d.querySelector(".body");
if (importDiv.style.display !== 'none') {
importDiv.style.display = 'none';
body.style.display = '';
} else {
importDiv.style.display = '';
body.style.display = 'none';
d.querySelector(".import textarea").focus();
}
document.getElementById("replaceFooter").innerHTML = "Alle Bannlisten anzeigen"
document.getElementById("replaceFooter").href = "https://github.com/TwitchmodsDACH/Bannlisten"
}
// Function to verify a user is already banned/unbannd in a channel
function userAlreadyBanned(user, button) {
if (!bannedUsersStore.includes(user)) {
queueList.add(user)
} else {
document.getElementById(button).innerHTML = "already banned"
console.log(LOGPREFIX, user + " already banned" + activeChannel)
}
}
function userAlreadyUnBanned(user, button) {
if (!unbannedUsersStore.includes(user)) {
queueList.add(user)
} else {
document.getElementById(button).innerHTML = "already unbanned"
console.log(LOGPREFIX, user + " already unbanned in " + activeChannel)
}
}
// Function to import the list
function importList() {
const textarea = d.querySelector(".import textarea");
const lines = textarea.value.split(/\n/).map(line => line.trim()).filter(Boolean);
for (const line of lines) {
if (/^[\w_]+$/.test(line)) {
queueList.add(line);
}
}
textarea.value = '';
toggleImport();
renderList();
}
// Function to insert list into textarea
function insertText(text) {
document.getElementById("textfield").value = text;
}
// Functions to import lists from TwitchModsDACH Repository
function importMDGtrolls0() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_hate_troll_list_0_g.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnTrolls0"))
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste 'isds_hate_troll_list_0_g.txt' anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_hate_troll_list_0_g.txt"
function dumdidum() {
document.getElementById("mdgBtnTrolls0").innerHTML = mdgBtnTrollsText0
}
setTimeout(dumdidum, 5000)
}
function importMDGtrolls1() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_hate_troll_list_h_m.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnTrolls1"))
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste 'isds_hate_troll_list_h_m.txt' anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_hate_troll_list_h_m.txt"
function dumdidum() {
document.getElementById("mdgBtnTrolls1").innerHTML = mdgBtnTrollsText1
}
setTimeout(dumdidum, 5000)
}
function importMDGtrolls2() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_hate_troll_list_n_z.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnTrolls2"))
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste 'isds_hate_troll_list_n_z.txt' anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_hate_troll_list_n_z.txt"
function dumdidum() {
document.getElementById("mdgBtnTrolls2").innerHTML = mdgBtnTrollsText2
}
setTimeout(dumdidum, 5000)
}
function importMDGsec() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_security_ban_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnSec"))
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste 'isds_security_ban_list.txt' anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_security_ban_list.txt"
function dumdidum() {
document.getElementById("mdgBtnSec").innerHTML = mdgBtnSec
}
setTimeout(dumdidum, 5000)
}
function importMDGUnban() {
queueList.clear();
var usersToBan = [];
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_unbanlist.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyUnBanned(name.replace(/\r/g, ""), "mdgBtnUnban"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_unbanlist.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_unbanlist.txt"
function dumdidum() {
document.getElementById("mdgBtnUnban").innerHTML = mdgBtnUnbanText
}
setTimeout(dumdidum, 5000)
}
function importMDGViewerBots() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_viewer_bot_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnViewerBots"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_viewer_bot_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_viewer_bot_list.txt"
function dumdidum() {
document.getElementById("mdgBtnViewerBots").innerHTML = mdgBtnViewerBotsText
}
setTimeout(dumdidum, 5000)
}
function importMDGFlirtyMad() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_mad_tos_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnFlirtyMad"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_mad_tos_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_mad_tos_list.txt"
function dumdidum() {
document.getElementById("mdgBtnFlirtyMad").innerHTML = mdgBtnFlirtyMadText
}
setTimeout(dumdidum, 5000)
}
function importMDGFollowBot() {
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
queueList.clear();
var usersToBan = [];
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_follower_bot_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnFollowBot"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_follower_bot_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_follower_bot_list.txt"
function dumdidum() {
document.getElementById("mdgBtnFollowBot").innerHTML = mdgBtnFollowBotText
}
setTimeout(dumdidum, 5000)
}
function importMDGAdvertising() {
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
queueList.clear();
var usersToBan = [];
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_seller_advertising_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnAdvertising"));
renderList()
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_seller_advertising_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_seller_advertising_list.txt"
function dumdidum() {
document.getElementById("mdgBtnAdvertising").innerHTML = mdgBtnAdvertisingText
}
setTimeout(dumdidum, 5000)
}
function importMDGSpamBots() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_spam_bot_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnSpamBots"));
textarea.value = '';
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_spam_bot_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_spam_bot_list.txt"
function dumdidum() {
document.getElementById("mdgBtnSpamBots").innerHTML = mdgBtnSpamBotsText
}
setTimeout(dumdidum, 5000)
}
function importMDGStreamSniper() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_streamsniper_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "tmdBtnStreamSniper"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_streamsniper_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_streamsniper_list.txt"
function dumdidum() {
document.getElementById("tmdBtnStreamSniper").innerHTML = mdgBtnStreamSniperText
}
setTimeout(dumdidum, 5000)
}
function importMDGFakeScam() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_fake_scam_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnFakeScam"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_fake_scam_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_fake_scam_list.txt"
function dumdidum() {
document.getElementById("mdgBtnFakeScam").innerHTML = mdgBtnFakeScamText
}
setTimeout(dumdidum, 5000)
}
function importMDGPorn() {
queueList.clear();
var usersToBan = [];
if (document.getElementById("banReason").value == "") {
document.getElementById("banReason").value = urlBannlisten
}
fetch("https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_porn_bot_acc_list.txt")
.then((response) => response.text())
.then((data) => {
usersToBan.push(...data.split("\n").filter(Boolean));
usersToBan.forEach(name => userAlreadyBanned(name.replace(/\r/g, ""), "mdgBtnPornBot"));
textarea.value = '';
insertText(Array.from(queueList))
if (queueList.size != "0") { toggleImport(); renderList(); }
});
document.getElementById("replaceFooter").innerHTML = "Geladene Liste isds_porn_bot_acc_list.txt anzeigen"
document.getElementById("replaceFooter").href = "https://raw.githubusercontent.com/TwitchmodsDACH/Bannlisten/main/isds_porn_bot_acc_list.txt"
function dumdidum() {
document.getElementById("mdgBtnPornBot").innerHTML = mdgBtnPornBotText
}
setTimeout(dumdidum, 5000)
}
// Functions to ban/unban/ignore/accountage
function ignoreAll() {
console.log(LOGPREFIX, 'Ignoring all...', queueList);
for (const user of queueList) {
ignoreItem(user);
}
}
async function banAll() {
console.log(LOGPREFIX, 'Banning all...', queueList);
for (const user of queueList) {
if (isPaused) {
// breake until button pressed again
while (isPaused) {
await delay(1000);
}
}
banItem(user);
await delay(125);
}
}
async function unbanAll() {
console.log(LOGPREFIX, 'Unbanning all...', queueList);
for (const user of queueList) {
if (isPaused) {
// breake until button pressed again
while (isPaused) {
await delay(1000);
}
}
unbanItem(user);
await delay(125);
}
}
// Function to set Mod-Channels
async function addModChannelsAll() {
console.log(LOGPREFIX, 'Add Mod-Channels...', queueList);
for (const user of queueList) {
if (isPaused) {
// breake until button pressed again
while (isPaused) {
await delay(1000);
}
}
addModChannels(user);
await delay(100);
}
}
// Function send !accountage user into chat, to trigger Streamelements Bot
function accountage(user) {
console.log(LOGPREFIX, 'send !accountage', user);
sendMessage('!accountage ' + user);
}
// Function to remove User from action list
function ignoreItem(user) {
console.log(LOGPREFIX, 'Ignore user:', user);
queueList.delete(user)
ignoredList.add(user)
renderList();
}
// Function to unban a user
function unbanItem(user) {
console.log(LOGPREFIX, 'Unban user:', user);
queueList.delete(user);
bannedList.add(user);
unbannedUsersStore.push(user)
sendMessage('/unban ' + user);
localStorage.setItem(TMDLocalStorageUnBanList, JSON.stringify(unbannedUsersStore));
localStorage.setItem(TMDLocalStorageBanList, JSON.stringify(JSON.parse(localStorage.getItem(TMDLocalStorageBanList)).filter(unbannedUser => unbannedUser !== user)));
renderList();
}
// Function to remove channels from ModChannels
function removeModChannel(user) {
console.log(LOGPREFIX, 'Remove User from ModChannels:', user);
queueList.delete(user);
bannedList.add(user);
localStorage.setItem(TMDLocalStorageModChannels, JSON.stringify(JSON.parse(localStorage.getItem(TMDLocalStorageModChannels)).filter(modChannel => modChannel !== user)));
renderList();
}
// Function to ban a user
function banItem(user) {
banReason = document.getElementById("banReason").value;
//console.log(LOGPREFIX, 'Ban user', user);
queueList.delete(user);
bannedList.add(user);
bannedUsersStore.push(user)
localStorage.setItem(TMDLocalStorageBanList, JSON.stringify(bannedUsersStore));
sendMessage('/ban ' + user + ' ' + banReason );
renderList();
}
// Function add channel to Mod-Channels
function addModChannels(user) {
if (!modChannelStore.includes(user)) {
console.log(LOGPREFIX, user + " zu ModChannels hinzugefügt")
queueList.delete(user);
bannedList.add(user);
modChannelStore.push(user);
localStorage.setItem(TMDLocalStorageModChannels, JSON.stringify(modChannelStore));
renderList();
} else {
console.log(LOGPREFIX, "Benutzer " + user + " ist bereits in den ModChannels.");
}
}
// Functions for sending chat messages
function sendMessage(msg) {
try{
sendMessageOld(msg);
} catch(_) {
sendMessageSlate(msg);
}
}
function sendMessageOld(msg) {
const textarea = document.querySelector("[data-a-target='chat-input']");
const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
nativeTextAreaValueSetter.call(textarea, msg);
const event = new Event('input', { bubbles: true });
textarea.dispatchEvent(event);
document.querySelector("[data-a-target='chat-send-button']").click();
}
function sendMessageSlate(msg) {
function _injectInput(el, data) {[ 'keydown', 'beforeinput'].forEach((event, i) => {
const eventObj = {
altKey: false,
charCode: 0,
ctrlKey: false,
metaKey: false,
shiftKey: false,
which: '',
keyCode: '',
data: data,
inputType: 'insertText',
key: data,
};
el.dispatchEvent(new InputEvent(event, eventObj));
});}
function _triggerKeyboardEvent(el, keyCode) {
const eventObj = document.createEventObject ? document.createEventObject() : document.createEvent("Events");
if (eventObj.initEvent) {
eventObj.initEvent("keydown", true, true);
}
eventObj.keyCode = keyCode;
eventObj.which = keyCode;
el.dispatchEvent ? el.dispatchEvent(eventObj) : el.fireEvent("onkeydown", eventObj);
}
const editor = document.querySelector('[data-slate-editor="true"]');
editor.focus();
_injectInput(editor, msg);
_triggerKeyboardEvent(editor, 13);
}
// Render list or show logo
function renderList() {
d.querySelector(".ignoreAll").style.display = queueList.size ? '' : 'none';
d.querySelector(".banAll").style.display = queueList.size ? '' : 'none';
d.querySelector(".back").style.display = queueList.size ? '' : 'none';
d.querySelector(".pause").style.display = queueList.size ? '' : 'none';
d.querySelector(".modChannels").style.display = queueList.size ? '' : 'none';
d.querySelector(".unbanAll").style.display = queueList.size ? '' : 'none';
const renderItem = item => `
<li>
<button class="accountage" data-user="${item}" title="Schreibt ''!accountage ${item}'' in den Chat">?</button>
<button class="ignore" data-user="${item}" title="Benutzer aus Liste entfernen">❌</button>
<button class="unban" data-user="${item}" title="Benutzer entbannen">Unban</button>
<button class="ban" data-user="${item}" title="Benutzer bannen">Ban</button>
<button class="addModChannels" data-user="${item}" title="Kanal als Mod-Kanal hinzufügen">➕⚔</button>
<button class="removeModChannel" data-user="${item}" title="Kanal als Mod-Kanal entfernen">➖⚔</button>
<span><a href="https://twitch-tools.rootonline.de/followinglist_viewer.php?username=${item}" title="Dieser User folgt....(Weiterleitung zu comanderroot)" target="_blank" rel="noopener noreferrer">${item}</a></span>
</li>`;
let inner = queueList.size ? [...queueList].map(user => renderItem(user)).join('') : `
<div id="empty" class="empty">
<img class="toggleImport" src="https://github.com/TwitchmodsDACH/Bann-Hammer/blob/main/logo.png?raw=true" title="Start Bann-Hammer" width="370px" style="cursor: pointer; max-height: 80px; min-height: 80px">
</div>`;
d.querySelector('.list').innerHTML = `
<ul>
${inner}
</ul>`;
}
})();
function modMenu() {
'use strict';
function processStoredModChannels() {
'use strict';
const storedModChannels = JSON.parse(localStorage.getItem("myModChannels"));
const links = storedModChannels ? storedModChannels : [];
return links;
}
function createDropdownMenu(links) {
'use strict';
var modMenuAV = document.getElementById('modMenu')
var referenceButton1 = document.querySelector('div.Layout-sc-1xcs6mc-0.eSWdAT'); //Layout-sc-1xcs6mc-0.cXWuNa //Layout-sc-1xcs6mc-0.crbrgc eSWdAT
var referenceButton2 = document.querySelector('div.Layout-sc-1xcs6mc-0.khvQsi');
if (modMenuAV) { return; }
if (!location.href.includes("twitch.tv/moderator")) {
var referenceButton = referenceButton1
} else {
var referenceButton = referenceButton2
}
if (referenceButton) {
var dropdownMenu = referenceButton.parentElement;
}
const container = document.createElement('div');
container.style.position = 'relative';
container.style.width = "100%"
const dropdownButton = document.createElement('button');
dropdownButton.id = "modMenu";
dropdownButton.innerHTML = "<img src='https://static-cdn.jtvnw.net/mod-view-image-assets/modview-sword.svg' width='35px' height='35px'>";
dropdownButton.title = "Mod-Channels";
dropdownButton.style.width = "25px";
dropdownButton.style.display = "block";
dropdownButton.style.color = "#34AE0C";
dropdownButton.style.backgroundColor = "transparent";
dropdownButton.style.position = 'relative';
const dropdownList = document.createElement('ul');
dropdownList.style.display = "none";
dropdownList.style.listStyle = "none";
dropdownList.style.padding = 0;
dropdownList.style.margin = 0;
dropdownList.style.position = "absolute";
dropdownList.style.top = "40px";
dropdownList.style.left = "auto";
dropdownList.style.zIndex = "99999999";
dropdownList.style.backgroundColor = "#000";
dropdownMenu.appendChild(dropdownList);
if (location.href.includes("twitch.tv/moderator")) {
var targetSrc = 'https://static-cdn.jtvnw.net/mod-view-image-assets/modview-sword.svg';
if (document.querySelector(`img[src="${targetSrc}"][height="30"][width="30"]`)) {
var img = document.querySelector(`img[src="${targetSrc}"][height="30"][width="30"]`);
container.appendChild(dropdownButton);
dropdownList.style.top = "100%";
dropdownList.style.left = "0";
container.appendChild(dropdownList);
img.parentNode.replaceChild(container, img);
}
} else {
dropdownMenu.insertBefore(dropdownButton, referenceButton);
}
if (links.length == 0) {
const listItem = document.createElement('li');
const linkItem = document.createElement('a');
linkItem.innerText = "Bitte lies die Anleitung hier";
linkItem.href = "https://github.com/TwitchmodsDACH/Bann-Hammer#Mod-Men%C3%BC";
linkItem.target = "_blank";
linkItem.title = "Anleitung lesen";
listItem.appendChild(linkItem);
dropdownList.appendChild(listItem);
} else {
links.forEach(link => {
const listItem = document.createElement('li');
const linkItem = document.createElement('a');
linkItem.innerText = link;
linkItem.href = "https://twitch.tv/moderator/" + link;
linkItem.target = "_blank";
linkItem.title = "Visit Mod-View for channel " + link;
listItem.appendChild(linkItem);
dropdownList.appendChild(listItem);
});
}
dropdownButton.addEventListener('click', () => {
dropdownList.style.display = dropdownList.style.display === "none" ? "block" : "none";
});
}
const links = processStoredModChannels();
createDropdownMenu(links);
const css = `
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
`;
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
const selector = "#modMenu";
const element = document.querySelector(selector);
if (element) {
element.style.animation = "pulse 2s infinite";
}
}
// Startup
(function() {
const intervalDuration = 5000;
const totalTime = 5000;
let elapsedTime = 0;
const modMenuInterval = setInterval(() => {
if (elapsedTime >= totalTime) {
clearInterval(modMenuInterval);
} else {
modMenu();
elapsedTime += intervalDuration;
}
}, intervalDuration);
})();