洛谷卷网监视器
C++
// ==UserScript==
// @name         卷王监视器(时间筛选版)
// @namespace    https://greasyfork.org/zh-CN/users/1124544-sz-jinzikai
// @version      2.3
// @description  监控指定用户在特定时间内的做题记录
// @author       sz_jinzikai
// @match        https://www.luogu.com.cn/123
// @icon         https://cdn.luogu.com.cn/upload/usericon/743373.png
// @grant        none
// ==/UserScript==

var userlist = [1049425,886055, 1023865, 1081095, 643743, 1057868, 1055731, 1098988, 1059683, 1054396, 903392, 1472024, 1058204];
var displaylist = ["ZHL_myself","LBY", "XZY", "QMR", "WYT", "HSH", "GYC", "HJM", "QAS", "ZYM", "YSJ", "ZGR", "ZXY"];
var pages = 2; // 检查页数
var cd = 10000; // 检查间隔

// 时间选项配置(单位:毫秒)
var timeOptions = {
    "24小时": 24 * 60 * 60 * 1000,
    "一周": 7 * 24 * 60 * 60 * 1000,
    "一个月": 30 * 24 * 60 * 60 * 1000,
    "全部": 0
};

// 当前选择的时间范围
var currentTimeRange = timeOptions["24小时"];
var lastCheckTime = Date.now() - currentTimeRange;

function onSearch() {
    var storeId = document.getElementById('store');
    var rowsLength = storeId.rows.length;
    var key = document.getElementById('key').value;
    var timeFilter = document.getElementById('timeFilter').value;

    for (var i = 1; i < rowsLength; i++) {
        var searchText = storeId.rows[i].cells[0].innerHTML;
        var recordTime = parseInt(storeId.rows[i].cells[3].getAttribute('data-timestamp'));
        var now = Date.now();

        var showRow = true;

        // 用户名筛选
        if (key != "*" && !searchText.match(key)) {
            showRow = false;
        }

        // 时间筛选
        if (timeFilter != "全部" && timeOptions[timeFilter] > 0) {
            if (now - recordTime > timeOptions[timeFilter]) {
                showRow = false;
            }
        }

        storeId.rows[i].style.display = showRow ? '' : 'none';
    }
}

function changeTimeRange() {
    var timeFilter = document.getElementById('timeFilter');
    currentTimeRange = timeOptions[timeFilter.value];
    lastCheckTime = Date.now() - currentTimeRange;

    // 重新筛选显示的内容
    onSearch();
}

// 创建界面
var str = '<div style="margin-bottom: 10px;">';
str += '<select onchange="onSearch()" name="key" id="key" style="margin-right: 10px; padding: 5px;"><option value="*">全部用户</option>';
for (var i = 0; i < userlist.length; i++) {
    str += '<option value="' + userlist[i] + '">' + displaylist[i] + '</option>';
}
str += '</select>';

str += '<select onchange="changeTimeRange()" name="timeFilter" id="timeFilter" style="padding: 5px;">';
for (var key in timeOptions) {
    str += '<option value="' + key + '"' + (key === "24小时" ? ' selected' : '') + '>' + key + '</option>';
}
str += '</select>';
str += '</div>';

str += '<table id="store" style="white-space: nowrap; width: 100%; border-collapse: collapse;">';
str += '<tr style="background-color: #f2f2f2;">';
str += '<th style="padding: 8px; border: 1px solid #ddd;">用户</th>';
str += '<th style="padding: 8px; border: 1px solid #ddd;">题号</th>';
str += '<th style="padding: 8px; border: 1px solid #ddd;">标题</th>';
str += '<th style="padding: 8px; border: 1px solid #ddd;">提交时间</th>';
str += '</tr></table>';

document.body.innerHTML = "<h1 style='text-align:center;color:red;font-family:Microsoft Yahei'>卷王监视器 2.3</h1>";
document.body.innerHTML += "<p style='text-align:center;font-family:FangSong'>运筹首页之中,偷袭千里之外😜……时间范围可筛选</p><hr>" + str;

var colors = ['rgb(191, 191, 191)', 'rgb(254, 76, 97)', 'rgb(243, 156, 17)', 'rgb(255, 193, 22)', 'rgb(82, 196, 26)', 'rgb(52, 152, 219)', 'rgb(157, 61, 207)', 'rgb(14, 29, 105)'];
var name = "灰红橙黄绿蓝紫黑";
var lst = Array(userlist.length).fill(0);
var cnt = 0;

function formatTime(timestamp) {
    var date = new Date(timestamp * 1000);
    var now = new Date();
    var diff = now - date;

    // 如果是今天
    if (date.toDateString() === now.toDateString()) {
        return date.getHours().toString().padStart(2, '0') + ':' +
               date.getMinutes().toString().padStart(2, '0');
    }
    // 如果是今年
    else if (date.getFullYear() === now.getFullYear()) {
        return (date.getMonth() + 1) + '-' + date.getDate();
    }
    // 其他
    else {
        return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
    }
}

