Discord Watch Deleted Messages

Records all deleted messages in every opened channel and stores them so you can read it later ;)

< Feedback on Discord Watch Deleted Messages

Review: OK - script works, but has bugs

§
Posted: 2023/04/17

There is a bug, if you click on a reply, it will appear in the deleted message.

|to reproduce:

click on a reply to a message (see image)
the replied message will appear in the deleted list

§
Posted: 2023/04/17

It would seem that discord removes the same node for clicking a reply (only first time) and it is treated the same as a delete. it seems from the HTML, one can't tell the difference between a "delete" and a "clicked on reply link"

the attatched is the first one is clicking on the reply, and the second is deleting a message

§
Posted: 2023/04/17
Edited: 2023/04/17

seems when you click a reply, the element is removed then wrapped in a div tag to add the backgroundFlash flash. I guess you would need to check for added nodes and ensure that the node just removed wasn't added back to the dom wrapped in a backgroundFlash div tag.

This also means a message you clicked on the reply link was deleted, the script would not pick it up because a div tag would have been deleted, not a li.

removed_node.tagName == 'LI' might also need to check for DIV

§
Posted: 2023/04/17
Edited: 2023/04/17

I think i fixed it. I added || removed_node.tagName === 'DIV') to check method to see if you are removing a div tag. then i added a check for the message ID being removed still exists, as the wrapped element contains the same message ID, and message ID's are unique.

const messageId = removed_node.id.split("-").pop();
if(document.querySelector(`#message-content-${messageId}`) !== null){
    return;
}

I added that just inside the if statement:

