Greasy Fork

.io & Agar.io Clone Bots - 2022!

The best open sourced & proxyless bots for .io & agar.io clone games.

目前为 2022-01-02 提交的版本。查看 最新版本

// ==UserScript==
// @name         .io & Agar.io Clone Bots - 2022!
// @namespace    https://greasyfork.org/en/scripts/433283-agar-io-clone-bots-and-io-bots-2021
// @version      22
// @description  The best open sourced & proxyless bots for .io & agar.io clone games.
// @author       Tatsuya
// @match        *.cubedot.kr/*
// @match        *.agar.cc/*
// @match        *.aquar.io/*
// @match        *.oceanar.io/*
// @match        *.agar.rip/*
// @match        *.agario.network/*
// @match        *.agario.work/*
// @match        *.agario.zafer2.com/*
// @match        *.agarabi.net/*
// @match        *.agario.boston/*
// @match        *.agar.chat/*
// @match        *.agario.nl/*
// @match        *.agariomoddedserver.com/*
// @match        *.agario.in/*
// @match        *.agario.id/*
// @match        *.agariomodded.com/*
// @match        *.bestagario.org/*
// @match        *.agariohub.cc/*
// @match        *.agar.team/*
// @match        *.agarprivateservers.org/*
// @match        *.agarprivateservers.net/*
// @match        *.agarprivateserver.com/*
// @match        *.agario.cc/*
// @match        *.easyagario.icu/*
// @run-at       document-start
// @icon         https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTJNVczs2oU6qdgJBw2ZSSe4ibVAGjaZMgWosjYzjXZU1B6Lp9MHoQ27ARzAtofWYHxz3U&usqp=CAU
// @grant        none
// ==/UserScript==

class Client {
    constructor() {
        this.serverIP = '';
        this.spawned = 0;
        this.BotAmount = 60;
        this.positioning = {
            'x': 0,
            'y': 0
        };
        this.movebuf = null;
        this.countInt = null;
        this.started = false;
        this.bots = [];
        this.gui = new GUI(this.startBots.bind(this), this.stopBots.bind(this), this.split.bind(this), this.eject.bind(this));
        this.setup();
    }

    setup() {
        alert("Thanks for using the bots. Make sure to join my discord for any updates. These bots are up to date as of Jan 1, 2022. More games coming soon, make sure to check the greasyfork page for more updates. Chatspam will be fixed and added back soon.")
        for(let i = 0; i < this.BotAmount; i++) {this.bots.push(new Bot())}
        this.UpdateCount();
        this.HookMouse();
    }

    HookMouse() {
        this.countInt = setInterval(() => {this.bots.forEach(bot => {bot.sendmouse(this.movebuf)})}, 50);
    }

    UpdateCount() {
        this.countInt = setInterval(() => {this.gui.updateCount(this.spawned, this.BotAmount)}, 1000);
    }

    split() {this.bots.forEach((bot, i) => {bot.split()})}

    eject() {this.bots.forEach((bot, i) => {bot.eject()})}

    startBots() {
        if (this.started || !this.serverIP) return;
        this.bots.forEach((bot, i) => {
            bot.connect(this.serverIP);
        });
        this.started = true;
    }

    stopBots() {
        if(!this.started) return;
        this.bots.forEach((bot, i) => {
            bot.close();
        });
        this.started = false;
    }
}

