Kahoot AntiBot

Remove all bots from a kahoot game.

目前為 2021-01-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Kahoot AntiBot
// @namespace    http://tampermonkey.net/
// @version      2.13.0
// @icon         https://cdn.discordapp.com/icons/641133408205930506/31c023710d468520708d6defb32a89bc.png
// @description  Remove all bots from a kahoot game.
// @author       theusaf
// @copyright    2019-2021, Daniel Lau (https://github.com/theusaf/kahoot-antibot)
// @match        *://play.kahoot.it/*
// @exclude      *://play.kahoot.it/v2/assets/*
// @grant        none
// @run-at       document-start
// @license      MIT; https://opensource.org/licenses/MIT
// ==/UserScript==

if(window.fireLoaded || (window.parent && window.parent.page)){
  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("[ANTIBOT] - Patching Kahoot. Please wait. If this screen stays blank for long periods of time, please force reload or clear your cache.");
window.url = window.location.href;
window.page = new XMLHttpRequest();
window.page.open("GET",window.url);
window.page.send();
window.page.onload = ()=>{
  const scriptURL = window.page.response.match(/><\/script><script .*?vendors.*?><\/script>/mg)[0].substr(9).split("src=\"")[1].split("\"")[0],
    script2 = window.page.response.match(/\/v2\/assets\/js\/main.*?(?=")/mg)[0];
  let originalPage = window.page.response.replace(/><\/script><script .*?vendors.*?><\/script>/mg,"></script>");
  originalPage = originalPage.replace(/\/v2\/assets\/js\/main.*?(?=")/mg,"data:text/javascript,");
  const script = new XMLHttpRequest();
  script.open("GET","https://play.kahoot.it/"+scriptURL);
  script.send();
  script.onload = ()=>{
    const patchedScriptRegex = /\.onMessage=function\([a-z],[a-z]\)\{/mg,
      letter1 = script.response.match(patchedScriptRegex)[0].match(/[a-z](?=,)/g)[0],
      letter2 = script.response.match(patchedScriptRegex)[0].match(/[a-z](?=\))/g)[0],
      patchedScript = script.response.replace(script.response.match(patchedScriptRegex)[0],`.onMessage=function(${letter1},${letter2}){window.globalMessageListener(${letter1},${letter2});`),
      code = ()=>{
        const windw = window.parent;
        window.windw = windw;
        // create watermark
        const container = document.createElement("div");
        container.id = "antibotwtr";
        const waterMark = document.createElement("p");
        waterMark.innerHTML = "v2.13.0 @theusaf";
        const botText = document.createElement("p");
        botText.innerHTML = "0";
        botText.id = "killcount";
        const menu = document.createElement("details");
        menu.innerHTML = `<summary>config</summary>
        <div id="antibot-settings">
          <!-- Timeout -->
          <div>
            <input type="checkbox" id="antibot.config.timeout"></input>
            <label id="antibot.config.timeoutlbl" onclick="windw.specialData.config.timeout = !windw.specialData.config.timeout;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.timeout = windw.specialData.config.timeout;windw.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>
          </div>
          <!-- Random Names -->
          <div>
            <input type="checkbox" id="antibot.config.looksRandom" checked="checked"></input>
            <label id="antibot.config.lookrandlbl" onclick="windw.specialData.config.looksRandom = !windw.specialData.config.looksRandom;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.looksRandom = windw.specialData.config.looksRandom;windw.localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.looksRandom" title="Blocks names that seem 'random', such as 'OmEGaboOt'">Block Random Names</label>
          </div>
          <!-- Blocking Format 1 -->
          <div>
            <input type="checkbox" id="antibot.config.blockformat1" checked="checked"></input>
            <label id="antibot.config.blockformat1lbl" onclick="windw.specialData.config.banFormat1 = !windw.specialData.config.banFormat1;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.banFormat1 = windw.specialData.config.banFormat1;windw.localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.blockformat1" title="Blocks names using the format [First][random char][Last]">Block format First[._-,etc]Last</label>
          </div>
          <!-- Blocking kahootflood.weebly.com -->
          <div>
            <input type="checkbox" id="antibot.config.blockservice1"></input>
            <label onclick="windw.specialData.config.blockservice1 = !windw.specialData.config.blockservice1;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.blockservice1 = windw.specialData.config.blockservice1;windw.localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.blockservice1" title="A special filter focused on kahootflood.weebly.com">Block kahootflood.weebly.com</label>
          </div>
          <!-- Block Numbers -->
          <div>
            <input type="checkbox" id="antibot.config.blocknum"></input>
            <label onclick="windw.specialData.config.blocknum = !windw.specialData.config.blocknum;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.blocknum = windw.specialData.config.blocknum;windw.localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.blocknum" title="Marks names with numbers as suspicious. If multiple players join with numbers in their name in a short amount of time, they will be banned.">Block Numbers</label>
          </div>
          <!-- Block Non-Ascii -->
          <div>
            <input type="checkbox" id="antibot.config.forceascii"></input>
            <label onclick="windw.specialData.config.forceascii = !windw.specialData.config.forceascii;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.forceascii = windw.specialData.config.forceascii;windw.localStorage.antibotConfig = JSON.stringify(a);" for="antibot.config.forceascii" title="Marks names with non-alphanumeric characters as suspicious and bans them if multiple join.">Force Alphanumeric</label>
          </div>
          <!-- Additional Question Time -->
          <div>
            <label class="antibot-input" 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="windw.specialData.config.additionalQuestionTime = Number(document.getElementById('antibot.config.teamtimeout').value);if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.teamtime = windw.specialData.config.additionalQuestionTime;windw.localStorage.antibotConfig = JSON.stringify(a);">
          </div>
          <!-- Percent -->
          <div>
            <label class="antibot-input" 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="windw.specialData.config.percent = Number(document.getElementById('antibot.config.percent').value);if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.percent = windw.specialData.config.percent;windw.localStorage.antibotConfig = JSON.stringify(a);">
          </div>
          <!-- DDOS -->
          <div>
            <label class="antibot-input" for="antibot.config.ddos" title="Specify the number of bots/minute to lock the game. Set it to 0 to disable.">Auto Lock Threshold</label>
            <input type="number" step="1" value="0" id="antibot.config.ddos" onchange="windw.specialData.config.ddos = Number(document.getElementById('antibot.config.ddos').value);if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.ddos = windw.specialData.config.ddos;windw.localStorage.antibotConfig = JSON.stringify(a);">
          </div>
          <!-- Auto-Start-Lock -->
          <div>
            <label class="antibot-input" for="antibot.config.start_lock" title="Specify the maximum time in seconds for a lobby to stay open after a player joins. Setting this to 0 will disable it.">Lobby Auto-Start Time</label>
            <input type="number" step="1" value="0" id="antibot.config.start_lock" onchange="windw.specialData.config.start_lock = Number(document.getElementById('antibot.config.start_lock').value);if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.start_lock = windw.specialData.config.start_lock;windw.localStorage.antibotConfig = JSON.stringify(a);">
          </div>
          <!-- Toggling Streak Bonus -->
          <div>
            <input type="checkbox" id="antibot.config.streakBonus" onchange="windw.specialData.config.streakBonus = Number(document.getElementById('antibot.config.streakBonus').checked ? 1 : 2);if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.streakBonus = windw.specialData.config.streakBonus;localStorage.antibotConfig = JSON.stringify(a);alert('When modifying this option, reload the page for it to take effect')">
            <label for="antibot.config.streakBonus" title="Toggle the Streak Bonus.">Toggle Streak Bonus</label>
          </div>
          <!-- Show Antibot Counters -->
          <div>
            <input type="checkbox" id="antibot.config.counters" onchange="windw.specialData.config.counters = document.getElementById('antibot.config.counters').checked;if(!windw.localStorage.antibotConfig){windw.localStorage.antibotConfig = JSON.stringify({});}const a = JSON.parse(windw.localStorage.antibotConfig);a.counters = windw.specialData.config.counters;localStorage.antibotConfig = JSON.stringify(a);">
            <label for="antibot.config.counters" title="Shows Antibot Countdowns (Lobby Auto-Start/Auto-Lock)">Show Antibot Timers</label>
          </div>
          <!-- Counter cheats -->
          <div>
            <input type="checkbox" id="antibot.config.counterCheats" onchange="windw.specialData.config.counterCheats = document.getElementById('antibot.config.counterCheats').checked;
              if(!windw.localStorage.antibotConfig){
                windw.localStorage.antibotConfig = JSON.stringify({});
              }
              const a = JSON.parse(windw.localStorage.antibotConfig);
              a.counterCheats = windw.specialData.config.counterCheats;
              localStorage.antibotConfig = JSON.stringify(a);
              if(a.counterCheats){
                // enable cheats?
                alert('This setting will only be applied once the page is reloaded.');
              }else{
                // disable anti-cheat
                const q = windw.specialData.globalQuizData.questions;
                if(q[q.length - 1].isAntibotQuestion){
                  q.splice(-1,1);
                }
              }">
            <label for="antibot.config.counterCheats" title="Adds an additional 20 second question at the end to counter cheats.">Counter Kahoot Cheats</label>
          </div>
        </div>`;
        const counters = document.createElement("div");
        counters.id = "antibot-counters";
        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;
          background: white;
          text-align: center;
          border-radius: 0.5rem;
        }
        #antibotwtr summary{
          text-align: left;
        }
        #antibotwtr:hover{
          opacity: 1;
        }
        #antibotwtr p{
          display: inline-block;
        }
        #antibotwtr p:first-child{
          font-weight: 600;
        }
        #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: black;
          font-weight: 600;
          display: block;
          background: #c60929;
          border-radius: 0.5rem;
          height: 100%;
          word-break: break-word;
        }
        #antibotwtr .antibot-input{
          height: calc(100% - 1.5rem);
          background: #864cbf;
          color: white;
        }
        #antibotwtr input{
          position: absolute;
          bottom: 0;
          left: 0;
          width: 100%;
          height: 1rem;
          border-radius: 0.25rem;
          border: solid 1px black;
        }
        #antibotwtr input:checked+label{
          background: #26890c;
        }
        #antibot-settings{
          display: flex;
          flex-wrap: wrap;
          max-width: 25rem;
          max-height: 24rem;
          overflow: auto;
        }
        #antibot-settings > div{
          flex: 1;
          max-width: 33%;
          min-width: 33%;
          min-height: 6rem;
          box-sizing: border-box;
          position: relative;
          border: solid 0.5rem transparent;
        }
        #antibot-counters{
          position: absolute;
          right: 10rem;
          top: 11rem;
          font-size: 1.5rem;
          font-weight: 700;
          color: white;
          pointer-events: none;
        }
        #antibot-counters div{
          background: rgba(0,0,0,0.5);
          padding: 0.5rem;
          border-radius: 0.5rem;
          margin-bottom: 0.5rem;
        }
        .antibot-count-num{
          display: block;
          text-align: center;
        }
        .antibot-count-desc{
          text-align: center;
          font-size: 1.25rem;
          display: block;
        }`;
        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 id="antibot-broken-page" style="color: red; position: fixed; left: 0; top: 0; font-size: 1.25rem;line-height:1.75rem">
            <h2>[ANTIBOT] - Detected broken page. This message may appear due to slow internet. It will dissapear once the page loads. If the page doesn't load, try one of the following:</h2>
            <hr/>
            <h2>Reload the page</h2>
            <h2>Go back to <a href="https://create.kahoot.it/details/${location.search.split("quizId=")[1].split("&")[0]}">the kahoot launch screen</a>.</h2><br/>
            <h2>Clear the cache of this page and then reload.</h2><br/>
            <h2>Disable Kahoot AntiBot, reload the page, then re-enable Kahoot Antibot and reload the page again</h2>
          </div>`;
            document.body.append(temp.content.cloneNode(true));
            const RemoveBroke = setInterval(()=>{
              if(document.body.innerText.split("\n").length >= 20){
                clearInterval(RemoveBroke);
                document.getElementById("antibot-broken-page").outerHTML = "";
              }
            },1000);
          }
        },2000);
        document.body.append(container,styles,counters);
        const killcount = document.getElementById("killcount");
        windw.isUsingNamerator = false;
        windw.cachedUsernames = [];
        windw.confirmedPlayers = [];
        windw.cachedData = {};
        windw.loggedPlayers = {};
        windw.specialData = {
          startTime: 0,
          lastFakeLogin: 0,
          lastFakeUserID: 0,
          lastFakeUserName: "",
          config:{
            timeout: false,
            blocknum: false,
            looksRandom: true,
            banFormat1: true,
            additionalQuestionTime: null,
            percent: 0.6,
            streakBonus: 2,
            ddos: 0,
            start_lock: 0,
            counters: false,
            forceascii: false,
            blockservice1: false
          },
          inLobby: true,
          lobbyLoadTime: 0,
          lockInterval: null
        };
        // loading localStorage info
        if(windw.localStorage.antibotConfig){
          const a = JSON.parse(windw.localStorage.antibotConfig);
          if(a.timeout){
            const t = document.getElementById("antibot.config.timeoutlbl");
            if(t){
              t.click();
            }
          }
          if(a.blocknum){
            const t = document.getElementById("antibot.config.blocknum");
            t.checked = true;
            windw.specialData.config.blocknum = true;
          }
          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);
            windw.specialData.config.additionalQuestionTime = Number(a.teamtime);
          }
          if(a.percent){
            document.getElementById("antibot.config.percent").value = Number(a.percent);
            windw.specialData.config.percent = Number(a.percent);
          }
          if(!a.banFormat1){
            document.getElementById("antibot.config.blockformat1").checked = false;
            windw.specialData.config.banFormat1 = false;
          }
          if(a.streakBonus == 1){
            document.getElementById("antibot.config.streakBonus").checked = true;
            windw.specialData.config.streakBonus = 1;
          }
          if(a.ddos){
            document.getElementById("antibot.config.ddos").value = +a.ddos;
            windw.specialData.config.ddos = +a.ddos;
          }
          if(a.start_lock){
            document.getElementById("antibot.config.start_lock").value = +a.start_lock;
            windw.specialData.config.start_lock = +a.start_lock;
          }
          if(a.counters){
            document.getElementById("antibot.config.counters").checked = true;
            windw.specialData.config.counters = true;
          }
          if(a.forceascii){
            document.getElementById("antibot.config.forceascii").checked = true;
            windw.specialData.config.forceascii = true;
          }
          if(a.blockservice1){
            document.getElementById("antibot.config.blockservice1").checked = true;
            windw.specialData.config.blockservice1 = true;
          }
          if(a.counterCheats){
            document.getElementById("antibot.config.counterCheats").checked = true;
            windw.specialData.config.counterCheats = true;
          }
        }
        let messageId = 0,
          clientId = null,
          pin = null;
        // for names like KaHOotSmaSH
        function looksRandom(name){
        // Assumes player names have either all caps, no caps, or up to 3 capital letters
          return !/(^(([^A-Z\n]*)?[A-Z]?([^A-Z\n]*)?){0,3}$)|^([A-Z]*)$/.test(name);
        }
        // for names like AmazingRobot32
        // also matches other somewhat suspicious names
        function isFakeValid(name){
          if(windw.specialData.config.blocknum && /\d/.test(name)){
            return true;
          }
          if(windw.specialData.config.forceascii && /[^\d\s\w_-]/.test(name)){
            return true;
          }
          return /(^([A-Z][a-z]+){2,3}\d{1,2}$)|(^([A-Z][^A-Z\n]+?)+?(\d[a-z]+\d*?)$)|(^[a-zA-Z]+\d{4,}$)/.test(name);
        }
        function similarity(s1, s2) {
          // remove numbers from name if name is not only a number
          if(isNaN(s1) && typeof(s1) !== "object" && !windw.isUsingNamerator){
            s1 = s1.replace(/[0-9]/mg,"");
          }
          if(isNaN(s2) && typeof(s2) !== "object" && !windw.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(windw.isUsingNamerator){
            if(!/^([A-Z][a-z]+){2,3}$/.test(s2)){
              return -1;
            }
          }
          if(!s1){
            return;
          }
          // ignore case
          s1 = s1.toLowerCase();
          s2 = s2.toLowerCase();
          let longer = s1,
            shorter = s2;
          // begin math to determine similarity
          if (s1.length < s2.length) {
            longer = s2;
            shorter = s1;
          }
          const 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();

          const costs = new Array();
          for (let i = 0; i <= s1.length; i++) {
            let lastValue = i;
            for (let j = 0; j <= s2.length; j++) {
              if (i == 0){
                costs[j] = j;
              }
              else {
                if (j > 0) {
                  let 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(){
          const 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(windw.cachedUsernames.length == 0){
            if(similarity(null,player.name) == -1){
              const packet = createKickPacket(player.cid);
              socket.send(JSON.stringify(packet));
              console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`);
              killcount.innerHTML = +killcount.innerHTML + 1;
              delete windw.cachedData[player.cid];
              throw "[ANTIBOT] - Bot banned. Dont add";
            }
            windw.cachedUsernames.push({name: player.name, id:player.cid, time: 10, banned: false});
            windw.loggedPlayers[player.cid] = true;
          }else{
            let removed = false;
            if(similarity(null,player.name) == -1){
              removed = true;
              const packet1 = createKickPacket(player.cid);
              socket.send(JSON.stringify(packet1));
              console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`);
              killcount.innerHTML = +killcount.innerHTML + 1;
              delete windw.cachedData[player.cid];
              throw "[ANTIBOT] - Bot banned. Dont add";
            }
            for(const i in windw.cachedUsernames){
              if(windw.confirmedPlayers.includes(windw.cachedUsernames[i].name)){
                continue;
              }
              if(similarity(windw.cachedUsernames[i].name,player.name) >= windw.specialData.config.percent){
                removed = true;
                const packet1 = createKickPacket(player.cid);
                socket.send(JSON.stringify(packet1));
                if(!windw.cachedUsernames[i].banned){
                  const packet2 =createKickPacket(windw.cachedUsernames[i].id);
                  windw.cachedUsernames[i].banned = true;
                  socket.send(JSON.stringify(packet2));
                  clickName(windw.cachedUsernames[i].name);
                  killcount.innerHTML = +killcount.innerHTML + 1;
                }
                windw.cachedUsernames[i].time = 10;
                console.warn(`[ANTIBOT] - Bots ${player.name} and ${windw.cachedUsernames[i].name} have been banished`);
                killcount.innerHTML = +killcount.innerHTML + 1;
                delete windw.cachedData[player.cid];
                delete windw.cachedData[windw.cachedUsernames[i].id];
                throw "[ANTIBOT] - Bot banned. Dont add";
              }
            }
            if(!removed){
              windw.cachedUsernames.push({name: player.name,id: player.cid, time: 10, banned: false});
              windw.loggedPlayers[player.cid] = true;
            }
          }
        }
        function specialBotDetector(type,data,socket){
          switch (type) {
            case "joined":
              // if looks random
              if(windw.specialData.config.looksRandom){
                if(looksRandom(data.name)){
                  const packet = createKickPacket(data.cid);
                  socket.send(JSON.stringify(packet));
                  killcount.innerHTML = +killcount.innerHTML + 1;
                  windw.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 ban format 1 is enabled
              if(windw.specialData.config.banFormat1){
                if(/[a-z0-9]+[^a-z0-9\s][a-z0-9]+/gi.test(data.name)){
                  const packet = createKickPacket(data.cid);
                  socket.send(JSON.stringify(packet));
                  killcount.innerHTML = +killcount.innerHTML + 1;
                  windw.cachedUsernames.forEach(o=>{
                    if(o.id == data.cid){
                      o.banned = true;
                      o.time = 10;
                      return;
                    }
                  });
                  throw `[ANTIBOT] - Bot ${data.name} banned; Name matches format [F][R][L].`;
                }
              }
              // special filters for kahootflood.weebly.com
              if(windw.specialData.config.blockservice1){
                if(data.name.replace(/[ᗩᗷᑕᗪEᖴGᕼIᒍKᒪᗰᑎOᑭᑫᖇᔕTᑌᐯᗯ᙭Yᘔ]/g,"").length / data.name.length < 0.5){
                  const packet = createKickPacket(data.cid);
                  socket.send(JSON.stringify(packet));
                  killcount.innerHTML = +killcount.innerHTML + 1;
                  windw.cachedUsernames.forEach(o=>{
                    if(o.id == data.cid){
                      o.banned = true;
                      o.time = 10;
                      return;
                    }
                  });
                  throw `[ANTIBOT] - Bot ${data.name} banned; likely from kahootflood.weebly.com.`;
                }
              }
              if(!windw.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
                windw.cachedData[data.cid] = {
                  tries: 0,
                  loginTime: Date.now()
                };
              }else{
                if(windw.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`);
                windw.cachedUsernames.forEach(o=>{
                  if(o.id == data.cid){
                    o.banned = true;
                    o.time = 10;
                    return;
                  }
                });
                killcount.innerHTML = +killcount.innerHTML + 1;
                delete windw.cachedData[data.cid];
                throw "[ANTIBOT] - Bot banned. Dont add";
              }
              if(!windw.isUsingNamerator){
                if(isFakeValid(data.name)){
                  if(Date.now() - windw.specialData.lastFakeLogin < 5000){
                    if(windw.cachedData[windw.specialData.lastFakeUserID]){ // to get the first guy
                      const packet = createKickPacket(windw.specialData.lastFakeUserID);
                      socket.send(JSON.stringify(packet));
                      clickName(windw.specialData.lastFakeUserName);
                      delete windw.cachedData[windw.specialData.lastFakeUserID]; windw.cachedUsernames.forEach(o=>{
                        if(o.id == windw.specialData.lastFakeUserID){
                          o.banned = true;
                          o.time = 10;
                          return;
                        }
                      });
                    }
                    const packet = createKickPacket(data.cid);
                    socket.send(JSON.stringify(packet));
                    delete windw.cachedData[data.cid];
                    windw.cachedUsernames.forEach(o=>{
                      if(o.id == data.cid){
                        o.banned = true;
                        return;
                      }
                    });
                    killcount.innerHTML = +killcount.innerHTML + 1;
                    windw.specialData.lastFakeLogin = Date.now();
                    windw.specialData.lastFakeUserID = data.cid;
                    windw.specialData.lastFakeUserName = data.name;
                    throw `[ANTIBOT] - Banned bot ${data.name}; their name is suspicious, likely a bot.`;
                  }
                  windw.specialData.lastFakeLogin = Date.now();
                  windw.specialData.lastFakeUserID = data.cid;
                  windw.specialData.lastFakeUserName = data.name;
                }
              }
              break;
          }
        }
        function teamBotDetector(team,cid,socket){
          let kick = false;
          if(team.length == 0 || team.indexOf("") != -1 || team.indexOf("Player 1") != -1 || team.join("") === "Youjustgotbotted" /* kahootflood.weebly.com */){
            kick = true;
          }
          if(kick){
            const packet = createKickPacket(cid);
            socket.send(JSON.stringify(packet));
            killcount.innerHTML = +killcount.innerHTML + 1;
            let name = "";
            delete windw.cachedData[cid];
            windw.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.`;
          }
        }
        // Cache Manager Timer
        setInterval(function(){
          for(const i in windw.cachedUsernames){
            if(windw.cachedUsernames[i].time <= 0 && !windw.cachedUsernames[i].banned && !windw.confirmedPlayers.includes(windw.cachedUsernames[i].name)){
              windw.confirmedPlayers.push(windw.cachedUsernames[i].name);
              continue;
            }
            if(windw.cachedUsernames[i].time <= -20){
              windw.cachedUsernames.splice(i,1);
              continue;
            }
            windw.cachedUsernames[i].time--;
          }
        },1000);
        // 2 Factor Auth Timer
        setInterval(()=>{
          for(const i in windw.cachedData){
            windw.cachedData[i].tries = 0;
          }
        },10000);
        windw.sendHandler = function(data){
          data = JSON.parse(data)[0];
          if(data.data){
            if(!data.data.id){
              return;
            }
            switch (data.data.id) {
              case 2:
                // question start
                windw.specialData.startTime = Date.now();
                break;
              case 5:
                // restart
                windw.specialData.inLobby = true;
                windw.specialData.lobbyLoadTime = 0;
                break;
              case 9:
                // start
                windw.specialData.inLobby = false;
                if(windw.specialData.StartLockElem){
                  clearInterval(windw.specialData.StartLockInterval);
                  windw.specialData.StartLockElem.outerHTML = "";
                  windw.specialData.StartLockElem = null;
                }
                break;
            }
          }
        };
        let ExtraCheck2 = function(){};
        try{
          if(windw.localStorage.extraCheck2){
            ExtraCheck2 = new Function("return " + windw.localStorage.extraCheck2)();
          }
        }catch(e){/* Likely doesn't exist */}
        let oldamount = 0,
          locked = false;
        setInterval(()=>{
          oldamount = +killcount.innerHTML;
        },20e3);
        window.globalMessageListener = function(e,t){
          try{ExtraCheck2(e,t);}catch(e){console.error("[ANTIBOT] - Execution of PIN-CHECKER Failed: " + e);}
          windw.e = e;
          if(!windw.e.webSocket.oldSend){
            windw.e.webSocket.oldSend = windw.e.webSocket.send;
            windw.e.webSocket.send = function(data){
              windw.sendHandler(data);
              windw.e.webSocket.oldSend(data);
            };
          }
          try{
            const elem = document.querySelector("[data-functional-selector=\"game-pin\"]") || document.querySelector("[data-functional-selector=\"bottom-bar-game-pin\"]");
            pin = pin ? pin : Number(elem.innerText);
            if(Number(elem.innerText) !== pin && !isNaN(Number(elem.innerText))){
              pin = Number(elem.innerText);
            }
          }catch(err){/* Item doesn't exist */}
          // check DDOS
          if(!locked && pin){
            if(!!(+windw.specialData.config.ddos) && (+killcount.innerHTML - oldamount) > (+windw.specialData.config.ddos/3)){
              locked = true;
              const oldpin = pin,
                // LOCK THE GAME!
                // 2.12.0 - Repeats every 0.25 seconds until the game is actually locked.
                lockPacket = [{
                  channel: "/service/player",
                  clientId,
                  data: {
                    gameid: oldpin,
                    type: "lock"
                  },
                  ext: {},
                  id: ++messageId
                }];
              e.webSocket.send(JSON.stringify(lockPacket));
              windw.specialData.lockInterval = setInterval(()=>{
                lockPacket.id = ++messageId;
                e.webSocket.send(JSON.stringify(lockPacket));
              },250);
              console.log("[ANTIBOT] - Detected bot spam. Locking game for 1 minute.");
              if(windw.specialData.config.counters){
                const ddoscount = document.createElement("div");
                let int = 60;
                ddoscount.innerHTML = `<span class="antibot-count-num">60</span>
              <span class="antibot-count-desc">Until Unlock</span>`;
                counters.append(ddoscount);
                const countTimer = setInterval(()=>{
                  ddoscount.querySelector(".antibot-count-num").innerHTML = --int;
                  if(int <= 0){
                    clearInterval(countTimer);
                    ddoscount.outerHTML = "";
                  }
                },1e3);
              }
              setTimeout(()=>{
                locked = false;
                clearInterval(windw.specialData.lockInterval);
                // UNLOCK GAME
                console.log("[ANTIBOT] - Unlocking game.");
                e.webSocket.send(JSON.stringify([{
                  channel: "/service/player",
                  clientId,
                  data: {
                    gameid: oldpin,
                    type: "unlock"
                  },
                  ext: {},
                  id: ++messageId
                }]));
              },60e3);
            }
          }
          /*console.log(e); from testing: e[.webSocket] is the websocket*/
          const 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;
          }
          /*if the message is a player join message*/
          if(data.data && data.data.type === "joined"){
            console.warn("[ANTIBOT] - determining evil...");
            determineEvil(data.data,e.webSocket);
            specialBotDetector(data.data.type,data.data,e.webSocket);
            // Player was not banned.
            if(windw.specialData.inLobby && windw.specialData.config.start_lock !== 0 && window.globalFuncs && window.globalFuncs.gameOptions.automaticallyProgressGame){
              if(windw.specialData.lobbyLoadTime === 0){
                windw.specialData.lobbyLoadTime = Date.now();
                if(windw.specialData.config.counters){
                  const c = document.createElement("div");
                  c.innerHTML = `<span class="antibot-count-num">${Math.round((windw.specialData.config.start_lock - (Date.now() - windw.specialData.lobbyLoadTime)/1000))}</span>
                <span class="antibot-count-desc">Until Auto-Start</span>`;
                  const i = setInterval(()=>{
                    let t = Math.round((windw.specialData.config.start_lock - (Date.now() - windw.specialData.lobbyLoadTime)/1000));
                    if(t < 0){
                      t = "Please Wait...";
                    }
                    c.querySelector(".antibot-count-num").innerHTML = t;
                  },1e3);
                  counters.append(c);
                  windw.specialData.StartLockElem = c;
                  windw.specialData.StartLockInterval = i;
                }
              }
              if(Date.now() - windw.specialData.lobbyLoadTime > windw.specialData.config.start_lock * 1000){
              // max time passed, just start the darn thing!
                const {controllers} = window.globalFuncs;
                if(controllers.filter((controller)=>{
                  return !controller.isGhost && !controller.hasLeft;
                }).length === 0){
                // The only current player in the lobby.
                  windw.specialData.lobbyLoadTime = Date.now();
                }else{
                  window.globalFuncs.startQuiz();
                  if(windw.specialData.StartLockElem){
                    clearInterval(windw.specialData.StartLockInterval);
                    windw.specialData.StartLockElem.outerHTML = "";
                    windw.specialData.StartLockElem = null;
                  }
                }
              }
            }
          }else if(data.data && data.data.id === 45){
          // if player answers
            if(Date.now() - windw.specialData.startTime < 500 && windw.specialData.config.timeout){
              throw "[ANTIBOT] - Answer was too quick!";
            }
            // if player just recently joined (within 1 second)
            if(windw.cachedData[data.data.cid] && Date.now() - windw.cachedData[data.data.cid].loginTime < 1000){
              const packet = createKickPacket(data.data.cid);
              windw.e.webSocket.send(JSON.stringify(packet));
              killcount.innerHTML = +killcount.innerHTML + 1;
              delete windw.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){
            windw.cachedData[data.data.cid].tries++;
            if(windw.cachedData[data.data.cid].tries > 3){
              const kicker = createKickPacket(data.data.cid);
              e.webSocket.send(JSON.stringify(kicker));
              const name = windw.cachedUsernames.filter(o=>{return o.id == data.data.cid;}).length ? windw.cachedUsernames.filter(o=>{return o.id == data.data.cid;})[0].name : "bot";
              console.warn(`[ANTIBOT] - Bot ${name} banished. Seen spamming 2FA`);
              windw.cachedUsernames.forEach(o=>{
                if(o.id == windw.specialData.lastFakeUserID){
                  o.banned = true;
                  o.time = 10;
                  return;
                }
              });
              delete windw.cachedData[data.data.cid];
              killcount.innerHTML = +killcount.innerHTML + 1;
            }
          }else if(data.data && data.data.id === 18) {
            teamBotDetector(JSON.parse(data.data.content),data.data.cid,e.webSocket);
          }else if(data.data && data.data.status === "LOCKED"){
            clearInterval(windw.specialData.lockInterval);
          }
        };
        // remove loaded modules (allows turning off things to be a bit easier)
        delete localStorage.kahootThemeScript;
        delete localStorage.extraCheck;
        delete localStorage.extraCheck2;
      },
      mainScript = new XMLHttpRequest();
    mainScript.open("GET","https://play.kahoot.it/"+script2);
    mainScript.send();
    mainScript.onload = ()=>{
      let sc = mainScript.response;
      // Access the namerator option
      const nr = /=[a-z]\.namerator/gm,
        letter = sc.match(nr)[0].match(/[a-z](?=\.)/g)[0];
      sc = sc.replace(sc.match(nr)[0],`=(()=>{console.log(${letter}.namerator);windw.isUsingNamerator = ${letter}.namerator;return ${letter}.namerator})()`);
      // Access the currentQuestionTimer and change the question time
      const cqtr = /currentQuestionTimer:[a-z]\.payload\.questionTime/gm,
        letter2 = sc.match(cqtr)[0].match(/[a-z](?=\.payload)/g)[0];
      sc = sc.replace(sc.match(cqtr)[0],`currentQuestionTimer:${letter2}.payload.questionTime + (()=>{return (windw.specialData.config.additionalQuestionTime * 1000) || 0})()`);
      // Access the "NoStreakPoints", allowing it to be enabled
      const nsr = /[a-zA-Z]{2}\.NoStreakPoints/gm;
      sc = sc.replace(sc.match(nsr)[0],"windw.specialData.config.streakBonus || 2"); // yes = 1, no = 2
      // Access the StartQuiz function. Also gains direct access to the controllers!
      const sq = /=[a-zA-Z]\.startQuiz/gm,
        letter4 = sc.match(sq)[0].match(/[a-zA-Z](?=\.)/g)[0];
      sc = sc.replace(sc.match(sq)[0],`=(()=>{window.globalFuncs = e;return ${letter4}.startQuiz})()`);
      // Access the fetched quiz information. Allows the quiz to be modified when the quiz is fetched!
      const fqr = /ERROR",[A-Z][a-z]=function\([a-z]\){return Object\(\$[a-z]\.[a-z]\)\([A-Z][a-z],{response:[a-z]}\)}/gm,
        letter5 = sc.match(fqr)[0].match(/response:[a-z]/g)[0].split(":")[1],
        fqrt = sc.match(fqr)[0];
      sc = sc.replace(fqrt,`ERROR",${fqrt.split("ERROR\",")[1].split("response:")[0]}response:(()=>{windw.specialData.globalQuizData = e;if(!windw.specialData.config.counterCheats){return e;}e.questions.push({question:"[ANTIBOT] - This poll is for countering Kahoot cheating sites.",time:20000,type:"survey",isAntibotQuestion:true,choices:[{answer:"OK",correct:true}]});return ${letter5};})()})}`);
      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.parent.fireLoaded = window.fireLoaded = true;window.setupAntibot();</script></body>${changed[1]}`;
      console.log("[ANTIBOT] - loaded");
      document.open();
      document.write("<style>body{margin:0;}iframe{border:0;width:100%;height:100%;}</style><iframe src=\"about:blank\"></iframe>");
      document.close();
      window.stop();
      const doc = document.querySelector("iframe");
      doc.contentDocument.write(changed);
      document.title = doc.contentDocument.title;
    };
  };
};