Xero-Bots | .io Bots

Bots for Agar.io clones!

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Xero-Bots | .io Bots
// @namespace    https://xerobots.gt.tc
// @version      N/A
// @description  Bots for Agar.io clones!
// @             Please note that some of the sites included in this script may no longer be hosted, or the bots on them may have been patched.
// @author       XEROBOTS
// @match        *://agarz.com/*
// @match        *://eatcells.com/*
// @match        *://cellcraft.io/*
// @match        *://sakura-agar.net/*
// @match        *://happyagar.online/*
// @match        *://bublz.us/*
// @match        *://agar-kicoo.tk/*
// @match        *://cell.sh/*
// @match        *://ultrex.io/*
// @match        *://bubleroyal.com/*
// @match        *://agar-team.com/*
// @match        *://agargo.com/*
// @match        *://petridish.pw/*
// @match        *://agarmen.com/*
// @match        *://mortalcell.com/*
// @match        *://ogar.eatcells.com/*
// @match        *://mad-agar.pw/*
// @match        *://roogar.io/*
// @match        *://slither.com/*
// @match        *://slither.io/*
// @match        *://agar.boston/*
// @match        *://agar.live/*
// @match        *://es.agar.live/*
// @match        *://ru.agar.live/*
// @match        *://pt.agar.live/*
// @match        *://tr.agar.live/*
// @match        *://agar.cc/*
// @match        *://tr.agar.cc/*
// @match        *://agariogame.org/*
// @match        *://agariobr.com.br/*
// @match        *://agarioonline.org/*
// @match        *://agarioio.com/*
// @match        *://agario.onl/*
// @match        *://agario.zafer2.com/*
// @match        *://agariomoddedserver.com/*
// @match        *://agarprivateservers.org/*
// @match        *://aquar.io/*
// @match        *://bubbleam.pl/*
// @match        *://buble.am/*
// @match        *://dalr.ae/*
// @match        *://easyagario.icu/*
// @match        *://imsolo.pro/*
// @match        *://oceanar.io/*
// @match        *://ogarium.com/*
// @match        *://ogar.mivabe.nl/*
// @match        *://ryuten.io/*
// @match        *://agar.emupedia.net/*
// @match        *://agario.xingkong.tw/*
// @match        *://gota.io/*
// @match        *://play.gota.io/*
// @match        *://agar.lol/*
// @match        *://agar.fun/*
// @match        *://agario.lol/*
// @match        *://agario.fun/*
// @match        *://agario.skin/*
// @match        *://agar.cam/*
// @match        *://agarj.com/*
// @match        *://agarws.com/*
// @match        *://scrux.io/*
// @match        *://one.sigmally.com/*
// @match        *://privateagario.net/*
// @match        *://playagario.org/*
// @match        *://agario.monster/*
// @match        *://agariov.org/*
// @match        *://agariohere.com/*
// @match        *://agariogame.club/*
// @match        *://agario.cloud/*
// @match        *://agarioarabic.com/*
// @match        *://agario-play.com/*
// @match        *://alis.io/*
// @match        *://alis.nosx.pw/*
// @match        *://agario.org.uk/*
// @match        *://agario.guru/*
// @match        *://agario.fans/*
// @match        *://agrics.org/*
// @grant        none
// @icon         https://use.xerobots.gt.tc/icon.png
// @license      MIT
// @run-at       document-start
// ==/UserScript==

/*
   The MIT License (MIT)
   Copyright (c) XEROBOTS 2025
   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:
   The above copyright notice and this permission notice shall be included in all
   copies or substantial portions of the Software.
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   SOFTWARE.
*/

