Jump to content

User:DreamRimmer/User not around.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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>
// Fork of [[User:Andrybak/Scripts/Not around.js]]
// This script helps you add a "Not around" template to a user's talk page if they haven't edited in the last 6 months

(function() {
    'use strict';

    const config = {
        wikipage: '[[User:DreamRimmer/User not around|User not around.js]]',
    };

    const USERSCRIPT_NAME = 'Not around userscript';
    const LOG_PREFIX = `[${USERSCRIPT_NAME}]:`;

    function error(...toLog) {
        console.error(LOG_PREFIX, ...toLog);
    }

    function warn(...toLog) {
        console.warn(LOG_PREFIX, ...toLog);
    }

    function info(...toLog) {
        console.info(LOG_PREFIX, ...toLog);
    }

    function debug(...toLog) {
        console.debug(LOG_PREFIX, ...toLog);
    }

    function notify(notificationMessage) {
        mw.notify(notificationMessage, {
            title: USERSCRIPT_NAME
        });
    }

    function errorAndNotify(errorMessage, rejection) {
        error(errorMessage, rejection);
        notify(errorMessage);
    }

    const ABSENSE_MONTHS_MINIMUM = 6;
    const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    const mw = window.mw;
    const DEBUG = false;

    function constructAd() {
        return `(using ${config.wikipage})`;
    }

    function constructEditSummary(username, lastContribDate) {
        const day = lastContribDate.getUTCDate();
        const month = MONTHS[lastContribDate.getUTCMonth()];
        const year = lastContribDate.getUTCFullYear();
        return `/* top */ add [[Template:Not around]] – user ${username} hasn't edited since ${day} ${month} ${year} (${constructAd()})`;
    }

    /**
     * Asynchronously load specified number of contributions of specified username.
     */
    function loadNLastUserContribs(username, n) {
        const api = new mw.Api();
        return api.get({
            action: 'query',
            list: 'usercontribs',
            ucuser: username,
            uclimit: n
        });
    }

    /**
     * Asynchronously load the very last contribution of specified username.
     */
    function loadLastUserContrib(username) {
        return new Promise((resolve, reject) => {
            loadNLastUserContribs(username, 1).then(response => {
                debug(response);
                const lastContrib = response.query.usercontribs[0];
                resolve(lastContrib);
            }, rejection => {
                reject(rejection);
            });
        });
    }

    function isoStringToDate(timestamp) {
        const d = new Date(timestamp);
        return d;
    }

    function loadCurrentWikitext(pagename) {
        return new Promise((resolve, reject) => {
            const api = new mw.Api();
            api.get({
                action: 'query',
                titles: pagename,
                prop: 'revisions',
                rvprop: 'content',
                rvslots: 'main',
                formatversion: 2
            }).then(response => {
                resolve(response.query.pages[0].revisions[0].slots.main.content);
            }, rejection => {
                reject(rejection);
            });
        });
    }

    function addNotAroundTemplateIfAbsent(username, lastContribDate) {
    const day = lastContribDate.getUTCDate();
    const month = MONTHS[lastContribDate.getUTCMonth()];
    const year = lastContribDate.getUTCFullYear();
    info(`${username} hasn't edited since ${day} ${month} ${year}.`);

    const userTalkPageTitle = 'User_talk:' + username;
    loadCurrentWikitext(userTalkPageTitle).then(wikitext => {
        const regex = /\{\{\s*not\s*around\s*(?:\s*\|[^}]+)?\s*\}\}/i;
        if (regex.test(wikitext)) {
            info(userTalkPageTitle + ' already has the template. Showing it to the user and aborting.');
            location.assign('/wiki/' + userTalkPageTitle);
            return;
        }

        const formHtml = `
            <form id="templateChoiceForm">
            <p>Choose the appropriate option:</p>
                <label>
                    <input type="checkbox" id="inactiveCheckbox" />
                    This user is not currently active on Wikipedia
                </label><br>
                <label>
                    <input type="checkbox" id="leftCheckbox" checked />
                    This user may have left Wikipedia (default)
                </label>
            </form>
        `;

        const dialog = $('<div>').html(formHtml).dialog({
            title: 'Not around',
            width: 400,
            buttons: {
                'Preview': function() {
                    const isInactive = $('#inactiveCheckbox').prop('checked');
                    const isLeft = $('#leftCheckbox').prop('checked');

                    let template;
                    if (isInactive) {
                        template = `{{Not around|is not currently active on Wikipedia|date=${day} ${month} ${year}}}`;
                    } else if (isLeft) {
                        template = `{{Not around|date=${day} ${month} ${year}}}`;
                    }

                    const newWikitext = `${template}\n` + wikitext;
                    const editSummary = constructEditSummary(username, lastContribDate);

                    if (DEBUG) {
                        debug(newWikitext.slice(0, 40));
                        debug(editSummary);
                    }

                    const previewWikitext = newWikitext.split("\n").slice(0, 10).join("\n");
                    const previewText = previewWikitext.replace(template, `<span style="background-color: #d4fdd4">${template}</span>`);

                    const previewDialog = $('<div>').html(`
                        <p><strong>Preview of the edit (Wikitext):</strong></p>
                        <pre>${previewText}</pre>
                        <p><strong>Do you want to add the above template to the user talk page?</strong></p>
                    `).dialog({
                        title: 'Preview',
                        buttons: {
                            'Submit': function() {
                                const api = new mw.Api();
                                api.postWithEditToken({
                                    action: 'edit',
                                    title: userTalkPageTitle,
                                    text: newWikitext,
                                    summary: editSummary
                                }).then(response => {
                                    loadLastUserContrib(mw.user.getName()).then(theEdit => {
                                        location.assign('/wiki/Special:Diff/' + theEdit.revid);
                                    }, rejection => {
                                        errorAndNotify(`Cannot load last contribution by ${mw.user.getName()}.`, rejection);
                                    });
                                }, rejection => {
                                    errorAndNotify(`Cannot edit page [[${userTalkPageTitle}]]`, rejection);
                                });

                                $(this).dialog('close');
                            },
                            'Cancel': function() {
                                $(this).dialog('close');
                            }
                        }
                    });

                    $(this).dialog('close');
                },
                'Cancel': function() {
                    $(this).dialog('close');
                }
            }
        });

        $('#inactiveCheckbox').change(function() {
            if ($(this).prop('checked')) {
                $('#leftCheckbox').prop('checked', false);
            }
        });

        $('#leftCheckbox').change(function() {
            if ($(this).prop('checked')) {
                $('#inactiveCheckbox').prop('checked', false);
            }
        });
    });
}

    function runPortlet () {
        const username = mw.config.get('wgRelevantUserName');
        if (!username) {
            errorAndNotify('Cannot find a username', null);
            return;
        }
        loadLastUserContrib(username).then(lastContrib => {
            if (!lastContrib) {
                notify(`User ${username} has zero contributions. Aborting.`);
                return;
            }
            if (lastContrib.user != username) {
                errorAndNotify(`Received wrong user. Actual ${lastContrib.user} ≠ expected ${username}. Aborting.`, null);
                return;
            }
            const lastContribDate = isoStringToDate(lastContrib.timestamp);
            const currentDate = new Date();
            info('Last edit timestamp =', lastContrib.timestamp);

            const daysSinceLastEdit = Math.floor((currentDate - lastContribDate) / (1000 * 60 * 60 * 24));
            if (daysSinceLastEdit >= ABSENSE_MONTHS_MINIMUM * 30) {
                addNotAroundTemplateIfAbsent(username, lastContribDate);
            } else {
                notify(`${username} is still an active user. Last edit was on ${lastContribDate.getUTCDate()} ${MONTHS[lastContribDate.getUTCMonth()]} ${lastContribDate.getUTCFullYear()}. Aborting.`);
            }
        }, rejection => {
            errorAndNotify(`Cannot load contributions of ${username}. Aborting.`, rejection);
        });
    }

    function lazyLoadNotAround() {
        debug('Loading...');
        const namespaceNumber = mw.config.get('wgNamespaceNumber');
        if (namespaceNumber === -1 || namespaceNumber === 2 || namespaceNumber === 3) {
            if (!mw.loader.using) {
                warn('Function mw.loader.using is not loaded yet. Retrying...');
                setTimeout(lazyLoadNotAround, 300);
                return;
            }
            mw.loader.using(
                ['mediawiki.util'],
                () => {
                    const link = mw.util.addPortletLink('p-cactions', '#', 'Not around', 'ca-notaround', 'add template {{Not around}}');
                    if (!link) {
                        info('Cannot create portlet link (mw.util.addPortletLink). Assuming unsupported skin. Aborting.');
                        return;
                    }
                    link.onclick = event => {
                        event.preventDefault();
                        mw.loader.using('mediawiki.api', runPortlet);
                    };
                },
                (e) => {
                    error('Cannot add portlet link', e);
                }
            );
        } else {
            warn('Triggered on a bad namespace =', namespaceNumber);
        }
    }

    if (document.readyState !== 'loading') {
        lazyLoadNotAround();
    } else {
        warn('Cannot load yet. Setting up a listener...');
        document.addEventListener('DOMContentLoaded', lazyLoadNotAround);
    }
})();
//</nowiki>