if ((removed_node.tagName == 'LI' || removed_node.tagName === 'DIV') && !removed_node.querySelector('[class*="isSending-"]') && (removed_node.querySelector('[class^="markup-"]'))) {

here is the full method:


if ((removed_node.tagName == 'LI' || removed_node.tagName === 'DIV') && !removed_node.querySelector('[class*="isSending-"]') && (removed_node.querySelector('[class^="markup-"]'))) {
    const messageId = removed_node.id.split("-").pop();
    if(document.querySelector(`#message-content-${messageId}`) !== null){
        return;
    }
    let prevCount = 0;
    let prevNode = mutation.previousSibling;
    let olCount = 0; // OL child elements count

    if (prevNode) {
        if (prevNode.parentNode.tagName == 'OL') {
            olCount = prevNode.parentNode.childElementCount;

        }
    }
    while (prevNode /* && prevNode.tagName != 'OL'*/) {
        prevCount++;
        prevNode = prevNode.previousSibling;
    }

    let prevLimit = 10;
    if (olCount > prevLimit * 3 && prevCount < prevLimit) {
        return; // Skip adding deleted msgs to list if the there are less than 10 elements before the beginning of OL tag. Prevents adding deleted messages when channel dels them from cache.
    }

    /// USERNAME IN DELETED NODE
    let delmsg_usrname = ''; // Nickname of deleted msg
    let delmsg_time = ''; // time of deleted msg

    if (!(removed_node.querySelector('[class*="username-"]'))) {
        let findNode = mutation.previousSibling;
        let usrnameNode = false;
        while (findNode) {
            usrnameNode = findNode.querySelector('[class*="username-"]');
            if (usrnameNode) {
                break;
            }
            findNode = findNode.previousSibling;
        }
        if (usrnameNode) {
            delmsg_usrname = usrnameNode.textContent; // Nickname of deleted msg
        }
    } else { // if deleted message has nickname in it
        delmsg_usrname = removed_node.querySelector('[class*="username-"]').textContent;
    }

    if (delmsglist) {
        let id_curtimestamp = 'delmsg' + Date.now();
        let new_delnode = removed_node.querySelector('[id*="message-content-"]');
        let delnode_imgs = removed_node.querySelector('[id*="message-accessories-"]'); //if message has images and other accessories
        let msg_time_node = removed_node.querySelector('[id*="message-timestamp-"]');
        let msg_time_text = msg_time_node.getAttribute('datetime');
        //delmsg_time = msg_time_node.textContent;
        const mregex = /^20(\d{2})-(\d{2})-(\d{2})T(.+):.+Z/i;
        delmsg_time = msg_time_text.replace(mregex, '$4 $3/$2/$1');
        /// ADD NEW ITEM TO DELMSGS LIST /////////////
        let new_html = '<div id="' + id_curtimestamp + '" class="right-onhover-btn" style="position:absolute;">X</div> <b>' + delmsg_usrname + '</b> (' + delmsg_time + ') <br /> ' + new_delnode.innerHTML + delnode_imgs.outerHTML;// + msgs_underline;
        new_delnode.innerHTML = extra_change_message(new_html);
        new_delnode.classList.add("delmsgborder");
        delmsglist.appendChild(new_delnode);
        //delmsglist.innerHTML = delmsglist.innerHTML + msgs_underline;
        let cur_elem = delmsglist.querySelector('[id*="' + id_curtimestamp + '"]');
        // Set all mouse events for delete [X] button
        cur_elem.onclick = delmsg_close;
        delmsgs_count += 1;
        CheckMessagesCount(delmsgs_count);
        delmsgs_scroll.scrollTop = delmsgs_scroll.scrollHeight; // Scroll to the bottom of the list
        addLocalStorageItem(new_delnode.outerHTML); // Add new deleted message to localStorage array
    }

    let nextSibl = mutation.nextSibling;
    let new_node = removed_node;
    let parent_elem = mutation.parentNode;
}
if ((removed_node.tagName == 'LI' || removed_node.tagName === 'DIV') && !removed_node.querySelector('[class*="isSending-"]') && (removed_node.querySelector('[class^="markup-"]'))) {
    const messageId = removed_node.id.split("-").pop();
    if(document.querySelector(`#message-content-${messageId}`) !== null){
        return;
    }
    let prevCount = 0;
    let prevNode = mutation.previousSibling;
    let olCount = 0; // OL child elements count

    if (prevNode) {
        if (prevNode.parentNode.tagName == 'OL') {
            olCount = prevNode.parentNode.childElementCount;

        }
    }
    while (prevNode /* && prevNode.tagName != 'OL'*/) {
        prevCount++;
        prevNode = prevNode.previousSibling;
    }

    let prevLimit = 10;
    if (olCount > prevLimit * 3 && prevCount < prevLimit) {
        return; // Skip adding deleted msgs to list if the there are less than 10 elements before the beginning of OL tag. Prevents adding deleted messages when channel dels them from cache.
    }

    /// USERNAME IN DELETED NODE
    let delmsg_usrname = ''; // Nickname of deleted msg
    let delmsg_time = ''; // time of deleted msg

    if (!(removed_node.querySelector('[class*="username-"]'))) {
        let findNode = mutation.previousSibling;
        let usrnameNode = false;
        while (findNode) {
            usrnameNode = findNode.querySelector('[class*="username-"]');
            if (usrnameNode) {
                break;
            }
            findNode = findNode.previousSibling;
        }
        if (usrnameNode) {
            delmsg_usrname = usrnameNode.textContent; // Nickname of deleted msg
        }
    } else { // if deleted message has nickname in it
        delmsg_usrname = removed_node.querySelector('[class*="username-"]').textContent;
    }

    if (delmsglist) {
        let id_curtimestamp = 'delmsg' + Date.now();
        let new_delnode = removed_node.querySelector('[id*="message-content-"]');
        let delnode_imgs = removed_node.querySelector('[id*="message-accessories-"]'); //if message has images and other accessories
        let msg_time_node = removed_node.querySelector('[id*="message-timestamp-"]');
        let msg_time_text = msg_time_node.getAttribute('datetime');
        //delmsg_time = msg_time_node.textContent;
        const mregex = /^20(\d{2})-(\d{2})-(\d{2})T(.+):.+Z/i;
        delmsg_time = msg_time_text.replace(mregex, '$4 $3/$2/$1');
        /// ADD NEW ITEM TO DELMSGS LIST /////////////
        let new_html = '<div id="' + id_curtimestamp + '" class="right-onhover-btn" style="position:absolute;">X</div> <b>' + delmsg_usrname + '</b> (' + delmsg_time + ') <br /> ' + new_delnode.innerHTML + delnode_imgs.outerHTML;// + msgs_underline;
        new_delnode.innerHTML = extra_change_message(new_html);
        new_delnode.classList.add("delmsgborder");
        delmsglist.appendChild(new_delnode);
        //delmsglist.innerHTML = delmsglist.innerHTML + msgs_underline;
        let cur_elem = delmsglist.querySelector('[id*="' + id_curtimestamp + '"]');
        // Set all mouse events for delete [X] button
        cur_elem.onclick = delmsg_close;
        delmsgs_count += 1;
        CheckMessagesCount(delmsgs_count);
        delmsgs_scroll.scrollTop = delmsgs_scroll.scrollHeight; // Scroll to the bottom of the list
        addLocalStorageItem(new_delnode.outerHTML); // Add new deleted message to localStorage array
    }

    let nextSibl = mutation.nextSibling;
    let new_node = removed_node;
    let parent_elem = mutation.parentNode;
}

and this seems to fix the issue

§
Posted: 2023/04/17
Edited: 2023/04/17

Sorry to keep replying, but it seems deleting a message that you replied to can cause multiple issues. namely the

let new_delnode = removed_node.querySelector('[id*="message-content-"]');

if you allow div tags (because replied links messages are now div tags), then the deleted_node contains BOTH the replied message AND the deleted message, and querySelector('[id*="message-content-"]'); will only return the frist one (the replied message), so you will end up with the wrong message in the list.

to combat this, a querySelectorAll needs to be performed:

const contentElements = removed_node.querySelectorAll('[id*="message-content-"]');
let new_delnode = [...contentElements].find(el => !el.className.includes("repliedTextContent"));

this will ensure that the repliedTextContent is filtered out.

i also had to make changes to the above change i did:

if(document.querySelector(`#message-content-${messageId}`) !== null){
    return;
}

this would fail because the message of the same ID can be left over from the "are you sure you want to delete this message" modal. to fix this, you just need to check if the message is NOT inside a modal.

const messageId = removed_node.id.split("-").pop();
const messageContent = document.querySelector(`#message-content-${messageId}`);
if(messageContent){
    const popup = messageContent?.closest(`[class^="focusLock"]`) ?? null;
    if(!popup){
        return;
    }
}

now with these changes, i can confirm the following all work:

  • delete message normally (added to list)
  • click the reply link (not added to list)
  • deleting a message that was clicked from a reply (added to list and correct one)

here is the full check method, please disregard the previous message i sent:

function check(mutations) { // checks DOM mutations, fires when mouse over msg and new msg, even if scroll in somewhere up
    check_channel_change();
    let delmsgs_scroll = savedeletedWindow.querySelector('[id*="DELMSGS_CLASSDIV"]');
    let delmsglist = savedeletedWindow.querySelector('[id*="DELMSGS_OLMSGLIST"]');

    let scroll_elem = document.body.querySelector('[class*="scroller-kQBbkU"]');

    mutations.forEach(function (mutation) { // iterate all mutations
        mutation.removedNodes.forEach(function (removed_node) {

            let check_old_msgs = document.body.querySelector('[class*="jumpToPresentBar-"]');
            if (check_old_msgs) {
                return; // Skips adding new deleted msgs when scrolling old messages
            }

            let scroll_elem = document.body.querySelector('[class*="scroller-kQBbkU"]');
            if (scroll_elem && scroll_elem.scrollHeight > 7000 && scroll_elem.scrollTop) {
                let diff_scroll = 1;
                diff_scroll = scroll_elem.scrollHeight / scroll_elem.scrollTop;
                if (diff_scroll > 1.7) {
                    return; // Skip adding deleted mssgs because of scroll
                }
            }

            if ((removed_node.tagName == 'LI' || removed_node.tagName === 'DIV') && !removed_node.querySelector('[class*="isSending-"]') && (removed_node.querySelector('[class^="markup-"]'))) {
                const messageId = removed_node.id.split("-").pop();
                const messageContent = document.querySelector(`#message-content-${messageId}`);
                if(messageContent){
                    const popup = messageContent?.closest(`[class^="focusLock"]`) ?? null;
                    if(!popup){
                        return;
                    }
                }
                let prevCount = 0;
                let prevNode = mutation.previousSibling;
                let olCount = 0; // OL child elements count

                if (prevNode) {
                    if (prevNode.parentNode.tagName == 'OL') {
                        olCount = prevNode.parentNode.childElementCount;

                    }
                }
                while (prevNode /* && prevNode.tagName != 'OL'*/) {
                    prevCount++;
                    prevNode = prevNode.previousSibling;
                }

                let prevLimit = 10;
                if (olCount > prevLimit * 3 && prevCount < prevLimit) {
                    return; // Skip adding deleted msgs to list if the there are less than 10 elements before the beginning of OL tag. Prevents adding deleted messages when channel dels them from cache.
                }

                /// USERNAME IN DELETED NODE
                let delmsg_usrname = ''; // Nickname of deleted msg
                let delmsg_time = ''; // time of deleted msg

                if (!(removed_node.querySelector('[class*="username-"]'))) {
                    let findNode = mutation.previousSibling;
                    let usrnameNode = false;
                    while (findNode) {
                        usrnameNode = findNode.querySelector('[class*="username-"]');
                        if (usrnameNode) {
                            break;
                        }
                        findNode = findNode.previousSibling;
                    }
                    if (usrnameNode) {
                        delmsg_usrname = usrnameNode.textContent; // Nickname of deleted msg
                    }
                } else { // if deleted message has nickname in it
                    delmsg_usrname = removed_node.querySelector('[class*="username-"]').textContent;
                }

                if (delmsglist) {
                    let id_curtimestamp = 'delmsg' + Date.now();
                    const contentElements = removed_node.querySelectorAll('[id*="message-content-"]');
                    let new_delnode = [...contentElements].find(el => !el.className.includes("repliedTextContent"));
                    let delnode_imgs = removed_node.querySelector('[id*="message-accessories-"]'); //if message has images and other accessories
                    let msg_time_node = removed_node.querySelector('[id*="message-timestamp-"]');
                    let msg_time_text = msg_time_node.getAttribute('datetime');
                    //delmsg_time = msg_time_node.textContent;
                    const mregex = /^20(\d{2})-(\d{2})-(\d{2})T(.+):.+Z/i;
                    delmsg_time = msg_time_text.replace(mregex, '$4 $3/$2/$1');
                    /// ADD NEW ITEM TO DELMSGS LIST /////////////
                    let new_html = '<div id="' + id_curtimestamp + '" class="right-onhover-btn" style="position:absolute;">X</div> <b>' + delmsg_usrname + '</b> (' + delmsg_time + ') <br /> ' + new_delnode.innerHTML + delnode_imgs.outerHTML;// + msgs_underline;
                    new_delnode.innerHTML = extra_change_message(new_html);
                    new_delnode.classList.add("delmsgborder");
                    delmsglist.appendChild(new_delnode);
                    //delmsglist.innerHTML = delmsglist.innerHTML + msgs_underline;
                    let cur_elem = delmsglist.querySelector('[id*="' + id_curtimestamp + '"]');
                    // Set all mouse events for delete [X] button
                    cur_elem.onclick = delmsg_close;
                    delmsgs_count += 1;
                    CheckMessagesCount(delmsgs_count);
                    delmsgs_scroll.scrollTop = delmsgs_scroll.scrollHeight; // Scroll to the bottom of the list
                    addLocalStorageItem(new_delnode.outerHTML); // Add new deleted message to localStorage array
                }

                let nextSibl = mutation.nextSibling;
                let new_node = removed_node;
                let parent_elem = mutation.parentNode;
            }
        });
    });
}

thanks,

Victoria

§
Posted: 2023/04/17
Edited: 2023/04/17

oh. ONE more thing.

delmsg_usrname = removed_node.querySelector('[class*="username-"]').textContent;

needs to be replaced with

[...removed_node.querySelectorAll('[class*="username-"]')].find(el => el.closest(`[id^='message-reply-context']`) === null).textContent;

same issue as above with the message content including both the replied context AND the deleted message in it

Andew CoeAuthor
§
Posted: 2023/04/19

I approved that and updated the script, thankx a lot Victoria.

Post reply

Sign in to post a reply.