From 5259b56e53b766e886d638752f1df76590f4fbd8 Mon Sep 17 00:00:00 2001 From: Sebastian Kippe Date: Thu, 19 Apr 2018 11:50:59 +0200 Subject: [PATCH] Split out integrations, add code section comments --- index.js | 190 ++++++++--------------------------------- integrations/github.js | 153 +++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 155 deletions(-) create mode 100644 integrations/github.js diff --git a/index.js b/index.js index 1f41f72..96072db 100644 --- a/index.js +++ b/index.js @@ -31,6 +31,15 @@ const ipfsConfig = { }; module.exports = async function(robot) { + + function messageRoom(message) { + robot.messageRoom(process.env.KREDITS_ROOM, message); + } + + // + // Ethereum wallet setup + // + let wallet; try { wallet = await ethers.Wallet.fromEncryptedWallet(walletJson, process.env.KREDITS_WALLET_PASSWORD); @@ -39,26 +48,34 @@ module.exports = async function(robot) { process.exit(1); } + // + // Ethereum provider/node setup + // + const ethProvider = new ethers.providers.JsonRpcProvider(providerUrl, {chainId: networkId}); ethProvider.signer = wallet; wallet.provider = ethProvider; + // + // Kredits contracts setup + // + let kredits; try { kredits = await Kredits.setup(ethProvider, wallet, ipfsConfig); } catch(error) { - console.log('[hubot-kredits] Could not setup kredits:', error); + console.log('[hubot-kredits] Could not set up kredits:', error); process.exit(1); } const Contributor = kredits.Contributor; const Operator = kredits.Operator; - function messageRoom(message) { - robot.messageRoom(process.env.KREDITS_ROOM, message); - } - robot.logger.info('[hubot-kredits] Wallet address: ' + wallet.address); + // + // Check robot's wallet balance and alert when it's broke + // + ethProvider.getBalance(wallet.address).then(balance => { robot.logger.info('[hubot-kredits] Wallet balance: ' + ethers.utils.formatEther(balance) + 'ETH'); if (balance.lt(ethers.utils.parseEther('0.0001'))) { @@ -66,6 +83,10 @@ module.exports = async function(robot) { } }); + // + // Robot chat commands/interaction + // + robot.respond(/got ETH\??/i, res => { ethProvider.getBalance(wallet.address).then((balance) => { res.send(`my wallet contains ${ethers.utils.formatEther(balance)} ETH`); @@ -93,156 +114,9 @@ module.exports = async function(robot) { }); }); - function getContributorByGithubUser(username) { - return Contributor.all().then(contributors => { - let contrib = contributors.find(c => { - return c.github_username === username; - }); - if (!contrib) { - throw new Error(`No contributor found for ${username}`);A - } else { - return contrib; - } - }); - } - - function createProposal(githubUser, amount, description, url, details) { - return getContributorByGithubUser(githubUser).then((contributor) => { - robot.logger.debug(`[kredits] Creating proposal to issue ${amount}₭S to ${githubUser} for ${url}...`); - let contributionAttr = { - contributorId: contributor.id, - amount: amount, - contributorIpfsHash: contributor.ipfsHash, - url, - description, - details, - kind: 'dev' - }; - return Operator.addProposal(contributionAttr).then((result) => { - robot.logger.debug('[kredits] proposal created:', util.inspect(result)); - }); - }).catch((error) => { - console.log([hubot-kredits] Error:, error); - messageRoom(`I wanted to propose giving kredits to ${githubUser} for ${url}, but I can't find their contact data. Please add them as a contributor: https://kredits.kosmos.org`); - }); - } - - function amountFromIssueLabels(issue) { - let 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 = 50; - break; - case 'kredits-2': - amount = 150; - break; - case 'kredits-3': - amount = 500; - break; - } - - return amount; - } - - function handleGitHubIssueClosed(data) { - let recipients; - let issue = data.issue; - let assignees = issue.assignees.map(a => a.login); - let web_url = issue.html_url; - - let amount = amountFromIssueLabels(issue); - if (amount === 0) { - console.log('[hubot-kredits] Proposal amount from issue label is zero; ignoring'); - return Promise.resolve(); - } - - if (assignees.length > 0) { - recipients = assignees; - } else { - recipients = [issue.user.login]; - } - - let repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1]; - let description = `${repoName}: ${issue.title}`; - - let proposalPromisses = []; - recipients.forEach(recipient => { - proposalPromisses.push( - createProposal(recipient, amount, description, web_url, issue) - .catch(err => robot.logger.error(err)) - ); - }); - - return Promise.all(proposalPromisses); - } - - 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; - - if (assignees.length > 0) { - recipients = assignees; - } else { - recipients = [pull_request.user.login]; - } - - return fetch(pr_issue_url) - .then(response => { - if (response.status >= 400) { - throw new Error('Bad response from fetching PR issue'); - } - return response.json(); - }) - .then(issue => { - let amount = amountFromIssueLabels(issue); - if (amount === 0) { - console.log('[hubot-kredits] Proposal amount from issue label is zero; ignoring'); - return; - } - - let repoName = pull_request.base.repo.full_name; - let description = `${repoName}: ${pull_request.title}`; - - let proposalPromisses = []; - recipients.forEach(recipient => { - console.debug(`[hubot-kredits] Creating proposal for ${recipient}...`); - proposalPromisses.push( - createProposal(recipient, amount, description, web_url, pull_request) - .catch(err => robot.logger.error(err)) - ); - }); - return Promise.all(proposalPromisses); - }); - } - - - robot.router.post('/incoming/kredits/github/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => { - let 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 - if (data.payload) { data = JSON.parse(data.payload); } - - robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`); - - if (evt === 'pull_request' && data.action === 'closed') { - handleGitHubPullRequestClosed(data).then(() => res.send(200)); - } - else if (evt === 'issues' && data.action === 'closed') { - handleGitHubIssueClosed(data).then(() => res.send(200)); - } else { - res.send(200); - } - }); + // + // Smart contract events + // function watchContractEvents() { ethProvider.getBlockNumber().then((blockNumber) => { @@ -267,4 +141,10 @@ module.exports = async function(robot) { watchContractEvents(); + // + // Integrations + // + + require('integrations/github')(robot, kredits); + }; diff --git a/integrations/github.js b/integrations/github.js new file mode 100644 index 0000000..99805d8 --- /dev/null +++ b/integrations/github.js @@ -0,0 +1,153 @@ +module.exports = async function(robot, kredits) { + + function getContributorByGithubUser(username) { + return Contributor.all().then(contributors => { + let contrib = contributors.find(c => { + return c.github_username === username; + }); + if (!contrib) { + throw new Error(`No contributor found for ${username}`);A + } else { + return contrib; + } + }); + } + + function createProposal(githubUser, amount, description, url, details) { + return getContributorByGithubUser(githubUser).then((contributor) => { + robot.logger.debug(`[kredits] Creating proposal to issue ${amount}₭S to ${githubUser} for ${url}...`); + let contributionAttr = { + contributorId: contributor.id, + amount: amount, + contributorIpfsHash: contributor.ipfsHash, + url, + description, + details, + kind: 'dev' + }; + return Operator.addProposal(contributionAttr).then((result) => { + robot.logger.debug('[kredits] proposal created:', util.inspect(result)); + }); + }).catch((error) => { + console.log([hubot-kredits] Error:, error); + messageRoom(`I wanted to propose giving kredits to ${githubUser} for ${url}, but I can't find their contact data. Please add them as a contributor: https://kredits.kosmos.org`); + }); + } + + function amountFromIssueLabels(issue) { + let 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 = 50; + break; + case 'kredits-2': + amount = 150; + break; + case 'kredits-3': + amount = 500; + break; + } + + return amount; + } + + function handleGitHubIssueClosed(data) { + let recipients; + let issue = data.issue; + let assignees = issue.assignees.map(a => a.login); + let web_url = issue.html_url; + + let amount = amountFromIssueLabels(issue); + if (amount === 0) { + console.log('[hubot-kredits] Proposal amount from issue label is zero; ignoring'); + return Promise.resolve(); + } + + if (assignees.length > 0) { + recipients = assignees; + } else { + recipients = [issue.user.login]; + } + + let repoName = issue.repository_url.match(/.*\/(.+\/.+)$/)[1]; + let description = `${repoName}: ${issue.title}`; + + let proposalPromisses = []; + recipients.forEach(recipient => { + proposalPromisses.push( + createProposal(recipient, amount, description, web_url, issue) + .catch(err => robot.logger.error(err)) + ); + }); + + return Promise.all(proposalPromisses); + } + + 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; + + if (assignees.length > 0) { + recipients = assignees; + } else { + recipients = [pull_request.user.login]; + } + + return fetch(pr_issue_url) + .then(response => { + if (response.status >= 400) { + throw new Error('Bad response from fetching PR issue'); + } + return response.json(); + }) + .then(issue => { + let amount = amountFromIssueLabels(issue); + if (amount === 0) { + console.log('[hubot-kredits] Proposal amount from issue label is zero; ignoring'); + return; + } + + let repoName = pull_request.base.repo.full_name; + let description = `${repoName}: ${pull_request.title}`; + + let proposalPromisses = []; + recipients.forEach(recipient => { + console.debug(`[hubot-kredits] Creating proposal for ${recipient}...`); + proposalPromisses.push( + createProposal(recipient, amount, description, web_url, pull_request) + .catch(err => robot.logger.error(err)) + ); + }); + return Promise.all(proposalPromisses); + }); + } + + robot.router.post('/incoming/kredits/github/'+process.env.KREDITS_WEBHOOK_TOKEN, (req, res) => { + let 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 + if (data.payload) { data = JSON.parse(data.payload); } + + robot.logger.info(`Received GitHub hook. Event: ${evt}, action: ${data.action}`); + + if (evt === 'pull_request' && data.action === 'closed') { + handleGitHubPullRequestClosed(data).then(() => res.send(200)); + } + else if (evt === 'issues' && data.action === 'closed') { + handleGitHubIssueClosed(data).then(() => res.send(200)); + } else { + res.send(200); + } + }); + +};