Greasy Fork

Greasy Fork is available in English.

让MCN闪耀,让荣誉的勋章更容易看到!

发现更大的世界和更多的软广!

当前为 2024-11-28 提交的版本,查看 最新版本

// ==UserScript==
// @name         让MCN闪耀,让荣誉的勋章更容易看到!
// @namespace    mcn.is.very.very.good
// @version      1.0.1
// @description  发现更大的世界和更多的软广!
// @author       Dislike soft AD
// @license MIT
// @match        https://www.zhihu.com/*
// @grant        unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// ==/UserScript==


(async () => {
    'use strict';

    // 请不要将这里改为 true,否则将会在机构前增加一个emoji表情,这可能会增加攻击性。
    const useIcon = false;

    const setCache = (name, data) => {
        const cache = JSON.parse(localStorage.getItem('mcnCache')) || {};
        cache[name] = data;
        localStorage.setItem('mcnCache', JSON.stringify(cache));
        updateMenu();
    }

    const getCache = (name) => {
        const cache = JSON.parse(localStorage.getItem('mcnCache')) || {};
        return cache[name];
    }

    const setNoMcn = (name) => {
        const noMcn = new Set(JSON.parse(localStorage.getItem('noMcn')) || []);
        noMcn.add(name);
        localStorage.setItem('noMcn', JSON.stringify(Array.from(noMcn)));
    }

    const isNoMcn = (name) => {
        const noMcn = new Set(JSON.parse(localStorage.getItem('noMcn')) || []);
        return noMcn.has(name);
    }

    const promiseMap = {};

    const getAuthorMcn = async (token) => {
        const cache = getCache(token);
        if(cache?.mcn) return cache.mcn;
        if(isNoMcn(token)) return null;
        if(promiseMap[token]) {
            return promiseMap[token];
        }
        promiseMap[token] = new Promise(async (resolve) => {
            const url = `https://www.zhihu.com/people/${token}`;
            const html = await fetch(url)
                .then(response => response.text());
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            const initialData = doc.querySelector('#js-initialData');
            const json = initialData.textContent;
            const data = JSON.parse(json);
            console.log(token);
            const mcn = data?.initialState?.entities?.users?.[token]?.mcnCompany;
            if(mcn) {
                setCache(token, {
                    mcn,
                    nickname: data?.initialState?.entities?.users?.[token]?.name ?? token,
                });
            } else {
                setNoMcn(token);
            }
            resolve(mcn);
            promiseMap[token] = null;
        });
        return promiseMap[token];
    }

    const addMcnBadge = async (authorDom) => {
        if(authorDom.querySelector('.mcn-badge')) return;
        const box = authorDom.querySelector('.AuthorInfo-head');
        const mcnBadge = document.createElement('span');
        mcnBadge.className = 'mcn-badge';
        mcnBadge.textContent = 'Loading...';
        mcnBadge.style.marginLeft = '5px';
        mcnBadge.style.display = 'inline-block';
        mcnBadge.style.color = '#DDD';
        mcnBadge.style.fontSize = '12px';
        mcnBadge.style.padding = '2px';
        box.appendChild(mcnBadge);
        const userLink = authorDom.querySelector('.UserLink-link');
        if(!userLink) return;
        const link = userLink.getAttribute('href');
        const name = link.split('/').pop();
        const mcn = await getAuthorMcn(name);
        if (mcn) {
            mcnBadge.textContent = mcn;
            mcnBadge.style.marginLeft = '5px';
            mcnBadge.style.display = 'inline-block';
            mcnBadge.style.color = '#FFFFFF';
            mcnBadge.style.backgroundColor = '#FF0000';
            mcnBadge.style.borderRadius = '3px';
            mcnBadge.style.fontSize = '12px';
            mcnBadge.style.padding = '2px';
            if (useIcon) {
                mcnBadge.textContent = '🐕‍🦺' + mcn;
                mcnBadge.style.backgroundColor = null;
                mcnBadge.style.color = "#FF0000";
            }
            mcnBadge.title = "如果是被包养了,就不要谈独立人格"
        } else {
            mcnBadge.textContent = "No MCN";
        }
    }

    const blockAuthor = async (name) => {
        const url = `https://www.zhihu.com/api/v4/members/${name}/actions/block`;
        const response = await fetch(url, {
            method: 'POST',
            credentials: 'include',
        });
        console.log(response);
        return response.status === 204;
    }

    const getMcnAuthorMap = () => {
        const cache = JSON.parse(localStorage.getItem('mcnCache')) || {};
        const mcnMap = {};
        for(const [name, data] of Object.entries(cache)) {
            if(!mcnMap[data.mcn]) {
                mcnMap[data.mcn] = [];
            }
            mcnMap[data.mcn].push({token: name, ...data});
        }
        return mcnMap;
    }

    if (typeof unsafeWindow === 'undefined') {
        window.unsafeWindow = window;
    }

    const mcn = unsafeWindow.mcn = () => {
        const mcnMap = getMcnAuthorMap();
        console.group('MCN Map');
        for(const [mcn, list] of Object.entries(mcnMap)) {
            console.groupCollapsed(mcn + ' (' + list.length + ')');
            for(const data of list) {
                console.log(data.token, "\t", data.nickname, "\t", `https://www.zhihu.com/people/${data.token}`);
            }
            console.groupEnd();
        }
        console.groupEnd();
    }

    const blockMcn = unsafeWindow.blockMcn = async (name) => {
        const mcnMap = getMcnAuthorMap();
        const authors = mcnMap[name] || [];
        if (!authors.length) {
            console.error('没有找到已记录的MCN作者 ' + name);
            return;
        }
        for (const author of authors) {
            const result = await blockAuthor(author.token);
            if (result) {
                console.log(`已屏蔽 ${author.token} ${author.nickname}`);
            } else {
                console.error(`屏蔽失败 ${author.token} ${author.nickname}`);
            }
        };
        console.log("全部完成");
        alert("全部完成");
    }

    const headDom = document.querySelector('head');
    const hiddenContent = unsafeWindow.hiddenContent = () => {
        const style = document.createElement('style');
        style.id = 'hiddenContent';
        style.textContent = `
        .RichContent {
            display: none !important;
        }
        .LabelContainer-wrapper {
            display: none !important;
        }
        `;
        headDom.appendChild(style);
    }
    const showContent = unsafeWindow.showContent = () => {
        const style = document.querySelector('style#hiddenContent');
        if (style) {
            style.remove();
        }
    }
    
    let observer;
    function runObserver() {
      if (observer) observer.disconnect();
      const handle = (node) => {
        if (node.classList && node.classList.contains('AuthorInfo')) {
            setTimeout(() => {
                addMcnBadge(node);
            });
        }
      }
      // MutationObserver
      observer = new MutationObserver((mutationsList, observer) => {
        for (let mutation of mutationsList) {
          if (mutation.type === "attributes") {
            handle(mutation.target);
          } else {
            for (const node of mutation.addedNodes) {
                handle(node);
              if (node.childNodes) {
                const nodeIterator = document.createNodeIterator(node);
                let childNode = nodeIterator.nextNode();
                while (childNode) {
                  handle(childNode);
                  childNode = nodeIterator.nextNode();
                }
              }
            }
          }
        }
      });
      const targetNode = window.document.documentElement;
      observer.observe(targetNode, { childList: true, subtree: true });
    }

    const mcnMenuId = [];
    const updateMenu = () => {
        if(typeof GM_registerMenuCommand === 'undefined'){
            return;
        }
        try {
            mcnMenuId.forEach(id => {
                GM_unregisterMenuCommand(id);
            })
            const mcnMap = getMcnAuthorMap();
            for(const [mcn, list] of Object.entries(mcnMap)) {
                const id = GM_registerMenuCommand(`屏蔽 ${mcn} (${list.length})`, async () => {
                    if (!confirm(`确定要屏蔽 ${mcn} (${list.length}) 吗?\n${list.map(v => v.nickname).join(', ')}`)) {
                        return;
                    }
                    await blockMcn(mcn);
                });
                mcnMenuId.push(id);
            }
        } catch (error) {
            console.error(error);
        }
    }

    if(typeof GM_registerMenuCommand !== 'undefined') {
        GM_registerMenuCommand("隐藏回答正文", function(event) {
            hiddenContent();
        });
        GM_registerMenuCommand("显示回答正文", function(event) {
            showContent();
        });
        GM_registerMenuCommand("清理缓存", function(event) {
            localStorage.removeItem('mcnCache');
            localStorage.removeItem('noMcn');
            updateMenu();
        });
        updateMenu();
    }
    runObserver();
    const authorDomList = document.querySelectorAll('.AuthorInfo');
    for(const authorDom of authorDomList) {
        await addMcnBadge(authorDom);
    }
})();