function PARSE(First = false) {
    cnt = (cnt + 1) % userlist.length;
    var user = userlist[cnt];

    for (var Pagenum = 1; Pagenum <= pages; Pagenum++) {
        var httpRequest = new XMLHttpRequest();
        httpRequest.open('GET', 'https://www.luogu.com.cn/record/list?user=' + user + '&page=' + Pagenum, false);
        httpRequest.send();

        if (httpRequest.readyState == 4 && httpRequest.status == 200) {
            var content = httpRequest.responseText;
            var patten = /decodeURIComponent\(".*?"\)/;
            content = patten.exec(content)[0];
            content = content.substr(20, content.length - 22);
            content = JSON.parse(decodeURIComponent(content));

            for (var i = Math.min(content.currentData.records.result.length - 1, 19); i >= 0; i--) {
                var record = content.currentData.records.result[i];

                // 只处理AC记录
                if (record.status == 12 && record.id > lst[cnt]) {
                    var timestamp = record.submitTime;
                    var now = Date.now();
                    var recordTime = timestamp * 1000;

                    // 时间筛选:只显示在选定时间范围内的记录
                    if (currentTimeRange > 0 && (now - recordTime) > currentTimeRange) {
                        continue;
                    }

                    var prob = record.problem;
                    var col = colors[prob.difficulty];
                    var pid = prob.pid;
                    var title = prob.title;

                    // 添加记录到表格
                    document.getElementById('store').innerHTML +=
                        '<tr>' +
                        '<td style="padding: 8px; border: 1px solid #ddd;">' + displaylist[cnt] + '</td>' +
                        '<td style="padding: 8px; border: 1px solid #ddd;">' + pid + '</td>' +
                        '<td style="padding: 8px; border: 1px solid #ddd;">' +
                        '<a style="color:' + col + '; text-decoration: none;" href="https://www.luogu.com.cn/problem/' + pid + '" target="_blank">' +
                        title + '</a></td>' +
                        '<td style="padding: 8px; border: 1px solid #ddd;" data-timestamp="' + timestamp + '">' +
                        formatTime(timestamp) + '</td>' +
                        '</tr>';

                    // 如果不是初始化阶段,显示通知
                    if (!First) {
                        alert(displaylist[cnt] + " 刚刚卷了" + name[prob.difficulty] + "题 " + pid + " " + title);
                    }

                    lst[cnt] = record.id;
                }
            }
        }
    }
}

// 初始化:获取所有记录
for (var i = 0; i < userlist.length; i++) {
    cnt = i;
    PARSE(true);
}

