1 Commits

Author SHA1 Message Date
e9987b4d39 Remove network ID setting
Not needed anymore.
2019-04-24 13:32:14 +01:00
11 changed files with 324 additions and 1210 deletions

View File

@@ -1,4 +0,0 @@
template: |
## Changes
$CHANGES

View File

@@ -10,29 +10,9 @@ 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.
@@ -44,11 +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_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
@@ -59,12 +34,6 @@ 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:
@@ -76,27 +45,6 @@ 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
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 ### MediaWiki
@@ -113,6 +61,3 @@ 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,7 +7,6 @@ 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',
@@ -49,14 +48,13 @@ 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, opts).init(); kredits = await new Kredits(signer.provider, signer, {
// 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);
@@ -152,10 +150,6 @@ module.exports = async function(robot) {
require('./integrations/github')(robot, kredits); require('./integrations/github')(robot, kredits);
require('./integrations/gitea')(robot, kredits); require('./integrations/gitea')(robot, kredits);
if (typeof process.env.KREDITS_ZOOM_JWT !== 'undefined') {
require('./integrations/zoom')(robot, kredits);
}
if (typeof process.env.KREDITS_MEDIAWIKI_URL !== 'undefined') { if (typeof process.env.KREDITS_MEDIAWIKI_URL !== 'undefined') {
require('./integrations/mediawiki')(robot, kredits); require('./integrations/mediawiki')(robot, kredits);
} }

View File

@@ -1,7 +1,5 @@
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));
@@ -37,7 +35,7 @@ module.exports = async function(robot, kredits) {
}); });
} }
function createContribution(giteaUser, date, time, amount, kind, description, url, details) { function createContribution(giteaUser, date, time, amount, 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}...`);
@@ -47,15 +45,12 @@ module.exports = async function(robot, kredits) {
date, date,
time, time,
amount, amount,
kind,
description,
url, url,
details description,
details,
kind: 'dev'
}; };
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:`);
@@ -64,14 +59,35 @@ 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.number}`; const web_url = `${data.repository.html_url}/issues/${issue.id}`;
const description = `${repoName}: ${issue.title}`; const description = `${repoName}: ${issue.title}`;
const labels = issue.labels.map(l => l.name); const amount = amountFromLabels(issue.labels);
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');
@@ -92,8 +108,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, await createContribution(recipient, date, time, amount, description, web_url,
kind, description, web_url,
{ issue, repository: data.repository }); { issue, repository: data.repository });
await sleep(60000); await sleep(60000);
} }
@@ -108,9 +123,7 @@ 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 labels = pull_request.labels.map(l => l.name); const amount = amountFromLabels(pull_request.labels);
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');
@@ -129,10 +142,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, await createContribution(recipient, date, time, amount, description, web_url,
kind, description, web_url,
{ pull_request, repository: data.repository }); { pull_request, repository: data.repository });
await sleep(60000); await sleep(60000);
} }

View File

@@ -1,10 +1,5 @@
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));
@@ -24,8 +19,6 @@ 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;
@@ -42,9 +35,9 @@ module.exports = async function(robot, kredits) {
}); });
} }
function createContribution(githubUser, date, time, amount, kind, description, url, details) { function createContribution(githubUser, date, time, amount, description, url, details) {
return getContributorByGithubUser(githubUser).then(contributor => { return getContributorByGithubUser(githubUser).then(contributor => {
robot.logger.info(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${githubUser} for ${url}...`); robot.logger.debug(`[hubot-kredits] Creating contribution token for ${amount}₭S to ${githubUser} for ${url}...`);
const contributionAttr = { const contributionAttr = {
contributorId: contributor.id, contributorId: contributor.id,
@@ -52,15 +45,12 @@ module.exports = async function(robot, kredits) {
date, date,
time, time,
amount, amount,
kind,
description,
url, url,
details description,
details,
kind: 'dev'
}; };
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:`);
@@ -69,17 +59,38 @@ 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 labels = issue.labels.map(l => l.name); const amount = amountFromIssueLabels(issue);
const amount = amountFromLabels(labels); const repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1];
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) {
@@ -98,7 +109,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, kind, description, web_url, issue); await createContribution(recipient, date, time, amount, description, web_url, issue);
await sleep(60000); await sleep(60000);
} }
catch (err) { robot.logger.error(err); } catch (err) { robot.logger.error(err); }
@@ -130,10 +141,8 @@ module.exports = async function(robot, kredits) {
return response.json(); return response.json();
}) })
.then(async (issue) => { .then(async (issue) => {
const labels = issue.labels.map(l => l.name); const amount = amountFromIssueLabels(issue);
const amount = amountFromLabels(labels); const repoName = pull_request.base.repo.full_name;
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) {
@@ -146,7 +155,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, kind, description, web_url, pull_request); await createContribution(recipient, date, time, amount, description, web_url, pull_request);
await sleep(60000); await sleep(60000);
} }
catch (err) { robot.logger.error(err); } catch (err) { robot.logger.error(err); }
@@ -167,111 +176,14 @@ 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.sendStatus(200); res.send(200);
} }
else if (evt === 'issues' && data.action === 'closed') { else if (evt === 'issues' && data.action === 'closed') {
handleGitHubIssueClosed(data); handleGitHubIssueClosed(data);
res.sendStatus(200); res.send(200);
} else { } else {
res.sendStatus(200); res.send(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,7 +129,8 @@ module.exports = async function(robot, kredits) {
function calculateAmountForChanges(details) { function calculateAmountForChanges(details) {
let amount; let amount;
if (details.charsAdded < 500) { if (details.charsAdded < 280) {
// less than a tweet
amount = 500; amount = 500;
} else if (details.charsAdded < 2000) { } else if (details.charsAdded < 2000) {
amount = 1500; amount = 1500;

View File

@@ -1,21 +0,0 @@
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

@@ -1,18 +0,0 @@
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;
};

View File

@@ -1,96 +0,0 @@
const fetch = require('node-fetch');
module.exports = async function(robot, kredits) {
function messageRoom(message) {
robot.messageRoom(process.env.KREDITS_ROOM, message);
}
const { Contributor, Contribution } = kredits;
const kreditsContributionAmount = 500;
const kreditsContributionKind = 'community';
const zoomAccessToken = process.env.KREDITS_ZOOM_JWT;
const walletTransactionCount = await kredits.provider.getTransactionCount(kredits.signer.address);
let nonce = walletTransactionCount;
async function createContributionFor (displayName, meeting) {
const contributor = await getContributorByZoomDisplayName(displayName);
if (!contributor) {
robot.logger.info(`[hubot-kredits] Contributor not found: Zoom display name: ${displayName}`);
messageRoom(`I tried to add a contribution for zoom user ${displayName}, but did not find a matching contributor profile.`);
return Promise.resolve();
}
const contribution = {
contributorId: contributor.id,
contributorIpfsHash: contributor.ipfsHash,
amount: kreditsContributionAmount,
kind: kreditsContributionKind,
description: 'Team/Community Call',
date: meeting.end_time.split('T')[0],
time: meeting.end_time.split('T')[1]
}
return Contribution.add(contribution, { nonce: nonce++ })
.then(tx => {
robot.logger.info(`[hubot-kredits] Contribution created: ${tx.hash}`);
})
.catch(error => {
robot.logger.error(`[hubot-kredits] Adding contribution for Zoom call failed:`, error);
});
}
function getContributorByZoomDisplayName(displayName) {
return Contributor.findByAccount({ site: 'zoom.us', username: displayName });
}
function request(path) {
return fetch(
`https://api.zoom.us/v2${path}`,
{headers: {authorization: `Bearer ${zoomAccessToken}`}}
);
}
function getMeetingParticipants(meetingUUID) {
return request(`/past_meetings/${meetingUUID}/participants`)
.then(response => response.json())
.then(json => json.participants)
}
function getMeetingDetails(meetingUUID) {
return request(`/past_meetings/${meetingUUID}`)
.then(r => r.json());
}
async function handleZoomMeetingEnded(data) {
const meetingDetails = await getMeetingDetails(data.uuid);
const participants = await getMeetingParticipants(data.uuid);
const names = Array.from(new Set(participants.map(p => p.name)));
if (meetingDetails.duration < 15 || names.length < 3) {
robot.logger.info(`[hubot-kredits] Ignoring zoom call ${data.uuid} (duration: ${meetingDetails.duration}, participants_count: ${meetingDetails.participants_count})`);
return;
}
for (const displayName of names) {
await createContributionFor(displayName, meetingDetails);
};
}
robot.router.post('/incoming/kredits/zoom/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => {
let data = req.body;
const eventName = data.event;
const payload = data.payload;
const object = payload.object;
if (eventName === 'meeting.ended') {
handleZoomMeetingEnded(object);
}
res.sendStatus(200);
})
}

1088
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.5.1", "version": "3.1.2",
"description": "Kosmos Kredits functionality for chat bots", "description": "Kosmos Kredits functionality for chat bots",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -10,15 +10,11 @@
"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",
"express": "^4.17.1", "group-array": "^0.3.3",
"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.4.0", "kredits-contracts": "github:67P/kredits-contracts#feature/gitea_site",
"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"