Initial commit
This commit is contained in:
1 file changed
+133
@@ -0,0 +1,133 @@
|
||||
// ==UserScript==
|
||||
// @name 广谈大湾网 · 今日发帖数
|
||||
// @namespace local.dww.today-threads
|
||||
// @version 1.0.0
|
||||
// @description 在论坛主题管理的主题总数右侧显示北京时间当日新增主题数(不含回复)。
|
||||
// @match https://www.dww11.com/admin.php*
|
||||
// @match https://dww11.com/admin.php*
|
||||
// @run-at document-idle
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
// 不要添加 @noframes:管理列表就在后台的 main iframe 内。
|
||||
const params = new URLSearchParams(location.search);
|
||||
if (params.get('action') !== 'threads' || params.get('operation')) return;
|
||||
|
||||
const header = [...document.querySelectorAll('#cpform th.partition')]
|
||||
.find(el => /^\s*(?:最新主题数|符合条件的主题数)\s*\d+/.test(el.textContent));
|
||||
const form = document.querySelector('#threadforum');
|
||||
if (!header || !form || document.getElementById('dww-today-threads')) return;
|
||||
|
||||
const badge = document.createElement('span');
|
||||
badge.id = 'dww-today-threads';
|
||||
badge.style.cssText = 'display:inline-block;margin-left:20px;color:#008640;white-space:nowrap;';
|
||||
const label = document.createElement('span');
|
||||
label.setAttribute('role', 'status');
|
||||
label.setAttribute('aria-live', 'polite');
|
||||
label.textContent = '今日发帖数:统计中…';
|
||||
const refresh = document.createElement('button');
|
||||
refresh.type = 'button';
|
||||
refresh.textContent = '刷新';
|
||||
refresh.style.cssText = 'margin-left:8px;padding:0;border:0;background:none;color:#2369a0;cursor:pointer;font:inherit;font-weight:normal;';
|
||||
badge.append(label, refresh);
|
||||
header.append(badge);
|
||||
|
||||
function dayRange(now = new Date()) {
|
||||
// 用 UTC 计算固定东八区日期,避免电脑时区及夏令时影响。
|
||||
const offset = 8 * 60 * 60 * 1000;
|
||||
const localDay = new Date(now.getTime() + offset).toISOString().slice(0, 10);
|
||||
const previousDay = new Date(Date.parse(localDay + 'T00:00:00Z') - 86400000)
|
||||
.toISOString().slice(0, 10);
|
||||
// Discuz 原生查询使用 dateline > starttime 且 <= endtime。
|
||||
// 从前一天最后一秒之后开始,完整涵盖今日 00:00:00 至 23:59:59。
|
||||
return [localDay, `${previousDay} 23:59:59`, `${localDay} 23:59:59`];
|
||||
}
|
||||
|
||||
async function queryCount(start, end) {
|
||||
const hash = form.querySelector('input[name="formhash"]')?.value;
|
||||
if (!hash) throw new Error('未找到后台查询凭据,请重新登录后刷新');
|
||||
|
||||
// 只提交查询字段,不读取/提交 #cpform 的批量修改、删除字段。
|
||||
// 不继承当前筛选条件,今日数始终表示全部可查询版块的新增主题。
|
||||
const body = new URLSearchParams({
|
||||
formhash: hash, searchsubmit: '1', detail: '1',
|
||||
page: '1', pp: '', perpage: '20',
|
||||
inforum: 'all', intype: '', insort: 'all',
|
||||
starttime: start, endtime: end,
|
||||
users: '', keywords: '', viewsmore: '', viewsless: '',
|
||||
repliesmore: '', repliesless: '', readpermmore: '',
|
||||
pricemore: '', noreplydays: '', savethread: '0', hidethread: '0'
|
||||
});
|
||||
const endpoint = new URL('admin.php?action=threads', location.href);
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), 25000);
|
||||
try {
|
||||
const response = await fetch(endpoint.href, {
|
||||
method: 'POST', credentials: 'same-origin', cache: 'no-store',
|
||||
body, signal: controller.signal
|
||||
});
|
||||
if (!response.ok) throw new Error(`后台返回 HTTP ${response.status}`);
|
||||
const buffer = await response.arrayBuffer();
|
||||
const charset = response.headers.get('content-type')?.match(/charset=([^;\s]+)/i)?.[1]
|
||||
|| document.characterSet || 'utf-8';
|
||||
const html = new TextDecoder(charset).decode(buffer);
|
||||
const result = new DOMParser().parseFromString(html, 'text/html');
|
||||
const summary = [...result.querySelectorAll('#cpform th.partition')]
|
||||
.map(el => el.textContent.match(/^\s*符合条件的主题数\s*([\d,]+)/))
|
||||
.find(Boolean);
|
||||
if (summary) {
|
||||
// 必须是按指定日期查询的结果,避免把默认最新主题数误报成今日数。
|
||||
const returnedStart = result.querySelector('#threadforum [name="starttime"]')?.value;
|
||||
const returnedEnd = result.querySelector('#threadforum [name="endtime"]')?.value;
|
||||
if (returnedStart !== start || returnedEnd !== end) {
|
||||
throw new Error('后台未返回指定日期条件,无法确认统计结果');
|
||||
}
|
||||
return Number(summary[1].replaceAll(',', ''));
|
||||
}
|
||||
throw new Error('未读取到统计结果,可能登录已过期或后台查询异常');
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
let busy = false;
|
||||
let lastDay = '';
|
||||
async function update() {
|
||||
if (busy) return;
|
||||
busy = true;
|
||||
refresh.disabled = true;
|
||||
const [day, start, end] = dayRange();
|
||||
label.textContent = '今日发帖数:统计中…';
|
||||
badge.style.color = '#008640';
|
||||
try {
|
||||
const count = await queryCount(start, end);
|
||||
if (dayRange()[0] !== day) {
|
||||
// 请求恰好跨过午夜,不把昨日结果标为今日。
|
||||
label.textContent = '今日发帖数:请刷新';
|
||||
lastDay = '';
|
||||
return;
|
||||
}
|
||||
lastDay = day;
|
||||
label.textContent = `今日发帖数:${count}`;
|
||||
badge.title = `北京时间 ${day} 新增主题,不含回复;以后台正常主题查询口径为准。更新于 ${new Date().toLocaleTimeString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false })}`;
|
||||
} catch (error) {
|
||||
label.textContent = '今日发帖数:读取失败';
|
||||
badge.style.color = '#c0392b';
|
||||
badge.title = error.name === 'AbortError' ? '查询超时,请点击刷新重试' : error.message;
|
||||
} finally {
|
||||
busy = false;
|
||||
refresh.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
refresh.addEventListener('click', update);
|
||||
// 每分钟一条后台只读查询;后台标签页暂停轮询。
|
||||
setInterval(() => { if (!document.hidden) update(); }, 60000);
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && lastDay !== dayRange()[0]) update();
|
||||
});
|
||||
update();
|
||||
})();
|
||||
Reference in new issue
Block a user