// 设置定时器定期检查
window.setInterval(function() {
    PARSE(false);
}, cd);
洛谷卷网监视器加强
C++
// ==UserScript==
// @name         卷王监视器(时间筛选版)
// @namespace    https://greasyfork.org/zh-CN/users/1124544-sz-jinzikai
// @version      2.8
// @description  监控指定用户在特定时间内的做题记录(异步、可排序、不折叠、难度筛选、去重按钮)
// @author       sz_jinzikai
// @match        https://www.luogu.com.cn/123
// @icon         https://cdn.luogu.com.cn/upload/usericon/743373.png
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // ---------- 配置 ----------
    const userlist = [1049425, 886055, 1023865, 1081095, 643743, 1057868, 1055731, 1098988, 1059683, 1054396, 903392, 1472024, 1058204];
    const displaylist = ["ZHL_myself", "LBY", "XZY", "QMR", "WYT", "HSH", "GYC", "HJM", "QAS", "ZYM", "YSJ", "ZGR", "ZXY"];
    const pages = 1;          // 检查页数
    const cd = 3000;          // 轮询间隔(毫秒)

    // 时间选项:-1 今天内,其他固定毫秒数
    const timeOptions = {
        "今天内": -1,
        "24小时": 24 * 60 * 60 * 1000,
        "一周": 7 * 24 * 60 * 60 * 1000,
        "一个月": 30 * 24 * 60 * 60 * 1000,
        "全部": 0
    };

    // 难度选项
    const difficultyOptions = [
        { name: "全部难度", value: -1 },
        { name: "灰", value: 0 },
        { name: "红", value: 1 },
        { name: "橙", value: 2 },
        { name: "黄", value: 3 },
        { name: "绿", value: 4 },
        { name: "蓝", value: 5 },
        { name: "紫", value: 6 },
        { name: "黑", value: 7 }
    ];

    let currentTimeRange = timeOptions["今天内"];
    let lastRecordId = Array(userlist.length).fill(0);

    const colors = ['rgb(191,191,191)', 'rgb(254,76,97)', 'rgb(243,156,17)', 'rgb(255,193,22)', 'rgb(82,196,26)', 'rgb(52,152,219)', 'rgb(157,61,207)', 'rgb(14,29,105)'];
    const difficultyNames = "灰红橙黄绿蓝紫黑";

    // ---------- 辅助函数 ----------
    function formatTime(timestamp) {
        const date = new Date(timestamp * 1000);
        const now = new Date();
        if (date.toDateString() === now.toDateString()) {
            return date.getHours().toString().padStart(2, '0') + ':' +
                   date.getMinutes().toString().padStart(2, '0');
        } else if (date.getFullYear() === now.getFullYear()) {
            return (date.getMonth() + 1) + '-' + date.getDate();
        } else {
            return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
        }
    }

    function getTodayMidnight() {
        const d = new Date();
        d.setHours(0, 0, 0, 0);
        return d.getTime();
    }

    function isInTimeRange(recordTimeMs) {
        if (currentTimeRange === -1) {
            return recordTimeMs >= getTodayMidnight();
        } else if (currentTimeRange === 0) {
            return true;
        } else {
            return (Date.now() - recordTimeMs) <= currentTimeRange;
        }
    }

    // ---------- 异步请求单个用户记录 ----------
    function fetchUserRecords(userId, page = 1) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('GET', `https://www.luogu.com.cn/record/list?user=${userId}&page=${page}`, true);
            xhr.withCredentials = true;
            xhr.onload = () => {
                if (xhr.status === 200) {
                    try {
                        let raw = xhr.responseText;
                        const match = /decodeURIComponent\(".*?"\)/.exec(raw);
                        if (!match) return resolve([]);
                        let encoded = match[0];
                        encoded = encoded.substr(20, encoded.length - 22);
                        const data = JSON.parse(decodeURIComponent(encoded));
                        const records = data.currentData.records.result || [];
                        resolve(records);
                    } catch (e) {
                        reject(e);
                    }
                } else {
                    reject(new Error(`HTTP ${xhr.status}`));
                }
            };
            xhr.onerror = reject;
            xhr.send();
        });
    }

    // ---------- 处理新记录并添加到表格 ----------
    async function processNewRecords(userId, userName, userIdx, isFirstRun = false) {
        try {
            const records = await fetchUserRecords(userId, 1);
            for (let i = Math.min(records.length - 1, 19); i >= 0; i--) {
                const rec = records[i];
                if (rec.status === 12 && rec.id > lastRecordId[userIdx]) {
                    const submitTimeMs = rec.submitTime * 1000;
                    if (!isInTimeRange(submitTimeMs)) continue;

                    const tbody = document.getElementById('recordTableBody');
                    const row = tbody.insertRow(0);
                    row.setAttribute('data-user-idx', userIdx);
                    row.setAttribute('data-timestamp', rec.submitTime);
                    row.setAttribute('data-difficulty', rec.problem.difficulty);
                    row.innerHTML = `
                        <td class="user-cell">${userName}</td>
                        <td>${rec.problem.pid}</td>
                        <td><a style="color:${colors[rec.problem.difficulty]}" href="https://www.luogu.com.cn/problem/${rec.problem.pid}" target="_blank">${rec.problem.title}</a></td>
                        <td class="time-cell" data-timestamp="${rec.submitTime}">${formatTime(rec.submitTime)}</td>
                    `;

                    lastRecordId[userIdx] = rec.id;

                    if (!isFirstRun) {
                        alert(`${userName} 刚刚卷了 ${difficultyNames[rec.problem.difficulty]} 题 ${rec.problem.pid} ${rec.problem.title}`);
                    }
                }
            }
        } catch (e) {
            console.error(`获取用户 ${userName} 记录失败`, e);
        }
    }

    // ---------- 初始化加载所有用户记录(并发) ----------
    async function loadAllInitial() {
        const tbody = document.getElementById('recordTableBody');
        tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding:30px;">⏳ 加载中,请稍候...</td></tr>';

        const promises = userlist.map((id, idx) => processNewRecords(id, displaylist[idx], idx, true));
        await Promise.all(promises);

        filterTable();
    }

    // ---------- 轮询 ----------
    let pollIndex = 0;
    async function pollNextUser() {
        const idx = pollIndex % userlist.length;
        pollIndex++;
        await processNewRecords(userlist[idx], displaylist[idx], idx, false);
        filterTable();
    }

    // ---------- 筛选表格行(用户、时间、难度) ----------
    function filterTable() {
        const userSelect = document.getElementById('userSelect');
        const timeSelect = document.getElementById('timeSelect');
        const difficultySelect = document.getElementById('difficultySelect');
        const difficultyAboveCheck = document.getElementById('difficultyAbove');

        const selectedUser = userSelect.value;
        const selectedTimeVal = parseInt(timeSelect.value, 10);
        const selectedDiff = parseInt(difficultySelect.value, 10);
        const isAbove = difficultyAboveCheck.checked;

        const tbody = document.getElementById('recordTableBody');
        const rows = tbody.getElementsByTagName('tr');

        for (let row of rows) {
            if (row.cells.length === 1 && row.cells[0].colSpan === 4) continue;

            const userCell = row.cells[0].innerText;
            const timestamp = parseInt(row.cells[3].getAttribute('data-timestamp'), 10) * 1000;
            const difficulty = parseInt(row.getAttribute('data-difficulty'), 10);

            let show = true;

            if (selectedUser !== '*') {
                const selectedIdx = userlist.indexOf(parseInt(selectedUser));
                if (selectedIdx === -1 || userCell !== displaylist[selectedIdx]) show = false;
            }

            if (show && selectedTimeVal !== 0) {
                if (selectedTimeVal === -1) {
                    if (timestamp < getTodayMidnight()) show = false;
                } else {
                    if (Date.now() - timestamp > selectedTimeVal) show = false;
                }
            }

            if (show && selectedDiff !== -1) {
                if (isAbove) {
                    if (difficulty < selectedDiff) show = false;
                } else {
                    if (difficulty !== selectedDiff) show = false;
                }
            }

            row.style.display = show ? '' : 'none';
        }
    }

    // ---------- 排序 ----------
    function sortTableByUserAndTime() {
        const tbody = document.getElementById('recordTableBody');
        const rows = Array.from(tbody.getElementsByTagName('tr'));
        const dataRows = rows.filter(r => r.cells.length === 4 && r.cells[0].colSpan !== 4);

        const rowData = dataRows.map(row => {
            const userCell = row.cells[0].innerText;
            const userIdx = displaylist.indexOf(userCell);
            const timestamp = parseInt(row.cells[3].getAttribute('data-timestamp'), 10);
            return { row, userIdx, timestamp };
        });

        rowData.sort((a, b) => {
            if (a.userIdx !== b.userIdx) return a.userIdx - b.userIdx;
            return a.timestamp - b.timestamp;
        });

        tbody.innerHTML = '';
        rowData.forEach(item => tbody.appendChild(item.row));
        filterTable();
    }

    // ---------- 去重:同用户同题保留最新一条(时间戳最大)----------
    function deduplicateTable() {
        const tbody = document.getElementById('recordTableBody');
        const rows = Array.from(tbody.getElementsByTagName('tr'));
        const dataRows = rows.filter(r => r.cells.length === 4 && r.cells[0].colSpan !== 4);

        // 使用 Map 记录每个用户+题号的最佳行(时间戳最大)
        const bestRowMap = new Map(); // key: `${user}_${pid}`

        dataRows.forEach(row => {
            const user = row.cells[0].innerText;
            const pid = row.cells[1].innerText;
            const timestamp = parseInt(row.cells[3].getAttribute('data-timestamp'), 10); // 秒
            const key = `${user}_${pid}`;

            if (!bestRowMap.has(key) || timestamp > bestRowMap.get(key).timestamp) {
                bestRowMap.set(key, { row, timestamp });
            }
        });

        // 删除所有不在 bestRowMap 中的行
        dataRows.forEach(row => {
            const user = row.cells[0].innerText;
            const pid = row.cells[1].innerText;
            const key = `${user}_${pid}`;
            if (bestRowMap.get(key).row !== row) {
                row.remove();
            }
        });

        // 重新应用筛选(可能因删除行而需要更新显示)
        filterTable();
    }

    // ---------- 构建UI ----------
    function buildUI() {
        const style = document.createElement('style');
        style.textContent = `
            * { box-sizing: border-box; }
            body {
                font-family: 'Inter', 'Microsoft YaHei', sans-serif;
                background: linear-gradient(145deg, #f0f4f8 0%, #e6ecf5 100%);
                margin: 0;
                min-height: 100vh;
                padding: 24px;
            }
            .monitor-card {
                max-width: 1400px;
                margin: 0 auto;
                background: rgba(255,255,255,0.8);
                backdrop-filter: blur(10px);
                border-radius: 28px;
                box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
                padding: 28px 32px;
                border: 1px solid rgba(255,255,255,0.5);
            }
            h1 {
                margin: 0 0 4px 0;
                font-size: 32px;
                font-weight: 700;
                background: linear-gradient(135deg, #c44536, #e67e22);
                -webkit-background-clip: text;
                -webkit-text-fill-color: transparent;
                letter-spacing: -0.5px;
            }
            .subhead {
                display: flex;
                justify-content: space-between;
                align-items: center;
                border-bottom: 2px solid rgba(0,0,0,0.05);
                padding-bottom: 18px;
                margin-bottom: 20px;
                color: #4a5568;
            }
            .subhead p {
                margin: 0;
                font-style: italic;
                font-size: 15px;
            }
            .toolbar {
                display: flex;
                flex-wrap: wrap;
                gap: 12px;
                margin-bottom: 24px;
                background: white;
                padding: 16px 20px;
                border-radius: 60px;
                box-shadow: 0 4px 10px rgba(0,0,0,0.03);
                align-items: center;
            }
            .toolbar select, .toolbar button {
                padding: 8px 20px;
                border: 1px solid #dde7f0;
                border-radius: 40px;
                background: white;
                font-size: 14px;
                font-weight: 500;
                color: #1e293b;
                cursor: pointer;
                transition: all 0.2s;
                outline: none;
            }
            .toolbar select {
                min-width: 120px;
                background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234b5563' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
                background-repeat: no-repeat;
                background-position: right 12px center;
                appearance: none;
            }
            .toolbar select:hover {
                border-color: #94a3b8;
                background-color: #f8fafc;
            }
            .toolbar button {
                background: linear-gradient(145deg, #ffffff, #f1f5f9);
                border: 1px solid #cbd5e1;
                display: inline-flex;
                align-items: center;
                gap: 6px;
                box-shadow: 0 2px 4px rgba(0,0,0,0.02);
            }
            .toolbar button:hover {
                background: #e2e8f0;
                border-color: #94a3b8;
                transform: translateY(-1px);
            }
            .difficulty-wrapper {
                display: flex;
                align-items: center;
                gap: 8px;
                background: #f8fafc;
                padding: 4px 12px 4px 8px;
                border-radius: 40px;
                border: 1px solid #dde7f0;
            }
            .difficulty-wrapper select {
                min-width: 90px;
                border: none;
                background: transparent;
                padding-left: 4px;
            }
            .difficulty-wrapper label {
                display: flex;
                align-items: center;
                gap: 4px;
                font-size: 14px;
                color: #475569;
                cursor: pointer;
                white-space: nowrap;
            }
            .difficulty-wrapper input[type="checkbox"] {
                width: 16px;
                height: 16px;
                cursor: pointer;
                accent-color: #4ecdc4;
            }
            .table-container {
                background: white;
                border-radius: 20px;
                overflow: hidden;
                box-shadow: 0 8px 20px rgba(0,0,0,0.04);
                border: 1px solid #eef2f6;
            }
            table {
                width: 100%;
                border-collapse: collapse;
                table-layout: fixed;
            }
            th {
                background: #f8fafd;
                padding: 16px 12px;
                font-weight: 600;
                font-size: 13px;
                text-transform: uppercase;
                letter-spacing: 0.8px;
                color: #475569;
                border-bottom: 2px solid #e2e8f0;
            }
            td {
                padding: 14px 12px;
                border-bottom: 1px solid #edf2f7;
                font-size: 14px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            tbody tr:hover {
                background: #f1f5f9;
            }
            tbody tr:nth-child(even) {
                background-color: #fafcff;
            }
            .user-cell {
                font-weight: 600;
                color: #2563eb;
            }
            .time-cell {
                color: #64748b;
                font-family: 'JetBrains Mono', monospace;
            }
            td a {
                text-decoration: none;
                font-weight: 500;
            }
            td a:hover {
                text-decoration: underline;
            }
            .footer {
                text-align: right;
                margin-top: 16px;
                font-size: 13px;
                color: #94a3b8;
            }
        `;
        document.head.appendChild(style);

        const container = document.createElement('div');
        container.className = 'monitor-card';
        container.innerHTML = `
            <h1>📈 卷王监视器 2.8</h1>
            <div class="subhead">
                <p>⚡ 运筹首页之中,偷袭千里之外 · 默认今日内 · 难度筛选 · 去重按钮</p>
                <span style="font-size:13px; background:#eef2ff; padding:4px 12px; border-radius:40px; color:#2563eb;">${userlist.length} 位卷王</span>
            </div>

            <div class="toolbar">
                <select id="userSelect">
                    <option value="*">👥 全部用户</option>
                    ${userlist.map((id, idx) => `<option value="${id}">👤 ${displaylist[idx]}</option>`).join('')}
                </select>
                <select id="timeSelect">
                    ${Object.entries(timeOptions).map(([label, value]) =>
                        `<option value="${value}" ${label === '今天内' ? 'selected' : ''}>⏱️ ${label}</option>`
                    ).join('')}
                </select>

                <div class="difficulty-wrapper">
                    <select id="difficultySelect">
                        ${difficultyOptions.map(opt =>
                            `<option value="${opt.value}">🎯 ${opt.name}</option>`
                        ).join('')}
                    </select>
                    <label>
                        <input type="checkbox" id="difficultyAbove"> 及以上
                    </label>
                </div>

                <button id="sortBtn"><span>🔀</span> 排序</button>
                <button id="dedupBtn"><span>🧹</span> 去重(同人同题)</button>
            </div>

            <div class="table-container">
                <table>
                    <thead>
                        <tr><th>用户</th><th>题号</th><th>标题</th><th>提交时间</th></tr>
                    </thead>
                    <tbody id="recordTableBody">
                        <tr><td colspan="4" style="text-align:center; padding:40px;">⏳ 初始化数据中...</td></tr>
                    </tbody>
                </table>
            </div>
            <div class="footer">✨ 每 3 秒自动检查新记录 · 点击按钮手动整理</div>
        `;

        document.body.innerHTML = '';
        document.body.appendChild(container);
    }

    // ---------- 事件绑定 ----------
    function bindEvents() {
        document.getElementById('userSelect').addEventListener('change', filterTable);
        document.getElementById('timeSelect').addEventListener('change', function(e) {
            currentTimeRange = parseInt(e.target.value, 10);
            filterTable();
        });
        document.getElementById('difficultySelect').addEventListener('change', filterTable);
        document.getElementById('difficultyAbove').addEventListener('change', filterTable);
        document.getElementById('sortBtn').addEventListener('click', sortTableByUserAndTime);
        document.getElementById('dedupBtn').addEventListener('click', deduplicateTable);
    }

    // ---------- 启动 ----------
    (async function main() {
        buildUI();
        bindEvents();
        await loadAllInitial();
        setInterval(pollNextUser, cd);
    })();
})();
洛谷复制专栏源码
C++
// ==UserScript==
// @name         luogu-复制专栏源码
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  luogu 复制专栏源码
// @author       JiaY19
// @match        https://www.luogu.com/article/*
// @match        https://www.luogu.com.cn/article/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=luogu.com.cn
// @grant        none
// @license      MIT
// @downloadURL https://update.greasyfork.org/scripts/489299/luogu-%E5%A4%8D%E5%88%B6%E4%B8%93%E6%A0%8F%E6%BA%90%E7%A0%81.user.js
// @updateURL https://update.greasyfork.org/scripts/489299/luogu-%E5%A4%8D%E5%88%B6%E4%B8%93%E6%A0%8F%E6%BA%90%E7%A0%81.meta.js
// ==/UserScript==

(function () {
  'use strict';
  const url = window.location.href;
  async function clickBotton_blog() {
    var x = document.querySelector("#lentille-context").text
    var y = JSON.parse(x)
    var z = y.data.article.content
    navigator.clipboard.writeText(z);
  };
  if (url.includes("article")) {
    document.onkeydown = function (e) {
      if (e.keyCode == 67 && e.ctrlKey && e.altKey) {
        clickBotton_blog();
        alert("复制成功!");
      }
    }
  }
  // Your code here...
})();

bbc 卷网监视器
C++
// ==UserScript==
// @name         卷王监视器 - BBC OJ 最终版(弹窗提示+用户排序)
// @namespace    https://greasyfork.org/zh-CN/users/1124544-sz_jinzikai
// @version      2.9-bbcoj-final
// @description  监控BBC OnlineJudge用户AC记录(异步轮询,颜色取自oiRankScore,弹窗提示,可手动按用户排序)
// @author       sz_jinzikai
// @match        http://bbcoj.cn/juan
// @grant        none
// ==/UserScript==

// ========== 配置区域 ==========
var userlist = ["c23赵浩霖", "Asafish的MoonCake", "C23薛志渝", "Believe_in_Dreams", "qianqing", "Junly_JYukio", "iChen", "hjm0703", "C23钱安崧", "C23邹艺萌", "T7_Daniel", "C24朱冠如", "昕木"];
var displaylist = ["ZHL_myself","LBY", "XZY", "QMR", "WYT", "HSH", "GYC", "HJM", "QAS", "ZYM", "YSJ", "ZGR", "ZXY"];
var pages = 2;      // 检查页数
var cd = 5000;      // 每个用户处理间隔(毫秒)
// ========== 配置结束 ==========

