Kahoot AntiBot

Remove all bots from a kahoot game.

目前為 2019-11-19 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Kahoot AntiBot
// @namespace    http://tampermonkey.net/
// @version      2.5.7
// @description  Remove all bots from a kahoot game.
// @author       theusaf
// @match        *://play.kahoot.it/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

if(window.fireLoaded){
 throw "[ANTIBOT] - page is loaded";
}
//window.stop(); //stop loading and save data from the page.
if(window.localStorage.extraCheck){
    console.log("[ANTIBOT] - Detected PIN Checker");
}
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(t,n);");
    const code = /*window.setupAntibot = */ ()=>{
        const percent = 0.6;
        var waterMark = document.createElement("p");
        waterMark.innerText = "v2.5.7 @theusaf";
        waterMark.style = "position: fixed; bottom: 100px; right:100px; font-size: 1em";
        document.body.append(waterMark);
        window.isUsingNamerator = false;
        window.cachedUsernames = [];
        window.confirmedPlayers = [];
        window.cachedData = {};
		window.loggedPlayers = {};
        var messageId = 0;
        var clientId = null;
        var pin = null;
        function similarity(s1, s2) {
            if(!s2){
                return 0;
            }
            if(s1){
                if(!isNaN(s2) && !isNaN(s1) && s1.length == s2.length){
                    return 1;
                }
            }
            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;
            }
            s1 = s1.toLowerCase();
            s2 = s2.toLowerCase();
            var longer = s1;
            var shorter = s2;
            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 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){
            // Disabled in V 2.5.5
            /*if(window.loggedPlayers[player.cid] === true){
                let packet = createKickPacket(player.cid);
                socket.send(JSON.stringify(packet));
                window.loggedPlayers[player.cid] = false;
		        throw "[ANTIBOT] - Evil intentions discovered";
	        }*/
            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`);
                    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`);
                        throw "[ANTIBOT] - Bot banned. Dont add";
                    }
                    if(similarity(window.cachedUsernames[i].name,player.name) >= 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;
                            window.cachedUsernames[i].time = 10;
                            socket.send(JSON.stringify(packet2));
                        }
                        console.warn(`[ANTIBOT] - Bots ${player.name} and ${window.cachedUsernames[i].name} have been banished`);
                        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(!window.cachedData[data.cid] && !isNaN(data.cid) && Object.keys(data).length <= 5){ //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
                };
              }else{
                /*if(window.loggedPlayers[data.cid] === false){
                  break;
                }*/
		        var packet = createKickPacket(data.cid);
                socket.send(JSON.stringify(packet));
                console.warn(`[ANTIBOT] - Bot ${data.name} has been banished, clearly a bot from kahootsmash or something`);
                throw "[ANTIBOT] - Bot banned. Dont add";
              }
              break;
          }
        }
        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.globalMessageListener = function(e,t){
            window.e = e;
            /*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 contains the pin*/
            /*if(data.id == 3){
                pin = data.data.gameid;
            }*/
            /*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);
            }
	        /*if the message is a player leave message*/
            if(data.data ? data.data.type == "left" : false){
                //Disabled in V2.5.5
              /*if(window.loggedPlayers[data.data.cid] === false){ //if the antibot detected the evildoer
                  //As of V 2.5.2, this has been abandoned since it doesn't actually work :p
                  window.loggedPlayers[data.data.cid] = true;
                  throw "[ANTIBOT] - Preventing removal of real player.";
              }
		      window.loggedPlayers[data.data.cid] = false;*/
	        }
            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`);
                }
            }
        };
    };
    let mainScript = new XMLHttpRequest();
    mainScript.open("GET","https://play.kahoot.it/"+script2);
    mainScript.send();
    mainScript.onload = ()=>{
      let sc = mainScript.response;
      sc = sc.replace("r.namerator","(()=>{console.log(r.namerator);window.isUsingNamerator = r.namerator;return r.namerator})()");
      let changed = originalPage.split("</body>");
      changed = `${changed[0]}<script>${patchedScript}</script><script>${sc}</script><script>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();
      //document.documentElement.innerHTML = changed;
    };
  };
};