Nitro Type Bot

A Nitrotype Bot with GUI & variance sliders, also Mostly a Troll Hack TW: GETS YOU BANNED Theres no way currently to make a script that works using tampermonkey.

  1. // ==UserScript==
  2. // @license MIT
  3. // @description A Nitrotype Bot with GUI & variance sliders, also Mostly a Troll Hack TW: GETS YOU BANNED Theres no way currently to make a script that works using tampermonkey.
  4. // @name Nitro Type Bot
  5. // @namespace https://www.youtube.com/@InternetTyper
  6. // @match https://www.nitrotype.com/*
  7. // @author Internet Typer
  8. // @run-at document-start
  9. // @grant none
  10. // @version 1.0.3
  11. // ==/UserScript==
  12.  
  13. (() => {
  14. 'use strict';
  15.  
  16. function isRacePage() {
  17. return location.pathname.startsWith('/race');
  18. }
  19.  
  20. const sockets = [];
  21. const nativeWebSocket = window.WebSocket;
  22. window.WebSocket = function (...args) {
  23. const socket = new nativeWebSocket(...args);
  24. sockets.push(socket);
  25. return socket;
  26. };
  27.  
  28. function sleep(s) {
  29. return new Promise(resolve => setTimeout(resolve, s * 1000));
  30. }
  31.  
  32. function loadNum(key, def) {
  33. const val = sessionStorage.getItem(key);
  34. return val !== null ? parseInt(val) : def;
  35. }
  36. function saveNum(key, val) {
  37. sessionStorage.setItem(key, val.toString());
  38. }
  39. function loadBool(key, def) {
  40. const val = sessionStorage.getItem(key);
  41. return val === null ? def : val === 'true';
  42. }
  43. function saveBool(key, val) {
  44. sessionStorage.setItem(key, val ? 'true' : 'false');
  45. }
  46. function loadStr(key, def) {
  47. const val = sessionStorage.getItem(key);
  48. return val !== null ? val : def;
  49. }
  50. function saveStr(key, val) {
  51. sessionStorage.setItem(key, val);
  52. }
  53.  
  54. const keys = {
  55. enabled: 'ntbot_enabled',
  56. baseWpm: 'ntbot_wpm',
  57. baseAcc: 'ntbot_acc',
  58. wpmVar: 'ntbot_wpm_var',
  59. accVar: 'ntbot_acc_var',
  60. color: 'ntbot_color'
  61. };
  62. const defaults = {
  63. enabled: true,
  64. baseWpm: 200,
  65. baseAcc: 100,
  66. wpmVar: 10,
  67. accVar: 5,
  68. color: '#0f0'
  69. };
  70.  
  71. let enabled = loadBool(keys.enabled, defaults.enabled);
  72. let baseWpm = loadNum(keys.baseWpm, defaults.baseWpm);
  73. let baseAcc = loadNum(keys.baseAcc, defaults.baseAcc);
  74. let wpmVar = loadNum(keys.wpmVar, defaults.wpmVar);
  75. let accVar = loadNum(keys.accVar, defaults.accVar);
  76. let color = loadStr(keys.color, defaults.color);
  77.  
  78. function createGUI() {
  79. if (document.getElementById('ntbot_gui')) return;
  80. const style = document.createElement('style');
  81. style.textContent = `
  82. #ntbot_gui {
  83. position: fixed;
  84. top: 10px;
  85. right: 10px;
  86. background: #111;
  87. color: white;
  88. padding: 10px 15px;
  89. border-radius: 8px;
  90. font-family: Arial, sans-serif;
  91. font-size: 14px;
  92. width: 280px;
  93. box-shadow: 0 0 15px ${color};
  94. z-index: 99999;
  95. user-select: none;
  96. }
  97. #ntbot_gui h2 {
  98. margin: 0 0 10px 0;
  99. font-size: 18px;
  100. cursor: pointer;
  101. }
  102. #ntbot_gui .section {
  103. margin-bottom: 12px;
  104. }
  105. #ntbot_gui label {
  106. display: block;
  107. margin-bottom: 4px;
  108. font-weight: bold;
  109. }
  110. #ntbot_gui input[type="number"], #ntbot_gui input[type="range"], #ntbot_gui input[type="color"] {
  111. width: 100%;
  112. font-size: 14px;
  113. padding: 4px;
  114. border-radius: 4px;
  115. border: none;
  116. background: white;
  117. color: black;
  118. }
  119. #ntbot_gui .small-text {
  120. font-size: 11px;
  121. color: #aaa;
  122. margin-top: 2px;
  123. }
  124. #ntbot_gui button.toggle-btn {
  125. background: ${color};
  126. border: none;
  127. padding: 6px 10px;
  128. border-radius: 4px;
  129. cursor: pointer;
  130. font-weight: bold;
  131. color: #000;
  132. width: 100%;
  133. margin-top: 8px;
  134. }
  135. #ntbot_gui .variance-value {
  136. text-align: right;
  137. font-size: 12px;
  138. color: ${color};
  139. margin-top: -16px;
  140. margin-bottom: 6px;
  141. user-select: none;
  142. }
  143. #ntbot_credits {
  144. font-size: 12px;
  145. text-align: center;
  146. margin-top: 10px;
  147. color: #ccc;
  148. }
  149. #ntbot_credits .highlight {
  150. font-size: 14px;
  151. color: ${color};
  152. font-weight: bold;
  153. }
  154. #ntbot_credits a {
  155. color: ${color};
  156. text-decoration: none;
  157. }
  158. `;
  159. document.head.appendChild(style);
  160.  
  161. const gui = document.createElement('div');
  162. gui.id = 'ntbot_gui';
  163. gui.innerHTML = `
  164. <h2 id="ntbot_toggle">Nitrotype Bot ▲</h2>
  165. <div class="collapse-content">
  166. <div class="section">
  167. <label for="ntbot_enabled">Enable Bot</label>
  168. <button id="ntbot_enabled" class="toggle-btn">${enabled ? 'ON' : 'OFF'}</button>
  169. </div>
  170. <div class="section">
  171. <label for="ntbot_wpm_input">Base WPM</label>
  172. <input type="number" id="ntbot_wpm_input" min="1" max="3600" value="${baseWpm}" />
  173. </div>
  174. <div class="variance-value" id="wpm_var_val">${wpmVar} WPM variance</div>
  175. <input type="range" id="ntbot_wpm_var" min="0" max="50" step="1" value="${wpmVar}" />
  176. <div class="section" style="margin-top:12px;">
  177. <label for="ntbot_acc_input">Base Accuracy (%)</label>
  178. <input type="number" id="ntbot_acc_input" min="1" max="100" value="${baseAcc}" />
  179. </div>
  180. <div class="variance-value" id="acc_var_val">${accVar} accuracy variance</div>
  181. <input type="range" id="ntbot_acc_var" min="0" max="20" step="1" value="${accVar}" />
  182. <div class="section">
  183. <label for="ntbot_color_picker">Highlight Color</label>
  184. <input type="color" id="ntbot_color_picker" value="${color}" />
  185. </div>
  186. <div id="ntbot_credits">
  187. Credit: RedHawk and adl212 and<br>
  188. <a class="highlight" href="https://www.youtube.com/@InternetTyper" target="_blank">@InternetTyper on YouTube</a>
  189. </div>
  190. </div>
  191. `;
  192.  
  193. document.body.appendChild(gui);
  194.  
  195. gui.querySelector('#ntbot_toggle').addEventListener('click', () => {
  196. gui.classList.toggle('collapsed');
  197. gui.querySelector('#ntbot_toggle').textContent = gui.classList.contains('collapsed') ? 'Nitrotype Bot ▼' : 'Nitrotype Bot ▲';
  198. });
  199.  
  200. const enableBtn = gui.querySelector('#ntbot_enabled');
  201. enableBtn.addEventListener('click', () => {
  202. enabled = !enabled;
  203. saveBool(keys.enabled, enabled);
  204. enableBtn.textContent = enabled ? 'ON' : 'OFF';
  205. enableBtn.style.background = enabled ? color : '#555';
  206. });
  207. enableBtn.style.background = enabled ? color : '#555';
  208.  
  209. gui.querySelector('#ntbot_wpm_input').addEventListener('change', e => {
  210. let val = parseInt(e.target.value);
  211. if (isNaN(val) || val < 1) val = 1;
  212. if (val > 3600) val = 3600;
  213. baseWpm = val;
  214. saveNum(keys.baseWpm, baseWpm);
  215. e.target.value = baseWpm;
  216. });
  217.  
  218. gui.querySelector('#ntbot_wpm_var').addEventListener('input', e => {
  219. wpmVar = parseInt(e.target.value);
  220. saveNum(keys.wpmVar, wpmVar);
  221. gui.querySelector('#wpm_var_val').textContent = `${wpmVar} WPM variance`;
  222. });
  223.  
  224. gui.querySelector('#ntbot_acc_input').addEventListener('change', e => {
  225. let val = parseInt(e.target.value);
  226. if (isNaN(val) || val < 1) val = 1;
  227. if (val > 100) val = 100;
  228. baseAcc = val;
  229. saveNum(keys.baseAcc, baseAcc);
  230. e.target.value = baseAcc;
  231. });
  232.  
  233. gui.querySelector('#ntbot_acc_var').addEventListener('input', e => {
  234. accVar = parseInt(e.target.value);
  235. saveNum(keys.accVar, accVar);
  236. gui.querySelector('#acc_var_val').textContent = `${accVar} accuracy variance`;
  237. });
  238.  
  239. gui.querySelector('#ntbot_color_picker').addEventListener('change', e => {
  240. color = e.target.value;
  241. saveStr(keys.color, color);
  242. location.reload();
  243. });
  244. }
  245.  
  246. async function main(ws, event) {
  247. if (!enabled || !isRacePage()) return;
  248. const message = event.data;
  249. const scan_for_text = msg => {
  250. try {
  251. const parsed = JSON.parse(msg.slice(1));
  252. if (parsed && parsed.payload && typeof parsed.payload.l === 'string') {
  253. return parsed.payload.l;
  254. }
  255. } catch (e) {}
  256. return null;
  257. };
  258.  
  259. const text = scan_for_text(message);
  260. if (typeof text !== 'string') return;
  261.  
  262. function randrange(min, max) {
  263. if (max <= min) return min;
  264. return Math.floor(Math.random() * (max - min + 1)) + min;
  265. }
  266.  
  267. const words = text.split(' ');
  268. const fullText = words.join(' ');
  269. const numChars = fullText.length;
  270.  
  271. let charDelays = [];
  272. for (let i = 0; i < numChars; i++) {
  273. const wpm = Math.max(1, Math.min(3600, baseWpm + randrange(-wpmVar, wpmVar)));
  274. const cpm = wpm * 5;
  275. const charDelay = 60 * 1000 / cpm;
  276. charDelays.push(charDelay);
  277. }
  278.  
  279. await sleep(4.3);
  280. ws.send('4{"stream":"race","msg":"update","payload":{"t":1,"f":0}}');
  281. let t = 2;
  282. let e = 1;
  283.  
  284. for (let i = 0; i < fullText.length; i++) {
  285. ws.send(`4{"stream":"race","msg":"update","payload":{"t":${t},"f":${e}}}`);
  286. await sleep(charDelays[i] / 1000);
  287. t++;
  288. }
  289. }
  290.  
  291. createGUI();
  292.  
  293. const hookInterval = setInterval(() => {
  294. if (sockets.length > 0) {
  295. const ws = sockets[0];
  296. if (!ws._ntbot_hooked) {
  297. ws._ntbot_hooked = true;
  298. ws.addEventListener('message', event => main(ws, event));
  299. clearInterval(hookInterval);
  300. }
  301. }
  302. }, 500);
  303. })();