Greasy Fork

Greasy Fork is available in English.

妖火网增强脚本Plus

让妖火再次变得伟大(手动狗头.jpg)

目前为 2024-08-23 提交的版本,查看 最新版本

// ==UserScript==
// @name            妖火网增强脚本Plus
// @namespace       https://www.yaohuo.me/
// @version         0.0.5
// @description     让妖火再次变得伟大(手动狗头.jpg)
// @author          柠檬没有汁@27894
// @match           *://yaohuo.me/*
// @match           *://*.yaohuo.me/*
// @icon            https://yaohuo.me/css/favicon.ico
// @run-at          document-body
// @license         MIT
// @grant           none
// @noframes
// @homepage        https://www.yaohuo.me/bbs/userinfo.aspx?touserid=27894
// @supportURL      https://www.yaohuo.me/bbs/userinfo.aspx?touserid=27894
// @require         https://code.jquery.com/jquery-3.7.1.min.js
// ==/UserScript==

(async function () {
  "use strict";
  if (!checkLocation()) return;
  if (!checkJQueryLoad()) return;

  await initSetting();
  const customSetting = JSON.parse(localStorage.getItem("yaohuoBetterPlusSetting"));
  console.log("yaohuoBetterPlus 用户设置:", customSetting);

  // 确保在页面加载完成后再执行代码,否则jquery可能会获取不到 document 内容
  $(document).ready(() => {
    createScriptSetting();
    customSetting["showTopAndDownBtn"] && addTopAndDown();
    customSetting["showChuiniuHistory"] && executeFunctionForURL("/games/chuiniu/doit.aspx", chuiniuHistory);
    customSetting["oneClickCollectMoney"] && executeFunctionForURL(/^\/bbs-.*\.html$/, speedEatMoney, true);
    // bookViewAddUbb();
  });
})();

