Create contributions instead of proposals #27

Merged
raucao merged 5 commits from feature/26-contributions into master 2019-04-08 13:01:08 +00:00
4 changed files with 62 additions and 44 deletions

View File

@ -5,7 +5,7 @@
This repository provides scripts for integrating [Kosmos This repository provides scripts for integrating [Kosmos
Kredits](https://wiki.kosmos.org/Kredits) in [Hubot](http://hubot.github.com/) Kredits](https://wiki.kosmos.org/Kredits) in [Hubot](http://hubot.github.com/)
chatbots. The bot will watch for project-related things happening on the chatbots. The bot will watch for project-related things happening on the
Internet and automatically create proposals for issuing kredits for project Internet and automatically create ERC721 tokens for issuing kredits for project
contributions. contributions.
## Setup ## Setup
@ -33,7 +33,7 @@ As usual in Hubot, you can add all config as environment variables.
The GitHub integration will watch for closed issues and merged pull requests, The GitHub integration will watch for closed issues and merged pull requests,
which carry a kredits label: `kredits-1`, `kredits-2`, `kredits-3` for small, which carry a kredits label: `kredits-1`, `kredits-2`, `kredits-3` for small,
medium and large contributions. If there are multiple people assigned, it will medium and large contributions. If there are multiple people assigned, it will
issue proposals for all of them. issue contribution tokens for all of them.
#### Setup #### Setup
@ -50,7 +50,7 @@ Point a GitHub organization webhook to the following URL:
### MediaWiki ### MediaWiki
The MediaWiki integration will periodically check for wiki page creations and The MediaWiki integration will periodically check for wiki page creations and
edits. It will create kredits proposals based on amount of text added. edits. It will create kredits contribution tokens based on amount of text added.
#### Setup #### Setup

View File

@ -121,6 +121,7 @@ module.exports = async function(robot) {
ethProvider.resetEventsBlock(nextBlock); ethProvider.resetEventsBlock(nextBlock);
Proposal.on('ProposalCreated', handleProposalCreated); Proposal.on('ProposalCreated', handleProposalCreated);
Contribution.on('ContributionAdded', handleContributionAdded);
}); });
} }
@ -133,6 +134,14 @@ module.exports = async function(robot) {
}); });
} }
function handleContributionAdded(contributionId, contributorId, amount) {
Contributor.getById(contributorId).then((contributor) => {
Contribution.getById(contributionId).then((contribution) => {
robot.logger.debug(`[hubot-kredits] Contribution #${contribution.id} added (${contribution.description})`);
});
});
}
watchContractEvents(); watchContractEvents();
// //

View File

@ -1,6 +1,10 @@
const util = require('util'); const util = require('util');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = async function(robot, kredits) { module.exports = async function(robot, kredits) {
function messageRoom(message) { function messageRoom(message) {
@ -16,7 +20,6 @@ module.exports = async function(robot, kredits) {
} }
const Contributor = kredits.Contributor; const Contributor = kredits.Contributor;
const Proposal = kredits.Proposal;
const Contribution = kredits.Contribution; const Contribution = kredits.Contribution;
function getContributorByGithubUser(username) { function getContributorByGithubUser(username) {
@ -32,9 +35,9 @@ module.exports = async function(robot, kredits) {
}); });
} }
function createProposal(githubUser, amount, description, url, details) { function createContribution(githubUser, amount, description, url, details) {
return getContributorByGithubUser(githubUser).then(contributor => { return getContributorByGithubUser(githubUser).then(contributor => {
robot.logger.debug(`[hubot-kredits] Creating proposal to issue ${amount}₭S to ${githubUser} for ${url}...`); robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${githubUser} for ${url}...`);
let contributionAttr = { let contributionAttr = {
contributorId: contributor.id, contributorId: contributor.id,
@ -46,9 +49,10 @@ module.exports = async function(robot, kredits) {
kind: 'dev' kind: 'dev'
}; };
return Proposal.addProposal(contributionAttr).catch(error => { return Contribution.addContribution(contributionAttr).catch(error => {
robot.logger.error(`[hubot-kredits] Error:`, 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`); messageRoom(`I tried to add a contribution for ${githubUser} for ${url}, but I encountered an error when submitting the tx:`);
messageRoom(error.message);
}); });
}); });
} }
@ -76,7 +80,7 @@ module.exports = async function(robot, kredits) {
return amount; return amount;
} }
function handleGitHubIssueClosed(data) { async function handleGitHubIssueClosed(data) {
let recipients; let recipients;
let issue = data.issue; let issue = data.issue;
let assignees = issue.assignees.map(a => a.login); let assignees = issue.assignees.map(a => a.login);
@ -87,7 +91,7 @@ module.exports = async function(robot, kredits) {
let description = `${repoName}: ${issue.title}`; let description = `${repoName}: ${issue.title}`;
if (amount === 0) { if (amount === 0) {
robot.logger.info('[hubot-kredits] Proposal amount from issue label is zero; ignoring'); robot.logger.info('[hubot-kredits] Kredits amount from issue label is zero; ignoring');
return Promise.resolve(); return Promise.resolve();
} else if (repoBlackList.includes(repoName)) { } else if (repoBlackList.includes(repoName)) {
robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`); robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`);
@ -100,15 +104,15 @@ module.exports = async function(robot, kredits) {
recipients = [issue.user.login]; recipients = [issue.user.login];
} }
let proposalPromises = []; for (const recipient of recipients) {
recipients.forEach(recipient => { try {
proposalPromises.push( await createContribution(recipient, amount, description, web_url, issue);
createProposal(recipient, amount, description, web_url, issue) await sleep(60000);
.catch(err => robot.logger.error(err)) }
); catch (err) { robot.logger.error(err); }
}); }
return Promise.all(proposalPromises); return Promise.resolve();
} }
function handleGitHubPullRequestClosed(data) { function handleGitHubPullRequestClosed(data) {
@ -131,29 +135,28 @@ module.exports = async function(robot, kredits) {
} }
return response.json(); return response.json();
}) })
.then(issue => { .then(async (issue) => {
let amount = amountFromIssueLabels(issue); let amount = amountFromIssueLabels(issue);
let repoName = pull_request.base.repo.full_name; let repoName = pull_request.base.repo.full_name;
let description = `${repoName}: ${pull_request.title}`; let description = `${repoName}: ${pull_request.title}`;
if (amount === 0) { if (amount === 0) {
robot.logger.info('[hubot-kredits] Proposal amount from issue label is zero; ignoring'); robot.logger.info('[hubot-kredits] Kredits amount from issue label is zero; ignoring');
return Promise.resolve(); return Promise.resolve();
} else if (repoBlackList.includes(repoName)) { } else if (repoBlackList.includes(repoName)) {
robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`); robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`);
return Promise.resolve(); return Promise.resolve();
} }
let proposalPromises = []; for (const recipient of recipients) {
recipients.forEach(recipient => { try {
robot.logger.debug(`[hubot-kredits] Creating proposal for ${recipient}...`); await createContribution(recipient, amount, description, web_url, pull_request);
proposalPromises.push( await sleep(60000);
createProposal(recipient, amount, description, web_url, pull_request) }
.catch(err => robot.logger.error(err)) catch (err) { robot.logger.error(err); }
); }
});
return Promise.all(proposalPromises); return Promise.resolve();
}); });
} }
@ -167,10 +170,12 @@ module.exports = async function(robot, kredits) {
robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`); robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`);
if (evt === 'pull_request' && data.action === 'closed') { if (evt === 'pull_request' && data.action === 'closed') {
handleGitHubPullRequestClosed(data).then(() => res.send(200)); handleGitHubPullRequestClosed(data);
res.send(200);
} }
else if (evt === 'issues' && data.action === 'closed') { else if (evt === 'issues' && data.action === 'closed') {
handleGitHubIssueClosed(data).then(() => res.send(200)); handleGitHubIssueClosed(data);
res.send(200);
} else { } else {
res.send(200); res.send(200);
} }

View File

@ -4,6 +4,10 @@ const fetch = require('node-fetch');
const groupArray = require('group-array'); const groupArray = require('group-array');
const cron = require('node-cron'); const cron = require('node-cron');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = async function(robot, kredits) { module.exports = async function(robot, kredits) {
function messageRoom(message) { function messageRoom(message) {
@ -13,7 +17,6 @@ module.exports = async function(robot, kredits) {
robot.logger.debug('[hubot-kredits] Loading MediaWiki integration...') robot.logger.debug('[hubot-kredits] Loading MediaWiki integration...')
const Contributor = kredits.Contributor; const Contributor = kredits.Contributor;
const Proposal = kredits.Proposal;
const Contribution = kredits.Contribution; const Contribution = kredits.Contribution;
const wikiURL = process.env.KREDITS_MEDIAWIKI_URL; const wikiURL = process.env.KREDITS_MEDIAWIKI_URL;
@ -30,9 +33,9 @@ module.exports = async function(robot, kredits) {
}); });
} }
function createProposal(username, amount, description, url, details={}) { function createContribution(username, amount, description, url, details={}) {
return getContributorByWikiUser(username).then(contributor => { return getContributorByWikiUser(username).then(contributor => {
robot.logger.debug(`[hubot-kredits] Creating proposal to issue ${amount}₭S to ${contributor.name} for ${url}...`); robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${contributor.name} for ${url}...`);
let contribution = { let contribution = {
contributorId: contributor.id, contributorId: contributor.id,
@ -44,8 +47,8 @@ module.exports = async function(robot, kredits) {
kind: 'docs' kind: 'docs'
}; };
return Proposal.addProposal(contribution).catch(error => { return Contribution.addContribution(contribution).catch(error => {
robot.logger.error(`[hubot-kredits] Adding proposal failed:`, error); robot.logger.error(`[hubot-kredits] Adding contribution failed:`, error);
}); });
}).catch(() => { }).catch(() => {
robot.logger.info(`[hubot-kredits] No contributor found for ${username}`); robot.logger.info(`[hubot-kredits] No contributor found for ${username}`);
@ -107,14 +110,15 @@ module.exports = async function(robot, kredits) {
return results; return results;
} }
function createProposals (changes) { async function createContributions (changes) {
let promises = []; let promises = [];
Object.keys(changes).forEach(user => { for (const user of Object.keys(changes)) {
promises.push(createProposalForUserChanges(user, changes[user])); await createContributionForUserChanges(user, changes[user]);
}); await sleep(60000);
}
return Promise.all(promises); return Promise.resolve();
} }
function pageTitlesFromChanges(changes) { function pageTitlesFromChanges(changes) {
@ -136,7 +140,7 @@ module.exports = async function(robot, kredits) {
return amount; return amount;
} }
function createProposalForUserChanges (user, changes) { function createContributionForUserChanges (user, changes) {
const details = analyzeUserChanges(user, changes); const details = analyzeUserChanges(user, changes);
const amount = calculateAmountForChanges(details); const amount = calculateAmountForChanges(details);
@ -157,7 +161,7 @@ module.exports = async function(robot, kredits) {
url = `${wikiURL}index.php?title=${rc.title}&diff=${rc.revid}&oldid=${rc.old_revid}`; url = `${wikiURL}index.php?title=${rc.title}&diff=${rc.revid}&oldid=${rc.old_revid}`;
} }
return createProposal(user, amount, desc, url, details); return createContribution(user, amount, desc, url, details);
} }
function updateTimestampForNextFetch () { function updateTimestampForNextFetch () {
@ -168,7 +172,7 @@ module.exports = async function(robot, kredits) {
function processWikiChangesSinceLastRun () { function processWikiChangesSinceLastRun () {
fetchChanges() fetchChanges()
.then(res => groupChangesByUser(res)) .then(res => groupChangesByUser(res))
.then(res => createProposals(res)) .then(res => createContributions(res))
.then(() => updateTimestampForNextFetch()); .then(() => updateTimestampForNextFetch());
} }