Greasy Fork

Greasy Fork is available in English.

妖火复读机

妖火论坛每个回复后面加个复读按钮。

当前为 2025-07-14 提交的版本,查看 最新版本

// ==UserScript==
// @name        妖火复读机
// @namespace   http://yaohuo.me/
// @supportURL  http://zgcwkj.cn
// @version     20250714.01
// @description 妖火论坛每个回复后面加个复读按钮。
// @author      zgcwkj
// @match       *://yaohuo.me/bbs*
// @match       *://www.yaohuo.me/bbs*
// @grant       none
// @license     MIT
// ==/UserScript==

(function () {
  //回复内容组件
  let domTextarea = document.querySelector('textarea')
  //发表回复
  window.reply = function (txt, face) {
    //填写内容
    domTextarea.value += txt.trim()
    //不处理空白内容
    if (domTextarea.value == '') return
    //选择表情
    for (let i = 0; i < document.forms.length; i++) {
      const form = document.forms[i]
      if (form.name == 'f') {
        form.face.value = face
        break
      }
    }
    //点击回复按钮
    let domInput = document.querySelectorAll('input')
    for (let i = domInput.length - 1; i > 0; i--) {
      const btn = domInput[i]
      if (btn.value == '快速回复' || btn.value == '发表回复') {
        btn.click()
        break
      }
    }
  }
  //注入彩色回复按钮
  window.newBtn = function(){
    var domBtn = document.createElement('input')
    domBtn.type = 'submit'
    domBtn.className = 'btn'
    domBtn.value = '彩字回复'
    domBtn.onclick = function(e) {
      e.preventDefault()
      let text = domTextarea.value
      domTextarea.value = ''
      window.reply(generateFontUBB(text))
    }
    let domKSHF = document.querySelector('.kuaisuhuifu')
    domKSHF .childNodes.forEach(item => { item.style = '' })
    domKSHF.insertBefore(domBtn, domKSHF.children[1])
  }
  //注入更多按钮
  let moreButn = [
    { css: 'background:#937a3e;color:#fff', showText: '吃', text: '吃', face: '', },
    { css: 'background:#937a3e;color:#fff', showText: '过', text: '过', face: '', },
    { css: 'background:#a7588d;color:#fffa28', showText: '感谢分享', text: '感谢分享', face: '谢谢.gif' },
    { css: 'background:#3e933e;color:#fff', showText: '哈哈', text: '哈哈', face: '哈哈.gif' },
    { css: 'background:#3e933e;color:#fff', showText: '恭喜', text: '恭喜', face: '么么哒.gif' },
    { css: 'background:#3e933e;color:#fff', showText: '大佬带带', text: '大佬带带', face: '耶耶.gif' },
    { css: 'background:#3e933e;color:#fff', showText: '大水比', text: '大水比', face: '被揍.gif' },
  ]
  let domForm = document.querySelector('.recontent')
  window.moreReplyBtn = function () {
    if (domForm != null) {
      //注入按钮
      let className = 'moreReplyButn'
      let isAddBtn = domForm.querySelector(`.${className}`)
      if (isAddBtn == null) {
        let btnHtml = ''
        let btnStyle = 'padding:5px;border-radius:5px;font-size:14px;'
        moreButn.forEach(f => {
          btnHtml += ` <a href='javascript:;' class='${className}' style='${btnStyle}${f.css}' onclick='window.reply("${generateFontUBB(f.text)}","${f.face}")'>${f.showText}</a>`
        })
        domForm.innerHTML = `<div style='margin:0 10px 7px'>${btnHtml}</div>` + domForm.innerHTML
      }
    }
  }
  //注入复读按钮
  let isNewLayout = document.querySelectorAll('.forum-container').length > 0
  window.repeatBotBtn = function () {
    //获取当前主题版本
    let domTxt = {}
    if (isNewLayout) domTxt = document.querySelectorAll('.forum-post')//新主题
    else domTxt = document.querySelectorAll('.list-reply')//旧主题
    //注入按钮
    let className = 'repeatBotButn'
    domTxt.forEach(f => {
      let domSpan = f.querySelector('.retext')
      let isAddBtn = domSpan.querySelector(`.${className}`)
      if (isAddBtn == null) {
        let txt = generateHtmlUbb(domSpan.innerHTML)
        let btnHtml = ` <a href='javascript:;' class='${className}' onclick='window.reply(\`${txt}\`)'>+1</a>`
        domSpan.innerHTML += btnHtml
      }
    })
  }
  //检查页面
  if (domTextarea != null) {
    //注入彩色回复按钮
    window.newBtn()
    //注入更多按钮
    window.moreReplyBtn()
    //注入复读按钮
    window.repeatBotBtn()
    //元素监视(减少性能损耗)
    let domViewContent = document.querySelector('.viewContent')
    const callback = function (mutationsList, observer) {
      //隐藏提示
      var showTipe = document.querySelector('#retip')
      if (showTipe != null) showTipe.style.display = 'none'
      //注入更多按钮
      window.moreReplyBtn()
      //注入复读按钮
      window.repeatBotBtn()
    }
    const observer = new MutationObserver(callback)
    observer.observe(domViewContent, { childList: true, subtree: true })
  }
  //HTML转UBB
  function generateHtmlUbb(html) {
    //创建一个临时 DOM 元素来解析 HTML
    const tempDiv = document.createElement('div')
    tempDiv.innerHTML = html
    //获取所有的 img 元素并替换为 UBB 格式
    const imgElements = tempDiv.getElementsByTagName('img')
    for (let i = imgElements.length - 1; i >= 0; i--) {
      const img = imgElements[i]
      const ubbImage = `[img]${img.src}[/img]`
      //用 UBB 格式替换 img 元素
      img.replaceWith(ubbImage)
    }
    //获取所有的 br 元素并替换为换行符
    const brElements = tempDiv.getElementsByTagName('br')
    for (let i = brElements.length - 1; i >= 0; i--) {
        const br = brElements[i]
        const newLine = document.createTextNode('\n')
        //用换行符替换 br 元素
        br.replaceWith(newLine)
    }
    //返回处理后的文本
    return tempDiv.innerText || tempDiv.textContent
  }
  //生成彩色文字UBB
  function generateFontUBB(input) {
    //生成随机颜色
    let minDistance = 150
    let startColor, endColor, startRGB, endRGB
    //不断生成直到满足距离要求
    do {
        startColor = getRandomColor()
        endColor = getRandomColor()
        startRGB = hexToRGB(startColor)
        endRGB = hexToRGB(endColor)
    } while (colorDistance(startRGB, endRGB) < minDistance)
    //初始化UBB字符串
    let ubbString = ''
    const charCount = input.length
    //为每个字符生成渐变色
    for (let i = 0; i < charCount; i++) {
      const ratio = charCount > 1 ? i / (charCount - 1) : 0
      const charColor = getGradientColor(startRGB, endRGB, ratio)
      ubbString += `[forecolor=${charColor}]${input[i]}[/forecolor]`
    }
    //返回字符串
    return ubbString
  }
  //获取随机颜色
  function getRandomColor() {
    const letters = '0123456789ABCDEF'
    let color = '#'
    for (let i = 0; i < 6; i++) {
      color += letters[Math.floor(Math.random() * 16)]
    }
    return color
  }
  //将十六进制颜色转换为RGB对象
  function hexToRGB(hex) {
    hex = hex.replace('#', '')
    const r = parseInt(hex.substring(0, 2), 16)
    const g = parseInt(hex.substring(2, 4), 16)
    const b = parseInt(hex.substring(4, 6), 16)
    return { r, g, b }
  }
  //计算两个RGB颜色之间的欧氏距离
  function colorDistance(rgb1, rgb2) {
    return Math.sqrt(
        Math.pow(rgb2[0] - rgb1[0], 2) +
        Math.pow(rgb2[1] - rgb1[1], 2) +
        Math.pow(rgb2[2] - rgb1[2], 2)
    )
  }
  //根据比例生成渐变色
  function getGradientColor(start, end, ratio) {
    const r = Math.round(start.r + ratio * (end.r - start.r))
    const g = Math.round(start.g + ratio * (end.g - start.g))
    const b = Math.round(start.b + ratio * (end.b - start.b))
    return rgbToHex(r, g, b)
  }
  //将RGB值转换为十六进制颜色
  function rgbToHex(r, g, b) {
    return '#' + [r, g, b]
      .map(x => {
        const hex = x.toString(16)
        return hex.length === 1 ? '0' + hex : hex
      })
      .join('')
  }
})()