Kahoot AntiBot

Remove all bots from a kahoot game.

当前为 2019-04-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Kahoot AntiBot
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.1.2
  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. window.stop(); //stop loading and save data from the page.
  13. window.url = window.location.href;
  14. window.kahootScript = null;
  15. window.kahootScriptSrc = "https://play.kahoot.it/js/player.min.js?v=" + window.kahoot.version;
  16. document.documentElement.innerHTML = "";
  17. window.x = new XMLHttpRequest();
  18. window.x.open("GET",kahootScriptSrc);
  19. window.x.send();
  20. window.x.onload = ()=>{ //load kahoot resources and edit code to allow access to the websockets
  21. var fix = window.x.response.replace("i.onMessage=function(e,t){","i.onMessage=function(e,t){window.globalMessageListener(e,t);"); //edit code to be able to read all messages and access the websocket
  22. //console.log(fix);
  23. window.kahootScript = document.createElement("script");
  24. window.kahootScript.innerHTML = fix;
  25. var x = new XMLHttpRequest();
  26. x.open("GET","https://play.kahoot.it/js/bootstrap.js");
  27. x.send();
  28. x.onload = ()=>{
  29. var patch = x.response.replace(",$script([\"js/player.min.js?v\"+kahoot.version],function(){angular.bootstrap(document,[\"app\"])});","");
  30. var pS = document.createElement("script");
  31. pS.innerHTML = patch;
  32. var x2 = new XMLHttpRequest();
  33. x2.open("GET",url);
  34. x2.send();
  35. x2.onload = ()=>{
  36. var fin = x2.response.replace("js/bootstrap.js","data:text/plain,null");
  37. document.open();
  38. document.write(fin);
  39. document.close();
  40. window.onload = ()=>{
  41. document.body.append(pS);
  42. document.body.append(window.kahootScript);
  43. angular.bootstrap(document,["app"]); /*global angular*/
  44. setupAntibot(); //win!
  45. };
  46. };
  47. };
  48. };
  49.  
  50. function setupAntibot(){
  51. const percent = 0.6;
  52. var waterMark = document.createElement("p");
  53. waterMark.innerText = "v2.1.2 @theusaf";
  54. waterMark.style = "position: fixed; bottom: 100px; right: 100px; font-size: 1em";
  55. document.body.append(waterMark);
  56. window.isUsingNamerator = false;
  57. window.cachedUsernames = [];
  58. window.confirmedPlayers = [];
  59. var messageId = 0;
  60. var clientId = null;
  61. var pin = null;
  62. function similarity(s1, s2) {
  63. if(!s2){
  64. return 0;
  65. }
  66. if(!isNaN(s2) && !isNaN(s1) && s1.length == s2.length){
  67. return 1;
  68. }
  69. if(window.isUsingNamerator){
  70. let caps = s2.length - s2.replace(/[A-Z]/g, '').length;
  71. if(caps !== 2){ //has less than 2 or more than 2 capitals
  72. return 1;
  73. }
  74. if (s2.substr(0,1).replace(/[A-Z]/g,'').length == 1){ //first char is not a capital
  75. return 1;
  76. }
  77. if(s2.substr(s2.length - 1,1).replace(/[A-Z]/g,'').length == 0){ //last char is a capital
  78. return 1;
  79. }
  80. if(s2.replace(/[a-z]/ig,'').length > 0){ //has non-letter chars
  81. return 1;
  82. }
  83. }
  84. if(!s1){
  85. return;
  86. }
  87. s1 = s1.toLowerCase();
  88. s2 = s2.toLowerCase();
  89. var longer = s1;
  90. var shorter = s2;
  91. if (s1.length < s2.length) {
  92. longer = s2;
  93. shorter = s1;
  94. }
  95. var longerLength = longer.length;
  96. if (longerLength == 0) {
  97. return 1.0;
  98. }
  99. return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);
  100. }
  101. function editDistance(s1, s2) {
  102. s1 = s1.toLowerCase();
  103. s2 = s2.toLowerCase();
  104.  
  105. var costs = new Array();
  106. for (var i = 0; i <= s1.length; i++) {
  107. var lastValue = i;
  108. for (var j = 0; j <= s2.length; j++) {
  109. if (i == 0){
  110. costs[j] = j;
  111. }
  112. else {
  113. if (j > 0) {
  114. var newValue = costs[j - 1];
  115. if (s1.charAt(i - 1) != s2.charAt(j - 1)){
  116. newValue = Math.min(Math.min(newValue, lastValue),costs[j]) + 1;
  117. }
  118. costs[j - 1] = lastValue;
  119. lastValue = newValue;
  120. }
  121. }
  122. }
  123. if (i > 0){
  124. costs[s2.length] = lastValue;
  125. }
  126. }
  127. return costs[s2.length];
  128. }
  129. function checkSettings(){
  130. var namer = document.querySelectorAll("[data-functional-selector=\"game-settings-namerator-toggle\"]")[0];
  131. window.isUsingNamerator = Array.from(namer.classList).includes("on");
  132. let observer = new MutationObserver(function(button){
  133. window.isUsingNamerator = Array.from(button[0].target.classList).includes("on");
  134. });
  135. let conf = {
  136. attributes: true
  137. };
  138. observer.observe(namer,conf);
  139. }
  140. function setup(){
  141. var launch_button = document.querySelectorAll("[data-functional-selector=\"launch-button\"]")[0];
  142. if(launch_button){
  143. console.warn("launch button found!");
  144. checkSettings();
  145. }else{
  146. setTimeout(setup,1000);
  147. }
  148. }
  149. setup();
  150. function createKickPacket(id){
  151. messageId++;
  152. return [{
  153. channel: "/service/player",
  154. clientId: clientId,
  155. id: String(Number(messageId)),
  156. data: {
  157. cid: String(id),
  158. content: JSON.stringify({
  159. kickCode: 1,
  160. quizType: "quiz"
  161. }),
  162. gameid: pin,
  163. host: "play.kahoot.it",
  164. id: 10,
  165. type: "message"
  166. }
  167. }];
  168. }
  169. function determineEvil(player,socket){
  170. if(window.cachedUsernames.length == 0){
  171. if(similarity(null,player.name) >= percent){
  172. var packet = createKickPacket(player.cid);
  173. socket.send(JSON.stringify(packet));
  174. console.warn(`Bot ${player.name} has been banished`);
  175. throw "Bot banned. Dont add";
  176. }
  177. window.cachedUsernames.push({name: player.name, id: player.cid, time: 10, banned: false});
  178. }else{
  179. var removed = false;
  180. for(var i in window.cachedUsernames){
  181. if(window.confirmedPlayers.includes(window.cachedUsernames[i].name)){
  182. continue;
  183. }
  184. if(similarity(window.cachedUsernames[i].name,player.name) >= percent){
  185. removed = true;
  186. var packet1 = createKickPacket(player.cid);
  187. socket.send(JSON.stringify(packet1));
  188. if(!window.cachedUsernames[i].banned){
  189. var packet2 = createKickPacket(window.cachedUsernames[i].id);
  190. window.cachedUsernames[i].banned = true;
  191. window.cachedUsernames[i].time = 10;
  192. socket.send(JSON.stringify(packet2));
  193. }
  194. console.warn(`Bots ${player.name} and ${window.cachedUsernames[i].name} have been banished`);
  195. throw "Bot banned. Dont add";
  196. }
  197. }
  198. if(!removed){
  199. window.cachedUsernames.push({name: player.name, id: player.cid, time: 10, banned: false});
  200. }
  201. }
  202. }
  203. var timer = setInterval(function(){
  204. for(let i in window.cachedUsernames){
  205. if(window.cachedUsernames[i].time <= 0 && !window.cachedUsernames[i].banned && !window.confirmedPlayers.includes(window.cachedUsernames[i].name)){
  206. window.confirmedPlayers.push(window.cachedUsernames[i].name);
  207. continue;
  208. }
  209. if(window.cachedUsernames[i].time <= -20){
  210. window.cachedUsernames.splice(i,1);
  211. continue;
  212. }
  213. window.cachedUsernames[i].time--;
  214. }
  215. },1000);
  216. window.globalMessageListener = function(e,t){
  217. //console.log(e); from testing: e is the websocket
  218. var data = JSON.parse(t.data)[0];
  219. //console.log(data);
  220. messageId = data.id ? data.id : messageId;
  221. //if the message is the first message, which contains important clientid data
  222. if(data.id == 1){
  223. clientId = data.clientId;
  224. }
  225. //if the message contains the pin
  226. if(data.id == 3){
  227. pin = Number(data.subscription.split("r/")[1]);
  228. }
  229. //if the message is a player join message
  230. if(data.data ? data.data.type == "joined" : false){
  231. console.warn("determining evil...");
  232. determineEvil(data.data,e);
  233. }
  234. }
  235. }