Discord Message Text Replacer + Reset + Roblox Embed Tool (No Flicker)

Instantly replace Discord messages on refresh without flicker, plus Roblox embed & reset menu

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Discord Message Text Replacer + Reset + Roblox Embed Tool (No Flicker)
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  Instantly replace Discord messages on refresh without flicker, plus Roblox embed & reset menu
// @author       ChatGPT
// @match        *://*.discord.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    const STORAGE_KEY = 'discordTextReplacements';

    function getReplacements() {
        return GM_getValue(STORAGE_KEY, {}) || {};
    }

    function saveReplacements(replacements) {
        GM_setValue(STORAGE_KEY, replacements);
    }

    function getMessageKey(elem) {
        const content = (elem.textContent || '').trim().slice(0, 100);
        const authorElem = elem.closest('[class*="message-"]')?.querySelector('h3');
        const author = authorElem ? authorElem.textContent.trim() : 'unknown';
        return `${author}::${content}`;
    }

    function clearAllOutlines() {
        document.querySelectorAll('[style*="outline"]').forEach(el => {
            try { el.style.outline = ''; } catch (e) {}
        });
    }

    // Replace as soon as element is inserted
    const observer = new MutationObserver(mutations => {
        const replacements = getReplacements();

        for (const m of mutations) {
            if (!m.addedNodes) continue;
            m.addedNodes.forEach(node => {
                if (!(node instanceof Element)) return;
                const targets = node.matches('[class*="messageContent"]')
                    ? [node]
                    : Array.from(node.querySelectorAll('[class*="messageContent"]'));

                targets.forEach(elem => {
                    if (elem.dataset.replaced === '1') return;
                    const key = getMessageKey(elem);
                    if (replacements[key]) {
                        elem.innerHTML = replacements[key];
                        elem.dataset.replaced = '1';
                    }
                });
            });
        }
    });

    // Start observing immediately, even before load
    const startObserver = () => {
        try {
            observer.observe(document.documentElement || document.body, { childList: true, subtree: true });
        } catch { setTimeout(startObserver, 10); }
    };
    startObserver();

    function applySingleReplacement(elem, html) {
        if (!(elem instanceof Element)) return;
        elem.innerHTML = html;
        elem.dataset.replaced = '1';
    }

    function pickAndReplaceText() {
        alert('Click on the message TEXT to replace it.');

        function onMouseOver(e) { try { e.target.style.outline = '2px solid red'; } catch {} }
        function onMouseOut(e) { try { e.target.style.outline = ''; } catch {} }

        function onClick(e) {
            e.preventDefault();
            e.stopPropagation();
            cleanup();

            const contentElem = e.target.closest('[class*="messageContent"]');
            if (!contentElem) return alert('❌ Could not find message content.');

            const key = getMessageKey(contentElem);
            const newHTML = prompt("Enter new message content (HTML allowed):");
            if (newHTML === null) return;

            const replacements = getReplacements();
            replacements[key] = newHTML;
            saveReplacements(replacements);
            applySingleReplacement(contentElem, newHTML);
            clearAllOutlines();
            alert('✅ Message replaced and saved.');
        }

        function cleanup() {
            document.removeEventListener('mouseover', onMouseOver, true);
            document.removeEventListener('mouseout', onMouseOut, true);
            document.removeEventListener('click', onClick, true);
            clearAllOutlines();
        }

        document.addEventListener('mouseover', onMouseOver, true);
        document.addEventListener('mouseout', onMouseOut, true);
        document.addEventListener('click', onClick, true);
    }

    function addRobloxEmbedBelow() {
        alert('Click on the message TEXT you want to add the Roblox embed below.');

        function onMouseOver(e) { try { e.target.style.outline = '2px solid blue'; } catch {} }
        function onMouseOut(e) { try { e.target.style.outline = ''; } catch {} }

        function onClick(e) {
            e.preventDefault();
            e.stopPropagation();
            cleanup();

            const contentElem = e.target.closest('[class*="messageContent"]');
            if (!contentElem) return alert('❌ Could not find message content.');

            const key = getMessageKey(contentElem);
            const robloxEmbed = `<div id="message-accessories-1403787320477487104" class="container_b7e1cb" bis_skin_checked="1"><article class="embedFull__623de embed__623de markup__75297" aria-hidden="false"><div class="gridContainer__623de" bis_skin_checked="1"><div class="grid__623de hasThumbnail__623de" bis_skin_checked="1"><div class="embedSuppressButton__623de" aria-label="Remove all embeds" role="button" tabindex="0" bis_skin_checked="1"><svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24"><path fill="currentColor" d="M17.3 18.7a1 1 0 0 0 1.4-1.4L13.42 12l5.3-5.3a1 1 0 0 0-1.42-1.4L12 10.58l-5.3-5.3a1 1 0 0 0-1.4 1.42L10.58 12l-5.3 5.3a1 1 0 1 0 1.42 1.4L12 13.42l5.3 5.3Z" class=""></path></svg></div><div class="embedProvider__623de embedMargin__623de" bis_skin_checked="1"><span>Roblox</span></div><div class="embedTitle__623de embedMargin__623de" bis_skin_checked="1"><a class="anchor_edefb8 anchorUnderlineOnHover_edefb8 embedTitleLink__623de embedLink__623de embedTitle__623de" tabindex="0" href="https://www.roblox.com/users/1770677206/profile" rel="noreferrer noopener" target="_blank" role="button">aFz_iReyxiee's Profile</a></div><div class="embedDescription__623de embedMargin__623de" bis_skin_checked="1">aFz_iReyxiee is one of the millions creating and exploring the endless possibilities of Roblox. Join aFz_iReyxiee on Roblox and explore together!prof</div><div class="imageContent__0f481 embedThumbnail__623de" bis_skin_checked="1"><div class="imageContainer__0f481" bis_skin_checked="1"><div class="imageWrapper imageWrapper_af017a imageZoom_af017a clickable_af017a" style="width: 80px; height: 80px;" bis_skin_checked="1"><a tabindex="-1" aria-hidden="true" class="originalLink_af017a" href="https://tr.rbxcdn.com/30DAY-Avatar-6FB199DDEBB0D4A13318D53177663D44-Png/352/352/Avatar/Png/noFilter" data-role="img" data-safe-src="https://images-ext-1.discordapp.net/external/-h7OOazWS7vinJ_WzOGAa1BRqwuy3vNmPP_RdlErfiU/https/tr.rbxcdn.com/30DAY-Avatar-6FB199DDEBB0D4A13318D53177663D44-Png/352/352/Avatar/Png/noFilter?format=webp&amp;width=88&amp;height=88"></a><div class="clickableWrapper_af017a" tabindex="0" aria-label="Image" aria-describedby="uid_4" role="button" bis_skin_checked="1"><div class="loadingOverlay_af017a" style="aspect-ratio: 1 / 1;" bis_skin_checked="1"><img alt="Image" src="https://images-ext-1.discordapp.net/external/-h7OOazWS7vinJ_WzOGAa1BRqwuy3vNmPP_RdlErfiU/https/tr.rbxcdn.com/30DAY-Avatar-6FB199DDEBB0D4A13318D53177663D44-Png/352/352/Avatar/Png/noFilter?format=webp&amp;width=88&amp;height=88" style="width: 80px; height: 80px;"></div></div></div></div></div></div></div></article></div>`;

            const currentHTML = contentElem.innerHTML || '';
            const finalHTML = currentHTML + robloxEmbed;

            const replacements = getReplacements();
            replacements[key] = finalHTML;
            saveReplacements(replacements);
            applySingleReplacement(contentElem, finalHTML);

            if (window.getSelection) {
                const sel = window.getSelection();
                if (sel && sel.removeAllRanges) sel.removeAllRanges();
            }

            clearAllOutlines();
            alert('✅ Roblox embed added below message and saved.');
        }

        function cleanup() {
            document.removeEventListener('mouseover', onMouseOver, true);
            document.removeEventListener('mouseout', onMouseOut, true);
            document.removeEventListener('click', onClick, true);
            clearAllOutlines();
        }

        document.addEventListener('mouseover', onMouseOver, true);
        document.addEventListener('mouseout', onMouseOut, true);
        document.addEventListener('click', onClick, true);
    }

    function resetAllReplacements() {
        if (confirm("⚠️ This will remove ALL replaced/edited messages. Are you sure?")) {
            saveReplacements({});
            alert('♻️ All replaced messages have been reset.');
            location.reload();
        }
    }

    try {
        GM_registerMenuCommand("✏️ Replace a message", pickAndReplaceText);
        GM_registerMenuCommand("➕ Add Roblox embed below a message", addRobloxEmbedBelow);
        GM_registerMenuCommand("♻️ Reset all replaced messages", resetAllReplacements);
    } catch {}
})();