吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3412|回复: 52
上一主题 下一主题
收起左侧

[其他原创] [油猴脚本] 本论坛帖子筛选工具

[复制链接]
跳转到指定楼层
楼主
wonder2018 发表于 2024-11-18 21:23 回帖奖励
本帖最后由 wonder2018 于 2025-3-18 20:24 编辑

帖子筛选工具

在帖子列表头部添加了几个按钮支持以下功能:

  1. 过滤被标记为已解决的帖子
  2. 过滤回复数较高的帖子
  3. 过滤悬赏 CB 较少的帖子
  4. 还原被过滤的帖子
  5. 使用正则表达式过滤帖子
  6. 支持设置按钮添加位置
  7. 支持设置各功能开启状态
  8. 支持帖子排序和还原排序
  9. 支持对帖子按照创建时间正序排列
  10. 支持对帖子按照创建时间倒序排列
  11. 支持对帖子按照阅读量正序排列
  12. 支持对帖子按照阅读量倒序排列
  13. 支持对帖子按照CB数量正序排列
  14. 支持对帖子按照CB数量倒序排列


安装

  1. 通过 Greasy Fork 安装
  2. 通过 Github 安装
  3. 通过 本贴提供的源代码 安装

更新脚本时,需要在油猴菜单开启新增功能开关才能使用新增功能

鸣谢

  • 夜泉 报告脚本使用问题
  • 三滑稽甲苯 提供正则表达式过滤思路
  • 木头人丶123 提供按钮固定在屏幕右侧的相关代码
  • shenloon 提供将帖子按照创建时间排序的思路
  • FrankWkd 提供将帖子按照阅读量和CB排序的思路

排名不分先后

更新记录

2025-03-18

新增以下功能:

  1. 将帖子根据创建时间正序排列
  2. 将帖子根据阅读量正序排列
  3. 将帖子根据阅读量倒序排列
  4. 将帖子根据CB数量正序排列
  5. 将帖子根据CB数量倒序排列

2024-11-21

新增以下功能:

  1. 将帖子根据创建时间重新排序
  2. 将帖子恢复到默认排序

2024-11-20

新增以下功能:

  1. 使用正则表达式过滤帖子
  2. 支持设置按钮添加位置
  3. 支持设置各功能开启状态

2024-11-19

  1. 新增功能:过滤悬赏 CB 较少的帖子
  2. 修复问题:导读页筛选失效(mod=guide&view=newthread)

2024-11-18

发布以下功能:

  1. 过滤被标记为已解决的帖子
  2. 过滤回复数较高的帖子
  3. 还原被过滤的帖子

源代码

// ==UserScript==
// @name         52pojie主题筛选工具
// @namespace    http://tampermonkey.net/
// @version      0.0.1-b20250318
// @description  52pojie主题页面增强,可以对主题列表进行筛选和排序
// @author       wonder2018
// @license      CC-BY-4.0 license
// @match        https://www.52pojie.cn/forum*
// @icon         http://52pojie.cn/favicon.ico
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==