// 时间选项(默认24小时)
var timeOptions = {
    "24小时": 24 * 60 * 60 * 1000,
    "一周": 7 * 24 * 60 * 60 * 1000,
    "一个月": 30 * 24 * 60 * 60 * 1000,
    "全部": 0
};
var currentTimeRange = timeOptions["24小时"];

// 颜色映射(索引0~7:灰红橙黄绿蓝紫黑)
var colors = ['rgb(191,191,191)', 'rgb(254,76,97)', 'rgb(243,156,17)', 'rgb(255,193,22)', 'rgb(82,196,26)', 'rgb(52,152,219)', 'rgb(157,61,207)', 'rgb(14,29,105)'];
var difficultyNames = ["灰","红","橙","黄","绿","蓝","紫","黑"]; // 用于弹窗

// 根据oiRankScore获取难度索引(服务器存储为 ×10 后的整数,如10,100,200...)
function getDifficultyIndex(oiRankScore) {
    if (oiRankScore === undefined || oiRankScore === null) return 0;
    switch (oiRankScore) {
        case 10:   return 0; // 暂无评定 (0.1分)
        case 100:  return 1; // 入门 (1分)
        case 200:  return 2; // 普及- (2分)
        case 300:  return 3; // 普及/提高- (3分)
        case 500:  return 4; // 普及+/提高 (5分)
        case 700:  return 5; // 提高+/省选- (7分)
        case 1000: return 6; // 省选/NOI- (10分)
        case 2000: return 7; // NOI/NOI+/CTSC (20分)
        default:   return 0;
    }
}