// 发帖UBB增强
function bookViewAddUbb() {
  const bookViewPageList = [
    "/bbs/book_view_add.aspx",
    "/bbs/book_view_sendmoney.aspx",
    "/bbs/book_view_addvote.aspx",
    "/bbs/book_view_addfile.aspx",
    "/bbs/book_view_mod.aspx",
    "/bbs/book_view_addURL.aspx",
  ];
  console.log(window.location.pathname);
  // if (bookViewPageList.includes(window.location.pathname))
  // let targetEle = $('.book_view_add_height');
  // console.log(targetEle);
  // let newEle = $('<div class="btBox"><div class="bt2">123</div></div>');
  // // 在目标元素后面追加新元素
  // targetEle.append(newEle);
}
// 一键吃肉
function speedEatMoney() {
  $(".kuaisuhuifu a").remove(); // 移除“文件回帖”按钮

  // 生成“一键吃肉”按钮并插入到指定位置
  const vBtn = $("<span>一键吃肉</span>");
  vBtn.css({
    // "float": "right",
    "background-image": "linear-gradient(#f4f4f4,#ececec)",
    "margin": "0 5px 7px",
    "padding": ".5em 1em",
    "border": "1px solid #d4d4d4",
    "color": "deeppink",
    "border-radius": "5px",
    "font": "11px/normal sans-serif",
    "transform": "translateY(-5%)",
  });
  vBtn.click(() => {
    const isPaibi = $("div").hasClass("paibi");
    if (isPaibi) {
      const shengyuNum = $(".paibi .yushuzi").text();
      if (shengyuNum > 0) {
        const eatWordsArr = [
          "吃",
          "吃吃",
          "吃吃.",
          "吃吃。",
          "吃吃..",
          "吃吃。。",
          "吃了",
          "吃肉",
          "来吃肉",
          "吃.",
          "吃。",
          "吃了.",
          "吃了。",
          "吃肉.",
          "吃肉。",
          "来吃肉.",
          "来吃肉。",
          "吃..",
          "吃。。",
          "吃了..",
          "吃了。。",
          "吃肉..",
          "先吃肉",
        ];
        const index = Math.floor(Math.random() * eatWordsArr.length);
        insetCustomContent(eatWordsArr[index], ".retextarea");
      } else {
        notifyBox("已经没有肉了,错过了一个亿~", false);
      }
    } else {
      notifyBox("不是派币贴,你吃个 der", false);
    }
  });
  $(".kuaisuhuifu").append(vBtn);
}
// 查询吹牛发布者历史大话选项
async function chuiniuHistory() {
  const elementsWithText = $("body").find(":contains('自己挑战的只能由其它友友应战!')");
  if (elementsWithText.length > 0) return;

  // 创建胜率结果显示容器,写入提示信息
  $(
    `<p id="chuiniuWinningEle" style="background:#f0f9eb;color:gray;text-align:center;margin:0 auto;padding:0 20px;">历史记录获取中...</p>`
  ).insertBefore($(".subtitle"));

  const userinfoEle = $('a[href*="userinfo.aspx"]');
  const chuiniuQueUserId = getUrlParam("touserid", userinfoEle.attr("href")); // 发布者ID
  const chuiniuQueUserNickname = userinfoEle.text(); // 发布者昵称
  const queHistoryArr = Array.from(await getQueUserHistoryArr(chuiniuQueUserId)); // 发布者历史大话ID

  if (queHistoryArr.length > 0) {
    //  获取成功
    const queHistoryAnswers = await Promise.all(queHistoryArr.map(getChuiniuAnswer));
    const countAnswer1 = queHistoryAnswers.filter((v) => v === "1").length;
    const countAnswer2 = queHistoryAnswers.filter((v) => v === "2").length;
    $("#chuiniuWinningEle")
      .css({
        "text-align": "left",
      })
      .html(
        `“<span style="color:#3d68a8;">${chuiniuQueUserNickname}</span>”最近<span style="color:blue;font-weight:bold;">${queHistoryAnswers.length}</span>次已完成大话选项:答案<span style="color:blue;font-weight:bold;">1</span>次数:<span style="color:red;font-weight:bold;">${countAnswer1}</span>,答案<span style="color:blue;font-weight:bold;">2</span>次数: <span style="color:red;font-weight:bold;">${countAnswer2}</span>`
      );
  } else {
    // 获取失败
    $("#chuiniuWinningEle").html(`<span style="color:red;">未知错误,获取历史数据失败,请私信反馈</span>`);
  }

  // 获取指定大话答案
  async function getChuiniuAnswer(chuiniuId) {
    const chuiniuRes = await getPageContent(`/games/chuiniu/book_view.aspx?id=${chuiniuId}`);
    const ansRule1 = chuiniuRes.match(/挑战方出的是\[答案1\]/);
    const ansRule2 = chuiniuRes.match(/挑战方出的是\[答案2\]/);
    if (ansRule1) return "1";
    if (ansRule2) return "2";
  }
  // 获取对方大话指定条数历史记录ID
  async function getQueUserHistoryArr(toUserId, computeTotal = 15) {
    return new Promise(async (resolve, reject) => {
      const idArr = new Set(); // 存放大话ID,利用Set特性去重(翻页时会有重复项出现,非本脚本bug)
      let historyPage = 1; // 翻页,达到预设值时停止

      const historyText = await getPageContent(`/games/chuiniu/book_list.aspx?type=0&touserid=${toUserId}`);
      const userHistoryTotal = historyText.slice(historyText.indexOf("页,共 ") + 4, historyText.indexOf(" 条")); // 吹牛历史总条数

      const getQueUserHistoryid = async () => {
        const tempElements = $(historyText).filter(".line1, .line2");
        for (const line of tempElements) {
          const idLink = line.querySelector('a[href*="book_view.aspx"]');
          if (idLink && !line.textContent.includes("进行中")) {
            const dahuaId = getUrlParam("id", idLink.href);
            if (dahuaId) idArr.add(dahuaId);
            if (idArr.length >= computeTotal) break;
          }
        }
        if (idArr.length < 15 && userHistoryTotal > 15) {
          historyPage++;
          getQueUserHistoryid();
        } else {
          resolve(idArr);
        }
      };
      getQueUserHistoryid();
    });
  }
}

