ZhihuOneKeyReport-JavaScript

2022/9/3 15:00:00

// ==UserScript==
// @name        ZhihuOneKeyReport-JavaScript
// @namespace   Violentmonkey Scripts
// @match       https://www.zhihu.com/people/*
// @require     https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js
// @grant       window.open
// @grant       window.focus
// @version     1.1
// @author      尼尼尼@知乎
// @author      备份公众号:尼尼尼不打拳
// @description 2022/9/3 15:00:00
// @license     GPL3
// ==/UserScript==


// 用于举报“不友善内容”时简化操作步骤的Javascript脚本。
// 请勿滥用

// 简要操作说明:
// 1.在浏览器扩展商店中搜索“暴力猴“、”油猴“之类扩展插件,安装并启用。下面以”暴力猴“为例。
// 2.在暴力猴插件中点击”+“号,新建JS脚本,将此脚本内容全部复制粘贴进去并保存。
// 3.进入知乎主页,登录知乎账户。在暴力猴插件控制台中启用上面保存的脚本。注意不能与单页回答检查脚本同时启用。
// 4.刷新页面,右侧滚动条旁边会出多一个蓝色按钮(备用按钮,不是必须点击)
// 5.进入要举报内容的个人主页, 点击回答、文章、想法等模块后,注意需要手工刷新一次页面。
//   在每条内容后就会多出一个“一键举报”按钮。
// 6.知乎的文章、回答等模块是部分动态加载的,所以有时候出现只有一半内容加上按钮的情况,
//   这时点击一下右侧的蓝色按钮手工添加即可。


