From 434f8c9cf5eb21cb4c8fec72c98dee8887da93c2 Mon Sep 17 00:00:00 2001 From: Pedro Pombeiro Date: Mon, 19 Mar 2018 23:02:24 +0100 Subject: [PATCH] Add ESLint and convert code to ES6 --- .eslintrc.json | 3 + bot/github.js | 74 ++++++-------- bot/index.js | 249 +++++++++++++++++++++------------------------- bot/prices.js | 117 ++++++++++------------ config/default.js | 122 +++++++++++------------ config/index.js | 6 +- index.js | 145 ++++++++++++--------------- package.json | 13 ++- test/test_bot.js | 69 +++++++------ 9 files changed, 371 insertions(+), 427 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..d658236 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "standard" +} \ No newline at end of file diff --git a/bot/github.js b/bot/github.js index 0604f26..84d9ed1 100644 --- a/bot/github.js +++ b/bot/github.js @@ -2,54 +2,46 @@ const https = require('https') const config = require('../config') +const bot = require('../bot') // Returns the url for getting the labels of a request (Github API v3) // req has req.issue.labels_url -const getLabelsURL = function (req) { - let url = req.body.issue.labels_url - // Make the URL generic removing the name of the label - return url.replace('{/name}', '') -} - -// Returns the bounty labelNames of the request, only for testing motives -const getLabelsMock = function (req) { - return new Promise((resolve, reject) => { resolve(req.body.issue.labels) }) +function getLabelsURL (req) { + let url = req.body.issue.labels_url + // Make the URL generic removing the name of the label + return url.replace('{/name}', '') } // Returns all the bounty labelNames of a given issue (Github API v3) -const getLabels = function (req) { - if (config.debug) { - return getLabelsMock(req) - } else { - const path = getLabelsURL(req).replace('https://api.github.com', '') - const options = { - hostname: 'api.github.com', - path: path, - headers: { 'User-Agent': config.githubUsername } - } - return new Promise((resolve, reject) => { - const request = https.get(options, (response) => { - // handle http errors - if (response.statusCode < 200 || response.statusCode > 299) { - bot.error(response, 'Failed to load page, status code: ' + response.statusCode) - reject(new Error('Failed to load page, status code: ' + response.statusCode)) - } - // temporary data holder - const body = [] - // on every content chunk, push it to the data array - response.on('data', (chunk) => body.push(chunk)) - // we are done, resolve promise with those joined chunks - response.on('end', () => { - const labels = JSON.parse(body.join('')).map(labelObj => labelObj.name) - resolve(labels) - }) - }) - // handle connection errors of the request - request.on('error', (err) => reject(err)) - }) - } +function getLabels (req) { + const path = getLabelsURL(req).replace('https://api.github.com', '') + const options = { + hostname: 'api.github.com', + path: path, + headers: { 'User-Agent': config.githubUsername } + } + return new Promise((resolve, reject) => { + const request = https.get(options, (response) => { + // handle http errors + if (response.statusCode < 200 || response.statusCode > 299) { + bot.error(response, `Failed to load page, status code: ${response.statusCode}`) + reject(new Error(`Failed to load page, status code: ${response.statusCode}`)) + } + // temporary data holder + const body = [] + // on every content chunk, push it to the data array + response.on('data', (chunk) => body.push(chunk)) + // we are done, resolve promise with those joined chunks + response.on('end', () => { + const labels = JSON.parse(body.join('')).map(labelObj => labelObj.name) + resolve(labels) + }) + }) + // handle connection errors of the request + request.on('error', (err) => reject(err)) + }) } module.exports = { - getLabels: getLabels + getLabels: getLabels } diff --git a/bot/index.js b/bot/index.js index 6128ff2..0b45411 100644 --- a/bot/index.js +++ b/bot/index.js @@ -4,180 +4,155 @@ const ethers = require('ethers') const Wallet = ethers.Wallet const Contract = ethers.Contract const providers = ethers.providers -const utils = ethers.utils -const prices = require('./prices') const config = require('../config') +const prices = require('./prices') const github = require('./github') const contractAddressString = 'Contract address:' - const logger = winston.createLogger({ - level: 'info', - format: winston.format.json(), - transports: [ - new winston.transports.File({ filename: './log/error.log', level: 'error' }), - new winston.transports.File({ filename: './log/info.log', level: 'info' }), - // new winston.transports.File({ filename: './log/combined.log' }) - ] + level: 'info', + format: winston.format.json(), + transports: [ + new winston.transports.File({ filename: './log/error.log', level: 'error' }), + new winston.transports.File({ filename: './log/info.log', level: 'info' }) + // new winston.transports.File({ filename: './log/combined.log' }) + ] }) +function needsFunding (req) { + if (req.body.action !== 'edited' || !req.body.hasOwnProperty('comment')) { + return false + } else if (req.body.comment.user.login !== config.githubUsername) { + return false + } else if (!hasAddress(req)) { + return false + } + return true +} -const needsFunding = function (req) { - if (req.body.action !== 'edited' || !req.body.hasOwnProperty('comment')) { - return false - } else if (req.body.comment.user.login !== config.githubUsername) { - return false - } else if (!hasAddress(req)) { - return false - } +function hasAddress (req) { + const commentBody = req.body.comment.body + if (commentBody.search(contractAddressString) === -1) { + return false + } else { return true + } } -const hasAddress = function (req) { - const commentBody = req.body.comment.body - if (commentBody.search(contractAddressString) === -1) { - return false - } else { - return true - } +function getAddress (req) { + const commentBody = req.body.comment.body + const index = commentBody.search(contractAddressString) + 19 + return commentBody.substring(index, index + 42) } -const getAddress = function (req) { - const commentBody = req.body.comment.body - const index = commentBody.search(contractAddressString) + 19 - return commentBody.substring(index, index + 42) +async function getLabel (req) { + const labels = await github.getLabels(req) + const bountyLabels = labels.filter(label => config.bountyLabels.hasOwnProperty(label.name)) + if (bountyLabels.length === 1) { + return bountyLabels[0] + } else { + throw new Error('Error getting bounty labels') + } } -const getLabel = function (req) { - return new Promise((resolve, reject) => { - github.getLabels(req) - .then(labels => { - const bountyLabels = labels.filter(label => config.bountyLabels.hasOwnProperty(label.name)) - if (bountyLabels.length === 1) { - resolve(bountyLabels[0]) - } else { - reject('Error getting bounty labels') - } - }).catch(err => { - reject(err) - }) - }) +async function getAmount (req) { + const label = await getLabel(req) + const tokenPrice = await prices.getTokenPrice(config.token) + + const amountToPayDollar = config.priceHour * config.bountyLabels[label.name] + return (amountToPayDollar / tokenPrice) } -const getAmountMock = function (req) { - return new Promise((resolve, reject) => { - resolve(10) - }) -} - -const getAmount = function (req) { - return new Promise((resolve, reject) => { - const labelPromise = getLabel(req) - const tokenPricePromise = prices.getTokenPrice(config.token) - - Promise.all([labelPromise, tokenPricePromise]) - .then(function (values) { - const label = values[0] - const tokenPrice = values[1] - const amountToPayDollar = config.priceHour * config.bountyLabels[label.name] - resolve(amountToPayDollar / tokenPrice) - }) - .catch((err) => { - reject(err) - }) - }) -} - - // Logging functions -const logTransaction = function (tx) { - logger.info("[OK] Succesfully funded bounty with transaction " + tx.hash) - logger.info(" * From: " + tx.from) - logger.info(" * To: " + tx.to) - logger.info(" * Amount: " + tx.value) - logger.info(" * Gas Price: " + tx.gasPrice) - logger.info("====================================================") +function logTransaction (tx) { + logger.info(`[OK] Succesfully funded bounty with transaction ${tx.hash}`) + logger.info(` * From: ${tx.from}`) + logger.info(` * To: ${tx.to}`) + logger.info(` * Amount: ${tx.value}`) + logger.info(` * Gas Price: ${tx.gasPrice}`) + logger.info(`====================================================`) } -const info = function (msg) { - logger.info(msg) +function info (msg) { + logger.info(msg) } -const error = function (errorMessage) { - logger.error("[ERROR] Request processing failed: " + errorMessage) +function error (errorMessage) { + logger.error(`[ERROR] Request processing failed: ${errorMessage}`) } +async function sendTransaction (to, amount, gasPrice) { + let hash = null + let chainId = providers.Provider.chainId.ropsten + let chainName = providers.networks.ropsten -const sendTransaction = async function (to, amount, gasPrice) { - let chainId = providers.Provider.chainId.ropsten - let chainName = providers.networks.ropsten + if (config.realTransaction) { + chainId = providers.Provider.chainId.homestead + chainName = providers.networks.homestead + } - if (config.realTransaction) { - chainId = providers.Provider.chainId.homestead - chainName = providers.networks.homestead + const wallet = new Wallet(config.privateKey) + wallet.provider = ethers.providers.getDefaultProvider(chainName) + + if (config.token === 'ETH') { + const transaction = { + gasLimit: config.gasLimit, + gasPrice: gasPrice, + to: to, + value: amount, + chainId: chainId } - const wallet = new Wallet(config.privateKey) - const provider = ethers.providers.getDefaultProvider(chainName) - - wallet.provider = provider - if (config.token === 'ETH') { - const transaction = { - gasLimit: config.gasLimit, - gasPrice: gasPrice, - to: to, - value: amount, - chainId: chainId - } + hash = await wallet.sendTransaction(transaction) + } else { + const customSigner = getCustomSigner(wallet, sendTransaction) + const tokenContract = config.tokenContracts[config.token] + const contractAddress = tokenContract.address + const contract = new Contract(contractAddress, tokenContract.abi, customSigner) + const bigNumberAmount = ethers.utils.bigNumberify(amount) - return await wallet.sendTransaction(transaction) - } else { - let hash = null + await contract.transfer(to, bigNumberAmount) + } - async function getAddress() { return wallet.address } - async function sign(transaction) { return wallet.sign(transaction) } + return hash +} - async function resolveName(addressOrName) { return await provider.resolveName(addressOrName) } - async function estimateGas(transaction) { return await provider.estimateGas(transaction) } - async function getGasPrice() { return await provider.getGasPrice() } - async function getTransactionCount(blockTag) { return await provider.getTransactionCount(blockTag) } - async function sendTransaction(transaction) { - hash = await provider.sendTransaction(transaction) - return hash - } - - const customSigner = { - getAddress: getAddress, - provider: { - resolveName: resolveName, - estimateGas: estimateGas, - getGasPrice: getGasPrice, - getTransactionCount: getTransactionCount, - sendTransaction: sendTransaction - }, - sign: sign - } +function getCustomSigner (wallet, sendTransaction) { + const provider = wallet.provider - const tokenContract = config.tokenContracts[config.token] - const contractAddress = tokenContract.address - const contract = new Contract(contractAddress, tokenContract.abi, customSigner) - const bigNumberAmount = ethers.utils.bigNumberify(amount) - await contract.transfer(to, bigNumberAmount) + async function getAddress () { return wallet.address } + async function sign (transaction) { return wallet.sign(transaction) } - return hash - } + async function resolveName (addressOrName) { return provider.resolveName(addressOrName) } + async function estimateGas (transaction) { return provider.estimateGas(transaction) } + async function getGasPrice () { return provider.getGasPrice() } + async function getTransactionCount (blockTag) { return provider.getTransactionCount(blockTag) } + + const customSigner = { + getAddress: getAddress, + provider: { + resolveName: resolveName, + estimateGas: estimateGas, + getGasPrice: getGasPrice, + getTransactionCount: getTransactionCount, + sendTransaction: sendTransaction + }, + sign: sign + } + + return customSigner } module.exports = { - needsFunding: needsFunding, - getAddress: getAddress, - getAmount: getAmount, - getGasPrice: prices.getGasPrice, - sendTransaction: sendTransaction, - info: info, - logTransaction: logTransaction, - error: error + needsFunding: needsFunding, + getAddress: getAddress, + getAmount: getAmount, + getGasPrice: prices.getGasPrice, + sendTransaction: sendTransaction, + info: info, + logTransaction: logTransaction, + error: error } diff --git a/bot/prices.js b/bot/prices.js index c938336..bf78d2e 100644 --- a/bot/prices.js +++ b/bot/prices.js @@ -1,73 +1,62 @@ -"use strict" +'use strict' -const https = require("https") -const config = require("../config") - - -const getGasPrice = function () { - const url = 'https://ethgasstation.info/json/ethgasAPI.json' - // return new pending promise - return new Promise((resolve, reject) => { - // select http or https module, depending on reqested url - const lib = url.startsWith('https') ? require('https') : require('http') - const request = lib.get(url, (response) => { - // handle http errors - if (response.statusCode < 200 || response.statusCode > 299) { - reject('Failed to load page, status code: ' + response.statusCode) - } - // temporary data holder - const body = [] - // on every content chunk, push it to the data array - response.on('data', (chunk) => body.push(chunk)) - // we are done, resolve promise with those joined chunks - response.on('end', () => { - // safeLowWait returns GWei (10^10 Wei). - const jsonBody = JSON.parse(body.join('')) - const gasPriceWei = parseInt(jsonBody['safeLowWait']) * Math.pow(10, 10) - resolve(gasPriceWei) - }) - }) - // handle connection errors of the request - request.on('error', (err) => reject(err)) +function getGasPrice () { + const url = 'https://ethgasstation.info/json/ethgasAPI.json' + // return new pending promise + return new Promise((resolve, reject) => { + // select http or https module, depending on reqested url + const lib = url.startsWith('https') ? require('https') : require('http') + const request = lib.get(url, (response) => { + // handle http errors + if (response.statusCode < 200 || response.statusCode > 299) { + reject(new Error(`Failed to load page, status code: ${response.statusCode}`)) + } + // temporary data holder + const body = [] + // on every content chunk, push it to the data array + response.on('data', (chunk) => body.push(chunk)) + // we are done, resolve promise with those joined chunks + response.on('end', () => { + // safeLowWait returns GWei (10^10 Wei). + const jsonBody = JSON.parse(body.join('')) + const gasPriceWei = parseInt(jsonBody['safeLowWait']) * Math.pow(10, 10) + resolve(gasPriceWei) + }) }) + // handle connection errors of the request + request.on('error', (err) => reject(err)) + }) } -const getTokenPriceMock = function () { - return new Promise((resolve, reject) => resolve(0.35)) -} - -const getTokenPrice = function (token) { - if (config.debug) { - return getTokenPriceMock() - } - const currency = 'USD' - const url = 'https://min-api.cryptocompare.com/data/price?fsym=' + token + '&tsyms=' + currency - // return new pending promise - return new Promise((resolve, reject) => { - // select http or https module, depending on reqested url - const lib = url.startsWith('https') ? require('https') : require('http') - const request = lib.get(url, (response) => { - // handle http errors - if (response.statusCode < 200 || response.statusCode > 299) { - reject('Failed to load page, status code: ' + response.statusCode) - } - // temporary data holder - const body = [] - // on every content chunk, push it to the data array - response.on('data', (chunk) => body.push(chunk)) - // we are done, resolve promise with those joined chunks - response.on('end', () => { - const jsonBody = JSON.parse(body.join('')) - const tokenPrice = parseFloat(jsonBody[currency]) - resolve(tokenPrice) - }) - }) - // handle connection errors of the request - request.on('error', (err) => reject(err)) +function getTokenPrice (token) { + const currency = 'USD' + const url = `https://min-api.cryptocompare.com/data/price?fsym=${token}&tsyms=${currency}` + // return new pending promise + return new Promise((resolve, reject) => { + // select http or https module, depending on reqested url + const lib = url.startsWith('https') ? require('https') : require('http') + const request = lib.get(url, (response) => { + // handle http errors + if (response.statusCode < 200 || response.statusCode > 299) { + reject(new Error(`Failed to load page, status code: ${response.statusCode}`)) + } + // temporary data holder + const body = [] + // on every content chunk, push it to the data array + response.on('data', (chunk) => body.push(chunk)) + // we are done, resolve promise with those joined chunks + response.on('end', () => { + const jsonBody = JSON.parse(body.join('')) + const tokenPrice = parseFloat(jsonBody[currency]) + resolve(tokenPrice) + }) }) + // handle connection errors of the request + request.on('error', (err) => reject(err)) + }) } module.exports = { - getGasPrice: getGasPrice, - getTokenPrice: getTokenPrice + getGasPrice: getGasPrice, + getTokenPrice: getTokenPrice } diff --git a/config/default.js b/config/default.js index 22b0cf3..d25a227 100644 --- a/config/default.js +++ b/config/default.js @@ -1,85 +1,85 @@ // Work hours per label const BOUNTY_LABELS = { - 'bounty-xs': 1, - 'bounty-s': 10, - 'bounty-m': 100, - 'bounty-l': 1000, - 'bounty-xl': 10000 + 'bounty-xs': 1, + 'bounty-s': 10, + 'bounty-m': 100, + 'bounty-l': 1000, + 'bounty-xl': 10000 } const ERC20_ABI = [ - { - "constant": false, - "inputs": [ - { - "name": "_to", - "type": "address" - }, - { - "name": "_amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "name": "success", - "type": "bool" - } - ], - "payable": false, - "type": "function" - } + { + 'constant': false, + 'inputs': [ + { + 'name': '_to', + 'type': 'address' + }, + { + 'name': '_amount', + 'type': 'uint256' + } + ], + 'name': 'transfer', + 'outputs': [ + { + 'name': 'success', + 'type': 'bool' + } + ], + 'payable': false, + 'type': 'function' + } ] const TOKEN_CONTRACTS = { - 'STT': { - address: '0xc55cF4B03948D7EBc8b9E8BAD92643703811d162', - abi: ERC20_ABI - }, - 'SNT': { - address: '0x744d70fdbe2ba4cf95131626614a1763df805b9e', - abi: ERC20_ABI - } + 'STT': { + address: '0xc55cF4B03948D7EBc8b9E8BAD92643703811d162', + abi: ERC20_ABI + }, + 'SNT': { + address: '0x744d70fdbe2ba4cf95131626614a1763df805b9e', + abi: ERC20_ABI + } } module.exports = { - // Debug mode for testing the bot - debug: true, + // Debug mode for testing the bot + debug: true, - // URL where the bot is listening (e.g. '/funding') - urlEndpoint: '/', + // URL where the bot is listening (e.g. '/funding') + urlEndpoint: '/', - // URL for the signer - signerPath: 'https://ropsten.infura.io', + // URL for the signer + signerPath: 'https://ropsten.infura.io', - // Address with the funding for the bounties - sourceAddress: '0x26a4D114B98C4b0B0118426F10fCc1112AA2864d', + // Address with the funding for the bounties + sourceAddress: '0x26a4D114B98C4b0B0118426F10fCc1112AA2864d', - // Private key for ether.js wallet - privateKey: '', + // Private key for ether.js wallet + privateKey: '', - // Token of the currency for fetching real time prices (e.g. 'SNT') - token: 'SNT', + // Token of the currency for fetching real time prices (e.g. 'SNT') + token: 'SNT', - // Limit for the gas used in a transaction (e.g. 92000) - gasLimit: 92000, + // Limit for the gas used in a transaction (e.g. 92000) + gasLimit: 92000, - // Price per hour you will pay in dolars (e.g. 35) - priceHour: 1, + // Price per hour you will pay in dolars (e.g. 35) + priceHour: 1, - // Delay before funding a bounty (e.g. 3600000) - delayInMiliSeconds: 10000, + // Delay before funding a bounty (e.g. 3600000) + delayInMiliSeconds: 10000, - // Bounty Labels for the issues and the correspondent hours (e.g. {'bounty-xs': 3}) - bountyLabels: BOUNTY_LABELS, + // Bounty Labels for the issues and the correspondent hours (e.g. {'bounty-xs': 3}) + bountyLabels: BOUNTY_LABELS, - // Contract info for the different supported tokens - tokenContracts: TOKEN_CONTRACTS, + // Contract info for the different supported tokens + tokenContracts: TOKEN_CONTRACTS, - // username for the bot which has to comment for starting the process (e.g. status-bounty-) - githubUsername: 'status-open-bounty', + // username for the bot which has to comment for starting the process (e.g. status-bounty-) + githubUsername: 'status-open-bounty', - // Activate real transactions - realTransaction: false + // Activate real transactions + realTransaction: false } diff --git a/config/index.js b/config/index.js index 94567eb..8fbdbdd 100644 --- a/config/index.js +++ b/config/index.js @@ -1,4 +1,4 @@ -const _ = require("lodash") -const defaults = require("./default.js") -const config = require("./" + (process.env.NODE_ENV || "default") + ".js") +const _ = require('lodash') +const defaults = require('./default.js') +const config = require('./' + (process.env.NODE_ENV || 'default') + '.js') module.exports = _.merge({}, defaults, config) diff --git a/index.js b/index.js index 2215922..c9d0463 100644 --- a/index.js +++ b/index.js @@ -11,110 +11,89 @@ const config = require('./config') const bot = require('./bot') const crypto = require('crypto') - -const express = require('express'), - cors = require('cors'), - helmet = require('helmet'), - app = express(), - bodyParser = require('body-parser'), - jsonParser = bodyParser.json() +const express = require('express') +const cors = require('cors') +const helmet = require('helmet') +const app = express() +const bodyParser = require('body-parser') +const jsonParser = bodyParser.json() app.use(cors()) app.use(helmet()) // Receive a POST request at the url specified by an env. var. app.post(`${config.urlEndpoint}`, jsonParser, function (req, res, next) { - - if (!req.body || !req.body.action) { - return res.sendStatus(400) - } else if (!bot.needsFunding(req)) { - return res.sendStatus(204) - } - validation = validateRequest(req) + if (!req.body || !req.body.action) { + return res.sendStatus(400) + } else if (!bot.needsFunding(req)) { + return res.sendStatus(204) + } - if (validation.correct) { + const validation = validateRequest(req) - setTimeout(() => { - processRequest(req) - .then(() => { - bot.info('issue well funded: ' + req.body.issue.url) - }) - .catch((err) => { - bot.error('Error processing request: ' + req.body.issue.url) - bot.error('Error: ' + err) - bot.error('Dump: ', req.body) - }) - }, config.delayInMiliSeconds) - - } else { - bot.error('Error validating issue: ' + req.body.issue.url) - bot.error('Error: ' + validation.error) - } - return res.sendStatus(200) + if (validation.correct) { + setTimeout(async () => { + try { + await processRequest(req) + bot.info(`issue well funded: ${req.body.issue.url}`) + } catch (err) { + bot.error(`Error processing request: ${req.body.issue.url}`) + bot.error(`Error: ${err}`) + bot.error(`Dump: ${req.body}`) + } + }, config.delayInMiliSeconds) + } else { + bot.error(`Error validating issue: ${req.body.issue.url}`) + bot.error(`Error: ${validation.error}`) + } + return res.sendStatus(200) }) -const validateRequest = function (req) { - validation = {correct: false, error: ''} - webhookSecret = process.env.WEBHOOK_SECRET +function validateRequest (req) { + const validation = { correct: false, error: '' } + const webhookSecret = process.env.WEBHOOK_SECRET - if(!webhookSecret) { - validation.error = 'Github Webhook Secret key not found. ' + - 'Please set env variable WEBHOOK_SECRET to github\'s webhook secret value' + if (!webhookSecret) { + validation.error = 'Github Webhook Secret key not found. ' + + 'Please set env variable WEBHOOK_SECRET to github\'s webhook secret value' + } else { + const blob = JSON.stringify(req.body) + const hmac = crypto.createHmac('sha1', webhookSecret) + const ourSignature = `sha1=${hmac.update(blob).digest('hex')}` + + const theirSignature = req.get('X-Hub-Signature') + + const bufferA = Buffer.from(ourSignature, 'utf8') + const bufferB = Buffer.from(theirSignature, 'utf8') + + const safe = crypto.timingSafeEqual(bufferA, bufferB) + + if (safe) { + validation.correct = true } else { - - const blob = JSON.stringify(req.body) - const hmac = crypto.createHmac('sha1', webhookSecret) - const ourSignature = `sha1=${hmac.update(blob).digest('hex')}` - - const theirSignature = req.get('X-Hub-Signature') - - const bufferA = Buffer.from(ourSignature, 'utf8') - const bufferB = Buffer.from(theirSignature, 'utf8') - - const safe = crypto.timingSafeEqual(bufferA, bufferB) - - if (safe) { - validation.correct = true - } else { - validation.error = 'Invalid signature. Check that WEBHOOK_SECRET ' + - 'env variable matches github\'s webhook secret value' - } + validation.error = 'Invalid signature. Check that WEBHOOK_SECRET ' + + 'env variable matches github\'s webhook secret value' } + } - return validation + return validation } -const processRequest = function (req) { - // const wallet = bot.wallet +async function processRequest (req) { + // const wallet = bot.wallet - const from = config.sourceAddress - const to = bot.getAddress(req) + const to = bot.getAddress(req) - // Asynchronous requests for Gas Price and Amount - const amountPromise = bot.getAmount(req) - const gasPricePromise = bot.getGasPrice() - return new Promise((resolve, reject) => { - Promise.all([amountPromise, gasPricePromise]) - .then(function (results) { - const amount = results[0] - const gasPrice = results[1] + // Asynchronous requests for Gas Price and Amount + const amount = await bot.getAmount(req) + const gasPrice = await bot.getGasPrice() - bot.sendTransaction(to, amount, gasPrice) - .then(function (hash) { - bot.logTransaction(hash) - resolve() - }) - .catch(function (err) { - reject(err) - }) - }) - .catch(function (err) { - reject(err) - }) - }) + const hash = await bot.sendTransaction(to, amount, gasPrice) + + bot.logTransaction(hash) } const port = process.env.PORT || 8181 app.listen(port, function () { - bot.info('Autobounty listening on port', port) + bot.info('Autobounty listening on port', port) }) diff --git a/package.json b/package.json index 28b3548..269ebcc 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,7 @@ "license": "ISC", "dependencies": { "body-parser": "^1.17.2", - "chai": "^4.1.2", "cors": "^2.8.1", - "eslint": "^4.15.0", "ethers": "^2.2.6", "ethjs-provider-signer": "^0.1.4", "ethjs-query": "^0.2.4", @@ -22,8 +20,17 @@ "express": "^4.15.2", "helmet": "^3.9.0", "lodash": "^4.17.4", - "mocha": "^5.0.0", "web3": "^0.18.2", "winston": "^3.0.0-rc1" + }, + "devDependencies": { + "chai": "^4.1.2", + "eslint": "^4.19.0", + "eslint-config-standard": "^11.0.0", + "eslint-plugin-import": "^2.9.0", + "eslint-plugin-node": "^6.0.1", + "eslint-plugin-promise": "^3.7.0", + "eslint-plugin-standard": "^3.0.1", + "mocha": "^5.0.4" } } diff --git a/test/test_bot.js b/test/test_bot.js index c16b1f2..6215361 100644 --- a/test/test_bot.js +++ b/test/test_bot.js @@ -5,52 +5,51 @@ const should = require('chai').should const config = require('../config') const bot = require('../bot') - // status-open-bounty comment from https://github.com/status-im/autobounty/issues/1 -const sob_comment = 'Current balance: 0.000000 ETH\nTokens: SNT: 2500.00 ANT: 25.00\nContract address: 0x3645fe42b1a744ad98cc032c22472388806f86f9\nNetwork: Mainnet\n To claim this bounty sign up at https://openbounty.status.im and make sure to update your Ethereum address in My Payment Details so that the bounty is correctly allocated.\nTo fund it, send ETH or ERC20/ERC223 tokens to the contract address.' +const sobComment = 'Current balance: 0.000000 ETH\nTokens: SNT: 2500.00 ANT: 25.00\nContract address: 0x3645fe42b1a744ad98cc032c22472388806f86f9\nNetwork: Mainnet\n To claim this bounty sign up at https://openbounty.status.im and make sure to update your Ethereum address in My Payment Details so that the bounty is correctly allocated.\nTo fund it, send ETH or ERC20/ERC223 tokens to the contract address.' // Fake requests const requests = [ - { body: { action: 'created', comment: { body: 'Creating my first comment', user: { login: 'randomUser' } } } }, - { body: { action: 'edited', comment: { body: 'Editing my comment', user: { login: 'RandomUser' } } } }, - { body: { action: 'edited', comment: { body: sob_comment, user: { login: 'status-open-bounty' } } } }, - { body: { action: 'created', issue: { labels: ['bounty', 'bounty-s'] }, comment: { body: sob_comment, user: { login: 'status-open-bounty' } } } } + { body: { action: 'created', comment: { body: 'Creating my first comment', user: { login: 'randomUser' } } } }, + { body: { action: 'edited', comment: { body: 'Editing my comment', user: { login: 'RandomUser' } } } }, + { body: { action: 'edited', comment: { body: sobComment, user: { login: 'status-open-bounty' } } } }, + { body: { action: 'created', issue: { labels: ['bounty', 'bounty-s'] }, comment: { body: sobComment, user: { login: 'status-open-bounty' } } } } ] describe('Bot behavior', function () { - describe('#needsFunding()', function () { - it('should return false because the comment is not from status-open-bounty', function () { - assert.isFalse(bot.needsFunding(requests[0])) - }) - it('should return false because a user is editing a comment', function () { - assert.isFalse(bot.needsFunding(requests[1])) - }) - it('should return false because status-open-bounty edited a comment', function () { - assert.isFalse(bot.needsFunding(requests[2])) - }) - it('should return true, it is all right and we should fund', function () { - assert.isTrue(bot.needsFunding(requests[3])) - }) + describe('#needsFunding()', function () { + it('should return false because the comment is not from status-open-bounty', function () { + assert.isFalse(bot.needsFunding(requests[0])) }) + it('should return false because a user is editing a comment', function () { + assert.isFalse(bot.needsFunding(requests[1])) + }) + it('should return false because status-open-bounty edited a comment', function () { + assert.isFalse(bot.needsFunding(requests[2])) + }) + it('should return true, it is all right and we should fund', function () { + assert.isTrue(bot.needsFunding(requests[3])) + }) + }) - describe('#getAddress', function () { - it('should return the address from a status-open-bounty bot comment', function () { - assert.equal(bot.getAddress(requests[3]), '0x3645fe42b1a744ad98cc032c22472388806f86f9') - }) + describe('#getAddress', function () { + it('should return the address from a status-open-bounty bot comment', function () { + assert.equal(bot.getAddress(requests[3]), '0x3645fe42b1a744ad98cc032c22472388806f86f9') }) + }) - describe('#getAmount', function () { - it('should return the amount for the issue given the price per hour and the bounty label for this issue', (done) => { - bot.getAmount(requests[3]) - .then(function (amount) { - const label = 'bounty-s' - const tokenPrice = 0.35 - const priceInDollars = config.priceHour * config.bountyLabels[label] - expected_amount = priceInDollars / tokenPrice - assert.equal(amount, expected_amount) - done() - }) - .catch(() => { console.log('error'), done() }) + describe('#getAmount', function () { + it('should return the amount for the issue given the price per hour and the bounty label for this issue', (done) => { + bot.getAmount(requests[3]) + .then(function (amount) { + const label = 'bounty-s' + const tokenPrice = 0.35 + const priceInDollars = config.priceHour * config.bountyLabels[label] + expected_amount = priceInDollars / tokenPrice + assert.equal(amount, expected_amount) + done() }) + .catch(() => { console.log('error'), done() }) }) + }) })