Kahoot AntiBot

Remove all bots from a kahoot game.

当前为 2021-01-11 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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;
    };
  };
};