Merge pull request #9 from lbryio/next-tipbot

Latest version of tipbot, rewritten and solving most of the issues.
This commit is contained in:
filipnyquist 2018-07-06 15:52:08 +02:00 committed by GitHub
commit 05dc97bda8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1135 additions and 1744 deletions

93
.gitignore vendored
View file

@ -1,21 +1,78 @@
# Created by http://www.gitignore.io # Logs
### Node ###
lib-cov
lcov.info
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs logs
results *.log
build npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
.idea/
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt .grunt
node_modules # Bower dependency directory (https://bower.io/)
config/config.yml bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# Configurations
default.json
.idea/*

View file

@ -1 +0,0 @@
/lib/

27
LICENSE
View file

@ -1,20 +1,15 @@
Copyright (c) 2014 unek <unekpl@gmail.com> The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining Copyright (c) 2018 LBRY Inc
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
included in all copies or substantial portions of the Software. "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

102
README.md
View file

@ -1,74 +1,46 @@
This is a fork of node-tip-bot-twitter, an open-source node.js twitter bot for tipping with altcoins. # Twitter tipbot - A twitter tipbot for LBRY
In particular, this fork is for [LBC](https://lbry.io/). LBRY.io, the network behind it, describes
itself as:
This repo contains the twitter tipbot used by LBRY. This bot allows users to tip each other LBC on twitter.
## Installation
### Prerequisites
* Lbrycrd-daemon
* Node.js v8+
* Yarn
* A twitter application on the tipbot account
>To get started you should clone the git:
``` ```
...a free, open, and community-run digital marketplace. git clone https://github.com/lbryio/twitter-tipbot
You own your data. You control the network. Indeed, you are the network.
Hollywood films, college lessons, amazing streamers and more are on the first media network ruled by you.
``` ```
>Install all modules with yarn:
If you want to get started with LBRY.io, go to https://lbry.io/get. If you wish to set up your own twitter
tip bot, it may be more beneficial to start with the [original node-tip-bot-twitter](https://github.com/gameunits/node-tip-bot-twitter).
# Installation
To install node-tip-bot simply clone this repo and install dependencies:
```bash
git clone https://github.com/gameunits/node-tip-bot-twitter
cd node-tip-bot-twitter
npm install
``` ```
After installation proceed to the configuration. yarn install
# Configuration
To configure, copy the `config/config.sample.yml` file to `config/config.yml`.
## twitter
Create an application at dev.twitter.com and fill in your keys.
* **consumer_key: ''
* **consumer_secret: ''
* **access_token_key: ''
* **access_token_secret: ''
## log
Logging settings.
* **file** - file to log to. Set to `false` to disable logging to file.
## rpc
JSON RPC API connection info.
* **host** - JSON RPC API hostname
* **port** - API port (by default 1337)
* **user** - API username
* **pass** - API password (keep that secure)
## coin
Basic coin settings.
* **withdrawal_fee** - fee charged on withdraw to cover up txfee, the rest goes to bot's wallet.
* **min_withdraw** - minimum amount of coins to withdraw
* **min_confirmations** - minimum amount of confirmations needed to tip/withdraw coins
* **min_tip** - minimum amount of coins to tip
# How to run it?
Before running the bot, you have to be running your coin daemon with JSON-RPC API enabled. To enable, add this to your coin daemon configuration file (eg. `~/.gameunits/gameunits.conf`):
```ini
server=1
daemon=1
rpcuser=<your username>
rpcpassword=<your super secret password>
rpcallowip=<your bot's ip address or just 127.0.0.1 if hosted on the same machine>
``` ```
To run the bot simply use `node bin/tipbot` or `npm start`. >Rename default.example.json to default.json and enter the twitter tokens and daemon settings.
## Commands >Run the bot with:
Commands are executed by placing gameunits <command> <arguments> in a tweet. ```
node index.js
```
>If you want to move over accounts from the old tipbot format which used usernames as identifier, run move_helper.js:
```
node move_helper.js
```
>It will automatically move over the old accounts to the new id based system.
| **Command** | **Arguments** | **Description** ## Contributing
|-------------|-------------------|--------------------------------------------------------------------
| `balance` | | displays your current wallet balance
| `address` | | displays address where you can send your funds to the tip bot
| `withdraw` | `<address>` | withdraws your whole wallet balance to specified address
| `tip` | `<nick> <amount>` | sends the specified amount of coins to the specified nickname
| `help` | | displays configured help message (by default similiar to this one)
| `terms` | | displays terms and conditions for using the tip bot
Contributions to this project are welcome, encouraged, and compensated. For more details, see [lbry.io/faq/contributing](https://lbry.io/faq/contributing)
## License
This project is MIT Licensed &copy; [LBRYio](https://github.com/lbryio)
## Security
We take security seriously. Please contact security@lbry.io regarding any security issues.
Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it.
## Contact
The primary contact for this project is [@filipnyquist](https://github.com/filipnyquist) (filip@lbry.io)

View file

@ -1,325 +0,0 @@
const winston = require('winston'),
fs = require('fs'),
yaml = require('js-yaml'),
coind = require('node-gameunits');
const Twitter = require('twitter');
// check if the config file exists
if (!fs.existsSync('./config/config.yml')) {
winston.error("Configuration file doesn't exist! Please read the README.md file first.");
process.exit(1);
}
// load settings
const settings = yaml.load(fs.readFileSync('./config/config.yml', 'utf-8'));
// load winston's cli defaults
winston.cli();
// write logs to file
if (settings.log.file) {
winston.add(winston.transports.File, {
filename: settings.log.file,
level: 'debug'
});
}
// connect to coin json-rpc
winston.info('Connecting to coind...');
let coin = coind({
host: settings.rpc.host,
port: settings.rpc.port,
user: settings.rpc.user,
pass: settings.rpc.pass
});
// checking if we are connected.
coin.getBalance(function (err, balance) {
if (err) {
winston.error('Could not connect to %s RPC API! ', settings.coin.full_name, err);
process.exit(1);
return;
}
var balance = typeof balance == 'object' ? balance.result : balance;
winston.info('Connected to JSON RPC API. Current total balance is %d' + settings.coin.short_name, balance);
});
// connect to twitter
winston.info('Connecting to Twitter');
var client = new Twitter({
consumer_key: settings.twitter.consumer_key,
consumer_secret: settings.twitter.consumer_secret,
access_token_key: settings.twitter.access_token_key,
access_token_secret: settings.twitter.access_token_secret
});
//
client.get('followers/ids', function (error, tweets, response) {
if (error) {
console.log(error);
}
winston.info('Connected to Twitter. Followers:' + tweets.ids.length);
});
// basic handlers
var locks = [];
function makeid() {
var text = '';
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
function replytweet(to, replyid, themessage) {
winston.info('Preparing tweet' + '@' + to + ' :' + themessage);
var newtweet = '@' + to + ' ' + themessage + ' \nMsg_ID:(' + makeid() + ')';
winston.info('' + '@' + to + ' :' + newtweet);
client.post('statuses/update', {
status: newtweet,
in_reply_to_status_id: replyid
}, function (error, params, response) {
if (error) {
console.log(error);
//throw error;
}
console.log('Tweeting');
});
}
function getAddress(nickname, callback) {
winston.debug('Requesting address for %s', nickname);
coin.send('getaccountaddress', settings.rpc.prefix + nickname.toLowerCase(), function (err, address) {
if (err) {
winston.error('Something went wrong while getting address. ' + err);
callback(err);
return false;
}
callback(false, address);
});
}
String.prototype.expand = function (values) {
var global = {
nick: settings.twitter.twittername
};
return this.replace(/%([a-zA-Z_]+)%/g, function (str, variable) {
return typeof values[variable] == 'undefined' ? (typeof settings.coin[variable] == 'undefined' ? (typeof global[variable] == 'undefined' ? str : global[variable]) : settings.coin[variable]) : values[variable];
});
};
client.stream('statuses/filter', {track: settings.twitter.twitterkeyword}, function (stream) {
stream.on('error', function (error) {
winston.error('Something went wrong with the twitter streaming api.');
winston.error(error);
});
stream.on('end', function (reason) {
winston.error('Twitter streaming api failed with');
winston.error(reason);
});
stream.on('data', function (tweet) {
console.log('@' + tweet.user.screen_name + '|' + tweet.text);
if (tweet.text.substring(0, 2) === 'RT') {
console.log('Retweet Ingrored');
return;
}
var regex = new RegExp('(' + settings.twitter.twitterkeyword + ')(\\s)([a-zA-Z]+)', 'i');
var match = tweet.text.match(regex);
if (match == null) return;
var command = match[3];
var from = tweet.user.screen_name;
var msg = tweet.txt;
var message = tweet.text;
var replyid = tweet.id_str;
if (command == 'help' || command == 'terms') {
for (var i = 0; i < settings.messages[command].length; i++) {
replytweet(from, replyid, settings.messages[command][i].expand({}));
}
return;
}
switch (command) {
case 'tip':
var regex = new RegExp('(' + settings.twitter.twitterkeyword + ')(\\s)([a-zA-Z]+)(\\s)(\\@)(.+)(\\s)(.+)', 'i'); //Uglyfix
var match = tweet.text.match(regex);
console.log('tip');
console.log(match[0] + ',' + match[1] + ',' + match[2] + ',' + match[3] + ',' + match[4] + ',' + match[5] + ',' + match[6] + ',' + match[7] + ',' + match[8]);
if (match == null || match.length < 3) {
replytweet(from, replyid, 'Usage: nameofbot tip <twitterhandle> <amount>');
return;
}
var to = match[6];
var amount = Number(match[8]);
console.log('To:' + amount);
// lock
if (locks.hasOwnProperty(from.toLowerCase()) && locks[from.toLowerCase()]) return;
locks[from.toLowerCase()] = true;
if (isNaN(amount)) {
locks[from.toLowerCase()] = null;
replytweet(from, replyid, settings.messages.invalid_amount.expand({name: from, amount: match[8]}));
return;
}
if (to.toLowerCase() == from.toLowerCase()) {
locks[from.toLowerCase()] = null;
replytweet(from, replyid, settings.messages.tip_self.expand({name: from}));
return;
}
if (amount < settings.coin.min_tip) {
locks[from.toLowerCase()] = null;
replytweet(from, replyid, settings.messages.tip_too_small.expand({
from: from,
to: to,
amount: amount
}));
return;
}
// check balance with min. 5 confirmations
coin.getBalance(settings.rpc.prefix + from.toLowerCase(), settings.coin.min_confirmations, function (err, balance) {
if (err) {
locks[from.toLowerCase()] = null;
winston.error('Error in tip command.', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
var balance = typeof balance == 'object' ? balance.result : balance;
if (balance >= amount) {
coin.send('move', settings.rpc.prefix + from.toLowerCase(), settings.rpc.prefix + to.toLowerCase(), amount, function (err, reply) {
locks[from.toLowerCase()] = null;
if (err || !reply) {
winston.error('Error in tip command', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
winston.info('%s tipped %s %d%s', from, to, amount, settings.coin.short_name);
replytweet(from, replyid, settings.messages.tipped.expand({
from: from,
to: to,
amount: amount
}));
});
} else {
locks[from.toLowerCase()] = null;
winston.info('%s tried to tip %s %d, but has only %d', from, to, amount, balance);
replytweet(from, replyid, settings.messages.no_funds.expand({
name: from,
balance: balance,
short: amount - balance,
amount: amount
}));
}
});
break;
case 'deposit':
case 'address':
console.log('adress');
var user = from.toLowerCase();
getAddress(user, function (err, address) {
if (err) {
winston.error('Error in address command', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
replytweet(from, replyid, settings.messages.deposit_address.expand({name: user, address: address}));
});
break;
case 'balance':
console.log('balance');
var user = from.toLowerCase();
coin.getBalance(settings.rpc.prefix + user, settings.coin.min_confirmations, function (err, balance) {
if (err) {
winston.error('Error in balance command', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
var balance = typeof balance == 'object' ? balance.result : balance;
coin.getBalance(settings.rpc.prefix + user, 0, function (err, unconfirmed_balance) {
if (err) {
winston.error('Error in balance command', err);
replytweet(from, replyid, settings.messages.balance.expand({balance: balance, name: user}));
return;
}
var unconfirmed_balance = typeof unconfirmed_balance == 'object' ? unconfirmed_balance.result : unconfirmed_balance;
replytweet(from, replyid, settings.messages.balance_unconfirmed.expand({
balance: balance,
name: user,
unconfirmed: unconfirmed_balance - balance
}));
});
});
break;
case 'withdraw':
console.log('withdrawl');
var user = from.toLowerCase();
var match = message.match(/.?withdraw (\S+)$/);
if (match == null) {
replytweet(from, replyid, 'Usage: withdraw <' + settings.coin.full_name + ' address>');
return;
}
var address = match[1];
coin.validateAddress(address, function (err, reply) {
if (err) {
winston.error('Error in withdraw command', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
if (reply.isvalid) {
coin.getBalance(settings.rpc.prefix + from.toLowerCase(), settings.coin.min_confirmations, function (err, balance) {
if (err) {
winston.error('Error in withdraw command', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
var balance = typeof balance == 'object' ? balance.result : balance;
if (balance < settings.coin.min_withdraw) {
winston.warn('%s tried to withdraw %d, but min is set to %d', from, balance, settings.coin.min_withdraw);
replytweet(from, replyid, settings.messages.withdraw_too_small.expand({
name: from,
balance: balance
}));
return;
}
coin.sendFrom(settings.rpc.prefix + from.toLowerCase(), address, balance - settings.coin.withdrawal_fee, function (err, reply) {
if (err) {
winston.error('Error in withdraw command', err);
replytweet(from, replyid, settings.messages.error.expand({name: from}));
return;
}
var values = {
name: from,
address: address,
balance: balance,
amount: balance - settings.coin.withdrawal_fee,
transaction: reply
};
for (var i = 0; i < settings.messages.withdraw_success.length; i++) {
var msg = settings.messages.withdraw_success[i];
replytweet(from, replyid, msg.expand(values));
}
// transfer the rest (withdrawal fee - txfee) to bots wallet
coin.getBalance(settings.rpc.prefix + from.toLowerCase(), function (err, balance) {
if (err) {
winston.error('Something went wrong while transferring fees', err);
return;
}
var balance = typeof balance == 'object' ? balance.result : balance;
// moves the rest to bot's wallet
coin.move(settings.rpc.prefix + from.toLowerCase(), settings.rpc.prefix + settings.twitter.twittername.toLowerCase(), balance);
});
});
});
} else {
winston.warn('%s tried to withdraw to an invalid address', from);
replytweet(from, replyid, settings.messages.invalid_address.expand({
address: address,
name: from
}));
}
});
break;
default:
winston.warn('Invalid Command' + command);
replytweet(from, replyid, 'Invalid command. Tweet "@LBC_TipBot lbryian help" for list of commands or see reply below');
break;
}
});
});

View file

@ -1,80 +0,0 @@
twitter:
consumer_key: ''
consumer_secret: ''
access_token_key: ''
access_token_secret: ''
twittername: ''
twitterkeyword: 'gameunitsbot'
log:
file: tipbot.log
rpc:
host: localhost
port: 1337
user: gameunitsrpc
pass: pass
prefix: ''
coin:
withdrawal_fee: 1
min_withdraw: 1
min_confirmations: 10
min_tip: 0.1
min_rain: 1
short_name: ' UNITS'
full_name: Gameunits
webadmin:
enabled: false
port: 8080
users:
unek: supersecretpassword
admintwo: password
commands:
help:
pm: true
channel: false
tip:
pm: true
channel: true
balance:
pm: true
channel: false
withdraw:
pm: true
channel: false
address:
pm: true
channel: false
terms:
pm: true
channel: false
rain:
pm: false
channel: true
rain_on_last_active: 0 # amount in seconds. rain tips will fall only on users active within x seconds. leave 0 for no such behavior.
messages:
error: 'Sorry %name%, something went wrong.'
no_funds: "Sorry %name%, you don't have enough funds (you're %short%%short_name% short)"
not_identified: '%name%: You need to be identified with NickServ to tip.'
tipped: ' Tipped @%to% %amount%%short_name%! "tweet @%nick% gameunits help" to claim.'
balance: '%name% has %balance%%short_name%.'
balance_unconfirmed: '%name% has %balance%%short_name% (unconfirmed: %unconfirmed%%short_name%)'
deposit_address: Your deposit address %address%
withdraw_too_small: 'Sorry %name%, you need to withdraw at least %min_withdraw%%short_name% (you have %balance%%short_name%)'
invalid_address: 'Sorry %name%, the address you specified is invalid (%address%).'
tip_too_small: 'Sorry %from%, your tip to %to% (%amount%%short_name%) is too small (min. %min_tip%%short_name%).'
tip_self: "Sorry %name%, you can't tip yourself!"
invalid_amount: 'Sorry %name%, "%amount%" is not a correct amount.'
withdraw_success:
- '%name%: %amount%%short_name% has been withdrawn from your account to %address%'
- 'You have been charged %withdrawal_fee%%short_name% withdrawal fee.'
- 'Transaction %transaction% completed.'
help:
- 'You can use following commands:'
- 'balance - displays your current wallet balance'
- 'address - displays %full_name% your depositaddress'
- 'withdraw <%full_name% address> - withdraws your whole wallet balance to specified address (minus %withdrawal_fee%%short_name% withdrawal fee)'
- 'tip <nick> <amount> - sends the specified amount of %full_name% to nickname'
- 'terms - displays terms and conditions for using %nick%'
- 'payout - How can I withdraw my Gameunits from tipbot ? Just Download Gameunits client from : http://gameunits.org/#downloads'
terms:
- 'There are no fees to use %nick%, with the exception of %withdrawal_fee%%short_name% fee on withdrawals.'
- 'In no event shall %nick% be responsible in the event of lost, stolen or misdirected funds.'

View file

@ -0,0 +1,17 @@
{
"bot":{
"handle": "@devlbctipbot",
"requiredConfirms": 2
},
"twitter": {
"consumer_key": "****",
"consumer_secret": "****",
"access_token": "****",
"access_token_secret": "****"
},
"lbrycrd": {
"username": "lbry",
"password": "lbry",
"port": 9245
}
}

235
index.js Normal file
View file

@ -0,0 +1,235 @@
const Twit = require("twit");
const config = require("config");
const winston = require("winston");
require("winston-daily-rotate-file");
const Client = require("bitcoin-core");
const lbry = new Client({
username: config.get("lbrycrd.username"),
password: config.get("lbrycrd.password"),
port: config.get("lbrycrd.port")
});
const logger = winston.createLogger({
level: "info",
format: winston.format.json(),
transports: [
new winston.transports.DailyRotateFile({
filename: "tipbot-%DATE%.log",
dirname: "./logs",
datePattern: "YYYY-MM-DD-HH",
zippedArchive: true,
maxSize: "20m",
maxFiles: "14d"
}),
new winston.transports.Console({
format: winston.format.simple(),
level: "debug"
})
]
});
const T = new Twit({
consumer_key: config.get("twitter.consumer_key"),
consumer_secret: config.get("twitter.consumer_secret"),
access_token: config.get("twitter.access_token"),
access_token_secret: config.get("twitter.access_token_secret"),
timeout_ms: 60 * 1000, // optional HTTP request timeout to apply to all requests.
strictSSL: true // optional - requires SSL certificates to be valid.
});
const stream = T.stream("statuses/filter", { track: config.get("bot.handle") });
logger.info("Started LBRY twitter tipbot.");
stream.on("tweet", function(tweet) {
if(tweet.user.screen_name === config.get("bot.handle").substring(1)) return;
let msg = checkTrunc(tweet);
msg = msg.slice(msg.indexOf(config.get("bot.handle"))).split(" ");
if (msg.length >= 2) checkTweet(tweet, msg);
});
function checkTweet(tweet, msg) {
switch (msg[1]) {
case "help":
doHelp(tweet, msg);
break;
case "balance":
doBalance(tweet, msg);
break;
case "deposit":
doDeposit(tweet, msg);
break;
case "withdraw":
doWithdraw(tweet, msg);
break;
case "tip":
doTip(tweet, msg);
break;
case "terms":
doTerms(tweet, msg);
break;
case "lbryian":
logger.info("Got a command with the old format, handling it...");
checkTweet(tweet, msg.splice(1));
break;
}
}
async function doHelp(tweet, msg) {
try {
let post = await T.post("statuses/update", {
status:
`@${tweet.user.screen_name} `+
`All commands should be called with ${config.get("bot.handle")} + subcommand \n` +
"help - Shows this command. \n" +
"balance - Get your balance. \n" +
"deposit - Get address for your deposits. \n" +
"withdraw ADDRESS AMOUNT - Withdraw AMOUNT credits to ADDRESS. \n" +
"tip USER AMOUNT - Tip USER AMOUNT.\n"+
"terms - Sends you the TOS.",
in_reply_to_status_id: tweet.id_str
});
logger.info(
`Sent help to ${tweet.user.screen_name}, tweet id: ${tweet.id_str}`
);
} catch (e) {
logger.error(e);
}
}
async function doTerms(tweet, msg){
// ADD terms
await T.post("statuses/update", {
status:
`@${tweet.user.screen_name} `+
"There are no fees to use this bot except the automatic daemon fee. \n"+
"Under no circumstances shall LBRY Inc. be held responsible for lost, stolen or misdirected funds.",
in_reply_to_status_id: tweet.id_str
});
}
async function doBalance(tweet, msg) {
try {
const balance = await lbry.getBalance(id(tweet.user.id_str), config.get("bot.requiredConfirms")); // Amount of confirms before we can use it.
const post = await T.post("statuses/update", {
in_reply_to_status_id: tweet.id_str,
status: `@${tweet.user.screen_name} You have ${balance} LBC.`
});
logger.info(
`Sent balance command to ${tweet.user.screen_name}, tweet id: ${
tweet.id_str
}`
);
} catch (e) {
logger.error(e);
}
}
async function doDeposit(tweet, msg) {
try {
const post = await T.post("statuses/update", {
status: `@${tweet.user.screen_name} Your deposit address is ${await getAddress(id(tweet.user.id_str))}.`,
in_reply_to_status_id: tweet.id_str
});
logger.info(
`Sent deposit address to ${tweet.user.screen_name}, tweet id: ${
tweet.id_str
}`
);
} catch (e) {
logger.error(e);
}
}
async function doWithdraw(tweet, msg) {
try {
if (msg.length < 4) return doHelp(tweet, msg);
let address = msg[2];
let amount = getValidatedAmount(msg[3]);
if (amount === null) {
return await T.post("statuses/update", {
status: `@${tweet.user.screen_name} I don´t know how to withdraw that many credits...`,
in_reply_to_status_id: tweet.id_str
});
}
let txId = await lbry.sendFrom(id(tweet.user.id_str), address, amount);
await T.post("statuses/update", {
status: `@${tweet.user.screen_name} You withdrew ${amount} LBC to ${address}. \n${txLink(txId)}`,
in_reply_to_status_id: tweet.id_str
});
logger.info(
`User ${
tweet.user.screen_name
} withdraw ${amount} LBC to ${address}, tweet id: ${tweet.id_str}`
);
} catch (e) {
logger.error(e);
}
}
async function doTip(tweet, msg) {
try {
if (msg.length < 3) {
return doHelp(tweet, msg);
}
const amount = getValidatedAmount(msg[3]);
if (amount === null) {
return await T.post("statuses/update", {
status: `@${tweet.user.screen_name} I don´t know how to tip that many credits...`,
in_reply_to_status_id: tweet.id_str
});
}
const userToTip = tweet.entities.user_mentions.find(u => `@${u.screen_name}` === msg[2]).id_str;
let tipToAddress = await getAddress(id(userToTip)) // Call this to ensure user has an account.
if (userToTip === null) {
return await T.post("statuses/update", {
status: `@${tweet.user.screen_name} I could not find that user...`,
in_reply_to_status_id: tweet.id_str
});
}
const balanceFromUser = await lbry.getBalance(id(tweet.user.id_str), config.get("bot.requiredConfirms"));
if (balanceFromUser < amount) {
return await T.post("statuses/update", {
status: `@${tweet.user.screen_name} You tried tipping more than you have! You are ${amount-balanceFromUser} LBC short.`,
in_reply_to_status_id: tweet.id_str
});
}
const txId = await lbry.sendFrom(id(tweet.user.id_str), tipToAddress, Number(amount), 1);
await T.post("statuses/update", {
status: `@${tweet.user.screen_name} Tipped ${
msg[2]
} ${amount} LBC! \nTransaction: ${txLink(txId)} \nSee https://lbry.io/faq/tipbot-twitter for more information.`,
in_reply_to_status_id: tweet.id_str
});
logger.info(
`@${tweet.user.screen_name}(${tweet.user.id_str}) tipped ${
msg[2]
}(${userToTip}) ${amount} LBC.`
);
} catch (e) {
logger.error(e);
}
}
async function getAddress(userId) {
try {
let uAddresses = await lbry.getAddressesByAccount(userId);
if (uAddresses.length > 0) return uAddresses[0];
let nAddress = await lbry.getNewAddress(userId);
return nAddress;
} catch (e) {
logger.error(e);
}
}
function getValidatedAmount(amount) {
amount = amount.trim();
if (amount.toLowerCase().endsWith("lbc")) {
amount = amount.substring(0, amount.length - 3);
}
return amount.match(/^[0-9]+(\.[0-9]+)?$/) ? amount : null;
}
function txLink(txId) {
return `https://explorer.lbry.io/tx/${txId}`;
}
function checkTrunc(tweet) {
if (tweet.truncated) return tweet.extended_tweet.full_text;
return tweet.text;
}
function id(usrId){
return `t-${usrId}`;
}

96
move_helper.js Normal file
View file

@ -0,0 +1,96 @@
// This file helps with moving over accounts from the old username system to the id system.
// It uses the same configuration files as index.js
// Checks for the old format, gets their id from twitter, creates new acc, moves balance.
const Twit = require("twit");
const config = require("config");
const winston = require("winston");
require("winston-daily-rotate-file");
const Client = require("bitcoin-core");
const lbry = new Client({
version: "0.12.0",
username: config.get("lbrycrd.username"),
password: config.get("lbrycrd.password"),
port: config.get("lbrycrd.port")
});
const logger = winston.createLogger({
level: "info",
format: winston.format.json(),
transports: [
new winston.transports.DailyRotateFile({
filename: "move-helper-%DATE%.log",
dirname: "./logs",
datePattern: "YYYY-MM-DD-HH",
zippedArchive: true,
maxSize: "20m",
maxFiles: "14d"
}),
new winston.transports.Console({
format: winston.format.simple(),
level: "debug"
})
]
});
let notSynced = [];
const T = new Twit({
consumer_key: config.get("twitter.consumer_key"),
consumer_secret: config.get("twitter.consumer_secret"),
access_token: config.get("twitter.access_token"),
access_token_secret: config.get("twitter.access_token_secret"),
timeout_ms: 60 * 1000, // optional HTTP request timeout to apply to all requests.
strictSSL: true // optional - requires SSL certificates to be valid.
});
async function main(){
let accs = await getAccounts();
logger.info(`Trying to move ${accs.length} accounts...`)
for (let i in accs){
try {
//Get user details from twitter.
let data = await T.get('users/show', { screen_name: accs[i] });
//Create a account for the user by id.
let usr = data.data.id_str;
await getAddress(id(usr));
//Move over from old account to the new account
const balanceFromOld = await lbry.getBalance(`twttr-${accs[i]}`);
if (balanceFromOld !== 0) {
let res = await lbry.move(
`twttr-${accs[i]}`,
id(usr),
Number(balanceFromOld)
);
// If move is successful, log it!
if (res) logger.info(`Transferred ${balanceFromOld} LBC from twttr-${accs[i]} to ${id(usr)}!`);
}
}catch(e){
logger.info(`Could not sync ${accs[i]}, error occured:`, e.allErrors);
notSynced.push({ user: accs[i], error: e.allErrors});
logger.info("Could not sync these:"+JSON.stringify(notSynced));
}
}
}
// Get a list of all twitter accounts on lbrycrd.
async function getAccounts(){
let accs = await lbry.listAccounts();
accs = Object.entries(accs);
let accsArr = [];
for (let i in accs){
if(accs[i][0].startsWith('twttr-')) accsArr.push(accs[i][0].substring(6));
}
return accsArr;
}
async function getAddress(userId) {
try {
let uAddresses = await lbry.getAddressesByAccount(userId);
if (uAddresses.length > 0) return;
await lbry.getNewAddress(userId);
} catch (e) {
throw("Something went wrong while creating an account for the user: ", e);
}
}
function id(usrId){
return `t-${usrId}`;
}
main();

1198
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,45 +1,17 @@
{ {
"name": "node-tip-bot-twitter",
"version": "1.0.2",
"description": "node.js based tipping bot for any coin",
"main": "bin/tipbot",
"dependencies": { "dependencies": {
"express": "^3.21.2", "bitcoin-core": "^2.0.0",
"js-yaml": "^3.11.0", "config": "^1.30.0",
"node-gameunits": "^0.3.5", "twit": "^2.2.11",
"twitter": "^1.7.1", "winston": "3",
"winston": "^2.4.2" "winston-daily-rotate-file": "^3.2.3"
}, },
"devDependencies": { "name": "lbry-twitter-tipbot",
"prettier": "^1.12.0" "version": "1.0.0",
}, "description": "A twitter tipbot for LBRY",
"scripts": { "main": "index.js",
"test": "echo \"Error: no test specified\" && exit 1", "repository": "https://github.com/lbryio/twitter-tipbot/",
"start": "node bin/tipbot", "author": "filipnyquist <filip@lbry.io>",
"prettier": "prettier --write \"{bot,.}/**/*.{js,json}\" --single-quote --print-width 240",
"build": "babel bot -d dist",
"prod": "babel bot -d dist & node dist/bot.js",
"lint": "prettier --write \"{bot,.}/**/*.{js,json}\" --single-quote --print-width 240",
"precommit": "prettier --write \"{bot,.}/**/*.{js,json}\" --single-quote --print-width 240"
},
"repository": {
"type": "git",
"url": "http://github.com/gameunits/node-tip-bot"
},
"author": "gameunits",
"license": "MIT", "license": "MIT",
"bugs": { "private": false
"url": "https://github.com/gameunits/node-tip-bot/issues"
},
"homepage": "https://github.com/gameunits/node-tip-bot",
"keywords": [
"gameunits",
"bitcoin",
"litecoin",
"altcoin",
"tip",
"tipbot",
"irc",
"bot"
]
} }

