// ==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 // @downloadURL https://update.greasyfork.icu/scripts/453622/%E4%BB%80%E4%B9%88%E5%80%BC%E5%BE%97%E4%B9%B0-%E8%90%A5%E9%94%80%E5%8F%B7%E5%B1%8F%E8%94%BD%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/453622/%E4%BB%80%E4%B9%88%E5%80%BC%E5%BE%97%E4%B9%B0-%E8%90%A5%E9%94%80%E5%8F%B7%E5%B1%8F%E8%94%BD%E5%99%A8.meta.js // ==/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) => 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} 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)=>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, `${blockKeyword}`)); } // 查询屏蔽数据库 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 = $(``); var blockIcon = $(` `); var 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 = $(``); 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 = $(``) var blockIcon = $(` `); var 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('取消
屏蔽') userList.manualBlockUser(user); } else { span.html('屏蔽
作者'); userList.manualUnblockUser(user); } refreshUserBlockTab(); }); testSpammer(user, block => { if (block) { span.html('取消
屏蔽'); } else { span.html('屏蔽
作者'); } 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 = $(` `); var a = $(``); var 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 = $(`
  • 屏蔽
    设置
  • `); $('#elevator>.back-top').before(li); li.click(function () { $('.block-setting-box-bg').show(); $('#search-user').val(""); setPageDataForSettingBox(1, 12, ''); }); }; // 添加设置框 function addSettingDiv() { var bg = $(`
    `); var box = $(`
    `); var boxHead = $(`
    `); var closeIcon = $(` `); var title = $(`屏蔽设置`); var tabChoose = $(`
    • 屏蔽作者
    • 标题关键字屏蔽
    • 作者关键字屏蔽
    • 作者签名关键字屏蔽
    • 文章话题屏蔽
    • 文章分类屏蔽
    • 更多选项
    `); 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 = $(`
    `); var searchUserBox = $(``); var blockUserUL = $(`
      `); var blockUserPageUL = $(`
        `); 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 = ` `; let tab = $(``); tab.attr("id", id); let ul = $(`
          `); tab.append(ul); // 新建屏蔽词 let newBlockKeyworkLi = $(`
        • `); let newBlockKeyworkinput = $(``); let newBlockKeyworkAddBtn = $(`
          +
          `); newBlockKeyworkLi.append(newBlockKeyworkinput); newBlockKeyworkLi.append(newBlockKeyworkAddBtn); ul.append(newBlockKeyworkLi); ul.append($("
          ")); 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 = $(``); 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 = $(``); imga.attr('href', 'https://zhiyou.smzdm.com/member/' + uinfo.uid); imga.append(img); let title = $(``); title.text(uinfo.uname); title.attr('href', 'https://zhiyou.smzdm.com/member/' + uinfo.uid); let blockBtn = $(`取消屏蔽`); let li = $(`
        • `); li.append(imga); let userRight = $(`
          `); let blockReasons = userList.getUserTag(uinfo) .filter(reason => reason && reason.length > 0); let blockReasonDiv = $(`
          `); 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 = $(`
        • `); $('#list-block-user-page').append(lastPage); lastPage.click(function () { gotoPageCallback(currentPage - 1); }); let firstPage = $(`
        • 1
        • `); $('#list-block-user-page').append(firstPage); firstPage.click(function () { gotoPageCallback(1); }); $('#list-block-user-page').append($(`
        • ...
        • `)); } for (let i = pagerStart; i <= pagerEnd; i++) { let pager = $(`
        • `); pager.text(i); if (i == currentPage) { pager.addClass('current'); } pager.click(function () { gotoPageCallback(i); }); $('#list-block-user-page').append(pager); } if (pagerEnd != totalPageCount) { $(`
        • `); $('#list-block-user-page').append($(`
        • ...
        • `)); let nextPage = $( `
        • `); $('#list-block-user-page').append(nextPage); nextPage.click(function () { gotoPageCallback(currentPage + 1); }); } } function createBlockKeywordItem(ul, keyword) { let span = $(``); span.text(keyword); let li = $(`
        • `); li.append(span); ul.append(li); } function createMoreOptionTab() { var tab = $(``); var column0 = $(`
          `) var column1 = $(`
          `) let createOption = (id, title, initValue, cb) => { let div = $(`
          `); let checkbox = $(``); let checkboxLabel = $(``); 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'); } }); } })();