您需要先安装一个扩展,例如 篡改猴、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 1.5 // @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'; // Global variables const versionNumber = '1.5'; let categoryName = ''; // Stores the requested category name let pageLinks = []; // Stores all applicable page links on the current page let selectedLinks = []; // Stores selected page links for categorization let currentIndex = 0; // Tracks current page being processed let csrfToken = ''; // Stores CSRF token for authenticated actions let isCancelled = false; // Flag to cancel ongoing categorization let isRunning = false; // Indicates if categorization is in progress let requestInterval = 500; // Initial interval between API requests, adapts based on rate limiting const maxInterval = 5000; // Maximum interval to wait after rate limiting occurs const excludedPrefixes = ["Template:", "File:", "Category:", "Module:"]; // Pages to exclude // Add UI elements to start, cancel, and display progress of the categorization process function addButtonAndProgressBar() { console.log("Adding UI elements for categorization."); const container = document.createElement('div'); container.id = 'categorize-ui'; container.style = `position: fixed; bottom: 20px; right: 20px; z-index: 1000; background-color: #2b2b2b; color: #ffffff; padding: 12px; border: 1px solid #595959; border-radius: 8px; font-family: Arial, sans-serif; width: 250px;`; // Start Button const startButton = document.createElement('button'); startButton.textContent = 'Start Categorizing'; startButton.style = `background-color: #4caf50; color: #fff; border: none; padding: 6px 12px; border-radius: 5px; cursor: pointer;`; startButton.onclick = promptCategoryName; container.appendChild(startButton); // Cancel Button const cancelButton = document.createElement('button'); cancelButton.textContent = 'Cancel'; cancelButton.style = `background-color: #d9534f; color: #fff; border: none; padding: 6px 12px; border-radius: 5px; cursor: pointer; margin-left: 10px;`; cancelButton.onclick = cancelCategorization; container.appendChild(cancelButton); // Progress Bar and Status Text const progressBarContainer = document.createElement('div'); progressBarContainer.style = `width: 100%; margin-top: 10px; background-color: #3d3d3d; height: 20px; border-radius: 5px; position: relative;`; progressBarContainer.id = 'progress-bar-container'; const progressBar = document.createElement('div'); progressBar.style = `width: 0%; height: 100%; background-color: #4caf50; border-radius: 5px;`; progressBar.id = 'progress-bar'; progressBarContainer.appendChild(progressBar); const progressText = document.createElement('span'); progressText.id = 'progress-text'; progressText.style = `position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-size: 12px; color: #ffffff; white-space: nowrap; overflow: visible; text-align: center;`; progressBarContainer.appendChild(progressText); container.appendChild(progressBarContainer); document.body.appendChild(container); console.log("UI elements added successfully."); } // Prompt user for category name and initiate the link fetching process function promptCategoryName() { console.log("Prompting user for category name."); categoryName = prompt("Enter the category name you'd like to add:"); if (!categoryName) { alert("Category name is required."); console.warn("User did not enter a category name."); return; } getPageLinks(); if (pageLinks.length === 0) { alert("No pages found to categorize."); console.warn("No applicable pages found."); return; } displayPageSelectionPopup(); } // Retrieve applicable page links on the current page, filtering out excluded prefixes function getPageLinks() { console.log("Retrieving applicable page links."); 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))); console.log(`Found ${pageLinks.length} page links.`); } // Display popup for selecting pages to categorize function displayPageSelectionPopup() { console.log("Displaying page selection popup."); const popup = document.createElement('div'); popup.style = `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1001; background-color: #2b2b2b; padding: 15px; color: white; border-radius: 8px; max-height: 80vh; overflow-y: auto; border: 1px solid #595959;`; const title = document.createElement('h3'); title.textContent = 'Select Pages to Categorize'; title.style = `margin: 0 0 10px; font-family: Arial, sans-serif;`; popup.appendChild(title); const listContainer = document.createElement('div'); listContainer.style = `max-height: 300px; overflow-y: auto;`; pageLinks.forEach(link => { const listItem = document.createElement('div'); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = true; checkbox.value = link; listItem.appendChild(checkbox); listItem.appendChild(document.createTextNode(` ${link}`)); listContainer.appendChild(listItem); }); popup.appendChild(listContainer); const buttonContainer = document.createElement('div'); buttonContainer.style = `margin-top: 10px; display: flex; justify-content: space-between;`; const confirmButton = document.createElement('button'); confirmButton.textContent = 'Confirm Selection'; confirmButton.style = `padding: 5px 10px; background-color: #4caf50; border: none; color: white; cursor: pointer; border-radius: 5px;`; confirmButton.onclick = () => { selectedLinks = Array.from(listContainer.querySelectorAll('input:checked')).map(input => input.value); console.log("Selected links confirmed:", selectedLinks); document.body.removeChild(popup); if (selectedLinks.length > 0) { startCategorization(); } else { alert("No pages selected."); console.warn("User did not select any pages."); } }; const cancelPopupButton = document.createElement('button'); cancelPopupButton.textContent = 'Cancel'; cancelPopupButton.style = `padding: 5px 10px; background-color: #d9534f; border: none; color: white; cursor: pointer; border-radius: 5px;`; cancelPopupButton.onclick = () => { document.body.removeChild(popup); console.log("Popup canceled by user."); }; buttonContainer.appendChild(confirmButton); buttonContainer.appendChild(cancelPopupButton); popup.appendChild(buttonContainer); document.body.appendChild(popup); } // Start categorization process and fetch CSRF token for authorized API calls function startCategorization() { console.log("Starting categorization process."); isCancelled = false; isRunning = true; currentIndex = 0; document.getElementById('progress-bar-container').style.display = 'block'; console.log("Fetching CSRF token..."); fetchCsrfToken(() => processNextPage()); } // Process each page one at a time, updating progress bar and adjusting speed for rate limits function processNextPage() { if (isCancelled || currentIndex >= selectedLinks.length) { isRunning = false; alert(isCancelled ? "Categorization canceled." : "Categorization complete."); console.log("Categorization process completed or canceled."); return; } const page = selectedLinks[currentIndex]; console.log(`Processing page: ${page}`); categorizePage(page, () => { currentIndex++; updateProgress(); // Adaptive request timing requestInterval = adjustRequestInterval(); setTimeout(processNextPage, requestInterval); }); } // Fetch CSRF token from the server for authenticated requests function fetchCsrfToken(callback) { console.log("Requesting CSRF token..."); GM_xmlhttpRequest({ method: 'GET', url: 'https://oldschool.runescape.wiki/api.php?action=query&meta=tokens&type=edit&format=json', onload: function(response) { if (response.status === 200) { const data = JSON.parse(response.responseText); csrfToken = data.query.tokens.csrftoken; console.log("CSRF token fetched:", csrfToken); callback(); } else { console.error("Failed to fetch CSRF token:", response.statusText); alert("Error fetching CSRF token. Please try again."); } } }); } // Categorize a specific page and handle possible errors function categorizePage(page, callback) { console.log(`Categorizing page: ${page}`); GM_xmlhttpRequest({ method: 'POST', url: 'https://oldschool.runescape.wiki/api.php', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: `action=edit&title=${encodeURIComponent(page)}&appendtext=[[Category:${encodeURIComponent(categoryName)}]]&token=${encodeURIComponent(csrfToken)}&format=json`, onload: function(response) { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.error) { console.error(`Error categorizing page: ${page} - ${data.error.info}`); } else { console.log(`Successfully categorized page: ${page}`); } } else { console.error(`Failed to categorize page: ${page} - ${response.statusText}`); } callback(); } }); } // Update the progress bar and text based on the current status function updateProgress() { const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text'); const progressPercentage = Math.floor((currentIndex / selectedLinks.length) * 100); progressBar.style.width = `${progressPercentage}%`; progressText.textContent = `Processing ${currentIndex + 1} of ${selectedLinks.length} pages`; console.log(`Progress updated: ${progressText.textContent}`); } // Cancel the ongoing categorization process function cancelCategorization() { console.log("Categorization process has been cancelled by the user."); isCancelled = true; } // Adjust the request interval based on API response function adjustRequestInterval() { // Add logic here based on your requirements, for example, increase delay on errors return Math.min(requestInterval * 2, maxInterval); // Example: double the interval } // Initialize the script by adding UI elements addButtonAndProgressBar(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址