NGA Auto Pagerize

简单的自动翻页

当前为 2022-11-28 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        NGA Auto Pagerize
// @namespace   https://greasyfork.org/users/263018
// @version     1.4.4
// @author      snyssss
// @description 简单的自动翻页

// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*

// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @noframes
// ==/UserScript==

((ui, n = {}, api = {}, uid) => {
  if (!ui) return;

  // KEY
  const ATTACHMENT_STYLE_ENABLE_KEY = "ATTACHMENT_STYLE_ENABLE";
  const PAGE_BUTTON_STYLE_ENABLE_KEY = "PAGE_BUTTON_STYLE_ENABLE_KEY";
  const HOTKEYS_ENABLE_KEY = "HOTKEYS_ENABLE_KEY";
  const FORUM_NAME_ENABLE_KEY = "FORUM_NAME_ENABLE_KEY";
  const POST_LOSS_DETECTION_KEY = "POSTS_LOSS_DETECTION_KEY";
  const AUTO_CHECK_IN_ENABLE_KEY = "AUTO_CHECK_IN_ENABLE_KEY";
  const AUTO_CHECK_IN_LAST_TIME_KEY = "AUTO_CHECK_IN_LAST_TIME_KEY";

  // 附件样式
  const attachmentStyleEnable =
    GM_getValue(ATTACHMENT_STYLE_ENABLE_KEY) || false;

  // 页码样式
  const pageButtonStyleEnable =
    GM_getValue(PAGE_BUTTON_STYLE_ENABLE_KEY) || false;

  // 快捷翻页
  const hotkeysEnable = GM_getValue(HOTKEYS_ENABLE_KEY) || false;

  // 版面名称
  const forumNameEnable = GM_getValue(FORUM_NAME_ENABLE_KEY) || false;

  // 抽楼检测
  const postLossDetectionEnable = GM_getValue(POST_LOSS_DETECTION_KEY) || false;

  // 自动签到
  const autoCheckInEnable = GM_getValue(AUTO_CHECK_IN_ENABLE_KEY) || false;

  // 自动签到时间
  const autoCheckInLastTime = GM_getValue(AUTO_CHECK_IN_LAST_TIME_KEY) || 0;

  // 自动签到 UA
  const autoCheckInUserAgent = "Nga_Official/80024(Android12)";

  // 加载脚本
  (() => {
    const hookFunction = (object, functionName, callback) => {
      ((originalFunction) => {
        object[functionName] = function () {
          const returnValue = originalFunction.apply(this, arguments);

          callback.apply(this, [returnValue, originalFunction, arguments]);

          return returnValue;
        };
      })(object[functionName]);
    };

    const hooked = {
      autoPagerize: false,
      uniqueTopic: false,
      attachmentStyle: false,
      pageButtonStyle: false,
      hotkeys: false,
      forumName: false,
      postLossDetection: false,
      postLossDetectionTopic: false,
    };

    const hook = () => {
      // 翻页
      const loadReadHidden = (() => {
        const delay = (interval) =>
          new Promise((resolve) => setTimeout(resolve, interval));

        const retry = async (fn, retriesLeft = 10, interval = 160) => {
          try {
            return await fn();
          } catch (error) {
            await delay(interval);

            if (retriesLeft > 0) {
              return await retry(fn, retriesLeft - 1, interval);
            }
          }
        };

        return (p, opt = 1) => {
          if (ui.loadReadHidden) {
            retry(() => {
              if (ui.loadReadHidden.lock) {
                throw new Error();
              }

              ui.loadReadHidden(p, opt);
            });
          }
        };
      })();

      // 自动翻页
      if (hooked.autoPagerize === false) {
        if (ui.pageBtn) {
          const execute = (() => {
            const observer = new IntersectionObserver((entries) => {
              if (entries.find((item) => item.isIntersecting)) {
                loadReadHidden(0, 2);
              }
            });

            return () => {
              const anchor = document.querySelector('[title="加载下一页"]');

              if (anchor) {
                observer.observe(anchor);
              } else {
                observer.disconnect();
              }
            };
          })();

          hookFunction(ui, "pageBtn", execute);

          hooked.autoPagerize = true;

          execute();
        }
      }

      // 移除重复内容
      if (hooked.uniqueTopic === false) {
        if (ui.topicArg) {
          const execute = () => {
            if (location.search.indexOf("searchpost=1") > 0) {
              return;
            }

            ui.topicArg.data = ui.topicArg.data.reduce(
              (accumulator, currentValue) => {
                if (document.contains(currentValue[0])) {
                  const index = accumulator.findIndex(
                    (item) => item[8] === currentValue[8]
                  );

                  if (index < 0) {
                    return [...accumulator, currentValue];
                  }

                  currentValue[0].closest("TBODY").remove();
                }

                return accumulator;
              },
              []
            );
          };

          hookFunction(ui.topicArg, "loadAll", execute);

          hooked.uniqueTopic = true;

          execute();
        }
      }

      // 附件样式
      if (hooked.attachmentStyle === false && attachmentStyleEnable) {
        if (ui.topicArg) {
          const execute = () => {
            const elements =
              document.querySelectorAll('[title="主题中有附件"]');

            elements.forEach((element) => {
              element.className = "block_txt white nobr vertmod";
              element.style = "background-color: #BD7E6D";
              element.innerHTML = "附件";
            });
          };

          hookFunction(ui.topicArg, "loadAll", execute);

          hooked.attachmentStyle = true;

          execute();
        }
      }

      // 页码样式
      if (hooked.pageButtonStyle === false && pageButtonStyleEnable) {
        const execute = () => {
          if (ui.pageBtn) {
            const elements = document.querySelectorAll('[name="pageball"] A');

            elements.forEach((element) => {
              const matches = element.innerHTML.match(/\d+/);

              if (matches) {
                element.innerHTML = `&nbsp;${matches[0]}&nbsp;`;
              }
            });
          }
        };

        hookFunction(ui, "pageBtn", execute);

        hooked.pageButtonStyle = true;

        execute();
      }

      // 快捷翻页
      if (hooked.hotkeys === false && hotkeysEnable) {
        const delay = (interval) =>
          new Promise((resolve) => setTimeout(resolve, interval));

        const retry = async (fn, retriesLeft = 10, interval = 160) => {
          try {
            return await fn();
          } catch (error) {
            await delay(interval);

            if (retriesLeft > 0) {
              return await retry(fn, retriesLeft - 1, interval);
            }
          }
        };

        const execute = () => {
          document.addEventListener("keydown", ({ key, ctrlKey }) => {
            if (__PAGE) {
              const max = __PAGE[1];
              const cur = __PAGE[2];

              if (key === "ArrowLeft" && ctrlKey) {
                loadReadHidden(1);
                return;
              }

              if (key === "ArrowLeft") {
                loadReadHidden(Math.max(cur - 1, 1));
                return;
              }

              if (key === "ArrowRight" && ctrlKey) {
                loadReadHidden(max);
                return;
              }

              if (key === "ArrowRight") {
                loadReadHidden(Math.min(cur + 1, max));
                return;
              }
            }
          });
        };

        hooked.hotkeys = true;

        execute();
      }

      // 版面名称
      if (hooked.forumName === false && forumNameEnable) {
        if (ui.topicArg) {
          if (!n.doRequest || !api.indexForumList) {
            return;
          }

          class Queue {
            execute(task) {
              task(this.data).finally(() => {
                if (this.waitingQueue.length) {
                  const next = this.waitingQueue.shift();

                  this.execute(next);
                } else {
                  this.isRunning = false;
                }
              });
            }

            enqueue(task) {
              if (this.initialized === false) {
                this.initialized = true;
                this.init();
              }

              if (this.isRunning) {
                this.waitingQueue.push(task);
              } else {
                this.isRunning = true;

                this.execute(task);
              }
            }

            init() {
              this.enqueue(async () => {
                this.data = await new Promise((resolve) => {
                  try {
                    n.doRequest({
                      u: api.indexForumList(),
                      f: function (res) {
                        if (res.data) {
                          resolve(res.data[0]);
                        } else {
                          resolve({});
                        }
                      },
                    });
                  } catch (e) {
                    resolve({});
                  }
                });
              });
            }

            constructor() {
              this.waitingQueue = [];
              this.isRunning = false;

              this.initialized = false;
            }
          }

          const deepSearch = (content = {}, fid = 0) => {
            const children = Object.values(content);

            for (let i = 0; i < children.length; i += 1) {
              const item = children[i];

              if (item.fid === fid) {
                return item;
              }

              if (item.content) {
                const result = deepSearch(item.content || [], fid);

                if (result !== null) {
                  return result;
                }
              }
            }

            return null;
          };

          const queue = new Queue();

          const execute = () => {
            if (location.search.indexOf("authorid") < 0) {
              return;
            }

            ui.topicArg.data.forEach((item) => {
              const parentNode = item[1].closest(".c2");

              if (parentNode.querySelector(".titleadd2") === null) {
                const fid = item[7];

                queue.enqueue(async (data) => {
                  const result = deepSearch(data.all, parseInt(fid, 10));

                  if (result) {
                    const anchor = parentNode.querySelector(".topic_content");

                    const title = document.createElement("SPAN");

                    title.className = "titleadd2";
                    title.innerHTML = `<a href="/thread.php?fid=${fid}" class="silver">[${result.name}]</a>`;

                    if (anchor) {
                      anchor.before(title);
                    } else {
                      parentNode.append(title);
                    }
                  }
                });
              }
            });
          };

          hookFunction(ui.topicArg, "loadAll", execute);

          hooked.forumName = true;

          execute();
        }
      }

      // 抽楼检测
      if (postLossDetectionEnable) {
        const cache = {};

        const fetchData = async (key, tid, pid) => {
          if (cache[key] === undefined) {
            cache[key] = await new Promise((resolve) => {
              fetch(`/post.php?lite=js&tid=${tid}&pid=${pid}`)
                .then((res) => res.blob())
                .then((blob) => {
                  const reader = new FileReader();

                  reader.onload = () => {
                    const text = reader.result;
                    const result = JSON.parse(
                      text.replace("window.script_muti_get_var_store=", "")
                    );

                    const { error } = result;

                    if (error) {
                      resolve(error[0]);
                    } else {
                      resolve("");
                    }
                  };

                  reader.readAsText(blob, "GBK");
                })
                .catch(() => {
                  resolve("");
                });
            });
          }

          return cache[key];
        };

        if (hooked.postLossDetection === false) {
          if (ui.postArg && uid) {
            const execute = () => {
              Object.values(ui.postArg.data)
                .filter((item) => +item.pAid === uid)
                .forEach(async ({ tid, pid, pInfoC }) => {
                  const key = `${tid}#${pid}`;

                  const error = await fetchData(key, tid, pid);

                  if (error) {
                    if (pInfoC) {
                      if (pInfoC.querySelector(`[id="${key}"]`)) {
                        return;
                      }

                      const node = document.createElement("SPAN");

                      node.id = key;
                      node.className =
                        "small_colored_text_btn block_txt_c0 stxt";
                      node.style = "margin-left: 0.4em; line-height: inherit;";
                      node.innerHTML = error;

                      pInfoC.prepend(node);
                    }
                  }
                });
            };

            hookFunction(ui.postArg, "proc", execute);

            hooked.postLossDetection = true;

            execute();
          }
        }

        if (hooked.postLossDetectionTopic === false) {
          if (ui.topicArg && uid) {
            const execute = () => {
              if (location.search.indexOf(`authorid=${uid}`) < 0) {
                return;
              }

              Object.values(ui.topicArg.data).forEach(async (item) => {
                const tid = item[8];
                const pid = item[9] || 0;

                const postDate = item[12];

                if (pid && postDate) {
                  const key = `${tid}#${pid}`;

                  const error = await fetchData(key, tid, pid);

                  if (error) {
                    const parentNode = item[1].closest(".c2");

                    if (parentNode.querySelector(`[id="${key}"]`)) {
                      return;
                    }

                    const anchor = parentNode.querySelector(".topic_content");

                    const node = document.createElement("SPAN");

                    node.id = key;
                    node.className = "small_colored_text_btn block_txt_c0";
                    node.style = "float:right; line-height: inherit;";
                    node.innerHTML = error;

                    if (anchor) {
                      anchor.after(node);
                    } else {
                      parentNode.append(node);
                    }
                  }
                }
              });
            };

            hookFunction(ui.topicArg, "loadAll", execute);

            hooked.postLossDetectionTopic = true;

            execute();
          }
        }
      }
    };

    hookFunction(ui, "eval", () => {
      if (Object.values(hooked).findIndex((item) => item === false) < 0) {
        return;
      }

      hook();
    });

    hook();
  })();

  // 加载菜单项
  (() => {
    if (attachmentStyleEnable) {
      GM_registerMenuCommand("附件样式:启用", () => {
        GM_setValue(ATTACHMENT_STYLE_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("附件样式:禁用", () => {
        GM_setValue(ATTACHMENT_STYLE_ENABLE_KEY, true);
        location.reload();
      });
    }

    if (pageButtonStyleEnable) {
      GM_registerMenuCommand("页码样式:启用", () => {
        GM_setValue(PAGE_BUTTON_STYLE_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("页码样式:禁用", () => {
        GM_setValue(PAGE_BUTTON_STYLE_ENABLE_KEY, true);
        location.reload();
      });
    }

    if (hotkeysEnable) {
      GM_registerMenuCommand("快捷翻页:启用", () => {
        GM_setValue(HOTKEYS_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("快捷翻页:禁用", () => {
        GM_setValue(HOTKEYS_ENABLE_KEY, true);
        location.reload();
      });
    }

    if (forumNameEnable) {
      GM_registerMenuCommand("版面名称:启用", () => {
        GM_setValue(FORUM_NAME_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("版面名称:禁用", () => {
        GM_setValue(FORUM_NAME_ENABLE_KEY, true);
        location.reload();
      });
    }

    if (postLossDetectionEnable) {
      GM_registerMenuCommand("抽楼检测:启用", () => {
        GM_setValue(POST_LOSS_DETECTION_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("抽楼检测:禁用", () => {
        GM_setValue(POST_LOSS_DETECTION_KEY, true);
        location.reload();
      });
    }

    if (autoCheckInEnable) {
      GM_registerMenuCommand("自动签到:启用", () => {
        GM_setValue(AUTO_CHECK_IN_ENABLE_KEY, false);
        GM_setValue(AUTO_CHECK_IN_LAST_TIME_KEY, 0);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("自动签到:禁用", () => {
        GM_setValue(AUTO_CHECK_IN_ENABLE_KEY, true);
        location.reload();
      });
    }
  })();

  // 自动签到
  if (autoCheckInEnable && uid) {
    const today = new Date();

    const lastTime = new Date(autoCheckInLastTime);

    const isToday =
      lastTime.getDate() === today.getDate() &&
      lastTime.getMonth() === today.getMonth() &&
      lastTime.getFullYear() === today.getFullYear();

    if (isToday === false) {
      fetch(`/nuke.php?__lib=check_in&__act=check_in&lite=js`, {
        method: "POST",
        headers: {
          "X-User-Agent": autoCheckInUserAgent,
        },
      })
        .then((res) => res.blob())
        .then((blob) => {
          const reader = new FileReader();

          reader.onload = () => {
            const text = reader.result;
            const result = JSON.parse(
              text.replace("window.script_muti_get_var_store=", "")
            );

            const { data, error } = result;

            if (data || error) {
              alert((data || error)[0]);
            }

            GM_setValue(AUTO_CHECK_IN_LAST_TIME_KEY, today.getTime());
          };

          reader.readAsText(blob, "GBK");
        });
    }
  }
})(commonui, __NUKE, __API, __CURRENT_UID);