2023-01-19 14:59:01 +08:00

182 lines
5.8 KiB
JavaScript

const url = require('url');
const util = require('util');
const fetch = require('node-fetch');
const groupArray = require('group-array');
const cron = require('node-cron');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = async function(robot, kredits) {
function messageRoom(message) {
robot.messageRoom(process.env.KREDITS_ROOM, message);
}
robot.logger.debug('[hubot-kredits] Loading MediaWiki integration...')
const Contributor = kredits.Contributor;
const Contribution = kredits.Contribution;
const wikiURL = process.env.KREDITS_MEDIAWIKI_URL;
const apiURL = wikiURL + 'api.php';
function getContributorByWikiUser(username) {
let account = {
site: url.parse(process.env.KREDITS_MEDIAWIKI_URL).hostname,
username: username
}
return Contributor.findByAccount(account).then(contributor => {
robot.logger.debug('CONTRIBUTOR: ', contributor)
if (contributor) { return contributor; } else { throw new Error(); }
});
}
function createContribution(username, date, amount, description, url, details={}) {
return getContributorByWikiUser(username).then(contributor => {
robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${contributor.name} for ${url}...`);
let contribution = {
contributorId: contributor.id,
contributorIpfsHash: contributor.ipfsHash,
date,
amount: amount,
url,
description,
details,
kind: 'docs'
};
return Contribution.add(contribution).catch(error => {
robot.logger.error(`[hubot-kredits] Adding contribution failed:`, error);
});
}).catch(() => {
robot.logger.info(`[hubot-kredits] No contributor found for ${username}`);
messageRoom(`I wanted to propose giving kredits to wiki user ${username}, but I cannot find their info. Please add them as a contributor: https://kredits.kosmos.org`);
});
}
function fetchChanges () {
const params = [
'action=query',
'format=json',
'list=recentchanges',
'rctype=edit|new',
'rcshow=!minor|!bot|!anon|!redirect',
'rclimit=max',
'rcprop=ids|title|timestamp|user|sizes|comment|flags'
];
let endTime = robot.brain.get('kredits:mediawiki:last_processed_at');
if (endTime) {
robot.logger.debug(`[hubot-kredits] Fetching wiki edits since ${endTime}`);
params.push(`rcend=${endTime}`);
}
const url = `${apiURL}?${params.join('&')}`;
return fetch(url).then(res => {
if (res.status === 200) {
return res.json();
} else {
robot.logger.info(`Fetching ${url} returned HTTP status ${res.status}:`);
robot.logger.info(res.body);
throw Error('Unexpected response from '+url);
}
}).then(res => {
return res.query.recentchanges;
}).catch(res => {
robot.logger.error(`[hubot-kredits] Failed to fetch ${url} (likely due to a network issue)`);
});
}
function groupChangesByUser (changes) {
return Promise.resolve(groupArray(changes, 'user'));
}
function analyzeUserChanges (user, changes) {
// robot.logger.debug(`Analyzing ${changes.length} edits from ${user} ...`);
const results = {};
results.pagesCreated = changes.filter(c => c.type === 'new');
results.pagesChanged = changes.filter(c => c.type === 'edit');
results.charsAdded = changes
.map(c => { return (c.oldlen < c.newlen) ? (c.newlen - c.oldlen) : 0; })
.reduce((a, b) => a + b);
// robot.logger.debug(`Created ${results.pagesCreated.length} pages`);
// robot.logger.debug(`Edited ${results.pagesChanged.length} pages`);
// robot.logger.debug(`Added ${results.charsAdded} lines of text\n`);
return results;
}
async function createContributions (changes) {
for (const user of Object.keys(changes)) {
await createContributionForUserChanges(user, changes[user]);
await sleep(60000);
}
}
function pageTitlesFromChanges(changes) {
return [...new Set(changes.map(c => `"${c.title}"`))].join(', ');
}
// Currently not used
function calculateAmountForChanges(details) {
let amount;
if (details.charsAdded < 500) {
amount = 500;
} else if (details.charsAdded < 2000) {
amount = 1500;
} else {
amount = 5000;
}
return amount;
}
function createContributionForUserChanges (user, changes) {
const dateNow = new Date();
const dateYesterday = dateNow.setDate(dateNow.getDate() - 1);
const date = (new Date(dateYesterday)).toISOString().split('T')[0];
const details = analyzeUserChanges(user, changes);
const amount = 500;
let desc = `Added ${details.charsAdded} characters of text.`;
if (details.pagesChanged.length > 0) {
desc = `Edited ${pageTitlesFromChanges(details.pagesChanged)}. ${desc}`;
}
if (details.pagesCreated.length > 0) {
desc = `Created ${pageTitlesFromChanges(details.pagesCreated)}. ${desc}`;
}
desc = `Wiki contributions: ${desc}`;
let url;
if (changes.length > 1) {
url = `${wikiURL}Special:Contributions/${user}?hideMinor=1`;
} else {
rc = changes[0];
url = `${wikiURL}index.php?title=${rc.title}&diff=${rc.revid}&oldid=${rc.old_revid}`;
}
return createContribution(user, date, amount, desc, url, details);
}
function updateTimestampForNextFetch () {
robot.logger.debug(`[hubot-kredits] Set timestamp for wiki changes fetch`);
robot.brain.set('kredits:mediawiki:last_processed_at', new Date().toISOString());
}
function processWikiChangesSinceLastRun () {
fetchChanges()
.then(res => groupChangesByUser(res))
.then(res => createContributions(res))
.then(() => updateTimestampForNextFetch());
}
cron.schedule('0 7 * * *', processWikiChangesSinceLastRun);
};