Moomoo.io Multibox Cheat (30+ bots) as well as Ultra Fast Autoheal
// ==UserScript==
// @name 🌙 Moonlight Client (MOOMOO.IO) 🌙
// @namespace http://tampermonkey.net/
// @version v1.3.2
// @description Moomoo.io Multibox Cheat (30+ bots) as well as Ultra Fast Autoheal
// @author freepentests
// @match *://*.moomoo.io/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=moomoo.io
// @grant none
// @run-at document-start
// @require https://update.greasyfork.org/scripts/423602/1005014/msgpack.js
// ==/UserScript==
alert('https://discord.gg/VU8t67TBKs\n\nThis script will not work unless you join our Discord server first.');
let multiboxAlts = [];
const mousePosition = {x: 0, y: 0};
//const ninetyDegreesInRadians = 1.57079633;
const upgradeOptions = {}; // when the main player upgrades at a specific age, the ID of the item the main player upgrades to will be stored here.
let placingSpikes = false;
let placingTraps = false;
let repellingAlts = false;
const updateAltsCounter = () => {
document.getElementById('altsCounter').innerText = String(multiboxAlts.length);
};
class PowSolver {
constructor() {
console.log('PowSolver initialized');
};
createToken(json, solution) {
return 'alt:' + btoa(JSON.stringify({
algorithm: "SHA-256",
challenge: json.challenge,
number: solution,
salt: json.salt,
signature: json.signature || null,
took: 15439
}));
};
async getCaptcha() {
const resp = await fetch('https://api.moomoo.io/verify');
const json = await resp.json();
return json;
};
async hash(string) {
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(string));
return new Uint8Array(hash).toHex();
};
async solveCaptcha(json) {
for (let i = 0; i < json.maxnumber; i++) {
if (await this.hash(json.salt + i) == json.challenge) {
return i;
};
};
};
async generateAltchaToken() {
const json = await this.getCaptcha();
const solution = await this.solveCaptcha(json);
return this.createToken(json, solution);
};
};
class Input {
constructor(ws) {
this.msgpack = msgpack;
this.ws = ws;
};
sendMsg(data) {
this.ws.send(this.msgpack.encode(data));
};
sendChatMessage(message) {
this.sendMsg(['6', [message]]);
};
useItem(id) {
this.sendMsg(['z', [id, null]]);
this.sendMsg(['F', [1, null]]);
this.sendMsg(['F', [0, null]]);
this.sendMsg(['z', [document.ws.player.entity.weapon, true]]);
};
healPlayer(currentHealth) {
let timeout = 115;
if (currentHealth <= 60) {
timeout = 1;
};
setTimeout(() => {
this.useItem(0); // heal with apple
this.useItem(1); // heal with cookie
}, timeout);
};
moveTowardsDirection(angle) {
this.sendMsg(['9', [angle]]);
};
sendEnterWorld(name) {
this.sendMsg(['M', [{
name: name,
moofoll: true,
skin: 0
}]]);
};
joinTribe(name) {
this.sendMsg(['b', [name]]);
};
placeTrap() {
return this.useItem(15);
};
placeBoost() {
return this.useItem(16);
};
placeSpike(spikeType) {
switch (spikeType) {
case 'regular':
this.useItem(6);
break;
case 'greater':
this.useItem(7);
break;
case 'poison':
this.useItem(8);
break;
case 'spinning':
this.useItem(9);
break;
};
};
};
class Player {
constructor(ws) {
this.ws = ws;
this.input = new Input(this.ws);
this.autoheal = true;
this.entity = {
id: null,
health: 100,
knownPlayers: [],
position: {
x: 0,
y: 0
},
aimingYaw: 0,
object: -1,
weapon: 0,
clan: null,
isLeader: 0,
hat: 0,
accessory: 0
};
this.fullyUpgraded = true;
this.ws.addEventListener('message', this.handleMessage.bind(this));
};
handleMessage(msg) {
const data = msgpack.decode(msg.data);
switch (data[0]) {
case 'C': // C means id. it is a message received from the server that tells you what your entity ID is.
this.entity.id = data[1][0];
break;
case 'O': // O means health change. it follows this format: ['O', [entityId, health]]
// autoheal causes u to get the clown hat very quickly, but who cares when you have 30 alts as well as op insta?
if (data[1][0] == this.entity.id) this.entity.health = data[1][1];
if (data[1][0] == this.entity.id && this.autoheal && data[1][1] < 100) this.input.healPlayer(this.entity.health);
break;
case 'a':
// credits to the creator of x-RedDragon client for most of this
this.entity.knownPlayers = [];
var playerInfos = data[1][0];
for (let j = 0; j < playerInfos.length; j += 13) {
const playerInfo = playerInfos.slice(j, j + 13);
if (playerInfo[0] == this.entity.id) {
this.entity.position.x = playerInfo[1];
this.entity.position.y = playerInfo[2];
this.entity.aimingYaw = playerInfo[3];
this.entity.object = playerInfo[4];
this.entity.weapon = playerInfo[5];
this.entity.clan = playerInfo[7];
this.entity.isLeader = playerInfo[8];
this.entity.hat = playerInfo[9];
this.entity.accessory = playerInfo[10];
} else {
this.entity.knownPlayers.push({
id: playerInfo[0],
position: {
x: playerInfo[1],
y: playerInfo[2],
},
aimingYaw: playerInfo[3],
object: playerInfo[4],
weapon: playerInfo[5],
clan: playerInfo[7],
isLeader: playerInfo[8],
hat: playerInfo[9],
accessory: playerInfo[10]
});
};
};
break;
case 'U':
this.upgradeAge = ((data[1][0] + data[1][1]) - data[1][0]);
if (data[1][0] == 0) {
this.fullyUpgraded = true;
} else {
this.fullyUpgraded = false;
};
break;
};
};
};
class Bot {
constructor(name, serverUrl) {
this.powSolver = new PowSolver();
this.name = name;
this.age = 1;
this.powSolver.generateAltchaToken().then((token) => {
this.ws = new WebSocket(document.ws.url.split('?token=')[0] + `?token=${token}`);
this.ws.binaryType = 'arraybuffer';
this.ws.player = new Player(this.ws);
this.ws.addEventListener('message', this.handleMessage.bind(this));
setInterval(() => {
this.ws.player.input.sendChatMessage('Moonlight Client! Age: ' + this.age);
if (!this.ws.player.fullyUpgraded) {
try {
this.ws.player.input.sendMsg(['H', [upgradeOptions[this.ws.player.upgradeAge]]]);
} catch(e) {
; // do nothing
};
};
}, 1000);
});
};
handleMessage(msg) {
const data = this.ws.player.input.msgpack.decode(msg.data);
switch (data[0]) {
case 'io-init':
multiboxAlts.push(this.ws);
this.ws.player.input.sendEnterWorld(this.name);
setInterval(() => { // attempt to respawn every second and join clan
this.ws.player.input.sendEnterWorld(this.name);
this.ws.player.input.joinTribe(document.ws.player.entity.clan);
}, 1000);
updateAltsCounter();
console.log(multiboxAlts.length);
break;
case 'a':
var mouseXWorld = (document.ws.player.entity.position.x - this.ws.player.entity.position.x) + (mousePosition.x - (window.innerWidth / 2)) * (1+(1/3));
var mouseYWorld = (document.ws.player.entity.position.y - this.ws.player.entity.position.y) + (mousePosition.y - (window.innerHeight / 2)) * (1+(1/3));
var dirToMove;
if (repellingAlts) {
dirToMove = Math.atan2(mouseYWorld, mouseXWorld) - 3.14159; // 3.14159 is 180 degrees in radians
} else {
dirToMove = Math.atan2(mouseYWorld, mouseXWorld);
};
this.ws.player.input.sendMsg(['D', [dirToMove]]); // face in the direction of the mouse
this.ws.player.input.moveTowardsDirection(dirToMove);
break;
case 'U':
this.age = (data[1][0] + data[1][1]) - 1;
};
};
};
const init = () => {
document.getElementById('promoImgHolder').remove(); // remove the promo
for (let i = 0; i < document.getElementsByClassName('adsbygoogle').length; i++) {
document.getElementsByClassName('adsbygoogle')[0].remove();
};
document.getElementById('gameName').innerText = 'Moonlight'
document.getElementById('gameName').style = 'color: #f00';
document.getElementById('mainMenu').style = 'background-color: #000';
document.getElementById('diedText').innerText = 'GET REVENGE';
document.getElementById('diedText').style = 'color: #f00; background-color: #000;';
const altCounter = document.createElement('h2');
altCounter.style = 'text-align: center; font-size: 25px; position: fixed; top: 10px; left: 50%; transform: translateX(-50%);';
altCounter.innerHTML = 'Alts: <span id="altsCounter">0</span>';
document.getElementById('gameUI').appendChild(altCounter);
document.getElementById('touch-controls-fullscreen').addEventListener('mousemove', (e) => {
mousePosition.x = e.clientX;
mousePosition.y = e.clientY;
});
// store the main player's websocket in the document
const originalWebSocket = WebSocket;
const wsInterceptor = {
construct(target, args) {
const ws = new originalWebSocket(...args);
document.ws = ws;
console.log('captured ws');
window.WebSocket = originalWebSocket; // this sets the websocket constructor back to normal
document.ws.player = new Player(document.ws);
window.addEventListener('keyup', (e) => {
if (e.target.tagName === 'INPUT') {
return;
};
if (e.key === 'l') {
new Bot('gg/VU8t67TBKs')
};
if (e.key === ',') {
multiboxAlts.forEach((sock) => {
sock.close();
})
multiboxAlts = [];
updateAltsCounter();
};
if (e.key === 'v') {
placingSpikes = false;
};
if (e.key === 'f') {
placingTraps = false;
};
if (e.key === '.') {
repellingAlts = false;
};
});
window.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT') {
return;
};
if (e.key === 'f') {
placingTraps = true;
};
if (e.key === 'v') {
placingSpikes = true;
};
if (e.key === '.') {
repellingAlts = true;
};
});
setInterval(() => {
if (placingSpikes) {
if (upgradeOptions[5] == 23) {
document.ws.player.input.placeSpike('greater');
} else {
document.ws.player.input.placeSpike('regular');
};
if (upgradeOptions[9] == 24) {
document.ws.player.input.placeSpike('poison');
};
if (upgradeOptions[9] == 25) {
document.ws.player.input.placeSpike('spinning');
};
};
if (placingTraps) {
if (upgradeOptions[4] == 31) {
document.ws.player.input.placeTrap();
} else {
document.ws.player.input.placeBoost();
};
};
}, 50);
let originalSend = document.ws.send.bind(document.ws);
document.ws.send = (msg) => {
if (msgpack.decode(msg)[0] === 'F' || msgpack.decode(msg)[0] === 'z' || msgpack.decode(msg)[0] === 'c') {
multiboxAlts.forEach((sock) => {
sock.send(msg);
});
};
if (msgpack.decode(msg)[0] === 'H') {
upgradeOptions[document.ws.player.upgradeAge] = msgpack.decode(msg)[1][0];
};
console.log(msgpack.decode(msg));
originalSend(msg);
};
return ws;
}
};
window.WebSocket = new Proxy(originalWebSocket, wsInterceptor);
};
let waitForGameName = setInterval(() => {
if (document.getElementById('gameName')) {
clearInterval(waitForGameName);
return init();
};
}, 100);