// 一键回到顶部/底部,在原作者基础上做了删减、改动,原作者发布地址:http://greasyfork.icu/zh-CN/scripts/38899-回到顶部-底部
function addTopAndDown() {
  if (window.self != window.top) return;
  function ce(n) {
    return document.createElement(n);
  }

  function addStyle(css) {
    let head = document.head || document.getElementsByTagName("head")[0];
    if (head) {
      let style = ce("style");
      style.type = "text/css";
      style.appendChild(document.createTextNode(css));
      head.appendChild(style);
    }
  }

  let el =
      navigator.userAgent.indexOf("Firefox") != -1 || navigator.userAgent.indexOf("MSIE") != -1
        ? document.documentElement
        : document.body,
    t1,
    t2,
    speed_by_click = 200,
    zIindex = 1001;

  function getDocumentHeight() {
    return document.body.scrollHeight > document.body.offsetHeight
      ? document.body.scrollHeight
      : document.body.offsetHeight;
  }
  function get_scroll(a) {
    let d = document,
      b = d.body,
      e = d.documentElement,
      c = "client" + a,
      f = "scroll" + a;
    return /CSS/.test(d.compatMode) ? e[c] < e[f] : b[c] < b[f];
  }
  function scrollTo(element, to, duration) {
    (start = document.documentElement.scrollTop || document.body.scrollTop),
      (change = to - start),
      (currentTime = 0),
      (increment = 20),
      (newDuration = typeof duration === "undefined" ? 500 : duration);

    let animateScroll = function () {
      currentTime += increment;
      let val = Math.easeInOutQuad(currentTime, start, change, newDuration);
      window.scrollTo(0, val);
      if (currentTime < newDuration) {
        setTimeout(animateScroll, increment);
      }
    };
    animateScroll();
  }

  Math.easeInOutQuad = function (t, b, c, d) {
    t /= d / 2;
    if (t < 1) return (c / 2) * t * t + b;
    t--;
    return (-c / 2) * (t * (t - 2) - 1) + b;
  };

  function shareCSS() {
    let s = "",
      img_up,
      img_dn;

    img_up =
      "data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAYAAACAl21KAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAB+SURBVDhPY1i1atV/amAGahgCMoNhaIGlS5cKAp19BoRBbLJcj2QILDJINwzoAmMgfoclIkBixkS5DI8hMJcRNgxoSBoOl6CnNZBhaVhdBjWE1MSJahjQkA4KEmYH2GUrV66cSYEhYB+AzKBtFiHkQqKiH6Ro1CDCQTWgYQQAs81DU0G/83sAAAAASUVORK5CYII=";
    img_dn =
      "data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAYAAACAl21KAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACPSURBVDhPY2DAAlatWvUfH8amB6vYqEGEg2pgw4iQ7cTKM6xcuXImsYpxqQOZAQ4woIIOCgzrQAl1oEFpZBiWhitFgwx7R4SBIDXYDYGZDFRgTMAwkCHGhBMRJMxwGUa8ITCbli5dKgg08AySN8+AxIhyCboiJMPIN4Qsm6miiYioxltawvSDYogohYTUAQC80UNTOht/YwAAAABJRU5ErkJggg==";
    s +=
      "#play_btn_up { position:fixed; right:0; top:55%;z-index:" +
      zIindex +
      "; height:40px; width:36px; cursor:pointer; background:url(" +
      img_up +
      ") no-repeat scroll 50% 50% rgba(0, 0, 0, 0.7); border-radius:5px 0 0 5px; margin-top:-24px; }";
    s +=
      "#play_btn_dn { position:fixed; right:0; top:60%;   z-index:" +
      zIindex +
      "; height:40px; width:36px; cursor:pointer; background:url(" +
      img_dn +
      ") no-repeat scroll 50% 50% rgba(0, 0, 0, 0.7); border-radius:5px 0 0 5px; margin-top:-24px; }";

    s +=
      ".play_btn { -webkit-transition-duration:0.5s linear; -o-transition-duration:0.5s linear; -moz-transition-duration:0.5s linear; transition-duration:0.5s linear; opacity:0.65; }";
    s += ".play_btn:hover { opacity:1; }";

    addStyle("" + s);
  }

  function create_btn_element() {
    let up,
      dn,
      scrolled,
      h = get_scroll("Height");
    if (!h) {
      return;
    }
    shareCSS();

    if (el) {
      up = ce("span");
      dn = ce("span");
      up.setAttribute("id", "play_btn_up");
      dn.setAttribute("id", "play_btn_dn");
      up.className = "play_btn";
      dn.className = "play_btn";
      document.body.appendChild(up);
      document.body.appendChild(dn);

      scrolled = window.pageYOffset || document.documentElement.scrollTop;
      up.style.display = scrolled > 0 ? "" : "none";

      up.addEventListener(
        "mouseout",
        function () {
          clearTimeout(t1);
        },
        false
      );
      dn.addEventListener(
        "mouseout",
        function () {
          clearTimeout(t2);
        },
        false
      );
      up.addEventListener(
        "click",
        function () {
          scrollTo(el, 0, speed_by_click);
        },
        false
      );
      dn.addEventListener(
        "click",
        function () {
          scrollTo(el, getDocumentHeight(), speed_by_click);
        },
        false
      );

      window.onscroll = function () {
        let scrolled = document.documentElement.scrollTop,
          diffHeight = document.body.scrollHeight - window.innerHeight;
        up.style.display = scrolled > 0 ? "" : "none";
        dn.style.display = diffHeight > scrolled ? "" : "none";
      };
    }
  }

  create_btn_element();
}