class GUI {
    constructor(start, stop, split, eject) {
        this.IDs = {
            'startButton': 'startbtn',
            'stopButton': 'stopbtn',
            'botCount': 'botAmount',
            'DiscordURL': 'discord'
        };
        this.injected = false;
        this.startBots = start;
        this.stopBots = stop;
        this.splitBots = split;
        this.ejectBots = eject;
        this.inject();
        this.setupKeys();
    }
    inject() {
        this.uiCode =
        `
        <div div="" id="mainUI" style='margin-left: 10px; top: 50%; transform: translateY(-50%); position: absolute; border: 2px solid rgb(255, 255, 255); border-radius: 2px;
        text-align: center; z-index: 999999; background-color: rgba(0, 0, 0, 0.7); font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;' > <h4 style="color: rgb(255,255,255); margin: 4px;">.io Bots!</h4>
        <div div = ""; id = "discord" style="color: white; margin: 1px; font-size: 12px; cursor: pointer; ">discord.gg/bAstbAfem9</div>
        <div><p div="" id="botAmount" style="color: rgb(50, 255, 88);">0 / 0</p></div> <div style="margin: 5px;">
        <button div="" id="startbtn" style="background-color: rgb(50, 255, 88); color: white; border: rgb(56, 211, 84); padding: 5px; border-radius: 2px;" >
        Start Bots</button ><button div="" id="stopbtn" style="margin-left: 3px; background-color: rgb(255, 66, 66); color: white; border: rgb(199, 52, 52); padding: 5px; border-radius: 2px;" > Stop Bots </button>
        </div> </div>
        `
        this.append(this.uiCode);
    }
    append(html) {
        const BOTUI = document.createElement('div');
        BOTUI.innerHTML = html;
        document.body.appendChild(BOTUI);
        this.injected = true;
        document.getElementById(this.IDs.startButton).onclick = this.startBots;
        document.getElementById(this.IDs.stopButton).onclick = this.stopBots;
        document.getElementById(this.IDs.DiscordURL).onclick =() => {window.location.href = 'https://discord.gg/bAstbAfem9'};
    }
    setupKeys() {
        window.addEventListener('keypress', (event) => {
            switch(event.key) {
                case 'q':
                    this.splitBots();
                    break;
                case 'w':
                    this.ejectBots();
                    break;
            }
        });
    }
    updateCount(spawned, max) {
        document.getElementById(this.IDs.botCount).innerText = spawned + " / " + max;
    }
}

window.addEventListener('load', () => {
    const client = new Client();
    WebSocket.prototype.realSend = WebSocket.prototype.send;
    WebSocket.prototype.send = function(pkt) {
        this.realSend(pkt);
        if(typeof pkt == 'string') return;
        if(this.url.includes('localhost')) return;
        if(pkt instanceof ArrayBuffer) pkt = new DataView(pkt);
        else if(pkt instanceof DataView) pkt = pkt;
        else pkt = new DataView(pkt.buffer);
        let offset = 0;
        switch(pkt.getUint8(0, true)) {
            case 16:
                client.positioning.x = pkt.getFloat64(1, true);
                client.positioning.y = pkt.getFloat64(9, true);
                break;
            case 5:
            case 14:
                client.movebuf = pkt.buffer;
                break;
        }
        client.serverIP = this.url;
    }
    Hooks.run(client);
});

var Hooks = {
    Client: 'function'
};

Hooks.run = (func) => {
    Hooks.Client = func;
}

class Bot {
    constructor() {
        this.ws = null;
        this.sprkcore = null;
        this.spawnInt = null;
        this.mouseInt = null;
        this.started = false;
        this.BotAppearance = 255;
        this.botName = 'Free Bots!';
        this.server = '';
        this.skins = [
            '26', '30', '32', '40', '60', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
            '21', '22', '23'
        ];
        this.botSkin = this.skins[~~(Math.random() * this.skins.length)];
        this.SkinName = `{${this.botSkin}}` + this.botName;
    }

    Buffer(buf) {
        return new DataView(new ArrayBuffer(!buf ? 1 : buf));
    }

    connect(wss) {
        this.server = wss;
        this.ws = new WebSocket(this.server);
        this.ws.binaryType = "arraybuffer";
        this.ws.onopen = this.onopen.bind(this);
        this.ws.onclose = this.onclose.bind(this);
    }

    Proto5(version, key) {
        let init = this.Buffer(0x5);
        init.setUint8(
            0x0, 0xfe
        )
        init.setUint32(
            0x1, version
        );

        this.send(init);

        init = this.Buffer(0x5);

        init.setUint8(
            0x0, 0xff
        )
        init.setUint32(
            0x1, key
        );

        this.send(init)
    }

