// ==UserScript==
// @name 知乎-匿名提问者标注
// @namespace http://tampermonkey.net/
// @version 1.5.1
// @license MPL-2.0
// @description 在问题页, 标注匿名提问, 防止钓鱼
// @author C4r
// @match https://www.zhihu.com/*
// @require https://cdn.jsdelivr.net/npm/jquery@3.5.0/dist/jquery.min.js
// @grant GM_addStyle
// @grant GM.setValue
// @grant GM.getValue
// @downloadURL https://update.greasyfork.icu/scripts/414898/%E7%9F%A5%E4%B9%8E-%E5%8C%BF%E5%90%8D%E6%8F%90%E9%97%AE%E8%80%85%E6%A0%87%E6%B3%A8.user.js
// @updateURL https://update.greasyfork.icu/scripts/414898/%E7%9F%A5%E4%B9%8E-%E5%8C%BF%E5%90%8D%E6%8F%90%E9%97%AE%E8%80%85%E6%A0%87%E6%B3%A8.meta.js
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(' \
.wrapper { \
margin: 102px 0;\
}\
.toggle_radio{\
position: relative;\
background: #497dd0;\
margin: 4px auto;\
overflow: hidden;\
padding: 0 !important;\
-webkit-border-radius: 50px;\
-moz-border-radius: 50px;\
border-radius: 50px;\
position: relative;\
height: 26px;\
width: 110px;\
}\
.toggle_radio > * {\
float: left;\
}\
.toggle_radio input[type=radio]{\
display: none;\
}\
.toggle_radio label{\
font: 90%/1.618 "Source Sans Pro";\
color: rgba(255,255,255,.9);\
z-index: 0;\
display: block;\
width: 30px;\
height: 20px;\
margin: 3px 3px;\
-webkit-border-radius: 50px;\
-moz-border-radius: 50px;\
border-radius: 50px;\
cursor: pointer;\
z-index: 1;\
/*background: rgba(0,0,0,.1);*/\
text-align: center;\
}\
.toggle_option_slider{\
width: 30px;\
height: 20px;\
position: absolute;\
top: 3px;\
-webkit-border-radius: 15px;\
-moz-border-radius: 15px;\
border-radius: 15px;\
-webkit-transition: all .4s ease;\
-moz-transition: all .4s ease;\
-o-transition: all .4s ease;\
-ms-transition: all .4s ease;\
transition: all .4s ease;\
}\
#AnonymousToggleOff:checked ~ .toggle_option_slider{\
background: rgba(255,255,255,.3);\
left: 3px;\
}\
#AnonymousToggleLight:checked ~ .toggle_option_slider{\
background: rgba(255,255,255,.3);\
left: 38px;\
}\
#AnonymousToggleTight:checked ~ .toggle_option_slider{\
background: rgba(255,255,255,.3);\
left: 75px;\
');
function isHome() {
return $("#TopstoryContent").length > 0
}
function isQuestionPage() {
return $('.QuestionPage').length > 0
}
function isAuthorPage() {
return $('#ProfileHeader').length > 0
}
function getLogURL(questionURL) {
// return new URL('log', questionURL).href
return questionURL + '/log'
}
function httpGetAsync(theUrl, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
function getAuthorUrl(logURL) {
return new Promise((resolve, reject) => {
httpGetAsync(logURL, (responseText) => {
// console.log('get response')
let repHTML = $.parseHTML(responseText)
let author = $(repHTML).find('.zm-item:last > div > a').attr('href')
if (author != undefined) {
let userInfo = {
'name': $(repHTML).find('.zm-item:last > div > a').text(),
'url': $(repHTML).find('.zm-item:last > div > a').get(0).href,
'a': $(repHTML).find('.zm-item:last > div > a').get(0).outerHTML
}
resolve(userInfo)
} else {
// console.log( '匿名提问 : ', undefined)
resolve(undefined)
}
})
})
}
class Author {
constructor(url) {
this.url = url
this.name = undefined
this.avatar = undefined
this.isOrg = undefined
this.isVerified = undefined
this.ask = undefined
this.answer = undefined
// 关注了
this.following = undefined
// 关注者
this.follower = undefined
this.post = undefined
this.agree = undefined
this.score = undefined
this.scoreMarker = undefined
this.timeStamp = undefined
}
print() {
console.log('url : ', this.url )
console.log('name : ', this.name )
console.log('avatar : ', this.avatar )
console.log('isOrg : ', this.isOrg )
console.log('isVerified : ', this.isVerified )
console.log('ask : ', this.ask )
console.log('answer : ', this.answer )
console.log('following : ', this.following )
console.log('follower : ', this.follower )
console.log('post : ', this.post )
console.log('agree : ', this.agree )
console.log('score : ', this.score )
console.log('scoreMarker: ', this.scoreMarker )
console.log('timeStamp : ', this.timeStamp )
}
updateAuthorFromPage(pageHTML) {
this.name = $(pageHTML).find('.ProfileHeader-name').text()
this.avatar = $(pageHTML).find('img.Avatar').attr('src')
this.ask = parseInt($(pageHTML).find('.Profile-mainColumn li.Tabs-item[aria-controls="Profile-asks"] span').text())
this.answer = parseInt($(pageHTML).find('.Profile-mainColumn li.Tabs-item[aria-controls="Profile-answers"] span:eq(0)').text())
+ parseInt($(pageHTML).find('.Profile-mainColumn li.Tabs-item[aria-controls="Profile-answers"] span:eq(1)').text())
this.post = parseInt($(pageHTML).find('.Profile-mainColumn li.Tabs-item[aria-controls="Profile-posts"] span').text())
this.following = parseInt($(pageHTML).find('.FollowshipCard a:eq(0) .NumberBoard-itemValue').attr('title'))
this.follower = parseInt($(pageHTML).find('.FollowshipCard a:eq(1) .NumberBoard-itemValue').attr('title'))
let arrayAgree = $($(pageHTML).find('.Zi--Like').closest('.css-12ofpn8').find('.css-vurnku').contents().get(0)).text().match(/\d+/g)
if (arrayAgree) {
this.agree = parseInt(arrayAgree.join(''))
} else {
this.agree = 0
}
// this.agree = parseInt($($(pageHTML).find('.Zi--Like').closest('.css-12ofpn8').find('.css-vurnku').contents().get(0)).text().match(/\d+/g).join(''))
this.evaluateAuthor()
this.timeStamp = Date.now()
// this.print()
this.store()
}
/**
* 评估作者
* @returns 1-10
*/
evaluateAuthor() {
let score = 5
if( this.isBanned() ){
this.score = 1
this.scoreMarker = '🔥'
return
}
if(this.answer > 5 || this.ask > 5 ){
if (this.agree >= 1.1 * this.answer) {
score = score + 1 // Math.ceil( this.agree/this.answer )
// console.log('agree up')
} else if (this.agree <= 0.2 * this.answer) {
score = score - 2
// console.log('agree down')
} else if (this.agree <= 0.5 * this.answer) {
score = score - 1
}
if (this.answer >= 2. * this.ask) {
score = score + 1
// console.log('answer up')
} else if (this.answer <= 0.5 * this.ask) {
score = score - 3
} else if (this.answer <= 0.7 * this.ask) {
score = score - 2
} else if (this.answer <= 1. * this.ask) {
score = score - 1
}
if (this.follower < 10) {
score = score - 1
// console.log('follower up')
}
if (this.follower < 100) {
score = score - 1
// console.log('follower up')
}
if (this.follower >= 100) {
score = score + 1
// console.log('follower up')
}
if (this.follower >= 1000) {
score = score + 2
}
}
if (score > 10) {
score = 10
} else if (score < 1) {
score = 1
}
this.score = score
if (this.score > 6) {
this.scoreMarker = '☼'
} else if (this.score < 4) {
this.scoreMarker = '🔥'
} else {
this.scoreMarker = '☉'
}
}
isBanned(){
return (
isNaN(this.answer)
|| isNaN(this.ask)
|| isNaN(this.agree)
|| isNaN(this.follower)
|| isNaN(this.following))
}
toJSON() {
return {
'url' : this.url ,
'name' : this.name ,
'avatar' : this.avatar ,
'isOrg' : this.isOrg ,
'isVerified' : this.isVerified ,
'ask' : this.ask ,
'answer' : this.answer ,
'following' : this.following ,
'follower' : this.follower ,
'post' : this.post ,
'agree' : this.agree ,
'score' : this.score ,
'scoreMarker': this.scoreMarker,
'timeStamp' : this.timeStamp
}
}
fromJSON(json) {
this.url = json['url' ]
this.name = json['name' ]
this.avatar = json['avatar' ]
this.isOrg = json['isOrg' ]
this.isVerified = json['isVerified' ]
this.ask = json['ask' ]
this.answer = json['answer' ]
this.following = json['following' ]
this.follower = json['follower' ]
this.post = json['post' ]
this.agree = json['agree' ]
this.score = json['score' ]
this.scoreMarker= json['scoreMarker' ]
this.timeStamp = json['timeStamp' ]
}
store() {
if(this.isBanned() ){
return
}
let allData = JSON.parse(localStorage.getItem('zhihu-Anonymous') || '{}')
if (allData['author'] == undefined || allData['author'][this.url] == undefined ) {
allData['author'][this.url] = this.toJSON()
localStorage.setItem('zhihu-Anonymous', JSON.stringify(allData))
} else {
if ( (this.timeStamp - allData['author'][this.url]['timeStamp']) > 24 * 60 * 60 * 1000) {
allData['author'][this.url] = this.toJSON()
// console.log(this.toJSON())
// localStorage.setItem('zhihu-Anonymous', JSON.stringify(allData))
}
}
}
get() {
let allData = JSON.parse(localStorage.getItem('zhihu-Anonymous') || '{}')
// console.log(allData)
if (allData['author'] == undefined) {
return false
} else {
if (allData['author'][this.url]){
this.fromJSON( allData['author'][this.url])
return true
}else{
return false
}
}
}
}
/**
*
* @param {str} authorUrl 作者页链接
* https://www.zhihu.com/org/guan-cha-zhe-wang-31 <- 官方帐号
* https://www.zhihu.com/people/liaoxuefeng <- 个人帐号
* @returns {Author} author
*
*/
function getAuthorInfoDetail(authorUrl) {
return new Promise((resolve, reject) => {
let author = new Author(authorUrl)
if(author.get()){
// console.log('author exist ', author.name)
resolve(author)
}else{
httpGetAsync(authorUrl, (responseText) => {
let repHTML = $.parseHTML(responseText)
author.updateAuthorFromPage(repHTML)
resolve(author)
})
}
})
}
function topic() {
return '\
\
'
}
function noteQuestionPage(content, jump) {
return '\
\
\
\
\
\
\
\
\
'+ content + '\
\
'+ jump + '
\
\
\
\
\
\
\
'
}
function addNoteQuestionPage(content, jump) {
if ($('[AnonymousNote]').length > 0) {
$('[AnonymousNote] .PositiveLabelBar-title').empty()
$('[AnonymousNote] .PositiveLabelBar-title').append(content)
$('[AnonymousNote] .PositiveLabelBar-side').empty()
$('[AnonymousNote] .PositiveLabelBar-side').append(jump)
} else {
// console.log('插入')
$('.QuestionHeader h1.QuestionHeader-title').after(noteQuestionPage(content, jump));
}
}
function loadHotlist() {
$('div[AnonymousToggleCount]').text('🏃')
let num_section = $('.HotList-list section').length
let arrayHide = new Array(num_section)
$('.HotList-list section').each((index, section) => {
arrayHide[index] = new Promise(resolveHide => {
if ($(section).find('[AnonymousNote]').length == 0) {
// if($(section).find('[AnonymousNote]').length == 0 ){
let questionURL = $(section).find('.HotItem-content a').attr('href')
let logURL = getLogURL(questionURL)
if (logURL.includes('question')) {
if ($(section).find('[AnonymousNote][checking]').length == 0) {
$(section).find('.HotItem-metrics').append(' 🔍 👤 ')
}
getAuthorUrl(logURL).then(authorInfo => {
if (authorInfo == undefined) {
if ($(section).find('[AnonymousNote]').length > 0) {
$(section).find('[AnonymousNote]').empty()
$(section).find('[AnonymousNote]').append('👻 匿名 ')
$(section).find('[AnonymousNote]').removeAttr('checking')
$(section).find('[AnonymousNote]').attr('done', '')
} else {
$(section).find('.HotItem-metrics').append('👻 匿名 ')
}
if ($('[AnonymousToggle]').length > 0 && ($('#AnonymousToggleTight').prop('checked') || $('#AnonymousToggleLight').prop('checked'))) {
$(section).hide()
resolveHide(true)
} else {
resolveHide(false)
}
} else {
// console.log('找到题主 : ', authorInfo)
if ($(section).find('[AnonymousNote]').length > 0) {
$(section).find('[AnonymousNote]').empty()
$(section).find('[AnonymousNote]').append(' 👤 ' + authorInfo.a + ' 🔍')
$(section).find('[AnonymousNote]').removeAttr('checking')
$(section).find('[AnonymousNote]').attr('done', '')
} else {
$(section).find('.HotItem-metrics').append(' 👤 ' + authorInfo.a + ' 🔍 ')
}
getAuthorInfoDetail(authorInfo.url).then(author => {
if ($(section).find('[AnonymousNote] [valueAuthor]').length > 0) {
$(section).find('[AnonymousNote] [valueAuthor]').remove()
}
$(section).find('[AnonymousNote]').append('' + author.scoreMarker + '')
if ($('[AnonymousToggle]').length > 0 && $('#AnonymousToggleTight').prop('checked') && author.score < 4) {
$(section).hide()
resolveHide(true)
} else {
resolveHide(false)
}
})
}
})
} else {
resolveHide(undefined)
}
} else {
resolveHide(undefined)
}
})
})
Promise.all(arrayHide).then((values) => {
// console.log(num_section, ' hide ', values)
let countHide = values.filter(isHide => isHide).length
if (countHide > 0) {
$('div[AnonymousToggleCount]').text('-' + countHide.toString())
setTimeout(() => {
$('div[AnonymousToggleCount]').text('')
}, 1000);
} else {
$('div[AnonymousToggleCount]').text('')
}
});
}
function callbackHotList(mutationsList) {
if ($('.HotList-list').length > 0) {
// console.log('refresh author info...')
let count = 0
for (let mutation of mutationsList) {
if ($(mutation.target).find('[AnonymousNote]').length == 0
&& $(mutation.target).find('.HotItem-content a').attr('href').includes('question')) {
count = count + 1
}
}
if (count > 0) {
loadHotlist()
}
}
}
$(document).on('click', '[AnonymousToggle]', () => {
let countHide = 0
let countShow = 0
if ($('#AnonymousToggleLight').prop('checked')) {
GM.setValue("zhihu-AnonymousToggle", 1)
$('.HotList-list section').each((index, section) => {
if ($(section).find('[AnonymousNote]').length > 0) {
if ($(section).find('[valueAuthor]') && parseInt($(section).find('[valueAuthor]').attr('score')) == 0) {
$(section).hide()
countHide = countHide + 1
} else {
$(section).show()
countShow = countShow + 1
}
}
})
$('div[AnonymousToggleCount]').text('-' + countHide.toString())
setTimeout(() => {
$('div[AnonymousToggleCount]').text('')
}, 1000);
} else if ($('#AnonymousToggleTight').prop('checked')) {
GM.setValue("zhihu-AnonymousToggle", 2)
$('.HotList-list section').each((index, section) => {
if ($(section).find('[AnonymousNote]').length > 0) {
if ($(section).find('[valueAuthor]') && parseInt($(section).find('[valueAuthor]').attr('score')) < 4) {
$(section).hide()
countHide = countHide + 1
}
}
})
$('div[AnonymousToggleCount]').text('-' + countHide.toString())
setTimeout(() => {
$('div[AnonymousToggleCount]').text('')
}, 1000);
} else {
// if($('#AnonymousToggleOff').prop('checked')){
GM.setValue("zhihu-AnonymousToggle", 0)
$('.HotList-list section').each((index, section) => {
if ($(section).find('[AnonymousNote]').length > 0) {
if ($(section).find('[valueAuthor]') && $(section).is(":hidden")) {
$(section).show()
countShow = countShow + 1
}
}
})
$('div[AnonymousToggleCount]').text('+' + countShow.toString())
setTimeout(() => {
$('div[AnonymousToggleCount]').text('')
}, 1000);
}
})
let c4rHTML = ''
// fresh data
/**
* zhihu-Anonymous :{
* lastFreshTimeStamp : sec
* author :{
* url : author class
* ...
* }
* question :{
* url : { timeStamp: sec; author : url}
* }
* }
*/
let allData = JSON.parse(localStorage.getItem('zhihu-Anonymous') || '{}')
if (allData['lastFreshTimeStamp'] == undefined) {
allData['lastFreshTimeStamp'] = Date.now()
allData['author'] = {}
allData['question'] = {}
localStorage.setItem('zhihu-Anonymous', JSON.stringify(allData))
} else {
let nowTimeStamp = Date.now()
if (nowTimeStamp - allData['lastFreshTimeStamp'] > 7 * 24 * 60 * 60 * 1000) {
let authorList = {}
for (let authorValue of Object.values(allData['author'])) {
if (nowTimeStamp - authorValue['timeStamp'] < 24 * 60 * 60 * 1000) {
authorList[authorValue['url']] = authorValue
}
}
let questionList = {}
for (let [qKey, qValue] of Object.entries(allData['question'])) {
if (nowTimeStamp - qValue['timeStamp'] < 24 * 60 * 60 * 1000) {
questionList[qKey] = qValue
}
}
allData['author'] = authorList
allData['question'] = questionList
allData['lastFreshTimeStamp'] = nowTimeStamp
localStorage.setItem('zhihu-Anonymous', JSON.stringify(allData))
}
}
$(document).ready(() => {
if (isHome()) {
GM.getValue("zhihu-AnonymousToggle", 0).then((anonymousToggle) => {
if ($('[AnonymousToggle]').length > 0) {
switch (anonymousToggle) {
case 0:
$('#AnonymousToggleOff').prop('checked', true);
break;
case 1:
$('#AnonymousToggleLight').prop('checked', true);
break;
case 2:
$('#AnonymousToggleTight').prop('checked', true);
break;
default:
break;
}
} else {
$('.AppHeader-userInfo').prepend(
'🏃
\
\
\
\
\
\
\
\
\
\
')
}
}).catch((error) => {
// console.log('AnonymousToggle error ',error )
})
// 热榜
if ($('.HotList-list').length > 0) {
loadHotlist()
}
let observerHotList = new MutationObserver(callbackHotList)
observerHotList.observe($('#TopstoryContent').get(0),
{
subtree: true, childList: false, characterData: false, attributes: true, attributeFilter: ['data-za-detail-view-path-module'],
attributeOldValue: false, characterDataOldValue: false
})
} else if (isQuestionPage()) {
let questionURL = $('.QuestionPage >meta[itemprop="url"]').attr('content')
let logURL = getLogURL(questionURL)
if ($('.QuestionAuthor').length == 0) {
// console.log('问题页 ', logURL)
addNoteQuestionPage('读取日志中...', '
问题日志')
getAuthorUrl(logURL).then(authorInfo => {
if (authorInfo == undefined) {
addNoteQuestionPage('
⚠ 注意 : 这是一篇匿名提问 👻 ', '
问题日志')
let oText = $('.PageHeader h1.QuestionHeader-title').text()
$('.PageHeader h1.QuestionHeader-title').text('👻 ' + oText)
} else {
// console.log('找到题主 : ', authorInfo)
addNoteQuestionPage('👤 ' + authorInfo.a + '
🔍', '
问题日志')
// let oText = $('.PageHeader h1.QuestionHeader-title').text()
// $('.PageHeader h1.QuestionHeader-title').text('👤 ' + oText)
getAuthorInfoDetail(authorInfo.url).then(author => {
addNoteQuestionPage(
'👤 ' + authorInfo.a +
'
' + author.scoreMarker + '',
'
问题日志')
let oText = $('.PageHeader h1.QuestionHeader-title').text()
$('.PageHeader h1.QuestionHeader-title').text(author.scoreMarker + oText)
})
}
})
} else {
$('.QuestionAuthor').append('
问题日志')
$('.QuestionAuthor div.AuthorInfo-content').append('
🔍')
getAuthorUrl(logURL).then(authorInfo => {
getAuthorInfoDetail(authorInfo.url).then(author => {
if ($('.QuestionAuthor div.AuthorInfo-content [valueAuthor]').length > 0) {
$('.QuestionAuthor div.AuthorInfo-content [valueAuthor]').remove()
}
$('.QuestionAuthor div.AuthorInfo-content').append('
' + author.scoreMarker + '')
let oText = $('.PageHeader h1.QuestionHeader-title').text()
$('.PageHeader h1.QuestionHeader-title').text(author.scoreMarker + oText)
})
})
}
} else if (isAuthorPage()) {
$('.Profile-lightList').prepend(
'
题主估分🖩')
let author = new Author(window.location.href)
if(! author.get()){
// console.log('not found author')
author.updateAuthorFromPage(document)
}else{
// console.log('author exist ')
}
// author.print()
// console.log('分数 : ',author.evaluateAuthor())
// append score
let scoreString = author.score.toString() + author.scoreMarker
if ($('[valueAuthor]').length > 0) {
$('[valueAuthor] .Profile-lightItemValue').text(scoreString)
} else {
$('.Profile-lightList').prepend(
'
题主估分' + scoreString + '')
}
}
})
})();