// ==UserScript==
// @name Diep.io Tool
// @description made with much love.
// @version 4.2.9
// @author Cazka#9552
// @namespace *://diep.io/*
// @match *://diep.io/*
// @grant GM_info
// @grant GM_addStyle
// ==/UserScript==
'use strict';
/*
* C O N S T A N T S
*/
//it was very fun. thanks for still caring about the game.
const DTTOKEN = 'Cazka_wants_to_be_in_the_source_code';
const UPDATE = {
SERVER_PARTY: 0,
NAME: 1,
GAMEMODE: 2,
};
const COMMAND = {
JOIN_BOTS: 0,
MULTIBOX: 1,
AFK: 2,
CLUMP: 3,
SPINBOT: 4,
PUSHBOT: 5,
};
const SERVERS = ['wss://amsterdam.s.dieptool.com', 'wss://la.s.dieptool.com/', 'wss://miami.s.dieptool.com', 'wss://singapore.s.dieptool.com'];
/*
* C L A S S E S
*/
class PowWorker {
constructor() {
this.worker = new Worker(this._getWorkerPath());
this.nextJobId = 0;
this.busy = false;
this.workerCallbacks = {};
this.worker.onmessage = (e) => this._onmessage(e);
}
_getWorkerPath() {
return (
window.location.protocol +
'//' +
window.location.hostname +
window.location.pathname +
'pow_worker.js'
);
}
_onmessage(e) {
const data = e.data;
const id = data[0];
this.workerCallbacks[id](data.slice(1));
}
solve(prefix, difficulty, cb) {
const id = this.nextJobId++;
this.worker.postMessage([id, 'solve', prefix, difficulty]);
this.workerCallbacks[id] = cb;
}
terminate() {
this.worker.terminate();
}
}
/*
* This is from @cx88 with little modifications made by me.
* https://github.com/cx88/diepssect/blob/master/diep-bot/coder.js
*/
const convo = new ArrayBuffer(4);
const u8 = new Uint8Array(convo);
const u16 = new Uint16Array(convo);
const u32 = new Uint32Array(convo);
const float = new Float32Array(convo);
const endianSwap = (val) =>
((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val >> 8) & 0xff00) | ((val >> 24) & 0xff);
class Reader {
constructor(content) {
this.at = 0;
this.buffer = new Uint8Array(content);
}
u8() {
const out = this.buffer[this.at++];
this.assertNotOOB();
return out;
}
u16() {
u8.set(this.buffer.subarray(this.at, (this.at += 2)));
this.assertNotOOB();
return u16[0];
}
u32() {
u8.set(this.buffer.subarray(this.at, (this.at += 4)));
this.assertNotOOB();
return u32[0];
}
float() {
u8.set(this.buffer.subarray(this.at, (this.at += 4)));
this.assertNotOOB();
return float[0];
}
vu() {
let out = 0;
let at = 0;
while (this.buffer[this.at] & 0x80) {
out |= (this.buffer[this.at++] & 0x7f) << at;
at += 7;
}
out |= this.buffer[this.at++] << at;
this.assertNotOOB();
return out;
}
vi() {
let out = this.vu();
const sign = out & 1;
out >>= 1;
if (sign) out = ~out;
this.assertNotOOB();
return out;
}
vf() {
u32[0] = endianSwap(this.vi());
this.assertNotOOB();
return float[0];
}
string() {
let out;
const at = this.at;
while (this.buffer[this.at]) this.at++;
out = new TextDecoder().decode(this.buffer.subarray(at, this.at++));
this.assertNotOOB();
return out;
}
buf() {
let out;
const length = this.vu();
out = this.buffer.slice(this.at, this.at + length);
this.at += length;
this.assertNotOOB();
return out;
}
flush() {
const slice = this.buffer.slice(this.at);
this.at += slice.length;
return slice;
}
isEOF() {
return this.at === this.buffer.byteLength;
}
assertNotOOB() {
if (this.at > this.buffer.byteLength) {
throw new Error(`Error at ${this.at}: Out of Bounds.\n${this.debugStringFullBuffer()}`);
}
}
debugStringFullBuffer() {
const s = this.buffer.reduce((acc, x, i) => {
x = x.toString(16).padStart(2, 0).toUpperCase();
if (this.at === i) x = `>${x}`;
if (i % 16 === 0) acc = `${acc}\n${x}`;
else acc = `${acc} ${x}`;
return acc;
}, '');
return s.trim();
}
}
class Writer {
constructor() {
this.length = 0;
this.buffer = new Uint8Array(16384);
}
u8(num) {
this.buffer[this.length] = num;
this.length += 1;
return this;
}
u16(num) {
u16[0] = num;
this.buffer.set(u8, this.length);
this.length += 2;
return this;
}
u32(num) {
u32[0] = num;
this.buffer.set(u8, this.length);
this.length += 4;
return this;
}
float(num) {
float[0] = num;
this.buffer.set(u8, this.length);
this.length += 4;
return this;
}
vu(num) {
do {
let part = num;
num >>>= 7;
if (num) part |= 0x80;
this.buffer[this.length++] = part;
} while (num);
return this;
}
vi(num) {
const sign = (num & 0x80000000) >>> 31;
if (sign) num = ~num;
const part = (num << 1) | sign;
this.vu(part);
return this;
}
vf(num) {
float[0] = num;
this.vi(endianSwap(u32[0]));
return this;
}
string(str) {
const bytes = new TextEncoder().encode(str);
this.buffer.set(bytes, this.length);
this.length += bytes.length;
this.buffer[this.length++] = 0;
return this;
}
buf(buf) {
const length = buf.byteLength;
this.vu(length);
this.buffer.set(buf, this.length);
this.length += length;
return this;
}
out() {
return this.buffer.slice(0, this.length);
}
dump() {
return Array.from(this.buffer.subarray(0, this.length))
.map((r) => r.toString(16).padStart(2, 0))
.join(' ');
}
}
class DTSocket {
constructor() {
this._socket;
this._lastPing = Date.now();
this._pow_workers = [...Array(8)].map((x) => new PowWorker());
this.accepted = false;
}
get region() {
return new URL(this._socket.url).host.split('.')[0];
}
connect(url) {
this._socket = new WebSocket(url);
this._socket.binaryType = 'arraybuffer';
this._socket.onopen = () => {
this._onopen();
if (this.onopen) this.onopen();
};
this._socket.onmessage = (event) => {
this._onmessage(event);
if (this.onmessage) this.onmessage(event);
};
this._socket.onclose = (event) => {
this._onclose(event);
if (this.onclose) this.onclose(event);
};
}
_onopen() {
this.send('heartbeat');
this.send('initial', {
version: GM_info.script.version,
authToken: window.localStorage[DTTOKEN],
});
}
_onmessage(event) {
const reader = new Reader(event.data);
switch (reader.vu()) {
case 0: {
const authToken = reader.string();
window.localStorage[DTTOKEN] = authToken;
break;
}
case 1: {
const buffer = reader.buf();
if (this.oncustom_diep_serverbound) this.oncustom_diep_serverbound(buffer);
break;
}
case 2: {
const buffer = reader.buf();
WebSocket_onmessage.call(gWebSocket, { data: buffer });
break;
}
case 3: {
this.accepted = true;
if (this.onaccept) this.onaccept();
break;
}
case 4: {
if (this.ondeny) this.ondeny();
break;
}
case 5: {
const latency = Date.now() - this._lastPing;
this.send('heartbeat');
this._lastPing = Date.now();
if (this.onlatency) this.onlatency(latency);
break;
}
case 6: {
const id = reader.vu();
const difficulty = reader.vu();
const prefix = reader.string();
console.log('got pow', id, difficulty, prefix);
//look for a worker thats not busy, but definetly take the last one
for (let i = 0; i < this._pow_workers.length; i++) {
if (!this._pow_workers[i].busy || i + 1 === this._pow_workers.length) {
this._pow_workers[i].busy = true;
this._pow_workers[i].solve(prefix, difficulty, (result) => {
this._pow_workers[i].busy = false;
console.log('sent pow', id, difficulty, prefix);
this.send('pow_result', { id, result });
});
return;
}
}
break;
}
case 7: {
const message = reader.string();
if (this.onalert) this.onalert(message);
break;
}
}
}
_onclose(event) {}
send(type, content) {
if (this.isClosed()) return;
const writer = new Writer();
switch (type) {
case 'initial': {
const { version, authToken } = content;
writer.vu(0);
writer.string(version);
writer.string(authToken);
break;
}
case 'diep_serverbound': {
const { buffer } = content;
writer.vu(1);
writer.buf(buffer);
break;
}
case 'diep_clientbound': {
const { buffer } = content;
writer.vu(2);
writer.buf(buffer);
break;
}
case 'update': {
const { id, value } = content;
writer.vu(3);
writer.vu(id);
writer.string(value);
break;
}
case 'command': {
const { id, value } = content;
writer.vu(4);
writer.vu(id);
writer.vu(value);
break;
}
case 'heartbeat': {
writer.vu(5);
break;
}
case 'pow_result': {
const { id, result } = content;
writer.vu(6);
writer.vu(id);
writer.string(result);
break;
}
default:
console.error('unrecognized packet type:', type);
}
WebSocket_send.call(this._socket, writer.out());
}
isClosed() {
if (this._socket) return this._socket.readyState !== WebSocket.OPEN;
return true;
}
static findServerPreference(urls) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => resolve(), 5000);
for (let i = 0; i < urls.length; i++) {
const ws = new WebSocket(urls[i]);
ws.onopen = function () {
clearTimeout(timeout);
ws.close();
resolve(ws.url);
};
}
});
}
}
/*
* H E L P E R F U N C T I O N S
*/
function UTF8ToString(utf8 = '') {
return decodeURI(
utf8
.split('')
.map((c) => `%${c.charCodeAt(0).toString(16)}`)
.join('')
);
}
function updateInformation(type, data) {
const updates = {
[UPDATE.NAME](data) {
let name = new TextDecoder().decode(data.slice(1, data.length - 1));
return name;
},
[UPDATE.SERVER_PARTY]({ url, party }) {
let [userURL, userParty] = gUserInfo[UPDATE.SERVER_PARTY].split(':');
if (url) {
userURL = url.match(/(?<=wss:\/\/).[0-9a-z]{3}(?=.s.m28n.net\/)/)[0];
userParty = '';
}
if (party) {
function parseParty(data) {
let party = '';
for (let i = 1; i < data.byteLength; i++) {
let byte = data[i].toString(16).split('');
if (byte.length === 1) {
party += byte[0] + '0';
} else {
party += byte[1] + byte[0];
}
}
return party;
}
userParty = parseParty(party);
}
return `${userURL}:${userParty}`;
},
[UPDATE.GAMEMODE](data) {
let gamemode = new TextDecoder().decode(data.slice(1, data.length)).split('\u0000')[0];
return gamemode;
},
};
gUserInfo[type] = updates[type](data);
dtSocket.send('update', { id: type, value: gUserInfo[type] });
}
function addButton(parent, text, onclick, keyCode) {
let button = document.createElement('button');
parent.appendChild(button);
button.innerHTML = text;
button.keyCode = keyCode;
button.onclick = onclick;
button.style['background-color'] = guiColors[guiButtons.length % guiColors.length];
guiButtons.push(button);
return button;
}
function enableGui() {
guiBody.style.display = 'block';
}
function disableGui() {
guiBody.style.display = 'none';
}
async function onBtnHead() {
if (dtSocket.isClosed()) {
if (!window.localStorage[DTTOKEN])
window.location.href =
'https://discord.com/api/oauth2/authorize?client_id=737680273860329553&redirect_uri=https%3A%2F%2Fdiep.io&response_type=code&scope=identify&prompt=none';
else {
const url = await DTSocket.findServerPreference(SERVERS);
if (!url) {
console.log('Please try again later.');
btnHead.innerHTML = 'Please try again later';
return;
}
this.innerHTML = 'Connecting...';
dtSocket.connect(url);
}
} else if (dtSocket.accepted) {
if (guiBody.style.display === 'block') disableGui();
else enableGui();
}
}
function onBtnJoinBots() {
dtSocket.send('command', { id: COMMAND.JOIN_BOTS, value: window.prompt('Please enter the amount of bots', 5) });
}
function onBtnMultibox() {
this.active = !this.active;
if (this.active) {
this.innerHTML = 'Multiboxing: ON';
dtSocket.send('command', { id: COMMAND.MULTIBOX, value: 1 });
} else {
this.innerHTML = 'Multiboxing: OFF';
dtSocket.send('command', { id: COMMAND.MULTIBOX, value: 0 });
}
}
function onBtnAfk() {
this.active = !this.active;
if (this.active) {
gAfk = true;
this.innerHTML = 'AFK: ON';
dtSocket.send('command', { id: COMMAND.AFK, value: 1 });
} else {
gAfk = false;
this.innerHTML = 'AFK: OFF';
dtSocket.send('command', { id: COMMAND.AFK, value: 0 });
}
}
function onBtnClump() {
this.active = !this.active;
if (this.active) {
this.innerHTML = 'Clump: ON';
dtSocket.send('command', { id: COMMAND.CLUMP, value: 1 });
} else {
this.innerHTML = 'Clump: OFF';
dtSocket.send('command', { id: COMMAND.CLUMP, value: 0 });
}
}
function onBtnSpinbot() {
this.active = !this.active;
if (this.active) {
gSpinbot = true;
this.innerHTML = 'Spinbot: ON';
dtSocket.send('command', { id: COMMAND.SPINBOT, value: 1 });
} else {
gSpinbot = false;
this.innerHTML = 'Spinbot: OFF';
dtSocket.send('command', { id: COMMAND.SPINBOT, value: 0 });
}
}
function onBtnPushbot() {
this.active = !this.active;
if (this.active) {
this.innerHTML = 'Pushbot: ON';
dtSocket.send('command', { id: COMMAND.PUSHBOT, value: 1 });
} else {
this.innerHTML = 'Pushbot: OFF';
dtSocket.send('command', { id: COMMAND.PUSHBOT, value: 0 });
}
}
function onBtnDiscord() {
window.open('https://discord.gg/8saC9pq');
}
function onBtnPatreon() {
window.open('https://www.patreon.com/dieptool');
}
function _send(data) {
dtSocket.send('diep_serverbound', { buffer: data });
if (data[0] === 2) updateInformation(UPDATE.NAME, data);
if (data[0] === 10) {
const d = new Int8Array(data);
const time = Date.now() - this.lastPow;
setTimeout(() => WebSocket_send.call(this, d), 5000 - time);
return;
}
if (data[0] === 1) {
if (gSpinbot) {
const reader = new Reader(data);
const id = reader.vu();
if (reader.vu() & 1 && !gAfk) {
gCSBisBlocked = true;
} else {
gCSBisBlocked = false;
return;
}
}
if (gAfk) return;
}
if (data[0] === 7) return;
WebSocket_send.call(this, data);
}
function _onmessage(event) {
const data = new Uint8Array(event.data);
dtSocket.send('diep_clientbound', { buffer: data });
if (data[0] === 4) updateInformation(UPDATE.GAMEMODE, data);
else if (data[0] === 6) updateInformation(UPDATE.SERVER_PARTY, { party: data });
else if (data[0] === 10) gReadyToInit = true;
else if (data[0] === 11) this.lastPow = Date.now();
WebSocket_onmessage.call(this, event);
}
const WebSocket_send = window.WebSocket.prototype.send;
let WebSocket_onmessage;
(function hijackWebSocket() {
const wsInstances = new Set();
window.WebSocket.prototype.send = function (data) {
if (this.url.match(/s.m28n.net/) && data instanceof Int8Array) {
_send.call(this, data);
if (!wsInstances.has(this)) {
wsInstances.add(this);
gWebSocket = this;
if (updateInformation) updateInformation(UPDATE.SERVER_PARTY, { url: this.url });
WebSocket_onmessage = this.onmessage;
this.onmessage = function (event) {
_onmessage.call(this, event);
};
}
} else WebSocket_send.call(this, data);
};
})();
(function enableShortcuts() {
document.addEventListener('keydown', (event) => {
if (document.getElementById('textInputContainer').style.display === 'block') return;
guiButtons.forEach((button) => {
if (button.keyCode === event.code) button.onclick();
});
});
})();
(function freezeMouse() {
const canvas = document.getElementById('canvas');
canvas._onmousemove = canvas.onmousemove;
canvas.onmousemove = function (e) {
if (!gFreezeMouse) this._onmousemove(e);
};
})();
// we dont need this anyway
/*(function removeAnnoyingAlert() {
const _alert = unsafeWindow.alert;
unsafeWindow.alert = function (msg) {
if (msg.startsWith('Your browser version')) return;
_alert(msg);
};
})();*/
(function overrideButtonLength(){
const Document_getElementsByTagName = Document.prototype.getElementsByTagName;
Document.prototype.getElementsByTagName = function(tagName){
if(tagName === 'button') return [];
return Document_getElementsByTagName.call(document, tagName);
}
})();
(function authCallback() {
function parseQuery(q) {
const parsed = {};
q.substring(1)
.split('&')
.forEach((e) => {
e = e.split('=');
parsed[e[0]] = e[1];
});
return parsed;
}
const query = parseQuery(window.location.search);
if (query.code) {
window.localStorage[DTTOKEN] = query.code;
window.history.pushState(null, 'diep.io', 'https://diep.io/');
} else if (query.error) {
window.localStorage[DTTOKEN] = '';
window.history.pushState(null, 'diep.io', 'https://diep.io/');
}
})();
/*
* M A I N
*/
const gUserInfo = {
[UPDATE.NAME]: UTF8ToString(window.localStorage.name),
[UPDATE.SERVER_PARTY]: '',
[UPDATE.GAMEMODE]: window.localStorage.gamemode,
};
let gWebSocket;
let gAfk = false;
let gFreezeMouse = false;
let gSpinbot = false;
let gCSBisBlocked = false;
let gReadyToInit = false;
let dtSocket = new DTSocket();
/*
* G U I
*/
GM_addStyle(`
.gui-dieptool button {
display: block;
font-family: Ubuntu;
color: #fff;
text-shadow: -.1em -.1em 0 #000, 0 -.1em 0 #000, .1em -.1em 0 #000, .1em 0 0 #000, .1em .1em 0 #000, 0 .1em 0 #000, -.1em .1em 0 #000, -.1em 0 0 #000;
opacity: .8;
border: 0;
padding: .3em .5em;
width: 100%;
transition: all .15s
}
.gui-dieptool {
top: 0;
left: 0;
position: absolute
}
.gui-dieptool button:active:not([disabled]) {
filter: brightness(.9)
}
.gui-dieptool button:hover:not([disabled]):not(:active) {
filter: brightness(1.1)
}`);
const guiColors = [
'#E8B18A',
'#E666EA',
'#9566EA',
'#6690EA',
'#E7D063',
'#EA6666',
'#92EA66',
'#66EAE6',
];
const guiButtons = [];
const guiDiepTool = document.createElement('div');
guiDiepTool.className = 'gui-dieptool';
document.body.appendChild(guiDiepTool);
const guiHead = document.createElement('div');
guiDiepTool.appendChild(guiHead);
const guiBody = document.createElement('div');
guiDiepTool.appendChild(guiBody);
//add buttons
let btnHead;
if (window.localStorage[DTTOKEN]) {
btnHead = addButton(guiHead, 'Connecting...', onBtnHead);
addButton(guiBody, 'Join Bots', onBtnJoinBots, 'KeyJ');
addButton(guiBody, 'Multiboxing: OFF', onBtnMultibox, 'KeyF');
addButton(guiBody, 'Clump: OFF', onBtnClump, 'KeyX');
addButton(guiBody, 'AFK: OFF', onBtnAfk, 'KeyQ');
addButton(guiBody, 'Spinbot OFF', onBtnSpinbot);
addButton(guiBody, 'Pushbot OFF', onBtnPushbot);
disableGui();
} else {
btnHead = addButton(guiHead, 'Login to DiepTool', onBtnHead);
addButton(guiBody, 'Discord Server', onBtnDiscord);
addButton(guiBody, 'Patreon', onBtnPatreon);
}
(async function initializeSocket() {
const url = await DTSocket.findServerPreference(SERVERS);
console.log('found server preference', url);
if (!url) {
console.log('Please try again later.');
btnHead.innerHTML = 'Please try again later';
return;
}
dtSocket.onclose = function () {
console.log('disconnected from DT server');
btnHead.innerHTML = 'Disconnected';
disableGui();
};
dtSocket.onaccept = function () {
this.onlatency = (latency) => (btnHead.innerHTML = `${latency} ms ${this.region} DiepTool`);
const int = setInterval(() => {
if (gReadyToInit) {
clearInterval(int);
for (let [key, value] of Object.entries(gUserInfo))
this.send('update', { id: key, value });
}
}, 100);
};
dtSocket.ondeny = function () {
window.localStorage[DTTOKEN] = '';
setTimeout(() => window.location.reload(), 4000);
};
dtSocket.onalert = function (message) {
console.log('DiepTool alert:', message);
const btnAlert = addButton(guiHead, message);
setTimeout(() => btnAlert.parentNode.removeChild(btnAlert), 4000);
if(message.includes('Free')) addButton(guiBody, 'Patreon', onBtnPatreon);
};
dtSocket.oncustom_diep_serverbound = function (data) {
if (!gCSBisBlocked) WebSocket_send.call(gWebSocket, data);
};
if (window.localStorage[DTTOKEN]) dtSocket.connect(url);
})();