    onopen() {
        Hooks.Client.spawned++
        let parseOrigin = /(\w+)\:\/\/(\w+.\w+)/gi.exec(window.location.origin)[2];
        if(parseOrigin == "aquar.io" || parseOrigin == "oceanar.io") {
            this.spawn();
            this.ping();
        } else if(parseOrigin == "cubedot.kr") {
            this.Proto5(0x6, 0x1);
            this.spawn();
        } else {
            this.Proto5(0x5, 0x75BCD15);
            this.spawn();
        }

        this.spawnInt = setInterval(() => {this.spawn()}, 3000);
        this.mouseInt = setInterval(() => {this.sendmouse()}, 150);
    }

    spawn() {
        let parseOrigin = /(\w+)\:\/\/(\w+.\w+)/gi.exec(window.location.origin)[2];
        if(parseOrigin == "aquar.io" || parseOrigin == "oceanar.io") {

            let spawnBuf = this.Buffer(52);
            spawnBuf.setUint8(0, 22);
            var o = 0;
            for(; o < 25; ++o) {
                spawnBuf.setUint16(1 + 2 * o, o < this.botName.length ? this.botName.charCodeAt(o) : 0, true);
            }
            spawnBuf.setUint8(51, this.BotAppearance)
            this.send(spawnBuf);

        } else if(parseOrigin == "cubedot.kr") {

            var msg = this.Buffer(1 + 2 * this.botName.length);
            msg.setUint8(0, 0);
            for(var ii = 0; ii < this.botName.length; ++ii) {
                msg.setUint16(1 + 2 * ii, this.botName.charCodeAt(ii), true);
            }
            this.send(msg);

        } else {

            var spawnbuf = this.Buffer(1 + 2 * this.SkinName.length);
            spawnbuf.setUint8(0, 192);
            for(var i = 0; i < this.SkinName.length; ++i) {
                spawnbuf.setUint16(1 + 2 * i, this.SkinName.charCodeAt(i), true);
            };
            this.send(spawnbuf);

        }
    }

    split() {
        let parseOrigin = /(\w+)\:\/\/(\w+.\w+)/gi.exec(window.location.origin)[2];
        if(parseOrigin == "aquar.io" || parseOrigin == "oceanar.io") {
            return;
        } else {
            this.send(new Uint8Array([17]));
        }
    }

    eject() {
        let parseOrigin = /(\w+)\:\/\/(\w+.\w+)/gi.exec(window.location.origin)[2];
        if(parseOrigin == "aquar.io" || parseOrigin == "oceanar.io") {
            return;
        } else {
            this.send(new Uint8Array([21]));
        }
    }

    get open() {return this.ws && this.ws.readyState === WebSocket.OPEN}

    send(data) {if(this.open) {this.ws.send(data)}}

    sendmouse() {
        let parseOrigin = /(\w+)\:\/\/(\w+.\w+)/gi.exec(window.location.origin)[2];
        if(parseOrigin == "aquar.io" || parseOrigin == "oceanar.io") {
            this.send(Hooks.Client.movebuf)
        } else {
            let movebuf = this.Buffer(0x15);
            movebuf.setUint8(
                0x0, 0x10
            )
            movebuf.setFloat64(
                0x1, Hooks.Client.positioning.x, true
            )
            movebuf.setFloat64(
                0x9, Hooks.Client.positioning.y, true
            )
            movebuf.setUint32(
                0x11, 0x0, true
            );
            this.send(movebuf);
        }
    }

    sendchat(msg) {
        let parseOrigin = /(\w+)\:\/\/(\w+.\w+)/gi.exec(window.location.origin)[2];
        if(parseOrigin == "aquar.io" || parseOrigin == "oceanar.io" || parseOrigin == "cubedot.kr") {
            return;
        } else {
            return;
        }
    }

    ping() {
        let ParseTime = 268435455 & Date.now();
        let oneByte = this.Buffer(0x5);
        oneByte.setUint8(
            0x0, 0x1
        )
        oneByte.setUint32(
            0x1, ParseTime
        );
        this.send(oneByte);
    }

    close() {
        if(this.ws) this.ws.close();
        this.onclose();
        Hooks.Client.spawned--
    }

    onclose() {
        clearInterval(this.spawnInt);
        clearInterval(this.mouseInt);
        clearInterval(this.sprkcore);
        setTimeout(() => {this.connect()}, 5000);
    }
}