Facebook (Messenger) Blacklist

This is how you really block people

// ==UserScript==
// @name         Facebook (Messenger) Blacklist
// @namespace    AAAAAAAA.com
// @version      2.1
// @description  This is how you really block people
// @author       ducktrshessami
// @match        *://www.facebook.com/*
// @run-at       document-end
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
// ==/UserScript==

(function() {
    const blacklist = [ // Fill this list with their Facebook names
        ""
    ];

    /***************** ^^ Edit above list ^^ *****************/

    function main() { // Triggered by page change on facebook.com
        theMeat();
        recentMessage();
    }

    function cleanup() { // Check for removed names from the blacklist
        for (var name in blacklistMetadata) {
            if (!blacklist.includes(name)) {
                delete blacklistMetadata[name];
            }
        }
    }

    async function update(name, data, thread) { // Update a blacklisted user's nickname
        if (!blacklistMetadata[name]) {
            blacklistMetadata[name] = { nicknames: {} };
        }
        blacklistMetadata[name].avatar = data.avatar || blacklistMetadata[name].avatar;
        blacklistMetadata[name].nicknames[thread] = data.nickname || blacklistMetadata[name].nicknames[thread];

        cleanup();
        window.localStorage.setItem("MessengerBlacklistMetadata", JSON.stringify(blacklistMetadata));
    }

    function theMeat() { // Hide messages and read receipts (also trigger nickname update)
        let tabs = $("[data-pagelet='ChatTab']");
        if (tabs.length) {
            blacklist.forEach((name) => {
                let curTab, title, messages, nickname;
                let seen = tabs.find(`span > img[alt*="Seen by ${name}"]:visible`);
                let avatar = seen.attr("src");
                let filTabs = tabs.has(seen);
                seen.hide();
                if (filTabs.length) { // Get messages with same avatar as read receipt
                    for (let i = 0; i < filTabs.length; i++) {
                        curTab = $(filTabs[i]);
                        title = curTab.children().children().attr("aria-label");
                        messages = curTab.find("div[data-testid='incoming_group']:visible").has(`img[src='${avatar}']:visible`).hide();
                        nickname = messages.first().children(":last-child").children(":first-child").text();
                        if (title && (nickname || avatar)) {
                            update(name, { nickname: nickname, avatar: avatar }, title);
                        }
                    }
                }
                else {
                    if (blacklistMetadata[name]) { // Get messages with stored avatar
                        for (let i = 0; i < tabs.length; i++) {
                            curTab = $(tabs[i]);
                            title = curTab.children().children().attr("aria-label");
                            avatar = blacklistMetadata[name].avatar;
                            messages = curTab.find("div[data-testid='incoming_group']:visible").has(`img[src='${avatar}']:visible`).hide();
                            nickname = messages.first().children(":last-child").children(":first-child").text();
                            if (title && (nickname || avatar)) {
                                update(name, { nickname: nickname, avatar: avatar }, title);
                            }
                        }
                    }
                }
            });
        }
    }

    async function recentMessage() { // Hide latest message in chat head tooltip thing
        for (let name in blacklistMetadata) {
            for (let thread in blacklistMetadata[name].nicknames) {
                let nickname = blacklistMetadata[name].nicknames[thread] || name;
                let recent = $(`div > span:visible`).filter(function() {
                    if (this.childElementCount) {
                        let content = getCleanContent(this.children[0]);
                        return content.startsWith(`${nickname}:`);
                    }
                });
                recent.hide();
            }
        }
    }

    function getCleanContent(element) { // Parse text content with image alts
        let content = "";
        for (let child of element.childNodes) {
            if (child.tagName == "SPAN") {
                let imgText = $(child).children().attr("alt");
                if (imgText) {
                    content += imgText;
                }
            }
            else {
                content += child.textContent;
            }
        }
        return content;
    }

    var blacklistMetadata, observer = new MutationObserver(main);
    if (!(blacklistMetadata = JSON.parse(window.localStorage.getItem("MessengerBlacklistMetadata")))) { // Check for stored value
        blacklistMetadata = new Object;
        window.localStorage.setItem("MessengerBlacklistMetadata", JSON.stringify(blacklistMetadata));
    }
    observer.observe(document.body, { // Wait for page change
        childList: true,
        subtree: true
    });
    window.addEventListener("beforeunload", () => { // Store before leaving
        cleanup();
        window.localStorage.setItem("MessengerBlacklistMetadata", JSON.stringify(blacklistMetadata));
    });
})();