40 Commits

Author SHA1 Message Date
f20a8327af 3.4.1 2019-09-01 17:05:42 +02:00
571de43aa8 Merge pull request #47 from 67P/chore/fix_warnings
Fix warnings from express session
2019-09-01 17:05:22 +02:00
50002194c5 Fix warnings from express session 2019-09-01 17:04:19 +02:00
cecc632620 Merge pull request #46 from 67P/bugfix/signup_issue
Fix critical bug in signup code
2019-09-01 17:03:06 +02:00
6972d6c88e Fix critical bug in signup code
Variable scope/assignment issue
2019-09-01 17:00:56 +02:00
157d2dee49 3.4.0 2019-08-31 13:42:50 +02:00
590ca961a6 Merge pull request #45 from 67P/docs/github-signup
Add GitHub signup oracle documentation
2019-08-31 13:26:23 +02:00
6476fe5c88 Improve signup docs 2019-08-31 13:23:11 +02:00
101e35622a Add GitHub signup oracle documentation 2019-08-31 12:52:36 +02:00
afa67e5ac7 Prefix all configuration environment variables
The convention here is to prefix all Kredit related configuration
variables with `KREDITS_`
2019-08-31 12:51:44 +02:00
cbbc6c359e Merge pull request #41 from 67P/feature/github-signup
Add GitHub signup oracle
2019-08-28 15:56:54 +02:00
c16ea51769 Error handling for fetching Github access token 2019-08-27 18:35:40 +02:00
fc49213b01 Update contracts min version 2019-08-27 18:23:22 +02:00
32dc6a7358 GitHub signup fixes
* Don't fail when profile has no name set
* Use scope that can only read public profile information (no scope)
* Better error handling
2019-08-27 18:22:36 +02:00
53dee63a76 Add env variable for session secret 2019-08-27 15:30:30 +02:00
97ec6ce9bc Add KREDITS_DAO_ADDRESS to readme 2019-08-27 15:22:38 +02:00
342a5cd829 Add gasLimit option when adding contributor 2019-08-27 13:16:43 +02:00
45064df737 Fix wrong logger function call 2019-08-27 12:57:36 +02:00
d9a9ad7793 Update deps
Fixes security vulns
2019-08-27 12:51:31 +02:00
d65e92e1fe Merge branch 'master' into feature/github-signup 2019-08-27 12:39:15 +02:00
0fff3b1db0 Merge pull request #43 from 67P/feature/custom_dao_address
Add ENV var for custom DAO address
2019-08-27 12:38:45 +02:00
1f1053e289 Remove apm address from options
It's already the default in the contracts module.
2019-08-27 12:37:50 +02:00
84f20efff0 Add ENV var for custom DAO address
This makes it much easier to test hubot-kredits with local development
chains.
2019-08-27 12:19:01 +02:00
cd8343516f 3.3.1 2019-08-14 15:42:23 +02:00
Greg Karékinian
41c5903cf5 Merge pull request #42 from 67P/chore/mediawiki_small_threshold
Change threshold for small wiki contributions
2019-08-14 12:51:48 +02:00
9a4dc43aa5 Change threshold for small wiki contributions
Turns out 280 is a bit low. Let's try 500 instead.
2019-08-14 12:46:19 +02:00
082bf4848d Minor changes 2019-07-26 20:50:36 +02:00
41dd813eaf Replace custom CORS header solution with middleware 2019-07-26 20:48:20 +02:00
b35cbcc63a Finish implementation of GitHub signup oracle 2019-07-26 04:15:11 +02:00
137e9eb4ed Add initial implementation of the GitHub signup oracle 2019-07-11 15:44:58 +02:00
67fd9ff031 3.3.0 2019-05-08 16:45:55 +02:00
800950ebc5 Merge pull request #39 from 67P/feature/38-label_mappings
Map issue/PR labels to contribution kind
2019-05-08 16:42:05 +02:00
98ccae96f0 [gitea] Use labels for contribution kind 2019-05-08 16:16:40 +02:00
e6730802f6 Use proper syntax for sending HTTP status
Fixes deprecation warnings for the old syntax.
2019-05-08 16:14:41 +02:00
d2976b312e Create utility modules for label processing 2019-05-08 16:14:41 +02:00
d2feee8b14 [github] Use labels for contribution kind 2019-05-08 16:14:24 +02:00
a5acf466a4 3.2.1 2019-04-30 10:41:50 +01:00
Greg Karékinian
f1bb5b391d Merge pull request #35 from 67P/bugfix/33-gitea_issue_id
Fix wrong Gitea issue IDs/URLs
2019-04-30 10:26:21 +02:00
840fcf9d25 Add commented setting for custom local DAO address 2019-04-28 13:01:33 +01:00
c390fc9ce7 Fix Gitea issue ID/URL being wrong
Internal issue IDs are different from the public ones in Gitea. The one
used in URLs etc. is called "number" instead of "id" in hook payloads.

