☰

leoonjoo script

Chat commands for agma.io: /leo level, coins, gold, fps, ping, buyskins, help. Runs only when you press Enter in the chat, so no lag.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴、Greasemonkey 油猴子 或 Violentmonkey 暴力猴,才能安装此脚本。

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴 或 Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴 或 Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         leoonjoo script
// @namespace    https://agma.io/
// @version      3.1
// @description  Chat commands for agma.io: /leo level, coins, gold, fps, ping, buyskins, help. Runs only when you press Enter in the chat, so no lag.
// @match        https://agma.io/*
// @match        https://www.agma.io/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  var MODE = 'send';

  var YT_NAME = 'Leoo magic';
  var YT_LINK = 'https://www.youtube.com/results?search_query=Leoo+magic';

  var LEVEL_MESSAGE = 'Your level is: {level} 🏴';
  var COINS_MESSAGE = 'Your coins are: {coins} 🏴';
  var GOLD_MESSAGE = 'Your Gold Member days left: {gold} 🏴';
  var NO_GOLD_MESSAGE = 'You are not a Gold Member 🏴';
  var FPS_MESSAGE = 'Your FPS: {fps} 🏴';
  var PING_MESSAGE = 'Your ping: {ping} ms 🏴';
  var SKINS_MESSAGE = 'Your custom skins: {skins} 🏴';
  var HELP_MESSAGE = 'Commands: /leo level, coins, gold, fps, ping, buyskins 🏴';

  var LEVEL_COMMAND = /^\/(?:leo\s+)?(?:level|lvl)\s*$/i;
  var COINS_COMMAND = /^\/(?:leo\s+)?(?:coins|coin|monedhat)\s*$/i;
  var GOLD_COMMAND = /^\/(?:leo\s+)?gold\s*$/i;
  var FPS_COMMAND = /^\/(?:leo\s+)?fps\s*$/i;
  var PING_COMMAND = /^\/(?:leo\s+)?ping\s*$/i;
  var SKINS_COMMAND = /^\/(?:leo\s+)?(?:buyskins|skins)\s*$/i;
  var HELP_COMMAND = /^\/(?:leo\s+)?(?:help|komandat)\s*$/i;
  var DEBUG_COMMAND = /^\/(?:leo\s+)?debug\s*$/i;

  function readNumber(id) {
    var el = document.getElementById(id);
    if (!el) return null;
    var digits = (el.innerText || el.textContent || '').replace(/[^\d]/g, '');
    return digits ? parseInt(digits, 10) : null;
  }

  var GOLD_RE = /(\d+)\s*days?\s*left\s*of\s*gold\s*member/gi;
  var SKIP_IDS = { canvas: 1, minimap: 1, leaderboard: 1 };
  var goldDays = null;
  var welcomeSeen = false;
  var lastUser = null;
  var recentTexts = [];
  var drawCount = 0;
  var chatDrawImage = 0;

  function hookText(ctx) {
    if (!ctx || ctx.__leoText) return;
    ctx.__leoText = true;

    ['fillText', 'strokeText'].forEach(function (name) {
      var original = ctx[name];
      if (typeof original !== 'function') return;

      ctx[name] = function (text) {
        try {
          if (typeof text === 'string') {
            drawCount++;
            recentTexts.push(text);

            if (recentTexts.length > 40) {
              recentTexts.shift();
            }

            if (text.indexOf('elcome') !== -1 || text.indexOf('gma') !== -1) {
              var PHRASE = 'welcomebacktoagma';
              var raw = recentTexts.slice(-10).join('');
              var joined = raw.replace(/\s+/g, '').toLowerCase();
              var curLen = text.replace(/\s+/g, '').length;
              var at = joined.lastIndexOf(PHRASE);

              if (at !== -1 && at + PHRASE.length > joined.length - curLen) {
                welcomeSeen = true;

                var userRe = /welcome\s*back\s*to\s*agma\s*,?\s*([^\s\[\],]+)/gi;
                var um;
                var user = null;

                while ((um = userRe.exec(raw)) !== null) {
                  user = um[1];
                }

                if (user !== null) {
                  if (user !== lastUser) {
                    goldDays = null;
                  }
                  lastUser = user;
                }
              }
            }

            if (text.indexOf('ember') !== -1) {
              var all = recentTexts.join('');
              var m;
              var last = null;

              GOLD_RE.lastIndex = 0;

              while ((m = GOLD_RE.exec(all)) !== null) {
                last = m[1];
              }

              if (last !== null) {
                goldDays = parseInt(last, 10);
              }
            }
          }
        } catch (err) {}

        return original.apply(this, arguments);
      };
    });
  }

  try {
    var originalGetContext = HTMLCanvasElement.prototype.getContext;

    HTMLCanvasElement.prototype.getContext = function (type) {
      var ctx = originalGetContext.apply(this, arguments);

      try {
        if (ctx && type === '2d' && !SKIP_IDS[this.id]) {
          hookText(ctx);
        }
      } catch (err) {}

      return ctx;
    };
  } catch (err) {}

  function hookChatCanvas() {
    var canvas = document.getElementById('chtCanvas');

    if (!canvas) return false;

    var ctx = canvas.getContext('2d');

    if (!ctx) return true;

    if (ctx.__leoChat) return true;

    ctx.__leoChat = true;
    hookText(ctx);

    var original = ctx.drawImage;

    if (typeof original === 'function') {
      ctx.drawImage = function () {
        chatDrawImage++;
        return original.apply(this, arguments);
      };
    }

    return true;
  }

  var toast = null;
  var hideTimer = null;

  function showToast(text, ms) {
    if (!toast) {
      toast = document.createElement('div');

      toast.style.cssText =
        'position:fixed;top:16px;left:50%;transform:translateX(-50%);' +
        'z-index:2147483647;padding:10px 20px;border-radius:10px;' +
        'border:2px solid #e41e20;background:#111;color:#fff;' +
        'font:600 16px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;' +
        'pointer-events:none;';

      document.body.appendChild(toast);
    }

    toast.textContent = text;
    toast.style.display = 'block';

    clearTimeout(hideTimer);

    hideTimer = setTimeout(function () {
      toast.style.display = 'none';
    }, ms || 4000);
  }

  function setField(field, text) {
    field.value = text;
  }

  function stopEvent(e) {
    e.preventDefault();
    e.stopPropagation();
    e.stopImmediatePropagation();
  }

  function debugText() {
    return 'Chat canvas: ' +
      (canvasDone ? 'connected' : 'NOT connected') +
      ' | texts captured: ' + drawCount +
      ' | drawImage chat: ' + chatDrawImage +
      ' | gold: ' + goldDays +
      ' | user: ' + lastUser +
      ' | last text: ' +
      (recentTexts.slice(-8).join(' | ') || '(nothing)');
  }

  function onKeyDown(e) {
    if (e.key !== 'Enter') return;

    var box = e.target;
    var typed = (box.value || '').trim();

    if (typed.charAt(0) !== '/') return;

    var text = null;
    var missing = null;
    var failText = null;

    if (LEVEL_COMMAND.test(typed)) {
      var level = readNumber('level');

      if (level === null) {
        missing = 'level';
        failText = 'Level not found, refresh the page 🏴';
      } else {
        text = LEVEL_MESSAGE.replace('{level}', String(level));
      }

    } else if (COINS_COMMAND.test(typed)) {
      var coins = readNumber('coinsDash');

      if (coins === null) {
        missing = 'coins';
        failText = 'Coins not found, refresh the page 🏴';
      } else {
        text = COINS_MESSAGE.replace(
          '{coins}',
          coins.toLocaleString('en-US')
        );
      }

    } else if (GOLD_COMMAND.test(typed)) {
      if (goldDays !== null) {
        text = GOLD_MESSAGE.replace('{gold}', String(goldDays));
      } else if (welcomeSeen) {
        text = NO_GOLD_MESSAGE;
      } else {
        missing = 'Gold Member days';
        failText = 'Gold info not found, join the game and try again 🏴';
      }

    } else if (FPS_COMMAND.test(typed)) {
      var fps = readNumber('fps');

      if (fps === null) {
        missing = 'FPS';
        failText =
          'FPS not found, turn on FPS / Ping / Stats in settings 🏴';
      } else {
        text = FPS_MESSAGE.replace('{fps}', String(fps));
      }

    } else if (PING_COMMAND.test(typed)) {
      var ping = readNumber('ping');

      if (ping === null) {
        missing = 'ping';
        failText =
          'Ping not found, turn on FPS / Ping / Stats in settings 🏴';
      } else {
        text = PING_MESSAGE.replace('{ping}', String(ping));
      }

    } else if (SKINS_COMMAND.test(typed)) {
      var pane = document.getElementById('skinsCustom');

      if (!pane) {
        missing = 'skins';
        failText =
          'Skins not loaded, open the skin shop once and try again 🏴';
      } else {
        var skinCount =
          pane.querySelectorAll('img[id^="skinCustomImg"]').length;

        text = SKINS_MESSAGE.replace(
          '{skins}',
          String(skinCount)
        );
      }

    } else if (HELP_COMMAND.test(typed)) {
      text = HELP_MESSAGE;

    } else if (DEBUG_COMMAND.test(typed)) {
      stopEvent(e);
      setField(box, '');
      showToast(debugText(), 12000);
      return;

    } else {
      return;
    }

    if (missing && MODE === 'send') {
      setField(box, failText);
      return;
    }

    if (missing) {
      stopEvent(e);
      setField(box, '');

      if (GOLD_COMMAND.test(typed)) {
        showToast(
          'Could not find your Gold Member days. ' +
          debugText(),
          12000
        );
      } else {
        var hint =
          (FPS_COMMAND.test(typed) || PING_COMMAND.test(typed))
            ? ' Turn on "FPS / Ping / Stats" in the settings and try again.'
            : ' Refresh the page and try again.';

        showToast(
          'Could not find your ' +
          missing +
          '.' +
          hint
        );
      }

      return;
    }

    if (MODE === 'send') {
      setField(box, text);
      return;
    }

    stopEvent(e);
    setField(box, '');
    showToast(text);
  }

  document.addEventListener(
    'keydown',
    function (e) {
      if (e.key !== 'Enter') return;

      var t = e.target;

      if (!t || t.id !== 'chtbox') return;

      onKeyDown(e);
    },
    true
  );

  var COMMAND_LIST = [
    ['/leo level', 'shows your level'],
    ['/leo coins', 'shows your coins'],
    ['/leo gold', 'shows your Gold Member days'],
    ['/leo fps', 'shows your FPS'],
    ['/leo ping', 'shows your ping'],
    ['/leo buyskins', 'shows your custom skins'],
    ['/leo help', 'shows the command list']
  ];

  var leooPanel = null;

  function getPanel() {
    if (leooPanel) return leooPanel;

    var FONT =
      'Ubuntu,"Open Sans",system-ui,-apple-system,Segoe UI,Roboto,sans-serif';

    var panel = document.createElement('div');

    panel.id = 'leoo-magic-panel';

    panel.style.cssText =
      'position:fixed;display:none;z-index:2147483647;width:300px;' +
      'box-sizing:border-box;padding:12px 16px 14px;background:#1b1b1b;' +
      'border:1px solid #3b3018;border-radius:10px;font:600 14px/1.5 ' +
      FONT +
      ';color:#888;text-align:left;box-shadow:0 6px 18px rgba(0,0,0,.6);';

    var header = document.createElement('div');

    header.textContent = YT_NAME;

    header.style.cssText =
      'text-align:center;color:#f3c37b;font-weight:700;font-size:15px;' +
      'padding-bottom:8px;margin-bottom:8px;border-bottom:1px solid #3b3018;';

    panel.appendChild(header);

    COMMAND_LIST.forEach(function (row) {
      var line = document.createElement('div');

      line.style.cssText =
        'display:flex;align-items:flex-start;gap:8px;margin:5px 0;';

      var box = document.createElement('span');

      box.textContent = '\u2713';

      box.style.cssText =
        'flex:none;width:14px;height:14px;margin-top:4px;border-radius:3px;' +
        'background:#0075ff;color:#fff;font-size:11px;line-height:14px;' +
        'text-align:center;';

      var text = document.createElement('span');

      var cmd = document.createElement('span');

      cmd.textContent = row[0];

      cmd.style.cssText =
        'color:#df901c;font-weight:700;';

      text.appendChild(cmd);

      text.appendChild(
        document.createTextNode(' \u2013 ' + row[1])
      );

      line.appendChild(box);
      line.appendChild(text);

      panel.appendChild(line);
    });

    var note = document.createElement('div');

    note.textContent = '* Type the command in the game chat';

    note.style.cssText =
      'margin-top:8px;font-size:12px;font-style:italic;color:#888;';

    panel.appendChild(note);

    var link = document.createElement('a');

    link.href = YT_LINK;
    link.target = '_blank';
    link.rel = 'noopener noreferrer';
    link.textContent = 'YouTube: ' + YT_NAME;

    link.style.cssText =
      'display:block;margin-top:12px;padding:8px 0;background:#4caf50;' +
      'color:#fff;text-align:center;font-weight:700;border-radius:4px;' +
      'text-decoration:none;';

    panel.appendChild(link);

    document.body.appendChild(panel);

    leooPanel = panel;

    return panel;
  }

  function togglePanel(btn) {
    var p = getPanel();

    if (p.style.display === 'block') {
      p.style.display = 'none';
      return;
    }

    p.style.display = 'block';

    var r = btn.getBoundingClientRect();
    var pw = p.offsetWidth || 300;
    var ph = p.offsetHeight || 260;

    var left = Math.min(
      Math.max(8, r.left + r.width / 2 - pw / 2),
      Math.max(8, window.innerWidth - pw - 8)
    );

    p.style.left = left + 'px';

    if (
      r.bottom + 8 + ph <= window.innerHeight - 8 ||
      r.top - ph - 8 < 8
    ) {
      p.style.top = r.bottom + 8 + 'px';
      p.style.bottom = 'auto';
    } else {
      p.style.bottom =
        window.innerHeight - r.top + 8 + 'px';
      p.style.top = 'auto';
    }
  }

  function findControlsButton() {
    if (!document.body) return null;

    var walker = document.createTreeWalker(
      document.body,
      NodeFilter.SHOW_TEXT,
      null
    );

    var node;
    var first = null;

    while ((node = walker.nextNode())) {
      if ((node.nodeValue || '').trim() !== 'Controls') continue;

      var el = node.parentElement;

      if (!el) continue;

      var btn =
        el.closest(
          'button, a, .btn, [role="button"]'
        ) || el;

      if (
        !btn.parentElement ||
        btn.parentElement.children.length < 2
      ) continue;

      if (
        btn.offsetParent !== null ||
        btn.getClientRects().length
      ) {
        return btn;
      }

      if (!first) first = btn;
    }

    return first;
  }

  function makeLeooButton(model) {
    var btn = model.cloneNode(false);

    [
      'id',
      'onclick',
      'href',
      'title',
      'data-toggle',
      'data-target',
      'data-bs-toggle',
      'data-bs-target'
    ].forEach(function (a) {
      btn.removeAttribute(a);
    });

    btn.id = 'leoo-magic-block';
    btn.textContent = 'L';
    btn.title = YT_NAME;

    if (btn.tagName === 'BUTTON') {
      btn.type = 'button';
    }

    var cs = window.getComputedStyle(model);

    btn.style.background = '#32363d';
    btn.style.backgroundColor = '#32363d';
    btn.style.color = '#999999';
    btn.style.border = '0';
    btn.style.fontWeight = '700';
    btn.style.minWidth = '40px';
    btn.style.textAlign = 'center';
    btn.style.cursor = 'pointer';

    if (cs.borderRadius) {
      btn.style.borderRadius = cs.borderRadius;
    }

    if (cs.height && cs.height !== 'auto') {
      btn.style.height = cs.height;
    }

    btn.style.marginLeft = '6px';

    btn.addEventListener('click', function (ev) {
      ev.preventDefault();
      ev.stopPropagation();
      togglePanel(btn);
    });

    return btn;
  }

  function insertLeooButton() {
    if (document.getElementById('leoo-magic-block')) {
      return true;
    }

    var model = findControlsButton();

    if (!model) return false;

    model.parentElement.appendChild(
      makeLeooButton(model)
    );

    return true;
  }

  document.addEventListener(
    'click',
    function (e) {
      if (
        !leooPanel ||
        leooPanel.style.display !== 'block'
      ) return;

      var t = e.target;

      if (
        leooPanel.contains(t) ||
        (t.closest &&
          t.closest('#leoo-magic-block'))
      ) {
        return;
      }

      leooPanel.style.display = 'none';
    },
    true
  );

  var lastAttempt = 0;
  var attempts = 0;

  document.addEventListener(
    'click',
    function () {
      if (
        document.getElementById(
          'leoo-magic-block'
        )
      ) return;

      var now = Date.now();

      if (
        attempts >= 15 ||
        now - lastAttempt < 2000
      ) return;

      lastAttempt = now;
      attempts++;

      setTimeout(
        insertLeooButton,
        200
      );
    },
    true
  );

  var tries = 0;
  var settingsDone = false;
  var canvasDone = false;
  var observing = false;

  function init() {
    if (!canvasDone) {
      canvasDone = hookChatCanvas();
    }

    if (!observing) {
      var chat =
        document.getElementById('chat');

      if (chat) {
        observing = true;

        new MutationObserver(function () {
          hookChatCanvas();
        }).observe(chat, {
          childList: true,
          subtree: true
        });
      }
    }

    if (!settingsDone) {
      settingsDone = insertLeooButton();
    }

    tries++;

    if (
      !(canvasDone &&
        observing &&
        settingsDone) &&
      tries < 100
    ) {
      setTimeout(init, 200);
    }
  }

  init();
})();