水源保障组模拟器

识别水源用户发言中的谐音词,追踪反贼的所有言论,不放过任何一个危害社区的言论

// ==UserScript==
// @name         水源保障组模拟器
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  识别水源用户发言中的谐音词,追踪反贼的所有言论,不放过任何一个危害社区的言论
// @author       5ur01
// @match        https://shuiyuan.sjtu.edu.cn/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        GM_log
// @grant        GM_addStyle
// @require      https://unpkg.com/pinyin-pro
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

let { pinyin } = pinyinPro;

function match(string, pattern, contextLength) {
    let patternStr = pattern.join('')
    let index = string.indexOf(patternStr)
    if (index != -1) {
        return Array.from({ length: pattern.length }, (_, i) => index + i)
    }
    let stringPos = 0
    let patternPos = 0
    let prevIsNotLetter = true
    let matchedPos = []
    while(true){
        if (patternPos == pattern.length) {
            if (pattern.length == 0 || contextLength <= 0 || stringPos - matchedPos[0] <= contextLength) {
                break
            } else {
                GM_log(matchedPos)
                stringPos = matchedPos[0] + 1
                patternPos = 0
                matchedPos = []
                prevIsNotLetter = false
                continue
            }
        }
        if (stringPos == string.length) {
            return []
        }
        let stringCur = string[stringPos].toLowerCase()
        let patternCur = pattern[patternPos]

        let isChinese = pinyin(stringCur) != stringCur

        let matchOne =
            (prevIsNotLetter || isChinese) ?
            pinyin(stringCur, { multiple: true, type: "array", pattern: "initial" }).some(c => c == patternCur)
        :false

        function isLetter(c) {
            return c.toLowerCase() != c.toUpperCase();
        }
        let isLetterCur = isLetter(stringCur)

        if (matchOne) {
            matchedPos.push(stringPos)
            stringPos = stringPos + 1
            patternPos = patternPos + 1
            prevIsNotLetter = !isLetterCur
        } else {
            stringPos = stringPos + 1
            prevIsNotLetter = !isLetterCur
        }
    }
    return matchedPos
}
function makeBold(string, pattern, contextLength) {
    let array = match(string, pattern, contextLength)
    let starts = [0].concat(array.map(value => value + 1))
    let ends = array.concat([string.length])
    return [array.length != 0, array.map((v, i, a) => string.substring(starts[i], ends[i]) + "<b>" + string[v] + "</b>").concat(string.substring(starts.at(-1), ends.at(-1))).join('')]
}


function divHoverme(content) {
    return "<span class=\"hoverme\">" + content + "</span>"
}
function divShowme(content) {
    return "<div class=\"showme\">" + content + "</div>"
}


(function () {
    'use strict';
    GM_addStyle(
        `
.showme {
  display: none;
}
.hoverme {
  color: gray;
  border:solid;
  border-radius: 2px;
  padding: 2px;
  margin: 2px;
}
.hoverme:hover + .showme {
  display: block;
  color: gray;
}`
    )
    let patterns = [["x", "j", "p"], ['p', 'l', 'y'], ['j', 'z', 'm'], ['b', 'x', 'l'], ['h', 'j', 't'], ['l', 'k', 'q'], ['zh', 'z', 'y'], ['z', 'z', 'y'], ['zh', 'zh', 'y'], ['z', 'zh', 'y'], ['w', 'j', 'b'], ['w', 'h', 'n'], ['m', 'z', 'd'], ['d', 'x', 'p']]
    let contextLength = 20
    function check(dom, contextLength) {
        let classDone = "done"
        if (!dom.className.includes(classDone)) {
            let extraHTML = ""
            patterns.forEach(
                pattern => {
                    let [matched, bold] = makeBold(dom.textContent, pattern, contextLength)
                    if (matched) {
                        dom.classList.add(classDone)
                        extraHTML += divHoverme(pattern.join('')) + divShowme(bold)
                    }
                }
            )
            if (extraHTML.length != 0) {
                dom.innerHTML += extraHTML
            }
        }
    }
    setInterval(
        () => {
            document.querySelectorAll("div.topic-body.clearfix > div.regular.contents > div.cooked")
                .forEach(x => check(x, contextLength))
            document.querySelectorAll("div.fps-topic > div.blurb.container")
                .forEach(x => check(x, contextLength))
        }
        , 1000)
})();