Jump to content

User:MolecularPilot/massRollback.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/// Mass rollback function with user-specified rate limit and bot tagging (if flagged)
/// Re-written by MolecularPilot, based on original by Mr.Z-man, John254, Writ Keeper and TheDJ 

if (typeof wkContribsCheckboxInit === "undefined") {
    wkContribsCheckboxInit = false;
}

if (typeof wkRollbackPortlet === "undefined") {
    wkRollbackPortlet = "p-cactions";
}

function rollbackEverythingWKMR(editSummary) {
    if (editSummary === null) {
        return false;
    }
    if (mw.config.get("wgRelevantUserName") === mw.config.get("wgUserName")) {
        if (!(confirm("You are about to roll back *all* of *your own* edits. Please note that this will be very difficult to undo. Are you *ABSOLUTELY SURE* you want to do this?"))) {
            return false;
        }
    }

    // Prompt user for the max rollbacks per minute
    var maxRollbacksPerMinute = parseInt(prompt("Enter the maximum number of rollbacks you want to perform in one minute:", "10"));
    if (isNaN(maxRollbacksPerMinute) || maxRollbacksPerMinute <= 0) {
        mw.notify("Invalid input! Please enter a positive integer.");
        return false;
    }

    mw.loader.using('mediawiki.api').done(function () {
        var rbMetadata = {};
        rbMetadata.api = new mw.Api();
        rbMetadata.userName = mw.config.get("wgRelevantUserName");
        rbMetadata.ipRange = (rbMetadata.userName === null);
        rbMetadata.titleRegex = /title=([^&]+)/;
        rbMetadata.editSummary = editSummary;
        var rollbackLinks = $("a[href*='action=rollback']");

        // Calculate delay per rollback based on user-specified limit
        var delayBetweenRollbacks = 60000 / maxRollbacksPerMinute; // Delay in ms for each rollback to stay within the limit (60,000ms = 1 minute)
        var rollbacksMade = 0;
        var startTime = Date.now(); // Time tracking for 1-minute window

        // Start the rollbacks with a delay
        rollbackLinks.each(function (ind, el) {
            // Ensure we don't exceed the max rollbacks in 1 minute
            var timeElapsed = Date.now() - startTime;
            var timeLeft = 60000 - timeElapsed; // Time remaining in the current minute

            if (rollbacksMade >= maxRollbacksPerMinute) {
                // If we've hit the limit, wait until the next minute
                var waitForNextMinute = 60000 - timeElapsed;
                setTimeout(function () {
                    startTime = Date.now(); // Reset start time for the new minute
                    rollbacksMade = 0; // Reset the rollback counter
                    rollbackOneThingWKMR(el, rbMetadata);
                }, waitForNextMinute);
            } else {
                // If we can proceed with a rollback
                setTimeout(function () {
                    rollbackOneThingWKMR(el, rbMetadata);
                    rollbacksMade++; // Increment the rollback counter
                }, ind * delayBetweenRollbacks); // Apply delay based on user input
            }
        });
    });
    return false;
}

function rollbackSomeThingsWKMR(editSummary) {
    if (editSummary === null) {
        return false;
    }

    // Prompt user for the max rollbacks per minute
    var maxRollbacksPerMinute = parseInt(prompt("Enter the maximum number of rollbacks you want to perform in one minute:", "10"));
    if (isNaN(maxRollbacksPerMinute) || maxRollbacksPerMinute <= 0) {
        mw.notify("Invalid input! Please enter a positive integer.");
        return false;
    }

    mw.loader.using('mediawiki.api').done(function () {
        var rbMetadata = {};
        rbMetadata.api = new mw.Api();
        rbMetadata.userName = mw.config.get("wgRelevantUserName");
        rbMetadata.titleRegex = /title=([^&]+)/;
        rbMetadata.editSummary = editSummary;
        var rollbackList = $("input.revdelIds:checked").parents("li.mw-contributions-current").find("a[href*='action=rollback']");

        if (rollbackList.length <= 0) {
            mw.notify("You didn't select any edits that could be rolled back!");
            return;
        }

        // Calculate delay per rollback based on user-specified limit
        var delayBetweenRollbacks = 60000 / maxRollbacksPerMinute; // Delay in ms for each rollback to stay within the limit (60,000ms = 1 minute)
        var rollbacksMade = 0;
        var startTime = Date.now(); // Time tracking for 1-minute window

        // Start the rollbacks with a delay
        rollbackList.each(function (ind, el) {
            // Ensure we don't exceed the max rollbacks in 1 minute
            var timeElapsed = Date.now() - startTime;
            var timeLeft = 60000 - timeElapsed; // Time remaining in the current minute

            if (rollbacksMade >= maxRollbacksPerMinute) {
                // If we've hit the limit, wait until the next minute
                var waitForNextMinute = 60000 - timeElapsed;
                setTimeout(function () {
                    startTime = Date.now(); // Reset start time for the new minute
                    rollbacksMade = 0; // Reset the rollback counter
                    rollbackOneThingWKMR(el, rbMetadata);
                }, waitForNextMinute);
            } else {
                // If we can proceed with a rollback
                setTimeout(function () {
                    rollbackOneThingWKMR(el, rbMetadata);
                    rollbacksMade++; // Increment the rollback counter
                }, ind * delayBetweenRollbacks); // Apply delay based on user input
            }
        });
    });
}

