自动随机浏览实验室安全文章,带可视化控制面板
// ==UserScript==
// @name UESTC 实验室安全知识·随机阅读
// @namespace https://labsafetest.uestc.edu.cn/
// @version 1.3
// @description 自动随机浏览实验室安全文章,带可视化控制面板
// @match *://labsafetest.uestc.edu.cn/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
// 避免嵌套 iframe 中运行
if (window.top !== window.self) return;
const LS_KEY = 'uestc_safe_reader_cfg';
const defaultCfg = {
running: false,
intervalMs: 60000,
jitterPct: 0.15,
autoScroll: true
};
let cfg = loadCfg();
const CATALOG_RANGE = { min: 121, max: 129 };
const BASE = 'https://labsafetest.uestc.edu.cn/redir.php';
let timers = [];
let scrollTimer = null;
init();
function init() {
console.log('[SafeReader] 初始化脚本...');
createPanel();
if (cfg.running) scheduleNext();
}
function loadCfg() {
try {
const saved = JSON.parse(localStorage.getItem(LS_KEY));
return { ...defaultCfg, ...saved };
} catch (e) {
console.warn('[SafeReader] 配置解析失败,使用默认配置', e);
return { ...defaultCfg };
}
}
function saveCfg() {
localStorage.setItem(LS_KEY, JSON.stringify(cfg));
}
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function withJitter(ms) {
const d = cfg.jitterPct * ms;
return Math.max(1000, Math.floor(ms + (Math.random() * 2 - 1) * d));
}
function pageType() {
const u = new URL(location.href);
if (u.searchParams.has('object_id')) return 'article';
if (u.searchParams.has('catalog_id')) return 'category';
return 'other';
}
function randomCategoryUrl() {
const id = randInt(CATALOG_RANGE.min, CATALOG_RANGE.max);
const u = new URL(BASE);
u.searchParams.set('catalog_id', String(id));
return u.toString();
}
function openRandomCategory() {
const url = randomCategoryUrl();
log(`跳转随机分类: ${url}`);
location.href = url;
}
function findArticleLinksInDoc(root = document) {
return Array.from(root.querySelectorAll('a[href*="redir.php"]'))
.map(a => [a, new URL(a.href, location.origin)])
.filter(([a, u]) => u.searchParams.has('object_id'))
.map(([a]) => a);
}
function openRandomArticle() {
const links = findArticleLinksInDoc();
if (links.length === 0) {
log('未找到文章链接,跳转分类页');
openRandomCategory();
return;
}
const a = links[Math.floor(Math.random() * links.length)];
log(`跳转随机文章: ${a.href}`);
location.href = a.href;
}
function scheduleNext() {
clearTimers();
if (!cfg.running) return;
const typ = pageType();
if (typ === 'category') {
timers.push(setTimeout(() => openRandomArticle(), withJitter(4000)));
} else if (typ === 'article') {
if (cfg.autoScroll) startAutoScroll();
timers.push(setTimeout(() => openRandomCategory(), withJitter(cfg.intervalMs)));
} else {
timers.push(setTimeout(() => openRandomCategory(), withJitter(3000)));
}
}
function clearTimers() {
timers.forEach(t => clearTimeout(t));
timers = [];
stopAutoScroll();
}
function startAutoScroll() {
stopAutoScroll();
let dir = 1;
scrollTimer = setInterval(() => {
window.scrollBy(0, 200 * dir);
const atBottom = window.innerHeight + window.scrollY >= document.body.scrollHeight - 2;
const atTop = window.scrollY <= 0;
if (atBottom || atTop) dir *= -1;
}, 1200 + Math.floor(Math.random() * 600));
}
function stopAutoScroll() {
if (scrollTimer) {
clearInterval(scrollTimer);
scrollTimer = null;
}
}
function log(...args) {
console.log('[SafeReader]', ...args);
updateStatus(args.join(' '));
}
function updateStatus(text) {
const el = document.getElementById('usp-status');
if (el) el.textContent = `${new Date().toLocaleTimeString()} | ${text}`;
}
function toast(msg) {
updateStatus(msg);
}
function createPanel() {
const box = document.createElement('div');
box.id = 'uestc-safe-panel';
box.innerHTML = `
<div style="
position:fixed;right:16px;bottom:16px;z-index:999999;
background:#0f172a;color:#e5e7eb;border:1px solid #334155;
padding:12px 12px 10px;border-radius:10px;font:14px/1.4 system-ui,Segoe UI,Arial;">
<div style="font-weight:700;margin-bottom:6px;">安全知识·随机阅读</div>
<div style="display:flex;gap:6px;align-items:center;margin:6px 0;">
<label>周期(ms):</label>
<input id="usp-interval" type="number" min="5000" step="1000"
style="width:110px;padding:4px;border-radius:6px;border:1px solid #475569;background:#111827;color:#e5e7eb;">
<button id="usp-save" style="padding:4px 8px;border:1px solid #475569;border-radius:6px;background:#1f2937;color:#e5e7eb;">保存</button>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin:6px 0;">
<label style="display:flex;align-items:center;gap:6px;"><input id="usp-scroll" type="checkbox">自动滚动</label>
<label style="display:flex;align-items:center;gap:6px;"><input id="usp-jitter" type="checkbox">时间抖动±15%</label>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin:8px 0;">
<button id="usp-start" style="padding:6px 10px;border:1px solid #22c55e;color:#16a34a;background:#052e16;border-radius:8px;">开始</button>
<button id="usp-stop" style="padding:6px 10px;border:1px solid #ef4444;color:#ef4444;background:#2a0b0b;border-radius:8px;">停止</button>
<button id="usp-test-cat" style="padding:6px 10px;border:1px solid #475569;background:#111827;color:#e5e7eb;border-radius:8px;">测试:开分类</button>
<button id="usp-test-article" style="padding:6px 10px;border:1px solid #475569;background:#111827;color:#e5e7eb;border-radius:8px;">测试:开文章</button>
<button id="usp-count" style="padding:6px 10px;border:1px solid #475569;background:#111827;color:#e5e7eb;border-radius:8px;">统计本页文章</button>
</div>
<div id="usp-status" style="font-size:12px;color:#a3a3a3;">已加载脚本</div>
</div>`;
document.body.appendChild(box);
document.getElementById('usp-interval').value = cfg.intervalMs;
document.getElementById('usp-scroll').checked = !!cfg.autoScroll;
document.getElementById('usp-jitter').checked = !!cfg.jitterPct;
document.getElementById('usp-save').onclick = () => {
const val = Number(document.getElementById('usp-interval').value);
if (isNaN(val) || val < 5000) { alert('间隔不得小于 5000ms'); return; }
cfg.intervalMs = val;
cfg.autoScroll = document.getElementById('usp-scroll').checked;
cfg.jitterPct = document.getElementById('usp-jitter').checked ? 0.15 : 0;
saveCfg(); toast(`保存成功:${cfg.intervalMs}ms`);
scheduleNext();
};
document.getElementById('usp-start').onclick = () => {
cfg.running = true; saveCfg(); toast('已启动'); scheduleNext();
};
document.getElementById('usp-stop').onclick = () => {
cfg.running = false; saveCfg(); toast('已停止'); clearTimers();
};
document.getElementById('usp-test-cat').onclick = () => openRandomCategory();
document.getElementById('usp-test-article').onclick = () => openRandomArticle();
document.getElementById('usp-count').onclick = () => {
const n = findArticleLinksInDoc().length;
toast(`本页找到 ${n} 个文章链接`);
};
}
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址