Your auto chat...

Create your own personal automated chat. To open the menu, click on the new button next to the chat button. To create a message, enter it in the input field where the timer is shown, then click on the "Create" button. To delete a message, click on the "Delete" button. You can change existing messages, just click on the text in them and change.

// ==UserScript==
// @name         Your auto chat...
// @namespace    -
// @version      0.2
// @description  Create your own personal automated chat. To open the menu, click on the new button next to the chat button. To create a message, enter it in the input field where the timer is shown, then click on the "Create" button. To delete a message, click on the "Delete" button. You can change existing messages, just click on the text in them and change.
// @author       Nudo#7346
// @match        *://moomoo.io/*
// @match        *://*.moomoo.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=moomoo.io
// @grant        none
// @license      MIT
// @run-at       document-start
// ==/UserScript==

// Shit code is our everything!

(function() {
    const Config = {}

    Config.random = function(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min
    }

    Config.stopStupidProcesses = function(event) {
        if (!this.hasOwnProperty("socket")) {
            return void 0
        }

        if (event.data.toLowerCase() === "e") {
            this.socket.send(new Uint8Array(Array.from(Config.msgpack.encode(["7", [1]]))))
        }

        this.socket.send(new Uint8Array(Array.from(Config.msgpack.encode(["33", [null]]))))
    }

    class Message {
        constructor(id, message, position) {
            this.id = id
            this.text = message
            this.position = position
        }

        send() {
            if (!Config.hasOwnProperty("socket")) {
                return void 0
            }

            const message = document.querySelector(`#storageInvInput[data-iid="${this.id}"]`).value

            this.text = message

            Config.messageManager.messages[this.id].text = this.text

            window.localStorage.setItem("__srg", JSON.stringify(Config.messageManager.messages))

            Config.socket.send(new Uint8Array(Array.from(Config.msgpack.encode(["ch", [message]]))))
        }
    }

    class MessageManager {
        constructor() {
            this.messages = {}
            this.savedMessages = {}
            this.limit = 99
            this.lastMessage = Date.now()
            this.updateSpeed = 3000
            this.currentMessage = 0
        }

        get newID() {
            return Config.random(1e9, 10e9)
        }

        addSavedMessage() {
            this.savedMessages = window.localStorage.__srg

            if (typeof this.savedMessages === 'undefined') {
                return void 0
            }

            let messages = Object.values(JSON.parse(this.savedMessages))
            messages = messages.sort((a, b) => a.position - b.position)

            for (const i in messages) {
                const message = messages[i]

                this.add(message.text, message.id)
            }
        }

        add(message, oldID) {
            const messages = Object.values(this.messages)

            if (messages.length >= this.limit || message.match(/[а-яА-Я]/)) {
                return void 0
            }

            const id = typeof oldID === 'undefined' ? this.newID : oldID
            const position = messages.length + 1

            this.messages[id] = new Message(id, message, position)

            Config.createMessageItem(id, message, position)

            this.savedMessages = window.localStorage.__srg

            window.localStorage.setItem("__srg", JSON.stringify(this.messages))

            return this.messages[id]
        }

        remove(id) {
            const position = this.messages[id].position - 1
            let messages = Object.values(this.messages)

            for (let i = position; i < messages.length; i++) {
                messages = messages.sort((a, b) => a.position - b.position)

                this.messages[messages[i].id].position -= 1

                document.getElementById(`position-${messages[i].id}`).textContent = `${this.messages[messages[i].id].position}.`
            }

            Config.removeMessageItem(id)

            delete this.messages[id]

            this.savedMessages = window.localStorage.__srg

            window.localStorage.setItem("__srg", JSON.stringify(this.messages))

            return this.messages
        }

        clear(clearStorage) {
            this.messages = {}

            const storageHolder = document.getElementById("storageHolder")

            storageHolder.innerHTML = `
            <div class="storageItem">No Auto Message Yet</div>
            `

            if (clearStorage) {
                window.localStorage.removeItem("__srg")
            }

            return this.messages
        }

        setUpdateSpeed(speed) {
            speed = speed[1]

            if (!speed.length) {
                return void 0
            }

            speed = parseInt(speed)

            if (speed < 0) {
                speed = 0
            }

            this.updateSpeed = speed

            return this.updateSpeed
        }

        init() {
            setInterval(() => {
                try {
                    const storageInput = document.getElementById("storageInput")
                    const storageButtonM = document.querySelector(".storageButtonM")

                    if (storageInput.value === "__srg:clear" || storageInput.value.startsWith("__srg:speed=") || storageInput.value.startsWith("__srg:sp=")) {
                        storageButtonM.textContent = "Execute"
                    } else {
                        storageButtonM.textContent = "Create"
                    }

                    if (!this.lastMessage || Date.now() - this.lastMessage >= this.updateSpeed) {
                        let messages = Object.values(this.messages)

                        messages = messages.sort((a, b) => a.position - b.position)

                        this.lastMessage = Date.now()

                        if (!messages.length) {
                            return void 0
                        }

                        if (this.currentMessage >= messages.length) {
                            this.currentMessage = 0
                        }

                        messages[this.currentMessage].send()

                        this.currentMessage += 1
                    } else {
                        const storageInput = document.getElementById("storageInput")

                        storageInput.placeholder = `${this.updateSpeed - (Date.now() - this.lastMessage)}ms`
                    }
                } catch {}
            })
        }
    }

    Config.messageManager = new MessageManager()

    Config.messageManager.init()

    window.cfg = Config

    window.addEventListener("load", () => {
        const gameUI = document.getElementById("gameUI")
        const html = `
        <div id="storageButton" class="uiElement gameButton">
		  <i class="material-icons" style="font-size:40px;vertical-align:middle">post_add</i>
        </div>

        <div id="storageMenu" style="display: none;">
		  <div id="storageHolder">
            <div class="storageItem">No Auto Message Yet</div>
          </div>
		  <div id="storageManager">
            <input type="text" id="storageInput" maxlength="30" placeholder="unique message">
            <div class="storageButtonM" style="width: 140px;">Create</div>
          </div>
	    </div>

        <style>
        .removeMsgBtn {
          float: right;
          font-size: 24px;
          text-align: right;
          cursor: pointer;
          color: #80eefc;
        }

        .removeMsgBtn:hover {
          color: #72d3e0;
        }

        .storageItem {
          font-size: 24px;
          color: #fff;
          padding: 5px;
        }

        .storageButtonM {
          pointer-events: all;
          cursor: pointer;
          margin-top: 10px;
          font-size: 24px;
          color: #fff;
          padding: 5px;
          background-color: rgba(0, 0, 0, 0.25);
          -webkit-border-radius: 4px;
          -moz-border-radius: 4px;
          border-radius: 4px;
          text-align: center;
          display: inline-block;
        }

        #storageInput {
          pointer-events: all;
          font-size: 24px;
          color: #fff;
          background-color: rgba(0, 0, 0, 0.25);
          -webkit-border-radius: 4px;
          -moz-border-radius: 4px;
          border-radius: 4px;
          padding: 5px;
          display: inline-block;
          outline: none;
          border-color: none;
          border: 0;
          -webkit-box-shadow: none;
          box-shadow: none;
          width: 200px;
          margin-right: 7px;
        }

        #storageInvInput {
          pointer-events: all;
          font-size: 24px;
          color: rgba(255, 255, 255, 0.6);
          background-color: rgba(0, 0, 0, 0);
          outline: none;
          border-color: none;
          border: 0;
          -webkit-box-shadow: none;
          box-shadow: none;
          width: 184px;
        }

        #storageHolder {
          pointer-events: all;
          height: 200px;
          max-height: calc(100vh - 260px);
          overflow-y: scroll;
          -webkit-overflow-scrolling: touch;
          width: 350px;
          display: inline-block;
          text-align: left;
          padding: 10px;
          background-color: rgba(0, 0, 0, 0.25);
          -webkit-border-radius: 4px;
          -moz-border-radius: 4px;
          border-radius: 4px;
        }

        #storageMenu {
          display: none;
          width: 100%;
          position: absolute;
          text-align: center;
          top: 50%;
          transform: translateY(-50%);
          -ms-transform: translateY(-50%);
          -moz-transform: translateY(-50%);
          -webkit-transform: translateY(-50%);
          -o-transform: translateY(-50%);
        }

        #storageButton {
          right: 450px;
        }

        @media only screen and (max-width: 768px) {
          #storageButton {
            top: inherit;
            left: 60px;
          }

          .storageItem, #storageInput, .storageButtonM, .removeMsgBtn, #storageInvInput {
            font-size: 18px;
          }
        }
        </style>
        `

        gameUI.insertAdjacentHTML("beforeend", html)

        const storageButton = document.getElementById("storageButton")
        const storageMenu = document.getElementById("storageMenu")
        const storageInput = document.getElementById("storageInput")
        const storageHolder = document.getElementById("storageHolder")
        const storageButtonM = document.querySelector(".storageButtonM")
        const gameActionBtns = ["allianceButton", "storeButton", "chatButton"]
        const gameActionMenus = ["chatHolder", "allianceMenu", "storeMenu"]

        storageButton.addEventListener("click", () => {
            const action = {
                "block": "none",
                "none": "block"
            }

            for (const menu of gameActionMenus) {
                document.getElementById(menu).style.display = "none"
            }

            storageMenu.style.display = action[storageMenu.style.display]
        })

        Config.removeMessageItem = function(id) {
            const removeMsgBtns = document.querySelectorAll(".removeMsgBtn")

            for (const btn of removeMsgBtns) {
                if (btn.dataset.id === id.toString()) {
                    btn.parentNode.remove()
                }
            }

            if (removeMsgBtns.length === 1) {
                storageHolder.innerHTML = `
                <div class="storageItem">No Auto Message Yet</div>
                `
            }
        }

        Config.createMessageItem = function(id, message, position) {
            const removeMsgBtns = document.querySelectorAll(".removeMsgBtn")

            if (!removeMsgBtns.length) {
                storageHolder.innerHTML = ""
            }

            const messagePosition = position

            storageHolder.innerHTML += `
              <div class="storageItem" data-siid="${id}">
                <span id="position-${id}" style="color: rgba(255, 255, 255, 0.6)">${messagePosition}.</span>
                <input type="text" id="storageInvInput" value="${message}" maxlength="30" placeholder="3000ms" data-iid="${id}">
                <div class="removeMsgBtn" data-id="${id}">Remove</div>
              </div>
            `

            const storageInvInputs = document.querySelectorAll("#storageInvInput")

            for (const input of storageInvInputs) {
                input.addEventListener("input", (event) => {
                    Config.stopStupidProcesses(event)
                })
            }
        }

        storageButtonM.addEventListener("click", (event) => {
            let value = storageInput.value

            if (value === "__srg:clear") {
                storageHolder.innerHTML = ""
                storageInput.value = ""

                return Config.messageManager.clear(true)
            }

            if (value.startsWith("__srg:speed=")) {
                const speed = value.split("=")

                storageInput.value = ""

                return Config.messageManager.setUpdateSpeed(speed)
            }

            if (!value.length) {
                return void 0
            }

            if (value.length >= Config.messageManager.limit) {
                value = value.slice(0, 30)
            }

            storageInput.value = ""

            Config.messageManager.add(value)
        })

        storageHolder.addEventListener("click", (event) => {
            const target = event.target

            if (target.className === "removeMsgBtn") {
                Config.messageManager.remove(target.dataset.id)
            }
        })

        storageInput.addEventListener("input", (event) => {
            Config.stopStupidProcesses(event)
        })

        document.addEventListener("keydown", (event) => {
            if (event.code === "Enter" || event.code === "Escape") {
                storageMenu.style.display = "none"
            }
        })

        for (const btn of gameActionBtns) {
            document.getElementById(btn).addEventListener("click", (event) => {
                storageMenu.style.display = "none"
            })
        }

        Config.messageManager.addSavedMessage()
    })

    Config.msgpack = {}

    Function.prototype.call = new Proxy(Function.prototype.call, {
        apply(target, _this, args) {
            const data = target.apply(_this, args)

            if (args[1] && args[1].i) {
                const i = args[1].i

                if (i === 9) {
                    Config.msgpack.encode = args[0].encode
                }

                if (i === 15) {
                    Config.msgpack.decode = args[0].decode
                    Function.prototype.call = target
                }
            }

            return data
        }
    })

    const set = Object.getOwnPropertyDescriptor(WebSocket.prototype, "onmessage").set;
    Object.defineProperty(WebSocket.prototype, "onmessage", {
        set(callback) {
            return set.call(this, new Proxy(callback, {
                apply(target, _this, args) {
                    Config.socket = _this

                    return target.apply(_this, args)
                }
            }))
        }
    })
})()