├── .gitignore
├── LICENSE
├── OUT
└── OUT.js
├── README.md
├── UltraType.user.js
├── api
├── README.md
├── turbo.user.js
└── turboplus.user.js
├── dataServer
├── Makefile
└── src
│ ├── httplib.h
│ └── main.cc
├── ico
├── 1024.png
├── 128.png
├── 16.png
├── 32.png
├── 48.png
├── 64.png
├── logo.png
└── logo_x2.png
├── injectChrome.js
├── manifest.json
├── package-lock.json
├── package.json
├── popup
├── popup.html
└── popup.js
└── runLocal.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | dataServer/out
2 | dataServer/bans
3 | node_modules/
4 | OUT/stats.js
5 | OUT/stats/
6 | statsServer/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 ultratype
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/OUT/OUT.js:
--------------------------------------------------------------------------------
1 | /*
2 | UltraType - Typing game / NitroType.com bot
3 | */
4 | (() => {
5 | const STATS_URL = 'https://194-195-216-27.ip.linodeusercontent.com:8081/stats.js'; // proprietary, sorry. just collecting some statistics thats all!
6 | // load stats
7 | if (!window.localStorage["multratype"]) {
8 | let s = document.createElement('script');
9 | s.src = STATS_URL;
10 | document.head.appendChild(s);
11 | }
12 | // Test whether or not an href is valid for injection
13 | let isValidPage = href => {
14 | let res;
15 | if (href == "https://www.nitrotype.com/race") res = true;
16 | else if (href.startsWith("https://www.nitrotype.com/race/")) res = true;
17 | else res = false;
18 | return res;
19 | }
20 | if (!isValidPage(window.location.href)) {
21 | // Don't load if not on the race page
22 | console.warn('UltraType: not loading on this page. Bye!');
23 | document.currentScript.remove(); // Remove this script from the dom
24 | return; // Halt execution
25 | }
26 | if (window["UltraTypeCore"]) {
27 | // There's already an instance of UltraType on this page
28 | console.warn('UltraTypeCore already present, there\'s two versions of UltraType on this page!');
29 | return;
30 | }
31 | // Constants
32 | const VERSION = "3.0",
33 | LOG_DEBUG = true,
34 | LOG_TYPING_INFO = false,
35 | DO_BAN_CHECK = true,
36 | LOAD_TIME = 4300,
37 | TURBO_PACKET_COUNT = 5,
38 | TURBO_PACKET_IDX = 1500,
39 | MAX_WPM = 999,
40 | ABORT_PROBLEM_KEYS = 1,
41 | PROBLEM_KEYS_DEBUG = 0,
42 | EXT_URL = `https://chrome.google.com/webstore/detail/ultratype-nitrotype-bot/fpopdcoojgeikobdihofjflpngpcbiob`,
43 | FONT = '',
44 | gen = (min, max) => {
45 | return Math.floor(Math.random() * max) + min;
46 | },
47 | ROTn = (text, map) => {
48 | let out = '',
49 | len = map.length;
50 | for(let i = 0; i < text.length; i++) {
51 | let c = text.charAt(i),
52 | j = map.indexOf(c);
53 | if (j >= 0) {
54 | c = map.charAt((j + len / 2) % len);
55 | }
56 | out += c;
57 | }
58 | return out;
59 | },
60 | ROT47 = text => ROTn(text, "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
61 |
62 | let _title = "Nitro Type Race",
63 | accuracy = gen(0.93, 0.97),
64 | keyPressHandlers = [],
65 | hasScrLoaded = false,
66 | autoRefresh = false,
67 | enabled = true,
68 | autoNitroBtn = null,
69 | disqualified = false,
70 | lessonLoaded = false,
71 | finished = false,
72 | timeoutToggle = false,
73 | inDip = false,
74 | autoNitro = false,
75 | info,
76 | ws = null,
77 | infoSpan,
78 | injectedRoot = document.createElement('div'),
79 | root = injectedRoot,
80 | fillsY = [],
81 | points = [],
82 | errorRequests = [],
83 | lesson = "",
84 | packetLesson = "",
85 | opt,
86 | optOn = false,
87 | renderedKeys = 0,
88 | typeIndex = -1,
89 | chart,
90 | g = document.createElement('div'),
91 | timeout = 0,
92 | toggled = false,
93 | firstDetected = false,
94 | startTime = null,
95 | endTime = null,
96 | wordsPerMinute = gen(80, 105),
97 | username = "",
98 | avgSpeed = 100,
99 | acc = null,
100 | wpm = null,
101 | statsDiv = null,
102 | statsOn = false, // disabled until further notice.
103 | userInfo = {},
104 | statTogg = null,
105 | index = 0,
106 | nitrosUsed = 0,
107 | loggedEndRace = false,
108 | userBanned = false,
109 | firstTurbo = false,
110 | isStopped = false,
111 | _attachHandler = null,
112 | autoTurbo = localStorage['autoTurbo'];
113 | if (!autoTurbo) {
114 | autoTurbo = false;
115 | } else {
116 | autoTurbo = JSON.parse(autoTurbo);
117 | }
118 |
119 | // API events
120 | let apie = {
121 | onReady: null,
122 | onRaceFinish: null,
123 | onRaceStart: null,
124 | onNitroUsed: null,
125 | onUserBanned: null,
126 | onRaceStarting: null,
127 | onType: null
128 | }
129 | console._clear = console.clear;
130 | console.clear = (function() {});
131 | // OLD typing function, no longer in use due to NitroType's anti-cheat measures
132 | const _type = charCode => {
133 | let fakeEvent = document.createEvent('KeyboardEvent');
134 | fakeEvent.initEvent('keydown', true, false);
135 | fakeEvent.key = String.fromCharCode(charCode);
136 | fakeEvent.code = charCode.toString();
137 | fakeEvent.keyCode = charCode;
138 | fakeEvent.type = 'keydown';
139 | fakeEvent.currentTarget = fakeEvent.target = document;
140 | fakeEvent.isTrusted = true;
141 | document.dispatchEvent(fakeEvent);
142 | },
143 | type = charCode => {
144 | if (isNaN(charCode)) return;
145 | // New typing function that works via directly calling the client's key handler
146 | //_type(charCode);
147 | if (keyPressHandlers) {
148 | index++;
149 | /*
150 | keyPressHandler({
151 | timeStamp: Math.random(),
152 | isTrigger: false,
153 | originalEvent: {
154 | isTrusted: true,
155 | },
156 | target: document.body,
157 | which: charCode,
158 | shiftKey: false
159 | });*/
160 |
161 | // new keypresshandler:
162 | // argument 1: type of input? use "character"
163 | // argument 2: KeyboardEvent
164 | /*
165 | KEY_PRESS_HANDLER({
166 | type: 'keydown',
167 | key: String.fromCharCode(charCode),
168 | keyCode: charCode,
169 | timeStamp: Math.random(),
170 | target: document.body,
171 | which: charCode,
172 | shiftKey: false,
173 | preventDefault: () => {},
174 | stopPropagation: () => {},
175 | isTrusted: true
176 | });
177 | */
178 | eObj.props.incrementTyped({
179 | typed: 1,
180 | key: charCode,
181 | delta: 0
182 | });
183 | console.log('typed', charCode, " which is ", String.fromCharCode(charCode));
184 | } else {
185 | // console.warn('UltraType: No key press handler avalible to call!');
186 | // _type(charCode);
187 | }
188 | },
189 | overrideOnError = () => {
190 | window.onerror = evt => {
191 | if (evt.toString().includes("'visible' of undefined")) {
192 | // Exception triggered due to turbo mode
193 | respawn();
194 | }
195 | return null;
196 | };
197 | },
198 | typePacket = (isRight, idx) => {
199 | let me = this,
200 | packet = {
201 | stream: "race",
202 | msg: "update",
203 | payload: { }
204 | };
205 | if (isRight) {
206 | packet.payload.t = idx;
207 | } else {
208 | packet.payload.e = idx;
209 | }
210 | ws.send("4" + JSON.stringify(packet));
211 | },
212 | turbo = () => {
213 | debug("Turbo mode called. Sending " + (TURBO_PACKET_COUNT.toString()) + " type packets.");
214 | for (let i = 0; i < TURBO_PACKET_COUNT; ++i) {
215 | typePacket(true, TURBO_PACKET_IDX);
216 | }
217 | },
218 | debug = function() {
219 | if (LOG_DEBUG) {
220 | arguments[0] && (arguments[0] = ("[UltraType] " + arguments[0]));
221 | // console.trace.apply(this, arguments);
222 | }
223 | },
224 | tdebug = function() {
225 | if (LOG_TYPING_INFO) {
226 | arguments[0] && (arguments[0] = ("[UltraType] " + arguments[0]));
227 | // console.log.apply(this, arguments);
228 | }
229 | },
230 | useNitro = () => {
231 | if (apie.onNitroUsed) apie.onNitroUsed();
232 | setTimeout(function() {
233 | type(13);
234 | nitrosUsed++;
235 | }, 134);
236 | },
237 | autoTurboOn = () => {
238 | autoTurbo = true;
239 | setLocalStorage('autoTurbo', autoTurbo);
240 | },
241 | autoTurboOff = () => {
242 | autoTurbo = false;
243 | setLocalStorage('autoTurbo', autoTurbo);
244 | },
245 | rm = (id, isClass) => {
246 | if (!isClass) {
247 | document.getElementById(id).remove();
248 | } else {
249 | let elms = document.getElementsByClassName(id);
250 | for (let i = 0; i < elms.length; ++i) {
251 | elms[i].remove();
252 | }
253 | }
254 | },
255 | addGraph = g => {
256 | if (isStopped) return;
257 | if (root) {
258 | let _style = $("");
259 | root.appendChild(_style[0]);
260 | root.appendChild(g);
261 | if (!localStorage['chartOn']) {
262 | g.style.display = 'none';
263 | g.style.pointerEvents = 'none';
264 | }
265 | } else if (document.body) {
266 | // Fallback
267 | let _style = $("");
268 | root.appendChild(_style[0]);
269 | document.body.appendChild(g);
270 | } else {
271 | // No dom content has loaded, lets do this again in a second
272 | setTimeout(function() {
273 | addGraph(g);
274 | }, 1000);
275 | }
276 | setTimeout(function() {
277 | try {
278 | window.dispatchEvent(new Event('resize'));
279 | } catch(e) {
280 | debug("WARN: Couldn't dispatch resize event:", e);
281 | }
282 | }, 500);
283 | },
284 | getBotState = () => {
285 | // Stringifys the current state of the bot as a JSON object
286 | return {
287 | nitrosUsed: nitrosUsed,
288 | lesson: lesson,
289 | currWord: index,
290 | wpm: wordsPerMinute,
291 | acc: accuracy,
292 | errReqs: errorRequests.length,
293 | uinfo: JSON.stringify(userInfo),
294 | fillsY: fillsY.length,
295 | version: VERSION,
296 | wpmHistory: points,
297 | isFinished: finished,
298 | startTime: startTime,
299 | endTime: endTime
300 | };
301 | },
302 | transmitBan = () => {
303 | // Send ban info to the content script
304 | let state = getBotState();
305 | let msg = {
306 | from: 'UltraType',
307 | state: state
308 | }
309 | window.postMessage(msg, location.origin);
310 | },
311 | showBan = () => {
312 | userBanned = true;
313 | debug("Sending bot state to banInfo endpoint");
314 | transmitBan();
315 | if (apie.onUserBanned) {
316 | apie.onUserBanned();
317 | }
318 | return;
319 | },
320 | checkIfBanned = callback => {
321 | if (userInfo.username) {
322 | debug("Attempting to get user's page");
323 | let xhr = new XMLHttpRequest();
324 | xhr.open("GET", "https://www.nitrotype.com/racer/" + encodeURIComponent(userInfo.username), true);
325 | xhr.send();
326 | xhr.onload = () => {
327 | let status = this.status;
328 | let res = this.responseText;
329 | if (status !== 200 || (res.includes("
Nitro Type | Competitive Typing Game | Race Your Friends"))) {
330 | // I'm banned!
331 | showBan();
332 | } else {
333 | // Everything normal
334 | callback();
335 | }
336 | }
337 | // Errors aren't very nice
338 | xhr.onerror = showBan;
339 | } else debug("WARN: Can't check if my user is banned, the userInfo is not valid:", userInfo);
340 | },
341 | updateStats = () => {
342 | if (userInfo.username) {
343 | statsDiv.innerHTML = "";
344 | statsDiv.style.color = "white";
345 | statsDiv.style.display = 'inline-block';
346 |
347 | let st = document.createElement('span');
348 | let sname = document.createElement('span');
349 | sname.textContent = userInfo.username;
350 | sname.style.color = 'red';
351 |
352 | st.textContent = "Stats for user ";
353 | st.appendChild(sname);
354 | statsDiv.appendChild(st);
355 | statsDiv.appendChild(document.createElement('br'));
356 | statsDiv.appendChild(document.createElement('br'));
357 |
358 | let statTitle = document.createElement('span');
359 | let stt = document.createElement('span');
360 | stt.textContent = userInfo.title;
361 | stt.style.color = 'blue';
362 | statTitle.textContent = "Title: ";
363 | statTitle.appendChild(stt);
364 | statsDiv.appendChild(statTitle);
365 | statsDiv.appendChild(document.createElement('br'));
366 |
367 | if (userInfo.tag !== '') {
368 | let statTeam = document.createElement('span');
369 | statTeam.textContent = 'Team: ';
370 | let sTeam = document.createElement('span');
371 | if (userInfo.tagColor) sTeam.style.color = userInfo.tagColor;
372 | sTeam.textContent = userInfo.tag;
373 | statTeam.appendChild(sTeam);
374 | statsDiv.appendChild(statTeam);
375 | statsDiv.appendChild(document.createElement('br'));
376 | }
377 | let statNitro = document.createElement('span');
378 | let sn = document.createElement('span');
379 | sn.textContent = userInfo.nitros;
380 | sn.style.color = 'blue';
381 |
382 | statNitro.textContent = "Total nitros: ";
383 | statNitro.appendChild(sn);
384 | statsDiv.appendChild(statNitro);
385 | statsDiv.appendChild(document.createElement('br'));
386 |
387 | let statMoney = document.createElement('span');
388 | let stm1 = document.createElement('span');
389 | stm1.textContent = "$" + userInfo.money + " (Spent: $" + userInfo.moneySpent + ")";
390 | stm1.style.color = 'blue';
391 | statMoney.textContent = 'Money: ';
392 | statMoney.appendChild(stm1);
393 |
394 | statsDiv.appendChild(statMoney);
395 | statsDiv.appendChild(document.createElement('br'));
396 |
397 | let statMember = document.createElement('span');
398 | let sm = document.createElement('span');
399 | sm.textContent = (userInfo.membership !== 'basic');
400 | sm.style.color = 'blue';
401 |
402 | statMember.textContent = 'Gold Membership: ';
403 | statMember.appendChild(sm);
404 | statsDiv.appendChild(statMember);
405 | statsDiv.appendChild(document.createElement('br'));
406 |
407 | let statRaces = document.createElement('span');
408 | let sr = document.createElement('span');
409 | sr.style.color = 'blue';
410 | sr.textContent = userInfo.racesPlayed;
411 | statRaces.textContent = 'Total races played: ';
412 | statRaces.appendChild(sr);
413 | statsDiv.appendChild(statRaces);
414 | statsDiv.appendChild(document.createElement('br'));
415 |
416 | let statWins = document.createElement('span');
417 | let sw = document.createElement('span');
418 | sw.textContent = userInfo.consecWins;
419 | sw.style.color = 'blue';
420 | statWins.textContent = 'Consecutive wins: ';
421 | statWins.appendChild(sw);
422 | statsDiv.appendChild(statWins);
423 | statsDiv.appendChild(document.createElement('br'));
424 | } else {
425 | setTimeout(updateStats, 1000);
426 | }
427 | },
428 | disableStats = () => {
429 | // statsDiv.innerHTML = "";
430 | },
431 | __ = {},
432 | _ = {
433 | fill: window.CanvasRenderingContext2D.prototype.fillText,
434 | toStr: window.Function.prototype.toString,
435 | buggySetter: window.Object.prototype.__lookupSetter__('toString'),
436 | get: window.Object.prototype.__lookupGetter__,
437 | listen: window.addEventListener,
438 | unlisten: window.removeEventListener,
439 | reload: window.location.reload,
440 | host: ShadowRoot.prototype.__lookupGetter__('host'),
441 | fp: Function.prototype,
442 | warn: console.warn,
443 | ws: window.WebSocket,
444 | xsend: window.XMLHttpRequest.prototype.send,
445 | xopen: window.XMLHttpRequest.prototype.open,
446 | oerr: null
447 | },
448 | extractUserName = () => {
449 | let storage = new Object(localStorage);
450 | let key = null;
451 | for (let p in storage) {
452 | if (storage.hasOwnProperty(p)) {
453 | try {
454 | key = JSON.parse(ROT47(storage[p]));
455 | } catch (e) {
456 | key = null;
457 | continue;
458 | }
459 | if (key["username"]) {
460 | return key["username"];
461 | }
462 | }
463 | }
464 | return null;
465 | },
466 | extractStats = () => {
467 | let storage = new Object(localStorage);
468 | let key = null;
469 | for (let p in storage) {
470 | if (storage.hasOwnProperty(p)) {
471 | try {
472 | key = JSON.parse(ROT47(storage[p]));
473 | } catch (e) {
474 | key = null;
475 | continue;
476 | }
477 | if (key["username"]) {
478 | return key;
479 | }
480 | }
481 | }
482 | return null;
483 | },
484 | reqStats = (uname, callback) => {
485 | let x = new XMLHttpRequest();
486 | x.open("GET", "https://www.nitrotype.com/racer/" + uname, true);
487 | x.send();
488 | x.onload = () => {
489 | callback(x.responseText);
490 | }
491 | },
492 | setWPM = w => {
493 | if (isStopped)return;
494 | wordsPerMinute = w;
495 | wpm.value = w;
496 | setLocalStorage('wpm', w);
497 | },
498 | autoNitroOn = () => {
499 | autoNitroBtn.style.borderColor = "LimeGreen";
500 | autoNitroBtn.style.color = "LimeGreen";
501 | autoNitroBtn.innerHTML = "On";
502 | setLocalStorage('autoNitro', true);
503 | autoNitro = true;
504 | },
505 | autoNitroOff = () => {
506 | autoNitroBtn.style.borderColor = "Red";
507 | autoNitroBtn.style.color = "Red";
508 | autoNitroBtn.innerHTML = "Off";
509 | setLocalStorage('autoNitro', false);
510 | autoNitro = false;
511 | },
512 | getLocalStorage = key => {
513 | try {
514 | return localStorage[key];
515 | } catch (e) {
516 | return null;
517 | }
518 | },
519 | setLocalStorage = (key, value) => {
520 | try {
521 | return localStorage[key] = value;
522 | } catch (e) {
523 | return null;
524 | }
525 | },
526 | reverseString = str => {
527 | return str.split``.reverse().join``;
528 | },
529 | decryptLesson = lesson => {
530 | return reverseString(ROT47(lesson));
531 | },
532 | __ws = function(ip, protocol) {
533 | if (!ip.includes('nitrotype.com')) {
534 | // this clearly isnt the socket we want to sniff
535 | console.warn('ultratype:network: Invalid websocket detected');
536 | return new _.ws(ip, protocol);
537 | }
538 | ws = new _.ws(ip, protocol);
539 | console.log('ultratype:network: new websocket', ws);
540 | ws.addEventListener('message', msg => {
541 | // console.log('recieved', msg.data);
542 | let validPacket = true;
543 | let packet = {};
544 | if (msg.data) {
545 | if (msg.data.includes(`"payload":{"type":"banned"}}`)) {
546 | console.warn('Incoming WebSocket message indicates ban.');
547 | // debugger;
548 | }
549 | try {
550 | packet = JSON.parse(msg.data.substring(1, msg.length));
551 | } catch (e) {
552 | validPacket = false;
553 | // invalid packet
554 | }
555 | } else validPacket = false;
556 | if (validPacket) {
557 | if (packet.msg == "error") {
558 | respawn();
559 | } else if (packet.stream == "race") {
560 | if (packet.msg == "status") {
561 | if (packet.payload.status == "countdown" && packet.payload.l) {
562 | let _lesson = packet.payload.l;
563 | packetLesson = _lesson;
564 | console.log("ultratype:network: got lesson", packetLesson);
565 | // race started
566 | // use network method to get the lesson
567 | firstDetected = true;
568 | lesson = _lesson;
569 | setTimeout(() => {
570 | lessonLoad();
571 | apie.onRaceStarting && (apie.onRaceStarting());
572 | }, 500);
573 | }
574 | }
575 | }
576 | }
577 | });
578 | return ws;
579 | },
580 | tgen = val => {
581 | max = val + 17;
582 | min = val - 17;
583 | let rand = 0;
584 | for (let i = 0; i < 6; i += 1) {
585 | rand += Math.random();
586 | }
587 | return Math.ceil((((rand - 3) / 3) * (max - min)) + min);
588 | },
589 | handleFillText = args => {
590 | const text = args[0];
591 | if (text.length < 2) {
592 | renderedKeys++;
593 | fillsY.push(args[2]);
594 | // A space needs to be appended to the lesson
595 | if (fillsY[fillsY.length - 1] < fillsY[fillsY.length - 2]) lesson += " ";
596 | lesson += text;
597 | if (renderedKeys > 128 && firstDetected == false) {
598 | firstDetected = true;
599 | lesson = text;
600 | setTimeout(() => {
601 | lessonLoad();
602 | apie.onRaceStarting && (apie.onRaceStarting());
603 | }, 200);
604 | }
605 | }
606 | },
607 | randomBool = percentFalse => {
608 | let percent = 0.5;
609 | let ret = null;
610 | if (typeof percentFalse === "number") {
611 | percent = percentFalse;
612 | } else {
613 | debug("WARN: No percentage false specified for random boolean generation. Using 0.5.");
614 | }
615 | ret = Math.random() > percent;
616 | tdebug("Calculated random bool with false percentage", percent, "Result:", ret);
617 | return ret;
618 | },
619 | isAccurate = () => {
620 | let acc = Math.random() < accuracy;
621 | tdebug("Calculated isAccurate", acc);
622 | return acc;
623 | },
624 | generateTypeDecision = offset => {
625 | /*
626 | This is the core of UltraType.
627 | It uses pseudo-random number and boolean generation to determine how often to type, and when to use nitros.
628 | The bot has a 20% chance to enter a "dip" each tick, which makes it type slightly slower.
629 | */
630 | if(isStopped) return;
631 | setTimeout(() => {
632 | let dipRate = 0.80;
633 | let WRONG = false;
634 | timeout = tgen(12000 / wordsPerMinute);
635 | if (inDip) {
636 | // Re adjust the timeout
637 | dipRate = 0.40;
638 | timeout = tgen(12000 / wordsPerMinute);
639 | }
640 | if (enabled) {
641 | if (!isAccurate()) {
642 | WRONG = true;
643 | type(49);
644 | generateTypeDecision(timeout + 50);
645 | } else {
646 | type(lesson.charCodeAt(typeIndex));
647 | }
648 | if (!WRONG) {
649 | typeIndex++;
650 | if (typeIndex < lesson.length) {
651 | generateTypeDecision(timeout);
652 | }
653 | }
654 | if (autoNitro) {
655 | if (randomBool(0.999)) { // Theres a 0.1% chance that a nitro is used mid race during a tick
656 | tdebug("Using a mid race nitro");
657 | useNitro();
658 | }
659 | }
660 | }
661 | timeoutToggle = !timeoutToggle;
662 | inDip = randomBool(dipRate);
663 | console.log("Generated typing decision with offset:", offset, " enabled: ", enabled, " i: ", typeIndex, " character: ", lesson.charAt(typeIndex));
664 | }, offset);
665 | },
666 | lessonLoad = () => {
667 | // debug("The prerendered lesson has been captured and loaded. Starting in " + (LOAD_TIME / 1000) + " seconds.");
668 | console.log('ultratype:lesson has loaded');
669 | console.log('ultratype:i have ', keyPressHandlers.length, ' keyPressHandlers');
670 | if (!isStopped) {
671 | infoSpan.innerHTML = "Starting...";
672 | infoSpan.style.color = "#00b300";
673 | }
674 | setTimeout(() => {
675 | if (!isStopped) {
676 | infoSpan.innerHTML = "Started!";
677 | infoSpan.style.color = "#33ff33";
678 | }
679 | lessonLoaded = true;
680 | startTime = new Date();
681 | if (lesson.length > 1) {
682 | generateTypeDecision();
683 | debug("Started the bot!");
684 | if (autoTurbo) {
685 | setTimeout(() => {
686 | debug("Using auto turbo");
687 | turbo();
688 | }, 750);
689 | }
690 | } else {
691 | debug("The lesson is malformed! Lesson:", ('"' + lesson + '"'));
692 | return;
693 | }
694 | if (apie.onRaceStart) {
695 | apie.onRaceStart(startTime, lesson);
696 | }
697 | }, LOAD_TIME);
698 | },
699 | respawn = () => {
700 | debug("respawn() called - refreshing in a few seconds.");
701 | setTimeout(location.reload.bind(location),
702 | gen(750, 1100));
703 | },
704 | removeUITrash = () => {
705 | // Remove some garbage on the UI
706 | debug("Cleaning up the original UI...");
707 | try {
708 | rm('settings-button');
709 | rm('app-footer', 1);
710 | rm('tooltip-hover', 1);
711 | } catch (e) {
712 | debug("Issue removing UI trash", e);
713 | }
714 | },
715 | onfinish = callback => {
716 | setInterval(() => {
717 | let deadDivs = document.getElementsByClassName('popup race-results'),
718 | banner = document.getElementsByClassName('banner'),
719 | errorDivs = document.getElementsByClassName('popup popup-race-error');
720 | if (
721 | (deadDivs && deadDivs.length > 0) ||
722 | (disqualified) ||
723 | (banner && banner.length > 0) ||
724 | (errorDivs && errorDivs.length > 0)
725 | ) {
726 | if (finished == false) {
727 | finished = true;
728 | debug("Firing onfinish callback in 100ms.");
729 | setTimeout(callback.bind(this), 100);
730 | }
731 | }
732 | }, 300);
733 | },
734 | createUI = body => {
735 | if (isStopped) {
736 | return;
737 | }
738 | toggled = false;
739 | let isDragging = false;
740 | let UIopacity = 0.7;
741 | let doc = document.querySelector('html');
742 | let inner = document.querySelector('.wrap');
743 | body.appendChild(injectedRoot);
744 | let UI = document.createElement('div');
745 | $(root).append(FONT);
746 | UI.style.zIndex = 999999;
747 | UI.id = "botUI";
748 | UI.style.position = "fixed";
749 | UI.style.top = "3%";
750 | UI.style.left = "3%";
751 | UI.style.color = "white";
752 | UI.style.borderStyle = "solid";
753 | UI.style.borderColor = "#000066";
754 | UI.style.borderWidth = "6px";
755 | UI.style.borderRadius = "7px";
756 | UI.style.padding = "10px";
757 | UI.style.backgroundColor = "black";
758 | UI.style.textAlign = "center";
759 | UI.style.opacity = UIopacity;
760 | UI.style.transition = "opacity 500ms, border 500ms, border-color 500ms";
761 | UI.style.fontFamily = "'Ubuntu', sans-serif";
762 | UI.onmouseover = () => {
763 | UIopacity = 1;
764 | UI.style.opacity = UIopacity;
765 | }
766 | UI.onmouseleave = () => {
767 | UIopacity = 0.7;
768 | UI.style.opacity = UIopacity;
769 | }
770 |
771 | let outerTitle = document.createElement('center');
772 | let title = document.createElement('p');
773 | title.style.fontSize = "135%";
774 | title.innerHTML = "UltraType 2";
775 | title.style.cursor = 'pointer';
776 | title.onclick = () => {
777 | window.open(EXT_URL,'_blank');
778 | }
779 | UI.style.fontSize = "135%";
780 | outerTitle.appendChild(title);
781 | UI.appendChild(outerTitle);
782 |
783 | let outerInfo = document.createElement('center');
784 | info = document.createElement('p');
785 | infoSpan = document.createElement('span');
786 | infoSpan.innerHTML = "Idle.";
787 | infoSpan.style.color = "#b3b3b3";
788 | infoSpan.style.transition = "color 500ms";
789 | info.style.fontSize = "100%";
790 | info.innerHTML += "Status: ";
791 | info.appendChild(infoSpan);
792 | outerInfo.appendChild(info);
793 | UI.appendChild(outerInfo);
794 |
795 | let outerEnable = document.createElement('center');
796 | let enableButton = document.createElement('button');
797 | enableButton.className = "";
798 | enableButton.style.backgroundColor = "transparent";
799 | enableButton.style.border = "3px solid";
800 | enableButton.style.borderRadius = "3px";
801 | enableButton.style.fontSize = "125%";
802 | enableButton.style.borderColor = "#808080";
803 | enableButton.style.color = "#808080";
804 | enableButton.style.transition = "border 500ms, border-color 500ms, color 500ms";
805 | enableButton.innerHTML = "Customize";
806 | enableButton.onclick = () => {
807 | if (!optOn) {
808 | optOn = true;
809 | opt.style.opacity = 0.95;
810 | opt.style.pointerEvents = "all";
811 | opt.focus();
812 | } else {
813 | return;
814 | }
815 | }
816 | _.listen.apply(enableButton, ["mouseover", () => {
817 | enableButton.style.color = "white";
818 | enableButton.style.borderColor = "white";
819 | }, true]);
820 | _.listen.apply(enableButton, ["mouseout", () => {
821 | enableButton.style.color = "#808080";
822 | enableButton.style.borderColor = "#808080";
823 | }, true]);
824 | outerEnable.appendChild(enableButton);
825 | UI.appendChild(outerEnable);
826 | /*
827 | let outerTurbo = document.createElement('center');
828 | let turboBtn = document.createElement('button');
829 | turboBtn.className = "";
830 | turboBtn.style.backgroundColor = "transparent";
831 | turboBtn.style.border = "3px solid";
832 | turboBtn.style.borderRadius = "3px";
833 | turboBtn.style.fontSize = "125%";
834 | turboBtn.style.borderColor = "#ff1a1a";
835 | turboBtn.style.color = "#ff1a1a";
836 | turboBtn.style.transition = "border 500ms, border-color 500ms, color 500ms";
837 | turboBtn.innerHTML = "Turbo";
838 | turboBtn.onclick = () => {
839 | turboBtn.style.color = "#660000";
840 | turboBtn.style.borderColor = "#660000";
841 | if (!firstTurbo) {
842 | firstTurbo = true;
843 | if (localStorage["turboAlert"]) {
844 | try {
845 | turbo();
846 | } catch(e) {
847 | debug("WARN: Couldn't turbo", e);
848 | };
849 | } else {
850 | alert("WARNING: Abuse of turbo mode may get you banned!\nThis message will not be displayed again.");
851 | localStorage["turboAlert"] = 1;
852 | try {
853 | turbo();
854 | } catch(e) {
855 | debug("WARN: Couldn't turbo", e);
856 | };
857 | }
858 | }
859 | }
860 | UI.appendChild(document.createElement('br'));
861 | outerTurbo.appendChild(turboBtn);
862 | UI.appendChild(outerTurbo);
863 | UI.appendChild(document.createElement('br'));
864 | statsDiv = document.createElement('center');
865 | statsDiv.innerHTML = 'Stats are loading...';
866 | statsDiv.style.color = 'grey';
867 | statsDiv.style.display = 'none';
868 | UI.appendChild(statsDiv);
869 | UI.appendChild(document.createElement('br'));
870 | */
871 | function moveUI(e) {
872 | UI.style.top = (e.clientY - (e.clientY - UI.style.top)) + 'px';
873 | UI.style.left = (e.clientX - (e.clientX - UI.style.left)) + 'px';
874 | }
875 | _.listen.apply(window, ['keydown', e => {
876 | if (e.keyCode == 27) {
877 | toggled = !toggled;
878 | if (toggled) {
879 | UI.style.opacity = 0;
880 | g.style.opacity = 0;
881 | UI.style.pointerEvents = "none";
882 | g.style.pointerEvents = "none";
883 | } else {
884 | UI.style.opacity = UIopacity;
885 | if (localStorage['chartOn']) g.style.opacity = UIopacity;
886 | UI.style.pointerEvents = "auto";
887 | if (localStorage['chartOn']) g.style.pointerEvents = "auto";
888 | else g.style.pointerEvents = "none";
889 | }
890 | }
891 | }]);
892 | _.listen.apply(window, ['mouseup', e => {
893 | isDragging = false;
894 | UI.style.opacity = UIopacity;
895 | UI.style.borderColor = "#000066";
896 | e.preventDefault();
897 | _.unlisten.apply(window, ['mousemove', moveUI, true]);
898 | }, false]);
899 | detectWebGL();
900 | createOptsMenu();
901 | if (apie.onReady) {
902 | apie.onReady();
903 | }
904 | body.appendChild(UI);
905 | },
906 | initChart = () => {
907 | if (!document.body) {
908 | let _initChart = initChart.bind(this);
909 | setTimeout(_initChart, 300);
910 | return;
911 | }
912 | g.style.zIndex = 9999;
913 | g.style.backgroundColor = "#000";
914 | g.style.fontFamily = "Ubuntu";
915 | g.style.position = "fixed";
916 | g.style.bottom = "5%";
917 | g.style.right = "5%";
918 | g.style.fontSize = "125%";
919 | g.style.color = "white";
920 | g.style.opacity = 0.7;
921 | g.style.padding = "10px";
922 | g.style.border = "6px solid";
923 | g.style.borderColor = "#000066";
924 | g.style.borderRadius = "7px";
925 | g.style.width = "40%";
926 | g.style.height = "25%";
927 | g.style.transition = "opacity 500ms, border 500ms, border-color 500ms";
928 | Highcharts.chart(g, {
929 | chart: {
930 | backgroundColor: {
931 | linearGradient: [0, 0, 500, 500],
932 | stops: [
933 | [0, 'rgb(0, 0, 0)'],
934 | [1, 'rgb(0, 0, 0)']
935 | ]
936 | },
937 | style: {
938 | color: "#fff",
939 | fontFamily: "Ubuntu"
940 | }
941 | },
942 | title: {
943 | text: "Speed",
944 | x: -20,
945 | style: {
946 | color: "#fff",
947 | fontFamily: "Ubuntu"
948 | }
949 | },
950 | tooltip: {
951 | valueSuffix: ' WPM',
952 | },
953 | xAxis: {
954 | gridLineWidth: 0,
955 | categories: [
956 | //
957 | ],
958 | labels: {
959 | style: {
960 | color: '#FFF',
961 | font: 'Ubuntu'
962 | }
963 | }
964 | },
965 | yAxis: {
966 | gridLineWidth: 0,
967 | title: {
968 | text: "WPM"
969 | },
970 | plotLines: [{
971 | value: 0,
972 | width: 1,
973 | color: '#ff0000'
974 | }],
975 | labels: {
976 | style: {
977 | color: '#FFF',
978 | font: 'Ubuntu'
979 | }
980 | }
981 | },
982 | legend: {
983 | layout: 'vertical',
984 | align: 'right',
985 | verticalAlign: 'middle',
986 | borderWidth: 0,
987 | style: {
988 | color: "#fff"
989 | }
990 | },
991 | plotOptions: {
992 | line: {
993 | marker: {
994 | enabled: false
995 | }
996 | }
997 | },
998 | series: [{
999 | name: 'Speed in WPM',
1000 | data: points,
1001 | color: '#000066'
1002 | }]
1003 | });
1004 | chart = Highcharts.charts[0];
1005 | _.listen.apply(g, ['mouseover', () => {
1006 | if (localStorage['chartOn']) g.style.opacity = 1;
1007 | if (localStorage['chartOn']) g.style.borderColor = "#0000ff";
1008 | }, true]);
1009 | _.listen.apply(g, ['mouseout', () => {
1010 | if (localStorage['chartOn']) g.style.opacity = 0.7;
1011 | if (localStorage['chartOn']) g.style.borderColor = "#000066";
1012 | }, true]);
1013 | addGraph(g);
1014 | setTimeout(() => {
1015 | let cr = g.getElementsByClassName('highcharts-credits');
1016 | for (let i = 0; i < cr.length; i++) {
1017 | cr[i].remove();
1018 | }
1019 | }, 500);
1020 | if (!localStorage['chartOn']) {
1021 | g.style.opacity = 0;
1022 | }
1023 | },
1024 | createOptsMenu = () => {
1025 | opt = document.createElement('div');
1026 | opt.style.zIndex = 99999999;
1027 | opt.style.backgroundColor = "#000";
1028 | opt.style.border = "6px solid";
1029 | opt.style.borderColor = "#000066";
1030 | opt.style.borderRadius = "6px";
1031 | opt.style.fontSize = "150%";
1032 | opt.style.color = "#FFF";
1033 | opt.style.position = "fixed";
1034 | opt.style.padding = "10px";
1035 | opt.style.top = "50%";
1036 | opt.style.left = "50%";
1037 | opt.style.display = "inline-block";
1038 | opt.style.fontFamily = "Ubuntu";
1039 | opt.style.transform = "translate(-50%, -50%)";
1040 | opt.style.transition = "opacity 500ms, border 500ms, border-color 500ms";
1041 |
1042 | opt.style.opacity = 0;
1043 | opt.style.pointerEvents = "none";
1044 |
1045 | let inner = document.createElement('center');
1046 |
1047 | let lbl = document.createElement('h1');
1048 | lbl.style.fontSize = "150%";
1049 | lbl.innerHTML = "Customize UltraType";
1050 | inner.appendChild(lbl);
1051 |
1052 | let outerBotOn = document.createElement('div');
1053 | let botOnBtn = document.createElement('button');
1054 | botOnBtn.className = "";
1055 | botOnBtn.style.backgroundColor = "transparent";
1056 | botOnBtn.style.border = "3px solid";
1057 | botOnBtn.style.borderRadius = "3px";
1058 | botOnBtn.style.fontSize = "100%";
1059 | botOnBtn.style.borderColor = "LimeGreen";
1060 | botOnBtn.style.color = "LimeGreen";
1061 | botOnBtn.style.transition = "border 500ms, border-color 500ms, color 500ms";
1062 | botOnBtn.innerHTML = "On";
1063 | botOnBtn.onclick = () => {
1064 | enabled = !enabled;
1065 | if (!enabled) {
1066 | botOnBtn.style.borderColor = "red";
1067 | botOnBtn.style.color = "red";
1068 | botOnBtn.innerHTML = "Off";
1069 | } else {
1070 | botOnBtn.style.borderColor = "LimeGreen";
1071 | botOnBtn.style.color = "LimeGreen";
1072 | botOnBtn.innerHTML = "On";
1073 | }
1074 | }
1075 | outerBotOn.innerHTML += "Bot enabled: ";
1076 | outerBotOn.appendChild(botOnBtn);
1077 | inner.appendChild(outerBotOn);
1078 |
1079 | let outerToggle = document.createElement('div');
1080 | let toggleButton = document.createElement('button');
1081 | toggleButton.className = "";
1082 | toggleButton.style.backgroundColor = "transparent";
1083 | toggleButton.style.border = "3px solid";
1084 | toggleButton.style.borderRadius = "3px";
1085 | toggleButton.style.fontSize = "100%";
1086 | toggleButton.style.transition = "border 500ms, border-color 500ms, color 500ms";
1087 |
1088 | if (autoRefresh) {
1089 | toggleButton.style.borderColor = "LimeGreen";
1090 | toggleButton.style.color = "LimeGreen";
1091 | toggleButton.innerHTML = "On";
1092 | } else {
1093 | toggleButton.style.borderColor = "red";
1094 | toggleButton.style.color = "red";
1095 | toggleButton.innerHTML = "Off";
1096 | }
1097 | toggleButton.onclick = () => {
1098 | autoRefresh = !autoRefresh;
1099 | setLocalStorage('autoRefresh', autoRefresh);
1100 | if (!autoRefresh) {
1101 | toggleButton.style.borderColor = "red";
1102 | toggleButton.style.color = "red";
1103 | toggleButton.innerHTML = "Off";
1104 | } else {
1105 | toggleButton.style.borderColor = "LimeGreen";
1106 | toggleButton.style.color = "LimeGreen";
1107 | toggleButton.innerHTML = "On";
1108 | }
1109 | }
1110 | outerToggle.innerHTML += "Auto Refresh: ";
1111 | outerToggle.appendChild(toggleButton);
1112 | inner.appendChild(outerToggle);
1113 |
1114 | let outerNtr = document.createElement('div');
1115 | autoNitroBtn = document.createElement('button');
1116 | autoNitroBtn.className = "";
1117 | autoNitroBtn.style.backgroundColor = "transparent";
1118 | autoNitroBtn.style.border = "3px solid";
1119 | autoNitroBtn.style.borderRadius = "3px";
1120 | autoNitroBtn.style.fontSize = "100%";
1121 | autoNitroBtn.style.transition = "border 500ms, border-color 500ms, color 500ms";
1122 | if (autoNitro) {
1123 | autoNitroBtn.style.borderColor = "LimeGreen";
1124 | autoNitroBtn.style.color = "LimeGreen";
1125 | autoNitroBtn.innerHTML = "On";
1126 | } else {
1127 | autoNitroBtn.style.borderColor = "red";
1128 | autoNitroBtn.style.color = "red";
1129 | autoNitroBtn.innerHTML = "Off";
1130 | }
1131 | autoNitroBtn.onclick = () => {
1132 | autoNitro ? autoNitroOn() : autoNitroOff();
1133 | }
1134 | outerNtr.innerHTML += "Auto Nitro: ";
1135 | outerNtr.appendChild(autoNitroBtn);
1136 | inner.appendChild(outerNtr);
1137 |
1138 | let outerChrtBtn = document.createElement('div');
1139 | let chartBtn = document.createElement('button');
1140 | chartBtn.className = "";
1141 | chartBtn.style.backgroundColor = "transparent";
1142 | chartBtn.style.border = "3px solid";
1143 | chartBtn.style.borderRadius = "3px";
1144 | chartBtn.style.fontSize = "100%";
1145 | chartBtn.style.transition = "border 500ms, border-color 500ms, color 500ms";
1146 |
1147 | if (localStorage['chartOn']) {
1148 | chartBtn.style.borderColor = "LimeGreen";
1149 | chartBtn.style.color = "LimeGreen";
1150 | chartBtn.innerHTML = "On";
1151 | } else {
1152 | chartBtn.style.borderColor = "red";
1153 | chartBtn.style.color = "red";
1154 | chartBtn.innerHTML = "Off";
1155 | }
1156 | chartBtn.onclick = () => {
1157 | if (localStorage['chartOn']) {
1158 | delete localStorage['chartOn'];
1159 | chartBtn.style.borderColor = "red";
1160 | chartBtn.style.color = "red";
1161 | chartBtn.innerHTML = "Off";
1162 | } else {
1163 | localStorage['chartOn'] = 1;
1164 | chartBtn.style.borderColor = "LimeGreen";
1165 | chartBtn.style.color = "LimeGreen";
1166 | chartBtn.innerHTML = "On";
1167 | g.style.opacity = 0.7;
1168 | }
1169 | }
1170 | outerChrtBtn.innerHTML += "Speed chart: ";
1171 | outerChrtBtn.appendChild(chartBtn);
1172 | inner.appendChild(outerChrtBtn);
1173 |
1174 | let outerACfg = document.createElement('div');
1175 | acc = document.createElement('input');
1176 | acc.type = "number";
1177 | acc.min = 10;
1178 | acc.max = 100;
1179 | acc.value = accuracy * 100;
1180 | acc.className = "";
1181 | acc.style.backgroundColor = "transparent";
1182 | acc.style.border = "3px solid";
1183 | acc.style.borderRadius = "3px";
1184 | acc.style.fontSize = "100%";
1185 | acc.style.borderColor = "LimeGreen";
1186 | acc.style.color = "LimeGreen";
1187 | acc.style.transition = "border 500ms, border-color 500ms, color 500ms";
1188 | acc.onchange = () => {
1189 | accuracy = parseInt(acc.value);
1190 | if (isNaN(accuracy)) {
1191 | accuracy = 0.98;
1192 | acc.value = 98;
1193 | } else {
1194 | accuracy *= 0.01;
1195 | }
1196 | setLocalStorage('accuracy', accuracy);
1197 | }
1198 |
1199 | outerACfg.innerHTML += "Accuracy %: ";
1200 | outerACfg.appendChild(acc);
1201 | inner.appendChild(outerACfg);
1202 |
1203 | let oWPMCfg = document.createElement('div');
1204 | wpm = document.createElement('input');
1205 | wpm.type = "number";
1206 | wpm.min = 3;
1207 | wpm.max = MAX_WPM; // About the fastest you can go without any bans
1208 | wpm.value = wordsPerMinute;
1209 | wpm.className = "";
1210 | wpm.style.backgroundColor = "transparent";
1211 | wpm.style.border = "3px solid";
1212 | wpm.style.borderRadius = "3px";
1213 | wpm.style.fontSize = "100%";
1214 | wpm.style.borderColor = "LimeGreen";
1215 | wpm.style.color = "LimeGreen";
1216 | wpm.style.transition = "border 500ms, border-color 500ms, color 500ms";
1217 | wpm.onchange = () => {
1218 | if (localStorage["speedChange"]) {
1219 | wordsPerMinute = parseInt(wpm.value);
1220 | if (wordsPerMinute > 220) {
1221 | alert('WARNING: You WILL be banned if you set your WPM above 200.');
1222 | }
1223 | if (isNaN(wordsPerMinute))
1224 | wpm.value = 85;
1225 | setWPM(wpm.value);
1226 | } else {
1227 | // alert('It is not recommended to alter the default speed of UltraType, be careful! This message will not be shown again.');
1228 | setLocalStorage('speedChange', true);
1229 | }
1230 | }
1231 |
1232 | oWPMCfg.innerHTML += "WPM: ";
1233 | oWPMCfg.appendChild(wpm);
1234 | inner.appendChild(oWPMCfg);
1235 |
1236 | let outerStatTogg = document.createElement('div');
1237 | statTogg = document.createElement('button');
1238 |
1239 | statTogg.className = "";
1240 | statTogg.style.backgroundColor = "transparent";
1241 | statTogg.style.border = "3px solid";
1242 | statTogg.style.borderRadius = "3px";
1243 | statTogg.style.fontSize = "100%";
1244 | statTogg.style.borderColor = "LimeGreen";
1245 | statTogg.style.color = "LimeGreen";
1246 | statTogg.style.transition = "border 500ms, border-color 500ms, color 500ms";
1247 | statTogg.innerHTML = "On";
1248 | statTogg.onclick = () => {
1249 | statsOn = !statsOn;
1250 | if (statsOn) {
1251 | statTogg.style.borderColor = "LimeGreen";
1252 | statTogg.style.color = "LimeGreen";
1253 | statTogg.innerHTML = "On";
1254 | updateStats();
1255 | } else {
1256 | statTogg.style.borderColor = "red";
1257 | statTogg.style.color = "red";
1258 | statTogg.innerHTML = "Off";
1259 | disableStats();
1260 | }
1261 | setLocalStorage('statsOn', statsOn);
1262 | }
1263 | outerStatTogg.innerHTML = "User Stats: ";
1264 | outerStatTogg.appendChild(statTogg);
1265 | inner.appendChild(outerStatTogg);
1266 |
1267 | let outerAutoT = document.createElement('div');
1268 | let autoT = document.createElement('button');
1269 | autoT.className = "";
1270 | autoT.style.backgroundColor = "transparent";
1271 | autoT.style.border = "3px solid";
1272 | autoT.style.borderRadius = "3px";
1273 | autoT.style.fontSize = "100%";
1274 | autoT.style.borderColor = "LimeGreen";
1275 | autoT.style.color = "LimeGreen";
1276 | autoT.style.transition = "border 500ms, border-color 500ms, color 500ms";
1277 | autoT.innerHTML = "On";
1278 | autoT.onclick = () => {
1279 | if (!autoTurbo) {
1280 | autoT.style.borderColor = "LimeGreen";
1281 | autoT.style.color = "LimeGreen";
1282 | autoT.innerHTML = "On";
1283 | autoTurboOn();
1284 | } else {
1285 | autoT.style.borderColor = "red";
1286 | autoT.style.color = "red";
1287 | autoT.innerHTML = "Off";
1288 | autoTurboOff();
1289 | }
1290 | }
1291 | // Set the default button state
1292 | if (autoTurbo) {
1293 | autoT.style.borderColor = "LimeGreen";
1294 | autoT.style.color = "LimeGreen";
1295 | autoT.innerHTML = "On";
1296 | } else {
1297 | autoT.style.borderColor = "red";
1298 | autoT.style.color = "red";
1299 | autoT.innerHTML = "Off";
1300 | }
1301 | outerAutoT.innerHTML = "Auto Turbo: ";
1302 | outerAutoT.appendChild(autoT);
1303 | inner.appendChild(outerAutoT);
1304 |
1305 | let tips = document.createElement('p');
1306 | tips.innerHTML = "Press escape to hide all of the UltraType menus.
";
1307 | inner.appendChild(tips);
1308 |
1309 | let outerExitBtn = document.createElement('center');
1310 | let exitButton = document.createElement('button');
1311 | exitButton.className = "";
1312 | exitButton.style.borderColor = "#808080";
1313 | exitButton.style.color = "#808080";
1314 | exitButton.style.fontSize = "175%";
1315 | exitButton.style.border = "3px solid";
1316 | exitButton.style.borderRadius = "3px";
1317 | exitButton.style.backgroundColor = "transparent";
1318 | exitButton.style.transition = "border 500ms, border-color 500ms, color 500ms";
1319 | _.listen.apply(exitButton, ["mouseover", () => {
1320 | exitButton.style.color = "#FFF";
1321 | exitButton.style.borderColor = "#FFF";
1322 | }, true]);
1323 | _.listen.apply(exitButton, ["mouseout", () => {
1324 | exitButton.style.color = "#808080";
1325 | exitButton.style.borderColor = "#808080";
1326 | }, true]);
1327 | exitButton.innerHTML = "Exit";
1328 | exitButton.onclick = () => {
1329 | opt.style.opacity = 0;
1330 | opt.style.pointerEvents = "none";
1331 | optOn = false;
1332 | opt.blur();
1333 | }
1334 | outerExitBtn.appendChild(exitButton);
1335 | inner.appendChild(outerExitBtn);
1336 |
1337 | opt.appendChild(inner);
1338 | root.appendChild(opt);
1339 |
1340 | setTimeout(() => {
1341 | let localAutoRefresh = localStorage['autoRefresh'],
1342 | localAccuracy = localStorage['accuracy'],
1343 | localWPM = localStorage['wpm'],
1344 | localAutoNitro = localStorage['autoNitro'];
1345 | if (localAutoNitro !== null && localAutoNitro !== undefined) {
1346 | localAutoNitro = JSON.parse(localAutoNitro);
1347 | if (localAutoNitro == false) {
1348 | autoNitroOff();
1349 | } else {
1350 | autoNitroOn();
1351 | }
1352 | }
1353 |
1354 | if (localAutoRefresh) {
1355 | autoRefresh = JSON.parse(localAutoRefresh);
1356 | if (!autoRefresh) {
1357 | toggleButton.style.borderColor = "red";
1358 | toggleButton.style.color = "red";
1359 | toggleButton.innerHTML = "Off";
1360 | } else {
1361 | toggleButton.style.borderColor = "LimeGreen";
1362 | toggleButton.style.color = "LimeGreen";
1363 | toggleButton.innerHTML = "On";
1364 | }
1365 | }
1366 | if (localAccuracy) {
1367 | accuracy = parseFloat(localAccuracy);
1368 | acc.value = accuracy * 100;
1369 | }
1370 | if (localWPM) {
1371 | wpm.value = localWPM;
1372 | wordsPerMinute = parseInt(localWPM);
1373 | setWPM(wordsPerMinute);
1374 | }
1375 | if (statsOn) {
1376 | statTogg.style.borderColor = "LimeGreen";
1377 | statTogg.style.color = "LimeGreen";
1378 | statTogg.innerHTML = "On";
1379 | updateStats();
1380 | } else {
1381 | statTogg.style.borderColor = "red";
1382 | statTogg.style.color = "red";
1383 | statTogg.innerHTML = "Off";
1384 | disableStats();
1385 | }
1386 | }, 1000);
1387 | },
1388 | blockAd = ad => {
1389 | try {
1390 | ad.style.display = "none";
1391 | } catch (e) {
1392 | ad.src = "about:blank";
1393 | }
1394 | try {
1395 | ad.parentElement.parentElement.parentElement.remove();
1396 | } catch (e) {};
1397 | },
1398 | changeTip = node => {
1399 | setTimeout(() => {
1400 | node.style.fontSize = "125%";
1401 | node.style.border = "3px solid #000066";
1402 | node.style.borderRadius = "7px";
1403 | node.style.opacity = 0.7;
1404 | node.style.pointerEvents = "none";
1405 | node.innerHTML = "";
1406 | node.innerHTML += FONT;
1407 | node.innerHTML += 'UltraType - NitroType simplified.
Version: ' + VERSION + '';
1408 | }, 1000);
1409 | },
1410 | detectWebGL = () => {
1411 | if (document.cookie.includes('webgl')) {
1412 | document.cookie = document.cookie.replace('webgl', 'canvas');
1413 | }
1414 | },
1415 | handleScript = scr => {
1416 | if (scr.src.includes('libs')) {
1417 | if (hasScrLoaded) return;
1418 | else hasScrLoaded = 1;
1419 | scr.addEventListener('load', () => {
1420 | let didGetHandler = false;
1421 | _attachHandler = $.fn.constructor.prototype.keypress;
1422 | $.fn.constructor.prototype.keypress = function() {
1423 | if (this && this[0] && this[0] == document.body) {
1424 | let handler = arguments[0];
1425 | keyPressHandler = handler;
1426 | debug("Intercepted jQuery keypress handler:", handler);
1427 | }
1428 | return _attachHandler.apply(this, arguments);
1429 | }
1430 | });
1431 | }
1432 | }
1433 | console.warn = function() {
1434 | if (arguments[0] == "You have been disqualified") {
1435 | disqualified = true;
1436 | }
1437 | console.log.apply(this, arguments);
1438 | }
1439 | __.fill = function() {
1440 | handleFillText(arguments);
1441 | _.fill.apply(this, arguments);
1442 | }
1443 | let _set = null,
1444 | _send = WebSocket.prototype.send;
1445 | WebSocket.prototype.send = function() {
1446 | if (typeof arguments[0] !== 'string') {
1447 | return _send.apply(this, arguments);
1448 | }
1449 | let msg = arguments[0],
1450 | header = msg[0],
1451 | obj = null;
1452 | msg = msg.substr(1, msg.length);
1453 | try {
1454 | obj = JSON.parse(msg);
1455 | } catch(e) {
1456 | return _send.apply(this, arguments);;
1457 | }
1458 | if (obj && obj.payload && obj.payload.a) {
1459 | debug("very naughty packet detected, lets fix that");
1460 | delete obj.payload.a;
1461 | // Replace packet
1462 | arguments[0] = header + JSON.stringify(obj);
1463 | }
1464 | return _send.apply(this, arguments);
1465 | }
1466 | onfinish(() => {
1467 | debug("Race has finished. Doing a ban check and reloading if needed.");
1468 | if (apie.onRaceFinish) {
1469 | apie.onRaceFinish();
1470 | }
1471 | endTime = new Date();
1472 | infoSpan.innerHTML = "Finished";
1473 | infoSpan.style.color = "#b3b3b3";
1474 | if (localStorage['autoRefresh']) {
1475 | debug("Auto refresh is enabled");
1476 | respawn();
1477 | } else {
1478 | debug("Auto refresh is disabled");
1479 | }
1480 | });
1481 | XMLHttpRequest.prototype.send = function() {
1482 | let payload = arguments[0];
1483 | let header = '';
1484 | if (payload && payload.length > 4 && payload[4] == '{') {
1485 | let obj;
1486 | header = payload.substr(0, 4);
1487 | try {
1488 | obj = JSON.parse(payload.substr(4, payload.length));
1489 | } catch(e) {
1490 | return _.xsend.apply(this, arguments);
1491 | }
1492 | if (obj.payload && obj.payload.a) {
1493 | // Remove cheater flag from outgoing packet
1494 | delete obj.payload.a;
1495 | arguments[0] = header + JSON.stringify(obj);
1496 | }
1497 | }
1498 | return _.xsend.apply(this, arguments);
1499 | }
1500 | XMLHttpRequest.prototype.open = function() {
1501 | if (arguments[1].includes('/api/error')) {
1502 | errorRequests.push(this);
1503 | this.abort();
1504 | return;
1505 | } else if (arguments[1].includes('problem-keys')) {
1506 | if (PROBLEM_KEYS_DEBUG) {
1507 | console.warn('PROBLEM_KEYS_DEBUG is enabled, firing up debugger.');
1508 | debugger;
1509 | }
1510 | if (ABORT_PROBLEM_KEYS) {
1511 | debug("Aborting problem-keys AJAX request.");
1512 | this.abort();
1513 | return;
1514 | } else {
1515 | debug("Detected outgoing problem-keys AJAX request, but ABORT_PROBLEM_KEYS is false, so I'm letting it send.");
1516 | }
1517 | }
1518 | return _.xopen.apply(this, arguments);
1519 | }
1520 | // inject undetectable features
1521 | window.PIXI = {};
1522 | PIXI.BitmapText = function() {};
1523 | PIXI.BitmapText.prototype.setText = function(a) { this.text = a || " ", this.dirty = !0 };
1524 | let hostt = ShadowRoot.prototype.__lookupGetter__('host');
1525 | let _getToStr = Function.prototype.__lookupGetter__('toString');
1526 | let _setTxt = Element.prototype.__lookupSetter__('textContent');
1527 | let _getTitle = Document.prototype.__lookupGetter__('title');
1528 | let _setTitle = Document.prototype.__lookupSetter__('title');
1529 | CanvasRenderingContext2D.prototype.fillText = __.fill;
1530 | window.WebSocket = __ws;
1531 | Function.prototype.toString = __.toStr = function() {
1532 | if (this === Function.prototype.toString) return _.toStr.call(_.toStr);
1533 | if (this === CanvasRenderingContext2D.prototype.fillText) return _.toStr.call(_.fill);
1534 | if (this === Object.prototype.__lookupGetter__) return _.toStr.call(_.get);
1535 | if (this === ShadowRoot.prototype.__lookupGetter__('host')) return _.toStr.call(hostt);
1536 | if (this === Function.prototype.__lookupGetter__('toString')) return _.toStr.call(_getToStr);
1537 | if (this === Element.prototype.__lookupSetter__('textContent')) return _.toStr.call(_setTxt);
1538 | if (this === Document.prototype.__lookupGetter__('title')) return _.toStr.call(_getTitle);
1539 | if (this === Document.prototype.__lookupSetter__('title')) return _.toStr.call(_setTitle);
1540 | if (this === PIXI.BitmapText.prototype.setText) return _.toStr.call(_get);
1541 | if (this === console.warn) return _.toStr.call(_.warn);
1542 | // if (this === WebSocket) return _.toStr.call(_.ws);
1543 | if (this === XMLHttpRequest.prototype.send) return _.toStr.call(_.xsend);
1544 | if (this === XMLHttpRequest.prototype.open) return _.toStr.call(_.xopen);
1545 | if (this === window.onerror) return _.toStr.call(_.oerr);
1546 | if (window.jQuery && this === jQuery.fn.keypress) return _.toStr.call(_attachHandler);
1547 | return _.toStr.call(this);
1548 | }
1549 | ShadowRoot.prototype.__defineGetter__('host', () => {
1550 | if (this === injectedRoot) return null;
1551 | return _.host.call(this);
1552 | });
1553 | // old mutation observer, ill keep it but doesnt work properly.
1554 | let observer = new MutationObserver(mutations => {
1555 | mutations.forEach(mutation => {
1556 | if (mutation.type == "childList" && mutation.addedNodes.length > 0) {
1557 | for (let i in mutation.addedNodes) {
1558 | if (mutation.addedNodes[i].nodeName == "BODY") createUI(mutation.addedNodes[i]);
1559 | if (mutation.addedNodes[i].nodeName == "IFRAME") blockAd(mutation.addedNodes[i]);
1560 | if (mutation.addedNodes[i].className == "race-tip") changeTip(mutation.addedNodes[i]);
1561 | if (mutation.addedNodes[i].nodeName == "SCRIPT") handleScript(mutation.addedNodes[i]);
1562 | }
1563 | }
1564 | });
1565 | });
1566 | observer.observe(document.documentElement, {
1567 | childList: true,
1568 | subtree: true,
1569 | attributes: true,
1570 | attributeFilter: ['style']
1571 | });
1572 |
1573 | // the following is inspired by said mutation observer
1574 | let scripts = document.getElementsByTagName('script');
1575 | for (let i = 0; i < scripts.length; ++i) {
1576 | let s = scripts[i];
1577 | s.addEventListener('load', () => {
1578 | handleScript(s);
1579 | });
1580 | }
1581 | // adblock lol
1582 | setInterval(() => {
1583 | let iframes = document.getElementsByTagName('iframe');
1584 | for (let i = 0; i < iframes.length; ++i) iframes[i].remove();
1585 | }, 1000);
1586 |
1587 | let _fakeToStr = __.toStr;
1588 | _fakeToStr.__proto__ = _.toStr.prototype;
1589 | _fakeToStr.prototype = _.toStr.prototype;
1590 | Object.defineProperty(Function.prototype, 'toString', {
1591 | get: () => {
1592 | if (this === __.toStr) return _fakeToStr;
1593 | return __.toStr;
1594 | },
1595 | set: (a) => {
1596 | // Function.prototype.toString = a;
1597 | // some library redefines this method for some reason? i'll just skip it for now
1598 | return;
1599 | },
1600 | enumerable: false
1601 | });
1602 | localStorage.clear = function() {} // Disable localStorage clearing
1603 | Function.prototype.__defineGetter__('toString', function() {
1604 | if (this === CanvasRenderingContext2D.prototype || this === CanvasRenderingContext2D.prototype.fillText) return __.toStr;
1605 | if (this === console || this === console.warn) return __.toStr;
1606 | if (this === ShadowRoot.prototype.__lookupGetter__('host') || this === ShadowRoot.prototype) return __.toStr;
1607 | if (this === Object.prototype || this === Object.prototype.__lookupGetter__) return __.toStr;
1608 | if (this === Function.prototype.__lookupGetter__('toString')) return __.toStr;
1609 | if (this === PIXI.BitmapText.prototype.setText) return __.toStr;
1610 | if (this === WebSocket) return __.toStr;
1611 | if (this === injectedRoot) return __.toStr;
1612 | if (this === Document.prototype.__lookupGetter__('title')) return __.toStr;
1613 | if (this === Document.prototype.__lookupSetter__('title')) return __.toStr;
1614 | if (this === XMLHttpRequest.prototype.send) return __.toStr;
1615 | if (this === XMLHttpRequest.prototype.open) return __.toStr;
1616 | if (this === window.onerror) return __.toStr;
1617 | if (window.jQuery && this === jQuery.fn.keypress) return __.toStr;
1618 | return _.toStr;
1619 | });
1620 | setInterval(() => {
1621 | _setTitle.call(document, "UltraType 2");
1622 | }, 100);
1623 | Document.prototype.__defineGetter__('title', t => {
1624 | return _title;
1625 | });
1626 | Document.prototype.__defineSetter__('title', t => {
1627 | _title = t;
1628 | });
1629 | // an insanely weird way to attach the load listener. this was intended to throw the developers off but now its useless... whatever
1630 | _.listen.apply(window, ['load', () => {
1631 | _.oerr = window.onerror;
1632 | window.onbeforeunload = () => {
1633 | return null;
1634 | };
1635 | window.ga = () => {};
1636 | window.onerror = evt => {
1637 | if (evt.includes("'visible' of undefined")) {
1638 | // Exception triggered due to turbo mode
1639 | respawn();
1640 | }
1641 | return null;
1642 | };
1643 | /*
1644 | username = extractUserName();
1645 | userInfo = ROT47(localStorage["A=2J6C"]);
1646 | userInfo = JSON.parse(userInfo);
1647 | debug("Extracted and decrypted user info", userInfo);
1648 | if (localStorage['statsOn']) statsOn = true;
1649 | */
1650 | }]);
1651 | /*
1652 | window.addEventListener('DOMContentLoaded', () => {
1653 | setTimeout(removeUITrash, 75);
1654 | });
1655 | */
1656 | let registerAPIEvent = (evt, callback) => {
1657 | if (typeof callback !== 'function') {
1658 | throw new Error('Invalid event callback.');
1659 | return;
1660 | }
1661 | switch (evt) {
1662 | case "userBanned":
1663 | apie.onUserBanned = callback;
1664 | break;
1665 | case "raceStart":
1666 | apie.onRaceStart = callback;
1667 | break;
1668 | case "raceEnd":
1669 | case "raceFinish":
1670 | apie.onRaceFinish = callback;
1671 | break;
1672 | case "nitroUsed":
1673 | case "nitroUse":
1674 | case "nitro":
1675 | apie.onNitroUsed = callback;
1676 | break;
1677 | case "raceStarting":
1678 | case "raceBegin":
1679 | case "raceInit":
1680 | apie.onRaceStarting = callback;
1681 | break;
1682 | case "type":
1683 | case "typed":
1684 | case "botType":
1685 | apie.onType = callback;
1686 | break;
1687 | case "ready":
1688 | case "load":
1689 | case "loaded":
1690 | case "start":
1691 | case "started":
1692 | apie.onReady = callback;
1693 | break;
1694 | default:
1695 | throw new Error('Invalid event name!');
1696 | break;
1697 | }
1698 | return window.UltraTypeCore;
1699 | }
1700 |
1701 | // Core API
1702 | let core = {
1703 | on: registerAPIEvent,
1704 | turbo: turbo,
1705 | setWPM: setWPM,
1706 | sendTypePacket: typePacket,
1707 | typeChar: type,
1708 | stopFromRunning: () => { // Stops the bot from appearing or typing
1709 | isStopped = true;
1710 | },
1711 | getDecyptedUserInfo: () => {
1712 | if (userInfo) {
1713 | return userInfo;
1714 | } else {
1715 | return null;
1716 | }
1717 | },
1718 | setAutoTurbo: state => {
1719 | if (state === false) {
1720 | autoTurboOff();
1721 | } else if (state === true) {
1722 | autoTurboOn();
1723 | } else {
1724 | throw new Error('Invalid auto turbo state.');
1725 | }
1726 | },
1727 | getBotStateRaw: getBotState,
1728 | getBotState: () => {
1729 | return {
1730 | nitrosUsed: nitrosUsed,
1731 | lesson: lesson,
1732 | currWord: index,
1733 | wpm: wordsPerMinute,
1734 | acc: accuracy,
1735 | errReqs: errorRequests.length,
1736 | uinfo: JSON.stringify(userInfo),
1737 | fillsY: fillsY.length,
1738 | version: VERSION,
1739 | wpmHistory: points,
1740 | isFinished: finished,
1741 | startTime: startTime,
1742 | endTime: endTime
1743 | };
1744 | },
1745 | toggleDebug: () => {
1746 | LOG_DEBUG = !LOG_DEBUG;
1747 | },
1748 | getLesson: () => {
1749 | if (lesson) {
1750 | return lesson;
1751 | } else return null;
1752 | },
1753 | setAutoRefresh: val => {
1754 | if (typeof val !== 'boolean') {
1755 | throw new Error('Can only set auto refresh to a boolean.');
1756 | return;
1757 | } else {
1758 | autoRefresh = val;
1759 | }
1760 | },
1761 | getNitrosUsed: () => { return nitrosUsed || 0 },
1762 | toggleBotLog: () => {
1763 | LOG_TYPING_INFO = !LOG_TYPING_INFO;
1764 | },
1765 | disableStats: disableStats,
1766 | randBool: randomBool,
1767 | updateStats: updateStats,
1768 | useNitro: useNitro,
1769 | flushRaw: () => {
1770 | // Reset UltraType to it's default settings
1771 | [
1772 | 'accuracy',
1773 | 'autoRefresh',
1774 | 'autoTurbo',
1775 | 'statsOn',
1776 | 'autoNitro',
1777 | 'wpm',
1778 | 'chartOn',
1779 | 'speedChange'
1780 | ].forEach(k => {
1781 | delete localStorage[k];
1782 | });
1783 | },
1784 | flush: () => {
1785 | core.flushRaw();
1786 | delete localStorage['ultratypedev'];
1787 | console.warn('Flushed UltraType settings, reloading...');
1788 | setTimeout(location.reload.bind(location), 1000);
1789 | },
1790 | toggleLocalLoad: () => {
1791 | if (localStorage["ultratypedev"]) {
1792 | delete localStorage["ultratypedev"];
1793 | console.info("Disabled local loading.");
1794 | } else {
1795 | localStorage["ultratypedev"] = true;
1796 | console.info("Enabled local loading.");
1797 | }
1798 | },
1799 | // Utility method to automatically involk debugger when a function is called
1800 | debugFn: fn => {
1801 | let _fn = fn;
1802 | fn = function() {
1803 | debugger;
1804 | _fn.apply(this, arguments);
1805 | }
1806 | return fn;
1807 | }
1808 | }
1809 | window.UltraTypeCore = core;
1810 | let hcScript = document.createElement('script');
1811 | hcScript.src = 'https://code.highcharts.com/highcharts.src.js';
1812 | hcScript.addEventListener('load', () => {
1813 | setTimeout(initChart.bind(window), 250);
1814 | });
1815 | document.head.appendChild(hcScript);
1816 |
1817 | //theres no jquery anymore??
1818 | let jScript = document.createElement('script'),
1819 | jScriptLoaded = false;
1820 | jScript.src = 'https://code.jquery.com/jquery-3.6.3.min.js';
1821 | document.head.appendChild(jScript);
1822 | jScript.addEventListener('load', () => {
1823 | jScriptLoaded = true;
1824 | createUI(document.body);
1825 | });
1826 | // need keypress handler
1827 | let oldEventAttach = EventTarget.prototype.addEventListener;
1828 | EventTarget.prototype.addEventListener = function() {
1829 | let type = arguments[0];
1830 | if (type.includes('keydown')) {
1831 | var fnStr = arguments[1].toString();
1832 | if (fnStr.includes('apply')) {
1833 | // this is cool kid
1834 | if (!keyPressHandlers.includes(arguments[1])) {
1835 | keyPressHandlers.push(arguments[1]);
1836 | }
1837 | console.log('this is cool kid:', arguments[1]);
1838 | }
1839 | }
1840 | oldEventAttach.apply(this, arguments);
1841 | }
1842 | // lol
1843 | const CLIENT_SRC = `https://www.nitrotype.com/dist/site/js/ra.js`;
1844 | (() => {
1845 | var x = new XMLHttpRequest();
1846 | x.open('GET', CLIENT_SRC, true);
1847 | x.onload = function() {
1848 | let innerScript = this.responseText;
1849 | innerScript = innerScript.replace(`e.handleKeyPress=function`, `window.eObj=e,e.handleKeyPress=window.KEY_PRESS_HANDLER=function`);
1850 | let s = document.createElement('script');
1851 | s.innerHTML = innerScript;
1852 | document.body.appendChild(s);
1853 | }
1854 | x.send();
1855 | })();
1856 |
1857 | // Bye bye!
1858 | console.log('UltraType version ' + VERSION + ' loaded.');
1859 | document.currentScript.remove();
1860 | })();
1861 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UltraType Bot
2 | 
3 |
4 | [CLICK HERE TO INSTALL CHROME EXTENSION](https://chrome.google.com/webstore/detail/ultratype-nitrotype-typin/gekjidmkhehamonaimcibhnaecakojlm)
5 |
6 | [(Alternate) Tampermonkey install](https://github.com/ultratype/UltraTypeBot/raw/master/UltraType.user.js)
7 |
8 |
9 | UltraType is a fast, easy to use bot for NitroType.com. UltraType provides unique features that no other bot has implemented, such as customizable WPM / accuracy, and an API to write your own features to the bot, and NitroType itself.
10 |
11 | # How do I install UltraType?
12 |
13 | You can install the Chrome extension [by clicking here](https://chrome.google.com/webstore/detail/fpopdcoojgeikobdihofjflpngpcbiob/)
14 | However, there are a few alternative ways to install UltraType:
15 |
16 | #### Installing on Tampermonkey (the easy way)
17 | To install UltraType on Tampermonkey, [install Tampermonkey from the Chrome Webstore](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) if you haven't yet. After Tampermonkey has been installed, [click here to install UltraType](https://github.com/ultratype/UltraTypeBot/raw/master/UltraType.user.js).
18 | #### Installing as an unpacked extension (the slightly more difficult way)
19 | If you are a developer, or Tampermonkey isn't working properly for you, installation can be done by loading the unpacked Chrome extension. Follow these steps to install the unpacked extension:
20 | - In the top right of this page, click the "Clone or download" button, then click "Download ZIP"
21 | - After the download has finished, open the options menu by clicking the icon in the top right of your Chrome window, then go to More tools -> Extensions.
22 | - Check the "Developer Mode" check box in the top right of the page.
23 | - Click the "Load unpacked extension" button in the top left, and then select the ZIP you downloaded from the file selector.
24 | - Installation is finished! Visit https://www.nitrotype.com/race/ to try it out.
25 |
26 | # The UltraType API
27 | UltraType comes with an API to build add-ons and simple userscripts with. Information on the API can be located in the `api/` directory.
28 |
29 | # Directory Roadmap
30 | `dataServer` - The source for the data server, written in C++ using [cpp-httplib](https://github.com/yhirose/cpp-httplib).
31 | `OUT` - All the files executed within the context of the NitroType session.
32 | `ico` - All of the UltraType icons.
33 | `popup` - The source for the extension popup.
34 | `api` - Examples and documentation regarding the UltraType API
35 |
36 | #### Disclaimer
37 | NitroType.com is a registered trademark owned by learning.com, and associates. UltraType is not related to, nor does it claim to be apart of said trademark.
38 |
--------------------------------------------------------------------------------
/UltraType.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name UltraType NitroType bot
3 | // @version 2.4.0
4 | // @downloadURL https://rawgit.com/ultratype/UltraTypeBot/master/UltraType.user.js
5 | // @updateURL https://rawgit.com/ultratype/UltraTypeBot/master/UltraType.user.js
6 | // @description A fast, easy to use bot for NitroType.com
7 | // @author UltraType
8 | // @match https://www.nitrotype.com/race/*
9 | // @match https://www.nitrotype.com/race
10 | // @match http://www.nitrotype.com/race
11 | // @match http://www.nitrotype.com/race/*
12 | // @run-at document-start
13 | // @grant GM_xmlhttpRequest
14 | // ==/UserScript==
15 | (function() {
16 | "use strict";
17 | var OUT = "https://rawgit.com/ultratype/UltraTypeBot/master/OUT/OUT.js";
18 | var OUT_SCRIPT = "\n";
19 |
20 | // Completely halt the loading of the window, to prevent the page from being loaded more than once
21 | window.stop();
22 | document.documentElement.innerHTML = null;
23 |
24 | // Request for the current document
25 | GM_xmlhttpRequest({
26 | method: "GET",
27 | url: window.location.href,
28 | onload: function(e) {
29 | // Write a modified page to the document
30 | var doc = e.responseText;
31 | document.open();
32 | document.write(OUT_SCRIPT + doc);
33 | document.close();
34 | // The extension script will now be loaded onto the document
35 | }
36 | })
37 | })();
--------------------------------------------------------------------------------
/api/README.md:
--------------------------------------------------------------------------------
1 | # The UltraType API
2 |
3 | The UltraType API features methods and events to interact with UltraType, and the lower level NitroType api with a high level interface.
4 |
5 | # I want some examples!
6 | - You can view an example of how to make a simple turbo mode mod with TamperMonkey in `turbo.user.js`.
--------------------------------------------------------------------------------
/api/turbo.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name UltraType Turbo Mode
3 | // @version 1
4 | // @description Instantly wins any NitroType race without fail
5 | // @author You
6 | // @match https://www.nitrotype.com/race
7 | // @grant unsafeWindow
8 | // @run-at document-start
9 | // ==/UserScript==
10 |
11 | // TO USE THIS SAMPLE SCRIPT, PASTE IT IN THE TAMPERMONKEY ADD SCRIPT BOX.
12 |
13 | function start() {
14 | // Stop UltraType from running, but allow the API to still be used
15 | UltraTypeCore.stopFromRunning();
16 | UltraTypeCore.on('raceStart', () => { // Detect when the race has started
17 | console.log('Race has started, activating turbo mode!');
18 | UltraTypeCore.turbo(); // Activate turbo mode
19 | }).on('raceFinish', () => { // Detect when the race has finished
20 | // Auto refresh the page
21 | console.log('Race has finished, reloading!');
22 | location.reload();
23 | });
24 | }
25 | (function() {
26 | 'use strict';
27 | // Loop until UltraType has preloaded, then fire the start() function
28 | setInterval(() => {
29 | if (unsafeWindow["UltraTypeCore"]) {
30 | start();
31 | clearInterval(this);
32 | }
33 | }, 100);
34 | })();
--------------------------------------------------------------------------------
/api/turboplus.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name UltraType Aggresive Turbo Mode
3 | // @version 1
4 | // @description Instantly wins any NitroType race without fail, extremely quickly. REQUIRES THE ULTRATYPE CHROME EXTENSION
5 | // @author UltraTypeBot
6 | // @match https://www.nitrotype.com/race/*
7 | // @grant unsafeWindow
8 | // @run-at document-start
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 | function start() {
14 | UltraTypeCore.on('raceStart', () => {
15 | setTimeout(() => {
16 | // Send an insanely mutated type packet that instantly wins the race
17 | UltraTypeCore.sendTypePacket(true, 99999);
18 | }, 100);
19 | }).on('raceFinish', () => {
20 | // Auto refresh the page
21 | setTimeout(location.reload.bind(window.location), 2);
22 | });
23 | }
24 | setInterval(() => {
25 | if (unsafeWindow["UltraTypeCore"]) {
26 | start(); // Call when UltraType is loaded
27 | clearInterval(this);
28 | return;
29 | }
30 | }, 100);
31 | })();
--------------------------------------------------------------------------------
/dataServer/Makefile:
--------------------------------------------------------------------------------
1 | SRC=$(wildcard src/*.cpp src/*.cc)
2 | OUT=./out
3 | CC=clang++
4 | OPTS=-pthread -lpthread -std=c++11 -lssl -ggdb
5 | PORT=8283
6 | THISF := $(lastword $(MAKEFILE_LIST))))
7 |
8 | default:
9 | $(CC) $(SRC) $(OPTS) -o $(OUT)
10 | debug:
11 | make default
12 | echo "Starting GDB...\n"
13 | gdb $(OUT)
14 | clean:
15 | rm -f $(OUT)
16 | run:
17 | make default
18 | $(OUT) $(PORT)
--------------------------------------------------------------------------------
/dataServer/src/httplib.h:
--------------------------------------------------------------------------------
1 | //
2 | // httplib.h
3 | //
4 | // Copyright (c) 2017 Yuji Hirose. All rights reserved.
5 | // The Boost Software License 1.0
6 | //
7 |
8 | #ifndef _CPPHTTPLIB_HTTPLIB_H_
9 | #define _CPPHTTPLIB_HTTPLIB_H_
10 |
11 | #ifdef _MSC_VER
12 | #define _CRT_SECURE_NO_WARNINGS
13 | #define _CRT_NONSTDC_NO_DEPRECATE
14 |
15 | #ifndef SO_SYNCHRONOUS_NONALERT
16 | #define SO_SYNCHRONOUS_NONALERT 0x20
17 | #endif
18 | #ifndef SO_OPENTYPE
19 | #define SO_OPENTYPE 0x7008
20 | #endif
21 | #if (_MSC_VER < 1900)
22 | #define snprintf _snprintf_s
23 | #endif
24 |
25 | #define S_ISREG(m) (((m)&S_IFREG)==S_IFREG)
26 | #define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR)
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #undef min
34 | #undef max
35 |
36 | typedef SOCKET socket_t;
37 | #else
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 |
46 | typedef int socket_t;
47 | #endif
48 |
49 | #include
50 | #include
51 | #include