Utilisateur:Bahati11/Test3.js
Note : après avoir enregistré la page, vous devrez forcer le rechargement complet du cache de votre navigateur pour voir les changements.
Mozilla / Firefox / Konqueror / Safari : maintenez la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou pressez Maj-Ctrl-R (Cmd-R sur Apple Mac) ;
Firefox (sur GNU/Linux) / Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5.(async function () {
'use strict';
const CONFIG = {
usernameToPublish: 'Bahati11',
pageTitle: 'User:Bahati11/Bilan',
periodDays: 30,
topN: 10,
timezone: 'Africa/Kinshasa',
editSummary: 'Mise à jour du bilan automatique (top10).'
};
if (typeof mw === 'undefined' || !mw.Api) return
const api = new mw.Api({ ajax: { url: 'https://fr.wikipedia.org/w/api.php' } });
const wait = ms => new Promise(r => setTimeout(r, ms));
function showProgress(msg) {
let $box = $('#bilanProgressBox');
if (!$box.length) {
$box = $('<div id="bilanProgressBox" style="border:1px solid #aaa; background:#f8f9fa; padding:0.5em; margin:0.5em 0; font-size:90%;"></div>');
$('#mw-content-text').prepend($box);
}
$box.append($('<div>').text(msg));
}
async function fetchRecentChanges() {
showProgress('Récupération des modifications récentes...');
const changes = []
let params = {
action: 'query',
list: 'recentchanges',
rcprop: 'title|ids|user|timestamp|comment|flags|patrolled|sizes|redirect|tags',
rctype: 'edit|new|log',
rcend: new Date(Date.now() - CONFIG.periodDays * 24 * 3600 * 1000).toISOString(),
rcstart: new Date().toISOString(),
rclimit: 'max'
}
let cont = {}
do {
const resp = await api.get(Object.assign({}, params, cont))
if (resp && resp.query && resp.query.recentchanges) changes.push(...resp.query.recentchanges)
cont = resp.continue || {}
await wait(200)
} while (cont && cont.rccontinue)
return changes
}
function aggregate(changes) {
showProgress('Analyse des données...');
const byUser = {}
const byPage = {}
const revertSignals = {}
for (const rc of changes) {
if (!rc.type || (rc.type !== 'edit' && rc.type !== 'new' && rc.type !== 'log')) continue
const user = rc.user || '(unknown)'
const title = rc.title || '(unknown)'
byUser[user] = (byUser[user] || 0) + 1
byPage[title] = (byPage[title] || 0) + 1
if (rc.comment && /revert|rollback|rv|undo|restaur|annul/i.test(rc.comment)) revertSignals[user] = (revertSignals[user] || 0) + 1
}
const topUsers = Object.keys(byUser).map(u => ({ user: u, edits: byUser[u] })).sort((a, b) => b.edits - a.edits)
const topPages = Object.keys(byPage).map(t => ({ title: t, edits: byPage[t] })).sort((a, b) => b.edits - a.edits)
const suspectedVandals = Object.keys(revertSignals).map(u => ({ user: u, revertSignals: revertSignals[u] })).sort((a, b) => b.revertSignals - a.revertSignals)
return { topUsers, topPages, suspectedVandals }
}
async function enrichUsers(listOfUsers) {
showProgress('Récupération des informations sur les utilisateurs...');
const CHUNK = 50
const usersInfo = {}
for (let i = 0; i < listOfUsers.length; i += CHUNK) {
const chunk = listOfUsers.slice(i, i + CHUNK)
const resp = await api.get({ action: 'query', list: 'users', ususers: chunk.join('|'), usprop: 'groups|editcount' })
if (resp && resp.query && resp.query.users) {
for (const u of resp.query.users) usersInfo[u.name] = { name: u.name, groups: u.groups || [], editcount: u.editcount || 0 }
}
await wait(200)
}
return usersInfo
}
function table(items, col1, col2, key1, key2) {
let w = `{| class="wikitable sortable" style="width:60%; text-align:center; margin:1em auto;"\n! Rang !! ${col1} !! ${col2}\n`
for (let i = 0; i < Math.min(CONFIG.topN, items.length); i++) {
const it = items[i]
w += `|-${'\n'}| ${i + 1} || ${it[key1]} || ${it[key2]}\n`
}
w += '|}\n\n'
return w
}
function buildWikitext(stats, botsList, patrollersList) {
const now = new Date()
let header = `== Bilan des ${CONFIG.topN} (derniers ${CONFIG.periodDays} jours) ==\n<small>Dernière mise à jour : ${now.toUTCString()} (${CONFIG.timezone}) — [[${CONFIG.pageTitle}|Mettre à jour maintenant]]</small>\n\n`
let body = header
body += `=== Meilleurs articles ===\n${table(stats.topPages, 'Article', 'Éditions', 'title', 'edits')}`
body += `=== Meilleurs contributeurs ===\n${table(stats.topUsers, 'Utilisateur', 'Éditions', 'user', 'edits')}`
body += `=== Meilleurs bots ===\n${table(botsList, 'Bot', 'Éditions', 'user', 'edits')}`
body += `=== Patrouilleurs actifs ===\n${table(patrollersList, 'Patrouilleur', 'Éditions', 'user', 'edits')}`
body += `=== Vandales présumés ===\n${table(stats.suspectedVandals, 'Utilisateur', 'Signaux', 'user', 'revertSignals')}`
return body
}
async function publishWikitext(text) {
showProgress('Publication sur la page...');
const tokenResp = await api.get({ action: 'query', meta: 'tokens', type: 'csrf' })
return api.postWithToken('csrf', { action: 'edit', title: CONFIG.pageTitle, text: text, summary: CONFIG.editSummary })
}
async function runUpdate() {
showProgress('Démarrage de la mise à jour...');
const changes = await fetchRecentChanges()
const stats = aggregate(changes)
const usersToCheck = Array.from(new Set(stats.topUsers.map(u => u.user).concat(stats.suspectedVandals.map(u => u.user))))
const usersInfo = await enrichUsers(usersToCheck)
const botsList = Object.values(usersInfo).filter(i => i.groups.includes('bot')).map(i => ({ user: i.name, edits: i.editcount })).sort((a, b) => b.edits - a.edits)
const patrollersList = Object.values(usersInfo).filter(i => i.groups.includes('patroller') || i.groups.includes('autoreview') || i.groups.includes('reviewer')).map(i => ({ user: i.name, edits: i.editcount })).sort((a, b) => b.edits - a.edits)
const wikitext = buildWikitext(stats, botsList, patrollersList)
await publishWikitext(wikitext)
showProgress('Mise à jour terminée.');
}
async function generateIfEmpty() {
const resp = await api.get({ action: 'query', prop: 'revisions', rvprop: 'content', titles: CONFIG.pageTitle })
const pages = resp.query.pages
const firstPage = pages[Object.keys(pages)[0]]
if (!firstPage.revisions) {
showProgress('Page inexistante détectée. Première génération en cours...');
await runUpdate()
location.reload()
}
}
function addLinkToTools() {
const $tools = $('#p-tb ul');
if ($tools.length && !$('#majBLink').length) {
const $link = $('<li id="majBLink"><a href="#">MàJ-B</a></li>');
$tools.append($link);
$link.find('a').on('click', async function (e) {
e.preventDefault();
$('#bilanProgressBox').remove();
await runUpdate();
alert('Mise à jour du Bilan terminée');
});
}
}
if (mw.config.get('wgPageName') === CONFIG.pageTitle.replace(/ /g, '_')) {
const link = $('<span style="float:right; font-size:small;">[<a href="#" id="updateBilan">Mettre à jour</a>]</span>')
$('#firstHeading').append(link)
$('#updateBilan').on('click', async function (e) {
e.preventDefault()
$('#bilanProgressBox').remove()
await runUpdate()
location.reload()
})
generateIfEmpty()
}
addLinkToTools();
})();