您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds listed pages to a category upon request with UI, CSRF token, adaptive speed, and global compatibility
当前为
// ==UserScript== // @name OSRS Wiki Auto-Categorizer with UI, Adaptive Speed, and Global Scope // @namespace http://tampermonkey.net/ // @version 0.8 // @description Adds listed pages to a category upon request with UI, CSRF token, adaptive speed, and global compatibility // @author Nick2bad4u // @match https://oldschool.runescape.wiki/* // @grant GM_xmlhttpRequest // @icon https://www.google.com/s2/favicons?sz=64&domain=oldschool.runescape.wiki // @license UnLicense // ==/UserScript== (function() { 'use strict'; let categoryName = ''; let pageLinks = []; let currentIndex = 0; let csrfToken = ''; let isCancelled = false; let requestInterval = 500; // Start with a fast interval of 500ms const maxInterval = 5000; // Maximum interval of 5 seconds for rate-limited adjustments const excludedPrefixes = ["Template:", "File:", "Category:", "Module:"]; // Add UI button, progress bar, and cancel button to the page function addButtonAndProgressBar() { const container = document.createElement('div'); container.style.position = 'fixed'; container.style.bottom = '20px'; container.style.right = '20px'; container.style.zIndex = '1000'; container.style.backgroundColor = '#2b2b2b'; container.style.color = '#ffffff'; container.style.padding = '12px'; container.style.border = '1px solid #595959'; container.style.borderRadius = '8px'; container.style.fontFamily = 'Arial, sans-serif'; // Fixed width for better text fitting container.style.width = '250px'; container.id = 'categorize-ui'; const startButton = document.createElement('button'); startButton.textContent = 'Start Categorizing'; startButton.style.backgroundColor = '#4caf50'; startButton.style.color = '#fff'; startButton.style.border = 'none'; startButton.style.padding = '6px 12px'; startButton.style.borderRadius = '5px'; startButton.style.cursor = 'pointer'; startButton.onclick = startCategorization; container.appendChild(startButton); const cancelButton = document.createElement('button'); cancelButton.textContent = 'Cancel'; cancelButton.style.backgroundColor = '#d9534f'; cancelButton.style.color = '#fff'; cancelButton.style.border = 'none'; cancelButton.style.padding = '6px 12px'; cancelButton.style.borderRadius = '5px'; cancelButton.style.cursor = 'pointer'; cancelButton.style.marginLeft = '10px'; cancelButton.onclick = cancelCategorization; container.appendChild(cancelButton); const progressBarContainer = document.createElement('div'); progressBarContainer.style.width = '100%'; progressBarContainer.style.marginTop = '10px'; progressBarContainer.style.backgroundColor = '#3d3d3d'; progressBarContainer.style.height = '20px'; progressBarContainer.style.borderRadius = '5px'; progressBarContainer.id = 'progress-bar-container'; progressBarContainer.style.position = 'relative'; const progressBar = document.createElement('div'); progressBar.style.width = '0%'; progressBar.style.height = '100%'; progressBar.style.backgroundColor = '#4caf50'; progressBar.style.borderRadius = '5px'; progressBar.id = 'progress-bar'; progressBarContainer.appendChild(progressBar); const progressText = document.createElement('span'); progressText.id = 'progress-text'; progressText.style.position = 'absolute'; progressText.style.left = '50%'; progressText.style.top = '50%'; progressText.style.transform = 'translate(-50%, -50%)'; progressText.style.fontSize = '12px'; progressText.style.color = '#ffffff'; progressText.style.whiteSpace = 'nowrap'; progressText.style.overflow = 'visible'; progressText.style.textAlign = 'center'; progressBarContainer.appendChild(progressText); container.appendChild(progressBarContainer); document.body.appendChild(container); } // Fetch CSRF token function fetchCsrfToken(callback) { const tokenUrl = `https://oldschool.runescape.wiki/api.php?action=query&meta=tokens&type=csrf&format=json`; GM_xmlhttpRequest({ method: 'GET', url: tokenUrl, onload: function(response) { const data = JSON.parse(response.responseText); csrfToken = data.query.tokens.csrftoken; callback(); }, onerror: function() { alert('Failed to retrieve CSRF token'); } }); } // Parse links from the infobox, filtering out excluded prefixes function getPageLinks() { pageLinks = Array.from(document.querySelectorAll('#mw-content-text a')) .map(link => link.getAttribute('href')) .filter(href => href && href.startsWith('/w/')) .map(href => decodeURIComponent(href.replace('/w/', ''))) .filter(page => !excludedPrefixes.some(prefix => page.startsWith(prefix))); } // Prompt for category and start processing function startCategorization() { categoryName = prompt("Enter the category name you'd like to add:"); if (!categoryName) { alert("Category name is required."); return; } getPageLinks(); if (pageLinks.length === 0) { alert("No pages found to categorize."); return; } isCancelled = false; currentIndex = 0; document.getElementById('progress-bar-container').style.display = 'block'; fetchCsrfToken(() => processNextPage()); } // Process each page with an adaptive delay to avoid rate limits function processNextPage() { if (isCancelled || currentIndex >= pageLinks.length) { if (isCancelled) alert("Categorization cancelled."); else alert("Categorization complete!"); resetUI(); return; } const pageTitle = pageLinks[currentIndex]; updateProgressBar(`Processing: ${pageTitle}`); addCategoryToPage(pageTitle, () => { currentIndex++; updateProgressBar(`Processed: ${pageTitle}`); setTimeout(processNextPage, requestInterval); // Use adaptive request interval }); } // Add a category to a page via the MediaWiki API with CSRF token function addCategoryToPage(pageTitle, callback) { const apiUrl = `https://oldschool.runescape.wiki/api.php?action=query&prop=categories&titles=${encodeURIComponent(pageTitle)}&format=json`; GM_xmlhttpRequest({ method: 'GET', url: apiUrl, onload: function(response) { const data = JSON.parse(response.responseText); const pageId = Object.keys(data.query.pages)[0]; const categories = data.query.pages[pageId].categories; if (!categories || !categories.some(cat => cat.title === `Category:${categoryName}`)) { // Edit page to add category const editUrl = `https://oldschool.runescape.wiki/api.php?action=edit&title=${encodeURIComponent(pageTitle)}&appendtext=[[Category:${categoryName}]]&format=json`; GM_xmlhttpRequest({ method: 'POST', url: editUrl, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Api-User-Agent': 'YourScriptName/0.6 (ContactInfo)' }, data: `token=${encodeURIComponent(csrfToken)}`, onload: function(editResponse) { console.log(`Category added to ${pageTitle}`); requestInterval = Math.max(500, requestInterval - 500); // Decrease interval if successful callback(); }, onerror: function() { console.error(`Failed to add category to ${pageTitle}`); requestInterval = Math.min(maxInterval, requestInterval + 500); // Increase interval if error callback(); } }); } else { console.log(`${pageTitle} already has the category`); callback(); } } }); } // Update the progress bar with status and completion percentage function updateProgressBar(status) { const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text'); const progressPercent = ((currentIndex + 1) / pageLinks.length) * 100; progressBar.style.width = `${progressPercent}%`; progressText.textContent = `${status} (${Math.round(progressPercent)}%)`; } // Cancel the categorization process function cancelCategorization() { isCancelled = true; } // Reset the UI to initial state after completion or cancellation function resetUI() { document.getElementById('progress-bar').style.width = '0%'; document.getElementById('progress-text').textContent = ''; document.getElementById('progress-bar-container').style.display = 'none'; currentIndex = 0; } // Add the UI button and progress bar on page load addButtonAndProgressBar(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址