Kahoot AntiBot

Remove all bots from a kahoot game.

目前为 2019-11-19 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Kahoot AntiBot
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.5.7
  5. // @description Remove all bots from a kahoot game.
  6. // @author theusaf
  7. // @match *://play.kahoot.it/*
  8. // @grant none
  9. // @run-at document-start
  10. // ==/UserScript==
  11.  
  12. if(window.fireLoaded){
  13. throw "[ANTIBOT] - page is loaded";
  14. }
  15. //window.stop(); //stop loading and save data from the page.
  16. if(window.localStorage.extraCheck){
  17. console.log("[ANTIBOT] - Detected PIN Checker");
  18. }
  19. document.write("");
  20. window.url = window.location.href;
  21. window.page = new XMLHttpRequest();
  22. window.page.open("GET",window.url);
  23. window.page.send();
  24. window.page.onload = ()=>{
  25. let scriptURL = window.page.response.match(/<\/script><script\ .*?vendors.*?><\/script>/mg)[0].substr(9).split("src=\"")[1].split("\"")[0];
  26. let script2 = window.page.response.match(/<\/script><script src=\"\/v2\/assets\/js\/main.*?(?=\")/mg)[0].substr(22);
  27. let originalPage = window.page.response.replace(/<\/script><script\ .*?vendors.*?><\/script>/mg,"</script>");
  28. originalPage = originalPage.replace(/<\/script><script src=\"\/v2\/assets\/js\/main.*?(?=\")/mg,"</script><script src=\"data:text/javascript,");
  29. let script = new XMLHttpRequest();
  30. script.open("GET","https://play.kahoot.it/"+scriptURL);
  31. script.send();
  32. script.onload = ()=>{
  33. let patchedScript =script.response.replace(".onMessage=function(e,n){",".onMessage=function(e,n){window.globalMessageListener(t,n);");
  34. const code = /*window.setupAntibot = */ ()=>{
  35. const percent = 0.6;
  36. var waterMark = document.createElement("p");
  37. waterMark.innerText = "v2.5.7 @theusaf";
  38. waterMark.style = "position: fixed; bottom: 100px; right:100px; font-size: 1em";
  39. document.body.append(waterMark);
  40. window.isUsingNamerator = false;
  41. window.cachedUsernames = [];
  42. window.confirmedPlayers = [];
  43. window.cachedData = {};
  44. window.loggedPlayers = {};
  45. var messageId = 0;
  46. var clientId = null;
  47. var pin = null;
  48. function similarity(s1, s2) {
  49. if(!s2){
  50. return 0;
  51. }
  52. if(s1){
  53. if(!isNaN(s2) && !isNaN(s1) && s1.length == s2.length){
  54. return 1;
  55. }
  56. }
  57. if(window.isUsingNamerator){
  58. let caps = s2.length - s2.replace(/[A-Z]/g, '').length;
  59. if(caps !== 2){ /*has less than 2 or more than 2 capitals*/
  60. return -1;
  61. }
  62. if (s2.substr(0,1).replace(/[A-Z]/g,'').length === 1){/*first char is not a capital*/
  63. return -1;
  64. }
  65. if (s2.substr(1,2).replace(/[A-Z]/g,'').length != 2){/*next few char have capitals*/
  66. return -1;
  67. }
  68. if(s2.substr(s2.length -2,2).replace(/[A-Z]/g,'').length !== 2){ /*last few char have acapital*/
  69. return -1;
  70. }
  71. if(s2.replace(/[a-z]/ig,'').length > 0){ /*hasnon-letter chars*/
  72. return -1;
  73. }
  74. }
  75. if(!s1){
  76. return;
  77. }
  78. s1 = s1.toLowerCase();
  79. s2 = s2.toLowerCase();
  80. var longer = s1;
  81. var shorter = s2;
  82. if (s1.length < s2.length) {
  83. longer = s2;
  84. shorter = s1;
  85. }
  86. var longerLength = longer.length;
  87. if (longerLength == 0) {
  88. return 1.0;
  89. }
  90. return (longerLength - editDistance(longer, shorter)) /parseFloat(longerLength);
  91. }
  92. function editDistance(s1, s2) {
  93. s1 = s1.toLowerCase();
  94. s2 = s2.toLowerCase();
  95.  
  96. var costs = new Array();
  97. for (var i = 0; i <= s1.length; i++) {
  98. var lastValue = i;
  99. for (var j = 0; j <= s2.length; j++) {
  100. if (i == 0){
  101. costs[j] = j;
  102. }
  103. else {
  104. if (j > 0) {
  105. var newValue = costs[j - 1];
  106. if (s1.charAt(i - 1) != s2.charAt(j - 1)){
  107. newValue = Math.min(Math.min(newValue,lastValue),costs[j]) + 1;
  108. }
  109. costs[j - 1] = lastValue;
  110. lastValue = newValue;
  111. }
  112. }
  113. }
  114. if (i > 0){
  115. costs[s2.length] = lastValue;
  116. }
  117. }
  118. return costs[s2.length];
  119. }
  120. function setup(){
  121. var launch_button =document.querySelectorAll("[data-functional-selector=\"launch-button\"]")[0];
  122. if(launch_button){
  123. console.warn("[ANTIBOT] - launch button found!");
  124. }else{
  125. setTimeout(setup,1000);
  126. }
  127. }
  128. setup();
  129. function createKickPacket(id){
  130. messageId++;
  131. return [{
  132. channel: "/service/player",
  133. clientId: clientId,
  134. id: String(Number(messageId)),
  135. data: {
  136. cid: String(id),
  137. content: JSON.stringify({
  138. kickCode: 1,
  139. quizType: "quiz"
  140. }),
  141. gameid: pin,
  142. host: "play.kahoot.it",
  143. id: 10,
  144. type: "message"
  145. },
  146. ext: {}
  147. }];
  148. }
  149. function determineEvil(player,socket){
  150. // Disabled in V 2.5.5
  151. /*if(window.loggedPlayers[player.cid] === true){
  152. let packet = createKickPacket(player.cid);
  153. socket.send(JSON.stringify(packet));
  154. window.loggedPlayers[player.cid] = false;
  155. throw "[ANTIBOT] - Evil intentions discovered";
  156. }*/
  157. if(window.cachedUsernames.length == 0){
  158. if(similarity(null,player.name) == -1){
  159. var packet = createKickPacket(player.cid);
  160. socket.send(JSON.stringify(packet));
  161. console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`);
  162. throw "[ANTIBOT] - Bot banned. Dont add";
  163. }
  164. window.cachedUsernames.push({name: player.name, id:player.cid, time: 10, banned: false});
  165. window.loggedPlayers[player.cid] = true;
  166. }else{
  167. var removed = false;
  168. for(var i in window.cachedUsernames){
  169. if(window.confirmedPlayers.includes(window.cachedUsernames[i].name)){
  170. continue;
  171. }
  172. if(similarity(window.cachedUsernames[i].name,player.name) == -1){
  173. removed = true;
  174. var packet1 = createKickPacket(player.cid);
  175. socket.send(JSON.stringify(packet1));
  176. console.warn(`[ANTIBOT] - Bot ${player.name} has been banished`);
  177. throw "[ANTIBOT] - Bot banned. Dont add";
  178. }
  179. if(similarity(window.cachedUsernames[i].name,player.name) >= percent){
  180. removed = true;
  181. let packet1 = createKickPacket(player.cid);
  182. socket.send(JSON.stringify(packet1));
  183. if(!window.cachedUsernames[i].banned){
  184. var packet2 =createKickPacket(window.cachedUsernames[i].id);
  185. window.cachedUsernames[i].banned = true;
  186. window.cachedUsernames[i].time = 10;
  187. socket.send(JSON.stringify(packet2));
  188. }
  189. console.warn(`[ANTIBOT] - Bots ${player.name} and ${window.cachedUsernames[i].name} have been banished`);
  190. throw "[ANTIBOT] - Bot banned. Dont add";
  191. }
  192. }
  193. if(!removed){
  194. window.cachedUsernames.push({name: player.name,id: player.cid, time: 10, banned: false});
  195. window.loggedPlayers[player.cid] = true;
  196. }
  197. }
  198. }
  199. function specialBotDetector(type,data,socket){
  200. switch (type) {
  201. case 'joined':
  202. 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
  203. window.cachedData[data.cid] = {
  204. tries: 0
  205. };
  206. }else{
  207. /*if(window.loggedPlayers[data.cid] === false){
  208. break;
  209. }*/
  210. var packet = createKickPacket(data.cid);
  211. socket.send(JSON.stringify(packet));
  212. console.warn(`[ANTIBOT] - Bot ${data.name} has been banished, clearly a bot from kahootsmash or something`);
  213. throw "[ANTIBOT] - Bot banned. Dont add";
  214. }
  215. break;
  216. }
  217. }
  218. var timer = setInterval(function(){
  219. for(let i in window.cachedUsernames){
  220. if(window.cachedUsernames[i].time <= 0 && !window.cachedUsernames[i].banned && !window.confirmedPlayers.includes(window.cachedUsernames[i].name)){
  221. window.confirmedPlayers.push(window.cachedUsernames[i].name);
  222. continue;
  223. }
  224. if(window.cachedUsernames[i].time <= -20){
  225. window.cachedUsernames.splice(i,1);
  226. continue;
  227. }
  228. window.cachedUsernames[i].time--;
  229. }
  230. },1000);
  231. const TFATimer = setInterval(()=>{
  232. for(let i in window.cachedData){
  233. window.cachedData[i].tries = 0;
  234. }
  235. },10000);
  236. window.globalMessageListener = function(e,t){
  237. window.e = e;
  238. /*console.log(e); from testing: e[.webSocket] is the websocket*/
  239. var data = JSON.parse(t.data)[0];
  240. /*console.log(data);*/
  241. messageId = data.id ? data.id : messageId;
  242. /*if the message is the first message, which contains important clientid data*/
  243. if(data.id == 1){
  244. clientId = data.clientId;
  245. }
  246. try{
  247. pin = pin ? pin : Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML);
  248. if(Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML) != pin){
  249. pin = Number(document.querySelector("[data-functional-selector=\"game-pin\"]").innerHTML);
  250. }
  251. }catch(err){}
  252. /*if the message contains the pin*/
  253. /*if(data.id == 3){
  254. pin = data.data.gameid;
  255. }*/
  256. /*if the message is a player join message*/
  257. if(data.data ? data.data.type == "joined" : false){
  258. console.warn("[ANTIBOT] - determining evil...");
  259. determineEvil(data.data,e.webSocket);
  260. specialBotDetector(data.data.type,data.data,e.webSocket);
  261. }
  262. /*if the message is a player leave message*/
  263. if(data.data ? data.data.type == "left" : false){
  264. //Disabled in V2.5.5
  265. /*if(window.loggedPlayers[data.data.cid] === false){ //if the antibot detected the evildoer
  266. //As of V 2.5.2, this has been abandoned since it doesn't actually work :p
  267. window.loggedPlayers[data.data.cid] = true;
  268. throw "[ANTIBOT] - Preventing removal of real player.";
  269. }
  270. window.loggedPlayers[data.data.cid] = false;*/
  271. }
  272. if(data.data ? data.data.id == 50 : false){
  273. window.cachedData[data.data.cid].tries++;
  274. if(window.cachedData[data.data.cid].tries > 3){
  275. const kicker = createKickPacket(data.data.cid);
  276. e.webSocket.send(JSON.stringify(kicker));
  277. 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";
  278. console.warn(`[ANTIBOT] - Bot ${name} banished. Seen spamming 2FA`);
  279. }
  280. }
  281. };
  282. };
  283. let mainScript = new XMLHttpRequest();
  284. mainScript.open("GET","https://play.kahoot.it/"+script2);
  285. mainScript.send();
  286. mainScript.onload = ()=>{
  287. let sc = mainScript.response;
  288. sc = sc.replace("r.namerator","(()=>{console.log(r.namerator);window.isUsingNamerator = r.namerator;return r.namerator})()");
  289. let changed = originalPage.split("</body>");
  290. 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]}`;
  291. console.log("[ANTIBOT] - loaded");
  292. document.open();
  293. document.write(changed);
  294. document.close();
  295. //document.documentElement.innerHTML = changed;
  296. };
  297. };
  298. };