User:Bot doubleredirects/Gadget-DoubleRedirectFixer.js: Difference between revisions
No edit summary |
No edit summary |
||
| (4 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
// Credit: HaMiklolah (Improved version - allows execution for any logged-in user) | |||
(() => { | |||
const api = new mw.Api(); | |||
/** | |||
* Gets the content of the redirect page. | |||
* @param {string} title - The page title. | |||
* @returns {Promise<string|null>} The page content or null if an error occurred. | |||
*/ | |||
const getRedirectContent = async (title) => { | |||
try { | |||
const response = await api.get({ | |||
prop: "revisions", | |||
titles: title, | |||
rvprop: "content", | |||
rvslots: "*", | |||
formatversion: "2", | |||
}); | |||
const page = response.query?.pages?.[0]; | |||
return page?.revisions?.[0]?.slots?.main?.content ?? null; | |||
} catch (error) { | |||
console.error("Error getting redirect content for " + title + ":", error); | |||
return null; | |||
} | |||
}; | |||
/** | |||
* Gets the final redirect target. | |||
* @param {string} title - The title of the redirect page. | |||
* @returns {Promise<string|null>} The redirect target or null if not found. | |||
*/ | |||
const getRedirectTarget = async (title) => { | |||
try { | |||
const { query } = await api.get({ | |||
titles: title, | |||
redirects: true, // Allows MediaWiki to follow redirects | |||
}); | |||
if (query?.pages) { | |||
// Find the page that is not missing (the final target) | |||
const page = Object.values(query.pages).find((p) => !p.missing); | |||
return page?.title ?? null; | |||
} | |||
return null; | |||
} catch (error) { | |||
console.error("Error getting redirect target for " + title + ":", error); | |||
return null; | |||
} | |||
}; | |||
/** | |||
* Extracts the anchor from redirect content (e.g., [[Target_Page#anchor]]). | |||
* @param {string} content - The content of the redirect page. | |||
* @returns {string} The anchor with '#' if it exists, otherwise an empty string. | |||
*/ | |||
const extractAnchorFromContent = (content) => { | |||
// Regex adjusted to look for '#REDIRECT' | |||
const match = content.match(/#REDIRECT\s*\[\[[^\]]+?(?:#(.+?))?\]\]/i); // 'i' for case-insensitive | |||
return match?.[1] ? "#" + match[1] : ""; | |||
}; | |||
/** | |||
* Gets the namespace prefix of a page (e.g., "MediaWiki:", "File:"). | |||
* @param {string} title - The page title. | |||
* @returns {Promise<string>} The namespace prefix or an empty string. | |||
*/ | |||
const getNamespacePrefix = async (title) => { | |||
try { | |||
const { query } = await api.get({ | |||
titles: title, | |||
prop: "info", | |||
}); | |||
if (query?.pages) { | |||
const page = Object.values(query.pages)[0]; | |||
if (page.ns !== undefined) { | |||
// Standard MediaWiki namespaces. Adjust '4' if Chabadpedia uses a different name for NS 4. | |||
const namespacePrefixes = { | |||
0: "", // Main/Article namespace | |||
1: "Talk:", | |||
2: "User:", | |||
3: "User talk:", | |||
4: "Chabadpedia:", // Common for English wikis, could also be "Project:" or "Wikipedia:" | |||
5: "Chabadpedia talk:", | |||
6: "File:", | |||
7: "File talk:", | |||
8: "MediaWiki:", | |||
9: "MediaWiki talk:", | |||
10: "Template:", | |||
11: "Template talk:", | |||
12: "Help:", | |||
13: "Help talk:", | |||
14: "Category:", | |||
15: "Category talk:", | |||
// Add more custom namespaces here if applicable, using their numerical IDs. | |||
}; | |||
return namespacePrefixes[page.ns] ?? ""; | |||
} | |||
} | |||
return ""; | |||
} catch (error) { | |||
console.error("Error getting namespace prefix for " + title + ":", error); | |||
return ""; | |||
} | |||
}; | |||
/** | |||
* Creates/fixes a redirect. | |||
* @param {string} title - The title of the page to become a redirect. | |||
* @param {string} target - The redirect target. | |||
*/ | |||
const createRedirect = async (title, target) => { | |||
try { | |||
await api.postWithEditToken({ | |||
action: "edit", | |||
format: "json", | |||
title: title, | |||
// Changed from #הפניה to #REDIRECT | |||
text: "#REDIRECT [[" + target + "]]", | |||
// Localized summary | |||
summary: "Bot: Fixing double redirect to [[" + target + "]]", | |||
}); | |||
// Localized success notification | |||
mw.notify(title + ": Redirect successfully fixed to " + target, { type: 'success', title: 'Success' }); | |||
} catch (error) { | |||
console.error("Error creating redirect for " + title + " to " + target + ":", error); | |||
// Localized error notifications | |||
if (error.code === 'badtags') { | |||
mw.notify(title + ": Error: Permission denied to use edit tags. Please contact a system administrator.", { type: 'error', title: 'Permission Error' }); | |||
} else { | |||
mw.notify(title + ": Failed to fix redirect. See console for details.", { type: 'error', title: 'Error' }); | |||
} | |||
} | |||
}; | |||
/** | |||
* Processes a single double redirect: finds the final target and fixes it. | |||
* @param {string} title - The page title of the double redirect. | |||
*/ | |||
const processRedirect = async (title) => { | |||
try { | |||
const content = await getRedirectContent(title); | |||
if (content === null) { | |||
mw.notify(title + ": Could not retrieve redirect page content.", { type: 'warn' }); | |||
return; | |||
} | |||
const anchor = extractAnchorFromContent(content); | |||
const finalTarget = await getRedirectTarget(title); | |||
if (!finalTarget) { | |||
mw.notify(title + ": No final redirect target found.", { type: 'warn' }); | |||
return; | |||
} | |||
const namespace = await getNamespacePrefix(finalTarget); | |||
const fullTarget = namespace + finalTarget + anchor; | |||
// Check to prevent circular redirects or unnecessary fixes | |||
if (title.replace(/_/g, " ") === finalTarget.replace(/_/g, " ") && !anchor) { | |||
mw.notify(title + ": Redirect is already valid. No fix needed.", { type: 'info' }); | |||
return; | |||
} | |||
if (title.replace(/_/g, " ") === fullTarget.replace(/_/g, " ")) { | |||
mw.notify(title + ": Circular or valid redirect. No fix needed.", { type: 'info' }); | |||
return; | |||
} | |||
console.log("Creating new redirect: " + title + " --> " + fullTarget); | |||
await createRedirect(title, fullTarget); | |||
} catch (error) { | |||
console.error("Error processing double redirect for " + title + ":", error); | |||
mw.notify(title + ": An error occurred while processing the redirect. See console for details.", { type: 'error', title: 'Error' }); | |||
} | |||
}; | |||
/** | |||
* The main function that runs the script. | |||
*/ | |||
const init = async () => { | |||
const pageName = mw.config.get("wgPageName"); | |||
// Ensure the script runs only on the Special:DoubleRedirects page | |||
if (pageName !== "Special:DoubleRedirects") { | |||
console.log("This script only runs on Special:DoubleRedirects."); | |||
return; | |||
} | |||
const numInput = prompt("How many double redirects to display and fix? (Recommended to start with a small number, e.g., 10)", "0"); | |||
const num = parseInt(numInput, 10); | |||
if (isNaN(num) || num <= 0) { | |||
mw.notify("Invalid input. Please enter a positive integer.", { type: 'error' }); | |||
return; | |||
} | |||
try { | |||
mw.notify("Starting check for " + num + " double redirects...", { type: 'info' }); | |||
// Get the list of double redirects via API | |||
const { query } = await api.get({ | |||
list: "querypage", | |||
qppage: "DoubleRedirects", | |||
qplimit: num, | |||
}); | |||
const redirects = query.querypage.results; | |||
if (redirects.length === 0) { | |||
mw.notify("No double redirects found to fix.", { type: 'info' }); | |||
return; | |||
} | |||
// Loop through each redirect and fix it | |||
for (const redirect of redirects) { | |||
const title = redirect.title.replace(/_/g, " "); | |||
await processRedirect(title); | |||
} | |||
mw.notify("Finished processing double redirects.", { type: 'success', title: 'Finished' }); | |||
} catch (error) { | |||
console.error("Error fetching double redirects:", error); | |||
mw.notify("An error occurred while fetching the list of double redirects.", { type: 'error', title: 'General Error' }); | |||
} | |||
}; | |||
// Run the script when the page is loaded | |||
$(init); | |||
})(); | })(); | ||