// 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: '[[w:User:DreamRimmer/User not around.js|User not around]]',
version: '3.4'
};
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} v${config.version}`;
}
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);
}
})();