class Client {
    constructor() {
        this.isDebugging = false;
        [this.socketServerURL, this.requestGuiURL] = this.isDebugging ? ['ws://localhost:3000', 'http://localhost:3000/botGUI'] : ['wss://use.xerobots.gt.tc', 'https://use.xerobots.gt.tc/botGUI'];
        this.clientSocketURL = '';
        this.coordinates = { x: 0, y: 0 };
        this.startedBots = false;
        this.injectedUI = false;
        this.queue = [];
        this.ModeActive = false;
this.keybinds = {
    split: localStorage.getItem("kb_split") || 'v',
    eject: localStorage.getItem("kb_eject") || 'f',
    startStop: localStorage.getItem("kb_startStop") || '8',
    followFarm: localStorage.getItem("kb_followFarm") || '9'
};
        this.getID = id => document.getElementById(id);
        this.injectUI();
        this.setKeyboardInput();
        this.connectToSocket();
        this.beginMouseInterval();
    }

    async injectUI(retries = 0) {
        const maxRetries = 3;
        const retryDelay = 3000;
        const timeout = 5000;

        try {
            this.uiCode = await this.fetchWithTimeout(this.fetchGUI(), timeout);
            if (!this.uiCode) {
                throw new Error('Error fetching the Bot GUI');
            }
            if (!this.isUIAppended()) {
                this.appendGUI(this.uiCode);
            } else return;
        } catch (error) {
            alert('Failed to fetch botUI, retrying...');
            if (retries < maxRetries) {
                await this.delay(retryDelay);
                return this.injectUI(retries + 1);
            } else {
                return alert(`An error occurred: ${error.message}. If this issue persists, refresh the page or contact a developer.`);
            }
        }
    }

    isUIAppended() {
        const uiContainer = document.getElementById('uiRoot');
        return !!uiContainer;
    }

