Skip to content

Instantly share code, notes, and snippets.

@mnixry
Created November 25, 2025 16:16
Show Gist options
  • Select an option

  • Save mnixry/5cabcbf07d6cb05e9de8a43ab8a8c249 to your computer and use it in GitHub Desktop.

Select an option

Save mnixry/5cabcbf07d6cb05e9de8a43ab8a8c249 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Auto-Submitter (Multi-Page + 100 Batch Limit)
// @namespace Violentmonkey Scripts
// @match https://match.ichunqiu.com/index*
// @version 1.3
// @author -
// @description Fetches 5 pages, dedupes, and submits in batches of 100 flags per request
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
// ================= CONFIGURATION =================
const RACE_UUID = "51a20bcf-acad-4ad9-a090-17efd8f5c285";
// {page} will be replaced dynamically
const API_TEMPLATE = `http://192.168.2.245:8080/api/Races/${RACE_UUID}/flags?page={page}&pageSize=50&status=&search=&exprunid=`;
const MAX_PAGES_BACK = 5; // How many pages to scrape
const BATCH_SIZE = 100; // Max flags per submission
// Selectors
const SEL_DROPDOWN_ITEMS = '.el-select-dropdown__item';
const SEL_INPUT = '[placeholder="请输入目标答案"]';
const SEL_SUBMIT_BTN = '.el-overlay button.btn';
// =================================================
// State Control
let isRunning = false;
let uiButton = null;
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// --- Helper: Chunk Array ---
function chunkArray(myArray, chunk_size) {
const results = [];
for (let i = 0; i < myArray.length; i += chunk_size) {
results.push(myArray.slice(i, i + chunk_size));
}
return results;
}
// --- Helper: Fetch ---
function fetchFlags(pageNumber) {
return new Promise((resolve, reject) => {
const targetUrl = API_TEMPLATE.replace('{page}', pageNumber);
GM_xmlhttpRequest({
method: "GET",
url: targetUrl,
onload: function(response) {
if (response.status >= 200 && response.status < 300) {
try {
const json = JSON.parse(response.responseText);
resolve(json);
} catch (e) {
reject("Failed to parse JSON: " + e.message);
}
} else {
reject("API Error: " + response.statusText);
}
},
onerror: function(err) {
reject("Network Error: " + err);
}
});
});
}
// --- Helper: Set Input Value ---
function setNativeValue(element, value) {
element.value = value;
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
}
// --- Main Logic ---
async function processBatch() {
try {
let allItems = [];
// 1. Fetch Data (Pages 1 to MAX_PAGES_BACK)
console.log(`Fetching last ${MAX_PAGES_BACK} pages...`);
for (let p = 1; p <= MAX_PAGES_BACK; p++) {
if (!isRunning) break;
try {
const data = await fetchFlags(p);
if (data.items && Array.isArray(data.items) && data.items.length > 0) {
allItems = allItems.concat(data.items);
} else {
break; // Stop if page is empty
}
await sleep(100);
} catch (e) {
console.error(`Error fetching page ${p}:`, e);
}
}
if (allItems.length === 0) {
console.warn("No flags found in API.");
return;
}
// 2. Deduplicate
const uniqueValues = [...new Set(allItems.map(item => item.value))];
// 3. Split into chunks of 100
const flagChunks = chunkArray(uniqueValues, BATCH_SIZE);
console.log(`Loaded ${uniqueValues.length} unique flags. Split into ${flagChunks.length} chunk(s).`);
// 4. Get Dropdown Items
const items = document.querySelectorAll(SEL_DROPDOWN_ITEMS);
if (items.length === 0) {
console.warn("No dropdown items found. Please click the dropdown menu.");
return;
}
// 5. Iterate Dropdown Items
for (let i = 0; i < items.length; i++) {
if (!isRunning) break;
const item = items[i];
if (item.style.display === 'none') continue;
// A. Click the Challenge/Dropdown Item
console.log(`\n--- Selecting Item ${i + 1}/${items.length} ---`);
item.click();
await sleep(600);
// B. Iterate Chunks (Submit 100, wait, Submit next 100...)
for (let c = 0; c < flagChunks.length; c++) {
if (!isRunning) break;
const currentChunkStr = flagChunks[c].join('\n');
console.log(` Submitting Chunk ${c + 1}/${flagChunks.length} (${flagChunks[c].length} flags)...`);
// Find Input
const inputEl = document.querySelector(SEL_INPUT);
if (!inputEl) {
console.error(" Input field not found!");
continue;
}
// Set Value
setNativeValue(inputEl, currentChunkStr);
await sleep(200);
// Click Submit
const btn = document.querySelector(SEL_SUBMIT_BTN);
if (btn) {
btn.click();
console.log(" Submit button clicked.");
} else {
console.error(" Submit button not found!");
}
// Wait for server processing before next chunk or next item
await sleep(10000);
}
}
} catch (err) {
console.error("Batch Error:", err);
}
}
// --- Infinite Loop Manager ---
async function runInfiniteLoop() {
console.log(">>> Auto-Submitter STARTED <<<");
while (isRunning) {
await processBatch();
if (isRunning) {
console.log("Cycle finished. Sleeping 2s...");
await sleep(2000);
}
}
console.log(">>> Auto-Submitter STOPPED <<<");
updateUIState(false);
}
// --- UI ---
function updateUIState(running) {
if (!uiButton) return;
if (running) {
uiButton.innerText = `Stop (Chunks of ${BATCH_SIZE})`;
uiButton.style.backgroundColor = "#F56C6C";
} else {
uiButton.innerText = `Start (Chunks of ${BATCH_SIZE})`;
uiButton.style.backgroundColor = "#409EFF";
}
uiButton.disabled = false;
}
function createUI() {
uiButton = document.createElement('button');
updateUIState(false);
uiButton.style.position = "fixed";
uiButton.style.top = "10px";
uiButton.style.right = "10px";
uiButton.style.zIndex = "999999";
uiButton.style.padding = "10px 20px";
uiButton.style.color = "white";
uiButton.style.border = "none";
uiButton.style.borderRadius = "4px";
uiButton.style.cursor = "pointer";
uiButton.style.boxShadow = "0 2px 12px 0 rgba(0,0,0,0.1)";
uiButton.style.fontWeight = "bold";
uiButton.onclick = function() {
if (isRunning) {
isRunning = false;
uiButton.innerText = "Stopping...";
uiButton.style.backgroundColor = "#E6A23C";
uiButton.disabled = true;
} else {
isRunning = true;
updateUIState(true);
runInfiniteLoop();
}
};
document.body.appendChild(uiButton);
}
window.addEventListener('load', createUI);
if (document.readyState === 'complete') createUI();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment