// ==UserScript== // @name GenGen-RMJ // @icon https://gengen.qzz.io/favicon.ico // @namespace https://gengen.qzz.io/ // @version 3.1.2.14 // @description GenGen RMJ 完整版本 // @author GenGen 队 // @run-at document-start // @match https://www.luogu.com.cn/* // @match https://atcoder.jp/contests/*/submit?RMJ=1 // @match https://codeforces.com/problemset/submit/* // @match https://codeforces.com/problemset/status?my=on // @require https://cdn.bootcdn.net/ajax/libs/html2canvas/1.4.1/html2canvas.js // @require https://cdn.bootcdn.net/ajax/libs/sweetalert2/11.23.0/sweetalert2.all.js // @require https://scriptcat.org/scripts/code/5095/GenGen-CF-RMJ-JL.user.js // @require https://scriptcat.org/scripts/code/5096/GenGen-Cloudflare-RMJ.user.js // @require https://scriptcat.org/scripts/code/5093/GenGen-AT-RMJ-JL.user.js // @require https://scriptcat.org/scripts/code/5094/GenGen-CF-RMJ.user.js // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // ==/UserScript== (function () { 'use strict'; const V = '3.1.2.14'; const D = '2026/2/6'; const I = 500; // SPA cleanup state let c = false; let u = location.href; // —————— 工具 —————— const r = fn => document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', fn) : fn(); const w = (s, cb) => { const t = setInterval(() => { const e = document.querySelector(s); if (e) { clearInterval(t); cb(e); } }, 100); }; // —————— 样式 —————— GM_addStyle(` .ghb { background: linear-gradient(120deg, #4da6ff, #2196f3); color: white !important; border: none; border-radius: 6px; padding: 4px 12px; font-weight: 700; font-style: normal; font-size: 16px; cursor: pointer; margin-right: 12px; box-shadow: 0 2px 6px rgba(0, 67, 133, 0.25); transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); height: 30px; display: inline-flex; align-items: center; justify-content: center; letter-spacing: 0.5px; } .ghb:hover { transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 67, 133, 0.35); background: linear-gradient(120deg, #3d8be6, #1976d2); } .ghb:active { transform: translateY(0); } #gp { position: fixed; width: 400px; backdrop-filter: blur(12px); background: rgba(255, 255, 255, 0.2); border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 5px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18); z-index: 2147483647; padding: 20px; font-size: 16px; display: flex; flex-direction: column; gap: 14px; opacity: 0; visibility: hidden; transform: translateY(-8px); transition: opacity 0.32s, transform 0.36s, visibility 0.32s; pointer-events: none; } #gp.v { opacity: 1; visibility: visible; transform: translateY(0); pointer-events: all; } #gp h3 { margin: 0 0 12px 0; font-size: 34px; font-weight: 700; font-style: normal; font-family: "Times New Roman", "Georgia", "SimSun", "宋体", "Songti SC", "STSong", serif; color: #1a237e; display: flex; align-items: center; justify-content: center; gap: 10px; border-bottom: 1.5px solid #e8eaf6; padding-bottom: 10px; letter-spacing: -0.5px; } #gp h3 img { width: 30px; height: 30px; border-radius: 6px; flex-shrink: 0; } #gp p { margin: 0; line-height: 1.6; color: #37474f; } #gp a { color: #1565c0; text-decoration: none; font-weight: 500; } #gp a:hover { text-decoration: underline; } .ar { display: flex; justify-content: space-between; padding: 6px 0; } .as { font-weight: 600; color: #2e7d32; } .nb { color: #c62828; font-weight: 500; } .bb { font-size: 13px; padding: 3px 8px; background: #e3f2fd; border: 1px solid #1e88e5; border-radius: 16px; cursor: pointer; color: #0d47a1; font-weight: 500; transition: all 0.2s; } .bb:hover { background: #bbdefb; transform: scale(1.05); } .sw { display: none; justify-content: center; align-items: center; min-height: 24px; width: 100%; } .m .oc { display: none !important; } .m .sw { display: flex !important; } `); // —————— 图标 —————— const ok = () => ``; const er = () => ``; const no = () => ``; // —————— 缓存 —————— let cf = new Map(), at = new Map(), lcf = '', lat = ''; function rc() { try { const a = GM_getValue('cf-submissions-cache', '[]'); const b = GM_getValue('at-submissions-cache', '[]'); if (a !== lcf) { lcf = a; cf = new Map(); if (a && a !== '[]') JSON.parse(a).forEach(s => { if (!s?.taskDisplay) return; const m = s.taskDisplay.match(/CF[A-Z0-9]+/i); if (m) cf.set(m[0].toUpperCase(), s.verdict || null); }); } if (b !== lat) { lat = b; at = new Map(); if (b && b !== '[]') JSON.parse(b).forEach(s => { if (s?.taskKey) at.set(s.taskKey.toLowerCase(), s.status || null); }); } } catch (e) {} } // —————— DOM 初始化(仅 CF/AT) —————— function ic(e) { if (e.dataset.g) return; const o = document.createElement('div'); o.className = 'oc'; while (e.firstChild) o.appendChild(e.firstChild); const s = document.createElement('div'); s.className = 'sw'; s.innerHTML = no(); e.appendChild(o); e.appendChild(s); e.dataset.g = '1'; } // —————— 更新行状态(安全版) —————— function ur(rw) { const cs = rw.children; if (cs.length < 2) return false; const c0 = cs[0], c1 = cs[1]; const m = c1.textContent.trim().match(/^(\S+)/); if (!m) return false; const p = m[1]; const isCF = /^CF[A-Z0-9]+$/i.test(p); const isAT = /^AT_[A-Za-z0-9_]+$/.test(p); const man = isCF || isAT; // —————— 非管辖题目:绝不碰 DOM —————— if (!man) { // 如果之前错误初始化了(比如开发残留),清理一次 if (c0.dataset.g) { const o = c0.querySelector('.oc'); if (o) { c0.innerHTML = o.innerHTML; // 恢复原始内容 delete c0.dataset.g; delete c0.dataset.s; } } return false; } // —————— 管辖题目:安全初始化 + 更新 —————— if (!c0.dataset.g) { // 初始化容器(仅移动节点,不 innerHTML) const o = document.createElement('div'); o.className = 'oc'; while (c0.firstChild) o.appendChild(c0.firstChild); const s = document.createElement('div'); s.className = 'sw'; s.innerHTML = no(); c0.appendChild(o); c0.appendChild(s); c0.dataset.g = '1'; } let ns = 'none', icn = no(); if (isCF) { const v = cf.get(p.toUpperCase()); if (v === 'OK') { icn = ok(); ns = 'AC'; } else if (v) { icn = er(); ns = 'WA'; } } else if (isAT) { const k = p.substring(3).toLowerCase(); const st = at.get(k); if (st === 'AC') { icn = ok(); ns = 'AC'; } else if (st) { icn = er(); ns = 'WA'; } } const os = c0.dataset.s || 'none'; const sw = c0.querySelector('.sw'); if (sw) sw.innerHTML = icn; c0.classList.add('m'); c0.dataset.s = ns; return os !== ns; } // —————— 清理函数(离开 CF/AT 时调用) —————— function cl() { document.querySelectorAll('[data-g="1"]').forEach(e => { const o = e.querySelector('.oc'); if (o) { while (o.firstChild) e.appendChild(o.firstChild); e.removeChild(o); const s = e.querySelector('.sw'); if (s) e.removeChild(s); delete e.dataset.g; delete e.dataset.s; e.classList.remove('m'); } }); } let curURL = location.href; function onNav() { const newURL = location.href; if (newURL === curURL) return; curURL = newURL; // 从 CF/AT 单题页离开? const wasCFAT = /^\/problem\/(CF|AT_)/.test(new URL.split('?')[0].replace(location.origin, '')); const nowCFAT = /^\/problem\/(CF|AT_)/.test(location.pathname); if (wasCFAT && !nowCFAT) { // 清理所有被 GenGen 修改的元素 document.querySelectorAll('[data-g="1"]').forEach(e => { const o = e.querySelector('.oc'); if (o) { e.innerHTML = o.innerHTML; delete e.dataset.g; delete e.dataset.s; } }); } } // —————— 循环更新 —————— let tid = null; function st() { if (tid) clearInterval(tid); tid = setInterval(() => { rc(); document.querySelectorAll('.row').forEach(ur); }, I); } function sp() { if (tid) { clearInterval(tid); tid = null; } } // —————— 面板 —————— let pn = null, ht = null; const sd = 200, hd = 300; function cp() { if (pn) return; pn = document.createElement('div'); pn.id = 'gp'; pn.innerHTML = `
版本:${V} (${D})
反馈:私信开发者
新特性:支持 CodeForces / AtCoder
工单进度:点击查看