Skip to content

Instantly share code, notes, and snippets.

@mixterjim
Last active October 9, 2020 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mixterjim/a1f4d28e7aafcff12d4fa1d18db85fb6 to your computer and use it in GitHub Desktop.
Save mixterjim/a1f4d28e7aafcff12d4fa1d18db85fb6 to your computer and use it in GitHub Desktop.
Improve "blocklist" feature in zhihu.com
// ==UserScript==
// @name Zhihu Blocklist Helper
// @name:zh 知乎黑名单助手
// @namespace https://github.com/mixterjim
// @version 0.7
// @author MixterJim
// @description 完善知乎黑名单功能,屏蔽黑名单用户回答
// @homepage https://greasyfork.org/zh-CN/scripts/412738-zhihu-blocklist-helper
// @icon https://static.zhihu.com/static/favicon.ico
// @updateURL https://gist.github.com/mixterjim/a1f4d28e7aafcff12d4fa1d18db85fb6.js
// @supportURL https://gist.github.com/mixterjim/a1f4d28e7aafcff12d4fa1d18db85fb6
// @include *://www.zhihu.com/*
// @connect www.zhihu.com
// @run-at document-body
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @nocompat Chrome
// @license GPL-3.0
// @create 2020.08.20
// @note 2020.08.31-V0.7 增加设置面板
// @thanks Google Closure
// ==/UserScript==
(function() {
var zbh = {
array: {
// *
// * Removes all duplicates from an array.
// *
// * Runtime: N,
// * Worstcase space: 2N
// *
// * @param {Array} arr The array from which to remove.
// * duplicates.
removeDuplicates: function(arr) {
let seen = {},
cursorInsert = 0,
cursorRead = 0;
while (cursorRead < arr.length) {
let current = arr[cursorRead++];
if (!Object.prototype.hasOwnProperty.call(seen, current)) {
seen[current] = true;
arr[cursorInsert++] = current;
}
};
arr.length = cursorInsert;
},
/**
* Removes the first occurrence of a particular value from an array.
* @param {IArrayLike<T>} arr Array from which to remove
* value.
* @param {T} obj Object to remove.
* @template T
*/
remove: function(arr, obj) {
var i = arr.indexOf(obj);
if (i >= 0) {
Array.prototype.splice.call(arr, i, 1);
}
},
},
/**
* Get blocked id from API.
*
* @param {Array} arr The array in which to save the results.
* @param {string} url An link for which we are getting blocked id.
* @param {number|string} offset The index at where we start to
* get the blocked id.
*/
getBlockedIdList: function(arr, url, offset) {
let request = new XMLHttpRequest();
request.onload = function() {
request.response.data.forEach(function(item, index, array) {
arr.push(item.id);
});
if (request.response.paging.totals > arr.length) {
zbh.getBlockedIdList(arr, url, arr.length);
} else {
zbh.array.removeDuplicates(arr);
GM_setValue("blockedIdList", arr);
}
};
request.open("GET", url + offset, true);
request.responseType = 'json';
request.send();
},
/**
* Set block button.
*
* @param {string} name The author name.
* @param {string} isBlocked Is the author blocded?
* @param {object} button The block botton.
*/
blockButton: function(name, isBlocked, button) {
let request = new XMLHttpRequest();
if (isBlocked === "true") {
request.open("DELETE", "https://www.zhihu.com/api/v4/members/" + name + "/actions/block", true);
button.textContent = "Block";
button.setAttribute('blocked', "false");
button.className = "Button Button--primary Button--blue";
zbh.array.remove(blockedIdList, button.id)
} else {
request.open("POST", "https://www.zhihu.com/api/v4/members/" + name + "/actions/block", true);
button.textContent = "Blocked";
button.setAttribute('blocked', "true");
button.className = "Button Button--primary Button--red";
blockedIdList.push(button.id);
}
request.send();
GM_setValue("blockedIdList", blockedIdList);
console.log(blockedIdList)
},
settingButtonClick: function() {
if (document.querySelector(".setting-panel").style.display == "block") {
document.querySelector(".setting-panel").style.display = "none";
} else {
document.querySelector(".isBlockHidden").checked = Config.isBlockHidden;
document.querySelector(".isBlockTempShow").checked = Config.isBlockTempShow;
document.querySelector(".isBlockBtnNotHidden").checked = Config.isBlockBtnNotHidden;
document.querySelector(".isBlockPost").checked = Config.isBlockPost;
document.querySelector(".setting-panel").style.display = "block";
}
},
hidenAnswer: function() {
let answerItemList = document.querySelectorAll(".List-item");
while (index < answerItemList.length) {
let item = answerItemList[index].querySelector(".ContentItem.AnswerItem");
let name = answerItemList[index].querySelector(".UserLink-link").href.slice(29);
let id = JSON.parse(item.getAttribute("data-za-extra-module")).card.content.author_member_hash_id;
if (blockedIdList.indexOf(id) != -1) {
item.hidden = true;
item.querySelector(".AuthorInfo.AnswerItem-authorInfo.AnswerItem-authorInfo--related").innerHTML += '<button style="float:right" class="Button Button--primary Button--red" blocked="true" id=' + index + ' value=' + name + '>Blocked</button>';
answerItemList[index].innerHTML += '<div class="Blockeditem" style="text-align:center"><span class="blockShow" id=' + id + '>Block by ' + JSON.parse(item.getAttribute("data-zop")).authorName + '</span></div>';
// answerItemList[index].querySelector(".blockShow").onclick=function(){answerItemList[this.id].querySelector(".ContentItem.AnswerItem").hidden = false;this.hidden = true};
// <BUG> 取消隐藏后原 click 侦听器消失
} else {
item.querySelector(".AuthorInfo.AnswerItem-authorInfo.AnswerItem-authorInfo--related").innerHTML += '<button style="float:right" class="Button Button--primary Button--blue" blocked="false" id=' + id + ' value=' + name + '>Block</button>';
}
item.querySelector(".Button.Button--primary").onclick = function() {
zbh.blockButton(this.value, this.getAttribute("blocked"), this);
};
index++;
};
return index;
},
hidenPost: function() {
let itemList = document.querySelectorAll(".Card.TopstoryItem.TopstoryItem-isRecommend");
while (questionIndex < itemList.length) {
let item = itemList[questionIndex].firstChild;
try {
let itemType = JSON.parse(item.getAttribute("data-za-extra-module")).card.content.type;
if (itemType != "Answer") {
let data = JSON.parse(item.firstChild.getAttribute("data-zop"));
let authorName, postName;
if (data != null) {
authorName = data.authorName;
postName = data.title;
} else {
authorName = "视频";
postName = item.querySelector(".ContentItem-title").textContent;
}
let title = authorName + " 《" + postName + "》";
item.firstChild.hidden = true;
console.log(questionIndex, "hidden")
item.innerHTML += '<div class="Blockeditem" style="text-align:center"><span class="blockShow" id=' + questionIndex + '>Block by ' + title + '</span></div>';
itemList[questionIndex].querySelector(".blockShow").onclick = function() {
itemList[this.id].firstChild.firstChild.hidden = false;
this.hidden = true
};
// <BUG> 取消隐藏后原 click 侦听器消失
}
} catch (e) {
/* 此处应该是广告 */
questionIndex++;
continue;
}
questionIndex++;
}
},
zbhsettingButton: function() {
let settingButton = `
<div class="Setting">
<button class="Button AppHeader-notifications css-79elbk Button--plain">
<span style="display: inline-flex; align-items: center;">​&#8203;
<svg class="Zi Zi--Settings" fill="currentColor" viewBox="0 0 24 24" width="22" height="22">
<path d="M20.868 17.185a.896.896 0 0 1-.452.137c-.123 0-1.397-.26-1.617-.233-1.354.014-1.78 1.276-1.835 1.742-.055.453 0 .892.191 1.303a.8.8 0 0 1-.068.851C16.224 21.877 14.922 22 14.73 22a.548.548 0 0 1-.356-.151c-.11-.096-.685-1.138-1.069-1.468-1.304-.955-2.247-.329-2.63 0-.398.33-.672.7-.836 1.125a.632.632 0 0 1-.329.37c-1.354.426-2.918-.919-3.014-1.056a.564.564 0 0 1-.123-.356c-.014-.138.383-1.276.342-1.688-.342-1.9-1.836-1.687-2.096-1.673a3.192 3.192 0 0 0-.918.178.873.873 0 0 1-.59-.055c-.887-.462-1.136-2.332-1.109-2.51.055-.315.192-.521.438-.604.425-.164.809-.452 1.151-.85.931-1.262.343-2.25 0-2.634-.342-.356-.726-.645-1.15-.809-.138-.041-.234-.151-.33-.316-.38-1.434.613-2.552.867-2.77.255-.22.6-.055.723 0 .425.164.877.219 1.343.15C6.7 6.636 6.784 5.141 6.81 4.908c.014-.247-.11-1.29-.137-1.4a.488.488 0 0 1 .027-.315C7.317 2.178 9.071 2 9.222 2a.56.56 0 0 1 .439.178c.11.124.63 1.111 1 1.4.4.338 1.583.83 2.59.013.397-.274.959-1.29 1.082-1.413A.55.55 0 0 1 14.717 2c1.56 0 2.329 1.029 2.438 1.22a.458.458 0 0 1 .069.371c-.028.151-.329 1.152-.26 1.605.365 1.537 1.383 1.742 1.89 1.783.493.028 1.644-.356 1.809-.343a.63.63 0 0 1 .424.206c.535.31.85 1.715.905 2.14.027.233-.014.439-.11.562-.11.138-1.165.714-1.48 1.112-.855.982-.342 2.25-.068 2.606.26.37 1.22.905 1.288.96.15.137.26.302.315.494.146 1.413-.89 2.387-1.069 2.47zm-8.905-.535c.644 0 1.246-.123 1.822-.356a4.576 4.576 0 0 0 1.493-1.016 4.694 4.694 0 0 0 1-1.495c.247-.562.357-1.18.357-1.81 0-.659-.11-1.262-.356-1.825a4.79 4.79 0 0 0-1-1.481 4.542 4.542 0 0 0-1.494-1.002 4.796 4.796 0 0 0-3.631 0 4.627 4.627 0 0 0-1.48 1.002c-.424.425-.767.919-1 1.481a4.479 4.479 0 0 0-.37 1.825c0 .644.124 1.248.37 1.81a4.62 4.62 0 0 0 1 1.495c.425.426.918.768 1.48 1.016a4.677 4.677 0 0 0 1.809.356z" fill-rule="evenodd"></path>
</svg>
</span>
</button>
</div>
`;
let settingPanel = `
<div class="setting-panel">
<style type="text/css">
.setting-panel {
position: fixed;
top: 3vw;
right: 18vw;
z-index: 203;
background-color: #fff;
padding-bottom: 8px;
display: none;
border: 1px solid #ebebeb;
border-radius: 4px;
-webkit-box-shadow: 0 5px 20px rgba(26,26,26,.1);
}
.setting-header {
padding: 14px 0;
text-align: center;
border-bottom: 1px solid rgb(246, 246, 246);
}
.setting-list {
margin: 8px auto 9px;
padding: 0 16px;
}
.setting-button {
margin: 0 16px;
border-top: 1px solid rgb(246, 246, 246);
padding: 10px 8px;
}
.setting-list li {
position: relative;
font-size: 14px;
line-height: 24px;
color: #1a1a1a;
}
.clear {
float: right;
color: #8590a6;
font-size: 13px;
}
</style>
<div class="setting-header">知乎黑名单助手</div>
<div class="setting-list">
<li>
<label><input type="checkbox" class="isBlockHidden">隐藏拦截条目</label>
<label><input type="checkbox" class="isBlockTempShow">临时显示拦截条目</label>
</li>
<li>
<label><input type="checkbox" class="isBlockBtnNotHidden">显示拉黑按钮</label>
</li>
<li>
<label><input type="checkbox" class="isBlockPost">屏蔽专栏</label>
<button class="clear" title="恢复默认设置,清空黑名单">复位</button>
</li>
</div>
<div class="setting-button">
<span class="cancel" style="position: relative; float: left;">取消</span>
<span class="save" style="position: relative; float: right;">保存</span>
</div>
<script type="text/javascript">
// document.(".cancel").addEventListener("click", function(){document.querySelector(".setting-panel").style.display = "none";});
// document.getElementById("isBlockHidden").checked=Config.isBlockHidden;
</script>
</div>
`;
document.querySelector(".AppHeader-userInfo").insertAdjacentHTML("afterbegin", settingButton);
document.querySelector(".Setting").addEventListener("click", zbh.settingButtonClick);
document.querySelector(".Sticky.AppHeader.AppHeader--old").insertAdjacentHTML("afterend", settingPanel);
document.querySelector(".cancel").addEventListener("click", function() {
document.querySelector(".setting-panel").style.display = "none";
});
document.querySelector(".save").addEventListener("click", function() {
Config.isBlockHidden = document.querySelector(".isBlockHidden").checked;
Config.isBlockTempShow = document.querySelector(".isBlockTempShow").checked;
Config.isBlockBtnNotHidden = document.querySelector(".isBlockBtnNotHidden").checked;
Config.isBlockPost = document.querySelector(".isBlockPost").checked;
GM_setValue("Config", Config);
document.querySelector(".setting-panel").style.display = "none";
});
document.querySelector(".clear").addEventListener("click", function() {
GM_deleteValue("blockedIdList");
GM_deleteValue("Config");
location.reload();
// document.querySelector(".setting-panel").style.display = "none";
});
},
};
/*--------------------------------Main-----------------------------------*/
var Config = GM_getValue("Config", {
isBlockHidden: false, // 是否隐藏已拦截的条目
isBlockTempShow: false, //是否允许临时显示拦截条目
isBlockBtnNotHidden: false, // 是否隐藏拉黑按钮
isBlockPost: true, //是否屏蔽专栏
});
var getBlockedUserUrl = "/api/v3/settings/blocked_users?limit=20&offset=";
var blockedIdList = GM_getValue("blockedIdList", []);
if (blockedIdList.length === 0) {
console.log("Loading");
zbh.getBlockedIdList(blockedIdList, getBlockedUserUrl, blockedIdList.length);
}
// if (/.*zhihu.com\/question.*/.test(document.URL)) {
// // if (/.*zhihu.com\/question.*/.test(document.URL) && !/answer/.test(document.URL)) {
// var index = 0;
// setInterval(zbh.hidenAnswer, 1000);
// document.addEventListener('readystatechange', function() {
// document.querySelectorAll(".QuestionMainAction.ViewAll-QuestionMainAction").forEach(function(item, index, array) {
// item.addEventListener('click',function(){
// window.location.href=item.getAttribute("href")
// })
// console.log(item)
// });
// });
// console.log("Start hidenAnswer")
// }
if (/.*zhihu.com\/question.*/.test(document.URL)) {
// if (/.*zhihu.com\/question.*/.test(document.URL) && !/answer/.test(document.URL)) {
var index = 0;
setInterval(zbh.hidenAnswer, 1000);
document.addEventListener('readystatechange', function() {
var viewAllButton = document.getElementsByClassName("QuestionMainAction ViewAll-QuestionMainAction")
for(var i=0;i<viewAllButton.length;i++) {
viewAllButton[i].addEventListener('click',function(){
window.location.href=viewAllButton[i].href;
});
console.log("Start hidenAnswer")
}
});
}
if ((location.pathname === "/" || /.*zhihu.com\/search?.*/.test(document.URL)) & Config.isBlockPost) {
var questionIndex = 0;
setInterval(zbh.hidenPost, 1000);
}
if (/.*zhihu.com\/people\/.*/.test(document.URL)) {
document.addEventListener('readystatechange', function() {
if (document.readyState === 'complete') {
try {
document.querySelector("div.MemberButtonGroup.ProfileButtonGroup.ProfileHeader-buttons Button.Button--primary.Button--red").addEventListener("click", function() {
GM_deleteValue("blockedIdList");
})
} catch (e) {
console.log('ok')
}
}
});
}
document.addEventListener('readystatechange', function() {
if (document.readyState === 'complete') {
zbh.zbhsettingButton();
}
});
document.addEventListener(
/* 解除复制版权限制,删除复制时附带的版权声明 */
"copy",
function(e) {
e.stopPropagation();
},
true
);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment