Merge pull request #16 from 67P/feature/6-mediawiki_integration
MediaWiki integration & integration architecture improvements
This commit is contained in:
commit
b852bd24af
230
index.js
230
index.js
@ -31,34 +31,51 @@ const ipfsConfig = {
|
||||
};
|
||||
|
||||
module.exports = async function(robot) {
|
||||
let wallet;
|
||||
try {
|
||||
wallet = await ethers.Wallet.fromEncryptedWallet(walletJson, process.env.KREDITS_WALLET_PASSWORD);
|
||||
} catch(error) {
|
||||
console.log('[hubot-kredits] Could not load wallet:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ethProvider = new ethers.providers.JsonRpcProvider(providerUrl, {chainId: networkId});
|
||||
ethProvider.signer = wallet;
|
||||
wallet.provider = ethProvider;
|
||||
|
||||
let kredits;
|
||||
try {
|
||||
kredits = await Kredits.setup(ethProvider, wallet, ipfsConfig);
|
||||
} catch(error) {
|
||||
console.log('[hubot-kredits] Could not setup kredits:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
const Contributor = kredits.Contributor;
|
||||
const Operator = kredits.Operator;
|
||||
|
||||
function messageRoom(message) {
|
||||
robot.messageRoom(process.env.KREDITS_ROOM, message);
|
||||
}
|
||||
|
||||
//
|
||||
// Ethereum wallet setup
|
||||
//
|
||||
|
||||
let wallet;
|
||||
try {
|
||||
wallet = await ethers.Wallet.fromEncryptedWallet(walletJson, process.env.KREDITS_WALLET_PASSWORD);
|
||||
} catch(error) {
|
||||
robot.logger.warn('[hubot-kredits] Could not load wallet:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Ethereum provider/node setup
|
||||
//
|
||||
|
||||
const ethProvider = new ethers.providers.JsonRpcProvider(providerUrl, {chainId: networkId});
|
||||
ethProvider.signer = wallet;
|
||||
wallet.provider = ethProvider;
|
||||
|
||||
//
|
||||
// Kredits contracts setup
|
||||
//
|
||||
|
||||
let kredits;
|
||||
try {
|
||||
kredits = await Kredits.setup(ethProvider, wallet, ipfsConfig);
|
||||
} catch(error) {
|
||||
robot.logger.warn('[hubot-kredits] Could not set up kredits:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
const Contributor = kredits.Contributor;
|
||||
const Operator = kredits.Operator;
|
||||
|
||||
robot.logger.info('[hubot-kredits] Wallet address: ' + wallet.address);
|
||||
|
||||
//
|
||||
// Check robot's wallet balance and alert when it's broke
|
||||
//
|
||||
|
||||
ethProvider.getBalance(wallet.address).then(balance => {
|
||||
robot.logger.info('[hubot-kredits] Wallet balance: ' + ethers.utils.formatEther(balance) + 'ETH');
|
||||
if (balance.lt(ethers.utils.parseEther('0.0001'))) {
|
||||
@ -66,6 +83,10 @@ module.exports = async function(robot) {
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Robot chat commands/interaction
|
||||
//
|
||||
|
||||
robot.respond(/got ETH\??/i, res => {
|
||||
ethProvider.getBalance(wallet.address).then((balance) => {
|
||||
res.send(`my wallet contains ${ethers.utils.formatEther(balance)} ETH`);
|
||||
@ -93,163 +114,16 @@ module.exports = async function(robot) {
|
||||
});
|
||||
});
|
||||
|
||||
function getContributorByGithubUser(username) {
|
||||
return Contributor.all().then(contributors => {
|
||||
let contrib = contributors.find(c => {
|
||||
return c.github_username === username;
|
||||
});
|
||||
if (!contrib) {
|
||||
throw new Error(`No contributor found for ${username}`);A
|
||||
} else {
|
||||
return contrib;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createProposal(githubUser, amount, description, url, details) {
|
||||
return getContributorByGithubUser(githubUser).then((contributor) => {
|
||||
robot.logger.debug(`[kredits] Creating proposal to issue ${amount}₭S to ${githubUser} for ${url}...`);
|
||||
let contributionAttr = {
|
||||
contributorId: contributor.id,
|
||||
amount: amount,
|
||||
contributorIpfsHash: contributor.ipfsHash,
|
||||
url,
|
||||
description,
|
||||
details,
|
||||
kind: 'dev'
|
||||
};
|
||||
return Operator.addProposal(contributionAttr).then((result) => {
|
||||
robot.logger.debug('[kredits] proposal created:', util.inspect(result));
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.log([hubot-kredits] Error:, error);
|
||||
messageRoom(`I wanted to propose giving kredits to ${githubUser} for ${url}, but I can't find their contact data. Please add them as a contributor: https://kredits.kosmos.org`);
|
||||
});
|
||||
}
|
||||
|
||||
function amountFromIssueLabels(issue) {
|
||||
let kreditsLabel = issue.labels.map(l => l.name)
|
||||
.filter(n => n.match(/^kredits/))[0];
|
||||
// No label, no kredits
|
||||
if (typeof kreditsLabel === 'undefined') { return 0; }
|
||||
|
||||
// TODO move to config maybe?
|
||||
let amount;
|
||||
switch(kreditsLabel) {
|
||||
case 'kredits-1':
|
||||
amount = 50;
|
||||
break;
|
||||
case 'kredits-2':
|
||||
amount = 150;
|
||||
break;
|
||||
case 'kredits-3':
|
||||
amount = 500;
|
||||
break;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
function handleGitHubIssueClosed(data) {
|
||||
let recipients;
|
||||
let issue = data.issue;
|
||||
let assignees = issue.assignees.map(a => a.login);
|
||||
let web_url = issue.html_url;
|
||||
|
||||
let amount = amountFromIssueLabels(issue);
|
||||
if (amount === 0) {
|
||||
console.log('[hubot-kredits] Proposal amount from issue label is zero; ignoring');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (assignees.length > 0) {
|
||||
recipients = assignees;
|
||||
} else {
|
||||
recipients = [issue.user.login];
|
||||
}
|
||||
|
||||
let repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1];
|
||||
let description = `${repoName}: ${issue.title}`;
|
||||
|
||||
let proposalPromisses = [];
|
||||
recipients.forEach(recipient => {
|
||||
proposalPromisses.push(
|
||||
createProposal(recipient, amount, description, web_url, issue)
|
||||
.catch(err => robot.logger.error(err))
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(proposalPromisses);
|
||||
}
|
||||
|
||||
function handleGitHubPullRequestClosed(data) {
|
||||
let recipients;
|
||||
let pull_request = data.pull_request;
|
||||
let assignees = pull_request.assignees.map(a => a.login);
|
||||
let web_url = pull_request._links.html.href;
|
||||
let pr_issue_url = pull_request.issue_url;
|
||||
|
||||
if (assignees.length > 0) {
|
||||
recipients = assignees;
|
||||
} else {
|
||||
recipients = [pull_request.user.login];
|
||||
}
|
||||
|
||||
return fetch(pr_issue_url)
|
||||
.then(response => {
|
||||
if (response.status >= 400) {
|
||||
throw new Error('Bad response from fetching PR issue');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(issue => {
|
||||
let amount = amountFromIssueLabels(issue);
|
||||
if (amount === 0) {
|
||||
console.log('[hubot-kredits] Proposal amount from issue label is zero; ignoring');
|
||||
return;
|
||||
}
|
||||
|
||||
let repoName = pull_request.base.repo.full_name;
|
||||
let description = `${repoName}: ${pull_request.title}`;
|
||||
|
||||
let proposalPromisses = [];
|
||||
recipients.forEach(recipient => {
|
||||
console.debug(`[hubot-kredits] Creating proposal for ${recipient}...`);
|
||||
proposalPromisses.push(
|
||||
createProposal(recipient, amount, description, web_url, pull_request)
|
||||
.catch(err => robot.logger.error(err))
|
||||
);
|
||||
});
|
||||
return Promise.all(proposalPromisses);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
robot.router.post('/incoming/kredits/github/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => {
|
||||
let evt = req.header('X-GitHub-Event');
|
||||
let data = req.body;
|
||||
// For some reason data is contained in a payload property on one
|
||||
// machine, but directly in the root of the object on others
|
||||
if (data.payload) { data = JSON.parse(data.payload); }
|
||||
|
||||
robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`);
|
||||
|
||||
if (evt === 'pull_request' && data.action === 'closed') {
|
||||
handleGitHubPullRequestClosed(data).then(() => res.send(200));
|
||||
}
|
||||
else if (evt === 'issues' && data.action === 'closed') {
|
||||
handleGitHubIssueClosed(data).then(() => res.send(200));
|
||||
} else {
|
||||
res.send(200);
|
||||
}
|
||||
});
|
||||
//
|
||||
// Smart contract events
|
||||
//
|
||||
|
||||
function watchContractEvents() {
|
||||
ethProvider.getBlockNumber().then((blockNumber) => {
|
||||
// current block is the last mined one, thus we check from the next
|
||||
// mined one onwards to prevent getting previous events
|
||||
let nextBlock = blockNumber + 1;
|
||||
robot.logger.debug(`[kredits] watching events from block ${nextBlock} onward`);
|
||||
robot.logger.debug(`[hubot-kredits] Watching events from block ${nextBlock} onward`);
|
||||
ethProvider.resetEventsBlock(nextBlock);
|
||||
|
||||
Operator.on('ProposalCreated', handleProposalCreated);
|
||||
@ -259,7 +133,7 @@ module.exports = async function(robot) {
|
||||
function handleProposalCreated(proposalId, creatorAccount, contributorId, amount) {
|
||||
Contributor.getById(contributorId).then((contributor) => {
|
||||
Operator.getById(proposalId).then((proposal) => {
|
||||
console.debug('Proposal created:', proposal);
|
||||
robot.logger.debug(`[hubot-kredits] Proposal created (${proposal.description})`);
|
||||
// messageRoom(`Let's give ${contributor.name} some kredits for ${proposal.url} (${proposal.description}): https://kredits.kosmos.org`);
|
||||
});
|
||||
});
|
||||
@ -267,4 +141,14 @@ module.exports = async function(robot) {
|
||||
|
||||
watchContractEvents();
|
||||
|
||||
//
|
||||
// Integrations
|
||||
//
|
||||
|
||||
require('./integrations/github')(robot, kredits);
|
||||
|
||||
if (typeof process.env.KREDITS_MEDIAWIKI_URL !== 'undefined') {
|
||||
require('./integrations/mediawiki')(robot, kredits);
|
||||
}
|
||||
|
||||
};
|
||||
|
178
integrations/github.js
Normal file
178
integrations/github.js
Normal file
@ -0,0 +1,178 @@
|
||||
const util = require('util');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
module.exports = async function(robot, kredits) {
|
||||
|
||||
function messageRoom(message) {
|
||||
robot.messageRoom(process.env.KREDITS_ROOM, message);
|
||||
}
|
||||
|
||||
robot.logger.debug('[hubot-kredits] Loading GitHub integration...');
|
||||
|
||||
let repoBlackList = [];
|
||||
if (process.env.KREDITS_GITHUB_REPO_BLACKLIST) {
|
||||
repoBlackList = process.env.KREDITS_GITHUB_REPO_BLACKLIST.split(',');
|
||||
robot.logger.debug('[hubot-kredits] Ignoring GitHub actions from ', util.inspect(repoBlackList));
|
||||
}
|
||||
|
||||
const Contributor = kredits.Contributor;
|
||||
const Operator = kredits.Operator;
|
||||
|
||||
function getContributorByGithubUser(username) {
|
||||
return Contributor.all().then(contributors => {
|
||||
let contrib = contributors.find(c => {
|
||||
return c.github_username === username;
|
||||
});
|
||||
if (!contrib) {
|
||||
throw new Error(`No contributor found for ${username}`);
|
||||
} else {
|
||||
return contrib;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createProposal(githubUser, amount, description, url, details) {
|
||||
return getContributorByGithubUser(githubUser).then(contributor => {
|
||||
robot.logger.debug(`[hubot-kredits] Creating proposal to issue ${amount}₭S to ${githubUser} for ${url}...`);
|
||||
|
||||
let contributionAttr = {
|
||||
contributorId: contributor.id,
|
||||
amount: amount,
|
||||
contributorIpfsHash: contributor.ipfsHash,
|
||||
url,
|
||||
description,
|
||||
details,
|
||||
kind: 'dev'
|
||||
};
|
||||
|
||||
return Operator.addProposal(contributionAttr).catch(error => {
|
||||
robot.logger.error(`[hubot-kredits] Error:`, error);
|
||||
messageRoom(`I wanted to propose giving kredits to GitHub user ${githubUser} for ${url}, but I cannot find their info. Please add them as a contributor: https://kredits.kosmos.org`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function amountFromIssueLabels(issue) {
|
||||
let kreditsLabel = issue.labels.map(l => l.name)
|
||||
.filter(n => n.match(/^kredits/))[0];
|
||||
// No label, no kredits
|
||||
if (typeof kreditsLabel === 'undefined') { return 0; }
|
||||
|
||||
// TODO move to config maybe?
|
||||
let amount;
|
||||
switch(kreditsLabel) {
|
||||
case 'kredits-1':
|
||||
amount = 500;
|
||||
break;
|
||||
case 'kredits-2':
|
||||
amount = 1500;
|
||||
break;
|
||||
case 'kredits-3':
|
||||
amount = 5000;
|
||||
break;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
function handleGitHubIssueClosed(data) {
|
||||
let recipients;
|
||||
let issue = data.issue;
|
||||
let assignees = issue.assignees.map(a => a.login);
|
||||
let web_url = issue.html_url;
|
||||
|
||||
let amount = amountFromIssueLabels(issue);
|
||||
let repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1];
|
||||
let description = `${repoName}: ${issue.title}`;
|
||||
|
||||
if (amount === 0) {
|
||||
robot.logger.info('[hubot-kredits] Proposal amount from issue label is zero; ignoring');
|
||||
return Promise.resolve();
|
||||
} else if (repoBlackList.includes(repoName)) {
|
||||
robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (assignees.length > 0) {
|
||||
recipients = assignees;
|
||||
} else {
|
||||
recipients = [issue.user.login];
|
||||
}
|
||||
|
||||
let proposalPromises = [];
|
||||
recipients.forEach(recipient => {
|
||||
proposalPromises.push(
|
||||
createProposal(recipient, amount, description, web_url, issue)
|
||||
.catch(err => robot.logger.error(err))
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(proposalPromises);
|
||||
}
|
||||
|
||||
function handleGitHubPullRequestClosed(data) {
|
||||
let recipients;
|
||||
let pull_request = data.pull_request;
|
||||
let assignees = pull_request.assignees.map(a => a.login);
|
||||
let web_url = pull_request._links.html.href;
|
||||
let pr_issue_url = pull_request.issue_url;
|
||||
|
||||
if (assignees.length > 0) {
|
||||
recipients = assignees;
|
||||
} else {
|
||||
recipients = [pull_request.user.login];
|
||||
}
|
||||
|
||||
return fetch(pr_issue_url)
|
||||
.then(response => {
|
||||
if (response.status >= 400) {
|
||||
throw new Error('Bad response from fetching PR issue');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(issue => {
|
||||
let amount = amountFromIssueLabels(issue);
|
||||
let repoName = pull_request.base.repo.full_name;
|
||||
let description = `${repoName}: ${pull_request.title}`;
|
||||
|
||||
if (amount === 0) {
|
||||
robot.logger.info('[hubot-kredits] Proposal amount from issue label is zero; ignoring');
|
||||
return Promise.resolve();
|
||||
} else if (repoBlackList.includes(repoName)) {
|
||||
robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let proposalPromises = [];
|
||||
recipients.forEach(recipient => {
|
||||
console.debug(`[hubot-kredits] Creating proposal for ${recipient}...`);
|
||||
proposalPromises.push(
|
||||
createProposal(recipient, amount, description, web_url, pull_request)
|
||||
.catch(err => robot.logger.error(err))
|
||||
);
|
||||
});
|
||||
|
||||
return Promise.all(proposalPromises);
|
||||
});
|
||||
}
|
||||
|
||||
robot.router.post('/incoming/kredits/github/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => {
|
||||
let evt = req.header('X-GitHub-Event');
|
||||
let data = req.body;
|
||||
// For some reason data is contained in a payload property on one
|
||||
// machine, but directly in the root of the object on others
|
||||
if (data.payload) { data = JSON.parse(data.payload); }
|
||||
|
||||
robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`);
|
||||
|
||||
if (evt === 'pull_request' && data.action === 'closed') {
|
||||
handleGitHubPullRequestClosed(data).then(() => res.send(200));
|
||||
}
|
||||
else if (evt === 'issues' && data.action === 'closed') {
|
||||
handleGitHubIssueClosed(data).then(() => res.send(200));
|
||||
} else {
|
||||
res.send(200);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
178
integrations/mediawiki.js
Normal file
178
integrations/mediawiki.js
Normal file
@ -0,0 +1,178 @@
|
||||
const util = require('util');
|
||||
const fetch = require('node-fetch');
|
||||
const groupArray = require('group-array');
|
||||
const cron = require('node-cron');
|
||||
|
||||
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 Operator = kredits.Operator;
|
||||
|
||||
const apiURL = process.env.KREDITS_MEDIAWIKI_URL + 'api.php';
|
||||
|
||||
function getContributorByWikiUser(username) {
|
||||
return Contributor.all().then(contributors => {
|
||||
let contrib = contributors.find(c => {
|
||||
if (typeof c.accounts !== 'object') { return false; }
|
||||
return c.accounts.find(a => {
|
||||
a.url === `${process.env.KREDITS_MEDIAWIKI_URL}User:${username}`;
|
||||
});
|
||||
});
|
||||
if (!contrib) {
|
||||
throw new Error();
|
||||
} else {
|
||||
return contrib;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createProposal(username, amount, description, url, details={}) {
|
||||
return getContributorByWikiUser(username).then(contributor => {
|
||||
robot.logger.debug(`[hubot-kredits] Creating proposal to issue ${amount}₭S to ${contributor.name} for ${url}...`);
|
||||
|
||||
let contribution = {
|
||||
contributorId: contributor.id,
|
||||
amount: amount,
|
||||
contributorIpfsHash: contributor.ipfsHash,
|
||||
url,
|
||||
description,
|
||||
details,
|
||||
kind: 'docs'
|
||||
};
|
||||
|
||||
return Operator.addProposal(contribution).catch(error => {
|
||||
robot.logger.error(`[hubot-kredits] Adding proposal 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;
|
||||
}
|
||||
|
||||
function createProposals (changes) {
|
||||
let promises = [];
|
||||
|
||||
Object.keys(changes).forEach(user => {
|
||||
promises.push(createProposalForUserChanges(user, changes[user]));
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function pageTitlesFromChanges(changes) {
|
||||
return changes.map(c => `"${c.title}"`).join(', ');
|
||||
}
|
||||
|
||||
function calculateAmountForChanges(details) {
|
||||
let amount;
|
||||
|
||||
if (details.charsAdded < 280) {
|
||||
// less than a tweet
|
||||
amount = 500;
|
||||
} else if (details.charsAdded < 2000) {
|
||||
amount = 1500;
|
||||
} else {
|
||||
amount = 5000;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
function createProposalForUserChanges (user, changes) {
|
||||
const details = analyzeUserChanges(user, changes);
|
||||
const amount = calculateAmountForChanges(changes);
|
||||
|
||||
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}`;
|
||||
}
|
||||
|
||||
let url;
|
||||
if (changes.length > 1) {
|
||||
url = `https://wiki.kosmos.org/Special:Contributions/${user}?hideMinor=1`;
|
||||
} else {
|
||||
rc = changes[0];
|
||||
url = `https://wiki.kosmos.org/index.php?title=${rc.title}&diff=${rc.revid}&oldid=${rc.old_revid}`;
|
||||
}
|
||||
|
||||
return createProposal(user, 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 => createProposals(res))
|
||||
.then(() => updateTimestampForNextFetch());
|
||||
}
|
||||
|
||||
cron.schedule('* 7 * * *', processWikiChangesSinceLastRun);
|
||||
|
||||
};
|
193
package-lock.json
generated
193
package-lock.json
generated
@ -19,6 +19,16 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
|
||||
},
|
||||
"arr-flatten": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
|
||||
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
|
||||
},
|
||||
"arr-union": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
|
||||
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.22.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz",
|
||||
@ -289,15 +299,12 @@
|
||||
"repeating": "2.0.1"
|
||||
}
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz",
|
||||
"integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=",
|
||||
"encoding": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
||||
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"brorand": "1.1.0",
|
||||
"hash.js": "1.1.3",
|
||||
"inherits": "2.0.3"
|
||||
"iconv-lite": "0.4.21"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
@ -322,10 +329,26 @@
|
||||
"xmlhttprequest": "1.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"elliptic": {
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz",
|
||||
"integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"brorand": "1.1.0",
|
||||
"hash.js": "1.1.3",
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
|
||||
},
|
||||
"js-sha3": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
|
||||
"integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -334,16 +357,42 @@
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
|
||||
},
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"requires": {
|
||||
"is-extendable": "0.1.1"
|
||||
}
|
||||
},
|
||||
"eyes": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
|
||||
},
|
||||
"for-own": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
|
||||
"integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
|
||||
"requires": {
|
||||
"for-in": "1.0.2"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"get-value": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
|
||||
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
@ -362,6 +411,19 @@
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
||||
},
|
||||
"group-array": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/group-array/-/group-array-0.3.3.tgz",
|
||||
"integrity": "sha1-u9nS9xjfS+M/D7kEMqrxtDYOSY8=",
|
||||
"requires": {
|
||||
"arr-flatten": "1.1.0",
|
||||
"for-own": "0.1.5",
|
||||
"get-value": "2.0.6",
|
||||
"kind-of": "3.2.2",
|
||||
"split-string": "1.0.1",
|
||||
"union-value": "0.2.4"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
@ -376,7 +438,7 @@
|
||||
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"minimalistic-assert": "1.0.1"
|
||||
"minimalistic-assert": "1.0.0"
|
||||
}
|
||||
},
|
||||
"home-or-tmp": {
|
||||
@ -393,6 +455,14 @@
|
||||
"resolved": "https://registry.npmjs.org/i/-/i-0.3.5.tgz",
|
||||
"integrity": "sha1-HSuFQVjsgWkRPGy39raAHpniEdU="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.21",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz",
|
||||
"integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
|
||||
"requires": {
|
||||
"safer-buffer": "2.1.2"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -415,6 +485,16 @@
|
||||
"loose-envify": "1.3.1"
|
||||
}
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
|
||||
},
|
||||
"is-finite": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
|
||||
@ -423,21 +503,34 @@
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"requires": {
|
||||
"isobject": "3.0.1"
|
||||
}
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
|
||||
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"js-sha3": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
|
||||
"integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
@ -453,6 +546,14 @@
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
|
||||
"requires": {
|
||||
"is-buffer": "1.1.6"
|
||||
}
|
||||
},
|
||||
"kosmos-schemas": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/kosmos-schemas/-/kosmos-schemas-1.1.2.tgz",
|
||||
@ -475,9 +576,9 @@
|
||||
}
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
|
||||
"integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
@ -517,10 +618,19 @@
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz",
|
||||
"integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY="
|
||||
},
|
||||
"node-cron": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-1.2.1.tgz",
|
||||
"integrity": "sha1-jJC8XccjpWKJsHhmVatKHEy2A2g="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
|
||||
"integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"requires": {
|
||||
"encoding": "0.1.12",
|
||||
"is-stream": "1.1.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
@ -631,11 +741,27 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"scrypt-js": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz",
|
||||
"integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q="
|
||||
},
|
||||
"set-value": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
|
||||
"integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
|
||||
"requires": {
|
||||
"extend-shallow": "2.0.1",
|
||||
"is-extendable": "0.1.1",
|
||||
"is-plain-object": "2.0.4",
|
||||
"to-object-path": "0.3.0"
|
||||
}
|
||||
},
|
||||
"setimmediate": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz",
|
||||
@ -661,6 +787,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"split-string": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-1.0.1.tgz",
|
||||
"integrity": "sha1-vLqz9BUqzuOg1qskecDSh5w9s84=",
|
||||
"requires": {
|
||||
"extend-shallow": "2.0.1"
|
||||
}
|
||||
},
|
||||
"stack-trace": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||
@ -701,11 +835,30 @@
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
|
||||
"integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
|
||||
},
|
||||
"to-object-path": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
|
||||
"integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
|
||||
"requires": {
|
||||
"kind-of": "3.2.2"
|
||||
}
|
||||
},
|
||||
"trim-right": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
|
||||
},
|
||||
"union-value": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-0.2.4.tgz",
|
||||
"integrity": "sha1-c3UVJ4ZnkFfns3qmdug0aPwCdPA=",
|
||||
"requires": {
|
||||
"arr-union": "3.1.0",
|
||||
"get-value": "2.0.6",
|
||||
"is-extendable": "0.1.1",
|
||||
"set-value": "0.4.3"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
@ -11,10 +11,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ethers": "^3.0.15",
|
||||
"group-array": "^0.3.3",
|
||||
"kosmos-schemas": "^1.1.2",
|
||||
"node-fetch": "^2.1.1",
|
||||
"node-cron": "^1.2.1",
|
||||
"node-fetch": "^1.6.3",
|
||||
"prompt": "^1.0.0",
|
||||
"truffle-kredits": "github:67P/truffle-kredits#library"
|
||||
"truffle-kredits": "github:67P/truffle-kredits"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
Loading…
x
Reference in New Issue
Block a user