Create contributions instead of proposals #27
@ -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
|
||||||
|
|
||||||
|
9
index.js
9
index.js
@ -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();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user