【移动端】小红书优化

屏蔽登录弹窗、屏蔽广告、优化评论浏览、优化图片浏览、允许复制、禁止唤醒App、禁止唤醒弹窗、修复正确跳转等

目前为 2024-03-20 提交的版本。查看 最新版本

// ==UserScript==
// @name        【移动端】小红书优化
// @namespace    https://greasyfork.org/zh-CN/users/521923-whitesevs
// @icon         https://fe-video-qc.xhscdn.com/fe-platform/ed8fe781ce9e16c1bfac2cd962f0721edabe2e49.ico
// @version      2024.3.20
// @description  屏蔽登录弹窗、屏蔽广告、优化评论浏览、优化图片浏览、允许复制、禁止唤醒App、禁止唤醒弹窗、修复正确跳转等
// @author       WhiteSevs
// @license      GPL-3.0-only
// @match        http*://www.xiaohongshu.com/*
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @grant        unsafeWindow
// @run-at       document-start
// @connect      edith.xiaohongshu.com
// @require      https://update.greasyfork.org/scripts/449471/1305484/Viewer.js
// @require      https://update.greasyfork.org/scripts/462234/1322684/Message.js
// @require      https://update.greasyfork.org/scripts/456485/1345972/pops.js
// @require      https://update.greasyfork.org/scripts/455186/1345501/WhiteSevsUtils.js
// @require      https://update.greasyfork.org/scripts/465772/1344519/DOMUtils.js
// ==/UserScript==