651
yarn.lock Normal file
View file

@ -0,0 +1,651 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@uphold/request-logger@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@uphold/request-logger/-/request-logger-2.0.0.tgz#c585c0bdb94210198945c6597e4fe23d6e63e084"
dependencies:
uuid "^3.0.1"
ajv@^5.1.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
asn1@~0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
lodash "^4.17.10"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
aws4@^1.6.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
dependencies:
tweetnacl "^0.14.3"
bignumber.js@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1"
bitcoin-core@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bitcoin-core/-/bitcoin-core-2.0.0.tgz#defcd4b3bfb6738ea88051b5fe5a944e32c09df0"
dependencies:
"@uphold/request-logger" "^2.0.0"
bluebird "^3.4.1"
debugnyan "^1.0.0"
json-bigint "^0.2.0"
lodash "^4.0.0"
request "^2.53.0"
semver "^5.1.0"
standard-error "^1.1.0"
bluebird@^3.1.5, bluebird@^3.4.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
bunyan@^1.8.1:
version "1.8.12"
resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797"
optionalDependencies:
dtrace-provider "~0.8"
moment "^2.10.6"
mv "~2"
safe-json-stringify "~1"
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
color-convert@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
color-name@^1.0.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
color-string@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
dependencies:
color-name "^1.0.0"
color@0.8.x:
version "0.8.0"
resolved "https://registry.yarnpkg.com/color/-/color-0.8.0.tgz#890c07c3fd4e649537638911cf691e5458b6fca5"
dependencies:
color-convert "^0.5.0"
color-string "^0.3.0"
colornames@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/colornames/-/colornames-0.0.2.tgz#d811fd6c84f59029499a8ac4436202935b92be31"
colors@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.0.tgz#5f20c9fef6945cb1134260aab33bfbdc8295e04e"
colorspace@1.0.x:
version "1.0.1"
resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.0.1.tgz#c99c796ed31128b9876a52e1ee5ee03a4a719749"
dependencies:
color "0.8.x"
text-hex "0.0.x"
combined-stream@1.0.6, combined-stream@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
delayed-stream "~1.0.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
config@^1.30.0:
version "1.30.0"
resolved "https://registry.yarnpkg.com/config/-/config-1.30.0.tgz#1d60a9f35348a13c175798d384e81a5a16c3ba6e"
dependencies:
json5 "0.4.0"
os-homedir "1.0.2"
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
cycle@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
dependencies:
assert-plus "^1.0.0"
debug@^2.2.0:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
debugnyan@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/debugnyan/-/debugnyan-1.0.0.tgz#90386d5ebc2c63588f17f272be5c2a93b7665d83"
dependencies:
bunyan "^1.8.1"
debug "^2.2.0"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
diagnostics@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.0.tgz#e1090900b49523e8527be20f081275205f2ae36a"
dependencies:
colorspace "1.0.x"
enabled "1.0.x"
kuler "0.0.x"
dtrace-provider@~0.8:
version "0.8.7"
resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.7.tgz#dc939b4d3e0620cfe0c1cd803d0d2d7ed04ffd04"
dependencies:
nan "^2.10.0"
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
dependencies:
jsbn "~0.1.0"
enabled@1.0.x:
version "1.0.2"
resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93"
dependencies:
env-variable "0.0.x"
env-variable@0.0.x:
version "0.0.4"
resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.4.tgz#0d6280cf507d84242befe35a512b5ae4be77c54e"
extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
fast-safe-stringify@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.4.tgz#4fe828718aa61dbcf9119c3c24e79cc4dea973b2"
fecha@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd"
file-stream-rotator@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.2.1.tgz#0d6fea1a9a7aba25a87cfd31b6e269e44e8f0af2"
dependencies:
moment "^2.11.2"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
form-data@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
asynckit "^0.4.0"
combined-stream "1.0.6"
mime-types "^2.1.12"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
dependencies:
assert-plus "^1.0.0"
glob@^6.0.1:
version "6.0.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
dependencies:
inflight "^1.0.4"
inherits "2"
minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
har-validator@~5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
dependencies:
ajv "^5.1.0"
har-schema "^2.0.0"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
json-bigint@^0.2.0:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-0.2.3.tgz#118d7f6ff1d38659f19f94cf73e64a75a3f988a8"
dependencies:
bignumber.js "^4.0.0"
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
json5@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
kuler@0.0.x:
version "0.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-0.0.0.tgz#b66bb46b934e550f59d818848e0abba4f7f5553c"
dependencies:
colornames "0.0.2"
lodash@^4.0.0, lodash@^4.17.10:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
logform@^1.6.0, logform@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/logform/-/logform-1.9.1.tgz#58b29d7b11c332456d7a217e17b48a13ad69d60a"
dependencies:
colors "^1.2.1"
fast-safe-stringify "^2.0.4"
fecha "^2.3.3"
ms "^2.1.1"
triple-beam "^1.2.0"
mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
mime-types@^2.1.12, mime-types@~2.1.17:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
dependencies:
mime-db "~1.33.0"
mime@^1.3.4:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
"minimatch@2 || 3":
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
moment@^2.10.6, moment@^2.11.2:
version "2.22.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
ms@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
mv@~2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2"
dependencies:
mkdirp "~0.5.1"
ncp "~2.0.0"
rimraf "~2.4.0"
nan@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
ncp@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
one-time@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e"
os-homedir@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
qs@~6.5.1:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
readable-stream@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
request@^2.53.0, request@^2.68.0:
version "2.87.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
tough-cookie "~2.3.3"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
rimraf@~2.4.0:
version "2.4.5"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da"
dependencies:
glob "^6.0.1"
safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
safe-json-stringify@~1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd"
safer-buffer@^2.0.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
semver@^5.1.0, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
sshpk@^1.7.0:
version "1.14.2"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
dashdash "^1.12.0"
getpass "^0.1.1"
safer-buffer "^2.0.2"
optionalDependencies:
bcrypt-pbkdf "^1.0.0"
ecc-jsbn "~0.1.1"
jsbn "~0.1.0"
tweetnacl "~0.14.0"
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
standard-error@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/standard-error/-/standard-error-1.1.0.tgz#23e5168fa1c0820189e5812701a79058510d0d34"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
dependencies:
safe-buffer "~5.1.0"
text-hex@0.0.x:
version "0.0.0"
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-0.0.0.tgz#578fbc85a6a92636e42dd17b41d0218cce9eb2b3"
tough-cookie@~2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
dependencies:
punycode "^1.4.1"
triple-beam@^1.2.0, triple-beam@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
twit@^2.2.11:
version "2.2.11"
resolved "https://registry.yarnpkg.com/twit/-/twit-2.2.11.tgz#554343d1cf343ddf503280db821f61be5ab407c3"
dependencies:
bluebird "^3.1.5"
mime "^1.3.4"
request "^2.68.0"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
uuid@^3.0.1, uuid@^3.1.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
winston-compat@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/winston-compat/-/winston-compat-0.1.4.tgz#599b4ce807ffe728713ecc25ede3f6b89425b739"
dependencies:
cycle "~1.0.3"
logform "^1.6.0"
triple-beam "^1.2.0"
winston-daily-rotate-file@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-3.2.3.tgz#9f80e7a421ab32b073c1217bae62e762001197d6"
dependencies:
file-stream-rotator "^0.2.1"
semver "^5.5.0"
triple-beam "^1.3.0"
winston-compat "^0.1.4"
winston-transport "^4.2.0"
winston-transport@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.2.0.tgz#a20be89edf2ea2ca39ba25f3e50344d73e6520e5"
dependencies:
readable-stream "^2.3.6"
triple-beam "^1.2.0"
winston@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/winston/-/winston-3.0.0.tgz#1f0b24a96586798bcf0cd149fb07ed47cb01a1b2"
dependencies:
async "^2.6.0"
diagnostics "^1.0.1"
is-stream "^1.1.0"
logform "^1.9.0"
one-time "0.0.4"
readable-stream "^2.3.6"
stack-trace "0.0.x"
triple-beam "^1.3.0"
winston-transport "^4.2.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"