fixes #33
2019-04-28 13:01:09 +01:00
9 changed files with 1090 additions and 328 deletions

View File

@@ -10,9 +10,29 @@ contributions.
## Setup ## Setup
### Ethereum wallet
You will need an Ethereum wallet for your bot, so it can interact with the You will need an Ethereum wallet for your bot, so it can interact with the
Ethereum smart contracts. `npm run create-wallet` will do the job for you. Ethereum smart contracts. `npm run create-wallet` will do the job for you.
The wallet must be funded with enough ETH to interact with the contracts.
### Contract permissions
The bot wallet needs the following Aragon contract permissions to interact
with [kredits-contracts]:
1. `ADD_CONTRIBUTION_ROLE` on the `Contribution` contract
2. `MANAGE_CONTRIBUTORS_ROLE` on the `Contributor` contract
These permissions can be configured using the [Aragon
CLI](https://hack.aragon.org/docs/cli-intro.html) (see [kredits-contracts].
aragon dao acl grant [DAO address] [contribution app address] ADD_CONTRIBUTION_ROLE [bot wallet address]
aragon dao acl grant [DAO address] [contributor app address] MANAGE_CONTRIBUTORS_ROLE [bot wallet address]
To get the `Contribution` and `Contributor` app addresses use `aragon dao apps`.
## Configuration ## Configuration
As usual in Hubot, you can add all config as environment variables. As usual in Hubot, you can add all config as environment variables.
@@ -24,6 +44,11 @@ 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_PATH` | Path to an Etherum wallet JSON file (default: `./wallet.json`) |
| `KREDITS_WALLET_PASSWORD` | Wallet password | | `KREDITS_WALLET_PASSWORD` | Wallet password |
| `KREDITS_PROVIDER_URL` | Ethereum JSON-RPC URL (default: `http://localhost:7545`) | | `KREDITS_PROVIDER_URL` | Ethereum JSON-RPC URL (default: `http://localhost:7545`) |
| `KREDITS_WEB_URL` | URL of the Kredits Web app (default: `https://kredits.kosmos.org`) |
| `KREDITS_DAO_ADDRESS` | DAO Kernel address |
| `KREDITS_SESSION_SECRET` | Secret used by [grant](https://www.npmjs.com/package/grant) to sign the session ID |
| `KREDITS_GRANT_HOST` | Host used by [grant](https://www.npmjs.com/package/grant) to generate OAuth redirect URLs (default: `localhost:8888`) |
| `KREDITS_GRANT_PROTOCOL` | Protocol (http or https) used by [grant](https://www.npmjs.com/package/grant") to generate the OAuth redirect URLs (default: "http") |
## Integrations ## Integrations
@@ -34,6 +59,12 @@ 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 contribution tokens for all of them. issue contribution tokens for all of them.
If `KREDITS_GITHUB_KEY` and `KREDITS_GITHUB_SECRET` are set, the bot will also
expose OAuth endpoints to authenticate new contributors and register new
contributor profiles on the smart contract. For this feature, a [GitHub OAuth
app] is required and the [OAuth grant config variables](#Configuration) must be
set.
#### Setup #### Setup
Point a GitHub organization webhook to the following URL: Point a GitHub organization webhook to the following URL:
@@ -45,6 +76,8 @@ Point a GitHub organization webhook to the following URL:
| Key | Description | | Key | Description |
| --- | --- | | --- | --- |
| `KREDITS_GITHUB_REPO_BLACKLIST` | Repos which you do not want to issue kredits for. Format: `orgname/reponame`, e.g. `67P/test-one-two` | | `KREDITS_GITHUB_REPO_BLACKLIST` | Repos which you do not want to issue kredits for. Format: `orgname/reponame`, e.g. `67P/test-one-two` |
| `KREDITS_GITHUB_KEY` | Key of the [GitHub OAuth app] used to authenticate contributors |
| `KREDITS_GITHUB_SECRET` | Secret of the [GitHub OAuth app] used to authenticate contributors |
### Gitea ### Gitea
@@ -80,3 +113,6 @@ wiki's API on its own.
| Key | Description | | Key | Description |
| --- | --- | | --- | --- |
| `KREDITS_MEDIAWIKI_URL` | Your wiki URL, e.g. `https://wiki.kosmos.org/` | | `KREDITS_MEDIAWIKI_URL` | Your wiki URL, e.g. `https://wiki.kosmos.org/` |
[kredits-contracts]: https://github.com/67P/kredits-contracts
[GitHub OAuth app]: https://developer.github.com/apps/about-apps/#about-oauth-apps

View File

@@ -7,6 +7,7 @@ const Kredits = require('kredits-contracts');
const walletPath = process.env.KREDITS_WALLET_PATH || './wallet.json'; const walletPath = process.env.KREDITS_WALLET_PATH || './wallet.json';
const walletJson = fs.readFileSync(walletPath); const walletJson = fs.readFileSync(walletPath);
const providerUrl = process.env.KREDITS_PROVIDER_URL; const providerUrl = process.env.KREDITS_PROVIDER_URL;
const daoAddress = process.env.KREDITS_DAO_ADDRESS;
const ipfsConfig = { const ipfsConfig = {
host: process.env.IPFS_API_HOST || 'localhost', host: process.env.IPFS_API_HOST || 'localhost',
@@ -48,13 +49,14 @@ module.exports = async function(robot) {
// Kredits contracts setup // Kredits contracts setup
// //
const opts = { ipfsConfig };
if (daoAddress) {
opts.addresses = { Kernel: daoAddress };
}
let kredits; let kredits;
try { try {
kredits = await new Kredits(signer.provider, signer, { kredits = await new Kredits(signer.provider, signer, opts).init();
// TODO support local devchain custom address
apm: 'open.aragonpm.eth',
ipfsConfig
}).init();
} catch(error) { } catch(error) {
robot.logger.warning('[hubot-kredits] Could not set up kredits:', error); robot.logger.warning('[hubot-kredits] Could not set up kredits:', error);
process.exit(1); process.exit(1);

View File

@@ -1,5 +1,7 @@
const util = require('util'); const util = require('util');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const amountFromLabels = require('./utils/amount-from-labels');
const kindFromLabels = require('./utils/kind-from-labels');
function sleep(ms) { function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
@@ -35,7 +37,7 @@ module.exports = async function(robot, kredits) {
}); });
} }
function createContribution(giteaUser, date, time, amount, description, url, details) { function createContribution(giteaUser, date, time, amount, kind, description, url, details) {
return getContributorByGiteaUser(giteaUser).then(contributor => { return getContributorByGiteaUser(giteaUser).then(contributor => {
robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${giteaUser} for ${url}...`); robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${giteaUser} for ${url}...`);
@@ -45,12 +47,15 @@ module.exports = async function(robot, kredits) {
date, date,
time, time,
amount, amount,
url, kind,
description, description,
details, url,
kind: 'dev' details
}; };
robot.logger.debug(`[hubot-kredits] contribution attributes:`);
robot.logger.debug(util.inspect(contributionAttr, { depth: 1, colors: true }));
return Contribution.addContribution(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 tried to add a contribution for ${giteaUser} for ${url}, but I encountered an error when submitting the tx:`); messageRoom(`I tried to add a contribution for ${giteaUser} for ${url}, but I encountered an error when submitting the tx:`);
@@ -59,35 +64,14 @@ module.exports = async function(robot, kredits) {
}); });
} }
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) { async function handleGiteaIssueClosed(data) {
const issue = data.issue; const issue = data.issue;
const repoName = data.repository.full_name; const repoName = data.repository.full_name;
const web_url = `${data.repository.html_url}/issues/${issue.id}`; const web_url = `${data.repository.html_url}/issues/${issue.number}`;
const description = `${repoName}: ${issue.title}`; const description = `${repoName}: ${issue.title}`;
const amount = amountFromLabels(issue.labels); const labels = issue.labels.map(l => l.name);
const amount = amountFromLabels(labels);
const kind = kindFromLabels(labels);
const assignees = issue.assignees ? issue.assignees.map(a => a.login) : []; const assignees = issue.assignees ? issue.assignees.map(a => a.login) : [];
[ date, time ] = issue.closed_at.split('T'); [ date, time ] = issue.closed_at.split('T');
@@ -108,7 +92,8 @@ module.exports = async function(robot, kredits) {
for (const recipient of recipients) { for (const recipient of recipients) {
try { try {
await createContribution(recipient, date, time, amount, description, web_url, await createContribution(recipient, date, time, amount,
kind, description, web_url,
{ issue, repository: data.repository }); { issue, repository: data.repository });
await sleep(60000); await sleep(60000);
} }
@@ -123,7 +108,9 @@ module.exports = async function(robot, kredits) {
const repoName = data.repository.full_name; const repoName = data.repository.full_name;
const web_url = pull_request.html_url; const web_url = pull_request.html_url;
const description = `${repoName}: ${pull_request.title}`; const description = `${repoName}: ${pull_request.title}`;
const amount = amountFromLabels(pull_request.labels); const labels = pull_request.labels.map(l => l.name);
const amount = amountFromLabels(labels);
const kind = kindFromLabels(labels);
const assignees = pull_request.assignees ? pull_request.assignees.map(a => a.login) : []; const assignees = pull_request.assignees ? pull_request.assignees.map(a => a.login) : [];
[ date, time ] = pull_request.merged_at.split('T'); [ date, time ] = pull_request.merged_at.split('T');
@@ -142,10 +129,10 @@ module.exports = async function(robot, kredits) {
recipients = [pull_request.user.login]; recipients = [pull_request.user.login];
} }
for (const recipient of recipients) { for (const recipient of recipients) {
try { try {
await createContribution(recipient, date, time, amount, description, web_url, await createContribution(recipient, date, time, amount,
kind, description, web_url,
{ pull_request, repository: data.repository }); { pull_request, repository: data.repository });
await sleep(60000); await sleep(60000);
} }

View File

@@ -1,5 +1,10 @@
const util = require('util'); const util = require('util');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const session = require('express-session');
const grant = require('grant-express');
const cors = require('cors');
const amountFromLabels = require('./utils/amount-from-labels');
const kindFromLabels = require('./utils/kind-from-labels');
function sleep(ms) { function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
@@ -19,6 +24,8 @@ module.exports = async function(robot, kredits) {
robot.logger.debug('[hubot-kredits] Ignoring GitHub actions from ', util.inspect(repoBlackList)); robot.logger.debug('[hubot-kredits] Ignoring GitHub actions from ', util.inspect(repoBlackList));
} }
const kreditsWebUrl = process.env.KREDITS_WEB_URL || 'https://kredits.kosmos.org';
const Contributor = kredits.Contributor; const Contributor = kredits.Contributor;
const Contribution = kredits.Contribution; const Contribution = kredits.Contribution;
@@ -35,9 +42,9 @@ module.exports = async function(robot, kredits) {
}); });
} }
function createContribution(githubUser, date, time, amount, description, url, details) { function createContribution(githubUser, date, time, amount, kind, description, url, details) {
return getContributorByGithubUser(githubUser).then(contributor => { return getContributorByGithubUser(githubUser).then(contributor => {
robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${githubUser} for ${url}...`); robot.logger.info(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${githubUser} for ${url}...`);
const contributionAttr = { const contributionAttr = {
contributorId: contributor.id, contributorId: contributor.id,
@@ -45,12 +52,15 @@ module.exports = async function(robot, kredits) {
date, date,
time, time,
amount, amount,
url, kind,
description, description,
details, url,
kind: 'dev' details
}; };
robot.logger.debug(`[hubot-kredits] contribution attributes:`);
robot.logger.debug(util.inspect(contributionAttr, { depth: 1, colors: true }));
return Contribution.addContribution(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 tried to add a contribution for ${githubUser} for ${url}, but I encountered an error when submitting the tx:`); messageRoom(`I tried to add a contribution for ${githubUser} for ${url}, but I encountered an error when submitting the tx:`);
@@ -59,38 +69,17 @@ module.exports = async function(robot, kredits) {
}); });
} }
function amountFromIssueLabels(issue) {
const 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;
}
async function handleGitHubIssueClosed(data) { async function handleGitHubIssueClosed(data) {
let recipients; let recipients;
const issue = data.issue; const issue = data.issue;
const assignees = issue.assignees.map(a => a.login); const assignees = issue.assignees.map(a => a.login);
const web_url = issue.html_url; const web_url = issue.html_url;
[date, time] = issue.closed_at.split('T'); [date, time] = issue.closed_at.split('T');
const amount = amountFromIssueLabels(issue); const labels = issue.labels.map(l => l.name);
const repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1]; const amount = amountFromLabels(labels);
const kind = kindFromLabels(labels);
const repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1];
const description = `${repoName}: ${issue.title}`; const description = `${repoName}: ${issue.title}`;
if (amount === 0) { if (amount === 0) {
@@ -109,7 +98,7 @@ module.exports = async function(robot, kredits) {
for (const recipient of recipients) { for (const recipient of recipients) {
try { try {
await createContribution(recipient, date, time, amount, description, web_url, issue); await createContribution(recipient, date, time, amount, kind, description, web_url, issue);
await sleep(60000); await sleep(60000);
} }
catch (err) { robot.logger.error(err); } catch (err) { robot.logger.error(err); }
@@ -141,8 +130,10 @@ module.exports = async function(robot, kredits) {
return response.json(); return response.json();
}) })
.then(async (issue) => { .then(async (issue) => {
const amount = amountFromIssueLabels(issue); const labels = issue.labels.map(l => l.name);
const repoName = pull_request.base.repo.full_name; const amount = amountFromLabels(labels);
const kind = kindFromLabels(labels);
const repoName = pull_request.base.repo.full_name;
const description = `${repoName}: ${pull_request.title}`; const description = `${repoName}: ${pull_request.title}`;
if (amount === 0) { if (amount === 0) {
@@ -155,7 +146,7 @@ module.exports = async function(robot, kredits) {
for (const recipient of recipients) { for (const recipient of recipients) {
try { try {
await createContribution(recipient, date, time, amount, description, web_url, pull_request); await createContribution(recipient, date, time, amount, kind, description, web_url, pull_request);
await sleep(60000); await sleep(60000);
} }
catch (err) { robot.logger.error(err); } catch (err) { robot.logger.error(err); }
@@ -176,14 +167,111 @@ module.exports = async function(robot, kredits) {
if (evt === 'pull_request' && data.action === 'closed' && data.pull_request.merged) { if (evt === 'pull_request' && data.action === 'closed' && data.pull_request.merged) {
handleGitHubPullRequestClosed(data); handleGitHubPullRequestClosed(data);
res.send(200); res.sendStatus(200);
} }
else if (evt === 'issues' && data.action === 'closed') { else if (evt === 'issues' && data.action === 'closed') {
handleGitHubIssueClosed(data); handleGitHubIssueClosed(data);
res.send(200); res.sendStatus(200);
} else { } else {
res.send(200); res.sendStatus(200);
} }
}); });
//
// GitHub signup
//
if (process.env.KREDITS_GITHUB_KEY && process.env.KREDITS_GITHUB_SECRET) {
const grantConfig = {
defaults: {
protocol: (process.env.KREDITS_GRANT_PROTOCOL || "http"),
host: (process.env.KREDITS_GRANT_HOST || 'localhost:8888'),
transport: 'session',
response: 'tokens',
path: '/kredits/signup'
},
github: {
key: process.env.KREDITS_GITHUB_KEY,
secret: process.env.KREDITS_GITHUB_SECRET,
callback: '/kredits/signup/github'
}
};
robot.router.use(session({
secret: process.env.KREDITS_SESSION_SECRET || 'grant',
resave: false,
saveUninitialized: false
}));
robot.router.use('/kredits/signup', grant(grantConfig));
robot.router.get('/kredits/signup/github', async (req, res) => {
const access_token = req.session.grant.response.access_token;
res.redirect(`${kreditsWebUrl}/signup/github#access_token=${access_token}`);
});
robot.router.options('/kredits/signup/github', cors());
robot.router.post('/kredits/signup/github', cors(), async (req, res) => {
const accessToken = req.body.accessToken;
if (!accessToken) {
res.status(400).json({});
return;
}
let githubResponse;
try {
githubResponse = await fetch('https://api.github.com/user', {
headers: {
'Accept': 'application/vnd.github.v3+json',
'Authorization': `token ${accessToken}`
}
});
} catch (error) {
robot.logger.error('[hubot-kredits] Fetching user data from GitHub failed:', error);
res.status(500).json({ error });
};
if (githubResponse.status >= 300) {
res.status(githubResponse.status).json({});
return;
}
const user = await githubResponse.json();
const contributor = await kredits.Contributor.findByAccount({
site: 'github.com',
username: user.login
});
if (!contributor) {
let contributorAttr = {};
contributorAttr.account = req.body.account;
contributorAttr.name = user.name || user.login;
contributorAttr.kind = "person";
contributorAttr.url = user.blog;
contributorAttr.github_username = user.login;
contributorAttr.github_uid = user.id;
kredits.Contributor.add(contributorAttr, { gasLimit: 350000 })
.then(transaction => {
robot.logger.info('[hubot-kredits] Contributor added from GitHub signup', transaction.hash);
res.status(201);
res.json({
transactionHash: transaction.hash,
github_username: user.login
});
}, error => {
robot.logger.error(`[hubot-kredits] Adding contributor failed: ${error}`);
res.status(422);
res.json({ error })
});
} else {
res.json({
github_username: user.login
});
}
});
} else {
robot.logger.warning('[hubot-kredits] No KREDITS_GITHUB_KEY and KREDITS_GITHUB_SECRET configured for OAuth signup');
}
}; };

View File

@@ -129,8 +129,7 @@ module.exports = async function(robot, kredits) {
function calculateAmountForChanges(details) { function calculateAmountForChanges(details) {
let amount; let amount;
if (details.charsAdded < 280) { if (details.charsAdded < 500) {
// less than a tweet
amount = 500; amount = 500;
} else if (details.charsAdded < 2000) { } else if (details.charsAdded < 2000) {
amount = 1500; amount = 1500;

View File

@@ -0,0 +1,21 @@
module.exports = function (labels) {
const kreditsLabel = labels.filter(n => n.match(/^kredits/))[0];
// No label, no kredits
if (typeof kreditsLabel === 'undefined') { return 0; }
// TODO move amounts to config?
let amount;
switch(kreditsLabel) {
case 'kredits-1':
amount = 500;
break;
case 'kredits-2':
amount = 1500;
break;
case 'kredits-3':
amount = 5000;
break;
}
return amount;
};

View File

@@ -0,0 +1,18 @@
module.exports = function (labels) {
let kind = 'dev';
if (labels.find(l => l.match(/ops|operations/))) {
kind = 'ops';
}
else if (labels.find(l => l.match(/docs|documentation/))) {
kind = 'docs';
}
else if (labels.find(l => l.match(/design/))) {
kind = 'design';
}
else if (labels.find(l => l.match(/community/))) {
kind = 'community';
}
return kind;
};

1095
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "hubot-kredits", "name": "hubot-kredits",
"version": "3.2.0", "version": "3.4.1",
"description": "Kosmos Kredits functionality for chat bots", "description": "Kosmos Kredits functionality for chat bots",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -10,11 +10,15 @@
"create-wallet": "scripts/create-wallet.js" "create-wallet": "scripts/create-wallet.js"
}, },
"dependencies": { "dependencies": {
"cors": "^2.8.5",
"eth-provider": "^0.2.2", "eth-provider": "^0.2.2",
"ethers": "^4.0.27", "ethers": "^4.0.27",
"group-array": "^0.3.3", "express": "^4.17.1",
"express-session": "^1.16.2",
"grant-express": "^4.6.1",
"group-array": "^1.0.0",
"kosmos-schemas": "^1.1.2", "kosmos-schemas": "^1.1.2",
"kredits-contracts": "^5.3.0", "kredits-contracts": "^5.4.0",
"node-cron": "^2.0.3", "node-cron": "^2.0.3",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"prompt": "^1.0.0" "prompt": "^1.0.0"