Discord Auto Reaction Clicker

Automatically click reaction buttons on Discord.

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();
})();