Greasy Fork

【贴吧机器人】贴吧关键词找发帖、水贴机器人

贴吧自动找发帖,基于页面JS; ESC热键 停止打开页面;缓存只缓存 500 条,即发过500个贴后旧贴没沉可能会重复发帖。脚本部分参考《知乎增强》编写。用的人多接着更新图片上传改善配置UI~~

当前为 2022-02-18 提交的版本,查看 最新版本

// ==UserScript==
// @name         【贴吧机器人】贴吧关键词找发帖、水贴机器人
// @namespace    http://tampermonkey.net/
// @version      1.0.4
// @description  贴吧自动找发帖,基于页面JS; ESC热键 停止打开页面;缓存只缓存 500 条,即发过500个贴后旧贴没沉可能会重复发帖。脚本部分参考《知乎增强》编写。用的人多接着更新图片上传改善配置UI~~
// @author       贴吧用户:皮燕子
// @match        *://tieba.baidu.com/p/*
// @match        *://tieba.baidu.com/f*
// @license      bonelf.com
// @icon         http://tb3.bdstatic.com/public/icon/favicon-v2.ico
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_openInTab
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_notification
// @grant        GM_info
// ==/UserScript==

(function () {
    'use strict';
    // Your code here...

    var menu_ALL = [
        ['menu_search', '匹配关键词打开帖子功能', '', true],
        ['menu_send', '发帖功能(输入框填充回帖内容)', '', true],
        ['menu_sendAuto', '系统自动发帖(点击发送)', '', false],
        ['menu_closeAuto', '打开帖子后自动两秒后关闭', '', true],
        ['menu_openPinlv', '打开窗口频率', '秒/次', 5],
        ['menu_titleRegStr', '关键词', ';;分隔每个关键词,::分隔关键词与回复,;分隔多关键词匹配', "uzi;乌兹::红温温 脸红红 啃手手;;如何评价::感觉不如原神..".split(';;')],
        ['menu_page', '一次最多打开帖子数', '一次最多打开帖子数', 20],
        // ['menu_pic', '穿插图片(网络图片地址,不支持本地文件)', '使用网络图片地址', ''],
        ['menu_isCache', '缓存帖子防止重复', '这将使得下次打开页面运行脚本打开的帖子不会与上次运行打开的重复,只缓存 500 条', true]
    ], menu_ID = [];
    for (let i = 0; i < menu_ALL.length; i++) { // 如果读取到的值为 null 就写入默认值
        if (GM_getValue(menu_ALL[i][0]) == null) {
            GM_setValue(menu_ALL[i][0], menu_ALL[i][3])
        }
    }

    function customMenuPrompt(menuName) {
        let nowBlockKeywords = menu_value(menuName) || ''
        let newBlockKeywords = prompt('编辑', nowBlockKeywords);
        if (newBlockKeywords === '') {
            GM_setValue(menuName, '');
            registerMenuCommand(); // 重新注册脚本菜单
        } else if (newBlockKeywords != null) {
            GM_setValue(menuName, newBlockKeywords);
            registerMenuCommand(); // 重新注册脚本菜单
        }
    }

    function customMenuTitleRegStr() {
        let nowBlockKeywords = '';
        let titleRegStr = menu_value('menu_titleRegStr');
        if(typeof(titleRegStr) == 'string') {
          titleRegStr = titleRegStr.split(';;')
        }
        (titleRegStr || []).forEach(function (item) {
            nowBlockKeywords += ';;' + item
        })
        let newBlockKeywords = prompt('编辑', nowBlockKeywords);
        if (newBlockKeywords === '') {
            GM_setValue('menu_titleRegStr', []);
            registerMenuCommand(); // 重新注册脚本菜单
        } else if (newBlockKeywords != null) {
            GM_setValue('menu_titleRegStr', newBlockKeywords.split(';;'));
            registerMenuCommand(); // 重新注册脚本菜单
        }
    }

    /**
     * 注册脚本菜单
     */
    function registerMenuCommand() {
        if (menu_ID.length >= menu_ALL.length) { // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
            for (let i = 0; i < menu_ID.length; i++) {
                GM_unregisterMenuCommand(menu_ID[i]);
            }
        }
        for (let i = 0; i < menu_ALL.length; i++) { // 循环注册脚本菜单
            menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
            if (menu_ALL[i][0] === 'menu_titleRegStr') {
                if (menu_value(menu_ALL[i][0])) menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function () {
                    customMenuTitleRegStr();
                });
            } else if (menu_ALL[i][0] === 'menu_page' || menu_ALL[i][0] === 'menu_openPinlv' || menu_ALL[i][0] === 'menu_pic') {
                if (menu_value(menu_ALL[i][0])) menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function () {
                    customMenuPrompt(menu_ALL[i][0]);
                });
            } else {
                menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3] ? '✅' : '❌'} ${menu_ALL[i][1]}`, function () {
                    menu_switch(`${menu_ALL[i][3]}`, `${menu_ALL[i][0]}`, `${menu_ALL[i][2]}`)
                });
            }
        }
    }


// 菜单开关
    function menu_switch(menu_status, Name, Tips) {
        if (menu_status == 'true') {
            GM_setValue(`${Name}`, false);
            GM_notification({
                text: `已关闭 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function () {
                    location.reload();
                }
            });
        } else {
            GM_setValue(`${Name}`, true);
            GM_notification({
                text: `已开启 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function () {
                    location.reload();
                }
            });
        }
        registerMenuCommand(); // 重新注册脚本菜单
    }


// 返回菜单值
    function menu_value(menuName) {
        for (let menu of menu_ALL) {
            if (menu[0] == menuName) {
                return menu[3]
            }
        }
    }

    registerMenuCommand();

    // ===================== reply

    var pics = [menu_value("menu_pic")]

    function sleep(time) {
        return new Promise((resolve) => setTimeout(resolve, time));
    }

    async function wait(time) {
        // console.log('action finish, wait paging loading ' + time / 1000 + 's ...');
        await sleep(time);
        // console.log('finish load');
    }

    function parseQueryString(str) {
        var arr = [],
            length = 0,
            res = {},
            si = str.indexOf("?");
        str = si === -1 ? undefined : str.substring(si + 1);
        if (str) {
            // console.log(str);
            arr = str.split('&');
            length = arr.length;
            for (var i = 0; i < length; i++) {
                res[arr[i].split('=')[0]] = arr[i].split('=')[1];
            }
        }
        return res;
    }

    function execSend() {
        // console.log("exec")
        var data = parseQueryString(window.location.href);
        if (!data.keyword) {
            let dom = $('h3.core_title_txt.pull-left.text-overflow');
            var title = dom.attr("title")
            if (titleReg.length > 0) {
                out:for (let item of titleReg) {
                    for (const keyword of item.keywords) {
                        if (new RegExp(keyword).test(title)) {
                            data = {keyword: keyword, reply: item.reply}
                            break out;
                        }
                    }
                }
            }
        }

        var h = $(document).height() - $(window).height();
        $(document).scrollTop(h);
        var sendAuto = menu_value('menu_sendAuto');
        var closeAuto = menu_value('menu_closeAuto');
        setTimeout(() => {
            if (pics.length > 0) {
                var randomPic = pics[Math.floor(Math.random() * pics.length)];
                if (randomPic) {
                    $('.edui-btn.edui-btn-image').click()
                    while ($('.from_web>a').length <= 0) {
                        wait(500)
                    }
                    $('.from_web>a').click()
                    while ($('.l_netpic_input.j_input.ui_textfield').length <= 0) {
                        wait(500)
                    }
                    $('.l_netpic_input.j_input.ui_textfield').val(pics[Math.floor(Math.random() * pics.length)])
                    $('.ui_btn.ui_btn_m.j_addpic').click()
                    wait(1500)
                    $('#dialogJbody').find('.ui_btn.ui_btn_m').click()
                    wait(400)
                }
            }

            $("#ueditor_replace>p").html(
                (data.keyword ? decodeURIComponent(data.reply || "") : "") +
                "@" + decodeURIComponent(data.keyword));

            if (sendAuto) {
                $(".poster_submit").click()
            }

            if (closeAuto) {
                setTimeout(() => {
                    window.close();
                }, 2000)
            }
        }, 500)
    }

    // ============ find

    const openPinlvSave = Number(menu_value('menu_openPinlv'));
    const openPinlv = !openPinlvSave || openPinlvSave < 5 ? 5 : openPinlvSave;
    const pageSave = Number(menu_value('menu_page')) || 20;
    const page = pageSave > 100 ? 100 : pageSave;
    var titleRegStr = menu_value('menu_titleRegStr') || []
    const isCache = Boolean(menu_value('menu_isCache')) || false;

    var num = 0;
    var alreadySendArrTmp = isCache ? (localStorage.getItem("tiebaAlreadySendArr") || "").split(",") : []
    var alreadySendArr = alreadySendArrTmp.slice(alreadySendArrTmp.length - 100 < 0 ? 0 : alreadySendArrTmp.length - 100, alreadySendArrTmp.length - 1)

    var titleReg = []
    try {
        for (let item of titleRegStr) { //titleRegStr.split(";")
            var spl = item.split("::")
            titleReg.push({
                keywords: spl[0].split(";"),
                reply: spl[1]
            })
        }
    } catch (err) {
        alert("关键词规则错误,脚本停止")
        return;
    }
    // console.log(titleReg)

    var hrefStack = []

    var iter;

    var stop = false;

    function stopExec() {
        clearInterval(iter)
        stop = true;
    }

    function contains(arr, obj) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === obj) {
                return true;
            }
        }
        return false;
    }

    function execFind() {
        // console.log("开始执行")
        // console.log("已找到的帖子", alreadySendArr)

        iter = setInterval(function () {
            // console.log("剩余缓冲区帖子 ", hrefStack);
            if (hrefStack.length > 0) {
                let item = hrefStack.shift()
                if (alreadySendArr.length > 500) {
                    alreadySendArr.shift()
                }
                alreadySendArr.push(item.tid)
                if (isCache) {
                    localStorage.setItem("tiebaAlreadySendArr", alreadySendArr)
                }
                window.open("/p/" + item.tid + "?keyword=" + item.keyword + "&reply=" + item.reply)
                // window.open("/p/" + item.tid + "?keyword=" + item.keyword + "&reply=" + item.reply, '临时窗口', 'width=600,height=450', false)
            } else {

            }
        }, openPinlv * 1000);

        //var tomt = setTimeout(()=>{
        //    clearInterval(iter)
        //    // console.log("end")
        //}, 50 * 1000);

        var idx = 0;

        function jiansuo() {
            // console.log("正在检索第" + (idx + 1) + "页")
            var doms = $(".threadlist_title>a");
            doms.each(function () {
                let dom = $(this);
                var hrf = dom.attr("href")
                var title = dom.attr("title")
                var titleMatch = true
                var word = ""
                if (titleReg.length > 0) {
                    titleMatch = false
                    out:for (let item of titleReg) {
                        for (const keyword of item.keywords) {
                            if (new RegExp(keyword).test(title)) {
                                titleMatch = true;
                                word = {keyword: keyword, reply: item.reply}
                                break out;
                            }
                        }
                    }
                    if (hrf && titleMatch) {
                        var tid = hrf.split("/")[2]
                        if (!contains(alreadySendArr, tid)) {
                            // console.log("match " + tid + ":" + title);
                            num++;
                            hrefStack.push({tid: tid, keyword: word.keyword, reply: word.reply});
                        }
                    }
                }
            });
            // 下一页
            $(".next.pagination-item").click()
            // console.log("已找到(无重复)", alreadySendArr.length)
            setTimeout(function () {
                idx = idx + 1
                if (num <= page && !stop) {
                    jiansuo()
                }
            }, 2000)
        }

        jiansuo();

    }

    if ($) {
        $(document).ready(function () {
            let pathname = location.pathname
            if (pathname.startsWith("/p/") && menu_value('menu_send')) {
                execSend();
            } else if (menu_value('menu_search')) {
                execFind();
            }
        })
    }

    function keyDown(e) {
        if (e.which == 27) { //ESC
            e.returnValue = false;
            stopExec()
            return false;
        }
    }

    document.onkeydown = keyDown;

// 脚本设置
    function menu_setting(type, title, tips, line, menu) {
        let _br = '', _html = `<style class="tiebaS_SettingStyle">.tiebaS_SettingRoot {position: absolute;top: 50%;left: 50%;-webkit-transform: translate(-50%, -50%);-moz-transform: translate(-50%, -50%);-ms-transform: translate(-50%, -50%);-o-transform: translate(-50%, -50%);transform: translate(-50%, -50%);width: auto;min-width: 400px;max-width: 600px;height: auto;min-height: 150px;max-height: 400px;color: #535353;background-color: #fff;border-radius: 3px;}
                        .tiebaS_SettingBackdrop_1 {position: fixed;top: 0;right: 0;bottom: 0;left: 0;z-index: 203;display: -webkit-box;display: -ms-flexbox;display: flex;-webkit-box-orient: vertical;-webkit-box-direction: normal;-ms-flex-direction: column;flex-direction: column;-webkit-box-pack: center;-ms-flex-pack: center;justify-content: center;overflow-x: hidden;overflow-y: auto;-webkit-transition: opacity .3s ease-out;transition: opacity .3s ease-out;}
                        .tiebaS_SettingBackdrop_2 {position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 0;background-color: rgba(18,18,18,.65);-webkit-transition: background-color .3s ease-out;transition: background-color .3s ease-out;}
                        .tiebaS_SettingRoot .tiebaS_SettingHeader {padding: 10px 20px;color: #fff;font-weight: bold;background-color: #3994ff;border-radius: 3px 3px 0 0;}
                        .tiebaS_SettingRoot .tiebaS_SettingMain {padding: 10px 20px;border-radius: 0 0 3px 3px;}
                        .tiebaS_SettingHeader span {float: right;cursor: pointer;}
                        .tiebaS_SettingMain input {margin: 10px 6px 10px 0;cursor: pointer;vertical-align:middle}
                        .tiebaS_SettingMain label {margin-right: 20px;user-select: none;cursor: pointer;vertical-align:middle}
                        .tiebaS_SettingMain hr {border: 0.5px solid #f4f4f4;}
                        [data-theme="dark"] .tiebaS_SettingRoot {color: #adbac7;background-color: #343A44;}
                        [data-theme="dark"] .tiebaS_SettingHeader {color: #d0d0d0;background-color: #2D333B;}
                        [data-theme="dark"] .tiebaS_SettingMain hr {border: 0.5px solid #2d333b;}</style>
        <div class="tiebaS_SettingBackdrop_1"><div class="tiebaS_SettingBackdrop_2"></div><div class="tiebaS_SettingRoot">
            <div class="tiebaS_SettingHeader">${title}<span class="tiebaS_SettingClose" title="点击关闭"><svg class="Zi Zi--Close Modal-closeIcon" fill="currentColor" viewBox="0 0 24 24" width="24" height="24"><path d="M13.486 12l5.208-5.207a1.048 1.048 0 0 0-.006-1.483 1.046 1.046 0 0 0-1.482-.005L12 10.514 6.793 5.305a1.048 1.048 0 0 0-1.483.005 1.046 1.046 0 0 0-.005 1.483L10.514 12l-5.208 5.207a1.048 1.048 0 0 0 .006 1.483 1.046 1.046 0 0 0 1.482.005L12 13.486l5.207 5.208a1.048 1.048 0 0 0 1.483-.006 1.046 1.046 0 0 0 .005-1.482L13.486 12z" fill-rule="evenodd"></path></svg></span></div>
            <div class="tiebaS_SettingMain"><p>${tips}</p><hr>`
        if (line) _br = '<br>'
        for (let i = 0; i < menu.length; i++) {
            if (menu_value(menu[i][0])) {
                _html += `<label><input name="tiebaS_Setting" type="checkbox" value="${menu[i][0]}" checked="checked">${menu[i][1]}</label>${_br}`
            } else {
                _html += `<label><input name="tiebaS_Setting" type="checkbox" value="${menu[i][0]}">${menu[i][1]}</label>${_br}`
            }
        }
        _html += `</div></div></div>`
        document.body.insertAdjacentHTML('beforeend', _html); // 插入网页末尾
        setTimeout(function () { // 延迟 100 毫秒,避免太快
            // 关闭按钮 点击事件
            document.querySelector('.tiebaS_SettingClose').onclick = function () {
                this.parentElement.parentElement.parentElement.remove();
                document.querySelector('.tiebaS_SettingStyle').remove();
            }
            // 点击周围空白处 = 点击关闭按钮
            document.querySelector('.tiebaS_SettingBackdrop_2').onclick = function (event) {
                if (event.target == this) {
                    document.querySelector('.tiebaS_SettingClose').click();
                }
            }
            // 复选框 点击事件
            document.getElementsByName('tiebaS_Setting').forEach(function (checkBox) {
                checkBox.addEventListener('click', function () {
                    if (this.checked) {
                        GM_setValue(this.value, true);
                    } else {
                        GM_setValue(this.value, false);
                    }
                });
            })
        }, 100)
    }
})();