Kahoot AntiBot

Remove all bots from a kahoot game.

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

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