// ==UserScript== // @name 知乎首页信息流过滤 // @namespace https://zhaoji.wang/ // @version 0.2.2 // @description 可以按照标签过滤知乎首页信息流,隐藏含不喜欢标签的回答、文章及视频。并且可以在信息流标题下显示发布时间与编辑时间。 // @author Zhaoji Wang // @license Apache-2.0 // @match https://www.zhihu.com/ // @match https://www.zhihu.com/follow // @icon https://www.google.com/s2/favicons?domain=zhihu.com // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js // @require https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js // @require https://cdn.bootcdn.net/ajax/libs/localforage/1.9.0/localforage.min.js // @require https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js // @require https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/locale/zh-cn.min.js // @require https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/plugin/duration.min.js // @require https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/plugin/relativeTime.min.js // @grant GM_xmlhttpRequest // @connect www.zhihu.com // @connect zhuanlan.zhihu.com // @downloadURL https://update.greasyfork.icu/scripts/430427/%E7%9F%A5%E4%B9%8E%E9%A6%96%E9%A1%B5%E4%BF%A1%E6%81%AF%E6%B5%81%E8%BF%87%E6%BB%A4.user.js // @updateURL https://update.greasyfork.icu/scripts/430427/%E7%9F%A5%E4%B9%8E%E9%A6%96%E9%A1%B5%E4%BF%A1%E6%81%AF%E6%B5%81%E8%BF%87%E6%BB%A4.meta.js // ==/UserScript== "use strict"; $(() => { // 加载 Day.js dayjs.locale("zh-cn"); dayjs.extend(dayjs_plugin_duration); dayjs.extend(dayjs_plugin_relativeTime); // 将 GM_xmlhttpRequest 函数 Promise 化 const get = (url) => new Promise((resolve, reject) => GM_xmlhttpRequest({ method: "GET", url, onload(response) { resolve(response.responseText); }, onerror(error) { reject(error); } }) ); $(".Topstory-mainColumn").prepend('
'); const app = new Vue({ el: "#filter-rules", template: `
过滤规则
{{ v }}

当前尚未设置规则

`, data() { return { isLoadConfigDone: false, rules: [], titles: [], tags: {}, times: {}, barIsShown: true }; }, methods: { async loadConfig() { let config = await localforage.getItem("zhihu-filter-config"); if (!config) { config = await localforage.setItem("zhihu-filter-config", { barIsShown: true, rules: [] }); } this.barIsShown = config.barIsShown; this.rules = config.rules; this.isLoadConfigDone = true; }, async saveConfig() { await localforage.setItem("zhihu-filter-config", { barIsShown: this.barIsShown, rules: this.rules }); }, async toggleBarDisplayStatus() { this.barIsShown = !this.barIsShown; await this.saveConfig(); }, async addRule() { const newTag = prompt("请输入需要被过滤的标签"); if (newTag) { this.rules = Array.from(new Set([...this.rules, newTag])); await this.saveConfig(); } }, async removeRule(index) { this.rules.splice(index, 1); await this.saveConfig(); }, updateTitles() { this.titles = Array.from($(".ContentItem-title a")).map((v) => ({ title: $(v).text(), href: $(v).attr("href") })); setTimeout(this.updateTitles, 100); }, updateTagsAndTimes() { this.titles.forEach(async (v) => { if (!this.tags[v.title]) { if (v.href.includes("question") && !v.href.includes("answer")) { // 知乎问题 const html = await get(v.href); const tags = Array.from($(".QuestionTopic", html)).map((e) => $(e).text() ); const { created: createdTime, updatedTime } = Object.values( JSON.parse( Array.from($(html)).filter( (v) => v.id === "js-initialData" )[0].innerHTML ).initialState.entities.questions )[0]; this.tags[v.title] = tags; this.times[v.title] = { createdTime, updatedTime }; } else if (v.href.includes("question") && v.href.includes("answer")) { // 知乎问题的回答 const html = await get(v.href); const tags = Array.from($(".QuestionTopic", html)).map((e) => $(e).text() ); const { createdTime, updatedTime } = Object.values( JSON.parse( Array.from($(html)).filter( (v) => v.id === "js-initialData" )[0].innerHTML ).initialState.entities.answers )[0]; this.tags[v.title] = tags; this.times[v.title] = { createdTime, updatedTime }; } else if (v.href.includes("zhuanlan")) { // 知乎专栏的文章 const html = await get(v.href); const tags = Array.from($(".Tag.Topic", html)).map((e) => $(e).text() ); const { created: createdTime, updated: updatedTime } = Object.values( JSON.parse( Array.from($(html)).filter( (v) => v.id === "js-initialData" )[0].innerHTML ).initialState.entities.articles )[0]; this.tags[v.title] = tags; this.times[v.title] = { createdTime, updatedTime }; } else if (v.href.includes("zvideo")) { // 知乎视频 const html = await get(v.href); const tags = Array.from($(".ZVideoTag", html)).map((e) => $(e).text() ); const { publishedAt: createdTime, updatedAt: updatedTime } = Object.values( JSON.parse( Array.from($(html)).filter( (v) => v.id === "js-initialData" )[0].innerHTML ).initialState.entities.zvideos )[0]; this.tags[v.title] = tags; this.times[v.title] = { createdTime, updatedTime }; } else { this.tags[v.title] = true; } } }); setTimeout(this.updateTagsAndTimes, 1000); }, updateQuestionsDisplayStatus() { Array.from($(".TopstoryItem")).forEach((v, i) => { const title = $(v).find(".ContentItem-title a").text(); if ( !$(v).is(":hidden") && this.tags[title] && this.tags[title] !== true && this.tags[title].some((tag) => this.rules.includes(tag)) ) { $(v).hide(); console.log("已过滤问题:", title); } }); setTimeout(this.updateQuestionsDisplayStatus, 100); }, updateQuestionsTimeMark() { Array.from($(".TopstoryItem")).forEach((v, i) => { const $title = $(v).find(".ContentItem-title a"); const title = $title.text(); if ( !$(v).is(":hidden") && !$(v).find(".time-mark").length && this.times[title] && this.times[title] !== true ) { const createdTime = this.times[title].createdTime; const updatedTime = this.times[title].updatedTime; const createdTimeStr = `${dayjs .duration(dayjs().diff(this.times[title].createdTime * 1000)) .humanize()}前`; const updatedTimeStr = `${dayjs .duration(dayjs().diff(this.times[title].updatedTime * 1000)) .humanize()}前`; if (createdTime === updatedTime) { $title.parent().after( `
发布于 ${createdTimeStr}
` ); } else { if (createdTimeStr === updatedTimeStr) { $title.parent().after( `
编辑于 ${updatedTimeStr}
` ); } else { $title.parent().after( `
发布于 ${createdTimeStr} → 编辑于 ${updatedTimeStr}
` ); } } } }); setTimeout(this.updateQuestionsTimeMark, 100); } }, async created() { await this.loadConfig(); this.updateTitles(); this.updateTagsAndTimes(); this.updateQuestionsDisplayStatus(); this.updateQuestionsTimeMark(); } }); });