diff --git a/app.js b/app.js new file mode 100644 index 0000000..c38af58 --- /dev/null +++ b/app.js @@ -0,0 +1,37 @@ +var SlackBot = require('slackbots'); + +['SLACK_TOKEN', 'RPCUSER', 'RPCPASSWORD'].forEach(function(envVar) { + if (!process.env[envVar]) { + throw new Error(envVar + ' env var required'); + } +}); + +var slackbot = new SlackBot({ + token: process.env.SLACK_TOKEN, + name: 'wunderbot' +}); + + +var tipbot = require('./tipbot'); +tipbot.init(process.env.RPCUSER, process.env.RPCPASSWORD); + +var hashbot = require('./hashbot'); +hashbot.init(slackbot, process.env.MINING_CHANNEL); + + + +slackbot.on('start', function() { + slackbot.on('message', function(data) { + if (data.text) { + var command = data.text.trim().split(' ')[0]; + + if (command === hashbot.command) { + hashbot.respond(slackbot, data); + } + + if (command === tipbot.command) { + tipbot.respond(slackbot, data); + } + } + }); +}); diff --git a/bot.js b/bot.js deleted file mode 100644 index 7e5ae7b..0000000 --- a/bot.js +++ /dev/null @@ -1,57 +0,0 @@ -var SlackBot = require('slackbots'); -var needle = require('needle'); - -var SLACK_TOKEN = process.env.SLACK_TOKEN; -var CHANNEL = process.env.CHANNEL; - -if (!SLACK_TOKEN) { - throw new Error('SLACK_TOKEN env var required'); -} -if (!CHANNEL) { - throw new Error('CHANNEL env var required'); -} - - -var bot = new SlackBot({ - token: SLACK_TOKEN, - name: 'hashbot' -}); - -function numberWithCommas(x) { - return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); -} - -function getData() { - needle.get('https://explorer.lbry.io/api/getmininginfo', function(error, response) { - // if (!error && response.statusCode == 200) { - // console.log(response.body); - // } - - var data = response.body, - hashrate = Math.round(data.networkhashps / 1000000000), - difficulty = numberWithCommas(Math.round(data.difficulty)), - block = numberWithCommas(data.blocks); - - bot.postMessageToChannel(CHANNEL, - // 'Blockchain stats:\n' + - 'Hashrate: ' + hashrate + ' GH/s\n' + - 'Difficulty: ' + difficulty + '\n' + - 'Current block: ' + block + '\n' + - '_Source: https://explorer.lbry.io_' - ); - - }); -} - -bot.on('start', function() { - // more information about additional params https://api.slack.com/methods/chat.postMessage - bot.on('message', function(data) { - if (data.text && data.text.trim() === '!hash') { - getData(); - } - }); - - // Post every hour - setInterval(getData, 3600000); - getData(); -}); diff --git a/hashbot.js b/hashbot.js new file mode 100644 index 0000000..84d4c7f --- /dev/null +++ b/hashbot.js @@ -0,0 +1,64 @@ +var needle = require('needle'); + +var command = '!hash'; + +module.exports={ + command: command, + init: init, + respond: respond +}; + + +function init(slackbot, channel) { + if (channel) { + setInterval(function() { + sendMiningInfo(slackbot, channel); + }, 3600000); + sendMiningInfo(slackbot, channel); + } +} + + +function respond(slackbot, data) { + var words = data.text.trim().split(' '); + + if (words[0] !== command) { + // wtf? + return; + } + + if (words.length > 1) { + // e.g. "!hash and some other words" + return; + } + + sendMiningInfo(slackbot, data.channel); +} + + +function sendMiningInfo(slackbot, channel) { + needle.get('https://explorer.lbry.io/api/getmininginfo', function(error, response) { + if (error || response.statusCode !== 200) { + slackbot.postMessage(channel, 'Explorer API is not available'); + } + else { + var data = response.body, + hashrate = Math.round(data.networkhashps / 1000000000), + difficulty = numberWithCommas(Math.round(data.difficulty)), + block = numberWithCommas(data.blocks); + + slackbot.postMessage(channel, + // 'Blockchain stats:\n' + + 'Hashrate: ' + hashrate + ' GH/s\n' + + 'Difficulty: ' + difficulty + '\n' + + 'Current block: ' + block + '\n' + + '_Source: https://explorer.lbry.io_' + ); + } + }); +} + + +function numberWithCommas(x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} \ No newline at end of file diff --git a/package.json b/package.json index 28e53db..fb07e49 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A bot for slack wich displays lbrys current hashrate via the open api.", "main": "bot.js", "dependencies": { + "bitcoin": "^3.0.1", "needle": "^1.0.0", "slackbots": "^0.5.1" }, diff --git a/tipbot.js b/tipbot.js new file mode 100644 index 0000000..5b2ed1d --- /dev/null +++ b/tipbot.js @@ -0,0 +1,200 @@ +var lbry; + +var command = '!tip'; + +module.exports={ + command: command, + init: init, + respond: respond +}; + +function init(rpcuser, rpcpassword) { + if (lbry) { + throw new Error('init was already called once'); + } + + var bitcoin = require('bitcoin'); + lbry = new bitcoin.Client({ + host: 'localhost', + 'port': 9245, + 'user': rpcuser, + 'pass': rpcpassword + }); +} + +function respond(bot, data) { + var tipper = data.user, + channel = data.channel, + words = data.text.trim().split(' '); + + if (!lbry) { + bot.postMessage(channel, 'Failed to connect to lbrycrd'); + return; + } + + if (words[0] !== command) { + // wtf? + return; + } + + var subcommand = words.length >= 2 ? words[1] : 'help'; + + if (subcommand === 'help') { + doHelp(bot, channel); + } + else if (subcommand === 'balance') { + doBalance(bot, channel, tipper); + } + else if (subcommand === 'deposit') { + doDeposit(bot, channel, tipper); + } + else if (subcommand === 'withdraw') { + doWithdraw(bot, channel, tipper, words); + } + else { + doTip(bot, channel, tipper, words); + } +} + + + +function doBalance(bot, channel, tipper) { + lbry.getBalance(tipper, 1, function(err, balance) { + if (err) { + bot.postMessage(channel, 'Error getting balance'); + } + else { + bot.postMessage(channel, 'You have *' + balance + '* LBC'); + } + }); +} + + +function doDeposit(bot, channel, tipper) { + getAddress(tipper, function(err, address) { + if (err) { + bot.postMessage(channel, 'Error getting deposit address'); + } + else { + bot.postMessage(channel, 'Your address is ' + address); + } + }); +} + + +function doWithdraw(bot, channel, tipper, words) { + if (words.length < 4) { + doHelp(bot, channel); + return; + } + + var address = words[2], + amount = words[3]; + + if (!validateAmount(amount)) { + bot.postMessage(channel, 'I dont know how to withdraw that many credits'); + return; + } + + lbry.sendFrom(tipper, address, amount, function(err, txId) { + if (err) { + bot.postMessage(channel, err.message); + } + else { + bot.postMessage(channel, 'You withdrew ' + amount + ' to ' + address + ' (' + txLink(txId) + ')'); + } + }); +} + + +function doTip(bot, channel, tipper, words) { + if (words.length < 3) { + doHelp(bot, channel); + return; + } + + var user = words[1], + amount = words[2]; + + if (!validateAmount(amount)) { + bot.postMessage(channel, 'I dont know how to tip that many credits'); + return; + } + + if (user.match(/^<@U[^>]+>$/)) { + var id = user.substr(2,user.length-3); + sendLbc(bot, channel, tipper, id, amount); + } + else { + bot.getUser(user).then(function(data) { + if (data.id) { + sendLbc(bot, channel, tipper, data.id, amount); + } else + { + bot.postMessage(channel, 'Sorry, I dont know that person'); + } + }) + } +} + + +function doHelp(bot, channel) { + bot.postMessage(channel, + '`' + command + ' help`: this message\n' + + '`' + command + ' balance`: get your balance\n' + + '`' + command + ' deposit`: get address for deposits\n' + + '`' + command + ' withdraw ADDRESS AMOUNT`: withdraw AMOUNT credits to ADDRESS\n' + + '`' + command + ' USER AMOUNT`: send AMOUNT credits to USER\n' + ); +} + + +function sendLbc(bot, channel, tipper, id, amount) { + getAddress(id, function(err, address){ + if (err){ + bot.postMessage(channel, err.message); + } + else { + lbry.sendFrom(tipper, address, amount, 1, null, null, function(err, txId){ + if (err) { + bot.postMessage(channel, err.message); + } + else { + bot.postMessage(channel, 'Wubba lubba dub dub! <@' + tipper + '> tipped <@' + id + '> ' + amount + ' (' + txLink(txId) + ')'); + } + }); + } + }); +}; + + +function getAddress(userId, cb) { + lbry.getAddressesByAccount(userId, function(err, addresses) { + if (err) { + cb(err); + } + else if(addresses.length > 0) { + cb(null, addresses[0]); + } + else { + lbry.getNewAddress(userId, function(err, address) { + if (err) { + cb(err); + } + else { + cb(null, address); + } + }); + } + }); +} + + +function validateAmount(amount) { + return amount.match(/^[0-9]+(\.[0-9]+)?$/); +} + + +function txLink(txId) { + return ""; +} \ No newline at end of file