|
|
| Line 1: |
Line 1: |
| // Credit: HaMiklolah (Improved version - allows execution for any logged-in user)
| |
| (() => { | | (() => { |
| const api = new mw.Api();
| | const BOT_USERS = [508]; |
| | const api = new mw.Api(); |
|
| |
|
| /**
| | const getRedirectContent = async (title) => { |
| * Gets the content of the redirect page.
| | return api.get({ |
| * @param {string} title - The page title.
| | prop: "revisions", |
| * @returns {Promise<string|null>} The page content or null if an error occurred.
| | titles: title, |
| */
| | rvprop: "content", |
| const getRedirectContent = async (title) => {
| | rvslots: "*", |
| try {
| | formatversion: "2", |
| 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;
| |
| }
| |
| };
| |
|
| |
|
| /**
| | const getRedirectTarget = async (title) => { |
| * Gets the final redirect target.
| | const { query } = await api.get({ |
| * @param {string} title - The title of the redirect page.
| | titles: title, |
| * @returns {Promise<string|null>} The redirect target or null if not found.
| | redirects: true, |
| */
| | }); |
| const getRedirectTarget = async (title) => {
| |
| try {
| |
| const { query } = await api.get({
| |
| titles: title,
| |
| redirects: true, // Allows MediaWiki to follow redirects
| |
| });
| |
|
| |
|
| if (query?.pages) {
| | if (query?.pages) { |
| // Find the page that is not missing (the final target)
| | const page = Object.values(query.pages).find((page) => !page.missing); |
| const page = Object.values(query.pages).find((p) => !p.missing);
| | return page?.title ?? null; |
| return page?.title ?? null;
| | } |
| }
| | return null; |
| return null;
| | }; |
| } catch (error) {
| |
| console.error("Error getting redirect target for " + title + ":", error);
| |
| return null;
| |
| }
| |
| };
| |
|
| |
|
| /**
| | const extractAnchorFromContent = (content) => { |
| * Extracts the anchor from redirect content (e.g., [[Target_Page#anchor]]).
| | const match = content.match(/\[\[.*?(?:#(.+?))?\]\]/); |
| * @param {string} content - The content of the redirect page.
| | return match?.[1] ? `#${match[1]}` : ""; |
| * @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] : "";
| |
| };
| |
|
| |
|
| /**
| | const getNamespacePrefix = async (title) => { |
| * Gets the namespace prefix of a page (e.g., "MediaWiki:", "File:").
| | const { query } = await api.get({ |
| * @param {string} title - The page title.
| | titles: title, |
| * @returns {Promise<string>} The namespace prefix or an empty string.
| | prop: "info", |
| */
| | }); |
| const getNamespacePrefix = async (title) => {
| |
| try {
| |
| const { query } = await api.get({
| |
| titles: title,
| |
| prop: "info",
| |
| });
| |
|
| |
|
| if (query?.pages) {
| | if (query?.pages) { |
| const page = Object.values(query.pages)[0];
| | const page = Object.values(query.pages)[0]; |
| if (page.ns !== undefined) {
| | if (page.ns !== undefined) { |
| // Standard MediaWiki namespaces. Adjust '4' if Chabadpedia uses a different name for NS 4.
| | const namespacePrefixes = { |
| const namespacePrefixes = {
| | 4: 'חב"דפדיה:', |
| 0: "", // Main/Article namespace
| | 6: "קובץ:", |
| 1: "Talk:",
| | 10: "תבנית:", |
| 2: "User:",
| | 12: "עזרה:", |
| 3: "User talk:",
| | 14: "קטגוריה:", |
| 4: "Chabadpedia:", // Common for English wikis, could also be "Project:" or "Wikipedia:"
| | }; |
| 5: "Chabadpedia talk:",
| | return namespacePrefixes[page.ns] ?? ""; |
| 6: "File:",
| | } |
| 7: "File talk:",
| | } |
| 8: "MediaWiki:",
| | return ""; |
| 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 "";
| |
| }
| |
| };
| |
|
| |
|
| /**
| | const createRedirect = async (title, target) => { |
| * Creates/fixes a redirect.
| | try { |
| * @param {string} title - The title of the page to become a redirect.
| | await api.postWithEditToken({ |
| * @param {string} target - The redirect target.
| | action: "edit", |
| */
| | format: "json", |
| const createRedirect = async (title, target) => {
| | bot: true, |
| try {
| | title: title, |
| await api.postWithEditToken({
| | text: `#הפניה [[${target}]]`, |
| action: "edit",
| | summary: "תיקון הפניה כפולה", |
| format: "json",
| | }); |
| title: title,
| | mw.notify(`\nstatus:${title} success`); |
| // Changed from #הפניה to #REDIRECT
| | } catch (error) { |
| text: "#REDIRECT [[" + target + "]]",
| | console.error(error); |
| // 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' });
| |
| }
| |
| }
| |
| };
| |
|
| |
|
| /**
| | const processRedirect = async (title) => { |
| * Processes a single double redirect: finds the final target and fixes it.
| | try { |
| * @param {string} title - The page title of the double redirect.
| | const contentData = await getRedirectContent(title); |
| */
| | const content = |
| const processRedirect = async (title) => {
| | contentData.query.pages[0].revisions[0].slots.main.content; |
| try {
| | const anchor = extractAnchorFromContent(content); |
| 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); |
| const finalTarget = await getRedirectTarget(title);
| | if (!finalTarget) return; |
|
| |
|
| if (!finalTarget) {
| | const namespace = await getNamespacePrefix(finalTarget); |
| mw.notify(title + ": No final redirect target found.", { type: 'warn' });
| | const fullTarget = namespace + finalTarget + anchor; |
| return;
| |
| }
| |
|
| |
|
| const namespace = await getNamespacePrefix(finalTarget);
| | if (title === finalTarget) { |
| const fullTarget = namespace + finalTarget + anchor;
| | alert("הפניה מעגלית"); |
| | return; |
| | } |
|
| |
|
| // Check to prevent circular redirects or unnecessary fixes
| | console.log("Creating redirect to:", fullTarget); |
| if (title.replace(/_/g, " ") === finalTarget.replace(/_/g, " ") && !anchor) {
| | await createRedirect(title, fullTarget); |
| mw.notify(title + ": Redirect is already valid. No fix needed.", { type: 'info' });
| | } catch (error) { |
| return;
| | console.error("Error processing redirect:", error); |
| }
| | } |
| | }; |
|
| |
|
| if (title.replace(/_/g, " ") === fullTarget.replace(/_/g, " ")) {
| | const init = async () => { |
| mw.notify(title + ": Circular or valid redirect. No fix needed.", { type: 'info' });
| | const userGroups = mw.config.get("wgUserGroups"); |
| return;
| | const userId = mw.config.get("wgUserId"); |
| }
| | const pageName = mw.config.get("wgPageName"); |
|
| |
|
| console.log("Creating new redirect: " + title + " --> " + fullTarget);
| | if (!userGroups.includes("bot") && !BOT_USERS.includes(userId)) return; |
| await createRedirect(title, fullTarget);
| | if (pageName !== "מיוחד:הפניות_כפולות") return; |
|
| |
|
| } catch (error) {
| | const num = prompt("? כמה הפניות כפולות להציג", 0); |
| console.error("Error processing double redirect for " + title + ":", error);
| | if (!num) return; |
| mw.notify(title + ": An error occurred while processing the redirect. See console for details.", { type: 'error', title: 'Error' });
| |
| }
| |
| };
| |
|
| |
|
| /** | | try { |
| * The main function that runs the script.
| | const { query } = await api.get({ |
| */
| | list: "querypage", |
| const init = async () => {
| | qppage: "DoubleRedirects", |
| const pageName = mw.config.get("wgPageName"); | | qplimit: num, |
| | }); |
|
| |
|
| // Ensure the script runs only on the Special:DoubleRedirects page | | const redirects = query.querypage.results; |
| if (pageName !== "Special:DoubleRedirects") { | | for (const redirect of redirects) { |
| console.log("This script only runs on Special:DoubleRedirects.");
| | const title = redirect.title.replace(/_/g, " "); |
| return;
| | await processRedirect(title); |
| }
| | } |
| | } catch (error) { |
| | console.error("Error fetching redirects:", error); |
| | } |
| | }; |
|
| |
|
| const numInput = prompt("How many double redirects to display and fix? (Recommended to start with a small number, e.g., 10)", "0");
| | $(init); |
| 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);
| |
| })(); | | })(); |