- // ==UserScript==
- // @name Kahoot AntiBot
- // @namespace http://tampermonkey.net/
- // @version 2.5.7
- // @description Remove all bots from a kahoot game.
- // @author theusaf
- // @match *://play.kahoot.it/*
- // @grant none
- // @run-at document-start
- // ==/UserScript==
-
- if(window.fireLoaded){
- throw "[ANTIBOT] - page is loaded";
- }
- //window.stop(); //stop loading and save data from the page.
- if(window.localStorage.extraCheck){
- console.log("[ANTIBOT] - Detected PIN Checker");
- }
- document.write("");
- window.url = window.location.href;
- window.page = new XMLHttpRequest();
- window.page.open("GET",window.url);
- window.page.send();
- window.page.onload = ()=>{
- let scriptURL = window.page.response.match(/<\/script><script\ .*?vendors.*?><\/script>/mg)[0].substr(9).split("src=\"")[1].split("\"")[0];
- let script2 = window.page.response.match(/<\/script><script src=\"\/v2\/assets\/js\/main.*?(?=\")/mg)[0].substr(22);
- let originalPage = window.page.response.replace(/<\/script><script\ .*?vendors.*?><\/script>/mg,"</script>");
- originalPage = originalPage.replace(/<\/script><script src=\"\/v2\/assets\/js\/main.*?(?=\")/mg,"</script><script src=\"data:text/javascript,");
- let script = new XMLHttpRequest();
- script.open("GET","https://play.kahoot.it/"+scriptURL);
- script.send();
- script.onload = ()=>{
- let patchedScript =script.response.replace(".onMessage=function(e,n){",".onMessage=function(e,n){window.globalMessageListener(t,n);");
- const code = /*window.setupAntibot = */ ()=>{
- const percent = 0.6;
- var waterMark = document.createElement("p");
- waterMark.innerText = "v2.5.7 @theusaf";
- waterMark.style = "position: fixed; bottom: 100px; right:100px; font-size: 1em";
- document.body.append(waterMark);
- window.isUsingNamerator = false;
- window.cachedUsernames = [];
- window.confirmedPlayers = [];
- window.cachedData = {};
- window.loggedPlayers = {};
- var messageId = 0;
- var clientId = null;
- var pin = null;
- function similarity(s1, s2) {
- if(!s2){
- return 0;
- }
- if(s1){
- if(!isNaN(s2) && !isNaN(s1) && s1.length == s2.length){
- return 1;
- }
- }
- if(window.isUsingNamerator){
- let caps = s2.length - s2.replace(/[A-Z]/g, '').length;
- if(caps !== 2){ /*has less than 2 or more than 2 capitals*/
- return -1;
- }
- if (s2.substr(0,1).replace(/[A-Z]/g,'').length === 1){/*first char is not a capital*/
- return -1;
- }
- if (s2.substr(1,2).replace(/[A-Z]/g,'').length != 2){/*next few char have capitals*/
- return -1;
- }
- if(s2.substr(s2.length -2,2).replace(/[A-Z]/g,'').length !== 2){ /*last few char have acapital*/
- return -1;
- }
- if(s2.replace(/[a-z]/ig,'').length > 0){ /*hasnon-letter chars*/
- return -1;
- }
- }
- if(!s1){
- return;
- }
- s1 = s1.toLowerCase();
- s2 = s2.toLowerCase();
- var longer = s1;
- var shorter = s2;
- if (s1.length < s2.length) {
- longer = s2;
- shorter = s1;
- }
- var longerLength = longer.length;
- if (longerLength == 0) {
- return 1.0;
- }
- return (longerLength - editDistance(longer, shorter)) /parseFloat(longerLength);
- }
- function editDistance(s1, s2) {
- s1 = s1.toLowerCase();
- s2 = s2.toLowerCase();
-
- var costs = new Array();
- for (var i = 0; i <= s1.length; i++) {
- var lastValue = i;
- for (var j = 0; j <= s2.length; j++) {
- if (i == 0){
- costs[j] = j;
- }
- else {
- if (j > 0) {
- var newValue = costs[j - 1];
- if (s1.charAt(i - 1) != s2.charAt(j - 1)){
- newValue = Math.min(Math.min(newValue,lastValue),costs[j]) + 1;
- }
- costs[j - 1] = lastValue;
- lastValue = newValue;
- }
- }
- }
- if (i > 0){
- costs[s2.length] = lastValue;
- }
- }
- return costs[s2.length];
- }
- function setup(){
- var launch_button =document.querySelectorAll("[data-functional-selector=\"launch-button\"]")[0];
- if(launch_button){
- console.warn("[ANTIBOT] - launch button found!");
- }else{
- setTimeout(setup,1000);
- }
- }
- setup();
- function createKickPacket(id){
- messageId++;
- return [{
- channel: "/service/player",
- clientId: clientId,
- id: String(Number(messageId)),
- data: {
- cid: String(id),
- content: JSON.stringify({
- kickCode: 1,
- quizType: "quiz"
- }),
- gameid: pin,
- host: "play.kahoot.it",
- id: 10,
- type: "message"
- },
- ext: {}
- }];
- }
- function determineEvil(player,socket){
- // Disabled in V 2.5.5
- /*if(window.loggedPlayers[player.cid] === true){
- let packet = createKickPacket(player.cid);
- socket.send(JSON.stringify(packet));
- window.loggedPlayers[player.cid] = false;
- throw "[ANTIBOT] - Evil intentions discovered";
- }*/
- if(window.cachedUsernames.length == 0){
- if(similarity(null,player.name) == -1){
- var packet = createKickPacket(player.cid);
- socket.send(JSON.stringify(packet));
- console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`);
- throw "[ANTIBOT] - Bot banned. Dont add";
- }
- window.cachedUsernames.push({name: player.name, id:player.cid, time: 10, banned: false});
- window.loggedPlayers[player.cid] = true;
- }else{
- var removed = false;
- for(var i in window.cachedUsernames){
- if(window.confirmedPlayers.includes(window.cachedUsernames[i].name)){
- continue;
- }
- if(similarity(window.cachedUsernames[i].name,player.name) == -1){
- removed = true;
- var packet1 = createKickPacket(player.cid);
- socket.send(JSON.stringify(packet1));
- console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`);
- throw "[ANTIBOT] - Bot banned. Dont add";
- }
- if(similarity(window.cachedUsernames[i].name,player.name) >= percent){
- removed = true;
- let packet1 = createKickPacket(player.cid);
- socket.send(JSON.stringify(packet1));
- if(!window.cachedUsernames[i].banned){
- var packet2 =createKickPacket(window.cachedUsernames[i].id);
- window.cachedUsernames[i].banned = true;
- window.cachedUsernames[i].time = 10;
- socket.send(JSON.stringify(packet2));
- }
- console.warn(`[ANTIBOT] - Bots ${player.name} and ${window.cachedUsernames[i].name} have been banished`);
- throw "[ANTIBOT] - Bot banned. Dont add";
- }
- }
- if(!removed){
- window.cachedUsernames.push({name: player.name,id: player.cid, time: 10, banned: false});
- window.loggedPlayers[player.cid] = true;
- }
- }
- }
- function specialBotDetector(type,data,socket){
- switch (type) {
- case 'joined':
- if(!window.cachedData[data.cid] && !isNaN(data.cid) && Object.keys(data).length <= 5){ //if the id has not been cached yet or is an invalid id, and they are not a bot :p
- window.cachedData[data.cid] = {
- tries: 0
- };
- }else{
- /*if(window.loggedPlayers[data.cid] === false){
- break;
- }*/
- var packet = createKickPacket(data.cid);
- socket.send(JSON.stringify(packet));
- console.warn(`[ANTIBOT] - Bot ${data.name} has been banished, clearly a bot from kahootsmash or something`);
- throw "[ANTIBOT] - Bot banned. Dont add";
- }
- break;
- }
- }
- var timer = setInterval(function(){
- for(let i in window.cachedUsernames){
- if(window.cachedUsernames[i].time <= 0 && !window.cachedUsernames[i].banned && !window.confirmedPlayers.includes(window.cachedUsernames[i].name)){
- window.confirmedPlayers.push(window.cachedUsernames[i].name);
- continue;
- }
- if(window.cachedUsernames[i].time <= -20){
- window.cachedUsernames.splice(i,1);
- continue;
- }
- window.cachedUsernames[i].time--;
- }
- },1000);
- const TFATimer = setInterval(()=>{
- for(let i in window.cachedData){
- window.cachedData[i].tries = 0;
- }
- },10000);
- window.globalMessageListener = function(e,t){
- window.e = e;
- /*console.log(e); from testing: e[.webSocket] is the websocket*/
- var data = JSON.parse(t.data)[0];
- /*console.log(data);*/
- messageId = data.id ? data.id : messageId;
- /*if the message is the first message, which contains important clientid data*/
- if(data.id == 1){
- clientId = data.clientId;
- }
- try{
- pin = pin ? pin : Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML);
- if(Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML) != pin){
- pin = Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML);
- }
- }catch(err){}
- /*if the message contains the pin*/
- /*if(data.id == 3){
- pin = data.data.gameid;
- }*/
- /*if the message is a player join message*/
- if(data.data ? data.data.type == "joined" : false){
- console.warn("[ANTIBOT] - determining evil...");
- determineEvil(data.data,e.webSocket);
- specialBotDetector(data.data.type,data.data,e.webSocket);
- }
- /*if the message is a player leave message*/
- if(data.data ? data.data.type == "left" : false){
- //Disabled in V2.5.5
- /*if(window.loggedPlayers[data.data.cid] === false){ //if the antibot detected the evildoer
- //As of V 2.5.2, this has been abandoned since it doesn't actually work :p
- window.loggedPlayers[data.data.cid] = true;
- throw "[ANTIBOT] - Preventing removal of real player.";
- }
- window.loggedPlayers[data.data.cid] = false;*/
- }
- if(data.data ? data.data.id == 50 : false){
- window.cachedData[data.data.cid].tries++;
- if(window.cachedData[data.data.cid].tries > 3){
- const kicker = createKickPacket(data.data.cid);
- e.webSocket.send(JSON.stringify(kicker));
- const name = window.cachedUsernames.filter(o=>{return o.id == data.data.cid}).length ? window.cachedUsernames.filter(o=>{return o.id == data.data.cid})[0].name : "bot";
- console.warn(`[ANTIBOT] - Bot ${name} banished. Seen spamming 2FA`);
- }
- }
- };
- };
- let mainScript = new XMLHttpRequest();
- mainScript.open("GET","https://play.kahoot.it/"+script2);
- mainScript.send();
- mainScript.onload = ()=>{
- let sc = mainScript.response;
- sc = sc.replace("r.namerator","(()=>{console.log(r.namerator);window.isUsingNamerator = r.namerator;return r.namerator})()");
- let changed = originalPage.split("</body>");
- changed = `${changed[0]}<script>${patchedScript}</script><script>${sc}</script><script>try{(${window.localStorage.extraCheck})();}catch(err){}window.setupAntibot = ${code.toString()};window.fireLoaded = true;window.setupAntibot();</script></body>${changed[1]}`;
- console.log("[ANTIBOT] - loaded");
- document.open();
- document.write(changed);
- document.close();
- //document.documentElement.innerHTML = changed;
- };
- };
- };