什么值得买-营销号屏蔽器

smzdm user block 什么值得买 原创页面营销号屏蔽

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         什么值得买-营销号屏蔽器
// @namespace    http://blog.ywwzwb.pw/
// @version      1.14
// @description  smzdm user block 什么值得买  原创页面营销号屏蔽
// @author       ywwzwb
// @match        https://post.smzdm.com/*
// @match        https://zhiyou.smzdm.com/member/*
// @grant GM_xmlhttpRequest
// @grant GM.xmlhttpRequest
// @grant GM_setValue
// @grant GM.setValue
// @grant GM_getValue
// @grant GM.getValue
// @grant GM_listValues
// @grant GM.listValues
// @grant GM_deleteValue
// @grant GM.deleteValue
// @grant GM_addValueChangeListener
// @grant GM.addValueChangeListener
// @connect smzdm.com
// ==/UserScript==

(function () {
    'use strict';
    if (!GM_xmlhttpRequest) {
        GM_xmlhttpRequest = GM.xmlhttpRequest;
    }
    if (!GM_setValue) {
        GM_setValue = GM.setValue;
    }
    if (!GM_getValue) {
        GM_getValue = GM.getValue;
    }
    if (!GM_listValues) {
        GM_listValues = GM.listValues;
    }
    if (!GM_deleteValue) {
        GM_deleteValue = GM.deleteValue;
    }
    if (!GM_addValueChangeListener) {
        if (GM.addValueChangeListener) {
            GM_addValueChangeListener = GM.addValueChangeListener;
        } else {
            GM_addValueChangeListener = (gmKey, listener) => { };
        }
    }
    // css
    const customCSS = `
  .icon-block-left {
    width: 16px;
    height: 16px;
    fill: currentColor
  }
  .left-layer>div.J_block{
    height: auto;
    padding-bottom: 6px
  }
  .feed-grid-wrap #feed-main-list a.z-group-data.card-icon-with-block {
    width: 65px;
    padding-left: 0px;
    text-align: center;
  }
  .feed-grid-wrap #feed-main-list a.z-group-data.card-icon-with-block-btn {
    width: auto;
  }

  .icon-block-card {
    width: 16px;
    height: 16px;
    fill: currentColor;
    vertical-align: top;
    margin-top: 1px;
    margin-right: 5px;
  }
  .icon-block-homepage {
    width: 16px;
    height: 16px;
    fill: #e62828;
    vertical-align: top;
    margin-top: 5px;
    margin-right: 3px;
    margin-left: 5px;
  }
  a.block-homepage.focus-other {
    width:auto;
    margin-left:10px;
  }
  a.block-homepage>span {
    margin-right:5px;
    color:currentColor;
  }
  .block-setting-box-bg {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(0,0,0,.5);
    z-index: 9999;
    display: none;
  }
  .block-setting-box {
    width: 80%;
    z-index: 10000;
    position: fixed;
    left: 10%;
    top: 10%;
    background-color: #fff;
  }
  .block-setting-box-head-close:hover {
    fill: #e62828
  }
  .block-setting-box-head-close {
    width: 32px;
    float: right;
    padding-top: 6px;
    padding-bottom: 6px;
  }
  .block-setting-box-head-title {
    font-size: 16px;
    color: #333;
    float: left;
  }
  .block-setting-box-head {
    height: 44px;
    line-height: 45px;
    padding: 0 14px;
    border-bottom: 1px solid #f5f5f5;
  }
  .block-user-ul {
    width:100%;
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    border-bottom: 1px solid #f5f5f5;
  }
  .block-setting-box-content-li {
    float: left;
    margin-top: 5px;
    margin-bottom: 5px;
    margin-left: 10px;
    margin-right: 10px;
    width: calc(25% - 20px);
    height:92px;
    box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, .2);
    border-radius: 4px;
  }
  .block-setting-box-content-li-user-left {
    float: left;
    width: 84px;
    height: 84px;
    margin-right: 14px;
    font-size: 0;
    text-align: center;
    overflow: hidden;
    margin-top: 5px;
    margin-left: 7px;
  }
  .block-setting-box-content-li-user-right {
    float: left;
    width: calc(100% - 105px);
    height: 80px;
  }
  .block-setting-box-content-li-user-avatar {
    border-radius: 100%;
    overflow: hidden;
    width: 100%;
    height: auto;
  }
  .block-setting-box-content-li-user-title {
    font-size: 14px;
    color: #333;
    line-height: 18px;
    height: 35px;
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    margin-right: 4px;
  }
  .block-setting-box-content-li-user-block-btn {
    margin-top: 8px;
    margin-bottom: 8px;
    display: inline-block;
    line-height: 26px;
    border: 1px solid #fee4e4;
    text-align: center;
    width: 66px;
    color: #e62828;
    border-radius: 2px;
    background: #feecec;
  }
  .block-setting-box .pagenation-list-self {
    text-align: center;
    cursor: pointer;
    margin-bottom: 10px;
    margin-top: 10px;
    width:100%
  }
  .pagenation-list-self {
    font-size: 0;
  }

  .pagenation-list-self li {
    display: inline-block;
    width: 30px;
    height: 30px;
    line-height: 30px;
    margin: 0 5px;
    color: #666;
    font-size: 14px;
    border-radius: 2px;
    background: #f7f7f7;
    overflow: hidden
  }

  .pagenation-list-self li a,.pagenation-list-self li span {
    display: block;
    width: 100%;
    height: 100%;
    color: #666;
  }

  .pagenation-list-self li.current,.pagenation-list-self li.current a,.pagenation-list-self li.page-number:hover,.pagenation-list-self li.page-turn:hover,.pagenation-list-self li a:hover {
    background-color: #e62828;
    color: #fff;
  }

  .pagenation-list-self li i {
    font-size: 12px;
  }
  #search-user{
    height: 24px;
    line-height: 24px;
    margin-left: 10px;
    margin-right: 10px;
    margin-bottom: 10px;
    background-color: #f5f5f5;
    border: 0;
    color: #333;
    padding: 2px 10px;
    border-radius: 4px;
    width: 100%;
  }
  .block-tab {
    float: left;
    display: inline;
    margin-left: 40px;
  }
  .block-tab-box{
    box-sizing: border-box;
    margin-top: 10px;
    padding: 0 10px;
    width: 100%;
    overflow-y: scroll;
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    border-bottom: 1px solid #f5f5f5;xie
    min-height:120px;
  }
  .block-setting-tab{
    display: inline;
    padding: 2px;
    cursor: pointer;
  }
  .block-setting-tab.active{
    border-bottom-width: 2px;
    border-bottom-style: solid;
    color: #e62828;
  }
  .block-keyword-item {
    display: inline-flex;
    border: 1px solid #e62828;
    margin: 2px;
    border-radius: 3px;
    cursor: default;
  }
  .block-keyword-item>span {
    padding: 2px 4px 2px 4px;
  }
  .block-keyword-item>svg {
    background: #e62828;
    width: 20px;
    cursor: pointer;
  }
  .block-keywork-new {
    display: inline-flex;
    border: 1px solid #e62828;
    margin: 2px;
    border-radius: 3px;
  }
  .block-keywork-new>input{
    border: 0;
    margin: 2px 4px 2px 4px;
    width:100px;
  }
  .block-keywork-new>div{
    display: inline-flex;
    justify-content: center;
    align-items: center;
    background: #e62828;
    width: 20px;
    cursor: pointer;
  }
  .block-keywork-new>div>i{
    color: white;
  }
  .tab-block-more-option-column {
    width: 50%;
  }
  .tab-block-more-option-item {
    width: 100%;
  }
  .tab-block-more-option-item {
    width: 100%;
    margin-bottom: 8px;
  }
  .tab-block-more-option-item>label {
    margin-left:4px;
  }
  .block-highlight {
    text-decoration-line: underline;
    text-decoration-style: wavy;
    text-decoration-color: red;
    color: red;
  }
  .block-tag-sticky {
    right: 0;
    top: 0;
    z-index: 2;
    position: absolute;
    background-color: gray;
    color: #fff;
    height: 18px;
    padding: 0 5px;
    border-radius: 2px;
    line-height: 19px;
    font-size: 12px;
  }
  .user-block-tag {
    overflow: hidden;
    max-width: 100%;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  `
    var styleSheet = document.createElement('style')
    styleSheet.type = 'text/css'
    styleSheet.innerText = customCSS
    document.head.appendChild(styleSheet)
    // classes
    class BlockConfig {
        static BLOCK_ENABLE = "block_enable";
        static BLOCK_USER_KEYWORD = "block_user_keyword";
        static BLOCK_TITLE_KEYWORD = "block_title_keyword";
        static BLOCK_ZHONG_CE = "block_zhong_ce";
        static BLOCK_SHAI_WU = "block_shai_wu";
        static BLOCK_PING_CE = "block_ping_ce";
        static BLOCK_NOTE = "block_note";
        static BLOCK_MEDIA_ACCOUNT = "block_media_account";
        static BLOCK_SPAMMER_ACCOUNT = "block_spammer_account";
        static BLOCK_OFFICIAL_ACCOUNT = "block_official_account";
        static BLOCK_MERCHANT_ACCOUNT = "block_merchant_account";
        static BLOCK_USER_DESC_KEYWORD = "block_user_desc_keyword";
        static BLOCK_ANONYMOUS = "block_anonymous";
        static BLOCK_TAG = "block_tag";
        static BLOCK_CATETORY = "block_category";
        #config;
        #gmKey;
        /**
         *
         * @param {string} gmKey
         * @param {(newData:Map<string, boolean>) => void} onChange
         * @returns
         */
        constructor(gmKey, onChange) {
            this.#gmKey = gmKey;
            this.#config = new Map();
            for (const key of BlockConfig.allConfigKeyNames()) {
                // 默认全部启用
                this.#config.set(key, true);
            }
            let config = GM_getValue(gmKey);
            if (config != undefined) {
                Object.entries(config).forEach((item) => {
                    this.#config.set(item[0], item[1]);
                });
                this.#save();
            }
            GM_addValueChangeListener(gmKey, (key, oldValue, newValue, remote) => {
                if (key != gmKey) {
                    return;
                }
                if (!remote) {
                    return;
                }
                Object.entries(newValue).forEach((item) => {
                    this.#config.set(item[0], item[1]);
                });
                onChange(this.#config);
            });
        }
        /**
         * 保存配置
         * @param {string} key 配置的 key
         * @param {boolean} value 配置值
         */
        saveConfig(key, value) {
            this.#config.set(key, value);
            this.#save();
        }
        /**
         * 使用key 读取配置
         * @param {string} key
         */
        getConfig(key) {
            return this.#config.get(key) == true;
        }
        /**
         * 获取配置 key 的描述
         * @param {*} key 配置的key
         * @returns {string} 返回描述
         */
        static getConfigKeyName(key) {
            const keyNames = new Map([
                [BlockConfig.BLOCK_ENABLE, "屏蔽总开关"],
                [BlockConfig.BLOCK_ZHONG_CE, "屏蔽众测"],
                [BlockConfig.BLOCK_SHAI_WU, "屏蔽晒物"],
                [BlockConfig.BLOCK_PING_CE, "屏蔽评测"],
                [BlockConfig.BLOCK_MEDIA_ACCOUNT, "屏蔽媒体号"],
                [BlockConfig.BLOCK_SPAMMER_ACCOUNT, "屏蔽营销号"],
                [BlockConfig.BLOCK_OFFICIAL_ACCOUNT, "屏蔽值得买官方号"],
                [BlockConfig.BLOCK_MERCHANT_ACCOUNT, "屏蔽运营号"],
                [BlockConfig.BLOCK_NOTE, "屏蔽笔记"],
                [BlockConfig.BLOCK_USER_KEYWORD, "按用户关键字屏蔽"],
                [BlockConfig.BLOCK_USER_DESC_KEYWORD, "按用户签名关键字屏蔽"],
                [BlockConfig.BLOCK_TITLE_KEYWORD, "按标题关键字屏蔽"],
                [BlockConfig.BLOCK_ANONYMOUS, "屏蔽匿名文章"],
                [BlockConfig.BLOCK_TAG, "按话题屏蔽"],
                [BlockConfig.BLOCK_CATETORY, "按分类屏蔽"]]);
            return keyNames.get(key);
        }
        /**
         * 获取所有的配置 key
         * @returns {Array<string>} key 数组
         */
        static allConfigKeyNames() {
            return [
                BlockConfig.BLOCK_ENABLE,
                BlockConfig.BLOCK_USER_KEYWORD,
                BlockConfig.BLOCK_TITLE_KEYWORD,
                BlockConfig.BLOCK_ZHONG_CE,
                BlockConfig.BLOCK_SHAI_WU,
                BlockConfig.BLOCK_PING_CE,
                BlockConfig.BLOCK_NOTE,
                BlockConfig.BLOCK_MEDIA_ACCOUNT,
                BlockConfig.BLOCK_SPAMMER_ACCOUNT,
                BlockConfig.BLOCK_OFFICIAL_ACCOUNT,
                BlockConfig.BLOCK_MERCHANT_ACCOUNT,
                BlockConfig.BLOCK_USER_DESC_KEYWORD,
                BlockConfig.BLOCK_ANONYMOUS,
                BlockConfig.BLOCK_TAG,
                BlockConfig.BLOCK_CATETORY,
            ];
        }
        get enable() {
            return this.#config.get(BlockConfig.BLOCK_ENABLE) == true;
        }
        get blockZhongce() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_ZHONG_CE) == true;
        }
        get blockShaiwu() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_SHAI_WU) == true;
        }
        get blockPingce() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_PING_CE) == true;
        }
        get blockMediaAcount() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_MEDIA_ACCOUNT) == true;
        }
        get blockSpammer() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_SPAMMER_ACCOUNT) == true;
        }
        get blockOfficial() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_OFFICIAL_ACCOUNT) == true;
        }
        get blockMerchant() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_MERCHANT_ACCOUNT) == true;
        }
        get blockNote() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_NOTE) == true;
        }
        get blockUserKeyword() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_USER_KEYWORD) == true;
        }
        get blockTitleKeyword() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_TITLE_KEYWORD) == true;
        }
        get blockUserDescKeyword() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_USER_DESC_KEYWORD) == true;
        }
        get blockAnonymous() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_ANONYMOUS) == true;
        }
        get blockTag() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_TAG) == true;
        }
        get blockCategory() {
            return this.enable && this.#config.get(BlockConfig.BLOCK_CATETORY) == true;
        }
        // private:
        #save() {
            let object = {};
            for (const [key, val] of this.#config) {
                object[key] = val;
            }
            GM_setValue(this.#gmKey, object);
        }
    }
    class KeywordBlockList {
        #gmkey;
        #keywords;
        /**
         * create block list
         * @param {string} gmKey
         * @param {(newData)=>void} onChange
         */
        constructor(gmKey, onChange) {
            this.#gmkey = gmKey;
            this.#keywords = new Set(GM_getValue(gmKey, []));
            GM_addValueChangeListener(gmKey, (key, oldValue, newValue, remote) => {
                if (key != gmKey) {
                    return;
                }
                if (!remote) {
                    return;
                }
                this.#keywords = new Set(newValue);
                onChange(this.#keywords);
            });
        }
        /**
         * add keyword
         * @param {string} keyword
         * @returns true if keyword is not exist before
         */
        push(keyword) {
            if (keyword.length == 0) {
                return false;
            }
            if (this.#keywords.has(keyword)) {
                return false;
            }
            this.#keywords.add(keyword);
            this.#save();
            return true;
        }
        /**
         * remove keyword
         * @param {string} keyword
         * @returns true if keyword exist and delete
         */
        delete(keyword) {
            if (!this.#keywords.has(keyword)) {
                return false;
            }
            this.#keywords.delete(keyword);
            this.#save();
            return true;
        }
        /**
         * test string contain any keyword
         * @param {string} str
         */
        test(str) {
            if (!str || str.length == 0) {
                return "";
            }
            const lowerStr = str.toLowerCase();
            for (const keyword of this.#keywords) {
                if (lowerStr.indexOf(keyword.toLowerCase()) >= 0) {
                    return keyword;
                }
            }
            return "";
        }
        get keywordList() {
            return this.#keywords
        }
        // private:
        #save() {
            GM_setValue(this.#gmkey, Array.from(this.#keywords));
        }
    }
    class CategoryBlockList {
        #gmkey;
        #categories;
        #articleCategories;
        /**
         * create block list
         * @param {string} gmKey
         * @param {(newData)=>void} onChange
         */
        constructor(gmKey, onChange) {
            this.#gmkey = gmKey;
            this.#categories = new Set(GM_getValue(gmKey, []));
            this.#articleCategories = new Map();
            GM_addValueChangeListener(gmKey, (key, oldValue, newValue, remote) => {
                if (key != gmKey) {
                    return;
                }
                if (!remote) {
                    return;
                }
                this.#categories = new Set(newValue);
                onChange(this.#categories);
            });
        }
        /**
         * add category
         * @param {string} category
         * @returns true if category is not exist before
         */
        push(category) {
            if (category.length == 0) {
                return false;
            }
            if (this.#categories.has(category)) {
                return false;
            }
            this.#categories.add(category);
            this.#save();
            return true;
        }
        /**
         * remove category
         * @param {string} category
         * @returns true if category exist and delete
         */
        delete(category) {
            if (!this.#categories.has(category)) {
                return false;
            }
            this.#categories.delete(category);
            this.#save();
            return true;
        }
        /**
         * test match any category
         * @param {[string]} categories
         */
        test(categories) {
            if (!categories || categories.length == 0) {
                return "";
            }
            // const lowerStr = str.toLowerCase();
            for (const category of categories) {
                if (this.#categories.has(category)) {
                    return category
                }
            }
            return "";
        }
        get categories() {
            return this.#categories
        }
        // private:
        #save() {
            GM_setValue(this.#gmkey, Array.from(this.#categories));
        }
        /**
         * 获取文章分类
         * @param {string} articleId
         * @param {(categories: [string])=>void} cb
         */
        getArticleCategories(articleId, cb) {
            let categories = this.#articleCategories.get(articleId);
            if (categories != undefined) {
                cb(categories);
                return
            }
            GM_xmlhttpRequest({
                method: 'POST',
                url: 'https://post.smzdm.com/api/cards/detail/long_article',
                data: 'article_id=' + articleId,
                headers:
                    { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
                responseType: 'json',
                onload: response => {
                    var data = response.response;
                    let categories = data.data.tongji_data.main_category.map((x) => x.title);
                    this.#articleCategories.set(articleId, categories);
                    cb(categories);
                },
                onerror: response => {
                    cb([]);
                }
            });
        }
    }
    class ManaualAction {
        static None = 0;
        static FORCE_BLOCK = 1;
        static FORCE_ALLOW = 2;
    }
    class UserInfo {
        manualOverrideAction = ManaualAction.None;
        /**
         *
         * @param {string} uid
         * @param {string} uname
         * @param {string} iconUrl
         * @param {int} manualOverrideAction
         * @param {boolean} isSpammer        营销号
         * @param {boolean} isOffical        官方号
         * @param {boolean} isMedia          媒体号
         * @param {boolean} isMerchant       运营号
         * @param {boolean} userDesc         用户签名
         * @param {number} lastRefreshTime   资料刷新时间
         *
         */
        constructor(uid, uname, iconUrl, manualOverrideAction = ManaualAction.None, isSpammer = false, isOffical = false, isMedia = false, isMerchant = false, userDesc = "", lastRefreshTime = 0) {
            this.uid = uid;
            this.uname = uname;
            this.iconUrl = iconUrl;
            this.manualOverrideAction = manualOverrideAction;
            this.isSpammer = isSpammer == true;
            this.isOffical = isOffical == true;
            this.isMedia = isMedia == true;
            this.isMerchant = isMerchant == true;
            this.userDesc = userDesc;
            this.lastRefreshTime = lastRefreshTime;

        }
        static parseFromUInfoObject(uinfo) {
            return new UserInfo(uinfo.uid,
                uinfo.uname,
                uinfo.uicon,
                uinfo.manual_override_action,
                uinfo.is_spammer == true,
                uinfo.is_offical == true,
                uinfo.is_media == true,
                uinfo.is_merchant == true,
                uinfo.user_desc == undefined ? "" : uinfo.user_desc,
                uinfo.last_refresh_time == undefined ? 0 : uinfo.last_refresh_time);
        }
        toUInfoObject() {
            return {
                "uid": this.uid,
                "uname": this.uname,
                "uicon": this.iconUrl,
                "manual_override_action": this.manualOverrideAction,
                "is_spammer": this.isSpammer,
                "is_offical": this.isOffical,
                "is_media": this.isMedia,
                "is_merchant": this.isMerchant,
                "user_desc": this.userDesc,
                "last_refresh_time": this.lastRefreshTime
            }
        }

    }
    class UserBlocker {
        #gmKey;
        #userKeywordBlockList;
        #userDescriptionKeyword;
        #userList
        /**
         *
         * @param {string} gmKey
         * @param {KeywordBlockList} userKeywordBlockList
         * @param {KeywordBlockList} userDescriptionKeyword
         * @param {(map:Map<string, UserInfo>)=>void} onChange
         */
        constructor(gmKey, userKeywordBlockList, userDescriptionKeyword, onChange) {
            this.#gmKey = gmKey;
            this.#userKeywordBlockList = userKeywordBlockList;
            this.#userDescriptionKeyword = userDescriptionKeyword;
            this.#userList = new Map();
            GM_addValueChangeListener(gmKey, (key, oldValue, newValue, remote) => {
                if (key != gmKey) {
                    return;
                }
                if (!remote) {
                    return;
                }
                this.#userList.clear();
                Object.entries(newValue).forEach((item) => {
                    this.#userList.set(item[0], UserInfo.parseFromUInfoObject(item[1]));
                })
                onChange(this.#userList);
            });
            let list = GM_getValue(gmKey);
            if (list != undefined) {
                Object.entries(list).forEach((item) => {
                    this.#userList.set(item[0], UserInfo.parseFromUInfoObject(item[1]));
                })
                return;
            }
            // convert v1 to v2
            let allKeys = GM_listValues();
            for (var uid of allKeys) {
                let oldUInfo = GM_getValue(uid);
                if (oldUInfo.oldUInfo.uid == undefined) {
                    continue;
                }
                let userInfo = new UserInfo(oldUInfo.uid,
                    oldUInfo.uname,
                    oldUInfo.uicon,
                    oldUInfo.flag
                );
                this.#userList.set(uid, userInfo)
                GM_deleteValue(uid);
            }
            this.#save();
        }

        /**
         * 屏蔽用户
         * @param {UserInfo} uinfo
         */
        manualBlockUser(uinfo) {
            uinfo.manualOverrideAction = ManaualAction.FORCE_BLOCK;
            this.saveUserInfo(uinfo);
        }
        /**
         * 解除屏蔽
         * @param {UserInfo} uinfo
         */
        manualUnblockUser(uinfo) {
            uinfo.manualOverrideAction = ManaualAction.FORCE_ALLOW;
            this.saveUserInfo(uinfo);
        }
        /**
         * 保存用户信息
         * @param {UserInfo} uinfo
         * @returns
         */
        saveUserInfo(uinfo) {
            if (uinfo == undefined) {
                return;
            }
            if (uinfo instanceof UserInfo) {
                this.#userList.set(uinfo.uid, uinfo);
                this.#save();
            }
        }
        /**
         * 检查用户是否屏蔽
         * @param {UserInfo} uinfo
         * @returns {boolean}
         */
        isUserBlock(uinfo) {
            uinfo = this.getUserInfo(uinfo.uid, uinfo);
            if (!blockConfig.enable) {
                // 屏蔽功能未启用
                return false;
            }
            if (uinfo.manualOverrideAction == ManaualAction.FORCE_ALLOW) {
                // force allow
                return false;
            }
            if (uinfo.manualOverrideAction == ManaualAction.FORCE_BLOCK) {
                // force block
                return true;
            }
            if (uinfo.isSpammer && blockConfig.blockSpammer) {
                // 营销号
                return true;
            }
            if (uinfo.isOffical && blockConfig.blockOfficial) {
                // 官方号
                return true;
            }
            if (uinfo.isMerchant && blockConfig.blockMerchant) {
                // 运营号
                return true;
            }
            if (uinfo.isMedia && blockConfig.blockMediaAcount) {
                // 媒体号
                return true;
            }
            if (blockConfig.blockUserKeyword && this.#userKeywordBlockList.test(uinfo.uname)) {
                //用户名屏蔽
                return true;
            }
            if (blockConfig.blockUserDescKeyword && uinfo.userDesc != "" && this.#userDescriptionKeyword.test(uinfo.userDesc)) {
                //用户签名屏蔽
                return true;
            }
            // TODO: 按其他信息阻断
            return false;
        }
        /**
         * 获取用户信息
         * @param {string} id
         * @param {UserInfo} defaultUserInfo
         * @returns {UserInfo}
         */
        getUserInfo(id, defaultUserInfo) {
            let uinfo = this.#userList.get(id);
            if (uinfo == undefined) {
                return defaultUserInfo;
            }
            return uinfo;
        }
        /**
         * 检查用户屏蔽原因
         * @param {UserInfo} uinfo
         * @returns {string[]}
         */
        getUserTag(uinfo) {
            let result = new Array()
            do {
                if (uinfo.manualOverrideAction == ManaualAction.FORCE_BLOCK) {
                    // force block
                    result.push("手动屏蔽");
                } else if (uinfo.manualOverrideAction == ManaualAction.FORCE_ALLOW) {
                    // force allow
                    result.push("手动放行");
                }
                if (uinfo.isSpammer) {
                    // 营销号
                    result.push("营销号");
                }
                if (uinfo.isOffical) {
                    // 官方号
                    result.push("官方号");
                }
                if (uinfo.isMerchant) {
                    // 运营号
                    result.push("运营号");
                }
                if (uinfo.isMedia) {
                    // 媒体号
                    result.push("媒体号");
                }
                if (this.#userKeywordBlockList.test(uinfo.uname).length > 0) {
                    //用户名屏蔽
                    result.push("用户关键字");
                }
                if (this.#userDescriptionKeyword.test(uinfo.userDesc).length > 0) {
                    //用户签名屏蔽
                    result.push("签名关键字");
                }
                // TODO: 按其他信息阻断
            } while (0);
            return result;
        }
        get list() {
            return this.#userList;
        }
        // private:
        #save() {
            let object = {};
            this.#userList.forEach((userInfo, uid) => {
                object[uid] = userInfo.toUInfoObject();
            });
            GM_setValue(this.#gmKey, object);
        }

    };

    let postBlockKeywordList = new KeywordBlockList("block_post_keyword_list", (newData) => {
        refreshKeywordBlockTab("tab-block-title-keyword", newData);
        refreshArticalList();
    });
    let userBlockKeywordList = new KeywordBlockList("block_user_keyword_list", (newData) => {
        refreshKeywordBlockTab("tab-block-user-keyword", newData);
        refreshArticalList();
    });
    let userDescriptionBlockKeywordList = new KeywordBlockList("block_user_desc_keyword_list", (newData) => {
        refreshKeywordBlockTab("tab-block-user-desc-keyword", newData);
        refreshArticalList();
    });
    let articleTagBlockKeywordList = new KeywordBlockList("article_tag_block_keyword_list", (newData) => {
        refreshKeywordBlockTab("tab-article-tag-block-keyword-list", newData);
        refreshArticalList();
    });

    let articleCategoryBlockKeywordList = new CategoryBlockList("article_category_block_list", (newData) => {
        refreshKeywordBlockTab("tab-article-category-block-list", newData);
        refreshArticalList();
    });
    let userList = new UserBlocker("user_list", userBlockKeywordList, userDescriptionBlockKeywordList, (newData) => {
        refreshUserBlockTab();
        refreshArticalList();
    });
    let blockConfig = new BlockConfig("block_config", (newData) => {
        refreshMoreOptionTab();
        refreshArticalList();
    });

    addSettingBtn();
    addSettingDiv();


    if (window.location.href.startsWith('https://post.smzdm.com/p/')) {
        // post
        addBlockInPost();
        return;
    }
    if (window.location.href.startsWith('https://zhiyou.smzdm.com/member/')) {
        // homepage
        addBlockInHomepage();
        return;
    }
    refreshArticalList();
    $(document).ajaxComplete(function (event, xhr, settings) {
        // 下拉加载时屏蔽
        if (settings.url.startsWith('https://post.smzdm.com/json_more')) {
            refreshArticalList();
        }
    });

    // 刷新界面, 屏蔽特定的文章
    function refreshArticalList() {
        $.each($('#feed-main-list>li'), function (index, obj) {
            // 恢复状态
            $(obj).show();
            //添加屏蔽按钮
            if ($(obj).find('.card-icon-with-block-btn').length == 0) {
                addBlockBtnTo(obj);
            }
            let titleEle = $(obj).find('.z-feed-title .z-feed-titleThree .titleOne a');
            var title = titleEle.text();
            let tags = new Set()
            let hide = false;
            let articleTags = getArticleTag(obj);
            // 屏蔽众测广告
            if (isZhongce(obj)) {
                tags.add("众测");
                hide |= blockConfig.blockZhongce;
            }
            // 屏蔽晒物
            if (isShaiwu(obj)) {
                tags.add("晒物");
                hide |= blockConfig.blockShaiwu;
            }
            // 屏蔽评测文章
            if (isPingce(obj)) {
                tags.add("评测");
                hide |= blockConfig.blockPingce;
            }
            // 屏蔽评测文章
            if (isNote(obj)) {
                tags.add("笔记");
                hide |= blockConfig.blockNote;
            }
            let articleBlockTag = articleTagBlockKeywordList.test(articleTags);
            if (articleBlockTag.length > 0) {
                tags.add("文章标签");
                hide |= blockConfig.blockTag;
            }

            // 屏蔽标题关键字
            let blockKeyword = postBlockKeywordList.test(title);
            if (blockKeyword.length > 0) {
                tags.add("标题关键字");
                hide |= blockConfig.blockTitleKeyword;
                titleEle.html(title.replace(blockKeyword, `<span class="block-highlight">${blockKeyword}</span>`));
            }
            // 查询屏蔽数据库
            var uinfo = getUserForLi(obj);
            if (!uinfo) {
                tags.add("匿名");
                hide |= blockConfig.blockAnonymous;
            }
            if (uinfo) {
                uinfo = userList.getUserInfo(uinfo.uid, uinfo);
                // 屏蔽媒体号
                if (isMediaAccountSimple(obj)) {
                    uinfo.isMedia = true;
                    userList.saveUserInfo(uinfo);
                    tags.add("媒体号");
                    hide |= blockConfig.blockMediaAcount;
                }
                // 屏蔽垃圾营销号
                // 从数据库中查找垃圾账号
                if (userList.isUserBlock(uinfo)) {
                    hide |= true;
                    userList.getUserTag(uinfo).forEach(userTag => tags.add(userTag));
                } else if (Date.now() - uinfo.lastRefreshTime > 30 * 24 * 60 * 60 * 1000) {
                    testSpammer(uinfo, block => {
                        if (block) {
                            $(obj).hide();
                            //autoNextPage();
                        } else {
                            userList.getUserTag(uinfo).forEach(userTag => tags.add(userTag));
                            if ($(obj).find('.card-icon-with-block-btn').length == 0) {
                                addBlockBtnTo(obj);
                            }
                            setBlockTagTo(obj, tags);
                        }
                    });
                }
            }
            if (blockConfig.blockCategory) {
                let articleId = $(obj).attr("data-article-id");
                articleCategoryBlockKeywordList.getArticleCategories(articleId, (categories) => {
                    if (articleCategoryBlockKeywordList.test(categories)) {
                        $(obj).hide();
                        //autoNextPage();
                    }
                });
            }
            if (hide) {
                $(obj).hide();
            }
            setBlockTagTo(obj, tags);
        });
        //autoNextPage();
    }
    function autoNextPage() {
        if ($('#feed-main-list>li:visible').length == 0) {
            // next page
            $("#J_feed_pagenation > li.page-turn.next-page").click();
        }
    }
    // 众测软文屏蔽
    function isZhongce(item) {
        return $(item).find('.z-tag-sticky').text().trim() == '众测';
    }
    // 晒物文章屏蔽
    function isShaiwu(item) {
        return $(item).find('.z-tag-sticky').text().trim() == '晒物';
    }
    // 评测文章屏蔽
    function isPingce(item) {
        return $(item).find('.z-tag-sticky').text().trim() == '评测';
    }
    // 笔记文章屏蔽
    function isNote(item) {
        return $(item).find('.z-tag-sticky').text().trim() == '笔记';
    }
    // 判断媒体账号, 简易版
    function isMediaAccountSimple(item) {
        var imgsrc = $(item).find('.feed-talent-ordinary').attr('src');
        return imgsrc && imgsrc.includes('media_medal');
    }
    function getArticleTag(item) {
        return $(item).find(".titleTwo").text().trim();
    }
    /**
     *
     * @param {UserInfo} uinfo
     * @param {(uinfo: UserInfo, success: boolean) => void} cb
     */
    function refreshUserFullInfo(uinfo, cb) {
        GM_xmlhttpRequest({
            method: 'POST',
            url: 'https://api.smzdm.com/v1/users/info',
            data: 'user_smzdm_id=' + uinfo.uid,
            headers:
                { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
            responseType: 'json',
            onload: response => {
                var data = response.response;
                if (parseInt(data.error_code) != 0) {
                    cb(uinfo, false);
                    return;
                }
                if (parseInt(data.data.role.is_media) != 0) {
                    // 媒体号
                    uinfo.isMedia = true;
                }
                if (parseInt(data.data.role.is_official) != 0) {
                    // 官方号
                    uinfo.isOffical = true;
                }
                if (parseInt(data.data.role.is_merchant) != 0) {
                    // 运营号
                    uinfo.isMerchant = true;
                }
                //   评论 / 文章 < 5 or 评论 == 0 则是营销号
                const articalsCount = parseInt(data.data.articles.article);
                const commentsCount = parseInt(data.data.comments);
                if (articalsCount > 0 && (commentsCount == 0 || commentsCount / articalsCount < 5)) {
                    uinfo.isSpammer = true;
                }
                uinfo.userDesc = data.data.meta.description;
                uinfo.lastRefreshTime = Date.now();
                userList.saveUserInfo(uinfo);

                cb(uinfo, true);
            },
            onerror: response => {
                cb(uinfo, false);
            }
        });
    }
    // 检查用户是否是垃圾账号
    /**
     *
     * @param {UserInfo} uinfo
     * @param {(block: boolean) => void} cb
     */
    function testSpammer(uinfo, cb) {
        if (userList.isUserBlock(uinfo)) {
            cb(true);
            return;
        }
        refreshUserFullInfo(uinfo, (uinfo, success) => {
            if (!success) {
                cb(false);
                return;
            }
            cb(userList.isUserBlock(uinfo));
        });
    }

    // 获取用户信息
    function getUserForLi(li_item) {
        var a = $(li_item).find('.z-feed-foot-l .z-avatar-name');
        var uname = a.text().trim();
        var href = a.attr('href');
        if (!href) {
            return null;
        }
        var uidResult = /\/(\d+)\/?$/.exec(href);
        if (!uidResult) {
            return null;
        }
        var uicon = $(li_item).find('.z-feed-foot-l .z-avatar-pic>img')
            .attr('src')
            .replace('-small.', '-middle.');
        return new UserInfo(uidResult[1], uname, uicon);
    }
    // 文章列表页面屏蔽按钮
    function addBlockBtnTo(item) {
        $(item).find('.z-feed-foot-r').children('a').addClass('card-icon-with-block');
        var a = $(`<a class='z-group-data card-icon-with-block card-icon-with-block-btn' title='屏蔽'></a>`);
        var blockIcon = $(`
      <svg class='icon-block-card' version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox = "0 0 1024 1024" >
        <path d="M202.666667 256h-42.666667a32 32 0 0 1 0-64h704a32 32 0 0 1 0 64H266.666667v565.333333a53.333333 53.333333 0 0 0 53.333333 53.333334h384a53.333333 53.333333 0 0 0 53.333333-53.333334V352a32 32 0 0 1 64 0v469.333333c0 64.8-52.533333 117.333333-117.333333 117.333334H320c-64.8 0-117.333333-52.533333-117.333333-117.333334V256z m224-106.666667a32 32 0 0 1 0-64h170.666666a32 32 0 0 1 0 64H426.666667z m-32 288a32 32 0 0 1 64 0v256a32 32 0 0 1-64 0V437.333333z m170.666666 0a32 32 0 0 1 64 0v256a32 32 0 0 1-64 0V437.333333z" p-id="2813"></path>
      </svg>`);
        var span = $(`<span>屏蔽作者</span>`)
        a.append(blockIcon);
        a.append(span);
        $(item).find('.z-feed-foot-r').append(a);
        a.click(function () {
            var li = a.parents('li');
            var uinfo = getUserForLi(li);
            if (uinfo) {
                userList.manualBlockUser(uinfo);
            }
            refreshArticalList();
            refreshKeywordBlockTab();
        });
    }
    // 添加屏蔽原因到标签
    function setBlockTagTo(item, tags) {
        let imgEle = $(item).find(".z-feed-img");
        let tagEle = imgEle.find(".block-tag-sticky");
        if (tags.size == 0) {
            if (tagEle.length == 0) {
                // 不需要添加标签
                return;
            } else {
                // 已经有了,就去除
                tagEle.remove();
                return;
            }
        }
        // 需要添加标签
        if (tagEle.length == 0) {
            tagEle = $(`<a class="block-tag-sticky""></a>`);
            imgEle.append(tagEle);
        }
        tagEle.text("标签: " + Array.from(tags).join("/"));
    }
    // 文章内部屏蔽按钮
    function addBlockInPost() {
        if ($('.author-title').length == 0) {
            return;
        }
        var authorA = $($('.author-title')[0]);
        var authorHref = authorA.attr('href');
        if (!authorHref) {
            return null;
        }
        var uidResult = /\/(\d+)\/?$/.exec(authorHref);
        if (!uidResult || uidResult.length < 2) {
            return null;
        }
        var uid = uidResult[1];
        var uname = authorA.text().trim();
        var uicon = $('.tx_name>img').attr('src').replace('-big.', '-middle.');
        let user = userList.getUserInfo(uid, new User(uid, uname, uicon));
        user.uname = uname;
        user.iconUrl = uicon;
        userList.saveUserInfo(user);
        var span = $(`<span></span>`)
        var blockIcon = $(`
        <svg class='icon-block-left' version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox = "0 0 1024 1024" >
          <path d="M202.666667 256h-42.666667a32 32 0 0 1 0-64h704a32 32 0 0 1 0 64H266.666667v565.333333a53.333333 53.333333 0 0 0 53.333333 53.333334h384a53.333333 53.333333 0 0 0 53.333333-53.333334V352a32 32 0 0 1 64 0v469.333333c0 64.8-52.533333 117.333333-117.333333 117.333334H320c-64.8 0-117.333333-52.533333-117.333333-117.333334V256z m224-106.666667a32 32 0 0 1 0-64h170.666666a32 32 0 0 1 0 64H426.666667z m-32 288a32 32 0 0 1 64 0v256a32 32 0 0 1-64 0V437.333333z m170.666666 0a32 32 0 0 1 64 0v256a32 32 0 0 1-64 0V437.333333z" p-id="2813"></path>
        </svg>`);
        var div = $('<div class="J_block comment"> </div>');
        div.append(blockIcon);
        div.append(span);
        $('.qrcode').before(div)
        div.click(function () {
            var isBlock = $(this).attr('data-block') == 'true'
            isBlock = !isBlock;
            $(this).attr('data-block', isBlock);
            if (isBlock) {
                span.html('取消<br/>屏蔽')
                userList.manualBlockUser(user);
            } else {
                span.html('屏蔽<br/>作者');
                userList.manualUnblockUser(user);
            }
            refreshUserBlockTab();
        });
        testSpammer(user, block => {
            if (block) {
                span.html('取消<br/>屏蔽');
            } else {
                span.html('屏蔽<br/>作者');
            }
            div.attr('data-block', block);
        });
    };
    // 用户首页屏蔽按钮
    function addBlockInHomepage() {
        if ($('.info-stuff-nickname>a').length == 0) {
            return;
        }
        var authorA = $('.info-stuff-nickname>a');
        var authorHref = authorA.attr('href');
        if (!authorHref) {
            return null;
        }
        var uidResult = /\/(\d+)\/?$/.exec(authorHref);
        if (!uidResult || uidResult.length < 2) {
            return null;
        }
        var uid = uidResult[1];
        var uname = authorA.text().trim();
        var uicon = $('.avatar-box>img').attr('src');
        let user = userList.getUserInfo(uid, new UserInfo(uid, uname, uicon));
        userList.saveUserInfo(user);
        var blockIcon = $(`
      <svg class='icon-block-homepage' version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox = "0 0 1024 1024" >
        <path d="M202.666667 256h-42.666667a32 32 0 0 1 0-64h704a32 32 0 0 1 0 64H266.666667v565.333333a53.333333 53.333333 0 0 0 53.333333 53.333334h384a53.333333 53.333333 0 0 0 53.333333-53.333334V352a32 32 0 0 1 64 0v469.333333c0 64.8-52.533333 117.333333-117.333333 117.333334H320c-64.8 0-117.333333-52.533333-117.333333-117.333334V256z m224-106.666667a32 32 0 0 1 0-64h170.666666a32 32 0 0 1 0 64H426.666667z m-32 288a32 32 0 0 1 64 0v256a32 32 0 0 1-64 0V437.333333z m170.666666 0a32 32 0 0 1 64 0v256a32 32 0 0 1-64 0V437.333333z" p-id="2813"></path>
      </svg>`);
        var a = $(`<a class="focus-other block-homepage"></a>`);
        var span = $(`<span>屏蔽作者</span>`);
        a.append(blockIcon);
        a.append(span);
        $('.info-stuff-set').append(a)
        a.click(function () {
            var isBlock = $(this).attr('data-block') == 'true'
            isBlock = !isBlock;
            $(this).attr('data-block', isBlock);
            if (isBlock) {
                span.text('取消屏蔽');
                userList.manualBlockUser(user);
            } else {
                span.text('屏蔽作者');
                userList.manualUnblockUser(user);
            }
            refreshUserBlockTab();
        });
        testSpammer(user, block => {
            if (block) {
                span.html('取消屏蔽');
            } else {
                span.html('屏蔽作者');
            }
            a.attr('data-block', block);
        });
    };
    // 添加屏蔽设置按钮
    function addSettingBtn() {
        var li = $(`
      <li>
        <span class="elevator-report">屏蔽<br>设置</span>
      </li>`);
        $('#elevator>.back-top').before(li);
        li.click(function () {
            $('.block-setting-box-bg').show();
            $('#search-user').val("");
            setPageDataForSettingBox(1, 12, '');
        });
    };
    // 添加设置框
    function addSettingDiv() {
        var bg = $(`<div class="block-setting-box-bg"></div>`);
        var box = $(`<div class="block-setting-box"></div>`);
        var boxHead = $(`<div class="block-setting-box-head"></div>`);
        var closeIcon = $(`<svg class='block-setting-box-head-close' version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox = "0 0 1024 1024" >
            <path d="M676.44928 688.042667L542.17728 553.813333 675.510613 420.437333a21.290667 21.290667 0 0 0 0-30.165333 21.290667 21.290667 0 0 0-30.165333 0L512.011947 523.605333 378.635947 390.272a21.333333 21.333333 0 1 0-30.208 30.165333L481.846613 553.813333 347.574613 688.042667a21.333333 21.333333 0 0 0 30.208 30.165333L512.011947 583.978667l134.229333 134.229333a21.248 21.248 0 0 0 30.208 0 21.333333 21.333333 0 0 0 0-30.165333" p-id="3802"></path>
        </svg>`);
        var title = $(`<span class="block-setting-box-head-title">屏蔽设置</span>`);
        var tabChoose = $(`<ul class= "block-tab">
            <li class="block-setting-tab active" tabid="tab-block-user">屏蔽作者</li>
            <li class="block-setting-tab" tabid="tab-block-title-keyword">标题关键字屏蔽</li>
            <li class="block-setting-tab" tabid="tab-block-user-keyword">作者关键字屏蔽</li>
            <li class="block-setting-tab" tabid="tab-block-user-desc-keyword">作者签名关键字屏蔽</li>
            <li class="block-setting-tab" tabid="tab-article-tag-block-keyword-list">文章话题屏蔽</li>
            <li class="block-setting-tab" tabid="tab-article-category-block-list">文章分类屏蔽</li>
            <li class="block-setting-tab" tabid="tab-block-more-option">更多选项</li>
        </ul>`);
        var userBlockTab = createUserBlockTab();
        var postKeywordBlockTab = createKeywordBlockTab("tab-block-title-keyword", postBlockKeywordList.keywordList, keyword => {
            const ret = postBlockKeywordList.push(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        }, keyword => {
            const ret = postBlockKeywordList.delete(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        });
        var userKeywordBlockTab = createKeywordBlockTab("tab-block-user-keyword", userBlockKeywordList.keywordList, keyword => {
            const ret = userBlockKeywordList.push(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        }, keyword => {
            const ret = userBlockKeywordList.delete(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        });
        var userDescriptionKeywordBlockTab = createKeywordBlockTab("tab-block-user-desc-keyword", userDescriptionBlockKeywordList.keywordList, keyword => {
            const ret = userDescriptionBlockKeywordList.push(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        }, keyword => {
            const ret = userDescriptionBlockKeywordList.delete(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        });
        var articleTagKeywordBlockTab = createKeywordBlockTab("tab-article-tag-block-keyword-list", articleTagBlockKeywordList.keywordList, keyword => {
            const ret = articleTagBlockKeywordList.push(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        }, keyword => {
            const ret = articleTagBlockKeywordList.delete(keyword);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        });

        var articleCategoryBlockTab = createKeywordBlockTab("tab-article-category-block-list", articleCategoryBlockKeywordList.categories, category => {
            const ret = articleCategoryBlockKeywordList.push(category);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        }, category => {
            const ret = articleCategoryBlockKeywordList.delete(category);
            if (ret) {
                refreshArticalList();
            }
            return ret;
        });
        var moreOptionTab = createMoreOptionTab();
        boxHead.append(title);
        boxHead.append(tabChoose);
        boxHead.append(closeIcon);
        box.append(boxHead);
        box.append(userBlockTab);
        box.append(postKeywordBlockTab);
        box.append(userKeywordBlockTab);
        box.append(userDescriptionKeywordBlockTab);
        box.append(articleTagKeywordBlockTab);
        box.append(articleCategoryBlockTab);
        box.append(moreOptionTab);
        bg.append(box);
        $('body').append(bg);
        closeIcon.click(function () {
            $('.block-setting-box-bg').hide();
        });
        bg.click(event => {
            if (event.target != event.currentTarget) {
                return;
            }
            $('.block-setting-box-bg').hide();
        })
        $(".block-tab li").click(function () {
            console.log("click tab");
            for (const tab of $(".block-tab li")) {
                if (tab == this) {
                    $(tab).addClass("active");
                } else {
                    $(tab).removeClass("active");
                }
            }
            let displayTab = $(this).attr("tabid");
            for (const tabBox of $(".block-tab-box")) {
                if ($(tabBox).attr("id") == displayTab) {
                    $(tabBox).show();
                } else {
                    $(tabBox).hide();
                }
            }
        });
    }
    function createUserBlockTab() {
        var userBlockTab = $(`<div class="block-tab-box" id="tab-block-user"></div>`);
        var searchUserBox = $(`<input id="search-user" type="text" value="" placeholder="筛选"/>`);
        var blockUserUL = $(`<ul class="block-user-ul" id="list-block-user"></ul>`);
        var blockUserPageUL = $(`<ul class="pagenation-list-self" id="list-block-user-page"></ul>`);
        userBlockTab.append(searchUserBox);
        userBlockTab.append(blockUserUL);
        userBlockTab.append(blockUserPageUL);
        searchUserBox.keyup(e => {
            var value = e.target.value;
            setPageDataForSettingBox(1, 12, value);
        });
        return userBlockTab;
    }
    function refreshUserBlockTab() {
        // 暂时跳转到第 1 页
        $(`#search-user`).val("");
        setPageDataForSettingBox(1, 12, "");
    }
    function createKeywordBlockTab(id, blockListData, onInsertKeyword, onRemoveKeyword) {
        let closeBtnSVG = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
            <path fill="#fff" d="M676.44928 688.042667L542.17728 553.813333 675.510613 420.437333a21.290667 21.290667 0 0 0 0-30.165333 21.290667 21.290667 0 0 0-30.165333 0L512.011947 523.605333 378.635947 390.272a21.333333 21.333333 0 1 0-30.208 30.165333L481.846613 553.813333 347.574613 688.042667a21.333333 21.333333 0 0 0 30.208 30.165333L512.011947 583.978667l134.229333 134.229333a21.248 21.248 0 0 0 30.208 0 21.333333 21.333333 0 0 0 0-30.165333" p-id="3802"></path>
        </svg>`;
        let tab = $(`<div class="block-tab-box" style="display:none; padding-bottom: 16px"></div>`);
        tab.attr("id", id);
        let ul = $(`<ul></ul>`);
        tab.append(ul);
        // 新建屏蔽词
        let newBlockKeyworkLi = $(`<li class="block-keywork-new"></li>`);
        let newBlockKeyworkinput = $(`<input type="text" placeholder="添加屏蔽词"/>`);
        let newBlockKeyworkAddBtn = $(`<div><i>+</i></div>`);
        newBlockKeyworkLi.append(newBlockKeyworkinput);
        newBlockKeyworkLi.append(newBlockKeyworkAddBtn);
        ul.append(newBlockKeyworkLi);
        ul.append($("<br/>"));
        newBlockKeyworkAddBtn.on("click", (event) => {
            let keyword = newBlockKeyworkinput.val().trim();
            newBlockKeyworkinput.val("");
            if (onInsertKeyword(keyword)) {
                createBlockKeywordItem(ul, keyword);
            }
        });
        newBlockKeyworkinput.keyup((event) => {
            if (event.keyCode == 13) {
                newBlockKeyworkAddBtn.click();
            }
        });
        // 关键词列表
        for (const keyword of blockListData) {
            createBlockKeywordItem(ul, keyword);
        }
        // UI
        ul.on("mouseenter", ".block-keyword-item", (event) => {
            $(event.currentTarget).append($(closeBtnSVG));
        });
        ul.on("mouseleave", ".block-keyword-item", (event) => {
            $(event.currentTarget).find("svg").remove();
        });
        // 删除
        ul.on("click", "svg", (event) => {
            let item = $(event.currentTarget).parent(".block-keyword-item");
            let keyword = item.find(".block-keyword").text().trim();
            item.remove();
            onRemoveKeyword(keyword);
        });
        return tab;
    }
    function refreshKeywordBlockTab(id, blockListData) {
        let ul = $(`#${id}>ul`);
        ul.find(".block-keyword-item").remove();
        for (const keyword of blockListData) {
            createBlockKeywordItem(ul, keyword);
        }
    }
    function setPageDataForSettingBox(
        page /*: int*/,
        pageSize /*: int*/,
        usernameFilter /*: string*/) {
        if (page <= 0) {
            page = 1
        }
        if (pageSize <= 0) {
            pageSize = 10;
        }

        $('#list-block-user').empty();
        $('#list-block-user-page').empty();
        let filterdUser = [...userList.list.values()];
        usernameFilter = usernameFilter.trim();
        if (usernameFilter.length > 0) {
            filterdUser = filterdUser.filter(uinfo => {
                return uinfo.uname.includes(usernameFilter)
            });
        }
        const blockedUser = filterdUser.filter(uinfo => userList.getUserTag(uinfo).length > 0);
        const userInPage = blockedUser.filter((uinfo, index) => {
            return index >= (page - 1) * pageSize && index < page * pageSize;
        });
        for (const uinfo of userInPage) {
            $('#list-block-user').append(createUserBlockCard(uinfo, () => {
                setPageDataForSettingBox(page, pageSize, usernameFilter);
                refreshArticalList();
            }));
        }
        const totalPageCount = Math.ceil(blockedUser.length / pageSize);
        createPagerForUserBlock(page, totalPageCount, (newPage) => {
            setPageDataForSettingBox(newPage, pageSize, usernameFilter);
        });
    }
    /**
     *
     * @param {UserInfo} uinfo
     * @param {*} onReloadCallback
     * @returns
     */
    function createUserBlockCard(uinfo, onReloadCallback) {
        let img = $(`<img class="block-setting-box-content-li-user-avatar"></img>`);
        if (uinfo.iconUrl == undefined ||
            uinfo.iconUrl.length == 0) {
            getICONForUser(user.uid, function (success, url) {
                img.attr('src', url);
                if (success) {
                    uinfo.iconUrl = url;
                    userList.saveUserInfo(uinfo);
                }
            });
        } else {
            img.attr('src', uinfo.iconUrl);
        }
        let imga = $(`<a class="block-setting-box-content-li-user-left"></a>`);
        imga.attr('href', 'https://zhiyou.smzdm.com/member/' + uinfo.uid);
        imga.append(img);
        let title = $(`<a class="block-setting-box-content-li-user-title"></a>`);
        title.text(uinfo.uname);
        title.attr('href', 'https://zhiyou.smzdm.com/member/' + uinfo.uid);
        let blockBtn =
            $(`<a class="block-setting-box-content-li-user-block-btn"><span>取消屏蔽</span></a>`);
        let li = $(`<li class="block-setting-box-content-li"></li>`);
        li.append(imga);
        let userRight =
            $(`<div class="block-setting-box-content-li-user-right"></div>`);
        let blockReasons = userList.getUserTag(uinfo)
            .filter(reason => reason && reason.length > 0);
        let blockReasonDiv = $(`<div class="user-block-tag"/>`);
        blockReasonDiv.text("标签:" + blockReasons.join("/"));
        userRight.append(title);
        userRight.append(blockReasonDiv);
        userRight.append(blockBtn);
        li.append(userRight);
        blockBtn.click(function () {
            userList.manualUnblockUser(uinfo);
            onReloadCallback();
        });
        return li;
    }
    function createPagerForUserBlock(currentPage, totalPageCount, gotoPageCallback) {
        let maxPagerCount = 5;
        let pagerStart = currentPage - parseInt((maxPagerCount / 2));
        if (pagerStart <= 0) {
            pagerStart = 1;
        }
        let pagerEnd = pagerStart + maxPagerCount;
        if (pagerEnd > totalPageCount) {
            pagerEnd = totalPageCount;
        }
        if (pagerStart != 1) {
            let lastPage =
                $(`<li class=""><i class="icon-angle-left-o-thin"></i></li>`);
            $('#list-block-user-page').append(lastPage);
            lastPage.click(function () {
                gotoPageCallback(currentPage - 1);
            });
            let firstPage = $(`<li class="page-number">1</li>`);
            $('#list-block-user-page').append(firstPage);
            firstPage.click(function () {
                gotoPageCallback(1);
            });
            $('#list-block-user-page').append($(`<li class="noHover">...</li>`));
        }
        for (let i = pagerStart; i <= pagerEnd; i++) {
            let pager = $(`<li class="page-number"></li>`);
            pager.text(i);
            if (i == currentPage) {
                pager.addClass('current');
            }
            pager.click(function () {
                gotoPageCallback(i);
            });
            $('#list-block-user-page').append(pager);
        }
        if (pagerEnd != totalPageCount) {
            $(`<li class=""><i class="icon-angle-left-o-thin"></i></li>`);
            $('#list-block-user-page').append($(`<li class="noHover">...</li>`));
            let nextPage = $(
                `<li class="page-turn next-page"><i class="icon-angle-right-o-thin"></i></li>`);
            $('#list-block-user-page').append(nextPage);
            nextPage.click(function () {
                gotoPageCallback(currentPage + 1);
            });
        }
    }
    function createBlockKeywordItem(ul, keyword) {
        let span = $(`<span class="block-keyword"></span>`);
        span.text(keyword);
        let li = $(`<li class="block-keyword-item"></li>`);
        li.append(span);
        ul.append(li);
    }
    function createMoreOptionTab() {
        var tab = $(`<div class="block-tab-box" id="tab-block-more-option" style="display:none; padding-bottom: 16px"></div>`);
        var column0 = $(`<div class="tab-block-more-option-column"></div>`)
        var column1 = $(`<div class="tab-block-more-option-column"></div>`)
        let createOption = (id, title, initValue, cb) => {
            let div = $(`<div class="tab-block-more-option-item"></div>`);
            let checkbox = $(`<input type="checkbox" id="${id}">`);
            let checkboxLabel = $(`<label for="${id}">${title}</label>`);
            checkbox.prop('checked', initValue);
            div.append(checkbox);
            div.append(checkboxLabel);
            checkbox.change(function () {
                cb(id, this.checked);
            });
            return div;
        }
        let allConfigKeys = BlockConfig.allConfigKeyNames();
        let i = 0;
        for (const key of allConfigKeys) {
            let item = createOption(
                `option_block_item_${key}`,
                BlockConfig.getConfigKeyName(key),
                blockConfig.getConfig(key),
                (id, newValue) => {
                    blockConfig.saveConfig(key, newValue);
                    refreshArticalList();
                });
            if (i % 2 == 0) {
                column0.append(item);
            } else {
                column1.append(item);
            }
            i++;

        }
        tab.append(column0);
        tab.append(column1);
        return tab;
    }
    /**
     * 刷新配置
     * @param {BlockConfig} config
     */
    function refreshMoreOptionTab(config) {
        let allConfigKeys = BlockConfig.allConfigKeyNames();
        for (const key of allConfigKeys) {
            $(`option_block_item_${key}`).prop('checked', config.getConfig(key));
        }
    }
    function getICONForUser(uid, cb) {
        GM_xmlhttpRequest({
            method: 'POST',
            url: 'https://api.smzdm.com/v1/users/info',
            data: 'user_smzdm_id=' + uid,
            headers:
                { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
            responseType: 'json',
            onload: response => {
                var data = response.response;
                if (parseInt(data.error_code) != 0) {
                    cb(false,
                        'https://res.smzdm.com/images/user_logo/default_avatar/5-middle.png');
                    return;
                }
                cb(true, data.data.meta.avatar);
            },
            onerror: response => {
                cb(false,
                    'https://res.smzdm.com/images/user_logo/default_avatar/5-middle.png');
            }
        });
    }
})();