User:DreamRimmer/User not around.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:DreamRimmer/User not around. |
// 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);
}
})();