;(function () {
  'use strict'
  // #region 初始化
  const tableSelector = `div#threadlist table`
  const appendSelector = `ul#thread_types`
  const dftOrderAttrName = 'pj-dft-order'
  const sideBtnWrapId = `filterBox`
  const sideBtnWrapSelector = `ul#${sideBtnWrapId}`
  const allHideTypes = new Set()
  const filterBtnColor = '#66ccff'
  const showBtnColor = 'goldenrod'
  /** @type {HTMLLIElement[]} */
  const btnList = []
  const availableTools = [
    { switchName: '移除已解决', switchKey: 'useFilterBySolved', init: filterBySolved, menuId: null },
    { switchName: '移除回复多于x', switchKey: 'useFilterByReplyGt', init: filterByReplyGt, menuId: null },
    { switchName: '移除CB少于x', switchKey: 'useFilterByCBLt', init: filterByCBLt, menuId: null },
    { switchName: '正则过滤', switchKey: 'useFilterByRegExp', init: filterByRegExp, menuId: null },
    { switchName: '按创建时间正序', switchKey: 'useSortByCreateTimeAsc', init: sortByCreateTimeAsc, menuId: null },
    { switchName: '按创建时间倒序', switchKey: 'useSortByCreateTimeDesc', init: sortByCreateTimeDesc, menuId: null },
    { switchName: '按浏览量正序', switchKey: 'useSortByReadAsc', init: sortByReadAsc, menuId: null },
    { switchName: '按浏览量逆序', switchKey: 'useSortByReadDesc', init: sortByReadDesc, menuId: null },
    { switchName: '按CB正序', switchKey: 'useSortByCBAsc', init: sortByCBAsc, menuId: null },
    { switchName: '按CB逆序', switchKey: 'useSortByCBDesc', init: sortByCBDesc, menuId: null },
  ]
  const dftPJOpts = availableTools.reduce((p, i) => ((p[i.switchKey] = true), p), { showPos: 2 })
  dftPJOpts.useFilterByRegExp = false
  let togglePosMenuId = null
  // #endregion 初始化

  // #region 样式

  // #region 样式 > 基础样式
  GM_addStyle(`
    .pj-btn {
      border-radius: 5px;
      color: #fff;
      cursor: pointer;
    }
    .pj-btn>input.pj-ctl{
      width:2em;
      height:1em;
      pointer-events:all;
      text-align:center;
    }
  `)

  // #endregion 样式 > 基础样式

  // #region 样式 > fixed mode box
  GM_addStyle(`
    ${sideBtnWrapSelector} {
      position: fixed;
      top: 50%;
      right: 0;
      padding: 40px 20px;
      flex-direction: row;
      z-index: 99999;
      display: flex;
      flex-wrap: wrap;
      width: 295px;
      transform: translate(95%, -50%);
      transition: .3s;
      transition-delay: .3s;
    }

    ${sideBtnWrapSelector}:hover{
      transform: translate(0, -50%);
      transition-delay: 0s;
    }

    ${sideBtnWrapSelector}::after{
      content: '<';
      position: absolute;
      display: block;
      width: 30px;
      height: 50px;
      line-height: 50px;
      text-align: center;
      background-color: #66ccff;
      top: 0;
      bottom: 0;
      margin: auto;
      left: -40px;
      margin-left: 20px;
      font-weight: bold;
      color: rgb(0, 102, 255);
      border-radius: 5px;
    }

    ${sideBtnWrapSelector}>li {
      margin: 5px 0;
      flex-grow: 1;
      flex-basis: 40%;
    }
    ${sideBtnWrapSelector}>li>a {
      padding: 10px;
      border-color: rgb(0,102,255);
      border-radius: 5px;
      background-color: white;
      color: rgb(0,102,255);
      margin-right: 10px;
      box-shadow: rgb(207,207,207) 1px 1px 9px 3px;
      text-decoration: none;
      display: block;
      transition: .3s;
    }
    ${sideBtnWrapSelector}>li>a:hover {
      box-shadow: #888 1px 1px 9px 3px;
    }
    ${sideBtnWrapSelector}>li>a>input {
      float:right;
    }
  `)
  // #endregion > fixed mode box

  // #endregion 样式

  // #region 菜单
  const OPTION_KEY = 'PJ_FILTER_OPT'

  const showPosList = ['移除按钮', '列表头部', '窗口右侧']

  /** @type {typeof dftPJOpts} */
  const options = JSON.parse(GM_getValue(OPTION_KEY, JSON.stringify(dftPJOpts)))
  function saveOpt() {
    regMenu()
    GM_setValue(OPTION_KEY, JSON.stringify(options))
  }

  function toggleBtnPos(type = options.showPos) {
    options.showPos = (type || 0) % 3
    saveOpt()
    if (options.showPos === 0) return removeAllBtn()
    if (options.showPos === 1) return showBtnTableHead()
    if (options.showPos === 2) return showBtnFixed()
  }
  /** @param {(typeof availableTools)[number]} type */
  function togglePJToolUse(type) {
    if (type.switchKey === 'useFilterByRegExp') return changeRegExpFilter(type)
    options[type.switchKey] = !options[type.switchKey]
    saveOpt()
    initPJTools()
  }

  /** @param {(typeof availableTools)[number]} type */
  function changeRegExpFilter(type) {
    if (type.switchKey !== 'useFilterByRegExp') return
    let reg = prompt('输入正则表达式(留空点击确定可关闭此功能)', options[type.switchKey] || '')
    // 点击取消时不做任何操作
    if (typeof reg !== 'string') return
    reg = reg.trim()
    const regObj = strToRegExp(reg.trim())
    if (!(regObj instanceof RegExp) && regObj != null) return alert(`输入的正则表达式有误请检查:${reg}`)
    options[type.switchKey] = !!regObj && `/${regObj.source}/${regObj.flags}`
    saveOpt()
    initPJTools()
  }

  function initPJTools() {
    loadBtnByOpts()
    toggleBtnPos(options.showPos)
  }

  function regToggleBtnPosMenu() {
    if (togglePosMenuId != null) GM_unregisterMenuCommand(togglePosMenuId)
    togglePosMenuId = GM_registerMenuCommand(`切换按钮位置:[${showPosList[options.showPos]}]`, () => toggleBtnPos(options.showPos + 1))
  }

  /** @param {(typeof availableTools)[number]} type */
  function regToolsMenu(type) {
    if (type.switchKey === 'useFilterByRegExp') return regRegExpFilterMenu(type)
    if (type.menuId != null) GM_unregisterMenuCommand(type.menuId)
    type.menuId = GM_registerMenuCommand(`${type.switchName}[${options[type.switchKey] ? '开' : '关'}]`, () => togglePJToolUse(type))
  }

  /** @param {(typeof availableTools)[number]} type */
  function regRegExpFilterMenu(type) {
    if (type.switchKey !== 'useFilterByRegExp') return null
    if (type.menuId != null) GM_unregisterMenuCommand(type.menuId)
    type.menuId = GM_registerMenuCommand(`${type.switchName}[${options[type.switchKey] || '关'}]`, () => togglePJToolUse(type))
  }

  function regMenu() {
    regToggleBtnPosMenu()
    availableTools.forEach(i => regToolsMenu(i))
  }
  // #endregion 菜单

  // #region utils
  function createBtn(text, color) {
    const btn = document.createElement('li')
    btn.classList.add('pj-btn-wrap')
    btn.innerHTML = `<a class="pj-btn" style="background-color:${color};">${text}</a>`
    btnList.push(btn)
    return btn
  }

  /** 处理还原隐藏帖子事件 */
  function recoverByType(type) {
    /** @type {HTMLTableSectionElement[]} */
    const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_][${type}]`)]
    lines.forEach(i => {
      i.setAttribute(type, false)
      if ([...allHideTypes].every(t => !i.getAttribute(t) || i.getAttribute(t) == 'false')) {
        i.style.display = ''
      }
    })
  }

  /** 根据配置项初始化所有按钮 */
  function loadBtnByOpts() {
    // 移除已添加的按钮,和附带元素(比如fixed模式下的按钮容器)
    removeAllBtn()
    // 确保所有按钮都被移除再清空数组
    btnList.forEach(i => i.remove())
    btnList.length = 0
    availableTools.filter(i => options[i.switchKey]).forEach(i => i.init())
  }

  /** 按钮显示到列表头部 */
  function removeAllBtn() {
    btnList.forEach(i => i.remove())
    const sideBtnWrap = document.querySelector(sideBtnWrapSelector)
    sideBtnWrap && sideBtnWrap.remove()
  }

  /** 按钮显示到列表头部 */
  function showBtnTableHead() {
    const appendTarget = document.querySelector(appendSelector)
    if (!appendTarget) return console.warn('不是论坛列表页,不添加按钮。')
    btnList.forEach(i => appendTarget.appendChild(i))
    const sideBtnWrap = document.querySelector(sideBtnWrapSelector)
    sideBtnWrap && sideBtnWrap.remove()
  }

  /** 按钮固定在窗口右侧 */
  function showBtnFixed() {
    if (!btnList.length) return
    const filterButtonBox = document.createElement('ul')
    filterButtonBox.id = sideBtnWrapId
    btnList.forEach(i => filterButtonBox.appendChild(i))
    document.body.appendChild(filterButtonBox)
  }

  /** 字符串转正则表达式 */
  function strToRegExp(str) {
    if (!str || typeof str !== 'string') return null
    try {
      str = str.trim()
      const part = str.match(/^\/(.+?)\/(\w*)$/)
      /** @type {RegExp} */
      let regObj = null
      if (part) {
        regObj = new RegExp(part[1], part[2])
      } else if (str) {
        regObj = new RegExp(str, 'i')
      }
      return regObj
    } catch (error) {
      return new Error('错误的正则表达式!')
    }
  }

  /** 论坛目前默认排序方式是按照最新回复排序,为了兼容后续可能的改动,这里还是先保存默认排序,之后再按此顺序恢复 */
  /** 保存原始顺序 @param {HTMLTableSectionElement[]} lines */
  function setDftOrder(lines) {
    let maxOrder = Math.max(-Infinity, 1, ...lines.map(i => i.getAttribute(dftOrderAttrName) || 0))
    // 保存原始顺序
    lines.forEach(i => {
      // 已经记录了默认顺序的帖子跳过
      if (i.hasAttribute(dftOrderAttrName)) return
      i.setAttribute(dftOrderAttrName, maxOrder++)
    })
  }
  /** 还原帖子排序 */
  function recoverOrder() {
    /** @type {HTMLTableSectionElement[]} */
    const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
    // 为新增的没有参与排序的行添加顺序以便排序
    setDftOrder(lines)
    const sorted = lines.sort((a, b) => parseInt(a.getAttribute(dftOrderAttrName)) - parseInt(b.getAttribute(dftOrderAttrName)))
    sortLines(sorted)
  }
  /** 按照顺序排列帖子 @param {HTMLTableSectionElement[]} lines */
  function sortLines(lines) {
    if (!Array.isArray(lines) || !lines.length) return
    const container = lines[0].parentNode
    if (!container) return console.warn('列表没有祖先节点,不进行操作!')

    container.append(...lines)
  }

  /** 获取创建时间 @param {HTMLTableSectionElement} line */
  function readCreateTime(line) {
    let text = line.querySelector('.by')
    if (!text || typeof text.innerText != 'string' || !text.innerText.trim()) return 0
    let part = text.innerText
      .replaceAll('\n', '')
      .trim()
      .match(/^.*?(\d{4})-(\d+)-(\d+)\s*(\d+):(\d+)$/)
    // 获取时间失败,排在最后面
    if (!part) return 0
    // 将 part 转为标准数组,并且把获取到的值转为数字
    part = new Array(5).fill(0).map((_, i) => parseInt(part[i + 1]))
    // 获取date实例时月份要减1
    part[1] = part[1] - 1
    // 对 Invalid Date 执行 getTime 会得到 NaN
    return new Date(...part).getTime() || 0
  }

  /** 获取阅读量 @param {HTMLTableSectionElement} line */
  function readReadCount(line) {
    let text = line.querySelector('.num>em')
    if (!text || typeof text.innerText != 'string' || !text.innerText.trim()) return 0
    return text.innerText * 1 || 0
  }

  /** 获取CB @param {HTMLTableSectionElement} line */
  function readCB(line) {
    return parseInt(line.innerText.replaceAll('\n', '').replace(/.*悬赏\s*(\d+)\s*CB\s*吾爱币.*/, '$1'))
  }

  // #endregion utils

  // #region 脚本功能

  /** 移除已解决 */
  function filterBySolved() {
    const hideType = 'hide-by-solved'
    allHideTypes.add(hideType)
    const filterNonSolvedBtn = createBtn('移除已解决', filterBtnColor)
    filterNonSolvedBtn.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      lines.forEach(i => {
        if (!(i.innerText || '').includes('已解决')) return
        i.setAttribute(hideType, true)
        i.style.display = 'none'
      })
    })
    createBtn('还原已解决', showBtnColor).addEventListener('click', () => recoverByType(hideType))
  }
  /** 移除回复多于x */
  function filterByReplyGt() {
    const hideType = 'hide-by-reply-gt'
    allHideTypes.add(hideType)
    const filterByReply = createBtn(`移除回复多于 <input class="pj-ctl"/>`, filterBtnColor)
    const replyCount = filterByReply.querySelector('input')
    replyCount.addEventListener('click', e => e.stopPropagation())
    replyCount.value = 0
    filterByReply.addEventListener('click', () => {
      const maxCount = replyCount.value
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      lines.forEach(i => {
        const countEl = i.querySelector('tr>td.num>.xi2')
        if (parseInt(countEl.innerText) <= maxCount) return
        i.setAttribute(hideType, true)
        i.style.display = 'none'
      })
    })
    createBtn('还原回复多于x', showBtnColor).addEventListener('click', () => recoverByType(hideType))
  }
  /** 移除CB少于x */
  function filterByCBLt() {
    const hideType = 'hide-by-cb-lt'
    allHideTypes.add(hideType)
    const filterByCB = createBtn(`移除CB少于 <input class="pj-ctl"/>`, filterBtnColor)
    const CBCount = filterByCB.querySelector('input')
    CBCount.addEventListener('click', e => e.stopPropagation())
    CBCount.value = 0
    filterByCB.addEventListener('click', () => {
      const minCount = CBCount.value
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      lines.forEach(i => {
        const cb = readCB(i)
        if (cb >= minCount) return
        i.setAttribute(hideType, true)
        i.style.display = 'none'
      })
    })
    createBtn('还原CB少于x', showBtnColor).addEventListener('click', () => recoverByType(hideType))
  }

  /** 正则过滤 */
  function filterByRegExp() {
    const hideType = 'hide-by-reg-exp'
    allHideTypes.add(hideType)
    const filterByRegExp = createBtn(`正则过滤`, filterBtnColor)
    filterByRegExp.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      const regObj = strToRegExp(options.useFilterByRegExp)
      if (!(regObj instanceof RegExp)) return
      lines.forEach(i => {
        if (!regObj.test(i.innerText.replaceAll('\n', ''))) return
        i.setAttribute(hideType, true)
        i.style.display = 'none'
      })
    })
    createBtn('还原正则过滤', showBtnColor).addEventListener('click', () => recoverByType(hideType))
  }

  /** 按创建时间倒序 */
  function sortByCreateTimeDesc() {
    const sortByCreateTimeDescEl = createBtn(`按创建时间倒序`, filterBtnColor)
    sortByCreateTimeDescEl.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      setDftOrder(lines)
      const sorted = lines.sort((a, b) => readCreateTime(b) - readCreateTime(a))
      sortLines(sorted)
    })
    createBtn('还原排序', showBtnColor).addEventListener('click', recoverOrder)
  }

  /** 按创建时间正序 */
  function sortByCreateTimeAsc() {
    const sortByCreateTimeAscEl = createBtn(`按创建时间正序`, filterBtnColor)
    sortByCreateTimeAscEl.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      setDftOrder(lines)
      const sorted = lines.sort((a, b) => readCreateTime(a) - readCreateTime(b))
      sortLines(sorted)
    })
    createBtn('还原排序', showBtnColor).addEventListener('click', recoverOrder)
  }

  /** 按阅读量正序 */
  function sortByReadAsc() {
    const sortByReadAscEl = createBtn(`按阅读量正序`, filterBtnColor)
    sortByReadAscEl.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      setDftOrder(lines)
      const sorted = lines.sort((a, b) => readReadCount(a) - readReadCount(b))
      sortLines(sorted)
    })
    createBtn('还原排序', showBtnColor).addEventListener('click', recoverOrder)
  }

  /** 按阅读量倒序 */
  function sortByReadDesc() {
    const sortByReadDescEl = createBtn(`按阅读量倒序`, filterBtnColor)
    sortByReadDescEl.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      setDftOrder(lines)
      const sorted = lines.sort((a, b) => readReadCount(b) - readReadCount(a))
      sortLines(sorted)
    })
    createBtn('还原排序', showBtnColor).addEventListener('click', recoverOrder)
  }

  /** 按CB正序 */
  function sortByCBAsc() {
    const sortByCBAscEl = createBtn(`按CB正序`, filterBtnColor)
    sortByCBAscEl.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      setDftOrder(lines)
      const sorted = lines.sort((a, b) => readCB(a) - readCB(b))
      sortLines(sorted)
    })
    createBtn('还原排序', showBtnColor).addEventListener('click', recoverOrder)
  }

  /** 按CB倒序 */
  function sortByCBDesc() {
    const sortByCBDescEl = createBtn(`按CB倒序`, filterBtnColor)
    sortByCBDescEl.addEventListener('click', () => {
      /** @type {HTMLTableSectionElement[]} */
      const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)]
      setDftOrder(lines)
      const sorted = lines.sort((a, b) => readCB(b) - readCB(a))
      sortLines(sorted)
    })
    createBtn('还原排序', showBtnColor).addEventListener('click', recoverOrder)
  }

  // #endregion 脚本功能

  window.addEventListener('load', () => {
    regMenu()
    const appendTarget = document.querySelector(appendSelector)
    if (!appendTarget) return console.warn('不是论坛列表页,不添加按钮。')
    initPJTools()
  })
})()

免费评分

参与人数 7吾爱币 +10 热心值 +7 收起 理由
qmsy273 + 1 热心回复!
supersup + 1 谢谢@Thanks!
hsmx77 + 1 + 1 谢谢@Thanks!
木头人丶123 + 1 谢谢@Thanks!学习了
chengdragon + 1 + 1 感谢分享
0jiao0 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
夜泉 发表于 2024-11-18 23:50

免费评分

参与人数 2吾爱币 +1 热心值 +1 收起 理由
wonder2018 + 1 热心回复!
2300zjh + 1 热心回复!

查看全部评分

推荐
三滑稽甲苯 发表于 2024-11-19 08:03
哈哈,之前写了个正则表达式过滤帖子的小工具,感觉可以和这个一起用

免费评分

参与人数 1热心值 +1 收起 理由
wonder2018 + 1 感谢您的宝贵建议,我们会努力争取做得更好!

查看全部评分

推荐
木头人丶123 发表于 2024-11-20 00:46
本帖最后由 木头人丶123 于 2024-11-20 00:49 编辑

感谢楼主提供的工具
在楼主的基础上优化了一点样式,增加了按钮浮动显示方式
使用方式:修改filterBtnType=2,默认为1原有显示

[JavaScript] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// ==UserScript==
// @name         52pojie主题筛选
// @namespace    [url]http://tampermonkey.net/[/url]
// @version      2024-11-18
// @description  try to take over the world!
// @AuThor       You
// @match        [url]https://www.52pojie.cn/forum[/url]*
// @Icon         [url]https://www.google.com/s2/favicons?sz=64&domain=52pojie.cn[/url]
// @grant        none
// ==/UserScript==
 
(function () {
    "use strict";
    const tableSelector = `div#threadlist table`;
    const appendSelector = `ul#thread_types`;
    const allHideTypes = ["hide-by-solved", "hide-by-reply", "hide-by-lt-cb"];
    const btnList = [];
    //filterBtnType 1 按钮显示到顶部,2 按钮浮动显示
    const filterBtnType = 1;
 
    function createBtn(text, color) {
        const btn = document.createElement("li");
        btn.innerHTML = `<a style="background-color:${color};border-radius:5px;color:#fff;cursor:pointer;">${text}</a>`;
        btnList.push(btn);
        return btn;
    }
 
    function recoverByType(type) {
        /** @type {HTMLTableSectionElement[]} */
        const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_][${type}]`)];
        lines.forEach((i) => {
            i.setAttribute(type, false);
            if (allHideTypes.every((t) => !i.getAttribute(t) || i.getAttribute(t) == "false")) {
                i.style.display = "";
            }
        });
    }
 
    //按钮显示到顶部
    function showBtnTop(btnList) {
        btnList.forEach((i) => document.querySelector(appendSelector).appendChild(i));
    }
 
    //按钮浮动显示
    function showBtnFloat(btnList) {
        const domHead = document.getElementsByTagName('head')[0];
        const domStyle = document.createElement('style');
        domStyle.type = 'text/css';
        domStyle.rel = 'stylesheet';
        const filterStyle = `
        #filterBox {
            position: fixed;
            top: 50%;
            transform: translateY(-50%);
            right: 20px;
            gap: 20px;
            flex-direction: column;
            z-index: 99999;
            display: flex;
        }
        #filterBox>li {
            margin-top: 5px;
        }
        #filterBox>li>a {
            padding: 10px;
            border-color: rgb(0,102,255);
            border-radius: 5px;
            background-color: white;
            color: rgb(0,102,255);
            margin-right: 10px;
            box-shadow: rgb(207,207,207) 1px 1px 9px 3px;
            text-decoration: none;
        }
        `;
        domStyle.appendChild(document.createTextNode(filterStyle));
        domHead.appendChild(domStyle);
        const filterButtonBox = document.createElement("ul");
        filterButtonBox.id = "filterBox";
        btnList.forEach((i) => {
            filterButtonBox.appendChild(i);
        });
        document.body.appendChild(filterButtonBox);
    }
 
    window.addEventListener("load", () => {
        // #region 移除已解决
        {
            const filterNonSolvedBtn = createBtn("移除已解决", "#66ccff");
            filterNonSolvedBtn.addEventListener("click", () => {
                /** @type {HTMLTableSectionElement[]} */
                const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)];
                lines.forEach((i) => {
                    if (!(i.innerText || "").includes("已解决")) return;
                    i.setAttribute("hide-by-solved", true);
                    i.style.display = "none";
                });
            });
        }
        // #endregion 移除已解决
 
        // #region 还原已解决
        {
            const showNonSolvedBtn = createBtn("还原已解决", "Green");
            showNonSolvedBtn.addEventListener("click", () => recoverByType("hide-by-solved"));
        }
        // #endregion 还原已解决
 
        // #region 移除回复多余x
        {
            const filterByReply = createBtn(`移除回复多于 <input style="width:2em;height:1em;pointer-events:all;text-align:center"/>`, "#66ccff");
            const replyCount = filterByReply.querySelector("input");
            replyCount.addEventListener("click", (e) => e.stopPropagation());
            replyCount.value = 0;
            filterByReply.addEventListener("click", () => {
                const maxCount = replyCount.value;
                /** @type {HTMLTableSectionElement[]} */
                const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)];
                lines.forEach((i) => {
                    const countEl = i.querySelector("tr>td.num>.xi2");
                    if (parseInt(countEl.innerText) <= maxCount) return;
                    i.setAttribute("hide-by-reply", true);
                    i.style.display = "none";
                });
            });
        }
        // #endregion 移除回复多余
 
        // #region 还原回复多余x
        {
            const showByReply = createBtn("还原回复多于x", "Green");
            showByReply.addEventListener("click", () => recoverByType("hide-by-reply"));
        }
        // #endregion 还原已解决
 
        // #region 移除CB少于x
        {
            const filterByCB = createBtn(`移除CB少于 <input style="width:2em;height:1em;pointer-events:all;text-align:center"/>`, "#66ccff");
            const CBCount = filterByCB.querySelector("input");
            CBCount.addEventListener("click", (e) => e.stopPropagation());
            CBCount.value = 0;
            filterByCB.addEventListener("click", () => {
                const minCount = CBCount.value;
                /** @type {HTMLTableSectionElement[]} */
                const lines = [...document.querySelectorAll(`${tableSelector}>tbody[id*=normalthread_]`)];
                lines.forEach((i) => {
                    const cb = parseInt(i.innerText.replaceAll("\n", "").replace(/.*悬赏\s*(\d+)\s*CB\s*吾爱币.*/, "$1"));
                    if (cb >= minCount) return;
                    i.setAttribute("hide-by-lt-cb", true);
                    i.style.display = "none";
                });
            });
        }
        // #endregion 移除CB少于x
 
        // #region 还原CB少于x
        {
            const showByReply = createBtn("还原CB少于x", "Green");
            showByReply.addEventListener("click", () => recoverByType("hide-by-lt-cb"));
        }
        // #endregion 还原CB少于x
 
        if (filterBtnType === 1) {
            showBtnTop(btnList);
        } else if (filterBtnType === 2) {
            showBtnFloat(btnList);
        }
    });
})();

免费评分

参与人数 1吾爱币 +1 收起 理由
wonder2018 + 1 用心讨论,共获提升!

查看全部评分

推荐
 楼主| wonder2018 发表于 2024-11-20 22:29 |楼主
木头人丶123 发表于 2024-11-20 00:46
感谢楼主提供的工具
在楼主的基础上优化了一点样式,增加了按钮浮动显示方式
使用方式:修改fil ...

赞!你的改动已经合并了!
推荐
 楼主| wonder2018 发表于 2024-11-20 14:29 |楼主
c293943 发表于 2024-11-20 14:04
用脚本会不会被关小黑屋呀?

应该不会,我是基于以下几点确定的:

  1. 脚本只是本地运行,隐藏帖子是通过在对应帖子行上面添加隐藏属性实现。
  2. 运行时不会向服务器发送任何请求,也不会增加页面发送的请求。
  3. 不会破坏论坛原有功能,不会因此对论坛服务器造成更大的压力。

PS:如果你因为筛选过后剩下的帖子很少而疯狂点击下一页按钮,这种情况确实可能会对服务器造成更高的压力,但这不是因为运行此脚本造成的,而是你自己点击的。

推荐
shenloon 发表于 2024-11-21 13:07
感谢楼主,请问有没有默认按发帖时间排序的脚本?

免费评分

参与人数 1吾爱币 +1 收起 理由
wonder2018 + 1 感谢您的宝贵建议,我们会努力争取做得更好!

查看全部评分

推荐
 楼主| wonder2018 发表于 2024-11-21 23:44 |楼主
shenloon 发表于 2024-11-21 13:07
感谢楼主,请问有没有默认按发帖时间排序的脚本?

赞!此功能已添加!
推荐
wxd007 发表于 2024-11-19 08:08
学习学习,很厉害
推荐
yiluoen0502 发表于 2024-11-19 08:24
感谢分享&#128077;
推荐
FrankWkd 发表于 2025-3-15 15:56
Thanks!
建议增加一个根据悬赏CB数额排序/根据阅读数量排序的功能

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
wonder2018 + 1 + 1 热心回复!

查看全部评分

6#
zjtzjt 发表于 2024-11-19 06:52
感谢分享,筛选很方便
7#
sdieedu 发表于 2024-11-19 07:13
厉害厉害
8#
LCG999 发表于 2024-11-19 07:45
方便了很多,感谢分享
9#
sammmnmnmnm 发表于 2024-11-19 08:17
感谢分享 学习一下
10#
letum 发表于 2024-11-19 08:37
以前在悬赏区逛的时候,还得看看标题后面是不是带着已解决,见到已解决的就不点进去看了,现在有了这个油猴脚本就省事多了。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-5-20 09:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表