// 记录每个用户的最新提交ID
var lst = Array(userlist.length).fill(0);

// ---------- 排序功能 ----------
function sortTableByUser() {
    var table = document.getElementById('store');
    var rows = Array.from(table.rows).slice(1); // 跳过表头
    // 按照 displaylist 顺序排序
    rows.sort(function(a, b) {
        var userA = a.cells[0].innerText;
        var userB = b.cells[0].innerText;
        var indexA = displaylist.indexOf(userA);
        var indexB = displaylist.indexOf(userB);
        // 如果找不到(理论上不会),放在最后
        if (indexA === -1) indexA = displaylist.length;
        if (indexB === -1) indexB = displaylist.length;
        return indexA - indexB;
    });
    // 重新添加行
    for (var i = 0; i < rows.length; i++) {
        table.appendChild(rows[i]);
    }
}

// ---------- 美化样式 ----------
function injectStyles() {
    var style = document.createElement('style');
    style.textContent = `
        body {
            font-family: 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f7fa;
        }
        h1 {
            text-align: center;
            background: linear-gradient(135deg, #ff6b6b, #4ecdc4);
            color: white;
            padding: 15px 0;
            border-radius: 50px;
            box-shadow: 0 4px 10px rgba(0,0,0,0.1);
            font-weight: 400;
            letter-spacing: 2px;
            margin-top: 0;
        }
        .subtitle {
            text-align: center;
            color: #666;
            font-style: italic;
            margin: 5px 0 20px;
        }
        hr {
            border: none;
            height: 1px;
            background: linear-gradient(to right, transparent, #ccc, transparent);
            margin: 20px 0;
        }
        .filter-bar {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            justify-content: center;
            flex-wrap: wrap;
        }
        select, button {
            padding: 8px 16px;
            border: 2px solid #e0e0e0;
            border-radius: 30px;
            font-size: 14px;
            background-color: white;
            cursor: pointer;
            transition: all 0.3s;
            outline: none;
            min-width: 120px;
        }
        select:hover, button:hover {
            border-color: #4ecdc4;
            box-shadow: 0 2px 8px rgba(78, 205, 196, 0.2);
        }
        select:focus, button:focus {
            border-color: #ff6b6b;
        }
        button {
            background: #4ecdc4;
            color: white;
            border-color: #4ecdc4;
            font-weight: 500;
        }
        button:hover {
            background: #45b7ae;
        }
        table {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
            background-color: white;
            border-radius: 16px;
            overflow: hidden;
            box-shadow: 0 6px 20px rgba(0,0,0,0.08);
        }
        th {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            font-weight: 500;
            padding: 14px 8px;
            font-size: 16px;
            letter-spacing: 0.5px;
        }
        td {
            padding: 12px 8px;
            border-bottom: 1px solid #f0f0f0;
            color: #333;
            font-size: 14px;
        }
        tr:last-child td {
            border-bottom: none;
        }
        tr:nth-child(even) {
            background-color: #f9f9f9;
        }
        tr:hover {
            background-color: #f1f1f1;
            transition: background-color 0.2s;
        }
        a {
            text-decoration: none;
            font-weight: 500;
            transition: opacity 0.2s;
        }
        a:hover {
            opacity: 0.8;
            text-decoration: underline;
        }
        td:last-child {
            font-family: 'Consolas', monospace;
            color: #888;
        }
        td:nth-child(2) {
            font-family: 'Consolas', monospace;
            font-weight: 600;
        }
    `;
    document.head.appendChild(style);
}

