// ==UserScript==
// @name 知乎-我不感兴趣
// @namespace
// @version 4.3.4
// @description 隐藏掉所有不包含你关注话题的主页推送
// @author MQ
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_notification
// @grant GM_xmlhttpRequest
// @grant window
// @require https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.18.2/babel.js
// @require https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.js
// @connect zhuanlan.zhihu.com
// @include /www\.zhihu\.com/(follow)?$/
// @namespace https://greasyfork.org/users/159603
// @license MIT
// ==/UserScript==
/* jshint ignore:start */
var MultiString = function(f) {
return f.toString().split('\n').slice(1, -1).join('\n');
}
/* jshint ignore:end */
var inline_src = MultiString(function() {/**
(async() => {
'use strict';
const MAX_FILTER_COUNT = 100; //最多主动刷新的条目数
const USER_ID_KEY = "user-id";
const USER_TOPICS_KEY = "user-topics";
window.GM_xmlhttpRequest = GM_xmlhttpRequest
let GET = (url, headers) => {
return new Promise((resolve, reject) => {
let req = GM_xmlhttpRequest({
method:"GET",
url: url,
headers: headers,
fetch: true,
onerror: ()=>reject(`fetch ${url} failed`),
onload: (e)=>resolve(e.responseText)
});
});
};
let getUserId = async () => {
let raw = await GET("/api/v4/me");
let data = JSON.parse(raw);
return data.url_token;
};
let userId = GM_getValue(USER_ID_KEY, null);
if (!userId) {
userId = await getUserId();
GM_setValue(USER_ID_KEY, userId);
} else {
setTimeout(async () => {
let newValue = await getUserId();
if (newValue != userId) {
userId = newValue;
GM_setValue(USER_ID_KEY, userId);
}
}, 1000);
}
let getUserTopics = async (uid) => {
//以下部分获取用户关注话题
let isEnd = true;
let offset = 0;
let topics = [];
do {
let raw = await GET(`/api/v4/members/${uid}/following-topic-contributions?offset=${offset}&limit=20`);
let batch = JSON.parse(raw);
isEnd = batch.paging.is_end;
topics = topics.concat(batch.data.map(e => e.topic.name));
offset += batch.data.length;
} while(!isEnd);
console.log("your followed topics", topics);
return topics;
};
let followedTopics = GM_getValue(USER_TOPICS_KEY, null);
if (!followedTopics) {
followedTopics = await getUserTopics(userId);
GM_setValue(USER_TOPICS_KEY, followedTopics);
} else {
setTimeout(async ()=> {
let newValue = await getUserTopics(userId);
if (newValue != followedTopics) {
followedTopics = newValue;
GM_setValue(USER_TOPICS_KEY, followedTopics);
}
}, 1000);
}
//全局中没被隐藏的卡片数
let nonHiddenCards = 0;
let allCards = 0;
let fillNewCards = () => setTimeout(() => window.dispatchEvent(new Event("resize"))); //发送resize事件来补充新的内容
//检查函数
let checkCards = async (cards) => {
if (cards.length == 0) return;
await Promise.all(cards.map(async(e) => {
try{
if (e.hidden) return;
allCards++;
let answerUrl = e.querySelector("h2.ContentItem-title a");
if (!answerUrl) {
console.log(e, 'is not checked');
return;
}
e.hidden = true;
let realUrl = answerUrl.getAttribute('href');
if (realUrl.startsWith('//')) { //知乎专栏链接特殊处理
realUrl = "https:"+realUrl //GM_xmlhttpRequest不能识别//开头的链接,需要手动加https
}
let questionPage = await GET(realUrl);
let parser = new DOMParser();
let page = parser.parseFromString(questionPage, "text/html");
let topics = page.querySelector("meta[itemProp=keywords]");
if (topics) { //知乎回答通过这里筛选话题
topics = topics.content.split(',');
} else if (realUrl.search("zvideo") == -1) { //专栏文章通过这里筛选话题
topics = [...page.querySelectorAll("#root > div > main > div > article > div.Post-topicsAndReviewer > div > div > span > a > div")].map(e=>e.textContent);
} else { // 是视频号
topics = [...page.querySelectorAll("#root > div > main > article > div.ZVideo-mainColumn > div.ZVideo-tags > div > a")].map(e=>e.textContent);
}
topics = topics.filter(e=>e)
if (topics.length == 0) {
console.warn(answerUrl, "got empty topics");
return;
}
if (topics.filter(v=>followedTopics.indexOf(v) != -1).length == 0) { //如果里面没有一个话题是用户关注的
console.log(topics, "hide", e);
} else {
console.log(topics, "not hide");
nonHiddenCards++;
e.hidden = false;
}
} catch(x) {
console.log(x, e);
e.hidden = false;
} finally {
if (e.hidden) {
setTimeout(()=>e.remove());
}
}
}));
if (nonHiddenCards < 8 && allCards < MAX_FILTER_COUNT) {
console.log(`too few cards (${nonHiddenCards}/${allCards}) requesting for more...`);
fillNewCards();
} else if (nonHiddenCards < 8 && allCards >= MAX_FILTER_COUNT) {
let option = {
text: `知乎-我不感兴趣 已为您过滤了 ${allCards-nonHiddenCards} 条推送,仍然没有足够的条目填满您的主页,您可以去喝杯茶,看看别的东西。`,
title: "主动刷新已停止",
onclick: console.log,
ondone: console.log
};
GM_notification(option, null);
nonHiddenCards = 8;
}
};
//注册钩子
let registerHook = () => {
let firstCard = document.querySelector("div.Card.TopstoryItem");
if (!firstCard) {
setTimeout(registerHook, 500);
}
let mainFrame = firstCard.parentElement;
let ob = new MutationObserver((records) => {
//console.log("mutation records: ", records);
let addedCards = records.reduce((r, e) => r.concat([...e.addedNodes]), []);
checkCards(addedCards);
});
let config = { childList: true };
ob.observe(mainFrame, config);
console.log("hooked");
nonHiddenCards = 0;
allCards = 0;
checkCards([...mainFrame.children]);
};
let parentFrame = document.querySelector("div#TopstoryContent");
let config = { childList: true };
let ob = new MutationObserver((records) => {
//console.log("mutation records: ", records);
setTimeout(registerHook, 500);
console.log("rehook emitted");
});
ob.observe(parentFrame, config);
registerHook();
//进行首次检查,这里不DRY
})();
/**/});
/* jshint ignore:start */
var c = Babel.transform(inline_src, { presets: [ "es2015", "es2016", "es2017" ] });
console.log(c.code);
eval(c.code);
//eval(inline_src);
/* jshint ignore:end */