Greasy Fork 支持简体中文。

Discord Auto Reaction Clicker

Automatically click reaction buttons on Discord.

// ==UserScript==
// @name         Discord Auto Reaction Clicker
// @namespace    http://tampermonkey.net/
// @version      1.9.8
// @description  Automatically click reaction buttons on Discord.
// @match        https://discord.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  const STORAGE_KEY =
    "auto-reaction-bot-b74cc9e2-6d5b-4106-9902-991b2200f706-63f2195c-c55b-455a-a7d2-967699daa727-23b2ce48-50b8-4d0a-9f69-87a9f9e1e554";

  const defaults = {
    running: false,
    reactionDelayMin: 100,
    reactionDelayMax: 1000,
    scanDelayMin: 2000,
    scanDelayMax: 5000,
    minimized: false,
    includeSuperReactions: false,
    uiPosition: { top: 10, right: 10 },
  };

  let settings = Object.assign({}, defaults, loadSettings());

  let running = settings.running;
  let reactionDelayMin = settings.reactionDelayMin;
  let reactionDelayMax = settings.reactionDelayMax;
  let scanDelayMin = settings.scanDelayMin;
  let scanDelayMax = settings.scanDelayMax;
  let includeSuperReactions = settings.includeSuperReactions;
  let isMinimized = settings.minimized;
  let scanTimeout = null;

  function saveSettings() {
    settings.running = running;
    settings.reactionDelayMin = reactionDelayMin;
    settings.reactionDelayMax = reactionDelayMax;
    settings.scanDelayMin = scanDelayMin;
    settings.scanDelayMax = scanDelayMax;
    settings.includeSuperReactions = includeSuperReactions;
    settings.minimized = isMinimized;
    GM_setValue(STORAGE_KEY, JSON.stringify(settings));
  }

  function loadSettings() {
    let stored = GM_getValue(STORAGE_KEY);
    return stored ? JSON.parse(stored) : {};
  }

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

  // Recursively clicks each reaction sequentially with a random delay.
  function clickReactionsSequentially(reactions, index, callback) {
    if (!running) return;
    if (index >= reactions.length) {
      if (callback) callback();
      return;
    }
    const reaction = reactions[index];
    if (reaction && typeof reaction.click === "function") {
      reaction.click();
      console.log("Clicked reaction:", reaction);
    }
    const delay = getRandomDelay(reactionDelayMin, reactionDelayMax);
    setTimeout(() => {
      clickReactionsSequentially(reactions, index + 1, callback);
    }, delay);
  }

  // Main function to process reaction elements.
  function processReactions() {
    if (!running) return;
    // Get all unpressed reaction elements.
    let reactions = Array.from(
      document.querySelectorAll(
        "[class^='reactionInner'][aria-pressed='false']"
      )
    );
    // If includeSuperReactions is disabled, filter out reactions with aria-label
    // that include "press to super react"
    if (!includeSuperReactions) {
      reactions = reactions.filter((reaction) => {
        const ariaLabel = reaction.getAttribute("aria-label") || "";
        return !ariaLabel.toLowerCase().includes("press to super react");
      });
    }
    if (reactions.length === 0) {
      console.log("No clickable reactions found.");
      const delay = getRandomDelay(scanDelayMin, scanDelayMax);
      console.log(`Waiting ${delay} ms before the next scan.`);
      scanTimeout = setTimeout(processReactions, delay);
      return;
    }
    console.log(`Found ${reactions.length} reaction(s) to click.`);
    clickReactionsSequentially(reactions, 0, () => {
      if (!running) return;
      const delay = getRandomDelay(scanDelayMin, scanDelayMax);
      console.log(`Waiting ${delay} ms before the next scan.`);
      scanTimeout = setTimeout(processReactions, delay);
    });
  }

  // Create the UI.
  function createUI() {
    const container = document.createElement("div");
    container.id = "reactionBotUI";
    container.style.position = "fixed";
    container.style.top = settings.uiPosition.top + "px";
    container.style.right = settings.uiPosition.right + "px";

    Object.assign(container.style, {
      backgroundColor: "#2f3136",
      border: "1px solid #202225",
      padding: "8px",
      fontSize: "12px",
      zIndex: "9999",
      borderRadius: "4px",
      color: "#dcddde",
      fontFamily: "gg sans, Helvetica Neue, Helvetica, Arial, sans-serif",
      boxShadow: "0 2px 10px 0 rgba(0,0,0,.2)",
      transition: "all 0.2s ease",
      width: "200px",
    });

    const htmlContent = `
      <div id="botHeader" style="display:flex; justify-content: space-between;
          align-items: center; margin-bottom: 8px;">
        <span style="font-weight: 600;">Reaction Bot</span>
        <button id="minimizeButton" style="background: none; border: none;
          color: #dcddde; cursor: pointer; padding: 0 4px;">${
            isMinimized ? "▲" : "▼"
          }</button>
      </div>
      <div id="botControls" style="display: ${
        isMinimized ? "none" : "block"
      };">
        <button id="toggleButton" style="width: 100%; margin-bottom: 8px;">${
          running ? "Stop" : "Start"
        }</button>
        <div style="margin-bottom: 8px;">
          <div style="margin-bottom: 4px; font-size: 11px; color: #b9bbbe;">
            Reaction Delay (ms):
          </div>
          <div style="display: flex; gap: 4px;">
            <input type="number" id="reactionDelayMin" value="${reactionDelayMin}"
              placeholder="Min" style="width: 50%;">
            <input type="number" id="reactionDelayMax" value="${reactionDelayMax}"
              placeholder="Max" style="width: 50%;">
          </div>
        </div>
        <div style="margin-bottom: 8px;">
          <label style="font-size:11px; color: #b9bbbe;">
            <input type="checkbox" id="superReactToggle" ${
              includeSuperReactions ? "checked" : ""
            }> Include Super Reactions
          </label>
        </div>
        <button id="applyButton" style="width: 100%;">Apply Settings</button>
      </div>
    `;

    container.innerHTML = htmlContent;
    document.body.appendChild(container);

    const inputs = container.querySelectorAll("input");
    inputs.forEach((input) => {
      Object.assign(input.style, {
        backgroundColor: "#202225",
        border: "1px solid #040405",
        borderRadius: "3px",
        color: "#dcddde",
        padding: "4px",
        fontSize: "12px",
        width: "100%",
      });
    });

    const buttons = container.querySelectorAll("button");
    buttons.forEach((button) => {
      if (button.id !== "minimizeButton") {
        Object.assign(button.style, {
          backgroundColor: "#4f545c",
          border: "none",
          borderRadius: "3px",
          color: "#fff",
          padding: "6px 12px",
          cursor: "pointer",
          fontSize: "12px",
        });
      }
    });

    document.getElementById("minimizeButton").addEventListener("click", () => {
      const controls = document.getElementById("botControls");
      isMinimized = !isMinimized;
      controls.style.display = isMinimized ? "none" : "block";
      document.getElementById("minimizeButton").textContent =
        isMinimized ? "▲" : "▼";
      saveSettings();
    });

    document.getElementById("toggleButton").addEventListener("click", () => {
      if (running) {
        stopScript();
      } else {
        startScript();
      }
    });

    document.getElementById("applyButton").addEventListener("click", () => {
      reactionDelayMin =
        parseInt(document.getElementById("reactionDelayMin").value, 10) ||
        reactionDelayMin;
      reactionDelayMax =
        parseInt(document.getElementById("reactionDelayMax").value, 10) ||
        reactionDelayMax;
      includeSuperReactions =
        document.getElementById("superReactToggle").checked;
      console.log("Settings applied:", {
        reactionDelayMin,
        reactionDelayMax,
        includeSuperReactions,
      });
      saveSettings();
    });
  }

  function startScript() {
    running = true;
    document.getElementById("toggleButton").textContent = "Stop";
    document.getElementById("reactionDelayMin").disabled = true;
    document.getElementById("reactionDelayMax").disabled = true;
    document.getElementById("applyButton").disabled = true;
    console.log("Script started.");
    saveSettings();
    processReactions();
  }

  function stopScript() {
    running = false;
    document.getElementById("toggleButton").textContent = "Start";
    document.getElementById("reactionDelayMin").disabled = false;
    document.getElementById("reactionDelayMax").disabled = false;
    document.getElementById("applyButton").disabled = false;
    if (scanTimeout) clearTimeout(scanTimeout);
    console.log("Script stopped.");
    saveSettings();
  }

  createUI();
  saveSettings();
})();