Discord Fake Message Editor

Change the appearance of a Discord message locally with clickable links, styled pings, and images below text

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Discord Fake Message Editor
// @namespace    http://tampermonkey.net/
// @homepageURL  https://discord.gg/gFNAH7WNZj
// @version      1.0.0
// @description  Change the appearance of a Discord message locally with clickable links, styled pings, and images below text
// @author       Bacon But Pro
// @match        *://discord.com/channels/*
// @icon         https://cdn141.picsart.com/351217840073211.png
// @license      MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let originalMessages = {};
    let boxVisible = true;
    let clickToCopyEnabled = false;
    let helpBox;

    const ANIMATION_DURATION = 500;

    function startLoading() {
        let loadingBox = document.createElement('div');
        loadingBox.id = 'loadingBox';
        loadingBox.style.position = 'fixed';
        loadingBox.style.top = '50%';
        loadingBox.style.left = '50%';
        loadingBox.style.transform = 'translate(-50%, -50%) translateY(-20px)';
        loadingBox.style.opacity = '0';
        loadingBox.style.background = 'linear-gradient(135deg, #7289da, #2c2f33)';
        loadingBox.style.color = 'white';
        loadingBox.style.padding = '30px';
        loadingBox.style.borderRadius = '10px';
        loadingBox.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.3)';
        loadingBox.style.textAlign = 'center';
        loadingBox.style.zIndex = '9999';
        loadingBox.style.minWidth = '350px';
        loadingBox.style.fontFamily = `'Segoe UI', Tahoma, Geneva, Verdana, sans-serif`;
        loadingBox.style.transition = `opacity ${ANIMATION_DURATION}ms ease, transform ${ANIMATION_DURATION}ms ease`;

        let loadingText = document.createElement('div');
        loadingText.innerText = 'Loading Userscript';
        loadingText.style.fontSize = '20px';
        loadingText.style.marginBottom = '20px';
        loadingBox.appendChild(loadingText);

        let barContainer = document.createElement('div');
        barContainer.id = 'loadingBarContainer';
        barContainer.style.width = '100%';
        barContainer.style.height = '12px';
        barContainer.style.background = 'rgba(0, 0, 0, 0.2)';
        barContainer.style.borderRadius = '6px';
        barContainer.style.overflow = 'hidden';
        barContainer.style.marginTop = '10px';
        loadingBox.appendChild(barContainer);

        let loadingBar = document.createElement('div');
        loadingBar.id = 'loadingBar';
        loadingBar.style.height = '100%';
        loadingBar.style.width = '100%';
        loadingBar.style.background = 'linear-gradient(90deg, #99aab5, #7289da)';
        loadingBar.style.transformOrigin = 'right';
        loadingBar.style.transition = 'transform 10s linear';
        barContainer.appendChild(loadingBar);

        document.body.appendChild(loadingBox);

        requestAnimationFrame(() => {
            loadingBox.style.opacity = '1';
            loadingBox.style.transform = 'translate(-50%, -50%) translateY(0)';
        });

        requestAnimationFrame(() => {
            loadingBar.style.transform = 'scaleX(0)';
        });

        setTimeout(() => {
            loadingBox.style.opacity = '0';
            loadingBox.style.transform = 'translate(-50%, -50%) translateY(20px)';
            setTimeout(() => {
                if (loadingBox.parentNode) {
                    loadingBox.parentNode.removeChild(loadingBox);
                }
                createControlBox();
            }, ANIMATION_DURATION);
        }, 10000);
    }

    function createControlBox() {
        let box = document.createElement("div");
        box.id = "fakeMessageBox";
        box.style.position = "fixed";
        box.style.bottom = "20px";
        box.style.right = "20px";
        box.style.background = "linear-gradient(135deg, #2c2f33, #7289da)";
        box.style.color = "white";
        box.style.padding = "20px";
        box.style.borderRadius = "10px";
        box.style.boxShadow = "0 4px 20px rgba(0,0,0,0.3)";
        box.style.zIndex = "9999";
        box.style.width = "280px";
        box.style.display = "flex";
        box.style.flexDirection = "column";
        box.style.gap = "10px";
        box.style.fontFamily = `'Segoe UI', Tahoma, Geneva, Verdana, sans-serif`;
        box.style.opacity = "0";
        box.style.transform = "translateX(50px)";
        box.style.transition = `opacity ${ANIMATION_DURATION}ms ease, transform ${ANIMATION_DURATION}ms ease`;

        box.innerHTML = `
            <div>
                <label style="display: block; margin-bottom: 4px;">Message ID:</label>
                <input type="text" id="fakeMsgId" style="width: 100%; padding: 6px; border-radius: 4px; border: none; box-sizing: border-box;">
            </div>

            <div style="display: flex; flex-direction: column; gap: 5px;">
                <button id="clearMsgId" style="padding: 6px; border: none; border-radius: 4px; background: #99aab5; color: white; cursor: pointer;">Clear</button>
                <button id="toggleCopy" style="padding: 6px; border: none; border-radius: 4px; background: #99aab5; color: white; cursor: pointer;">Copy</button>
            </div>

            <div>
                <label style="display: block; margin-bottom: 4px;">New Message:</label>
                <textarea id="fakeMsgHtml" style="width: 100%; padding: 6px; border-radius: 4px; border: none; box-sizing: border-box; resize: vertical;" rows="3"></textarea>
            </div>

            <div>
                <label style="display: block; margin-bottom: 4px;">Image URL (optional):</label>
                <input type="text" id="fakeMsgImage" style="width: 100%; padding: 6px; border-radius: 4px; border: none; box-sizing: border-box;">
            </div>

            <div style="display: flex; gap: 5px;">
                <button id="applyFakeChange" style="flex: 1; padding: 6px; border: none; border-radius: 4px; background: #7289da; color: white; cursor: pointer;">Apply</button>
                <button id="resetFakeChange" style="flex: 1; padding: 6px; border: none; border-radius: 4px; background: #7289da; color: white; cursor: pointer;">Reset</button>
                <button id="hideBox" style="flex: 1; padding: 6px; border: none; border-radius: 4px; background: #7289da; color: white; cursor: pointer;">Hide</button>
                <button id="helpBtn" style="flex: 1; padding: 6px; border: none; border-radius: 4px; background: #7289da; color: white; cursor: pointer;">Help</button>
            </div>

            <div id="notification" style="display: none; color: lime; text-align: center;">Message updated!</div>
            <small style="display: block; text-align: center; margin-top: 5px;">By Bacon But Pro</small>
        `;
        document.body.appendChild(box);

        requestAnimationFrame(() => {
            box.style.opacity = "1";
            box.style.transform = "translateX(0)";
        });

        let toggleButton = document.createElement("button");
        toggleButton.id = "toggleBoxButton";
        toggleButton.innerText = "Show";
        toggleButton.style.position = "fixed";
        toggleButton.style.bottom = "20px";
        toggleButton.style.right = "20px";
        toggleButton.style.background = "#7289da";
        toggleButton.style.color = "white";
        toggleButton.style.padding = "8px 12px";
        toggleButton.style.border = "none";
        toggleButton.style.borderRadius = "4px";
        toggleButton.style.boxShadow = "0 2px 10px rgba(0,0,0,0.2)";
        toggleButton.style.zIndex = "9998";
        toggleButton.style.display = "none";
        toggleButton.style.cursor = "pointer";
        document.body.appendChild(toggleButton);

        helpBox = createHelpBox();

        document.getElementById("applyFakeChange").addEventListener("click", () => {
            let msgId = document.getElementById("fakeMsgId").value.trim();
            let newHtml = document.getElementById("fakeMsgHtml").value;
            let imageUrl = document.getElementById("fakeMsgImage").value.trim();
            if (msgId && (newHtml !== "" || imageUrl !== "")) {
                fakeEditMessage(msgId, newHtml, imageUrl);
                showNotification("Message updated!");
            }
        });

        document.getElementById("resetFakeChange").addEventListener("click", () => {
            let msgId = document.getElementById("fakeMsgId").value.trim();
            if (msgId) {
                resetMessage(msgId);
            }
        });

        document.getElementById("toggleCopy").addEventListener("click", () => {
            clickToCopyEnabled = !clickToCopyEnabled;
            alert(`Copy Mode: ${clickToCopyEnabled ? 'ON' : 'OFF'}`);
        });

        document.getElementById("hideBox").addEventListener("click", () => {
            toggleControlBox();
        });

        document.getElementById("clearMsgId").addEventListener("click", () => {
            document.getElementById("fakeMsgId").value = "";
        });

        document.getElementById("helpBtn").addEventListener("click", () => {
            if (helpBox.style.display === "none") {
                helpBox.style.display = "block";
            } else {
                helpBox.style.display = "none";
            }
        });

        toggleButton.addEventListener("click", () => {
            toggleControlBox();
        });

        document.addEventListener("keydown", (event) => {
            if (event.key === "F2") {
                toggleControlBox();
            }
        });

        document.addEventListener("click", (event) => {
            if (!clickToCopyEnabled) return;
            let messageElement = event.target.closest("[id^='message-content-']");
            if (messageElement) {
                let messageId = messageElement.id.replace("message-content-", "");
                document.getElementById("fakeMsgId").value = messageId;
                showNotification("Message ID copied!");
            }
        });
    }

    function createHelpBox() {
        let box = document.createElement("div");
        box.id = "fakeMessageHelpBox";
        box.style.position = "fixed";
        box.style.bottom = "20px";
        box.style.right = "320px";
        box.style.background = "#2c2f33";
        box.style.color = "white";
        box.style.padding = "15px";
        box.style.borderRadius = "10px";
        box.style.boxShadow = "0 4px 20px rgba(0,0,0,0.3)";
        box.style.zIndex = "9999";
        box.style.width = "250px";
        box.style.fontFamily = `'Segoe UI', Tahoma, Geneva, Verdana, sans-serif`;
        box.style.display = "none";

        box.innerHTML = `
            <h3 style="margin-top: 0;">Help</h3>
            <p style="font-size: 14px; line-height: 1.4;">
                This script lets you locally modify the appearance of a Discord message.<br><br>
                <strong>Message ID</strong>: Enable "Copy" mode and click a message to grab its ID.<br>
                <strong>New Message</strong>: Enter your custom text, links, or mentions in the format
                <code>&lt;@123456789|DisplayName&gt;</code>.<br>
                <strong>Image URL</strong>: An optional link to an image below your custom text.
            </p>
            <p style="font-size: 14px; line-height: 1.4;">
                <strong>Example:</strong><br>
                <code>&lt;@123456789|User&gt; Check out https://google.com</code>
            </p>
        `;

        document.body.appendChild(box);
        return box;
    }

    function toggleControlBox() {
        let box = document.getElementById("fakeMessageBox");
        let toggleButton = document.getElementById("toggleBoxButton");
        if (!box) return;

        if (boxVisible) {
            box.style.transition = `opacity ${ANIMATION_DURATION}ms ease, transform ${ANIMATION_DURATION}ms ease`;
            box.style.opacity = "0";
            box.style.transform = "translateX(50px)";
            setTimeout(() => {
                box.style.display = "none";
                toggleButton.style.display = "block";
                boxVisible = false;
            }, ANIMATION_DURATION);
        } else {
            box.style.display = "flex";
            box.style.opacity = "0";
            box.style.transform = "translateX(50px)";
            void box.offsetWidth;
            box.style.transition = `opacity ${ANIMATION_DURATION}ms ease, transform ${ANIMATION_DURATION}ms ease`;
            box.style.opacity = "1";
            box.style.transform = "translateX(0)";
            toggleButton.style.display = "none";
            boxVisible = true;
        }
    }

    function fakeEditMessage(messageId, newHtml, imageUrl) {
        let messageElement = document.querySelector(`#message-content-${messageId}`);
        if (messageElement) {
            if (!originalMessages[messageId]) {
                originalMessages[messageId] = messageElement.innerHTML;
            }
            let processedHtml = processContent(newHtml);
            let finalContent = "";
            if (processedHtml) {
                finalContent = `<div>${processedHtml}</div>`;
            }
            if (imageUrl) {
                finalContent += `<div style="margin-top: 5px;"><img src="${imageUrl}" style="max-width: 200px; border-radius: 5px;"></div>`;
            }
            messageElement.innerHTML = finalContent;
        } else {
            alert("Message not found. Make sure it's visible on screen.");
        }
    }

    function resetMessage(messageId) {
        let messageElement = document.querySelector(`#message-content-${messageId}`);
        if (messageElement && originalMessages[messageId]) {
            messageElement.innerHTML = originalMessages[messageId];
            delete originalMessages[messageId];
        } else {
            alert("Original message not stored or message not found.");
        }
    }

    function processLinks(text) {
        if (!text) return "";
        return text.replace(/(https:\/\/[^\s]+)/g, function(match) {
            return `<a href="${match}" target="_blank" style="color: #00b0f4; text-decoration: none;">${match}</a>`;
        });
    }

    function processMentions(text) {
        if (!text) return "";
        return text.replace(/<@([^>|]+)(?:\|([^>]+))?>/g, function(match, id, displayName) {
            if (!displayName) displayName = id;
            return `<span class="mention" style="color: #7289da; font-weight: bold;">@${displayName}</span>`;
        });
    }

    function processContent(text) {
        return processMentions(processLinks(text));
    }

    function showNotification(text) {
        let notif = document.getElementById("notification");
        notif.innerText = text;
        notif.style.display = "block";
        setTimeout(() => notif.style.display = "none", 2000);
    }

    window.addEventListener("load", startLoading);
})();