// 生成设置图标
function createScriptSetting() {
  createIcon();

  function createPopupContainer() {
    // 蒙版
    const overlay = $("<div>").addClass("popup-overlay").appendTo("body").click(closePopupContainer);
    // 弹出内容容器
    const container = $("<div>").addClass("popup-container").appendTo(overlay);

    // 设置蒙版的样式
    overlay.css({
      "background-color": "rgba(0, 0, 0, 0.6)",
      "position": "fixed",
      "top": 0,
      "left": 0,
      "width": "100%",
      "height": "100%",
      "z-index": 9999,
      "display": "flex",
      "justify-content": "center",
      "align-items": "center",
    });

    // 设置弹出内容容器的样式
    container.css({
      "background-color": "white",
      "max-width": "640px",
      "width": "80%",
      "height": "80%",
      "overflow-y": "auto",
      "padding": "20px",
      "text-align": "center",
      "border-radius": "5px",
    });

    // 添加弹出内容
    container.append($("<h2>").text("弹出内容"), $("<p>").text("这是一个弹出内容的示例。".repeat(500)));

    // 禁止蒙版下的body内容滚动
    $("body").css("overflow", "hidden");
  }

  function closePopupContainer() {
    // 恢复body内容滚动
    $("body").css("overflow", "auto");

    // 移除蒙版和弹出内容容器
    $(".popup-overlay").remove();
  }

  function createIcon() {
    const windowWidth = $(window).width();
    const bodyContentWidth = $("body").width();
    const iconSize = windowWidth > 720 ? "60px" : "40px";
    $("<img>")
      .attr("id", "settingICon")
      .attr("src", "https://pic.imgdb.cn/item/66c4cb46d9c307b7e92a418b.png")
      .css({
        "position": "fixed",
        "top": "10px",
        "right": "10px",
        "width": iconSize,
        "height": iconSize,
        "z-index": 9998,
      })
      .appendTo("body")
      .click(() => {
        createPopupContainer();
      });

    // PC端设置右偏移量
    if (windowWidth > bodyContentWidth) {
      const rightOffset = (windowWidth - bodyContentWidth) / 2 + 10;
      $("#settingICon").css("right", rightOffset + "px");
    }
  }
}

// 初始化本地设置文件(存放于localStorage,清除浏览器缓存会让设置失效)
function initSetting() {
  return new Promise((resolve, reject) => {
    const defaultSetting = {
      // 第一次加载脚本
      firstLoadScript: true,
      // 显示设置 logo
      showSettingIcon: true,
      // 设置 logo 大小
      settingIconSize: 50,
      // 显示吹牛胜率
      showChuiniuHistory: true,
      // 显示一键回到顶部/底部
      showTopAndDownBtn: true,
      // 发帖 ubb 展开
      showMoreUbb: true,
      // 显示勋章
      showMedal: true,
      // 回帖 ubb 展开
      // 回帖表情展开
      // 回帖 +1
      // 站内图片增强
      // 一键吃肉
      oneClickCollectMoney: true,
    };
    const localSetting = JSON.parse(localStorage.getItem("yaohuoBetterPlusSetting")) || {};
    const saveSetting = { ...defaultSetting, ...localSetting }; // 合并设置,自定义项覆盖默认选项,避免添加新功能时已缓存设置没有新功能相关从而产生bug
    try {
      localStorage.setItem("yaohuoBetterPlusSetting", JSON.stringify(saveSetting));
      console.log("======> [ 已成功初始化设置 ]");
      resolve();
    } catch (error) {
      reject(error);
    }
  });
}

/**
 * 递归检测jQuery是否成功加载
 * @param {Number} maxAttempts 最大检测次数
 * @param {Number} interval 检测间隔时间/ms
 * @param {Number} attempt  已检测次数
 * @returns
 */
function checkJQueryLoad(maxAttempts = 50, interval = 200, attempt = 0) {
  if (typeof jQuery !== "undefined") {
    console.log("已成功加载 jQuery");
    return true;
  } else if (attempt < maxAttempts) {
    console.log("已检测次数:", attempt + 1);
    setTimeout(() => {
      checkJQueryLoad(maxAttempts, interval, attempt + 1);
    }, interval);
  } else {
    console.log("无法加载 jQuery");
    notifyBox("jQuery 加载失败,增强脚本运行已终止", false);
    return false;
  }
}

// 判断是否是在网站中(有些手机端浏览器无法识别油猴 @match 标识,导致在所有网站都会执行脚本)
function checkLocation() {
  const currentUrl = window.location.href;
  const regex1 = new RegExp(/^https?:\/\/yaohuo\.me\/.*/i);
  const regex2 = new RegExp(/^https?:\/\/[^/]+\.yaohuo\.me\/.*/i);

  if (regex1.test(currentUrl) || regex2.test(currentUrl)) {
    return true;
  } else {
    return false;
  }
}

