// ==UserScript== // @name 信友队→CPH一键传送 // @namespace http://tampermonkey.net/ // @version 4.7 // @description 动态匹配测试样例并发送到VS Code CPH插件 // @author YourName // @match https://xinyoudui.com/ac/contest/*/problem/* // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_setClipboard // @connect localhost // ==/UserScript== (function() { 'use strict'; // 创建悬浮按钮 (仅右下角) const btn = document.createElement('button'); btn.innerHTML = '🚀 传送至CPH'; btn.style = ` position: fixed; bottom: 20px; right: 20px; padding: 12px 18px; background: linear-gradient(135deg, #6e8efb, #a777e3); color: white; border: none; border-radius: 50px; cursor: pointer; z-index: 99999; font-size: 16px; font-weight: bold; box-shadow: 0 4px 15px rgba(0,0,0,0.2); transition: all 0.3s; animation: fadeIn 1s; `; const fadeInStyle = document.createElement('style'); fadeInStyle.textContent = ` @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } `; document.head.appendChild(fadeInStyle); document.body.appendChild(btn); // 主功能函数 btn.addEventListener('click', async () => { let samples = []; // 在try外部定义samples,以便catch块可以使用 try { btn.disabled = true; btn.innerHTML = '🔄 采集中...'; // 1. 提取所有样例对 (Input -> Output) samples = await extractSamplePairs(); if (samples.length === 0) throw new Error('未找到测试样例'); // 2. 构建 CPH 数据包 const payload = { name: `信友队-${window.location.pathname.split('/').slice(-2, -1)[0]}`, group: "信友队", url: window.location.href, memoryLimit: 256, timeLimit: 1000, tests: samples.map(([input, output], i) => ({ input: input, output: output, id: i })), source: `#include \nusing namespace std;\n\nint main() {\n ios::sync_with_stdio(false);\n cin.tie(nullptr);\n \n // 你的代码\n \n return 0;\n}` }; // 3. 发送到 CPH 插件 await sendToCPH(payload); // 4. 成功反馈 btn.innerHTML = '✅ 传送成功!'; GM_notification({ title: "CPH传送成功", text: `已发送${samples.length}个测试样例对`, timeout: 3000 }); } catch (error) { console.error('CPH传送失败:', error); btn.innerHTML = '❌ 传送失败'; GM_notification({ title: "传送失败", text: error.message, timeout: 5000 }); // 自动复制样例到剪贴板作为备用 const sampleText = samples .map(([input, output], i) => `Input ${i+1}:\n${input}\n\nOutput ${i+1}:\n${output}`) .join('\n\n---分隔---\n\n'); GM_setClipboard(sampleText); } finally { setTimeout(() => { btn.disabled = false; btn.innerHTML = '🚀 传送至CPH'; }, 3000); } }); // 样例匹配函数 async function extractSamplePairs() { const samples = []; const inputs = []; const outputs = []; // 查找所有的标题节点 const sections = document.querySelectorAll('div, p, h3, h4, span'); const inputRegex = /^(Input|输入样例)/; const outputRegex = /^(Output|输出样例)/; // 记录当前状态 let currentState = null; // 遍历每个元素 for (let i = 0; i < sections.length; i++) { const el = sections[i]; const nextEl = sections[i + 1]; // 判断是否是 Input 或 Output 标题 if (el.textContent && inputRegex.test(el.textContent)) { currentState = 'input'; } else if (el.textContent && outputRegex.test(el.textContent)) { currentState = 'output'; } // 提取内容 if (currentState === 'input') { if (nextEl && nextEl.textContent?.trim()) { const inputContent = nextEl.textContent.trim(); inputs.push(inputContent); console.log('提取到 Input:', inputContent); currentState = null; // 重置状态 } } else if (currentState === 'output') { if (nextEl && nextEl.textContent?.trim()) { const outputContent = nextEl.textContent.trim(); outputs.push(outputContent); console.log('提取到 Output:', outputContent); currentState = null; // 重置状态 } } } // 将 Input 和 Output 一一配对 if (inputs.length && outputs.length) { for (let i = 0; i < inputs.length && i < outputs.length; i++) { samples.push([inputs[i], outputs[i]]); } } return samples; } // CPH 通信函数 function sendToCPH(data) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "http://localhost:27121/", data: JSON.stringify(data), headers: { "Content-Type": "application/json" }, timeout: 5000, onload: function(res) { if (res.status === 200) { resolve(res); } else { reject(new Error(`CPH返回错误: ${res.statusText}`)); } }, onerror: function(error) { reject(new Error(`无法连接到CPH插件: ${error}`)); } }); }); } })();