Jump to content

User:DreamRimmer/massMessageLite.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.
//<nowiki>
//WARNING: You take full responsibility for any actions taken using this script.
//You must read and understand all relevant Wikipedia policies and abide by them
//when using this tool; failure to do so may result in being blocked from editing.

$(document).ready(function() {
    function initializeMassMessage() {
        $('#mw-content-text > p').remove();
        $('#firstHeading').text('massMessageLite');

        var pagesTextarea = new OO.ui.MultilineTextInputWidget({
                placeholder: 'User talk:Example user\nUser talk:Example user2\nWikipedia talk:Example',
                autosize: true,
                rows: 10
            }),
            subjectInputField = new OO.ui.TextInputWidget({
                placeholder: 'Subject of the message'
            }),
            messageTextarea = new OO.ui.MultilineTextInputWidget({
                placeholder: 'Body of the message',
                autosize: true,
                rows: 10
            }),
            summaryInputField = new OO.ui.TextInputWidget({
                placeholder: 'Edit summary'
            }),
            visualPreviewButton = new OO.ui.ButtonWidget({
                label: 'Preview',
                flags: ['primary']
            }),
            startButton = new OO.ui.ButtonWidget({
                label: 'Send Message',
                icon: 'alert',
                flags: ['primary', 'progressive'],
                disabled: true
            }),
            cancelButton = new OO.ui.ButtonWidget({
                label: 'Cancel',
                flags: ['primary', 'destructive'],
                href: 'https:' + mw.config.get('wgServer')
            }),
            skipDuplicateCheckbox = new OO.ui.CheckboxInputWidget({
                selected: false
            }),
            logContainer = $("<ul>").css({
                'padding': '10px',
                'margin-top': '10px',
                'border': '1px solid #ccc',
                'list-style-type': 'none',
                'display': 'table-row',
                'width': '70%'
            }).hide(),
            previewContainer = $('<div>').css({
                'padding': '10px',
                'margin': '10px',
                'overflow': 'auto',
                'min-height': '200px',
                'max-height': '400px',
                'border': '1px solid #ccc',
                'width': '65%',
                'display': 'table-row'
            }).hide();

        $('#mw-content-text').append(
            $('<p>').html('<span style="font-weight: bold; color: red;">Warning:</span> <span style="font-weight: bold; color: black;">You take full responsibility for any actions taken using this script. You must read and understand all relevant <a href="https://en.wikipedia.org/wiki/Wikipedia:Policies_and_guidelines" target="_blank">Wikipedia policies</a> and abide by them when using this tool; failure to do so may result in being <a href="https://en.wikipedia.org/wiki/Wikipedia:Blocking_policy" target="_blank">blocked from editing</a>.</span>'),
            $('<p>').text('Enter list of pages (one per line):').css('font-weight', 'bold'),
            pagesTextarea.$element,
            $('<p>').text('Subject:').css('font-weight', 'bold'),
            subjectInputField.$element,
            $('<p>').text('Message:').css('font-weight', 'bold'),
            messageTextarea.$element,
            $('<p>').text('Edit summary:').css('font-weight', 'bold'),
            summaryInputField.$element,
            $('<div>').css('padding', '10px'),
            $('<label>').append(skipDuplicateCheckbox.$element, ' Skip pages with the same section name already present'),
            $('<div>').css('padding', '10px'),
            $('<div>').append(visualPreviewButton.$element, startButton.$element, cancelButton.$element),
            previewContainer,
            '<br/>',
            logContainer
        );

        function addLogEntry(icon, message, color) {
            var logEntry = $('<li>').css({ 'margin-bottom': '5px', 'color': color }).append(
                new OO.ui.IconWidget({ icon: icon }).$element.css({ 'margin-right': '5px' }),
                $('<span>').text(message)
            );
            logContainer.append(logEntry).show();
        }

        function previewMessage() {
            var subject = subjectInputField.getValue().trim(),
                message = messageTextarea.getValue().trim(),
                wikitext = `== ${subject} ==\n${message}`;

            var previewBox = $('<div>').css({
                'padding': '10px',
                'margin': '10px',
                'overflow': 'auto',
                'width': '70%',
                'height': '400px'
            });

            previewContainer.empty().append($('<h2>').text("Message preview:"), previewBox).show();
            new mw.Api().post({
                action: 'parse',
                text: wikitext,
                title: 'Preview',
                contentmodel: 'wikitext',
                pst: true,
                format: 'json'
            }).done(function(data) {
                previewBox.html(data.parse.text['*']);
                $('html, body').animate({
                    scrollTop: previewContainer.offset().top
                }, 500);
            }).fail(function() {
                previewBox.html('<p>Error loading preview</p>');
            });

            startButton.setDisabled(false);
        }

        function sendMessage() {
            var pages = pagesTextarea.getValue().split("\n").map(page => page.trim()).filter(page => page),
                subject = subjectInputField.getValue().trim(),
                message = messageTextarea.getValue().trim(),
                summary = summaryInputField.getValue().trim() + " (using [[User:DreamRimmer/massMessageLite|massMessageLite]])",
                wikitext = `== ${subject} ==\n${message}`,
                skipDuplicate = skipDuplicateCheckbox.isSelected(),
                userGroups = mw.config.get('wgUserGroups'),
                isSysop = userGroups.includes('sysop'),
                maxMessagesPerMinute = isSysop ? 25 : 15,
                sentMessages = 0,
                startTime = new Date().getTime();

            if (pages.length === 0 || subject === "" || message === "" || summary === "") {
                alert("Error: Please fill in all fields.");
                return;
            }

            logContainer.empty().append($('<h2>').text("Delivery logs:")).css('margin-top', '5px');
            logContainer.show();

            var currentIndex = 0;

            function processNextPage() {
                if (currentIndex >= pages.length) {
                    return;
                }

                var page = pages[currentIndex];

                new mw.Api().get({
                    action: 'query',
                    prop: 'revisions',
                    titles: page,
                    rvprop: 'content',
                    rvslots: '*'
                }).done(function(data) {
                    var pageId = Object.keys(data.query.pages)[0];
                    if (pageId === '-1') {
                        addLogEntry('alert', `Page ${page} does not exist.`, 'red');
                        currentIndex++;
                        processNextPage();
                        return;
                    }

                    var content = data.query.pages[pageId].revisions[0].slots.main['*'];
                    if (content.includes("{{User:DreamRimmer/NoMassMessage}}")) {
                        addLogEntry('alert', `${page} was skipped; (opted-out of message delivery)`, 'red');
                        currentIndex++;
                        processNextPage();
                        return;
                    }

                    if (skipDuplicate && content.includes(`== ${subject} ==`)) {
                        addLogEntry('alert', `Message already exists on ${page}.`, 'red');
                        currentIndex++;
                        processNextPage();
                        return;
                    }

                    var newContent = content + "\n\n" + wikitext;

                    new mw.Api().postWithToken('csrf', {
                        action: 'edit',
                        title: page,
                        text: newContent,
                        summary: summary
                    }).done(function() {
                        addLogEntry('check', `Message sent to ${page}.`, 'green');
                        sentMessages++;
                        currentIndex++;
                        checkRateLimitAndContinue();
                    }).fail(function() {
                        addLogEntry('alert', `Failed to send message to ${page}.`, 'red');
                        currentIndex++;
                        checkRateLimitAndContinue();
                    });
                }).fail(function() {
                    addLogEntry('alert', `Failed to retrieve content of ${page}.`, 'red');
                    currentIndex++;
                    checkRateLimitAndContinue();
                });
            }

            function checkRateLimitAndContinue() {
                var currentTime = new Date().getTime(),
                    elapsedTime = (currentTime - startTime) / 1000,
                    maxMessages = maxMessagesPerMinute,
                    sleepTime = (60 / maxMessages) * sentMessages - elapsedTime;

                if (sleepTime > 0) {
                    addLogEntry('clock', `Sleeping for ${Math.ceil(sleepTime)} seconds to avoid flooding recent changes...`, 'blue');
                    setTimeout(processNextPage, sleepTime * 1000);
                } else {
                    processNextPage();
                }
            }

            processNextPage();

            $('html, body').animate({
                scrollTop: logContainer.offset().top
            }, 500);
        }

        visualPreviewButton.on('click', previewMessage);
        startButton.on('click', sendMessage);
    }

    function checkUserAccess() {
        var username = mw.config.get('wgUserName');
        new mw.Api().get({
            action: 'query',
            titles: 'User:DreamRimmer/massmessage.json',
            prop: 'revisions',
            rvprop: 'content'
        }).done(function(data) {
            var pageId = Object.keys(data.query.pages)[0];
            if (pageId === '-1') {
                alert("Error: Cannot retrieve access control list.");
                window.location.href = mw.config.get('wgServer');
                return;
            }

            var content = data.query.pages[pageId].revisions[0]['*'],
                accessControl = JSON.parse(content);

            if (accessControl.blockedUsers.includes(username)) {
                alert("You are blocked from using this script.");
                window.location.href = mw.config.get('wgServer');
                return;
            }

            if (accessControl.allowedUsers.includes(username)) {
                initializeMassMessage();
            } else {
                var userGroups = mw.config.get('wgUserGroups');
                if (userGroups.includes('extendedconfirmed') || userGroups.includes('sysop')) {
                    initializeMassMessage();
                } else {
                    alert("You do not have permission to use this script. Please contact User:DreamRimmer.");
                    window.location.href = mw.config.get('wgServer');
                }
            }
        }).fail(function() {
            alert("Error: Cannot retrieve access control list.");
            window.location.href = mw.config.get('wgServer');
        });
    }

    $.when(mw.loader.using('mediawiki.util'), $.ready).then(function() {
        mw.util.addPortletLink(
            'p-tb',
            mw.util.getUrl('Special:BlankPage/massMessageLite'),
            'massMessageLite'
        );
    });

    if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' && mw.config.get('wgTitle').split('/', 2)[1] === 'massMessageLite') {
        $.when(mw.loader.using('oojs-ui-core'), $.ready).then(function() {
            checkUserAccess();
        });
    }
});
//</nowiki>