/* ================================================== 自定义方法开始 ================================================== */

/**
 * 在指定textarea/input当前光标处插入内容
 * @param {*} content 插入内容
 * @param {String} targetEle 插入目标 element(jquery可使用的选择器)
 */
function insetCustomContent(content, targetEle) {
  const textarea = $(targetEle); // 获取目标元素
  const cursorPosition = textarea[0].selectionStart; // 获取当前光标位置
  const currentValue = textarea.val(); // 当前内容
  const newValue = currentValue.slice(0, cursorPosition) + content + currentValue.slice(cursorPosition); // 将内容插入当前光标处。如果未选择输入框则插入最后
  textarea.val(newValue); // 写入完整内容
  // 将光标移到插入内容的最后
  textarea[0].selectionStart = cursorPosition + content.length;
  textarea[0].selectionEnd = cursorPosition + content.length;
  textarea.focus();
}

/**
 * 当前页面为指定 url 时执行函数
 * @param {*} targetPath 指定 url,可为正则表达式
 * @param {Function} executeFunction 执行函数
 * @param {Boolean} isRegex 是否使用正则判断 url
 */
function executeFunctionForURL(targetPath, executeFunction, isRegex = false) {
  if (isRegex) {
    targetPath.test(window.location.pathname) && executeFunction();
  } else {
    if (typeof targetPath !== "string" || typeof executeFunction !== "function") {
      throw new Error("参数无效!");
    }
    window.location.pathname === targetPath && executeFunction();
  }
}

// 从指定url获取get参数`
function getUrlParam(paramName, targetUrl = window.location.href) {
  try {
    let urlObj = new URL(targetUrl, window.location.origin);
    return urlObj.searchParams.get(paramName);
  } catch (error) {
    console.error("无效的URL:", targetUrl, error);
    return null;
  }
}
// 获取指定页面内容
function getPageContent(path, method = "GET") {
  const url = `${window.location.origin}${path}`;
  return new Promise((resolve, reject) => {
    $.ajax({
      url,
      method,
      dataType: "html",
      success: (response) => {
        resolve(response);
      },
      error: (error) => {
        reject(error);
      },
    });
  });
}

// 设置保存
function settingChange(setName, setValue) {
  let cacheSetting = JSON.parse(localStorage.getItem("yaohuoBetterPlusSetting"));
  cacheSetting[setName] = setValue;
  try {
    localStorage.setItem("yaohuoBetterPlusSetting", JSON.stringify(cacheSetting));
    notifyBox("保存成功");
  } catch (error) {
    notifyBox("保存失败", false);
  }
}
// 设置获取
function settingGet(name) {
  // let cacheSetting = JSON.parse(localStorage.getItem("yaohuoBetterPlusSetting"));
  // return cacheSetting[name];
  try {
    return JSON.parse(localStorage.getItem("yaohuoBetterPlusSetting"))[name];
  } catch (error) {
    throw new Error("未知错误,获取设置失败");
  }
}

/**
 * 弹出提示
 * @param {String} message 提示内容
 * @param {Boolean} status 提示状态,true成功,false失败,默认true
 * @param {Number} delayTime 提示时间/ms,默认1500ms
 */
let offsetY = 0; // 消息框初始垂直偏移量
function notifyBox(message, status = true, delayTime = 1500) {
  // 消息父容器
  const containerDiv = $("<div>")
    .css({
      position: "fixed",
      top: `calc(100px + ${offsetY}px)`,
      left: "50%",
      transform: "translateX(-50%)",
      maxWidth: "80%",
      width: "auto",
      textAlign: "center",
    })
    .appendTo("body");
  // 消息框创建
  const messageDiv = $("<div>")
    .text(message)
    .css({
      background: status ? "#f0f9eb" : "#fef0f0",
      color: status ? "#67c23a" : "#f56c6c",
      padding: "5px 20px",
      boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
      whiteSpace: "normal",
      wordWrap: "break-word",
      overflowWrap: "break-word",
      hyphens: "auto",
      borderRadius: "5px",
      maxWidth: "100%", // 设置消息框的最大宽度
    })
    .appendTo(containerDiv);
  // 延迟后消息框销毁
  messageDiv
    .fadeIn()
    .delay(delayTime)
    .fadeOut(function () {
      containerDiv.remove();
      offsetY -= 50; // 删除后减少50px的垂直偏移量
    });

  offsetY += 50; // 增加消息框的高度和间距
}

/* ================================================== 自定义方法结束 ================================================== */