// ---------- 界面构建 ----------
function buildUI() {
    injectStyles();

    var str = '<h1>📊 BBC 卷王监视器 2.9</h1>';
    str += '<p class="subtitle">异步轮巡 · 颜色取自 oiRankScore · 弹窗提示 · 可手动排序</p><hr>';
    str += '<div class="filter-bar">';
    str += '<select onchange="onSearch()" name="key" id="key">';
    str += '<option value="*">👥 全部用户</option>';
    for (var i = 0; i < userlist.length; i++) {
        str += '<option value="' + userlist[i] + '">' + displaylist[i] + '</option>';
    }
    str += '</select>';
    str += '<select onchange="changeTimeRange()" name="timeFilter" id="timeFilter">';
    for (var key in timeOptions) {
        str += '<option value="' + key + '"' + (key === "24小时" ? ' selected' : '') + '>' + key + '</option>';
    }
    str += '</select>';
    str += '<button onclick="sortTableByUser()">🔤 按用户排序</button>';
    str += '</div>';
    str += '<table id="store">';
    str += '<tr><th>用户</th><th>题号</th><th>标题</th><th>提交时间</th></tr>';
    str += '</table>';

    document.body.innerHTML = str;
}

// ---------- 工具函数 ----------
function formatTime(timestamp) {
    var date = new Date(timestamp * 1000);
    var now = new Date();
    if (date.toDateString() === now.toDateString()) {
        return date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0');
    } else if (date.getFullYear() === now.getFullYear()) {
        return (date.getMonth() + 1) + '-' + date.getDate();
    } else {
        return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
    }
}

function parseRecords(responseText) {
    try {
        var data = JSON.parse(responseText);
        var records = data.data && data.data.records ? data.data.records : (data.records || []);
        var result = [];
        for (var i = 0; i < records.length; i++) {
            var r = records[i];
            if (r.status !== 0) continue;
            var submitId = parseInt(r.submitId);
            if (isNaN(submitId)) continue;
            var timestamp = Math.floor(new Date(r.submitTime).getTime() / 1000);
            var diffIndex = getDifficultyIndex(r.oiRankScore);
            result.push({
                id: submitId,
                displayPid: r.displayPid,
                title: r.title,
                submitTime: timestamp,
                diffIndex: diffIndex
            });
        }
        return result;
    } catch (e) {
        console.error('解析失败', e);
        return [];
    }
}

function addRecordToTable(userDisplayName, record) {
    var problemLink = 'http://bbcoj.cn/problem/' + record.displayPid;
    var color = colors[record.diffIndex] || colors[0];
    var row = '<tr>' +
        '<td>' + userDisplayName + '</td>' +
        '<td>' + record.displayPid + '</td>' +
        '<td><a style="color:' + color + ';" href="' + problemLink + '" target="_blank">' + record.title + '</a></td>' +
        '<td data-timestamp="' + record.submitTime + '">' + formatTime(record.submitTime) + '</td>' +
        '</tr>';
    document.getElementById('store').innerHTML += row;
}

// ---------- 搜索与筛选 ----------
window.onSearch = function() {
    var storeId = document.getElementById('store');
    var rowsLength = storeId.rows.length;
    var key = document.getElementById('key').value;
    var timeFilter = document.getElementById('timeFilter').value;
    for (var i = 1; i < rowsLength; i++) {
        var searchText = storeId.rows[i].cells[0].innerHTML;
        var recordTime = parseInt(storeId.rows[i].cells[3].getAttribute('data-timestamp')) * 1000;
        var now = Date.now();
        var showRow = true;
        if (key != "*" && !searchText.match(key)) showRow = false;
        if (timeFilter != "全部" && timeOptions[timeFilter] > 0) {
            if (now - recordTime > timeOptions[timeFilter]) showRow = false;
        }
        storeId.rows[i].style.display = showRow ? '' : 'none';
    }
};

window.changeTimeRange = function() {
    var timeFilter = document.getElementById('timeFilter');
    currentTimeRange = timeOptions[timeFilter.value];
    onSearch();
};

// 将排序函数暴露到全局
window.sortTableByUser = sortTableByUser;

// ---------- 核心异步轮询 ----------
var currentUserIndex = 0;

function processUser(userIndex, isFirst) {
    var user = userlist[userIndex];
    var displayName = displaylist[userIndex];
    var page = 1;

    function fetchPage() {
        if (page > pages) {
            scheduleNext(isFirst);
            return;
        }

        var apiUrl = 'http://bbcoj.cn/api/get-submission-list?onlyMine=false&username=' + encodeURIComponent(user) +
                    '&currentPage=' + page + '&limit=15&completeProblemID=false';
        var xhr = new XMLHttpRequest();
        xhr.open('GET', apiUrl, true);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.onload = function() {
            if (xhr.status === 200) {
                var records = parseRecords(xhr.responseText);
                for (var i = 0; i < records.length; i++) {
                    var rec = records[i];
                    if (rec.id > lst[userIndex]) {
                        var now = Date.now();
                        var recTime = rec.submitTime * 1000;
                        if (currentTimeRange > 0 && (now - recTime) > currentTimeRange) continue;

                        addRecordToTable(displayName, rec);

                        if (!isFirst) {
                            // 使用 alert 弹窗(与参考脚本一致)
                            alert(displayName + " 刚刚AC了" + difficultyNames[rec.diffIndex] + "题 " + rec.displayPid + " " + rec.title);
                        }

                        lst[userIndex] = rec.id;
                    }
                }
            }
            page++;
            fetchPage();
        };
        xhr.onerror = function() {
            page++;
            fetchPage();
        };
        xhr.send();
    }

    fetchPage();
}