function rollbackOneThingWKMR(edit, rbMetadata) {
    var userName;
    // If in an anonymous IP range, determine the username for each edit individually.
    if (rbMetadata.userName === null) {
        userName = $(edit).parents("li:first").children("a.mw-anonuserlink").not(".mw-contributions-title").text();
    } else {
        userName = rbMetadata.userName;
    }
    var params = {
        bot: true // Mark rollback as a bot edit
    };
    if (rbMetadata.editSummary !== '') {
        params.summary = rbMetadata.editSummary;
    }
    rbMetadata.api.rollback(decodeURIComponent(rbMetadata.titleRegex.exec(edit.href)[1]), userName, params).done(function () {
        $(edit).after("reverted");
        $(edit).remove();
    });
}

$(document).ready(function () {
    if (mw.config.get("wgCanonicalSpecialPageName") == "Contributions" && $("span.mw-rollback-link").length > 0) {
        mw.loader.using("mediawiki.util").done(function () {
            mw.util.addPortletLink(wkRollbackPortlet, '#', "Rollback all", "ca-rollbackeverything", "rollback all edits displayed here");
            if (!wkContribsCheckboxInit) {
                if ($("ul.mw-contributions-list .mw-revdelundel-link").length > 0) {
                    $("ul.mw-contributions-list .mw-revdelundel-link").each(function (ind, el) {
                        if ($(this).children("a").length > 0) {
                            var revId = /ids=(\d+)/.exec($(this).children("a").attr("href"))[1];
                            var pageTitle = /target=([^&]+)/.exec($(this).children("a").attr("href"))[1];
                            $(el).prepend("<input type='checkbox' name='" + decodeURIComponent(pageTitle) + "' class='revdelIds' value='" + revId + "'>&nbsp;");
                            $(el).children(".revdelIds").data("index", ind);
                        }
                    });
                } else {
                    $("ul.mw-contributions-list a.mw-changeslist-date").each(function (ind, el) {
                        $(el).before("<input type='checkbox' class='revdelIds'>&nbsp;");
                    });
                }
                wkContribsCheckboxInit = true;
            }
            mw.util.addPortletLink(wkRollbackPortlet, '#', "Rollback selected", "ca-rollbacksome", "rollback selected edits");
            $("#ca-rollbackeverything").click(function (event) {
                event.preventDefault();
                mw.loader.load('mediawiki.api'); //start loading, while the user is in the prompt
                return rollbackEverythingWKMR(prompt("Rollback all edits: Enter an edit summary, or leave blank to use the default (or hit Cancel to cancel the rollback entirely)"));
            });
            $("#ca-rollbacksome").click(function (event) {
                event.preventDefault();
                mw.loader.load('mediawiki.api'); //start loading, while the user is in the prompt
                return rollbackSomeThingsWKMR(prompt("Rollback selected edits: Enter an edit summary, or leave blank to use the default (or hit Cancel to cancel the rollback entirely)"));
            });
            $("#ca-rollbacksome").data("lastSelectedIndex", -1);

            $("input.revdelIds").off("click").click(function (ev) {
                var lastSelectedRevdel = $("#ca-rollbacksome").data("lastSelectedIndex");
                var newIndex = $(this).data("index");
                if (ev.shiftKey && lastSelectedRevdel >= 0) {
                    var checkboxArray = $("input.revdelIds");
                    var start = lastSelectedRevdel;
                    var stop = newIndex;
                    if (start < stop) {
                        for (var i = start; i < stop; i++) {
                            if (i != lastSelectedRevdel) {
                                $(checkboxArray[i]).prop("checked", !($(checkboxArray[i]).prop("checked")));
                            }
                        }
                    } else {
                        for (var i = start; i > stop; i--) {
                            if (i != lastSelectedRevdel) {
                                $(checkboxArray[i]).prop("checked", !($(checkboxArray[i]).prop("checked")));
                            }
                        }
                    }
                }
                $("#ca-rollbacksome").data("lastSelectedIndex", newIndex);
            });

        });
    }
});