// ==UserScript== // @name Young People in Yaohuo // @description 青少年模式 // @version 0.8 // @author Polygon // @match https://yaohuo.me/* // @icon https://yaohuo.me/css/favicon.png // @grant GM_addStyle // @grant GM_info // @run-at document-end // @namespace https://greasyfork.org/users/808618 // @downloadURL https://update.greasyfork.icu/scripts/454965/Young%20People%20in%20Yaohuo.user.js // @updateURL https://update.greasyfork.icu/scripts/454965/Young%20People%20in%20Yaohuo.meta.js // ==/UserScript== (function() { 'use strict'; // 添加一个style,PC端字体有点看不清 GM_addStyle(` body, html { font-family: Arial, SimHei !important; } `) let forbiddenLocal if (localStorage.getItem("forbidden") == null) { forbiddenLocal = {keywords: [], usernames: []} localStorage.setItem("forbidden", JSON.stringify(forbiddenLocal)) } forbiddenLocal = JSON.parse(localStorage.getItem("forbidden")) const keywords = forbiddenLocal.keywords const usernames = forbiddenLocal.usernames // 在record中读取k,读取过程中可能会初始化record[k] let getRecord = (itemid) => { if (localStorage.getItem("record-version") != GM_info.script.version) { localStorage.removeItem("record") alert("清空老版本record成功") localStorage.setItem("record-version", GM_info.script.version) } // 读取最新localStorage的record let record = localStorage.getItem("record") record = JSON.parse(record) if (record == null) { // 第一次初始化 record = {} } if (!Object.keys(record).includes(itemid)) { record[itemid] = { "latest_time": null, // 最近一次访问时间戳 "latest_comment": 0, // 上一次访问时的评论数量 "latest_read": 0, // 上一次访问时的阅读数量 "count": 0, // 访问次数 } } return record } // 指定k,添加一个时间戳 let recordAdd = (itemid, comment=null, read=null) => { let record record = getRecord(itemid) // 防止重复添加 if (comment != null && read != null) { // 这俩应该在帖子打开后解析更新 record[itemid]["latest_comment"] = comment record[itemid]["latest_read"] = read } else { // 没有这两个参数就只更新次数 if (record[itemid]["latest_time"] && (new Date()).valueOf() - record[itemid]["latest_time"] < 1e3) return record[itemid]["latest_time"] = (new Date()).valueOf() record[itemid]["count"] ++ } // 记录更新后record localStorage.setItem("record", JSON.stringify(record)) } function timestampToTime(timestamp) { // https://www.byteblogs.com/article/259 var date = new Date(timestamp) var Y = date.getFullYear() + '-' var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-' var D = date.getDate() + ' ' var h = date.getHours() + ':' var m = date.getMinutes() + ':' var s = date.getSeconds() return Y+M+D+h+m+s } let getTimeInfo = (t) => { let delta = ((new Date()).valueOf() - t) / 1000 if (delta < 60) { // 小于60秒 delta = `${parseInt(delta)}秒前` } else if (delta / (60 ** 2) < 1) { // 小于一小时 delta = `${parseInt(delta / 60)}分前` } else if (delta / (60 ** 3) < 24) { // 小于一天 delta = `${parseInt(delta / (60 ** 2))}时前` } else { // 直接显示日期 delta = timestampToTime(t) } return delta } let parseElement = (ele) => { let itemid = ele.querySelector("a").href.match(/bbs-(\d+)/)[1] let [text, info, _] = ele.innerText.split("\n") let [username, comment, read] = info.split("/") comment = parseInt(comment.replace("回", "")) read = parseInt(read.replace("阅", "")) return [itemid, text, username, comment, read] } let validate = (text) => { return keywords.filter((keyword) => { return text.match(new RegExp(keyword, "gi")) }).length } let setting = () => { // 当前搜索屏蔽词settingText let div = document.createElement("div") div.setAttribute("id", "setting") let maxWidth = parseInt(getComputedStyle(document.querySelector(".btBox")).width.replace("px", "")) div.innerHTML = ` ` document.body.insertBefore(div, document.querySelector(".btbox")) setTimeout(() => { // 按钮展开 document.querySelector("#switch").addEventListener("click", function (e) { console.log(this) if (this.classList.contains("open")) { // 关闭 this.classList.remove("open") this.classList.add("close") this.querySelector("svg#close").style.display = "" this.querySelector("svg#open").style.display = "none" document.querySelector(".forbidden-selection").style.display = "none" } else if (this.classList.contains("close")){ // 打开 this.classList.remove("close") this.classList.add("open") this.querySelector("svg#close").style.display = "none" this.querySelector("svg#open").style.display = "" document.querySelector(".forbidden-selection").style.display = "flex" } }) // 数据选择 document.querySelector(".forbidden-selection input").addEventListener('click', (event) => { // 输入状态input-box边框变色 document.querySelector(".forbidden-selection .input-box").setAttribute('active', '') }) document.querySelector(".forbidden-selection input").addEventListener('blur', (event) => { // 失去焦点恢复 document.querySelector(".forbidden-selection .input-box").removeAttribute('active') }) let getSelectedForbiddenTags = () => { let forbiddenTags = [] let selectedNodes = document.querySelectorAll('.input-box .forbidden-tag') if (selectedNodes.length) { selectedNodes.forEach((ele) => { forbiddenTags.push(ele.getAttribute('value')) }) } return forbiddenTags } let isUseranme = (text) => { return text.search(/["'“].+["'”]/) != -1 } let removeForbiddenTag = (forbiddenTag) => { // 取消要从数据库删除 let forbiddenLocal = JSON.parse(localStorage.getItem("forbidden")) if (isUseranme(forbiddenTag)) { // 是用户 forbiddenLocal.usernames = forbiddenLocal.usernames.filter(tag=>{ return tag != forbiddenTag.slice(1, -1) }) } else { forbiddenLocal.keywords = forbiddenLocal.keywords.filter(tag=>{ return tag != forbiddenTag }) } localStorage.setItem("forbidden", JSON.stringify(forbiddenLocal)) } let saveForbiddenTag = (forbiddenTag) => { // 更新到本地 let forbiddenLocal = JSON.parse(localStorage.getItem("forbidden")) // 判断新增类型是否为用户 if (isUseranme(forbiddenTag)) { // 是用户 if (!forbiddenLocal.usernames.includes(forbiddenTag)) { forbiddenLocal.usernames.push(forbiddenTag.slice(1,-1)) } } else { if (!forbiddenLocal.keywords.includes(forbiddenTag)) { forbiddenLocal.keywords.push(forbiddenTag) } } // 储存 localStorage.setItem("forbidden", JSON.stringify(forbiddenLocal)) } let createForbiddenTag = (forbiddenTag) => { // 判断是否存在 if (getSelectedForbiddenTags().includes(forbiddenTag)) return let div = document.createElement('div') div.className = 'forbidden-tag' div.setAttribute('value', forbiddenTag) // 方便取变量 div.innerHTML = ` ${forbiddenTag}
×
` document.querySelector('.input-box').insertBefore(div, document.querySelector('.input-box .input')) // 绑定取消选择事件 div.querySelector('.cancel').addEventListener('click', (event) => { div.remove() // 取消选择对应的推荐标签 document.querySelectorAll('.forecommend-forbidden-tags .forbidden-tag').forEach((item) => { if (item.innerText == forbiddenTag) { item.classList.remove('forbidden-tag-selected') } }) removeForbiddenTag(forbiddenTag) }) saveForbiddenTag(forbiddenTag) // 检查推荐里面是否有同名 document.querySelectorAll(".forecommend-forbidden-tags>.forbidden-tag").forEach(ele=>{ if (ele.innerText == forbiddenTag) { ele.click() } }) } // 从已有变量选择 document.querySelectorAll(".forecommend-forbidden-tags .forbidden-tag").forEach((item) => { item.addEventListener('click', (event) => { if (item.innerText == "敬请期待") return // 选中颜色高亮 item.classList.add('forbidden-tag-selected') // 向数据框添加元素 createForbiddenTag(item.innerText) }) }) // 输入框输入变量 document.querySelector(".forbidden-selection input").addEventListener('keyup', function(event) { if (event.keyCode == 13) { let inputValue = this.value createForbiddenTag(inputValue) this.value = "" } }) // 首次进入需要从本地内存读取关键词 let forbiddenLocal forbiddenLocal = JSON.parse(localStorage.getItem("forbidden")) if (localStorage.getItem("forbidden") == null) { forbiddenLocal = {keywords: [], usernames: []} localStorage.setItem("forbidden", JSON.stringify(forbiddenLocal)) } forbiddenLocal.keywords.forEach(forbiddenTag=>{ createForbiddenTag(forbiddenTag) }) forbiddenLocal.usernames.forEach(forbiddenTag=>{ createForbiddenTag(`"${forbiddenTag}"`) }) }, 1e3); } let currentURL = window.location.href // 判断当前网址是否为主页 if (currentURL == 'https://yaohuo.me/' || currentURL.search(/bbs-\d+/) != -1) { console.log("主页/新帖") // 主页移除关键词 let items = [] document.querySelectorAll('.list a').forEach((e) => { // 每个节点有两个关键信息:1.href;2.innerText let href = e.href let text = e.innerText if (validate(text)) { console.log(`remove ${text}`) } else { items.push(`${items.length+1}.${text}`) } }) document.querySelector('.list').innerHTML = items.join("
") // 添加监控点击 document.querySelectorAll('.list>a').forEach((a) => { a.addEventListener("click", (e) => { let itemid = a.href.match(/bbs-(\d+)/)[1] recordAdd(itemid) }) }) } else if (currentURL.startsWith('https://yaohuo.me/bbs/book_list.aspx?')) { setting() // 可能是新帖,也可能是搜索页 if (currentURL.includes('key=')) { console.log("搜索页") let searchText = decodeURI(currentURL.match(/key=(.+?)&/)[1]) console.log(searchText) if (validate(searchText)) { setTimeout(() => { alert("你可以遗忘屏蔽词,本脚本将永远不会!\nYou can forget about blocking words, this script will never!") }, 233); } } else { console.log("新帖页") } if (!document.querySelector("#KL_show_next_list")) { let div = document.createElement("div") div.setAttribute('id', 'KL_show_next_list') document.body.insertBefore(div, document.querySelector(".btBox")) } // 把body下的条目统一放到KL_show_next_list下,便于统一操作 document.querySelector("#KL_show_next_list").style.display = "" document.querySelectorAll(".listdata").forEach((ele) => { document.querySelector("#KL_show_next_list").appendChild(ele.cloneNode(true)) ele.remove() }) let filterList = (mutations, observer) => { document.querySelectorAll("#KL_show_next_list>div").forEach((ele) => { // 每个节点有很多元素 let [itemid, text, username, comment, read] = parseElement(ele) if (validate(text) || usernames.includes(username)) { // 这是被过滤掉的e,需要移除 console.log(`remove ${text} - ${username}`) ele.remove() } else { // 这是保留下来的ele,先绑定一个点击事件 ele.querySelector("a").addEventListener("click", function(e) { // 更新次数 recordAdd(itemid) }) let record = getRecord(itemid)[itemid] if (record["count"] > 0) { // 有浏览记录才处理 ele.style.position = "relative" let add_comment = comment - record["latest_comment"] let add_read = read - record["latest_read"] if (ele.querySelector("#record")) { ele.querySelector("#count>span").innerText = record["count"] ele.querySelector("#time_info").innerText = getTimeInfo(record["latest_time"]) if (add_comment > 0) { ele.querySelector("#add_comment").style.display = 'inline-block' } else { ele.querySelector("#add_comment").style.display = 'none' } if (add_read > 0) { ele.querySelector("#add_read").style.display = 'inline-block' } else { ele.querySelector("#add_read").style.display = 'none' } ele.querySelector("#add_comment>span").innerText = (add_comment > 99) ? "+" : add_comment ele.querySelector("#add_read>span").innerText = (add_read > 99) ? "+" : add_read } else { let div = document.createElement('div') div.setAttribute("id", "record") div.style = ` position: absolute; right: 8px; top: 8px; color: #999; font-size: 10px; opacity: .7; ` div.innerHTML = `
${record["count"]}
${(add_comment > 99) ? "+" : add_comment}
${(add_read > 99) ? "+" : add_read}
${getTimeInfo(record["latest_time"])}
` ele.appendChild(div) } } } }) } filterList(null, null) var observer = new MutationObserver(filterList) var node = document.querySelector('#KL_show_tip') if (node) { observer.observe(node, {childList: true}) } setInterval(() => { // 5s一次更新时间 filterList(null, null) }, 1e3) } // 监控浏览页面数据变化及时更新 if (currentURL.search(/bbs-\d+/) != -1) { let update = (mutations, observer) => { let itemid = currentURL.match(/bbs-(\d+)/)[1] let comment // 根据最新回复显示楼层判断回复数量 let text = document.querySelector(".reline").innerText let res = text.match(/\[(\d+)楼\]/) if (res) { comment = parseInt(res[1]) } else { comment = document.querySelectorAll(".reline").length } let read = parseInt(document.querySelector(".content").innerText.match(/阅(\d+)/)[1]) recordAdd(itemid, comment, read) console.log(itemid, comment, read) } setTimeout(() => { update(null, null) let observer = new MutationObserver(update) const config = {childList: true, subtree: true, characterDataOldValue: true} document.querySelectorAll(".content").forEach(ele=>{ observer.observe(ele, config) }) }, 1000) } // 添加搜索按钮监听 if (currentURL == 'https://yaohuo.me/') { document.querySelector("input[type=submit]").addEventListener("click", (e) => { let text = document.querySelector("input[type=text]").value console.log(text) if (validate(text)) { e.preventDefault() document.querySelector("input[type=text]").value = "请重新组织你的语言!" setTimeout(() => { document.querySelector("input[type=text]").value = "" }, 1000); } }) } })();