34 Commits

Author SHA1 Message Date
23033b3813 3.2.0 2019-04-24 14:25:51 +01:00
fb1b10d2a3 Merge pull request #31 from 67P/feature/24-gitea
Add Gitea integration
2019-04-24 14:25:06 +01:00
d3b97d9129 Update kredits-contracts
Includes the Gitea accounts in profiles
2019-04-24 14:24:21 +01:00
d2f487b379 Merge pull request #32 from 67P/chore/remove_network_id_config
Remove network ID setting
2019-04-24 13:06:39 +00:00
ca9732c333 Add Gitea info to README 2019-04-24 13:38:16 +01:00
c03da5659f Remove network ID setting
Not needed anymore.
2019-04-24 13:34:00 +01:00
5a9edda1cd Add Gitea integration
Support for Gitea organization hooks, based on GitHub hooks code.

closes #24
2019-04-24 13:27:18 +01:00
2a3077d733 3.1.2 2019-04-17 11:14:11 +01:00
1255d7ce19 Fix another wrong const 2019-04-17 11:13:49 +01:00
d9e09ce041 3.1.1 2019-04-17 09:38:19 +01:00
a0f982432e Fix syntax error (variable re-assignment) 2019-04-17 09:38:00 +01:00
4550a911f0 3.1.0 2019-04-16 12:52:54 +01:00
d1580560b5 Update kredits-contracts 2019-04-16 12:51:49 +01:00
92c50ac69e Merge pull request #30 from 67P/feature/29-date_time
Add contribution date
2019-04-13 16:07:46 +00:00
fca991c685 Add date to mediawiki edits
Use the day before, as we collect them at 7am every day.
2019-04-13 14:18:47 +01:00
626712356a Add date and time for GitHub issues and PRs 2019-04-13 14:04:41 +01:00
65e34ee4e1 Update kredits-contracts 2019-04-13 12:44:10 +01:00
63057191d6 3.0.0 2019-04-08 15:02:01 +02:00
768c24da8e Merge pull request #27 from 67P/feature/26-contributions
Create contributions instead of proposals
2019-04-08 15:01:08 +02:00
0fdd7d0f1c Fix accidental commenting of cron schedule 2019-04-08 15:00:24 +02:00
1e0d637c26 "Fix" nonce issue for Mediawiki as well
See previous commit (dcbce66) for details.
2019-04-06 11:55:02 +02:00
dcbce66796 Hacky fix for nonce issues
This waits until a tx has likely been confirmed, before trying to send
the next one, because nonce management is broken in the ethers.js
wallet and it tries to replace a previous tx if two are sent quickly in
succession.

This will most likely break, when there are multiple independent
contrbution issuance calls (e.g. two PRs merged quickly in succession).
But at least it fixes issues/PRs with multiple assignees for most cases.
2019-04-06 11:41:19 +02:00
16b62a2545 Print actual tx error instead of guessing 2019-04-05 20:07:30 +02:00
f39bd41098 Create contributions instead of proposals 2019-04-05 19:52:48 +02:00
8cfbc222fc Merge pull request #25 from 67P/update-kredits-contracts
Update kredits contracts
2019-04-05 19:20:17 +02:00
21aaf2f3bf Use kredits-contracts from npm, update provider setup 2019-04-05 17:02:33 +02:00
7056772066 Fix function name for logger warning 2019-04-05 16:03:48 +02:00
d854b4e0fb Update npm packages 2019-03-31 17:17:16 +02:00
321fe95a27 Operator is now Proposal 2019-03-31 17:16:55 +02:00
b38e6faf8a Prepare for kredits-contracts update
Also updates ethers.js to v4.x
2019-03-31 17:09:58 +02:00
bfaf26cc9a Use robot logger 2019-03-13 12:21:48 +07:00
3ac2774c4f 2.1.3 2018-09-09 15:36:52 +08:00
83990a36dc Merge pull request #23 from 67P/bugfix/mediawiki_cron_syntax
Fix mediawiki polling cron
2018-09-09 15:36:25 +08:00
c427d7c313 Fix mediawiki polling cron
Was doing every minute after 7am, instead of every day once at 7am.
2018-09-09 15:35:09 +08:00
7 changed files with 1272 additions and 752 deletions

View File

