// ==UserScript==
// @name NGA Auto Pagerize
// @namespace https://gf.qytechs.cn/users/263018
// @version 1.5.0
// @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)";
// 临时修复系统翻页逻辑
(() => {
/////////////nouse
;(function(){
if(!XMLHttpRequest)
return
var pageStat={} ,//{页n:[贴1,2,3,...], 页n+1:[贴1,2,3,...], ...}
$ = _$, HTTP = new XMLHttpRequest(), l = location, tmp,
minp ,//当前最小页
maxp ,//当前最大页
iPo, //当前第一条
iPc,
ot = 0,
count = 1,//当前显示的总页数
progr = function(){
commonui.loadReadHidden.lock = 0
},
progt = function(v,o){
commonui.progbar(v*10,o?o:5000,v==10?progr:null)
},
cs = document.characterSet || document.defaultCharset || document.charset;
HTTP.onerror = function(e){
error('HTTP ERROR')
}
HTTP.onload = HTTP.onabout = function(e){
progt(10)
}
HTTP.onprogress = function(e){
/*
if (e.lengthComputable)
progv = (e.loaded / e.total)*prog.offsetWidth
else if(progv = this.getResponseHeader('X-NGA-CONTENT-LENGTH'))
progv = (e.loaded / progv)*prog.offsetWidth
else{
progv = e.loaded/100
if(progv>prog.offsetWidth) progv = prog.offsetWidth
}
*/
progt(3)
}
var
ifp = function(o,opt){
if(opt & 1024){
if(o.nodeName=='TBODY')
return 1
}
else
if(o.className=='forumbox postbox')
return 1
}
pr = function(txt,opt){
if(opt & 1024){
//console.log(txt)
var x = cut(txt,['<!--topicliststart-->','<!--topiclistend-->','//topicloadallstart','//topicloadallend'],0,1)
if(x.length<2)
return error('parse page error')
var y = cut(x[0],["<script type='text/javascript'>",'</script>'],0,0)
y.push(x[1])
return [
null,
x[0],//内容
y//取出内容加载脚本
]
}
var x = cut(txt,['//userinfostart','//userinfoend','<!--postliststart-->','<!--postlistend-->'],0,1)
if(x.length<2)
return error('parse page error')
return [x[0],//用户信息脚本
x[1],//内容
cut(x[1],['<script>','</script>'],0,0)//取出内容加载脚本
]
}
commonui.loadReadHidden = function(p,opt){// 页, &1替换加载指定页 &2连续加载下一页 &4连续加载上一页
if(this.loadReadHidden.lock)
return
this.loadReadHidden.lock = 1
if(!iPc){
iPc = $('m_posts_c')
if(!iPc){
iPc = $('topicrows')
ot |= 1024
}
}
opt|=ot
if(!minp){
minp = maxp = __PAGE[2]
pageStat[maxp] = []
for(var i = 0;i<iPc.childNodes.length;i++){
if(ifp(iPc.childNodes[i], opt))
pageStat[maxp].push(iPc.childNodes[i])
}
}
if(opt & 2)
p = maxp+1
else if(opt & 4)
p = minp-1
if(p<1 || (__PAGE[1]>0 && p>__PAGE[1]))
return error('page error '+p,1)
var ugo = __PAGE[0]+'&page='+p
HTTP.__rep = 0
HTTP.abort()
HTTP.open('GET', ugo)
HTTP.onreadystatechange = function () {
if (HTTP.readyState !== HTTP.DONE)
return
var all = HTTP.responseText
if (HTTP.status !== 200 || HTTP.getResponseHeader("X-NGA-CONTENT-TYPE")=='short-message') {
var c = all.match(/<!--msgcodestart-->(\d+)<!--msgcodeend-->/)
if(c && c[1]==15 && HTTP.__rep<1){
__COOKIE.setCookieInSecond('guestJs',__NOW,1200)
HTTP.__rep++
return setTimeout(function(){
HTTP.abort()
HTTP.open('GET',ugo)
HTTP.send()
},500)
}
var c = all.match(/<!--msginfostart-->(.+?)<!--msginfoend-->/)
if(c)
return error(c[1].replace(/<br\s*\/>/g,"\n").replace(/<\/?[A-Za-z]+(\s[^>]*)?>/g," ").replace(/^\s+|\s+$/g,''),2)
return error('HTTP ERROR '+HTTP.status,2);
}
progt(5)
var data = pr(all,opt)
if(commonui.eval.call(window,data[0]))
return error('parse data 0 error')
progt(6)
__PAGE[2] = p
if(opt&1){
minp = maxp = p
pageStat={}
count = 1
iPo = null
for(var i = iPc.childNodes.length-1;i>=0;i--){
//if(ifp(iPc.childNodes[i], opt) || iPc.childNodes[i].nodeName=='SCRIPT')
iPc.removeChild(iPc.childNodes[i])
}
if(history.replaceState)
history.replaceState('object or string', document.title, __PAGE[0]+'&page='+p)
}
else if(opt & 6){
count++
if(count>10){
count--
if(opt & 2)
tmp = minp, minp++
else if(opt & 4)
tmp = maxp, maxp--
for(var i = 0;i<pageStat[tmp].length;i++){
if(pageStat[tmp][i].parentNode)
pageStat[tmp][i].parentNode.removeChild(pageStat[tmp][i])
pageStat[tmp][i] = null
}
pageStat[tmp] = null
delete pageStat[tmp]
}
if(opt & 2)
maxp = p, iPo = null
else if(opt & 4)
iPo = pageStat[minp][0], minp = p
commonui.topicArg.opt |=1
//console.log('replacehis '+p)
//if(history.replaceState)
//history.replaceState('object or string', document.title, __PAGE[0]+'&page='+p)
}
progt(7)
var c = data[1].match(/\s*<tbody/) ? $('/table') : $('/span')
c.innerHTML = data[1]
pageStat[p] = []
for(var i=0;i<c.childNodes.length;i++){
if(ifp(c.childNodes[i], opt)){
pageStat[p].push(c.childNodes[i])
iPc.insertBefore(c.childNodes[i], iPo)
}
}
progt(8)
for(var i = 0;i<data[2].length;i++){
if(commonui.eval.call(window,data[2][i]))
return error('parse data 2 error')
}
var c= document.getElementsByName('pageball')
if(opt & 1){
commonui.pageBtn(c[0],{0:__PAGE[0],1:__PAGE[1],2:p,3:__PAGE[3]},16)
commonui.pageBtn(c[1],{0:__PAGE[0],1:__PAGE[1],2:p,3:__PAGE[3]},8)
}
else{
commonui.pageBtn(c[0],{0:__PAGE[0],1:__PAGE[1],2:minp,3:__PAGE[3]},4|16)
commonui.pageBtn(c[1],{0:__PAGE[0],1:__PAGE[1],2:maxp,3:__PAGE[3]},2|8)
}
progt(9)
if(window._czc)//cnzz统计
_czc.push(["_trackPageview", __PAGE[0]+'&page='+p]);
if(opt &1){
//console.log('scroll top')
scroll(0,0)
}
}
HTTP.overrideMimeType("text/html; charset="+cs);
progt(1)
HTTP.send()
}//fe
commonui.loadReadHidden.reset = function(){
tmp = null
minp = null
maxp = null
iPo = null
iPc = null
ot = 0
count = 1
}//
var error = function(e,a){
progt(10)
if((a&1)==0)
alert(e)
console.log(e)
}
var cut = function(txt,match,offset,opt){
var m,n, r=[], start = match.shift(), end = match.shift()
while(1){
m = txt.indexOf(start,offset)
if(m==-1)
break
n = txt.indexOf(end,m)
if(n==-1)
break
r.push(txt.substr(m+start.length,n-m-start.length))
offset = n+end.length
if(opt&1){
var start = match.shift(), end = match.shift()
if(!start || !end)
break
}
}
return r
}//fe
})();
})();
// 加载脚本
(() => {
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 THREAD_MAX_PAGE = 500;
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();
}
if (__PAGE) {
const max = __PAGE[1];
const cur = __PAGE[2];
if (location.pathname === "/thread.php") {
if (p > THREAD_MAX_PAGE) {
return;
}
if (p === 0 && opt === 2 && cur === THREAD_MAX_PAGE) {
return;
}
}
if (p < 1 && opt === 1) {
return;
}
if (p > max && max > 0) {
p = max;
}
if (p === cur) {
return;
}
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 = ` ${matches[0]} `;
}
});
}
};
hookFunction(ui, "pageBtn", execute);
hooked.pageButtonStyle = true;
execute();
}
// 快捷翻页
if (hooked.hotkeys === false && hotkeysEnable) {
const execute = () => {
document.addEventListener("keydown", ({ key, ctrlKey }) => {
if (__PAGE) {
const max = __PAGE[1];
const cur = __PAGE[2];
const activeElement = document.activeElement;
if (activeElement === null || activeElement.tagName !== "BODY") {
return;
}
if (key === "ArrowLeft" && ctrlKey) {
loadReadHidden(1);
return;
}
if (key === "ArrowRight" && ctrlKey) {
loadReadHidden(max);
return;
}
if (key === "ArrowLeft") {
document.getElementById("m_pbtntop").scrollIntoView();
loadReadHidden(0, 4);
return;
}
if (key === "ArrowRight") {
document.getElementById("m_pbtnbtm").scrollIntoView();
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);