MooMoo.io Anti-Kick

Anti-kick for MooMoo.io

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/474034/1271161/MooMooio%20Anti-Kick.js

// ==UserScript==
// @name         MooMoo.io Anti-Kick
// @namespace    https://greasyfork.org/users/1064285-vcrazy-gaming
// @version      1
// @description  Prevent Getting Kicked From MooMoo.io
// @author       _VcrazY_
// @match        *://*/*
// @icon         data:image/webp;base64,UklGRi4CAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSEcBAAANkCvbtmlb4xjXtm9k27azWze0bUWIbNvP72Wv3h/Ytm1jH58z9x9ExARAd32XdF/Tv9+vnL79DaTFpXoyQKDnE8tbWtKFy4XdDDSL3Bvs6u9qY+0V4m5uAO3Tmzd3NYibE/7IoLtj5bS6vAHQ3uiAqqDVB6Syw/NQFffLQPpr/CbUlkhA+r1GALUelaDV03+mhn8A6qGJlyqVMjLJYCMAaSzoY17dAWxNWbByOC+D9w8WOCFnGVjJ2LB+zcAEbFq+lYHDCv8fwHDZ+CMEPhpxWXgrAJ6Y6dHJHvKAa5aWdP/OAXj7zJ/u+RsAv04VcskOQfXAPoLq9YIa5BwW0vxvhPqPFSt6JMPQfOJsp55u/9aeayGbPD+jp1PfN2jLHGs4Gqzdq1To+jHVrcjHXMIFZL8/3T6zDcK9Y6/trYyFzLvPDyUcaAsAVlA4IMAAAADwBQCdASogACAAPt1kq0+opaQiKAqpEBuJbAC2+tqOAv/yo3pecQ1SQ38K5j1X4+D/7cw7/qVYgAD+8qOSIfqkS9a1lb6tEUU3vveBinOVdjPzUwth1t1Chw7D4CrZ5Naml0zrhLXC7P6Pzg2S9IRetxpXmem9ECxakv1JjYillR67TTKNMrgrs0z8UscOT49KzAEg5Vfk0ydfc/guvgT1t0NC16MURvoeRfej7JgO/Zj1yQsXi7520/2kDgfHgAA=
// @grant        none
// @license      MIT
// ==/UserScript==

// Check the current URL
if (window.location.hostname.includes("moomoo.io") ||
    window.location.hostname.includes("sandbox.moomoo.io") ||
    window.location.hostname.includes("dev.moomoo.io")) {
  // The code will only run on the specified domains

  (() => {
    "use strict";

    const PACKET_LIMITS = {
      PER_MINUTE: 1000,
      PER_SECOND: 80,
    };

    const IGNORED_PACKET_TYPES = new Set(["pp", "rmd"]);
    const IGNORED_QUEUE_PACKETS = new Set(["5", "c", "33", "2", "7", "13c"]);

    /**
     * Class to manage anti-kick functionality
     */
    class AntiKick {
      constructor() {
        this.resetRateLimit();
      }

      /**
       * Reset packet rate limits
       */
      resetRateLimit() {
        // Packet history
        this.packetHistory = new Map();
        // Packet queue
        this.packetQueue = [];
        // Last packet sent time
        this.lastSent = Date.now();
      }

      /**
       * Check if the packet is rate-limited
       * @param {ArrayBuffer} data - Binary packet data
       * @returns {boolean} True if rate-limited, false otherwise
       */
      isRateLimited(data) {
        const binaryData = new Uint8Array(data);

        if (Date.now() - this.lastSent > PACKET_LIMITS.PER_MINUTE) {
          this.resetRateLimit();
        }

        const packetType = binaryData[0];

        if (!IGNORED_PACKET_TYPES.has(packetType)) {
          if (this.packetHistory.has(packetType) &&
              (("2" === packetType || "33" === packetType) && this.packetHistory.get(packetType)[0] === binaryData[1])) {
            return true;
          }

          if (this.packetQueue.length > PACKET_LIMITS.PER_SECOND) {
            return IGNORED_QUEUE_PACKETS.has(packetType) || this.packetQueue.push(data);
          }

          this.packetQueue.push({ type: packetType, data: binaryData.slice(1) });
          this.packetHistory.set(packetType, binaryData.slice(1));
        }

        return false;
      }
    }

    const antiKick = new AntiKick();

    // Override the send method of WebSocket
    WebSocket.prototype.send = new Proxy(WebSocket.prototype.send, {
      /**
       * Apply the send method override
       * @param {function} target - WebSocket method to override
       * @param {object} thisArg - The WebSocket object
       * @param {ArrayBuffer} binary - Binary data to send
       * @returns {*} Result of the original send method
       */
      apply(target, thisArg, binary) {
        if (!thisArg.messageListenerSet) {
          // Listen for messages to process the packet queue
          thisArg.addEventListener("message", (event) => {
            if (antiKick.packetQueue.length) {
              const binaryData = new Uint8Array(event.data);
              if (binaryData[0] === 51) {
                thisArg.send(antiKick.packetQueue[0]);
                antiKick.packetQueue.shift();
              }
            }
          });
          thisArg.messageListenerSet = true;
        }

        // Send the packet only if it's not rate-limited
        if (!antiKick.isRateLimited(binary)) {
          return Reflect.apply(target, thisArg, binary);
        }
      },
    });
  })();
}