    async fetchWithTimeout(fetchPromise, timeout) {
        let timeoutId;
        const timeoutPromise = new Promise((_, reject) => {
            timeoutId = setTimeout(() => reject(new Error('Request timed out')), timeout);
        });
        return Promise.race([fetchPromise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
    }

    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    async fetchGUI() {
        const guiUrl = await fetch(this.requestGuiURL);
        if (!guiUrl.ok) {
            throw new Error(`Fetch failed with status code ${guiUrl.status} and text "${guiUrl.statusText}"`);
        }
        return await guiUrl.text();
    }

    appendGUI(html) {
        const div = document.createElement('div');
        div.id = 'uiRoot';
        div.innerHTML = html;
        document.body.appendChild(div);

        this.divIDs = {
            botCount: 'uiRootBotCounter',
            startButton: 'uiRootStartStop',
            botStatus: 'uiRootBotStatus',
            keybindBtn: 'uiRootKeybindsBtn',
            saveKeybinds: 'saveKeybinds',
            keySplit: 'keySplit',
            keyEject: 'keyEject',
            ModeButton: 'uiRootModeToggle'
        };

        this.injectedUI = true;

        this.getID(this.divIDs.startButton).addEventListener('click', (event) => {
            if (event.isTrusted) {
                this.setStartedInput();
            }
        });

        this.getID(this.divIDs.ModeButton).addEventListener('click', (event) => {
            if (event.isTrusted) {
                this.toggleMode();
            }
        });

        this.getID(this.divIDs.keybindBtn).addEventListener('click', () => {
            this.toggleKeybindPopup();
        });

        this.getID(this.divIDs.saveKeybinds).addEventListener('click', () => {
            this.saveKeybinds();
        });

        while (this.queue.length) {
            let [functionName, args] = this.queue.shift();
            this[functionName](...args);
        }
    }

    toggleMode() {
        const btn = this.getID(this.divIDs.ModeButton);
        const turningOn = btn.textContent === 'Follow';

        if (turningOn) {
            btn.textContent = 'Farm';
            btn.style.color = '#4287f5';
            this.ModeActive = true;
            this.sendUint8(10); // Start Mass Farm
        } else {
            btn.textContent = 'Follow';
            btn.style.color = '#4287f5';
            this.ModeActive = false;
            this.sendUint8(11); // Stop Mass Farm
        }
    }

    updateGUIStatus(color, status) {
        if (!this.injectedUI) {
            this.queue.push(['updateGUIStatus', [color, status]]);
            return;
        }
        this.getID(this.divIDs.botStatus).style.color = color;
        this.getID(this.divIDs.botStatus).innerHTML = status;
    }

    updateGUICounter(spawned, max) {
        if (!this.injectedUI) return;
        this.getID(this.divIDs.botCount).innerHTML = spawned + " / " + max;
    }

    updateGUIStarted(check) {
        if (!this.injectedUI) return;
        if (!check) {
            this.getID(this.divIDs.startButton).innerHTML = 'Start';
            this.getID(this.divIDs.startButton).style.color = '#ebd907';
        } else {
            this.getID(this.divIDs.startButton).innerHTML = 'Stop';
            this.getID(this.divIDs.startButton).style.color = '#ebd907';
        }
    }

    resetGUI() {
        if (!this.injectedUI) {
            this.queue.push(['resetGUI', []]);
            return;
        }
        this.updateGUIStatus('#FF6961', 'Disconnected');
        this.updateGUIStarted(this.startedBots);
        this.getID(this.divIDs.botCount).innerHTML = "0 / 0";
    }

setKeyboardInput() {
  window.addEventListener('keypress', (event) => {
    if (event.isTrusted) {
      const key = event.key.toLowerCase();
      if (key === this.keybinds.split) {
        this.sendUint8(2); // split command
      } else if (key === this.keybinds.eject) {
        this.sendUint8(3); // eject command
      } else if (key === this.keybinds.startStop) {
        this.setStartedInput(); // start/stop bots toggle
      } else if (key === this.keybinds.followFarm) {
        this.toggleMode(); // follow/farm toggle
      }
    }
  });
}

    beginMouseInterval() {
        this.isMouseMoved = false;
        this.mouseInt = setInterval(() => {
            if (!this.isMouseMoved) return;
            this.isMouseMoved = false;

            var test = this.Buffer(26);
            test.setUint8(0, 0x4, true);
            test.setFloat64(1, this.coordinates.x, true);
            test.setFloat64(9, this.coordinates.y, true);
            this.sendMsg(test);
        }, 50);
    }

    dataParse(args) {
        if (args instanceof ArrayBuffer) return new Uint8Array(args);
        if (args instanceof DataView) return new Uint8Array(args.buffer);
        if (args instanceof Uint8Array) return args;
        if (args instanceof Array) return new Uint8Array(args);
        throw new Error(`Unsupported data type: ${args}`);
    }

    mouseNavigation(data, ws) {
        try {
            data = this.dataParse(data);
            switch (data.length) {
                case 21:
                case 17:
                    data = new DataView(data.buffer);
                    var datax = data.getFloat64(1, true);
                    var datay = data.getFloat64(9, true);
                    this.clientSocketURL = ws.url;
                    if (this.coordinates.x !== datax || this.coordinates.y !== datay) {
                        this.isMouseMoved = true;
                    }
                    this.coordinates.x = datax;
                    this.coordinates.y = datay;
                    break;
                case 9:
                case 13:
                    data = new DataView(data.buffer);
                    datax = data.getInt32(1, true);
                    datay = data.getInt32(5, true);
                    this.clientSocketURL = ws.url;
                    if (this.coordinates.x !== datax || this.coordinates.y !== datay) {
                        this.isMouseMoved = true;
                    }
                    this.coordinates.x = datax;
                    this.coordinates.y = datay;
                    break;
                default:
                    this.clientSocketURL = ws.url;
                    break;
            }
        } catch (error) {
            console.log(`Error in parsing data: ${error}`);
        }
    }

    connectToSocket() {
        this.socket = new WebSocket(this.socketServerURL);
        this.socket.binaryType = 'arraybuffer';
        this.socket.onmessage = this.onmessage.bind(this);
        this.socket.onerror = this.onerror.bind(this);
        this.socket.onclose = this.onclose.bind(this);
        this.socket.onopen = this.onopen.bind(this);
    }

    onopen() {
        this.updateGUIStatus('#50C878', 'Connected');
    }

    onmessage(message) {
        try {
            message = this.dataParse(message.data);
            var data = new DataView(message.buffer);
            var opcode = data.getUint8(0);
            switch (opcode) {
                case 0x6:
                    var spawnedBots = data.getUint32(1, true);
                    var maxBots = data.getUint32(10, true);
                    this.updateGUICounter(spawnedBots, maxBots);
                    break;
            }
        } catch (error) {
            console.log(`Error in parsing data: ${error}`);
        }
    }

    onclose(msg) {
        if (msg.code == 1006) {
            setTimeout(this.connectToSocket.bind(this), 6000);
        } else {
            setTimeout(this.connectToSocket.bind(this), 10000);
        }
        this.startedBots = false;
        this.resetGUI();
    }

    onerror() {}

    setStartedInput() {
        if (this.startedBots) {
            this.startedBots = !this.stopBots();
        } else {
            this.startedBots = this.startBots();
        }
        this.updateGUIStarted(this.startedBots);
    }

    startBots() {
        if (!this.clientSocketURL || this.clientSocketURL.includes(this.socketServerURL)) return false;
        var serverData = this.Buffer(1 + 2 * this.clientSocketURL.length);
        serverData.setUint8(0, 0);
        for (var i = 0; i < this.clientSocketURL.length; ++i) {
            serverData.setUint16(1 + 2 * i, this.clientSocketURL.charCodeAt(i), true);
        }
        this.sendMsg(serverData);
        return true;
    }

    stopBots() {
        this.sendUint8(1);
        return true;
    }

    get open() {
        return this.socket && this.socket.readyState === 1;
    }

    Buffer(buffer = 1) {
        return new DataView(new ArrayBuffer(buffer));
    }

    sendUint8(offset) {
        var oneByte = this.Buffer(1);
        oneByte.setUint8(0, offset);
        this.sendMsg(oneByte);
    }

    sendMsg(message) {
        try {
            if (this.open) {
                this.socket.send(message);
            }
        } catch (error) {
            console.log(`Failed to send data: ${error}`);
        }
    }

toggleKeybindPopup() {
  const popup = document.getElementById('keybindPopup');
  popup.style.display = popup.style.display === 'block' ? 'none' : 'block';

  document.getElementById('keySplit').value = this.keybinds.split.toUpperCase();
  document.getElementById('keyEject').value = this.keybinds.eject.toUpperCase();
  document.getElementById('keyStartStop').value = this.keybinds.startStop.toUpperCase();
  document.getElementById('keyFollowFarm').value = this.keybinds.followFarm.toUpperCase();
}

saveKeybinds() {
  const split = document.getElementById('keySplit').value.trim().toLowerCase();
  const eject = document.getElementById('keyEject').value.trim().toLowerCase();
  const startStop = document.getElementById('keyStartStop').value.trim().toLowerCase();
  const followFarm = document.getElementById('keyFollowFarm').value.trim().toLowerCase();

  const keys = [split, eject, startStop, followFarm];
  if (keys.every(k => k.length === 1)) {
    localStorage.setItem("kb_split", split);
    localStorage.setItem("kb_eject", eject);
    localStorage.setItem("kb_startStop", startStop);
    localStorage.setItem("kb_followFarm", followFarm);

    this.keybinds.split = split;
    this.keybinds.eject = eject;
    this.keybinds.startStop = startStop;
    this.keybinds.followFarm = followFarm;

    document.getElementById('keybindPopup').style.display = 'none';
  } else {
  }
}
}

let usr;

WebSocket.prototype.send = new Proxy(WebSocket.prototype.send, {
    apply: (target, thisArg, args) => {
        var ret = target.apply(thisArg, args);
        if (thisArg.url.includes(usr?.socketServerURL)) return ret;
        usr?.mouseNavigation(...args, thisArg);
        return ret;
    }
});

window.addEventListener('load', () => {
    usr = new Client();
});