(function () {
  if(typeof unsafeWindow === "undefined"){
    unsafeWindow = globalThis;
  }
  /* -----------------↓公共配置↓----------------- */
  /**
   * @type {import("../库/Qmsg")}
   */
  const Qmsg = window.Qmsg;
  /**
   * @type {import("../库/pops")}
   */
  const pops = window.pops;
  /**
   * @type {import("../库/Utils")}
   */
  const utils = window.Utils.noConflict();
  /**
   * @type {import("../库/DOMUtils")}
   */
  const DOMUtils = window.DOMUtils.noConflict();
  Qmsg.config({
    position: "bottom",
    html: true,
    maxNums: 5,
    autoClose: true,
    showClose: false,
    showReverse: false,
  });
  const log = new utils.Log(GM_info, unsafeWindow.console || console);
  log.config({
    debug: false,
  });
  const httpx = new utils.Httpx(GM_xmlhttpRequest);
  httpx.config({
    onabort() {
      Qmsg.error("请求被取消");
    },
    ontimeout() {
      Qmsg.error("请求超时");
    },
    onerror(response) {
      Qmsg.error("请求异常");
      log.error(["httpx-onerror", response]);
    },
  });
  /**
   * 菜单对象
   */
  const GM_Menu = new utils.GM_Menu({
    GM_getValue,
    GM_setValue,
    GM_registerMenuCommand,
    GM_unregisterMenuCommand,
  });
  /* -----------------↑公共配置↑----------------- */

  /* 小红书屏蔽 */
  const littleRedBookShield = {
    /**
     * 屏蔽广告
     */
    shieldAd() {
      GM_addStyle(`
        /* 底部的App内打开 */
        .bottom-button-box,
        /* 顶部的打开看看 */
        .nav-bar-box{
          display: none !important;
        }

        /* 视频笔记 */
        /* 底部评论区的- 打开小红书查看全部评论  */
        #new-note-view-container .comment-box .comment-items .button-launch{
          display: none !important;
        }
        
        /* 用户主页 */
        /* 底部的-App内打开 */
        .launch-app-container.bottom-bar,
        /* 顶部的-打开看看 */
        .main-container > .scroll-view-container > .launch-app-container:first-child,
        /* 底部的-打开小红书看更多精彩内容 */
        .bottom-launch-app-tip.show-bottom-bar{
          display: none !important;
        }
        `);
      if (
        littleRedBookRouter.isHomePage() ||
        littleRedBookRouter.isSearchPage()
      ) {
        /* 首页 */
        GM_addStyle(`
        /* 底部的-App内打开 */
        .container .launch-app-container,
        /* 顶部的视频 */
        .container .banner{
          display: none !important;
        }
        `);
      }
    },
    /**
     * 允许复制
     */
    allowCopy() {
      GM_addStyle(`
        *{
            -webkit-user-select: unset;
            user-select: unset;
        }
        `);
    },
    /**
     * 屏蔽底部搜索发现
     */
    shieldBottomSearchFind() {
      GM_addStyle(`
        .hotlist-container,
        /* 一大块空白区域 */
        .safe-area-bottom.margin-placeholder{
            display: none !important;
        }
        `);
    },
    /**
     * 屏蔽底部工具栏
     */
    shieldBottomToorBar() {
      GM_addStyle(`
        .engage-bar-container{
            display: none !important;
        }
        `);
    },
    /**
     * 屏蔽视频笔记的作者热门笔记
     */
    shieldAuthorHotNote() {
      GM_addStyle(`
      .user-notes-box.user-notes-clo-layout-container{
        display: none !important;
      }
      `);
    },
    /**
     * 屏蔽视频笔记的热门推荐
     */
    shieldHotRecommendNote() {
      GM_addStyle(`
      #new-note-view-container .recommend-box{
        display: none !important;
      }
      `);
    },
  };
  /* 小红书api */
  const littleRedBookApi = {
    /**
     * 获取页信息
     * @returns {Promise<?{
     * comments: any[],
     * cursor: string,
     * has_more: boolean,
     * time: number,
     * user_id: string,
     * }>}
     */
    async getPageInfo(
      note_id,
      cursor = "",
      top_comment_id = "",
      image_formats = "jpg,webp"
    ) {
      let getResp = await httpx.get(
        `https://edith.xiaohongshu.com/api/sns/web/v2/comment/page?note_id=${note_id}&cursor=${cursor}&top_comment_id=${top_comment_id}&image_formats=${image_formats}`,
        {
          headers: {
            Accept: "application/json, text/plain, */*",
            "User-Agent": utils.getRandomPCUA(),
            Origin: "https://www.xiaohongshu.com",
            Referer: "https://www.xiaohongshu.com/",
            "X-T": Date.now(),
          },
        }
      );
      if (!getResp.status) {
        return;
      }
      let data = utils.toJSON(getResp.data.responseText);
      log.info(["获取页信息", data]);
      if (data["code"] === 0 || data["success"]) {
        return data["data"];
      } else if (data["code"] === -101) {
        /* 未登录 */
        return;
      } else {
        Qmsg.error(data["msg"]);
      }
    },
    /**
     * 获取楼中楼页信息
     * @returns {Promise<?{
     * comments: any[],
     * cursor: string,
     * has_more: boolean,
     * time: number,
     * user_id: string,
     * }>}
     */
    async getLzlPageInfo(
      note_id = "",
      root_comment_id = "",
      num = 10,
      cursor = "",
      image_formats = "jpg,webp"
    ) {
      let getResp = await httpx.get(
        `https://edith.xiaohongshu.com/api/sns/web/v2/comment/sub/page?note_id=${note_id}&root_comment_id=${root_comment_id}&num=${num}&cursor=${cursor}&image_formats=${image_formats}`,
        {
          headers: {
            Accept: "application/json, text/plain, */*",
            "User-Agent": utils.getRandomPCUA(),
            Origin: "https://www.xiaohongshu.com",
            Referer: "https://www.xiaohongshu.com/",
            "X-T": Date.now(),
          },
        }
      );
      if (!getResp.status) {
        return;
      }
      let data = utils.toJSON(getResp.data.responseText);
      log.info(["获取楼中楼页信息", data]);
      if (data["code"] === 0 || data["success"]) {
        return data["data"];
      } else {
        Qmsg.error(data["msg"]);
      }
    },
  };
  /* 小红书router */
  const littleRedBookRouter = {
    /**
     * 判断是否是笔记页面
     */
    isNotePage() {
      return globalThis.location.pathname.startsWith("/discovery/item/");
    },
    /**
     * 判断是否是用户主页页面
     */
    isUserHomePage() {
      return globalThis.location.pathname.startsWith("/user/profile/");
    },
    /**
     * 判断是否是主页
     */
    isHomePage() {
      return (
        globalThis.location.href === "https://www.xiaohongshu.com/" ||
        globalThis.location.href === "https://www.xiaohongshu.com"
      );
    },
    /**
     * 判断是否是搜索页面
     */
    isSearchPage() {
      return globalThis.location.pathname.startsWith("/search_result/");
    },
  };
  /* 小红书业务 */
  const littleRedBookBusiness = {
    /**
     * 优化评论浏览
     */
    optimizeCommentBrowsing() {
      /* 评论 */
      const Comments = {
        QmsgLoading: undefined,
        scrollFunc: undefined,
        noteData: {},
        commentData: {},
        emojiMap: {},
        emojiNameList: [],
        currentCursor: undefined,
        commentContainer: undefined,
        init() {
          /* emoji数据 */
          this.emojiMap =
            utils.toJSON(unsafeWindow.localStorage.getItem("redmoji"))?.[
              "redmojiMap"
            ] || {};
          /* emoji的名字数组 */
          this.emojiNameList = Object.keys(this.emojiMap);
          /* 滚动事件 */
          this.scrollFunc = new utils.LockFunction(this.scrollEvent, this);
          /* 笔记数据 */
          Comments.noteData =
            unsafeWindow["__INITIAL_STATE__"].noteData.data.noteData;
          /* 评论数据 */
          Comments.commentData =
            unsafeWindow["__INITIAL_STATE__"].noteData.data.commentData;
          log.info(["笔记数据", Comments.noteData]);
          log.info(["评论数据", Comments.commentData]);
        },
        /**
         *
         * @param {{
         * user_id: string,
         * user_avatar: string,
         * user_nickname: string,
         * content: string,
         * create_time: string|number,
         * ip_location: string
         * }} data
         * @returns
         */
        getCommentHTML(data) {
          return `
            <div class="little-red-book-comments-avatar">
                    <a target="_blank" href="/user/profile/${data.user_id}">
                        <img src="${data.user_avatar}" crossorigin="anonymous">
                    </a>
              </div>
              <div class="little-red-book-comments-content-wrapper">
                <div class="little-red-book-comments-author-wrapper">
                    <div class="little-red-book-comments-author">
                        <a href="/user/profile/${
                          data.user_id
                        }" class="little-red-book-comments-author-name" target="_blank">
                            ${data.user_nickname}
                        </a>
                    </div>
                    <div class="little-red-book-comments-content">
                        ${data.content}
                    </div>
                    <div class="little-red-book-comments-info">
                        <div class="little-red-book-comments-info-date">
                            <span class="little-red-book-comments-create-time">${utils.formatTime(
                              data.create_time
                            )}</span>
                            <span class="little-red-book-comments-location">${
                              data.ip_location
                            }</span>
                        </div>
                    </div>
                </div>
              </div>
            `;
        },
        /**
         * 获取内容元素
         * @param {object} data
         * @returns
         */
        getCommentElement(data) {
          /* 评论内容 */
          let content = data["content"];
          /* 发布时间 */
          let create_time = data["create_time"] || parseInt(data["time"]);
          /* 发布的id,用于加载楼中楼评论 */
          let id = data["id"];
          /* 发布者的ip地址 */
          let ip_location = data["ip_location"] || data["ipLocation"];
          /* 是否继续存在子评论 */
          let sub_comment_has_more = data["sub_comment_has_more"];
          /* 楼中楼回复的总数量 */
          let sub_comment_count = parseInt(data["sub_comment_count"]) || 0;
          /* 加载楼中楼更多回复的时候需要的参数 */
          let sub_comment_cursor = data["sub_comment_cursor"];
          /* 楼中楼评论的数据 */
          let sub_comments = data["sub_comments"] || data["subComments"];
          /* 评论的用户头像 */
          let user_avatar = (data["user_info"] || data["user"])["image"];
          /* 评论的用户名 */
          let user_nickname = (data["user_info"] || data["user"])["nickname"];
          /* 评论的用户id */
          let user_id =
            data?.["user_info"]?.["user_id"] || data?.["user"]?.["userId"];

          content = Comments.converContent(content);
          /* 创建元素 */
          let commentItemElement = DOMUtils.createElement("div", {
            className: "little-red-book-comments-item",
            innerHTML: `
            <div class="little-red-book-comments-parent">
              ${Comments.getCommentHTML({
                user_id: user_id,
                user_avatar: user_avatar,
                user_nickname: user_nickname,
                content: content,
                create_time: create_time,
                ip_location: ip_location,
              })}
            </div>
              `,
          });

          /* 判断是否存在楼中楼回复 */
          if (sub_comment_has_more && Array.isArray(sub_comments)) {
            sub_comments.forEach((subCommentInfo) => {
              let subCommentElement = DOMUtils.createElement("div", {
                className: "little-red-book-comments-reply-container",
                innerHTML: Comments.getCommentHTML({
                  user_id: subCommentInfo["user_info"]["user_id"],
                  user_avatar: subCommentInfo["user_info"]["image"],
                  user_nickname: subCommentInfo["user_info"]["nickname"],
                  content: Comments.converContent(subCommentInfo["content"]),
                  create_time: subCommentInfo["create_time"],
                  ip_location: subCommentInfo["ip_location"],
                }),
              });
              commentItemElement.appendChild(subCommentElement);
            });
            if (sub_comment_count !== sub_comments.length) {
              /* 楼中楼回复还没加载完 */
              /* 计算出还没加载完的楼中楼回复的数量 */
              let endReplyCount = sub_comment_count - sub_comments.length;
              /* 楼中楼的cursor */
              let lzlCursor = sub_comment_cursor;
              let showMoreElement = DOMUtils.createElement("div", {
                className: "little-red-book-comments-reply-show-more",
                innerText: `展开 ${endReplyCount} 条回复`,
              });
              async function showMoreEvent() {
                let QmsgLoading = Qmsg.loading("加载中,请稍后...");
                let pageInfo = await littleRedBookApi.getLzlPageInfo(
                  Comments.noteData["id"],
                  id,
                  10,
                  lzlCursor,
                  undefined
                );
                QmsgLoading.close();
                if (!pageInfo) {
                  return;
                }
                /* 覆盖cursor */
                lzlCursor = pageInfo.cursor;
                /* 重新计算剩余的回复数量 */
                endReplyCount = endReplyCount - pageInfo.comments.length;
                /* 修改页面显示 */
                showMoreElement.innerText = `展开 ${endReplyCount} 条回复`;
                pageInfo.comments.forEach((subCommentInfo) => {
                  let subCommentElement = DOMUtils.createElement("div", {
                    className: "little-red-book-comments-reply-container",
                    innerHTML: Comments.getCommentHTML({
                      user_id: subCommentInfo["user_info"]["user_id"],
                      user_avatar: subCommentInfo["user_info"]["image"],
                      user_nickname: subCommentInfo["user_info"]["nickname"],
                      content: Comments.converContent(
                        subCommentInfo["content"]
                      ),
                      create_time: subCommentInfo["create_time"],
                      ip_location: subCommentInfo["ip_location"],
                    }),
                  });
                  DOMUtils.before(showMoreElement, subCommentElement);
                });
                if (!pageInfo.has_more) {
                  /* 没有更多回复了 */
                  DOMUtils.off(
                    showMoreElement,
                    "click",
                    undefined,
                    showMoreEvent,
                    {
                      capture: true,
                    }
                  );
                  showMoreElement.remove();
                }
              }
              DOMUtils.on(showMoreElement, "click", undefined, showMoreEvent, {
                capture: true,
              });
              commentItemElement.appendChild(showMoreElement);
            }
          }
          return commentItemElement;
        },
        /**
         * 转换内容字符串中的emoji
         */
        converContent(content) {
          /* 将内容的emoji代码换成html元素 */
          Comments.emojiNameList.forEach((emojiName) => {
            if (content.includes(emojiName)) {
              content = content.replaceAll(
                emojiName,
                `<img class="little-red-book-note-content-emoji" crossorigin="anonymous" src="${Comments.emojiMap[emojiName]}">`
              );
            }
          });
          return content;
        },
        /**
         * 滚动事件
         */
        async scrollEvent() {
          if (!utils.isNearBottom(window.innerHeight / 3)) {
            return;
          }
          if (this.QmsgLoading == null) {
            this.QmsgLoading = Qmsg.loading("加载中,请稍后...");
          }
          let pageInfo = await littleRedBookApi.getPageInfo(
            Comments.noteData["id"],
            Comments.currentCursor
          );
          if (this.QmsgLoading) {
            this.QmsgLoading.close();
            this.QmsgLoading = undefined;
          }
          if (!pageInfo) {
            return;
          }
          Comments.currentCursor = pageInfo.cursor;
          pageInfo.comments.forEach((commentItem) => {
            let commentItemElement = Comments.getCommentElement(commentItem);
            Comments.commentContainer.appendChild(commentItemElement);
          });

          if (!pageInfo.has_more) {
            /* 没有更多数据了 */
            Qmsg.info("已加载全部评论");
            Comments.removeScrollEventListener();
            return;
          }
        },
        /**
         * 添加滚动监听
         */
        addSrollEventListener() {
          log.success("添加滚动监听事件");
          DOMUtils.on(document, "scroll", undefined, Comments.scrollFunc.run, {
            capture: true,
            once: false,
            passive: true,
          });
        },
        /**
         * 移除滚动监听
         */
        removeScrollEventListener() {
          log.success("移除滚动监听事件");
          DOMUtils.off(document, "scroll", undefined, Comments.scrollFunc.run, {
            capture: true,
          });
        },
      };
      /* 等待内容元素出现 */
      utils.waitNode(".narmal-note-container").then(async () => {
        let noteViewContainer = document.querySelector(".note-view-container");
        let loading = Qmsg.loading("获取评论中,请稍后...");
        let commentContainer = DOMUtils.createElement("div", {
          className: "little-red-book-comments-container",
          innerHTML: `
          <style>
            .little-red-book-comments-parent {
                position: relative;
                display: flex;
                padding: 8px;
                width: 100%;
            }
            
            .little-red-book-comments-reply-container {
                position: relative;
                display: flex;
                padding: 8px;
                width: 100%;
                padding-left: 52px;
            }
            .little-red-book-comments-container {
                background: #fff;
                position: relative;
                padding: 8px 8px;
            }
            
            .little-red-book-comments-item {
                position: relative;
            }
            
            .little-red-book-comments-avatar {
                flex: 0 0 auto;
            }
            
            .little-red-book-comments-avatar img {
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                border-radius: 100%;
                border: 1px solid rgba(0,0,0,0.08);
                object-fit: cover;
                width: 40px;
                height: 40px;
            }
            .little-red-book-comments-content-wrapper {
                margin-left: 12px;
                display: flex;
                flex-direction: column;
                font-size: 14px;
                flex-grow: 1;
            }
            
            .little-red-book-comments-author {display: flex;justify-content: space-between;align-items: center;}
            
            a.little-red-book-comments-author-name {
                line-height: 18px;
                color: rgba(51,51,51,0.6);
            }
            
            .little-red-book-comments-content {
                margin-top: 4px;
                line-height: 140%;
                color: #333;
            }
            
            .little-red-book-comments-info {
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                font-size: 12px;
                line-height: 16px;
                color: rgba(51,51,51,0.6);
            }
            
            .little-red-book-comments-info-date {
                margin: 8px 0;
            }
            
            span.little-red-book-comments-location {
                margin-left: 4px;
                line-height: 120%;
            }
            img.little-red-book-note-content-emoji {
                margin: 0 1px;
                height: 16px;
                transform: translateY(2px);
                position: relative;
            }
            .little-red-book-comments-reply-container .little-red-book-comments-avatar img {
                width: 24px;
                height: 24px;
            }
            .little-red-book-comments-total{
                font-size: 14px;
                color: rgba(51,51,51,0.6);
                margin-left: 8px;
                margin-bottom: 12px;
            }
            .little-red-book-comments-reply-show-more {
              padding-left: calc(52px + 24px + 12px);
              height: 32px;
              line-height: 32px;
              color: #13386c;
              cursor: pointer;
              font-weight: 500;
              font-size: 14px;
            }
          </style>
          `,
        });
        Comments.commentContainer = commentContainer;
        Comments.init();
        let totalElement = DOMUtils.createElement("div", {
          className: "little-red-book-comments-total",
          innerHTML: `共 ${Comments.noteData["comments"]} 条评论`,
        });
        commentContainer.appendChild(totalElement);
        let pageInfo = await littleRedBookApi.getPageInfo(
          Comments.noteData["id"]
        );
        /* 延迟一会儿 */
        await utils.sleep(800);
        if (pageInfo) {
          Comments.currentCursor = pageInfo.cursor;
          pageInfo.comments.forEach((commentItem) => {
            let commentItemElement = Comments.getCommentElement(commentItem);
            commentContainer.appendChild(commentItemElement);
          });
          /* 评论尚未加载完 */
          if (pageInfo.has_more) {
            Comments.addSrollEventListener();
          }
        } else if (Comments.commentData && Comments.commentData["comments"]) {
          /* 从固定的评论中加载 */
          log.info("从固定的评论中加载");
          Comments.commentData["comments"].forEach((commentItem) => {
            let commentItemElement = Comments.getCommentElement(commentItem);
            commentContainer.appendChild(commentItemElement);
          });
        }
        loading.close();
        DOMUtils.append(noteViewContainer, commentContainer);
      });
    },
    /**
     * 修复正确的点击跳转-用户主页
     * 点啥都不好使,都会跳转至下载页面
     */
    repariClick() {
      DOMUtils.on(
        document,
        "click",
        undefined,
        function (event) {
          /**
           * @type {Element}
           */
          let clickElement = event.target;
          if (clickElement?.className?.includes("follow-btn")) {
            /* 关注按钮 */
            /* 就不关注 */
          } else if (clickElement?.closest("button.reds-button.message-btn")) {
            /* 私信按钮 */
            /* 就不私信 */
          } else if (clickElement?.closest("div.reds-tab-item")) {
            /* 笔记/收藏按钮 */
          } else if (clickElement?.closest("section.reds-note-card")) {
            /* 笔记卡片 */
            let sectionElement = clickElement?.closest(
              "section.reds-note-card"
            );
            let note_id =
              sectionElement.getAttribute("id") ||
              utils.toJSON(sectionElement.getAttribute("impression"))?.[
                "noteTarget"
              ]?.["value"]?.["noteId"];
            if (note_id) {
              window.open(
                `https://www.xiaohongshu.com/discovery/item/${clickElement
                  ?.closest("section.reds-note-card")
                  .getAttribute("id")}`,
                "_blank"
              );
            } else {
              Qmsg.error("获取笔记note_id失败");
            }
          }
          log.info(["点击的按钮", clickElement]);
          utils.preventEvent(event);
          return false;
        },
        {
          capture: true,
        }
      );
    },
    /**
     * 优化图片浏览
     */
    optimizeImageBrowsing() {
      /**
       * 查看图片
       * @param {string[]} imgSrcList
       * @param {number} index
       */
      function viewIMG(imgSrcList = [], index = 0) {
        let viewerULNodeHTML = "";
        imgSrcList.forEach((item) => {
          viewerULNodeHTML += `<li><img data-src="${item}" loading="lazy"></li>`;
        });
        let viewerULNode = DOMUtils.createElement("ul", {
          innerHTML: viewerULNodeHTML,
        });
        /**
         * @type {import("../库/Viewer")}
         */
        let viewer = new Viewer(viewerULNode, {
          inline: false,
          url: "data-src",
          zIndex: utils.getMaxZIndex() + 100,
          hidden: () => {
            viewer.destroy();
          },
        });
        index = index < 0 ? 0 : index;
        viewer.view(index);
        viewer.zoomTo(1);
        viewer.show();
      }
      DOMUtils.on(document, "click", ".note-image-box", function (event) {
        /**
         * @type {HTMLElement}
         */
        let clickElement = event.target;
        let imgElement = clickElement.querySelector("img");
        let imgList = [];
        let imgBoxList = [];
        if (clickElement.closest(".onix-carousel-item")) {
          /* 多组图片 */
          imgBoxList = Array.from(
            clickElement
              .closest(".onix-carousel-item")
              .parentElement.querySelectorAll("img")
          );
        } else {
          /* 单个图片 */
          imgBoxList = [imgElement];
        }
        let index = imgBoxList.findIndex((value) => {
          return value == imgElement;
        });
        imgBoxList.forEach((element) => {
          let imgSrc =
            element.getAttribute("src") ||
            element.getAttribute("data-src") ||
            element.getAttribute("alt");
          imgList.push(imgSrc);
        });
        log.success(["点击浏览图片👉", imgList[index]]);
        viewIMG(imgList, index);
      });
    },
    /**
     * 优化视频笔记的描述(可滚动)
     */
    optimizeVideoNoteDesc() {
      GM_addStyle(`
      .author-box .author-desc-wrapper .author-desc{
        max-height: 70px !important;
        overflow: auto !important;
      }
      /* 展开按钮 */
      .author-box .author-desc-wrapper .author-desc .author-desc-trigger{
        display: none !important;
      }
      `);
    },
    /**
     * PC端 允许复制
     */
    allowPCCopy() {
      DOMUtils.on(
        globalThis,
        "copy",
        undefined,
        function (event) {
          utils.preventEvent(event);
          utils.setClip(globalThis.getSelection().toString());
          return false;
        },
        {
          capture: true,
        }
      );
    },
  };
  /* 小红书劫持函数 */
  const littleRedBookHijack = {
    /**
     * 劫持webpack
     * 笔记的
     */
    webpackChunkranchi() {
      let originObject = undefined;
      let webpackName = "webpackChunkranchi";
      Object.defineProperty(unsafeWindow, webpackName, {
        get() {
          return originObject;
        },
        set(newValue) {
          originObject = newValue;
          const oldPush = originObject.push;
          originObject.push = function (...args) {
            let _mainCoreData = args[0][0];
            if (typeof args[0][1] === "object") {
              Object.keys(args[0][1]).forEach((keyName, index) => {
                if (
                  typeof args[0][1][keyName] === "function" &&
                  args[0][1][keyName]
                    .toString()
                    .includes("是否打开小红书App?") &&
                  PopsPanel.getValue("little-red-book-hijack-webpack-mask")
                ) {
                  /* 这个是弹窗的 */
                  log.success(["成功劫持各种弹窗/遮罩层:" + keyName]);
                  args[0][1][keyName] = function () {};
                } else if (
                  typeof args[0][1][keyName] === "function" &&
                  args[0][1][keyName]
                    .toString()
                    .startsWith(
                      "function(e,n,t){t.d(n,{Z:function(){return y}});"
                    ) &&
                  args[0][1][keyName].toString().includes("jumpToApp") &&
                  PopsPanel.getValue("little-red-book-hijack-webpack-scheme")
                ) {
                  /* 这个scheme唤醒的 */
                  let oldFunc = args[0][1][keyName];
                  args[0][1][keyName] = function (...args_1) {
                    log.success(["成功劫持scheme唤醒", args_1]);
                    let oldD = args_1[2].d;
                    args_1[2].d = function (...args_2) {
                      if (
                        args_2.length === 2 &&
                        typeof args_2[1]?.["Z"] === "function"
                      ) {
                        let oldZ = args_2[1]["Z"];
                        if (oldZ.toString() === "function(){return y}") {
                          args_2[1]["Z"] = function (...args_3) {
                            let result = oldZ.call(this, ...args_3);
                            if (
                              typeof result === "function" &&
                              result.toString().includes("jumpToApp")
                            ) {
                              return function () {
                                return {
                                  jumpToApp(data) {
                                    log.success(["拦截唤醒", data]);
                                    if (
                                      data["deeplink"]?.startsWith(
                                        "xhsdiscover://user/"
                                      )
                                    ) {
                                      /* 正确跳转用户主页 */
                                      let userId = data["deeplink"].replace(
                                        /^xhsdiscover:\/\/user\//,
                                        ""
                                      );
                                      let userHomeUrl = `https://www.xiaohongshu.com/user/profile/${userId}`;
                                      window.open(userHomeUrl, "_blank");
                                    }
                                  },
                                };
                              };
                            }
                            return result;
                          };
                        }
                      }
                      oldD.call(this, ...args_2);
                    };
                    oldFunc.call(this, ...args_1);
                  };
                }
              });
            }

            return oldPush.call(this, ...args);
          };
        },
      });
    },
    /**
     * 劫持vue,恢复属性__Ivue__
     */
    webPackVue() {
      let originApply = unsafeWindow.Function.prototype.apply;
      let isHijack = false;
      unsafeWindow.Function.prototype.apply = function (...args) {
        const result = originApply.call(this, ...args);
        if (
          !isHijack &&
          args.length === 2 &&
          args[0]?.addRoute &&
          args[0]?.currentRoute &&
          args[0]?.getRoutes &&
          args[0]?.hasRoute &&
          args[0]?.install &&
          args[0]?.removeRoute
        ) {
          isHijack = !0;
          let __vue__ = args[1][0];
          log.success(["成功劫持vue,version版本:", __vue__.version]);
          __vue__["mixin"]({
            mounted: function () {
              this.$el["__Ivue__"] = this;
            },
          });
        }
        return result;
      };
    },
  };

  /**
   * 配置面板
   */
  const PopsPanel = {
    /**
     * 本地存储的总键名
     */
    key: "GM_Panel",
    /**
     * 属性attributes的data-key
     */
    attributeDataKey_Name: "data-key",
    /**
     * 属性attributes的data-default-value
     */
    attributeDataDefaultValue_Name: "data-default-value",
    /**
     * 初始化菜单
     */
    initMenu() {
      this.initLocalDefaultValue();
      if (unsafeWindow.top !== unsafeWindow.self) {
        return;
      }
      GM_Menu.add([
        {
          key: "show_pops_panel_setting",
          text: "⚙ 设置",
          autoReload: false,
          isStoreValue: false,
          showText(text) {
            return text;
          },
          callback: () => {
            this.showPanel();
          },
        },
      ]);
    },
    /**
     * 初始化本地设置默认的值
     */
    initLocalDefaultValue() {
      let content = this.getContent();
      content.forEach((item) => {
        if (!item["forms"]) {
          return;
        }
        item.forms.forEach((__item__) => {
          if (__item__.forms) {
            __item__.forms.forEach((containerItem) => {
              if (!containerItem.attributes) {
                return;
              }
              let key = containerItem.attributes[this.attributeDataKey_Name];
              let defaultValue =
                containerItem.attributes[this.attributeDataDefaultValue_Name];
              if (this.getValue(key) == null) {
                this.setValue(key, defaultValue);
              }
            });
          } else {
          }
        });
      });
    },
    /**
     * 设置值
     * @param {string} key 键
     * @param {any} value 值
     */
    setValue(key, value) {
      let localValue = GM_getValue(this.key, {});
      localValue[key] = value;
      GM_setValue(this.key, localValue);
    },
    /**
     * 获取值
     * @param {string} key 键
     * @param {any} defaultValue 默认值
     * @returns {any}
     */
    getValue(key, defaultValue) {
      let localValue = GM_getValue(this.key, {});
      return localValue[key] ?? defaultValue;
    },
    /**
     * 删除值
     * @param {string} key 键
     */
    deleteValue(key) {
      let localValue = GM_getValue(this.key, {});
      delete localValue[key];
      GM_setValue(this.key, localValue);
    },
    /**
     * 显示设置面板
     */
    showPanel() {
      pops.panel({
        title: {
          text: `${GM_info?.script?.name || "【移动端】小红书优化"}-设置`,
          position: "center",
        },
        content: this.getContent(),
        mask: {
          enable: true,
          clickEvent: {
            toClose: true,
          },
        },
        isMobile: true,
        width: "92vw",
        height: "80vh",
        only: true,
        drag: true,
      });
    },
    /**
     * 获取按钮配置
     * @param {string} text 文字
     * @param {string} description 描述
     * @param {string} key 键
     * @param {boolean} defaultValue 默认值
     * @param {?(event:Event,value: boolean)=>boolean} _callback_ 点击回调
     */
    getSwtichDetail(text, description, key, defaultValue, _callback_) {
      /**
       * @type {PopsPanelSwitchDetails}
       */
      let result = {
        text: text,
        description: description,
        type: "switch",
        attributes: {},
        getValue() {
          if (PopsPanel.getValue(key) == null) {
            PopsPanel.setValue(key, Boolean(defaultValue));
          }
          return Boolean(PopsPanel.getValue(key, defaultValue));
        },
        callback(event, value) {
          log.success(`${value ? "开启" : "关闭"} ${text}`);
          if (typeof _callback_ === "function") {
            if (_callback_(event, value)) {
              return;
            }
          }
          PopsPanel.setValue(key, Boolean(value));
        },
      };
      result.attributes[this.attributeDataKey_Name] = key;
      result.attributes[this.attributeDataDefaultValue_Name] =
        Boolean(defaultValue);
      return result;
    },
    /**
     * 获取配置内容
     */
    getContent() {
      return [
        {
          id: "little-red-book-panel-config-shield",
          title: "屏蔽",
          forms: [
            {
              text: "功能",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "【屏蔽】广告",
                  "如:App内打开",
                  "little-red-book-shieldAd",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "【屏蔽】底部搜索发现",
                  "建议开启",
                  "little-red-book-shieldBottomSearchFind",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "【屏蔽】底部工具栏",
                  "建议开启",
                  "little-red-book-shieldBottomToorBar",
                  true
                ),
              ],
            },
          ],
        },
        {
          id: "little-red-book-panel-config-note",
          title: "笔记",
          forms: [
            {
              text: "功能",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "优化评论浏览",
                  "加载评论,未登录最多查看1页评论(包括楼中楼的)",
                  "little-red-book-optimizeCommentBrowsing",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "优化图片浏览",
                  "更方便的浏览图片",
                  "little-red-book-optimizeImageBrowsing",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "允许复制",
                  "可以复制笔记的内容",
                  "little-red-book-allowCopy",
                  true
                ),
              ],
            },
            {
              text: "视频笔记",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "优化视频描述",
                  "让视频描述可以滚动显示更多",
                  "little-red-book-optimizeVideoNoteDesc",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "【屏蔽】作者热门笔记",
                  "建议开启",
                  "little-red-book-shieldAuthorHotNote",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "【屏蔽】热门推荐",
                  "建议开启",
                  "little-red-book-shieldHotRecommendNote",
                  true
                ),
              ],
            },
            {
              text: "劫持/拦截",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "劫持webpack-弹窗",
                  "如:打开App弹窗、登录弹窗",
                  "little-red-book-hijack-webpack-mask",
                  true
                ),
                PopsPanel.getSwtichDetail(
                  "劫持webpack-唤醒App",
                  "禁止跳转商店小红书详情页/小红书",
                  "little-red-book-hijack-webpack-scheme",
                  true
                ),
              ],
            },
          ],
        },
        {
          id: "little-red-book-panel-config-home",
          title: "主页",
          forms: [
            {
              text: "劫持/拦截",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "劫持点击事件",
                  "可阻止点击跳转至下载页面",
                  "little-red-book-repariClick",
                  true
                ),
              ],
            },
          ],
        },
        {
          id: "little-red-book-panel-config-other",
          title: "其它",
          forms: [
            {
              text: "功能",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "全局允许复制",
                  "PC端使用",
                  "little-red-book-pc-allow-copy",
                  false
                ),
              ],
            },
            {
              text: "劫持/拦截",
              type: "forms",
              forms: [
                PopsPanel.getSwtichDetail(
                  "劫持Vue",
                  "恢复__vue__属性",
                  "little-red-book-hijack-vue",
                  false
                ),
              ],
            },
          ],
        },
      ];
    },
  };
  /* -----------------↓函数区域↓----------------- */

  /* -----------------↑函数区域↑----------------- */

  /* -----------------↓执行入口↓----------------- */
  /* 修复一下Qmsg的loading图标问题 */
  GM_addStyle(`
    .qmsg svg.animate-turn {
        fill: none;
    }
  `);
  PopsPanel.initMenu();
  if (PopsPanel.getValue("little-red-book-hijack-vue")) {
    littleRedBookHijack.webPackVue();
  }
  if (PopsPanel.getValue("little-red-book-shieldAd")) {
    littleRedBookShield.shieldAd();
  }
  if (PopsPanel.getValue("little-red-book-allowCopy")) {
    littleRedBookShield.allowCopy();
  }

  if (littleRedBookRouter.isNotePage()) {
    if (PopsPanel.getValue("little-red-book-shieldBottomSearchFind")) {
      littleRedBookShield.shieldBottomSearchFind();
    }
    if (PopsPanel.getValue("little-red-book-shieldBottomToorBar")) {
      littleRedBookShield.shieldBottomToorBar();
    }
    if (
      PopsPanel.getValue("little-red-book-hijack-webpack-mask") ||
      PopsPanel.getValue("little-red-book-hijack-webpack-scheme")
    ) {
      littleRedBookHijack.webpackChunkranchi();
    }
    if (PopsPanel.getValue("little-red-book-optimizeImageBrowsing")) {
      littleRedBookBusiness.optimizeImageBrowsing();
    }
    if (PopsPanel.getValue("little-red-book-optimizeVideoNoteDesc")) {
      littleRedBookBusiness.optimizeVideoNoteDesc();
    }
    if (PopsPanel.getValue("little-red-book-shieldAuthorHotNote")) {
      littleRedBookShield.shieldAuthorHotNote();
    }
    if (PopsPanel.getValue("little-red-book-shieldHotRecommendNote")) {
      littleRedBookShield.shieldHotRecommendNote();
    }
  }
  if (PopsPanel.getValue("little-red-book-pc-allow-copy")) {
    littleRedBookBusiness.allowPCCopy();
  }

  DOMUtils.ready(function () {
    if (littleRedBookRouter.isNotePage()) {
      if (PopsPanel.getValue("little-red-book-optimizeCommentBrowsing")) {
        littleRedBookBusiness.optimizeCommentBrowsing();
      }
    }

    if (littleRedBookRouter.isUserHomePage()) {
      if (PopsPanel.getValue("little-red-book-repariClick")) {
        littleRedBookBusiness.repariClick();
      }
    }
  });
  /* -----------------↑执行入口↑----------------- */
})();