您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A tool for moderating Twitch easier during hate raids
// ==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); })();