Discord Message Logger

logs deleted discord messages

// ==UserScript==
// @name        Discord Message Logger
// @namespace   -
// @match         https://discordapp.com/activ*
// @match         https://discordapp.com/channel*
// @match         https://discord.com/activ*
// @match         https://discord.com/channel*
// @match         https://discord.com/channels/*
// @include       https://discord.com/*
// @grant         none
// @version       1.0
// @author        Koyd
// @description   logs deleted discord messages
// @run-at        document-start
// @compatible    chrome
// @compatible    firefox
// @compatible    opera
// @compatible    edge
// @compatible    safari
// @license       MIT
// ==/UserScript==

var allObservers = [];
var currentParent = null;

function disconnectAllObservers() {
    allObservers.forEach(function(observer) {
        observer.disconnect();
    });
    // Clear the array after disconnecting all observers
    allObservers = [];
}


function formatLoggedNode(node) {
    var text = "";

    var mediaContentNode = node.querySelector('.container_dbadf5');

    if (mediaContentNode) {
        // Convert all of it into plain text
        var mediaContentText = mediaContentNode.innerHTML;

        // Use regex to find links like 'https://cdn.discordapp.com/attachments*'
        var linkRegex = /https:\/\/cdn\.discordapp\.com\/attachments\/[^'"\s]*/g;
        var matches = mediaContentText.match(linkRegex);

        // Append the links to the text variable
        if (matches) {
            text += matches.join("\n");
        }

        // Delete the mediaContentNode from the main node
        mediaContentNode.remove();
    }

    var messageContentNode = node.querySelector('.contents_f41bb2')
    var textContentNode = messageContentNode.querySelector('.markup_a7e664.messageContent__21e69');
    textContentNode.style.color = 'red';

    var spanElement = textContentNode.querySelector('span');

    if (spanElement) {
        textContentNode.querySelector('span').innerHTML += "\n" + text;
    } else {
        // If span doesn't exist, create one and append text
        textContentNode.innerHTML = '<span>' + text + '</span>' + textContentNode.innerHTML;
    }

    return node;
};


function setupMutationObserver(parent) {
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.removedNodes.length > 0) {
                Array.from(mutation.removedNodes).forEach(function(node) {
                    if (node.classList && node.classList.contains('messageListItem__6a4fb')) {
                        // Check if the removed node has the specified class
                        var messageContentNode = node.querySelector('.contents_f41bb2')

                        var subTextNode = messageContentNode.querySelector('.markup_a7e664.messageContent__21e69');
                        
                         // Check if "isSending__6e109" class doesn't exist in the subnode
                        if (subTextNode && subTextNode.classList.contains('isSending__6e109')) {
                            return;
                        }

                        // Get the deleted message ID
                        var deletedIdMatch = node.id.match(/chat-messages-\d+-(\d+)/);
                        var deletedMessageId = deletedIdMatch ? parseInt(deletedIdMatch[1]) : null;

                        // Find the smallest index whose ID is greater than the deleted one
                        var minIndex = Array.from(parent.children).reduce(function(minIndex, child) {
                            var childIdMatch = child.id.match(/chat-messages-\d+-(\d+)/);
                            var childMessageId = childIdMatch ? parseInt(childIdMatch[1]) : null;

                            if (!isNaN(childMessageId) && childMessageId > deletedMessageId) {
                                return Math.min(minIndex, Array.from(parent.children).indexOf(child));
                            }

                            return minIndex;
                        }, Number.MAX_SAFE_INTEGER);

                        // Modify the node
                        node = formatLoggedNode(node);

                        // Append the modified node back to the parent at the calculated index
                        if (minIndex < Number.MAX_SAFE_INTEGER) {
                            parent.insertBefore(node, parent.children[minIndex]);
                        } else {
                            parent.insertBefore(node, parent.children[parent.children.length - 1]);
                        }
                    }
                });
            }
        });

      allObservers.push(observer)
    });


    // Configuration of the observer:
    var config = { childList: true };

    // Pass in the target node, as well as the observer options
    observer.observe(parent, config);
}

setInterval(function () {
    // Check if the URL is in the form https://discord.com/channels*
    if (!/^https:\/\/discord\.com\/channels/.test(window.location.href)) {
        // If not, disconnect observers and return
        disconnectAllObservers();
        currentParent = null;
        return;
    }

    var parent = document.querySelector('[data-list-id="chat-messages"]');

    if (!parent) {
        currentParent = null; // Set currentParent to null if the parent doesn't exist
        return;
    }

    if (parent !== currentParent) {
        // Disconnect observers only if the parent node has changed
        disconnectAllObservers();
        setupMutationObserver(parent);
        currentParent = parent; // Update the current parent
    }
}, 1000); // Check every 1000 milliseconds (1 second)