您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Remove all bots from a kahoot game.
当前为
// ==UserScript== // @name Kahoot AntiBot // @namespace http://tampermonkey.net/ // @version 2.6.14 // @description Remove all bots from a kahoot game. // @author theusaf // @match *://play.kahoot.it/* // @exclude *://play.kahoot.it/v2/assets/* // @grant none // @run-at document-start // ==/UserScript== if(window.fireLoaded){ throw "[ANTIBOT] - page is loaded"; } if(window.localStorage.extraCheck){ console.log("[ANTIBOT] - Detected PIN Checker"); } if(window.localStorage.kahootThemeScript){ console.log("[ANTIBOT] - Detected KonoSuba Theme"); } document.write(""); window.url = window.location.href; window.page = new XMLHttpRequest(); window.page.open("GET",window.url); window.page.send(); window.page.onload = ()=>{ let scriptURL = window.page.response.match(/<\/script><script\ .*?vendors.*?><\/script>/mg)[0].substr(9).split("src=\"")[1].split("\"")[0]; let script2 = window.page.response.match(/<\/script><script src=\"\/v2\/assets\/js\/main.*?(?=\")/mg)[0].substr(22); let originalPage = window.page.response.replace(/<\/script><script\ .*?vendors.*?><\/script>/mg,"</script>"); originalPage = originalPage.replace(/<\/script><script src=\"\/v2\/assets\/js\/main.*?(?=\")/mg,"</script><script src=\"data:text/javascript,"); let script = new XMLHttpRequest(); script.open("GET","https://play.kahoot.it/"+scriptURL); script.send(); script.onload = ()=>{ let patchedScript =script.response.replace(".onMessage=function(e,n){",".onMessage=function(e,n){window.globalMessageListener(e,n);"); const code = ()=>{ // create watermark const container = document.createElement("div"); container.id = "antibotwtr"; const waterMark = document.createElement("p"); waterMark.innerHTML = "v2.6.13 @theusaf"; const botText = document.createElement("p"); botText.innerHTML = "0"; botText.id = "killcount"; const menu = document.createElement("details"); menu.innerHTML = `<summary>config</summary> <input type="checkbox" id="antibot.config.timeout"></input> <label id="antibot.config.timeoutlbl" onclick="window.specialData.config.timeout = !window.specialData.config.timeout;if(!localStorage.antibotConfig){localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(localStorage.antibotConfig);a.timeout = window.specialData.config.timeout;localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.timeout" title="Blocks answers that are sent before 0.5 seconds after the question starts">Min Answer Timeout</label> <input type="checkbox" id="antibot.config.looksRandom" checked="checked"></input> <label id="antibot.config.lookrandlbl" onclick="window.specialData.config.looksRandom = !window.specialData.config.looksRandom;if(!localStorage.antibotConfig){localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(localStorage.antibotConfig);a.looksRandom = window.specialData.config.looksRandom;localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.looksRandom" title="Blocks names that seem 'random', such as 'OmEGaboOt'">Block Random Names</label> <label for="antibot.config.teamtimeout" title="Add extra seconds to the question.">Additional Question Time</label> <input type="number" step="1" value="0" id="antibot.config.teamtimeout" onchange="window.specialData.config.additionalQuestionTime = Number(document.getElementById('antibot.config.teamtimeout').value);if(!localStorage.antibotConfig){localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(localStorage.antibotConfig);a.teamtime = window.specialData.config.additionalQuestionTime;localStorage.antibotConfig = JSON.stringify(a);">; <label for="antibot.config.percent" title="Specify the match percentage.">Match Percent</label> <input type="number" step="0.1" value="0.6" id="antibot.config.percent" onchange="window.specialData.config.percent = Number(document.getElementById('antibot.config.percent').value);if(!localStorage.antibotConfig){localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(localStorage.antibotConfig);a.percent = window.specialData.config.percent;localStorage.antibotConfig = JSON.stringify(a);">`; const styles = document.createElement("style"); styles.type = "text/css"; styles.innerHTML = `#antibotwtr{ position: fixed; bottom: 100px; right: 100px; font-size: 1rem; opacity: 0.4; transition: opacity 0.4s; z-index: 5000; } #antibotwtr:hover{ opacity: 1; } #antibotwtr p{ display: inline-block; } #killcount{ margin-left: 0.25rem; background: black; border-radius: 0.5rem; color: white; } #antibotwtr details{ background: grey; } #antibotwtr input[type="checkbox"]{ display: none; } #antibotwtr label{ color: red; display: block; } #antibotwtr input:checked+label{ color: green; }`; container.append(waterMark,botText,menu); setTimeout(function(){ if(document.body.innerText.split("\n").length < 8){ // assume broken. (just the water mark) const temp = document.createElement("template"); temp.innerHTML = `<div style="color: red; position: fixed; left: 0; top: 0; font-size: 1.5rem;line-height:2rem"> <h1>[ANTIBOT] - Detected broken page. I haven't actually foud a way to fix this issue completely yet, so do one of the following:</h1> <hr/> <h1>Go back to <a href="https://create.kahoot.it/details/${location.search.split("quizId=")[1].split("&")[0]}">the kahoot launch screen</a>.</h1><br/> <h1>Clear the cache of this page and then reload.</h1><br/> <h1>Disable Kahoot AntiBot, reload the page, then re-enable Kahoot Antibot and reload the page again</h1> </div>`; document.body.append(temp.content.cloneNode(true)); delete localStorage["kahoot-quizPins"]; } },2000); document.body.append(container,styles); window.isUsingNamerator = false; window.cachedUsernames = []; window.confirmedPlayers = []; window.cachedData = {}; window.loggedPlayers = {}; window.specialData = { startTime: 0, lastFakeLogin: 0, lastFakeUserID: 0, lastFakeUserName: "", config:{ timeout: false, looksRandom: true, additionalQuestionTime: null, percent: 0.6 } }; // loading localStorage info if(localStorage.antibotConfig){ const a = JSON.parse(localStorage.antibotConfig); if(a.timeout){ const t = document.getElementById("antibot.config.timeoutlbl"); if(t){ t.click(); } } if(!a.looksRandom){ const t = document.getElementById("antibot.config.lookrandlbl"); if(t){ t.click(); } } if(a.teamtime){ document.getElementById("antibot.config.teamtimeout").value = Number(a.teamtime); window.specialData.config.additionalQuestionTime = Number(a.teamtime); } if(a.percent){ document.getElementById("antibot.config.percent").value = Number(a.percent); window.specialData.config.percent = Number(a.percent); } } var messageId = 0; var clientId = null; var pin = null; // for names like KaHOotSmaSH function looksRandom(name){ // assumes player names have either all caps, no caps, or up to 3 capital letters if(name.replace(/[A-Z\s]/gm,"").length == 0){ return false; } if(name.replace(/[a-z\s]/gm,"").length == 0){ return false; } if(name.length - name.replace(/[A-Z]/gm,"").length < 4){ return false; } return true; } // for names like AmazingRobot32 function isFakeValid(name){ let caps = name.length - name.replace(/[A-Z]/g, '').length; if(caps !== 2){ /*has less than 2 or more than 2 capitals*/ return false; } if (name.substr(0,1).replace(/[A-Z]/g,'').length === 1){/*first char is not a capital*/ return false; } if (name.substr(1,2).replace(/[A-Z]/g,'').length != 2){/*next few char have capitals*/ return false; } if(name.substr(name.length -2,2).replace(/[A-Z]/g,'').length !== 2){ /*last few char have acapital*/ return false; } if(name.replace(/([a-z]|[0-9])/ig,'').length > 0){ /*hasnon-letter/number chars*/ return false; } return true; } function similarity(s1, s2) { // remove numbers from name if name is not only a number if(!isNaN(s1) && typeof(s1) != "object" && !window.isUsingNamerator){ s1 = s1.replace(/[0-9]/mg,""); } if(!isNaN(s2) && typeof(s2) != "object" && !window.isUsingNamerator){ s2 = s2.replace(/[0-9]/mg,""); } if(!s2){ return 0; } // if is a number of the same length if(s1){ if(!isNaN(s2) && !isNaN(s1) && s1.length == s2.length){ return 1; } } // apply namerator rules if(window.isUsingNamerator){ let caps = s2.length - s2.replace(/[A-Z]/g, '').length; if(caps !== 2){ /*has less than 2 or more than 2 capitals*/ return -1; } if (s2.substr(0,1).replace(/[A-Z]/g,'').length === 1){/*first char is not a capital*/ return -1; } if (s2.substr(1,2).replace(/[A-Z]/g,'').length != 2){/*next few char have capitals*/ return -1; } if(s2.substr(s2.length -2,2).replace(/[A-Z]/g,'').length !== 2){ /*last few char have acapital*/ return -1; } if(s2.replace(/[a-z]/ig,'').length > 0){ /*hasnon-letter chars*/ return -1; } } if(!s1){ return; } // ignore case s1 = s1.toLowerCase(); s2 = s2.toLowerCase(); var longer = s1; var shorter = s2; // begin math to determine similarity if (s1.length < s2.length) { longer = s2; shorter = s1; } var longerLength = longer.length; if (longerLength == 0) { return 1.0; } return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength); } function editDistance(s1, s2) { s1 = s1.toLowerCase(); s2 = s2.toLowerCase(); var costs = new Array(); for (var i = 0; i <= s1.length; i++) { var lastValue = i; for (var j = 0; j <= s2.length; j++) { if (i == 0){ costs[j] = j; } else { if (j > 0) { var newValue = costs[j - 1]; if (s1.charAt(i - 1) != s2.charAt(j - 1)){ newValue = Math.min(Math.min(newValue,lastValue),costs[j]) + 1; } costs[j - 1] = lastValue; lastValue = newValue; } } } if (i > 0){ costs[s2.length] = lastValue; } } return costs[s2.length]; } function setup(){ var launch_button =document.querySelectorAll("[data-functional-selector=\"launch-button\"]")[0]; if(launch_button){ console.warn("[ANTIBOT] - launch button found!"); }else{ setTimeout(setup,1000); } } setup(); function clickName(name){ const names = document.querySelectorAll("[data-functional-selector=player-name]"); names.forEach(o=>{ if(o.innerText == name){ return o.click(); } }); } function createKickPacket(id){ messageId++; return [{ channel: "/service/player", clientId: clientId, id: String(Number(messageId)), data: { cid: String(id), content: JSON.stringify({ kickCode: 1, quizType: "quiz" }), gameid: pin, host: "play.kahoot.it", id: 10, type: "message" }, ext: {} }]; } function determineEvil(player,socket){ if(window.cachedUsernames.length == 0){ if(similarity(null,player.name) == -1){ var packet = createKickPacket(player.cid); socket.send(JSON.stringify(packet)); console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } delete window.cachedData[player.cid]; throw "[ANTIBOT] - Bot banned. Dont add"; } window.cachedUsernames.push({name: player.name, id:player.cid, time: 10, banned: false}); window.loggedPlayers[player.cid] = true; }else{ var removed = false; for(var i in window.cachedUsernames){ if(window.confirmedPlayers.includes(window.cachedUsernames[i].name)){ continue; } if(similarity(window.cachedUsernames[i].name,player.name) == -1){ removed = true; var packet1 = createKickPacket(player.cid); socket.send(JSON.stringify(packet1)); console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } delete window.cachedData[player.cid]; throw "[ANTIBOT] - Bot banned. Dont add"; } if(similarity(window.cachedUsernames[i].name,player.name) >= window.specialData.config.percent){ removed = true; let packet1 = createKickPacket(player.cid); socket.send(JSON.stringify(packet1)); if(!window.cachedUsernames[i].banned){ var packet2 =createKickPacket(window.cachedUsernames[i].id); window.cachedUsernames[i].banned = true; socket.send(JSON.stringify(packet2)); clickName(window.cachedUsernames[i].name); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } } window.cachedUsernames[i].time = 10; console.warn(`[ANTIBOT] - Bots ${player.name} and ${window.cachedUsernames[i].name} have been banished`); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } delete window.cachedData[player.cid]; delete window.cachedData[window.cachedUsernames[i].id]; throw "[ANTIBOT] - Bot banned. Dont add"; } } if(!removed){ window.cachedUsernames.push({name: player.name,id: player.cid, time: 10, banned: false}); window.loggedPlayers[player.cid] = true; } } } function specialBotDetector(type,data,socket){ switch (type) { case 'joined': // if looks random if(window.specialData.config.looksRandom){ if(looksRandom(data.name)){ const packet = createKickPacket(data.cid); socket.send(JSON.stringify(packet)); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } window.cachedUsernames.forEach(o=>{ if(o.id == data.cid){ o.banned = true; o.time = 10; return; } }); throw `[ANTIBOT] - Bot ${data.name} banned; name too random.`; } } if(!window.cachedData[data.cid] && !isNaN(data.cid) && Object.keys(data).length <= 5 && data.name.length < 16){ //if the id has not been cached yet or is an invalid id, and they are not a bot :p window.cachedData[data.cid] = { tries: 0, loginTime: Date.now() }; }else{ if(window.cachedData[data.cid]){ // now allowing reconnection return; } const packet = createKickPacket(data.cid); socket.send(JSON.stringify(packet)); console.warn(`[ANTIBOT] - Bot ${data.name} has been banished - invalid packet/name`); window.cachedUsernames.forEach(o=>{ if(o.id == data.cid){ o.banned = true; o.time = 10; return; } }); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } delete window.cachedData[data.cid]; throw "[ANTIBOT] - Bot banned. Dont add"; } if(!window.isUsingNamerator){ if(isFakeValid(data.name)){ if(Date.now() - window.specialData.lastFakeLogin < 5000){ if(window.cachedData[window.specialData.lastFakeUserID]){ // to get the first guy const packet = createKickPacket(window.specialData.lastFakeUserID); socket.send(JSON.stringify(packet)); clickName(window.specialData.lastFakeUserName); delete window.cachedData[window.specialData.lastFakeUserID]; window.cachedUsernames.forEach(o=>{ if(o.id == window.specialData.lastFakeUserID){ o.banned = true; o.time = 10; return; } }); } const packet = createKickPacket(data.cid); socket.send(JSON.stringify(packet)); delete window.cachedData[data.cid]; window.cachedUsernames.forEach(o=>{ if(o.id == data.cid){ o.banned = true; return; } }); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } window.specialData.lastFakeLogin = Date.now(); window.specialData.lastFakeUserID = data.cid; window.specialData.lastFakeUserName = data.name; throw `[ANTIBOT] - Banned bot ${data.name}; their name is suspicious, likely a bot.`; } window.specialData.lastFakeLogin = Date.now(); window.specialData.lastFakeUserID = data.cid; window.specialData.lastFakeUserName = data.name; } } break; } } function teamBotDetector(team,cid,socket){ kick = false; if(team.length == 0 || team.indexOf("") != -1 || team.indexOf("Player 1") != -1){ kick = true; } if(kick){ const packet = createKickPacket(cid); socket.send(JSON.stringify(packet)); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } let name = ""; delete window.cachedData[cid]; window.cachedUsernames.forEach(o=>{ name = o.name; if(o.id == cid){ o.banned = true; o.time = 10; return; } }); throw `[ANTIBOT] - Bot ${name} banned; invalid team members.`; } } var timer = setInterval(function(){ for(let i in window.cachedUsernames){ if(window.cachedUsernames[i].time <= 0 && !window.cachedUsernames[i].banned && !window.confirmedPlayers.includes(window.cachedUsernames[i].name)){ window.confirmedPlayers.push(window.cachedUsernames[i].name); continue; } if(window.cachedUsernames[i].time <= -20){ window.cachedUsernames.splice(i,1); continue; } window.cachedUsernames[i].time--; } },1000); const TFATimer = setInterval(()=>{ for(let i in window.cachedData){ window.cachedData[i].tries = 0; } },10000); window.sendHandler = function(data){ data = JSON.parse(data)[0]; if(data.data){ if(!data.data.id){ return; } switch (data.data.id) { case 2: window.specialData.startTime = Date.now(); break; } } } window.globalMessageListener = function(e,t){ window.e = e; if(!window.e.webSocket.oldSend){ window.e.webSocket.oldSend = window.e.webSocket.send; window.e.webSocket.send = function(data){ window.sendHandler(data); window.e.webSocket.oldSend(data); } } /*console.log(e); from testing: e[.webSocket] is the websocket*/ var data = JSON.parse(t.data)[0]; /*console.log(data);*/ messageId = data.id ? data.id : messageId; /*if the message is the first message, which contains important clientid data*/ if(data.id == 1){ clientId = data.clientId; } try{ pin = pin ? pin : Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML); if(Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML) != pin){ pin = Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML); } }catch(err){} /*if the message is a player join message*/ if(data.data ? data.data.type == "joined" : false){ console.warn("[ANTIBOT] - determining evil..."); determineEvil(data.data,e.webSocket); specialBotDetector(data.data.type,data.data,e.webSocket); }else /*if the message is a player leave message*/ if(data.data ? data.data.type == "left" : false){ }else if(data.data ? data.data.id == 45 : false){ // if player answers if(Date.now() - window.specialData.startTime < 500 && window.specialData.config.timeout){ throw "[ANTIBOT] - Answer was too quick!"; } // if player just recently joined (within 1 second) if(window.cachedData[data.data.cid] && Date.now() - window.cachedData[data.data.cid].loginTime < 1000){ const packet = createKickPacket(data.data.cid); window.e.webSocket.send(JSON.stringify(packet)); const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } delete window.cachedData[data.data.cid]; throw `[ANTIBOT] - Bot with id ${data.data.cid} banned. Answered too quickly after joining.`; } }else if(data.data ? data.data.id == 50 : false){ window.cachedData[data.data.cid].tries++; if(window.cachedData[data.data.cid].tries > 3){ const kicker = createKickPacket(data.data.cid); e.webSocket.send(JSON.stringify(kicker)); const name = window.cachedUsernames.filter(o=>{return o.id == data.data.cid}).length ? window.cachedUsernames.filter(o=>{return o.id == data.data.cid})[0].name : "bot"; console.warn(`[ANTIBOT] - Bot ${name} banished. Seen spamming 2FA`); window.cachedUsernames.forEach(o=>{ if(o.id == window.specialData.lastFakeUserID){ o.banned = true; o.time = 10; return; } }); delete window.cachedData[data.data.cid]; const c = document.getElementById("killcount"); if(c){ c.innerHTML = Number(c.innerHTML) + 1; } } }else if (data.data && data.data.id == 18) { teamBotDetector(JSON.parse(data.data.content),data.data.cid,e.webSocket); } }; }; let mainScript = new XMLHttpRequest(); mainScript.open("GET","https://play.kahoot.it/"+script2); mainScript.send(); mainScript.onload = ()=>{ let sc = mainScript.response; sc = sc.replace("o.namerator","(()=>{console.log(o.namerator);window.isUsingNamerator = o.namerator;return o.namerator})()"); sc = sc.replace("currentQuestionTimer:o.payload.time","currentQuestionTimer:o.payload.time + (()=>{return (window.specialData.config.additionalQuestionTime * 1000) || 0})()"); let changed = originalPage.split("</body>"); changed = `${changed[0]}<script>${patchedScript}</script><script>${sc}</script><script>try{(${window.localStorage.kahootThemeScript})();}catch(err){}try{(${window.localStorage.extraCheck})();}catch(err){}window.setupAntibot = ${code.toString()};window.fireLoaded = true;window.setupAntibot();</script></body>${changed[1]}`; console.log("[ANTIBOT] - loaded"); document.open(); document.write(changed); document.close(); }; }; };