@@ -5,7 +5,7 @@
This repository provides scripts for integrating [Kosmos
Kredits](https://wiki.kosmos.org/Kredits) in [Hubot](http://hubot.github.com/)
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.
## Setup
@@ -24,7 +24,6 @@ As usual in Hubot, you can add all config as environment variables.
| `KREDITS_WALLET_PATH` | Path to an Etherum wallet JSON file (default: `./wallet.json`) |
| `KREDITS_WALLET_PASSWORD` | Wallet password |
| `KREDITS_PROVIDER_URL` | Ethereum JSON-RPC URL (default: `http://localhost:7545`) |
| `KREDITS_NETWORK_ID` | The ethereum network ID to use (default: 100 = local) |
## Integrations
@@ -33,7 +32,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,
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
issue proposals for all of them.
issue contribution tokens for all of them.
#### Setup
@@ -47,10 +46,29 @@ Point a GitHub organization webhook to the following URL:
| --- | --- |
| `KREDITS_GITHUB_REPO_BLACKLIST` | Repos which you do not want to issue kredits for. Format: `orgname/reponame`, e.g. `67P/test-one-two` |
### Gitea
The Gitea integration will watch for closed issues and merged pull requests,
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
issue contribution tokens for all of them.
#### Setup
Point a Gitea organization webhook to the following URL:
https://your-hubot.example.com/incoming/kredits/gitea/{webhook_token}
#### Config
| Key | Description |
| --- | --- |
| `KREDITS_GITEA_REPO_BLACKLIST` | Repos which you do not want to issue kredits for. Format: `orgname/reponame`, e.g. `kosmos/test-one-two` |
### MediaWiki
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

View File

@@ -6,8 +6,7 @@ const Kredits = require('kredits-contracts');
const walletPath = process.env.KREDITS_WALLET_PATH || './wallet.json';
const walletJson = fs.readFileSync(walletPath);
const providerUrl = process.env.KREDITS_PROVIDER_URL || 'http://localhost:7545';
const networkId = parseInt(process.env.KREDITS_NETWORK_ID || 100);
const providerUrl = process.env.KREDITS_PROVIDER_URL;
const ipfsConfig = {
host: process.env.IPFS_API_HOST || 'localhost',
@@ -27,9 +26,9 @@ module.exports = async function(robot) {
let wallet;
try {
wallet = await ethers.Wallet.fromEncryptedWallet(walletJson, process.env.KREDITS_WALLET_PASSWORD);
wallet = await ethers.Wallet.fromEncryptedJson(walletJson, process.env.KREDITS_WALLET_PASSWORD);
} catch(error) {
robot.logger.warn('[hubot-kredits] Could not load wallet:', error);
robot.logger.warning('[hubot-kredits] Could not load wallet:', error);
process.exit(1);
}
@@ -37,9 +36,13 @@ module.exports = async function(robot) {
// Ethereum provider/node setup
//
const ethProvider = new ethers.providers.JsonRpcProvider(providerUrl, {chainId: networkId});
ethProvider.signer = wallet;
wallet.provider = ethProvider;
let ethProvider;
if (providerUrl) {
ethProvider = new ethers.providers.JsonRpcProvider(providerUrl);
} else {
ethProvider = new ethers.getDefaultProvider('rinkeby');
}
const signer = wallet.connect(ethProvider);
//
// Kredits contracts setup
@@ -47,13 +50,18 @@ module.exports = async function(robot) {
let kredits;
try {
kredits = await Kredits.setup(ethProvider, wallet, ipfsConfig);
kredits = await new Kredits(signer.provider, signer, {
// TODO support local devchain custom address
apm: 'open.aragonpm.eth',
ipfsConfig
}).init();
} catch(error) {
robot.logger.warn('[hubot-kredits] Could not set up kredits:', error);
robot.logger.warning('[hubot-kredits] Could not set up kredits:', error);
process.exit(1);
}
const Contributor = kredits.Contributor;
const Operator = kredits.Operator;
const Proposal = kredits.Proposal;
const Contribution = kredits.Contribution;
robot.logger.info('[hubot-kredits] Wallet address: ' + wallet.address);
@@ -74,7 +82,7 @@ module.exports = async function(robot) {
robot.respond(/got ETH\??/i, res => {
ethProvider.getBalance(wallet.address).then((balance) => {
res.send(`my wallet contains ${ethers.utils.formatEther(balance)} ETH`);
res.send(`My wallet contains ${ethers.utils.formatEther(balance)} ETH`);
});
});
@@ -87,7 +95,7 @@ module.exports = async function(robot) {
});
robot.respond(/list open proposals/i, res => {
Operator.all().then((proposals) => {
Proposal.all().then((proposals) => {
proposals.forEach((proposal) => {
if (!proposal.executed) {
Contributor.getById(proposal.contributorId).then((contributor) => {
@@ -111,19 +119,28 @@ module.exports = async function(robot) {
robot.logger.debug(`[hubot-kredits] Watching events from block ${nextBlock} onward`);
ethProvider.resetEventsBlock(nextBlock);
Operator.on('ProposalCreated', handleProposalCreated);
Proposal.on('ProposalCreated', handleProposalCreated);
Contribution.on('ContributionAdded', handleContributionAdded);
});
}
function handleProposalCreated(proposalId, creatorAccount, contributorId, amount) {
Contributor.getById(contributorId).then((contributor) => {
Operator.getById(proposalId).then((proposal) => {
Proposal.getById(proposalId).then((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`);
});
});
}
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();
//
@@ -131,6 +148,7 @@ module.exports = async function(robot) {
//
require('./integrations/github')(robot, kredits);
require('./integrations/gitea')(robot, kredits);
if (typeof process.env.KREDITS_MEDIAWIKI_URL !== 'undefined') {
require('./integrations/mediawiki')(robot, kredits);

179
integrations/gitea.js Normal file
View File

@@ -0,0 +1,179 @@
const util = require('util');
const fetch = require('node-fetch');
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 Gitea integration...');
let repoBlackList = [];
if (process.env.KREDITS_GITEA_REPO_BLACKLIST) {
repoBlackList = process.env.KREDITS_GITEA_REPO_BLACKLIST.split(',');
robot.logger.debug('[hubot-kredits] Ignoring Gitea actions from ', util.inspect(repoBlackList));
}
const Contributor = kredits.Contributor;
const Contribution = kredits.Contribution;
function getContributorByGiteaUser(username) {
return Contributor.all().then(contributors => {
const contrib = contributors.find(c => {
return c.gitea_username === username;
});
if (!contrib) {
throw new Error(`No contributor found for ${username}`);
} else {
return contrib;
}
});
}
function createContribution(giteaUser, date, time, amount, description, url, details) {
return getContributorByGiteaUser(giteaUser).then(contributor => {
robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${giteaUser} for ${url}...`);
const contributionAttr = {
contributorId: contributor.id,
contributorIpfsHash: contributor.ipfsHash,
date,
time,
amount,
url,
description,
details,
kind: 'dev'
};
return Contribution.addContribution(contributionAttr).catch(error => {
robot.logger.error(`[hubot-kredits] Error:`, error);
messageRoom(`I tried to add a contribution for ${giteaUser} for ${url}, but I encountered an error when submitting the tx:`);
messageRoom(error.message);
});
});
}
function amountFromLabels(labels) {
const kreditsLabel = 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;
}
async function handleGiteaIssueClosed(data) {
const issue = data.issue;
const repoName = data.repository.full_name;
const web_url = `${data.repository.html_url}/issues/${issue.id}`;
const description = `${repoName}: ${issue.title}`;
const amount = amountFromLabels(issue.labels);
const assignees = issue.assignees ? issue.assignees.map(a => a.login) : [];
[ date, time ] = issue.closed_at.split('T');
if (amount === 0) {
robot.logger.info('[hubot-kredits] Kredits 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 recipients;
if (assignees.length > 0) {
recipients = assignees;
} else {
recipients = [issue.user.login];
}
for (const recipient of recipients) {
try {
await createContribution(recipient, date, time, amount, description, web_url,
{ issue, repository: data.repository });
await sleep(60000);
}
catch (err) { robot.logger.error(err); }
}
return Promise.resolve();
}
async function handleGiteaPullRequestClosed(data) {
const pull_request = data.pull_request;
const repoName = data.repository.full_name;
const web_url = pull_request.html_url;
const description = `${repoName}: ${pull_request.title}`;
const amount = amountFromLabels(pull_request.labels);
const assignees = pull_request.assignees ? pull_request.assignees.map(a => a.login) : [];
[ date, time ] = pull_request.merged_at.split('T');
if (amount === 0) {
robot.logger.info('[hubot-kredits] Kredits 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 recipients;
if (assignees.length > 0) {
recipients = assignees;
} else {
recipients = [pull_request.user.login];
}
for (const recipient of recipients) {
try {
await createContribution(recipient, date, time, amount, description, web_url,
{ pull_request, repository: data.repository });
await sleep(60000);
}
catch (err) { robot.logger.error(err); }
}
return Promise.resolve();
}
robot.router.post('/incoming/kredits/gitea/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => {
const evt = req.header('X-Gitea-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 Gitea hook. Event: ${evt}, action: ${data.action}`);
if (evt === 'pull_request' && data.action === 'closed' && data.pull_request.merged) {
handleGiteaPullRequestClosed(data);
res.sendStatus(200);
}
else if (evt === 'issues' && data.action === 'closed') {
handleGiteaIssueClosed(data);
res.sendStatus(200);
} else {
res.sendStatus(200);
}
});
};

View File

@@ -1,6 +1,10 @@
const util = require('util');
const fetch = require('node-fetch');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = async function(robot, kredits) {
function messageRoom(message) {
@@ -16,11 +20,11 @@ module.exports = async function(robot, kredits) {
}
const Contributor = kredits.Contributor;
const Operator = kredits.Operator;
const Contribution = kredits.Contribution;
function getContributorByGithubUser(username) {
return Contributor.all().then(contributors => {
let contrib = contributors.find(c => {
const contrib = contributors.find(c => {
return c.github_username === username;
});
if (!contrib) {
@@ -31,30 +35,33 @@ module.exports = async function(robot, kredits) {
});
}
function createProposal(githubUser, amount, description, url, details) {
function createContribution(githubUser, date, time, amount, description, url, details) {
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 = {
const contributionAttr = {
contributorId: contributor.id,
amount: amount,
contributorIpfsHash: contributor.ipfsHash,
date,
time,
amount,
url,
description,
details,
kind: 'dev'
};
return Operator.addProposal(contributionAttr).catch(error => {
return Contribution.addContribution(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`);
messageRoom(`I tried to add a contribution for ${githubUser} for ${url}, but I encountered an error when submitting the tx:`);
messageRoom(error.message);
});
});
}
function amountFromIssueLabels(issue) {
let kreditsLabel = issue.labels.map(l => l.name)
.filter(n => n.match(/^kredits/))[0];
const kreditsLabel = issue.labels.map(l => l.name)
.filter(n => n.match(/^kredits/))[0];
// No label, no kredits
if (typeof kreditsLabel === 'undefined') { return 0; }
@@ -75,18 +82,19 @@ module.exports = async function(robot, kredits) {
return amount;
}
function handleGitHubIssueClosed(data) {
async function handleGitHubIssueClosed(data) {
let recipients;
let issue = data.issue;
let assignees = issue.assignees.map(a => a.login);
let web_url = issue.html_url;
const issue = data.issue;
const assignees = issue.assignees.map(a => a.login);
const web_url = issue.html_url;
let amount = amountFromIssueLabels(issue);
let repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1];
let description = `${repoName}: ${issue.title}`;
[date, time] = issue.closed_at.split('T');
const amount = amountFromIssueLabels(issue);
const repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1];
const description = `${repoName}: ${issue.title}`;
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();
} else if (repoBlackList.includes(repoName)) {
robot.logger.debug(`[hubot-kredits] ${repoName} is on black list; ignoring`);
@@ -99,23 +107,25 @@ module.exports = async function(robot, kredits) {
recipients = [issue.user.login];
}
let proposalPromises = [];
recipients.forEach(recipient => {
proposalPromises.push(
createProposal(recipient, amount, description, web_url, issue)
.catch(err => robot.logger.error(err))
);
});
for (const recipient of recipients) {
try {
await createContribution(recipient, date, time, amount, description, web_url, issue);
await sleep(60000);
}
catch (err) { robot.logger.error(err); }
}
return Promise.all(proposalPromises);
return Promise.resolve();
}
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;
const pull_request = data.pull_request;
const assignees = pull_request.assignees.map(a => a.login);
const web_url = pull_request._links.html.href;
const pr_issue_url = pull_request.issue_url;
[date, time] = pull_request.merged_at.split('T');
if (assignees.length > 0) {
recipients = assignees;
@@ -130,34 +140,33 @@ module.exports = async function(robot, kredits) {
}
return response.json();
})
.then(issue => {
let amount = amountFromIssueLabels(issue);
let repoName = pull_request.base.repo.full_name;
let description = `${repoName}: ${pull_request.title}`;
.then(async (issue) => {
const amount = amountFromIssueLabels(issue);
const repoName = pull_request.base.repo.full_name;
const description = `${repoName}: ${pull_request.title}`;
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();
} 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))
);
});
for (const recipient of recipients) {
try {
await createContribution(recipient, date, time, amount, description, web_url, pull_request);
await sleep(60000);
}
catch (err) { robot.logger.error(err); }
}
return Promise.all(proposalPromises);
return Promise.resolve();
});
}
robot.router.post('/incoming/kredits/github/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => {
let evt = req.header('X-GitHub-Event');
const 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
@@ -165,11 +174,13 @@ module.exports = async function(robot, kredits) {
robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`);
if (evt === 'pull_request' && data.action === 'closed') {
handleGitHubPullRequestClosed(data).then(() => res.send(200));
if (evt === 'pull_request' && data.action === 'closed' && data.pull_request.merged) {
handleGitHubPullRequestClosed(data);
res.send(200);
}
else if (evt === 'issues' && data.action === 'closed') {
handleGitHubIssueClosed(data).then(() => res.send(200));
handleGitHubIssueClosed(data);
res.send(200);
} else {
res.send(200);
}

View File

@@ -4,6 +4,10 @@ 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) {
@@ -13,7 +17,7 @@ module.exports = async function(robot, kredits) {
robot.logger.debug('[hubot-kredits] Loading MediaWiki integration...')
const Contributor = kredits.Contributor;
const Operator = kredits.Operator;
const Contribution = kredits.Contribution;
const wikiURL = process.env.KREDITS_MEDIAWIKI_URL;
const apiURL = wikiURL + 'api.php';
@@ -29,22 +33,23 @@ module.exports = async function(robot, kredits) {
});
}
function createProposal(username, amount, description, url, details={}) {
function createContribution(username, date, 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}...`);
robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${contributor.name} for ${url}...`);
let contribution = {
contributorId: contributor.id,
amount: amount,
contributorIpfsHash: contributor.ipfsHash,
date,
amount: amount,
url,
description,
details,
kind: 'docs'
};
return Operator.addProposal(contribution).catch(error => {
robot.logger.error(`[hubot-kredits] Adding proposal failed:`, error);
return Contribution.addContribution(contribution).catch(error => {
robot.logger.error(`[hubot-kredits] Adding contribution failed:`, error);
});
}).catch(() => {
robot.logger.info(`[hubot-kredits] No contributor found for ${username}`);
@@ -106,14 +111,15 @@ module.exports = async function(robot, kredits) {
return results;
}
function createProposals (changes) {
async function createContributions (changes) {
let promises = [];
Object.keys(changes).forEach(user => {
promises.push(createProposalForUserChanges(user, changes[user]));
});
for (const user of Object.keys(changes)) {
await createContributionForUserChanges(user, changes[user]);
await sleep(60000);
}
return Promise.all(promises);
return Promise.resolve();
}
function pageTitlesFromChanges(changes) {
@@ -135,7 +141,11 @@ module.exports = async function(robot, kredits) {
return amount;
}
function createProposalForUserChanges (user, changes) {
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 = calculateAmountForChanges(details);
@@ -156,7 +166,7 @@ module.exports = async function(robot, kredits) {
url = `${wikiURL}index.php?title=${rc.title}&diff=${rc.revid}&oldid=${rc.old_revid}`;
}
return createProposal(user, amount, desc, url, details);
return createContribution(user, date, amount, desc, url, details);
}
function updateTimestampForNextFetch () {
@@ -167,10 +177,10 @@ module.exports = async function(robot, kredits) {
function processWikiChangesSinceLastRun () {
fetchChanges()
.then(res => groupChangesByUser(res))
.then(res => createProposals(res))
.then(res => createContributions(res))
.then(() => updateTimestampForNextFetch());
}
cron.schedule('* 7 * * *', processWikiChangesSinceLastRun);
cron.schedule('0 7 * * *', processWikiChangesSinceLastRun);
};

1613
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "hubot-kredits",
"version": "2.1.2",
"version": "3.2.0",
"description": "Kosmos Kredits functionality for chat bots",
"main": "index.js",
"scripts": {
@@ -10,13 +10,14 @@
"create-wallet": "scripts/create-wallet.js"
},
"dependencies": {
"ethers": "^3.0.15",
"eth-provider": "^0.2.2",
"ethers": "^4.0.27",
"group-array": "^0.3.3",
"kosmos-schemas": "^1.1.2",
"node-cron": "^1.2.1",
"node-fetch": "^1.6.3",
"prompt": "^1.0.0",
"kredits-contracts": "3.x"
"kredits-contracts": "^5.3.0",
"node-cron": "^2.0.3",
"node-fetch": "^2.3.0",
"prompt": "^1.0.0"
},
"repository": {
"type": "git",