- // ==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://gf.qytechs.cn/users/788115
- // ==/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"))
- var keywords = forbiddenLocal.keywords
- var 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 = `
- <button id="switch" class="close">
- <svg viewBox="0 0 100 100" width="20" height="20" id="open" style="display: none;">
- <path d="M5,70 L50,30 95,70" style="
- fill: none;
- stroke: #1abc9c;
- stroke-linecap: round;
- stroke-linejoin: round;
- stroke-width: 10;
- "/>
- </svg>
- <svg viewBox="0 0 100 100" width="20" height="20" id="close">
- <path d="M5,30 L50,70 95,30" style="
- fill: none;
- stroke: #1abc9c;
- stroke-linecap: round;
- stroke-linejoin: round;
- stroke-width: 10;
- "/>
- </svg>
- </button>
- <div class="forbidden-selection" style="
- display: flex;
- flex-flow: column;
- align-items: center;
- display: none;
- ">
- <div class="input-box">
- <div class="forbidden-tag" value="[发晒看]工资">
- [发晒看]工资
- <div class="cancel">×</div>
- </div><div class="input">
- <input placeholder="按回车键Enter创建屏蔽词" type="text">
- </div>
- </div>
- <div class="forecommend-forbidden-tags">
- <div class="forbidden-tag forbidden-tag-selected">[发晒看]工资</div>
- <div class="forbidden-tag">(拼多多|并夕夕|pdd)</div>
- <div class="forbidden-tag">话费</div>
- <div class="forbidden-tag">"张三"</div>
- <div class="forbidden-tag" style="cursor: not-allowed;">敬请期待</div>
- </div>
- <div id="forbidden-list" style="width: 100%;"></div>
- </div>
- <style>
- *, *::after, *::before {
- box-sizing: border-box;
- --bakground-color: #2c3e50;
- --selected-color: #3498db;
- --pic-opacity: 23%;
- --title-height: 50px;
- --number-color: #00cec9;
- --forbidden-input-box-width: ${maxWidth * 0.9}px;
- --forbidden-input-box-border-width: 1.5px;
- }
- #switch {
- width: 100%;
- height: 20px;
- background: none;
- border: 0;
- cursor: pointer;
- }
- #setting {
- display: flex;
- justify-content: center;
- flex-flow: column;
- padding: 10px;
- border: 1px solid rgb(26, 188, 156);
- border-radius: 8px;
- width: 97%;
- margin: 0 auto;
- margin-bottom: 8px;
- }
- .forbidden-selection {
- margin-top: 10px;
- }
- .forbidden-selection > .input-box {
- width: var(--forbidden-input-box-width);
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- padding: 0px;
- margin-bottom: 5px;
- outline: solid var(--forbidden-input-box-border-width) rgba(0, 0, 0, .2);
- font-size: 14px;
- }
-
- .forbidden-selection > .input-box[active] {
- outline: solid calc(1.2*var(--forbidden-input-box-border-width)) var(--number-color);
- }
-
- .forbidden-selection > .input-box .input {
- display: block;
- min-width: calc(var(--forbidden-input-box-width) / 2);
- flex: 1;
- margin: 0 10px;
- height: 40px;
-
- }
-
- .forbidden-selection > .input-box .input input {
- width: 100%;
- height: 100%;
- outline: none;
- border: none;
- }
-
- .forbidden-selection > .input-box:not([active]):hover {
- outline: solid var(--forbidden-input-box-border-width) rgba(0, 0, 0, 1);
- }
-
- .forecommend-forbidden-tags {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- flex-direction: row;
- width: var(--forbidden-input-box-width);
- }
-
- .forecommend-forbidden-tags .forbidden-tag, .input-box .forbidden-tag {
- border: solid 1px white;
- border-radius: 5px;
- padding: 4px 10px;
- margin: 5px 0 5px 5px;
- background-color: #f6f6f6;
- cursor: pointer;
- font-size: 14px;
- line-height: 1.5em;
- user-select: none;
- }
- .input-box .forbidden-tag:active {
- background-color: #FF7D7D;
- }
- .forecommend-forbidden-tags {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- flex-direction: row;
- width: var(--forbidden-input-box-width);
- }
-
- .forecommend-forbidden-tags .forbidden-tag, .input-box .forbidden-tag {
- border: solid 1px white;
- border-radius: 5px;
- padding: 4px 10px;
- margin: 5px 0 5px 5px;
- background-color: #f6f6f6;
- cursor: pointer;
- font-size: 14px;
- }
-
- .forecommend-forbidden-tags .forbidden-tag-selected {
- background-color: var(--number-color);
- color: #fff;
- cursor: not-allowed;
- }
-
- .input-box .forbidden-tag {
- display: flex;
- flex-direction: row;
- justify-content: space-around;
- background-color: var(--number-color);
- color: #fff;
- padding-right: 5px;
-
- }
- .forbidden-selection > .input-box .forbidden-tag .cancel {
- margin-left: 5px;
- }
- </style>
- `
- document.body.insertBefore(div, document.querySelector(".btbox"))
- setTimeout(() => {
- // 按钮展开
- document.querySelector("#switch").addEventListener("click", function (e) {
- 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
- })
- }
- keywords = forbiddenLocal.keywords
- usernames = forbiddenLocal.usernames
- 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)
- }
- }
- // 储存
- keywords = forbiddenLocal.keywords
- usernames = forbiddenLocal.usernames
- 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}
- <div class="cancel">×</div>
- `
- document.querySelector('.input-box').insertBefore(div, document.querySelector('.input-box .input'))
- // 绑定取消选择事件
- div.querySelector('.cancel').addEventListener('click', (e) => {
- div.remove()
- // 取消选择对应的推荐标签
- document.querySelectorAll('.forecommend-forbidden-tags .forbidden-tag').forEach((item) => {
- if (item.innerText == forbiddenTag) {
- item.classList.remove('forbidden-tag-selected')
- }
- })
- removeForbiddenTag(forbiddenTag)
- e.stopPropagation()
- })
- // 点击标签编辑
- div.addEventListener("click", (e) => {
- let tag = div.getAttribute("value")
- div.querySelector(".cancel").click()
- document.querySelector(".forbidden-selection input").value = tag
- })
- // 保存
- 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}.<a href="${href}">${text}</a>`)
- }
- })
- document.querySelector('.list').innerHTML = items.join("<br>")
- // 添加监控点击
- 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.replace(" /", ""))) {
- // 这是被过滤掉的e,需要移除
- console.log(`remove ${text} - ${username}`)
- document.querySelector("#forbidden-list").appendChild(ele.cloneNode(true))
- 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 = `
- <div id="count" style="
- display: inline-block;
- text-align: center;
- line-height: 15px;
- width: 15px;
- height: 15px;
- background-color: #FF7D7D;
- border-radius: 50%;
- ">
- <span style="font-size: 12px; color: white;">${record["count"]}</span>
- </div>
- <div id="add_comment" style="
- display: inline-block;
- text-align: center;
- line-height: 15px;
- width: 15px;
- height: 15px;
- background-color: #54BAB9;
- border-radius: 50%;
- display: ${(add_comment<=0) ? 'none' : ''};
- ">
- <span style="font-size: 12px; color: white;">${(add_comment > 99) ? "+" : add_comment}</span>
- </div>
- <div id="add_read" style="
- display: inline-block;
- text-align: center;
- line-height: 15px;
- width: 15px;
- height: 15px;
- background-color: #636e72;
- border-radius: 50%;
- display: ${(add_read <= 0) ? 'none' : ''};
- ">
- <span style="font-size: 12px; color: white;">${(add_read > 99) ? "+" : add_read}</span>
- </div>
- <div style="
- width: 50px;
- display: inline-block;
- text-align: right;
- ">
- <span id="time_info">${getTimeInfo(record["latest_time"])}</span>
- </div>
- `
- 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);
- }
- })
- }
- })();