function scheduleNext(isFirst) {
    currentUserIndex = (currentUserIndex + 1) % userlist.length;
    if (isFirst) {
        processUser(currentUserIndex, true);
    } else {
        setTimeout(function() {
            processUser(currentUserIndex, false);
        }, cd);
    }
}

// ---------- 启动 ----------
buildUI();
processUser(0, true);
防止 ctj
C++
// ==UserScript==
// @name         别看题解
// @namespace    http://tampermonkey.net/
// @version      0.7.2.1
// @description  在你看洛谷题解的时候把自己挂到犇犇。
// @author       0x3b800001&Maxwell_dcc
// @match        https://www.luogu.com.cn/*
// @icon         https://cdn.luogu.com.cn/upload/usericon/3.png
// @grant        GM_xmlhttpRequest
// @connect      *
// @license      GPL
// @downloadURL https://update.greasyfork.org/scripts/460792/%E5%88%AB%E7%9C%8B%E9%A2%98%E8%A7%A3.user.js
// @updateURL https://update.greasyfork.org/scripts/460792/%E5%88%AB%E7%9C%8B%E9%A2%98%E8%A7%A3.meta.js
// ==/UserScript==

function mstt(ms) {
  ms = Number(ms);
  let res = "";
  res += String(Math.floor(ms / 86400000)) + "d ";
  res += String(Math.floor(ms % 86400000 / 3600000)) + "h ";
  res += String(Math.floor(ms % 3600000 / 60000)) + "min ";
  res += String(Math.floor(ms % 60000 / 1000)) + "s";
  return res;
}
(function () {
  'use strict';
  let f = () => {
    if (localStorage["bktj-format"] == undefined) {
      localStorage["bktj-format"] = "这个人在开题 bktjnumber bktjname 后 bktjtime 后看了题解\n\n![](https://cdn.luogu.com.cn/upload/image_hosting/3an86p0q.png) 警钟长鸣。";
    }
    if (localStorage["bktj-times"] == undefined) {
      localStorage["bktj-times"] = "2";
    }
    let lnk = window.location.href;
    if (lnk == "https://www.luogu.com.cn/user/setting#preference") {
      let r = prompt("格式(留空即为不变,具体使用方法见文档):");
      if (r.length != 0) localStorage["bktj-format"] = r.replace(/\\n/g, "\n");
      r = prompt("询问次数(留空即为不变,具体使用方法见文档):");
      if (r.length != 0) localStorage["bktj-times"] = r;
      if (confirm("是否清空重复挂 localStorage")) {
        let tmp = localStorage;
        for (let i = 0; i < localStorage.length; ++i) {
          if (localStorage.key(i).indexOf("bktj-problemcache-") != -1) {
            tmp.removeItem(localStorage.key(i));
          }
        }
        localStorage = tmp;
      }
    } else if (lnk.indexOf("https://www.luogu.com.cn/problem/solution/") != -1) {
      var name = (document.getElementsByClassName('lfe-h1'))[0].innerHTML;
      let P = (name.split(' '))[0];
      name = name.substring(P.length + 1, name.length).split(" 题解")[0];
      console.log(P);
      console.log(name);
      GM_xmlhttpRequest({
        url: "https://www.luogu.com.cn/problem/" + P + "?_contentOnly=1",
        method: "GET",
        onload: function (response) {
          let rp = response.responseText;
          let qwww = rp.split("recommendations")[0];
          if (qwww.indexOf("\"submitted\":true") == -1) {
            console.log("未通过");
            let see = true;
            for (let i = 1; i <= localStorage["bktj-times"]; ++i) {
              if (!confirm("您尚未通过" + P + ",确认要看题解吗?")) {
                see = false;
                break;
              }
            }
            if (see) {
              if (localStorage["bktj-problemcache-" + P] == undefined) {
                if (localStorage["bktj-opentime-" + P] == undefined) localStorage["bktj-opentime-" + P] = Date.now();
                GM_xmlhttpRequest({
                  url: "https://www.luogu.com.cn/api/feed/postBenben",
                  headers: {
                    "content-type": "application/json",
                    "referer": "https://www.luogu.com.cn/",
                    "x-csrf-token": document.querySelector("meta[name=csrf-token]").content
                  },
                  data: JSON.stringify({
                    content: String(localStorage["bktj-format"])
                      .replace(/bktjnumber/g, P)
                      .replace(/bktjname/g, name)
                      .replace(/bktjtime/g, mstt(Date.now() - Number(localStorage["bktj-opentime-" + P])))
                  }),
                  method: "POST",
                });
                localStorage["bktj-problemcache-" + P] = 1;
              } else {
                console.log("已经挂过了");
              }
            } else {
              document.getElementsByClassName("lfe-form-sz-middle")[0].click();
              return;
            }
          } else {
            console.log("已通过");
          }
        }
      });
    } else if (lnk.indexOf("https://www.luogu.com.cn/problem/") != -1) {
      let nm = lnk.substring(33);
      if (localStorage["bktj-opentime-" + nm] == undefined) {
        localStorage["bktj-opentime-" + nm] = Date.now();
      }
      console.log(nm);
    }
  };
  try {
    setTimeout(f, 1000);
  } catch (e) {
    window.location.reload();
  }
})();