(function () {
        'use strict';

        // 开始对Modal窗口的变化监视,及提交处理。
        function handleModalSubmit(target){
            // Firefox和Chrome早期版本中带有前缀
            var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
            // 创建Modal窗口的观察者对象
            var modalObserver = new MutationObserver(function (mutations) {
                try {
                    mutations.forEach(function (mutation) {
                        switch (mutation.type) {
                            case 'childList':
                                quickReport(modalObserver);
                                throw new Error("模态窗口变化已检测到,抛出异常中止。")
                        }
                    });
                }catch (e){
                    console.log(e);
                }
            });
            // 配置观察选项
            var config = {
                childList: true  //观察目标子节点的变化,添加或者删除
            }
            // 传入目标节点和观察选项
            modalObserver.observe(target, config);
        }

        function handlePopMenuObserver(observer) {
            // 终止对pop menu的observer监控
            observer.disconnect();

            // 开始处理Modal窗口的提交
            var target = document.querySelector('div[class="Modal-content"]');
            handleModalSubmit(target)

        }

        function handleNormalReport(reportBtn){
            var originalReportBtn = reportBtn.previousElementSibling;
            if(originalReportBtn === null ){
                console.log('错误: 未查找到原始举报按钮');
            }

            originalReportBtn.click();
            var target = document.querySelector('div[class="Modal-content"]');
            handleModalSubmit(target);
        }

        function handlePopMenuReport(reportBtn) {
            // 先点击pop menu,这里要注意选择button元素,才能click。不要选到外层的div.
            console.log('点击pop menu');
            var popMenu = reportBtn.previousElementSibling.firstChild;

            // 使用mutation observer监视点击pop menu后生成的原始举报按钮
            var docBody = document.querySelector('body');
            // Firefox和Chrome早期版本中带有前缀
            var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
            var btn = null;
            // 创建pop menu的观察者对象
            var popMenuObserver = new MutationObserver(function (mutations) {
                try{
                    mutations.forEach(function (mutation) {
                        switch (mutation.type) {
                            case 'childList':
                                var selfMenu = document.querySelector('div[class="Menu AnswerItem-selfMenu"]');
                                var nsl = selfMenu.childNodes;
                                // console.log(nsl);
                                for(let n of nsl){
                                    if(n.innerHTML === '举报'){
                                        btn = n;
                                        // console.log(btn);
                                    }
                                }
                                btn.click();
                                handlePopMenuObserver(popMenuObserver);
                                throw new Error("回答模块按钮栏的pop menu变化已检测到,抛出异常中止。")
                        }
                    });
                }
                catch(e) {
                    console.log(e);
                }
            });

            var config = {
                childList: true //观察目标子节点的变化,添加或者删除
            }
            // 传入目标节点和观察选项
            popMenuObserver.observe(docBody, config);

            // 建立观察者后再点击下拉菜单
            popMenu.click();
        }

        function quickReport(observer) {
            var spanList = document.querySelectorAll('div[class="Modal-content"] span');
            // console.log(spanList);
            var reason = null;

            for (let i = 0; i < spanList.length; ++i) {
                let span = spanList[i]
                if (span.innerHTML === '辱骂、人身攻击等不友善行为') {
                    reason = span.parentElement;
                }
            }

            // 终止对Modal窗口的变化监控
            observer.disconnect();
            reason.click();

            var dialog = document.querySelector('textarea[class="Input"]');
            dialog.innerHTML = '不友善,引发情绪对立';

            var btnArea = document.querySelector('textarea[class="Input"]').parentElement.nextSibling;
            var btnList = btnArea.childNodes;

            var btnOnDialog = null;

            for (let i = 0; i < btnList.length; ++i) {
                //console.log(btnList[i]);
                if (btnList[i].innerHTML === "举报") {
                    btnOnDialog = btnList[i];
                    break;
                }
            }
            console.log('准备点击');
            btnOnDialog.click();
        }

        // 一键举报按钮的关联事件
        function watchReasonLoad(e) {
            var reportBtn = e.target;
            //对于带弹出菜单和不带菜单的页面,需要作分别处理。
            var url = window.location.href;
            if(url.includes('answers')){
                console.log('回答页面有pop菜单,需要单独处理');
                handlePopMenuReport(reportBtn);
            }else{
                console.log('回答以外的页面通用处理');
                handleNormalReport(reportBtn);
            }
        }

        // 监听“回答”模块的内容框架中的加载情况。即等待回答的内容动态加载完成。
        function monitorAnswerTab() {
            // var answerBody = document.querySelector('div[class="ListShortcut"]');
            var answerBody = document.getElementById('Profile-answers');

            // Firefox和Chrome早期版本中带有前缀
            var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver

            var answerObserver = new MutationObserver(function (mutations) {
                try {
                    mutations.forEach(function (mutation) {
                        switch (mutation.type) {
                            case 'childList':
                                console.log("进到这里说明内容是异步加载的,已加载完成")
                                handlerContentObserver(answerObserver);
                                // 这里有多个ChildList的变动,所以这里执行一次之后,要跳出foreach。
                                // 不然会将堆栈中记录的所有变化遍历一次,重复执行。
                                throw new Error('内容模块已加载,抛出异常中止多余的遍历');
                        }
                    });
                }catch(e){
                    console.log(e);
                }
            });

            var config = {
                childList: true,
                subtree: true
            }
            // 传入目标节点和观察选项
            answerObserver.observe(answerBody, config);
        }

        // 处理回答的obeserver的回调函数
        function handlerContentObserver(answerObserver) {
            //终止监视
            answerObserver.disconnect();
            // 内容加载后,执行初始化
            console.log("内容加载后,执行初始化")
            init();
        }

        // 在每个"举报"按钮后添加"一键举报"按钮。
        // 想法和文章模块,是一部分随请求返回,另一部分异步加载。不保证每次都能全部添加上。
        function addReportButton() {

            var btnBarList = document.querySelectorAll('div[class="ContentItem-actions"]');
            //btnBarList = document.getElementsByClassName('ContentItem-actions');

            console.log("开始添加按钮")

            // 如果该Tab下一条内容都没有,直接return
            if (btnBarList.length === 0 ) {
                console.log("发生错误: 内容尚未加载,需手动点击右侧按钮注入");
                return;
            }

            for (let item of btnBarList) {

                // 视频模块的内容操作栏的结果与其它不同,多套了两层div, 需要单独处理。
                var url = window.location.href;
                if(url.includes('/zvideos')){
                    let newItem = item.firstChild.firstChild;
                    // console.log(newItem);
                    newItem.insertAdjacentHTML('beforeend', '<button id="OneKeyReport" type="button" class="one-key-report" style="color:blue">   【一键举报】</button>');
                }else{
                    // 其它模块正常处理
                    //$(item).append('<button id="OneKeyReport" type="button" class="one-key-report"> 【一键举报】</button>');
                    item.insertAdjacentHTML('beforeend', '<button id="OneKeyReport" type="button" class="one-key-report" style="color:blue">  【一键举报】</button>');
                }
            }

            // 遍历,给所有新增按钮注册事件
            var oneKeyReportList = document.querySelectorAll('button[class="one-key-report"]');

            for (let item of oneKeyReportList) {
                item.addEventListener("click", watchReasonLoad, false);
            }
            console.log('添加新按钮完毕');
        }

        // 在每个翻页按钮中绑定刷新页面的event listener
        function addPageBtnEvent() {
            var pageBtnList = document.querySelectorAll('div[class="Pagination"] > button');
            //console.log(pageBtnList);
            //如果没有翻页栏,直接return
            if(pageBtnList === null){
                return;
            }
            for (let item of pageBtnList) {
                //item.addEventListener("click", addReportButton, false);
                item.addEventListener("click", refreshPage, false);
            }
            console.log('翻页按钮添加事件完成');
        }

        // 翻页后一次刷新,保证显示新增按钮
        function refreshPage(e){
            console.log(e.target)
            console.log("自动刷新页面");
            e.target.click();
            window.location.reload();
        }

        // 监视翻页栏的的翻页变化, 发布变化时则执行添加按钮和刷新的操作
        // function observePageBar() {
        //     // Firefox和Chrome早期版本中带有前缀
        //     var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
        //
        //     // 创建翻页栏的观察者对象
        //     var targetPageBar = document.querySelector('div[class="Pagination"]');
        //     console.log("找翻页栏");
        //     console.log(targetPageBar);
        //     //如果当前Tab没有翻页栏,直接return,不用监视了
        //     if(targetPageBar === null){
        //         console.log("本页没有翻页栏");
        //         return;
        //     }
        //     var pageObserver = new MutationObserver(function (mutations) {
        //         mutations.forEach(function (mutation) {
        //             switch (mutation.type) {
        //                 case 'childList':
        //                     console.log(mutation);
        //                     console.log("发生翻页了===========");
        //                     addReportButton();
        //                     addPageBtnEvent();  // 因为页码元素会发生变化,所以每次都重新绑定
        //                     break;
        //             }
        //         });
        //     });
        //     // 配置观察选项,翻页栏监听子元素的变化
        //     var config = {
        //         childList: true,
        //         subtree: true,
        //         attributes:true
        //     }
        //     // 传入目标节点和观察选项
        //     pageObserver.observe(targetPageBar, config);
        // }

        function init(){
            // 先开始监听个人主页中回答模块内容的动态加载。
            // 知乎的回答模块的内容全部是动态加载, 还好办。
            // 想法和文章模块,则是一部分内容随请求返回,另一部分又是异步加载,这种处理起来很麻烦,靠手动按钮弥补。
            var url = window.location.href;
            if(url.includes('/answers')){
                console.log("当前为回答模块, 单独处理");
                // 如果目标内容没有加载,则开始监听;如果已经加载,则直接在下面作普通初始化
                var answerContent = document.querySelectorAll('div[class="ContentItem-actions"]');
                if(answerContent.length === 0){
                    console.log("回答模块的内容还没有加载,开始监听");
                    monitorAnswerTab();
                }
            }

            //回答之外的模块作普通初始化处理
            // 添加一键举报按钮
            addReportButton();
            // 在每个翻页按钮中绑定 listener
            addPageBtnEvent();

            // 取消监视翻页栏的的翻页变化, 通过每次翻页时重新加载页面,来重新绑定事件,不然容易循环调用。
            // observePageBar();
        }

        // 添加手动插入举报按钮
        function addFloatButton() {
            $('body').append('<div id="FloatButton">点<br>击<br>添<br>加<br>一<br>键<br>举<br>报</div>')
            $('#FloatButton').css('width', '20px')
            $('#FloatButton').css('position', 'fixed')
            $('#FloatButton').css('top', '150px')
            $('#FloatButton').css('right', '3px')
            $('#FloatButton').css('background-color', '#2F4F4F')
            $('#FloatButton').css('color', 'white')
            $('#FloatButton').css('font-size', 'small')
            $('#FloatButton').css('z-index', 100)
            $('#FloatButton').css('border-radius', '25px')
            $('#FloatButton').css('text-align', 'center')
            $('#FloatButton').css('cursor', 'default')
            $('#FloatButton').click(function () {
                init();
            });
        }

        document.onreadystatechange = function () {
            if (document.readyState === "complete") {

                console.log('添加手动插入举报按钮');
                addFloatButton();

                // 进行初始化
                init();
            }
        }
    }

)();