<commit_before>var db_lookup = require('../db_lookup');
var expect = require('chai').use(require('chai-subset')).expect;
var StreamTest = require('streamtest');
var PouchDB = require('pouchdb-core')
PouchDB.plugin(require('pouchdb-adapter-memory')).plugin(require('pouchdb-mapreduce'));
var db = new PouchDB('db_lookup', {
    adapter: 'memory'
});
describe('fs_lookup', () => {
    before(() => {
        db.put({
            _id: 'md5',
        })
    })
    StreamTest.versions.forEach(function (version) {
        describe('for ' + version + ' streams', function () {

            it('should find document by md5', (done) => {
                StreamTest[version].fromObjects([{
                    md5: 'md5'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {
                            _id: 'md5'
                        }
                    }])
                    done(error);
                }))
            })

            it('should construct document if not found', (done) => {
                StreamTest[version].fromObjects([{
                    md5: '!md5',
                    file: 'file',
                    exif: 'exif'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {
                            _id: '!md5',
                            files: {
                                file: 'PRESENT'
                            },
                            exif: 'exif'
                        }
                    }])
                    done(error);
                }))
            })
        })

    })
})<commit_msg>Split tests into smaller and more focused<commit_after>var db_lookup = require('../db_lookup');
var expect = require('chai').use(require('chai-subset')).expect;
var StreamTest = require('streamtest');
var PouchDB = require('pouchdb-core')
PouchDB.plugin(require('pouchdb-adapter-memory')).plugin(require('pouchdb-mapreduce'));
var db = new PouchDB('db_lookup', {
    adapter: 'memory'
});
describe('fs_lookup', () => {
    before(() => {
        db.put({
            _id: 'md5',
        })
    })
    StreamTest.versions.forEach(function (version) {
        describe('for ' + version + ' streams', function () {

            it('should find document by md5', (done) => {
                StreamTest[version].fromObjects([{
                    md5: 'md5'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {
                            _id: 'md5'
                        }
                    }])
                    done(error);
                }))
            })

            it('should construct document if not found', (done) => {
                StreamTest[version].fromObjects([{
                    md5: '!md5'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {}
                    }])
                    done(error);
                }))
            })

            it('should initialize id if not found', (done) => {
                StreamTest[version].fromObjects([{
                    md5: '!md5'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {
                            _id: '!md5'
                        }
                    }])
                    done(error);
                }))
            })

            it('should initialize files if not found', (done) => {
                StreamTest[version].fromObjects([{
                    md5: '!md5',
                    file: 'file'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {
                            files: {
                                file: 'PRESENT'
                            }
                        }
                    }])
                    done(error);
                }))
            })
            it('should initialize exif if not found', (done) => {
                StreamTest[version].fromObjects([{
                    md5: '!md5',
                    exif: 'exif'
                }]).pipe(db_lookup(db)).pipe(StreamTest[version].toObjects((error, objects) => {
                    expect(objects).to.containSubset([{
                        doc: {
                            exif: 'exif'
                        }
                    }])
                    done(error);
                }))
            })
        })

    })
})<|endoftext|>


<commit_before>// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf

if (typeof Array.prototype.indexOf !== "function") {
    Array.prototype.indexOf = function(searchElement, fromIndex) {
        if (!this) {
            throw (typeof TypeError === "function" ? new TypeError() : new Error("TypeError"));
        }

        var pivot = fromIndex || 0,
            length = this.length,
            i;

        if (length > 0 && pivot < length) {
            if (pivot < 0) {
                pivot = length - Math.abs(pivot);
            }

            for (i = pivot; i < length; i++) {
                if (this[i] === searchElement) {
                    return i;
                }
            }
        }

        return -1;
    };
}
<commit_msg>Add module exports<commit_after>// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf

if (typeof Array.prototype.indexOf !== "function") {
    Array.prototype.indexOf = function(searchElement, fromIndex) {
        if (!this) {
            throw (typeof TypeError === "function" ? new TypeError() : new Error("TypeError"));
        }

        var pivot = fromIndex || 0,
            length = this.length,
            i;

        if (length > 0 && pivot < length) {
            if (pivot < 0) {
                pivot = length - Math.abs(pivot);
            }

            for (i = pivot; i < length; i++) {
                if (this[i] === searchElement) {
                    return i;
                }
            }
        }

        return -1;
    };
}

module.exports = Array.prototype.indexOf;
<|endoftext|>


<commit_before>var util = require('util'),
    _ = require('underscore'),
    request	= require('request'),
    crypto = require('crypto'),
    VError = require('verror'),
    cheerio = require('cheerio');

var BTCChina = function BTCChina(key, secret, server, timeout)
{
    this.key = key;
    this.secret = secret;

    this.server = server || 'https://api.btcchina.com';

    this.timeout = timeout || 30000;
};

BTCChina.prototype.privateRequest = function(method, params, callback)
{
    var functionName = 'BTCChina.privateRequest()',
        self = this;

    if(!this.key || !this.secret)
    {
        var error = new VError('%s must provide key and secret to make this API request.', functionName);
        return callback(error);
    }

    if(!_.isArray(params))
    {
        var error = new VError('%s second parameter %s must be an array. If no params then pass an empty array []', functionName, params);
        return callback(error);
    }

    if (!callback || typeof(callback) != 'function')
    {
        var error = new VError('%s third parameter needs to be a callback function', functionName);
        return callback(error);
    }

    // TODO fix so there can be two requests in the one second
    var tonce = new Date() * 1000; // spoof microsecond

    var message = "tonce=" + tonce + "&" +
            "accesskey=" + this.key + "&" +
            "requestmethod=post&" +
            "id=1&" +
            "method=" + method + "&" +
            "params=" + params.join(',');

    var signer = crypto.createHmac('sha1', this.secret);
    var hmac = signer.update(message).digest('hex');
    var signature = new Buffer(this.key + ':' + hmac).toString('base64');

    var headers = {
        "User-Agent": "BTC China Javascript API Client",
        'Authorization': 'Basic ' + signature,
        'Json-Rpc-Tonce': tonce
    };

    var options = {
        url: this.server + '/api_trade_v1.php',
        method: 'POST',
        headers: headers,
        json: {
            method: method,
            params: params,
            id: 1}
    };

    var requestDesc = util.format('%s request to url %s with tonce %s, method %s and params %s',
        options.method, options.url, tonce, method, JSON.stringify(params));

    executeRequest(options, requestDesc, callback);
};

BTCChina.prototype.publicRequest = function(method, params, callback)
{
    var functionName = 'BTCChina.publicRequest()';

    if(!_.isObject(params))
    {
        var error = new VError('%s second parameter %s must be an object. If no params then pass an empty object {}', functionName, params);
        return callback(error);
    }

    if (!callback || typeof(callback) != 'function')
    {
        var error = new VError('%s third parameter needs to be a callback function with err and data parameters', functionName);
        return callback(error);
    }

    var headers = {"User-Agent": "BTC China Javascript API Client"};

    // TODO need a better way of handling trades using a different API
    // note the doco says ticker and order book should use https://data.btcchina.com but it doesn't work
    // support have advised ticket and order book need to use 'https://api.btcchina.com'
    var url;
    if (method === 'trades')
    {
        url = 'https://data.btcchina.com/data/' + method;
    }
    else
    {
        url = this.server + '/data/' + method;
    }

    var options = {
        url: url,
        method: 'GET',
        headers: headers,
        timeout: this.timeout,
        qs: params,
        json: {}        // request will parse the json response into an object
    };

    var requestDesc = util.format('%s request to url %s with parameters %s',
        options.method, options.url, JSON.stringify(params));

    executeRequest(options, requestDesc, callback)
};

function executeRequest(options, requestDesc, callback)
{
    var functionName = 'BTCChina.executeRequest()';

    request(options, function(err, response, data)
    {
        var error = null;   // default to no errors

        if(err)
        {
            error = new VError(err, '%s failed %s', functionName, requestDesc);
            error.name = err.code;
        }
        else if (response.statusCode < 200 || response.statusCode >= 300)
        {
            error = new VError('%s HTTP status code %s returned from %s. Status message: %s', functionName,
                response.statusCode, requestDesc, response.statusMessage);
            error.name = response.statusCode;
        }
        // if request was not able to parse json response into an object
        else if (!_.isObject(data) )
        {
            error = new VError('%s could not parse response from %s\n. HTTP status code %s. Response: %s', functionName, requestDesc, response.statusCode, data);
            error.name = data;
        }
        else if (_.has(data, 'error'))
        {
            error = new VError('%s API returned error code %s from %s\nError message: %s', functionName,
                data.error.code, requestDesc, data.error.message);
            error.name = data.error.message;
        }

        callback(error, data);
    });
}

function constructParamArray(args, maxArgs)
{
    var paramArray = [];

    for (i = 1; i <= maxArgs; i++)
    {
        // if the argument is undefined
        if (_.isUndefined(args[i]))
            break;
        else
            paramArray.push(args[i]);
    }

    return paramArray;
}

//
// Public Functions
//

BTCChina.prototype.getTicker = function getTicker(callback, market)
{
    this.publicRequest('ticker', {market: market}, callback);
};

BTCChina.prototype.getOrderBook = function getOrderBook(callback, market, limit)
{
    var params = {market: market};

    // add limit to parameters if it was passed to this function
    if (limit) params.limit = limit;

    this.publicRequest('orderbook', params, callback);
};

BTCChina.prototype.getHistoryData = function getHistoryData(callback, params)
{
    this.publicRequest('historydata', params, callback);
};

BTCChina.prototype.getTrades = function getTrades(callback, market, params)
{
    params = _.extend({market: market, sincetype: 'time'}, params);

    this.publicRequest('historydata', params, callback);
};

//
// Private Functions
//

BTCChina.prototype.buyOrder2 = function buyOrder2(callback, price, amount, market)
{
    var params = constructParamArray(arguments, 3);

    this.privateRequest('buyOrder2', params, callback);
};

BTCChina.prototype.sellOrder2 = function sellOrder2(callback, price, amount, market)
{
    var params = constructParamArray(arguments, 3);

    this.privateRequest('sellOrder2', params, callback);
};

// calls either buyOrder2 or sellOrder2 functions depending on the second type parameter
BTCChina.prototype.createOrder2 = function createOrder2(callback, type, price, amount, market)
{
    var functionName = 'BTCChina.createOrder2()',
        // rest removes the first element of the array
        params = constructParamArray(_.rest(arguments), 3);

    if (type === 'buy')
    {
        this.privateRequest('buyOrder2', params, callback);
    }
    else if (type === 'sell')
    {
        this.privateRequest('sellOrder2', params, callback);
    }
    else
    {
        var error = new VError('%s second parameter type "%s" needs to be either "buy" or "sell"', functionName, type);
        callback(error);
    }
};

BTCChina.prototype.cancelOrder = function cancelOrder(callback, id, market)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('cancelOrder', params, callback);
};

BTCChina.prototype.getOrders = function getOrders(callback, openOnly, market, limit, offset, since, withDetail)
{
    var params = constructParamArray(arguments, 6);

    this.privateRequest('getOrders', params, callback);
};

BTCChina.prototype.getOrder = function getOrder(callback, id, market, withDetail)
{
    var params = constructParamArray(arguments, 3);

    this.privateRequest('getOrder', params, callback);
};

BTCChina.prototype.getTransactions = function getTransactions(callback, type, limit, offset, since, sinceType)
{
    var params = constructParamArray(arguments, 5);

    this.privateRequest('getTransactions', params, callback);
};

BTCChina.prototype.getMarketDepth2 = function getMarketDepth2(callback, limit, market)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getMarketDepth2', params, callback);
};

BTCChina.prototype.getDeposits = function getDeposits(callback, currency, pendingOnly)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getDeposits', params, callback);
};

BTCChina.prototype.getWithdrawal = function getWithdrawal(callback, id, currency)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getWithdrawal', params, callback);
};

BTCChina.prototype.getWithdrawals = function getWithdrawals(callback, currency, pendingOnly)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getWithdrawals', params, callback);
};

BTCChina.prototype.requestWithdrawal = function requestWithdrawal(callback, currency, amount)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('requestWithdrawal', params, callback);
};

BTCChina.prototype.getAccountInfo = function getAccountInfo(callback, type)
{
    var params = constructParamArray(arguments, 1);

    this.privateRequest('getAccountInfo', params, callback);
};

/**
 * Screen scraps the fiat deposit and withdrawal exchange rates from BTCC's website
 * @param callback (err: Error, depositRate: number, withdrawalRate: number): void
 * @param baseCurrency USD in USD/CNY
 * @param quoteCurrency CNY in USD/CNY
 */
BTCChina.prototype.getFiatExchangeRates = function getFiatExchangeRates(callback, baseCurrency, quoteCurrency)
{
    var options = {
        url: "https://exchange.btcc.com/page/internationalvoucher",
        method: 'GET',
        timeout: this.timeout
    };

    var requestDesc = util.format('%s request to url %s',
        options.method, options.url);

    request(options, function(err, response, html)
    {
        var error;

        if(err)
        {
            error = new VError(err, 'failed %s', requestDesc);
            error.name = err.code;
            return callback(error);
        }
        else if (response.statusCode < 200 || response.statusCode >= 300)
        {
            error = new VError('HTTP status code %s returned from %s. Status message: %s',
                response.statusCode, requestDesc, response.statusMessage);
            error.name = response.statusCode;
            return callback(error);
        }
        else if (!html)
        {
            error = new VError('no HTML response from %s', requestDesc);
            return callback(error);
        }

        parserCurrencies(html, callback);
    });
};

function parserCurrencies(html, callback)
{
    var returnRates = {},
        returnError;

    // for each currency that can be convered to CNY
    ['USD','CNH','HKD','EUR'].forEach(function(baseCurrency)
    {
        var rates = parserRates(baseCurrency, 'CNY', html);

        if (rates instanceof Error)
        {
            returnError = rates;
        }
        else
        {
            returnRates[baseCurrency + 'CNY'] = rates;
        }
    })

    callback(returnError, returnRates);
}

function parserRates(baseCurrency, quoteCurrency, html)
{
    var symbol = baseCurrency + '/' + quoteCurrency;

    // try and parse HTML body form response
    $ = cheerio.load(html);
    var rateRow = $("table tr:contains('" + symbol +"')");

    if (rateRow.length > 0)
    {
        var depositRate = rateRow.children().eq(1).text();
        var withdrawalRate = rateRow.children().eq(2).text();

        return {
            deposit: Number(depositRate),
            withdrawal: Number(withdrawalRate)
        };
    }
    else
    {
        return new VError('Could not find exchange rate for symbol %s', symbol);
    }
}

module.exports = BTCChina;<commit_msg>use new data api<commit_after>var util = require('util'),
    _ = require('underscore'),
    request	= require('request'),
    crypto = require('crypto'),
    VError = require('verror'),
    cheerio = require('cheerio');

var BTCChina = function BTCChina(key, secret, server, timeout)
{
    this.key = key;
    this.secret = secret;

    this.tradeServer = server || 'https://api.btcchina.com';
    this.dataServer = server || 'https://data.btcchina.com';

    this.timeout = timeout || 30000;
};

BTCChina.prototype.privateRequest = function(method, params, callback)
{
    var functionName = 'BTCChina.privateRequest()',
        self = this;

    if(!this.key || !this.secret)
    {
        var error = new VError('%s must provide key and secret to make this API request.', functionName);
        return callback(error);
    }

    if(!_.isArray(params))
    {
        var error = new VError('%s second parameter %s must be an array. If no params then pass an empty array []', functionName, params);
        return callback(error);
    }

    if (!callback || typeof(callback) != 'function')
    {
        var error = new VError('%s third parameter needs to be a callback function', functionName);
        return callback(error);
    }

    // TODO fix so there can be two requests in the one second
    var tonce = new Date() * 1000; // spoof microsecond

    var message = "tonce=" + tonce + "&" +
            "accesskey=" + this.key + "&" +
            "requestmethod=post&" +
            "id=1&" +
            "method=" + method + "&" +
            "params=" + params.join(',');

    var signer = crypto.createHmac('sha1', this.secret);
    var hmac = signer.update(message).digest('hex');
    var signature = new Buffer(this.key + ':' + hmac).toString('base64');

    var headers = {
        "User-Agent": "BTC China Javascript API Client",
        'Authorization': 'Basic ' + signature,
        'Json-Rpc-Tonce': tonce
    };

    var options = {
        url: this.tradeServer + '/api_trade_v1.php',
        method: 'POST',
        headers: headers,
        json: {
            method: method,
            params: params,
            id: 1}
    };

    var requestDesc = util.format('%s request to url %s with tonce %s, method %s and params %s',
        options.method, options.url, tonce, method, JSON.stringify(params));

    executeRequest(options, requestDesc, callback);
};

BTCChina.prototype.publicRequest = function(method, params, callback)
{
    var functionName = 'BTCChina.publicRequest()';

    if(!_.isObject(params))
    {
        var error = new VError('%s second parameter %s must be an object. If no params then pass an empty object {}', functionName, params);
        return callback(error);
    }

    if (!callback || typeof(callback) != 'function')
    {
        var error = new VError('%s third parameter needs to be a callback function with err and data parameters', functionName);
        return callback(error);
    }

    var headers = {"User-Agent": "BTC China Javascript API Client"};

    var url = this.dataServer + '/data/' + method;

    var options = {
        url: url,
        method: 'GET',
        headers: headers,
        timeout: this.timeout,
        qs: params,
        json: {}        // request will parse the json response into an object
    };

    var requestDesc = util.format('%s request to url %s with parameters %s',
        options.method, options.url, JSON.stringify(params));

    executeRequest(options, requestDesc, callback)
};

function executeRequest(options, requestDesc, callback)
{
    var functionName = 'BTCChina.executeRequest()';

    request(options, function(err, response, data)
    {
        var error = null;   // default to no errors

        if(err)
        {
            error = new VError(err, '%s failed %s', functionName, requestDesc);
            error.name = err.code;
        }
        else if (response.statusCode < 200 || response.statusCode >= 300)
        {
            error = new VError('%s HTTP status code %s returned from %s. Status message: %s', functionName,
                response.statusCode, requestDesc, response.statusMessage);
            error.name = response.statusCode;
        }
        // if request was not able to parse json response into an object
        else if (!_.isObject(data) )
        {
            error = new VError('%s could not parse response from %s\n. HTTP status code %s. Response: %s', functionName, requestDesc, response.statusCode, data);
            error.name = data;
        }
        else if (_.has(data, 'error'))
        {
            error = new VError('%s API returned error code %s from %s\nError message: %s', functionName,
                data.error.code, requestDesc, data.error.message);
            error.name = data.error.message;
        }

        callback(error, data);
    });
}

function constructParamArray(args, maxArgs)
{
    var paramArray = [];

    for (i = 1; i <= maxArgs; i++)
    {
        // if the argument is undefined
        if (_.isUndefined(args[i]))
            break;
        else
            paramArray.push(args[i]);
    }

    return paramArray;
}

//
// Public Functions
//

BTCChina.prototype.getTicker = function getTicker(callback, market)
{
    this.publicRequest('ticker', {market: market}, callback);
};

BTCChina.prototype.getOrderBook = function getOrderBook(callback, market, limit)
{
    var params = {market: market};

    // add limit to parameters if it was passed to this function
    if (limit) params.limit = limit;

    this.publicRequest('orderbook', params, callback);
};

BTCChina.prototype.getHistoryData = function getHistoryData(callback, params)
{
    this.publicRequest('historydata', params, callback);
};

BTCChina.prototype.getTrades = function getTrades(callback, market, params)
{
    params = _.extend({market: market, sincetype: 'time'}, params);

    this.publicRequest('historydata', params, callback);
};

//
// Private Functions
//

BTCChina.prototype.buyOrder2 = function buyOrder2(callback, price, amount, market)
{
    var params = constructParamArray(arguments, 3);

    this.privateRequest('buyOrder2', params, callback);
};

BTCChina.prototype.sellOrder2 = function sellOrder2(callback, price, amount, market)
{
    var params = constructParamArray(arguments, 3);

    this.privateRequest('sellOrder2', params, callback);
};

// calls either buyOrder2 or sellOrder2 functions depending on the second type parameter
BTCChina.prototype.createOrder2 = function createOrder2(callback, type, price, amount, market)
{
    var functionName = 'BTCChina.createOrder2()',
        // rest removes the first element of the array
        params = constructParamArray(_.rest(arguments), 3);

    if (type === 'buy')
    {
        this.privateRequest('buyOrder2', params, callback);
    }
    else if (type === 'sell')
    {
        this.privateRequest('sellOrder2', params, callback);
    }
    else
    {
        var error = new VError('%s second parameter type "%s" needs to be either "buy" or "sell"', functionName, type);
        callback(error);
    }
};

BTCChina.prototype.cancelOrder = function cancelOrder(callback, id, market)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('cancelOrder', params, callback);
};

BTCChina.prototype.getOrders = function getOrders(callback, openOnly, market, limit, offset, since, withDetail)
{
    var params = constructParamArray(arguments, 6);

    this.privateRequest('getOrders', params, callback);
};

BTCChina.prototype.getOrder = function getOrder(callback, id, market, withDetail)
{
    var params = constructParamArray(arguments, 3);

    this.privateRequest('getOrder', params, callback);
};

BTCChina.prototype.getTransactions = function getTransactions(callback, type, limit, offset, since, sinceType)
{
    var params = constructParamArray(arguments, 5);

    this.privateRequest('getTransactions', params, callback);
};

BTCChina.prototype.getMarketDepth2 = function getMarketDepth2(callback, limit, market)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getMarketDepth2', params, callback);
};

BTCChina.prototype.getDeposits = function getDeposits(callback, currency, pendingOnly)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getDeposits', params, callback);
};

BTCChina.prototype.getWithdrawal = function getWithdrawal(callback, id, currency)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getWithdrawal', params, callback);
};

BTCChina.prototype.getWithdrawals = function getWithdrawals(callback, currency, pendingOnly)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('getWithdrawals', params, callback);
};

BTCChina.prototype.requestWithdrawal = function requestWithdrawal(callback, currency, amount)
{
    var params = constructParamArray(arguments, 2);

    this.privateRequest('requestWithdrawal', params, callback);
};

BTCChina.prototype.getAccountInfo = function getAccountInfo(callback, type)
{
    var params = constructParamArray(arguments, 1);

    this.privateRequest('getAccountInfo', params, callback);
};

/**
 * Screen scraps the fiat deposit and withdrawal exchange rates from BTCC's website
 * @param callback (err: Error, depositRate: number, withdrawalRate: number): void
 * @param baseCurrency USD in USD/CNY
 * @param quoteCurrency CNY in USD/CNY
 */
BTCChina.prototype.getFiatExchangeRates = function getFiatExchangeRates(callback, baseCurrency, quoteCurrency)
{
    var options = {
        url: "https://exchange.btcc.com/page/internationalvoucher",
        method: 'GET',
        timeout: this.timeout
    };

    var requestDesc = util.format('%s request to url %s',
        options.method, options.url);

    request(options, function(err, response, html)
    {
        var error;

        if(err)
        {
            error = new VError(err, 'failed %s', requestDesc);
            error.name = err.code;
            return callback(error);
        }
        else if (response.statusCode < 200 || response.statusCode >= 300)
        {
            error = new VError('HTTP status code %s returned from %s. Status message: %s',
                response.statusCode, requestDesc, response.statusMessage);
            error.name = response.statusCode;
            return callback(error);
        }
        else if (!html)
        {
            error = new VError('no HTML response from %s', requestDesc);
            return callback(error);
        }

        parserCurrencies(html, callback);
    });
};

function parserCurrencies(html, callback)
{
    var returnRates = {},
        returnError;

    // for each currency that can be convered to CNY
    ['USD','CNH','HKD','EUR'].forEach(function(baseCurrency)
    {
        var rates = parserRates(baseCurrency, 'CNY', html);

        if (rates instanceof Error)
        {
            returnError = rates;
        }
        else
        {
            returnRates[baseCurrency + 'CNY'] = rates;
        }
    })

    callback(returnError, returnRates);
}

function parserRates(baseCurrency, quoteCurrency, html)
{
    var symbol = baseCurrency + '/' + quoteCurrency;

    // try and parse HTML body form response
    $ = cheerio.load(html);
    var rateRow = $("table tr:contains('" + symbol +"')");

    if (rateRow.length > 0)
    {
        var depositRate = rateRow.children().eq(1).text();
        var withdrawalRate = rateRow.children().eq(2).text();

        return {
            deposit: Number(depositRate),
            withdrawal: Number(withdrawalRate)
        };
    }
    else
    {
        return new VError('Could not find exchange rate for symbol %s', symbol);
    }
}

module.exports = BTCChina;<|endoftext|>


<commit_before>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const helper = require('./helper');

class Dialog {
  /**
   * @param {!Session} client
   * @param {!Dialog.Type} type
   * @param {string} message
   * @param {(string|undefined)} defaultValue
   */
  constructor(client, type, message, defaultValue) {
    this._client = client;
    this.type = type;
    this._message = message;
    this._handled = false;
    this._defaultValue = defaultValue || '';
  }

  /**
   * @return {string}
   */
  message() {
    return this._message;
  }

  /**
   * @return {string}
   */
  defaultValue() {
    return this._defaultValue;
  }

  /**
   * @param {string=} promptText
   * @return {!Promise}
   */
  async accept(promptText) {
    console.assert(!this._handled, 'Cannot accept dialog which is already handled!');
    this._handled = true;
    await this._client.send('Page.handleJavaScriptDialog', {
      accept: true,
      promptText: promptText
    });
  }

  /**
   * @return {!Promise}
   */
  async dismiss() {
    console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
    this._handled = true;
    await this._client.send('Page.handleJavaScriptDialog', {
      accept: false
    });
  }
}

Dialog.Type = {
  Alert: 'alert',
  BeforeUnload: 'beforeunload',
  Confirm: 'confirm',
  Prompt: 'prompt'
};

module.exports = Dialog;
helper.tracePublicAPI(Dialog);
<commit_msg>Default value for property on dialog (#247)<commit_after>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const helper = require('./helper');

class Dialog {
  /**
   * @param {!Session} client
   * @param {!Dialog.Type} type
   * @param {string} message
   * @param {(string|undefined)} defaultValue
   */
  constructor(client, type, message, defaultValue = '') {
    this._client = client;
    this.type = type;
    this._message = message;
    this._handled = false;
    this._defaultValue = defaultValue;
  }

  /**
   * @return {string}
   */
  message() {
    return this._message;
  }

  /**
   * @return {string}
   */
  defaultValue() {
    return this._defaultValue;
  }

  /**
   * @param {string=} promptText
   * @return {!Promise}
   */
  async accept(promptText) {
    console.assert(!this._handled, 'Cannot accept dialog which is already handled!');
    this._handled = true;
    await this._client.send('Page.handleJavaScriptDialog', {
      accept: true,
      promptText: promptText
    });
  }

  /**
   * @return {!Promise}
   */
  async dismiss() {
    console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
    this._handled = true;
    await this._client.send('Page.handleJavaScriptDialog', {
      accept: false
    });
  }
}

Dialog.Type = {
  Alert: 'alert',
  BeforeUnload: 'beforeunload',
  Confirm: 'confirm',
  Prompt: 'prompt'
};

module.exports = Dialog;
helper.tracePublicAPI(Dialog);
<|endoftext|>


<commit_before>/**
 * GitBit Bot for Slack !
 * @author: Vasu Jain
 */

// Libraries
var https = require('https');
var BotConfig = require('./config.json');

var authTokenEncrypted = BotConfig.auth_token;
//var authTokenDecrypted = "token " + Buffer.from(authTokenEncrypted, 'base64').toString("ascii");

// For Node.js v5.11.1 and below
var buf = new Buffer(authTokenEncrypted, 'base64');
var authTokenDecrypted = "token " + buf.toString("ascii");
var colors = ['green', 'red', 'blue', 'yellow'];

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({
            user: installer
        }, function(err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am GitBit bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

// Configure the persistence options
var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({
            mongoUri: process.env.MONGOLAB_URI
        }),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN) ? './db_slack_bot_ci/' : './db_slack_bot_a/'), //use a different name if an app or CI
    };
}

if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
//} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

/**
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function(bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function(bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

// Core bot logic !
controller.on('bot_channel_join', function(bot, message) {
    bot.reply(message, "Thank you for inviting me to your Slack Channel!")
});

controller.hears(['hello', 'hi', 'greetings'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    bot.reply(message, 'Hello!');
});

controller.hears('pr (.*)', ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    var repo = message.match[1];
    if (typeof repo !== 'undefined' && repo) {
        var githubRepo = BotConfig.repos[repo];
        var flagZeroPRComment = false;
        if (githubRepo) {
            flagZeroPRComment = true;
            githubGetPullRequest(githubRepo, bot, message, flagZeroPRComment);
        } else if (repo == 'custom') {
            for (var r in BotConfig.repos) {
                githubGetPullRequest(BotConfig.repos[r], bot, message, flagZeroPRComment);
            }
        } else if (repo == 'all') {
            getListOfAllGithubReposInOrg(bot, message);
        } else {
            bot.reply(message, "Invalid Repo or Repo not configured");
        }
    } else {
        bot.reply(message, "Repo is undefined -- Invalid request or Repo not configured");
    }
});

// Make a POST call to GITHUB API to fetch all OPEN PR's
function githubGetPullRequest(repo, bot, message, flagZeroPRComment) {
    console.log("Making a POST call to GITHUB API to fetch all OPEN PR's...");
    var request = require('request');
    var url = BotConfig.github_api_url + 'repos/' + BotConfig.repo_org + repo + '/pulls?state=open';
    console.log(url);
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        parseAndResponse(body, bot, message, repo, flagZeroPRComment);
    });
}

// Parse the pull response json and extract PR#, Title, User out of it.
function parseAndResponse(body, bot, message, repo, flagZeroPRComment) {
    console.log("Parsing the pull response json and extracting PR#, Title, User out of it...");
    var repoSource = BotConfig.repo_org + repo + " Open Pull Requests : ";
    var response = "";
    var obj = JSON.parse(body);
    var objLength = obj.length;
    if (objLength == 0) {
        if (!BotConfig.disable_zero_pr_repo) { //if false, then only display Repo with Zero PR
            response = repoSource;
            if (flagZeroPRComment) {
                response += "No open PR's @ the moment ! Are you guys coding ?"
            } else {
                response += "0."
            }
            bot.reply(message, response);
        }
    } else {
        for (var i = 0; i < objLength; i++) {
            response += "\nPR # " + obj[i].number + " - " + obj[i].title + " by " + obj[i].user.login;
        }
        bot.reply(message, {
            "attachments": [{
                "fallback": repoSource,
                "color": "#36a64f",
                "title": repoSource,
//                "title_link": BotConfig.github_api_url + 'repos/' + BotConfig.repo_org + repo ,
                "text": response
            }]
        });
    }
    console.log(response);
    console.log("parseAndResponse for " + repo + " with " + objLength + " PR'(s) executed successfully.");
}

// Getting list of all Github Repos in an Org. Can be 100+. For the initial phase only top 100 results will display
function getListOfAllGithubReposInOrg(bot, message) {
    console.log("Getting list of all Github Repos in an Org. Can be 100+....");
    var ghArray = new Array();
    var url = BotConfig.github_api_url + 'orgs/' + BotConfig.repo_org + 'repos?per_page=' + BotConfig.max_page_count;
    console.log(url);
    var request = require('request');
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        ghArray = constructAllGithubRepoObject(body, bot, message);
    });
    console.log("getListOfAllGithubReposInOrg executed successfully.\n");
}

// Parse the Org Repos response json and extracting Repo details out of it.
function constructAllGithubRepoObject(body, bot, message) {
    console.log("Parsing the Org Repos response json and extracting Repo details out of it...");
    var orgGithubRepo = new Array();
    var obj = JSON.parse(body);
    var objLength = obj.length;
    for (var i = 0; i < objLength; i++) {
        orgGithubRepo.push(obj[i].name);
        githubGetPullRequest(obj[i].name, bot, message, false);
    }
    console.log("constructAllGithubRepoObject executed successfully.\n");
}<commit_msg>removed controller<commit_after>/**
 * GitBit Bot for Slack !
 * @author: Vasu Jain
 */

// Libraries
var https = require('https');
var BotConfig = require('./config.json');

var authTokenEncrypted = BotConfig.auth_token;
//var authTokenDecrypted = "token " + Buffer.from(authTokenEncrypted, 'base64').toString("ascii");

// For Node.js v5.11.1 and below
var buf = new Buffer(authTokenEncrypted, 'base64');
var authTokenDecrypted = "token " + buf.toString("ascii");
var colors = ['green', 'red', 'blue', 'yellow'];

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({
            user: installer
        }, function(err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am GitBit bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

// Configure the persistence options
var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({
            mongoUri: process.env.MONGOLAB_URI
        }),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN) ? './db_slack_bot_ci/' : './db_slack_bot_a/'), //use a different name if an app or CI
    };
}

if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
//} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
//    var app = require('./lib/apps');
//    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

/**
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function(bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function(bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

// Core bot logic !
controller.on('bot_channel_join', function(bot, message) {
    bot.reply(message, "Thank you for inviting me to your Slack Channel!")
});

controller.hears(['hello', 'hi', 'greetings'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    bot.reply(message, 'Hello!');
});

controller.hears('pr (.*)', ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    var repo = message.match[1];
    if (typeof repo !== 'undefined' && repo) {
        var githubRepo = BotConfig.repos[repo];
        var flagZeroPRComment = false;
        if (githubRepo) {
            flagZeroPRComment = true;
            githubGetPullRequest(githubRepo, bot, message, flagZeroPRComment);
        } else if (repo == 'custom') {
            for (var r in BotConfig.repos) {
                githubGetPullRequest(BotConfig.repos[r], bot, message, flagZeroPRComment);
            }
        } else if (repo == 'all') {
            getListOfAllGithubReposInOrg(bot, message);
        } else {
            bot.reply(message, "Invalid Repo or Repo not configured");
        }
    } else {
        bot.reply(message, "Repo is undefined -- Invalid request or Repo not configured");
    }
});

// Make a POST call to GITHUB API to fetch all OPEN PR's
function githubGetPullRequest(repo, bot, message, flagZeroPRComment) {
    console.log("Making a POST call to GITHUB API to fetch all OPEN PR's...");
    var request = require('request');
    var url = BotConfig.github_api_url + 'repos/' + BotConfig.repo_org + repo + '/pulls?state=open';
    console.log(url);
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        parseAndResponse(body, bot, message, repo, flagZeroPRComment);
    });
}

// Parse the pull response json and extract PR#, Title, User out of it.
function parseAndResponse(body, bot, message, repo, flagZeroPRComment) {
    console.log("Parsing the pull response json and extracting PR#, Title, User out of it...");
    var repoSource = BotConfig.repo_org + repo + " Open Pull Requests : ";
    var response = "";
    var obj = JSON.parse(body);
    var objLength = obj.length;
    if (objLength == 0) {
        if (!BotConfig.disable_zero_pr_repo) { //if false, then only display Repo with Zero PR
            response = repoSource;
            if (flagZeroPRComment) {
                response += "No open PR's @ the moment ! Are you guys coding ?"
            } else {
                response += "0."
            }
            bot.reply(message, response);
        }
    } else {
        for (var i = 0; i < objLength; i++) {
            response += "\nPR # " + obj[i].number + " - " + obj[i].title + " by " + obj[i].user.login;
        }
        bot.reply(message, {
            "attachments": [{
                "fallback": repoSource,
                "color": "#36a64f",
                "title": repoSource,
//                "title_link": BotConfig.github_api_url + 'repos/' + BotConfig.repo_org + repo ,
                "text": response
            }]
        });
    }
    console.log(response);
    console.log("parseAndResponse for " + repo + " with " + objLength + " PR'(s) executed successfully.");
}

// Getting list of all Github Repos in an Org. Can be 100+. For the initial phase only top 100 results will display
function getListOfAllGithubReposInOrg(bot, message) {
    console.log("Getting list of all Github Repos in an Org. Can be 100+....");
    var ghArray = new Array();
    var url = BotConfig.github_api_url + 'orgs/' + BotConfig.repo_org + 'repos?per_page=' + BotConfig.max_page_count;
    console.log(url);
    var request = require('request');
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        ghArray = constructAllGithubRepoObject(body, bot, message);
    });
    console.log("getListOfAllGithubReposInOrg executed successfully.\n");
}

// Parse the Org Repos response json and extracting Repo details out of it.
function constructAllGithubRepoObject(body, bot, message) {
    console.log("Parsing the Org Repos response json and extracting Repo details out of it...");
    var orgGithubRepo = new Array();
    var obj = JSON.parse(body);
    var objLength = obj.length;
    for (var i = 0; i < objLength; i++) {
        orgGithubRepo.push(obj[i].name);
        githubGetPullRequest(obj[i].name, bot, message, false);
    }
    console.log("constructAllGithubRepoObject executed successfully.\n");
}<|endoftext|>


<commit_before>/* Fiidmi API Library */

var util = require('util');
var extend = require('nor-extend');
var client = require('./client.js');

/** `Fiidmi` constructor. */
function Fiidmi(options) {
	var self = this;

	options = options || {};

	// If true, test environment is enabled, otherwise not.
	self._test = options.test || false;

	// The hostname to Fiidmi API
	self._hostname = options.hostname || (self._test ? 'testapi.fiidmi.fi' : 'api.fiidmi.fi');
	self._version = options.api_version || '3.3';
	self._url = 'https://'+self._hostname+'/'+self._version;
	self._api_key = options.api_key;
	self._secret = options.secret;

	extend.ActionObject.call(this);
}

util.inherits(Fiidmi, extend.ActionObject);

/** Returns the method name of href */
Fiidmi.getMethodName = function(href) {
	var parts = href.split(/[\/_]/)
	var first;
	do {
		first = parts.shift();
	} while(first === '');
	first = first || '';
	return first + parts.map(function(p) { return p.charAt(0).toUpperCase() + p.slice(1); }).join('');
};

/** All Fiidmi API actions */
Fiidmi._actions = require('./api/3.3');

/* Build methods */
function build_methods(obj, path) {
	path = path || '';
	Object.keys(obj).forEach(function(key) {
		var action = obj[key];
		var href = path+ '/' + key;
		if(action.type) {
			//console.error('DEBUG: href = ' + href + ' and name = ' + Fiidmi.getMethodName(href));
			extend.ActionObject.setup(Fiidmi, Fiidmi.getMethodName(href), function(opts) {
				var self = this;
				opts = opts || {};
				var results = require('jsonschema').validate(opts, action);
				if(results.errors.length >= 1) {
					throw new TypeError("bad arguments: " + results.errors.join(', ') );
				}
				return client.post(self._url+ href, opts);
			});
		} else {
			build_methods(obj[key], href);
		}
	});
}

build_methods(Fiidmi._actions);

// Export
module.exports = Fiidmi;

/* EOF */
<commit_msg>Small fixes<commit_after>/* Fiidmi API Library */

var is = require('is');
var util = require('util');
var extend = require('nor-extend');
var client = require('./client.js');

var API_VERSION = '3.3';

/** `Fiidmi` constructor. */
function Fiidmi(options) {
	var self = this;

	options = options || {};

	if(options.hostname === 'testapi.fiidmi.fi') {
		options.test = true;
	}

	// If true, test environment is enabled, otherwise not.
	self._test = is.def(options.test) ? is.true(options.test) : false;

	// The hostname to Fiidmi API
	self._hostname = options.hostname || (self._test ? 'testapi.fiidmi.fi' : 'api.fiidmi.fi');
	self._version = options.api_version || API_VERSION;
	self._url = 'https://'+self._hostname+'/'+self._version;
	self._api_key = options.api_key;
	self._secret = options.secret;

	extend.ActionObject.call(this);
}

util.inherits(Fiidmi, extend.ActionObject);

/** Returns the method name of href */
Fiidmi.getMethodName = function(href) {
	var parts = href.split(/[\/_]/)
	var first;
	do {
		first = parts.shift();
	} while(first === '');
	first = first || '';
	return first + parts.map(function(p) { return p.charAt(0).toUpperCase() + p.slice(1); }).join('');
};

/** All Fiidmi API actions */
Fiidmi._actions = require('./api/'+API_VERSION);

/* Build methods */
function build_methods(obj, path) {
	path = path || '';
	Object.keys(obj).forEach(function(key) {
		var action = obj[key];
		var href = path+ '/' + key;
		if(action.type) {
			//console.error('DEBUG: href = ' + href + ' and name = ' + Fiidmi.getMethodName(href));
			extend.ActionObject.setup(Fiidmi, Fiidmi.getMethodName(href), function(opts) {
				var self = this;
				opts = opts || {};
				var results = require('jsonschema').validate(opts, action);
				if(results.errors.length >= 1) {
					throw new TypeError("bad arguments: " + results.errors.join(', ') );
				}
				return client.post(self._url+ href, opts);
			});
		} else {
			build_methods(obj[key], href);
		}
	});
}

build_methods(Fiidmi._actions);

// Export
module.exports = Fiidmi;

/* EOF */
<|endoftext|>


<commit_before>const fs = require('fs');
const winston = require('winston');
const Telegraf = require('telegraf');
const commandParts = require('telegraf-command-parts');
const CronJob = require('cron').CronJob;
const secrets = require('./lib/secrets').load();
const {getUnreadMessages} = require('./lib/scraper');
const hashTracker = require('./lib/hashTracker');

let job;

const webmailLinkKeyboard = Telegraf.Extra
  .markdown()
  .markup((m) => m.inlineKeyboard([
    Telegraf.Markup.urlButton('Apri WebMail', 'https://webmail.studenti.unipi.it/horde/imp/')
  ]));

async function checkSecrets() {
  if (!secrets.telegram_token) {
    throw 'Non hai configurato il token del bot Telegram.';
  }
  if (!secrets.telegram_password) {
    throw 'Non hai configurato la password per il bot Telegram.';
  }
}

function formatNewMessage(message) {
  return 'Hai un nuovo messaggio! 📬\n\n' +
    `*Mittente*: ${message.from}\n` +
    `*Oggetto*: ${message.subject}\n` +
    `*Dimensione*: ${message.size}`
}

async function setCronjob(bot, chatid) {
  async function doCronjob() {
    winston.info('Running cronjob for chat ID', chatid, '...');
    let unseen = (await getUnreadMessages(secrets));
    if (unseen.length > 0) {
      for (let i = 0; i < unseen.length; i++) {
        let msg = formatNewMessage(unseen[i]);
        let msgHash = hashTracker.md5(msg);
        if (!hashTracker.isSeen(msgHash)) {
          bot.telegram.sendMessage(chatid, msg, webmailLinkKeyboard);
          hashTracker.setSeen(msgHash);
        }
      }
    }
    winston.info('Done running cronjob for chat ID', chatid, '.');
  }

  if (!job) {
    job = new CronJob({
      cronTime: '0 */15 * * * *',
      onTick: await doCronjob,
    });
  }
  job.start();
  await doCronjob();
}

checkSecrets()
  .then(() => {
    const bot = new Telegraf(secrets.telegram_token);

    if (process.env.WEBMAILBOT_LOCKFILE) {
      try {
        fs.accessSync(process.env.WEBMAILBOT_LOCKFILE, fs.constants.R_OK | fs.constants.W_OK);
        let chat_id = fs.readFileSync(process.env.WEBMAILBOT_LOCKFILE, 'utf8');
        if (chat_id) {
          setCronjob(bot, chat_id);
          winston.info("Loaded Chat ID", chat_id, "from WEBMAILBOT_LOCKFILE path.");
        } else {
          winston.info("Chat ID from WEBMAILBOT_LOCKFILE path is empty, assuming first cycle.");
        }
      } catch (e) {
        if (e.code === 'ENOENT') {
          try {
            fs.closeSync(fs.openSync(process.env.WEBMAILBOT_LOCKFILE, 'w'));
          } catch (f) {
            winston.info("Unable to create empty file at WEBMAILBOT_LOCKFILE path!");
            winston.error(e);
          }
        } else {
          winston.info("Unable to load Chat ID from WEBMAILBOT_LOCKFILE path!");
          winston.error(e);
        }
      }
    }

    bot.use(commandParts());

    bot.command('start', (ctx) => {
      let isPasswordOk = (ctx.state.command.args || '') === secrets.telegram_password;
      winston.info(ctx.chat.id, ctx.from.username, '/start', isPasswordOk ? 'PASS OK' : 'PASS KO');
      ctx.reply(isPasswordOk ? '👍✅ ⇒ 🔛' : '👎🚫');
      if (isPasswordOk) {
        setCronjob(bot, ctx.chat.id);
        if (process.env.WEBMAILBOT_LOCKFILE) {
          try {
            fs.accessSync(process.env.WEBMAILBOT_LOCKFILE, fs.constants.R_OK | fs.constants.W_OK);
            fs.writeFileSync(process.env.WEBMAILBOT_LOCKFILE, ctx.chat.id);
            winston.info("Wrote Chat ID", ctx.chat.id, "to WEBMAILBOT_LOCKFILE path.");
          } catch (e) {
            winston.info("Unable to write Chat ID to WEBMAILBOT_LOCKFILE path!");
            winston.error(e);
          }
        }
      }
    });

    bot.command('stop', (ctx) => {
      let isPasswordOk = (ctx.state.command.args || '') === secrets.telegram_password;
      winston.info(ctx.chat.id, ctx.from.username, '/stop', isPasswordOk ? 'PASS OK' : 'PASS KO');
      ctx.reply(isPasswordOk ? '👍✅ ⇒ 📴' : '👎🚫');
      if (isPasswordOk) {
        cleanup(false);
      }
    });

    bot.command('status', (ctx) => {
      winston.info(ctx.chat.id, ctx.from.username, '/status');
      ctx.reply((job && job.running) ? '🔛' : '📴');
    });

    bot.command('ping', (ctx) => {
      winston.info(ctx.chat.id, ctx.from.username, '/ping');
      ctx.reply('pong!');
    });

    bot.startPolling();
  })
  .catch((e) => {
    winston.error(e);
  });

// Cleanups & Promises handling

function cleanup(exit) {
  if (job && job.running) job.stop();
  if (exit) process.exit();
}

process.on('exit', () => {
  cleanup(true);
});
process.on('SIGINT', () => {
  cleanup(true);
});
process.on('SIGUSR1', () => {
  cleanup(true);
});
process.on('SIGUSR2', () => {
  cleanup(true);
});
process.on('uncaughtException', () => {
  cleanup(true);
});

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
<commit_msg>Logging aggiuntivo su notifica nuovo messaggio<commit_after>const fs = require('fs');
const winston = require('winston');
const Telegraf = require('telegraf');
const commandParts = require('telegraf-command-parts');
const CronJob = require('cron').CronJob;
const secrets = require('./lib/secrets').load();
const {getUnreadMessages} = require('./lib/scraper');
const hashTracker = require('./lib/hashTracker');

let job;

const webmailLinkKeyboard = Telegraf.Extra
  .markdown()
  .markup((m) => m.inlineKeyboard([
    Telegraf.Markup.urlButton('Apri WebMail', 'https://webmail.studenti.unipi.it/horde/imp/')
  ]));

async function checkSecrets() {
  if (!secrets.telegram_token) {
    throw 'Non hai configurato il token del bot Telegram.';
  }
  if (!secrets.telegram_password) {
    throw 'Non hai configurato la password per il bot Telegram.';
  }
}

function formatNewMessage(message) {
  return 'Hai un nuovo messaggio! 📬\n\n' +
    `*Mittente*: ${message.from}\n` +
    `*Oggetto*: ${message.subject}\n` +
    `*Dimensione*: ${message.size}`
}

async function setCronjob(bot, chatid) {
  async function doCronjob() {
    winston.info('Running cronjob for chat ID', chatid, '...');
    let unseen = (await getUnreadMessages(secrets));
    if (unseen.length > 0) {
      for (let i = 0; i < unseen.length; i++) {
        winston.info('Found new email! From: "', message.from, '"; Subject: "',
            message.subject, '"; Size:', message.size, '.');
        let msg = formatNewMessage(unseen[i]);
        let msgHash = hashTracker.md5(msg);
        if (!hashTracker.isSeen(msgHash)) {
          bot.telegram.sendMessage(chatid, msg, webmailLinkKeyboard);
          hashTracker.setSeen(msgHash);
        }
      }
    }
    winston.info('Done running cronjob for chat ID', chatid, '.');
  }

  if (!job) {
    job = new CronJob({
      cronTime: '0 */15 * * * *',
      onTick: await doCronjob,
    });
  }
  job.start();
  await doCronjob();
}

checkSecrets()
  .then(() => {
    const bot = new Telegraf(secrets.telegram_token);

    if (process.env.WEBMAILBOT_LOCKFILE) {
      try {
        fs.accessSync(process.env.WEBMAILBOT_LOCKFILE, fs.constants.R_OK | fs.constants.W_OK);
        let chat_id = fs.readFileSync(process.env.WEBMAILBOT_LOCKFILE, 'utf8');
        if (chat_id) {
          setCronjob(bot, chat_id);
          winston.info("Loaded Chat ID", chat_id, "from WEBMAILBOT_LOCKFILE path.");
        } else {
          winston.info("Chat ID from WEBMAILBOT_LOCKFILE path is empty, assuming first cycle.");
        }
      } catch (e) {
        if (e.code === 'ENOENT') {
          try {
            fs.closeSync(fs.openSync(process.env.WEBMAILBOT_LOCKFILE, 'w'));
          } catch (f) {
            winston.info("Unable to create empty file at WEBMAILBOT_LOCKFILE path!");
            winston.error(e);
          }
        } else {
          winston.info("Unable to load Chat ID from WEBMAILBOT_LOCKFILE path!");
          winston.error(e);
        }
      }
    }

    bot.use(commandParts());

    bot.command('start', (ctx) => {
      let isPasswordOk = (ctx.state.command.args || '') === secrets.telegram_password;
      winston.info(ctx.chat.id, ctx.from.username, '/start', isPasswordOk ? 'PASS OK' : 'PASS KO');
      ctx.reply(isPasswordOk ? '👍✅ ⇒ 🔛' : '👎🚫');
      if (isPasswordOk) {
        setCronjob(bot, ctx.chat.id);
        if (process.env.WEBMAILBOT_LOCKFILE) {
          try {
            fs.accessSync(process.env.WEBMAILBOT_LOCKFILE, fs.constants.R_OK | fs.constants.W_OK);
            fs.writeFileSync(process.env.WEBMAILBOT_LOCKFILE, ctx.chat.id);
            winston.info("Wrote Chat ID", ctx.chat.id, "to WEBMAILBOT_LOCKFILE path.");
          } catch (e) {
            winston.info("Unable to write Chat ID to WEBMAILBOT_LOCKFILE path!");
            winston.error(e);
          }
        }
      }
    });

    bot.command('stop', (ctx) => {
      let isPasswordOk = (ctx.state.command.args || '') === secrets.telegram_password;
      winston.info(ctx.chat.id, ctx.from.username, '/stop', isPasswordOk ? 'PASS OK' : 'PASS KO');
      ctx.reply(isPasswordOk ? '👍✅ ⇒ 📴' : '👎🚫');
      if (isPasswordOk) {
        cleanup(false);
      }
    });

    bot.command('status', (ctx) => {
      winston.info(ctx.chat.id, ctx.from.username, '/status');
      ctx.reply((job && job.running) ? '🔛' : '📴');
    });

    bot.command('ping', (ctx) => {
      winston.info(ctx.chat.id, ctx.from.username, '/ping');
      ctx.reply('pong!');
    });

    bot.startPolling();
  })
  .catch((e) => {
    winston.error(e);
  });

// Cleanups & Promises handling

function cleanup(exit) {
  if (job && job.running) job.stop();
  if (exit) process.exit();
}

process.on('exit', () => {
  cleanup(true);
});
process.on('SIGINT', () => {
  cleanup(true);
});
process.on('SIGUSR1', () => {
  cleanup(true);
});
process.on('SIGUSR2', () => {
  cleanup(true);
});
process.on('uncaughtException', () => {
  cleanup(true);
});

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
<|endoftext|>


<commit_before>'use strict';
const path = require('path');

class FilePath {
	static get SEP() {
		return path.sep;
	}

	static get DELIMITER() {
		return path.delimiter;
	}

	constructor(p) {
		Object.defineProperties(this, {
			path: {
				enumerable: true,
				value: p
			},
			sep: {
				enumerable: true,
				value: FilePath.SEP
			},
			delimiter: {
				enumerable: true,
				value: FilePath.DELIMITER
			}
		});
	}

	resolve(...paths) {
		const strings = paths.map((p, i) => {
			if (typeof p === 'string') return p;
			if (p && typeof p.path === 'string') return p.path;
			throw new Error(`Invalid argument ${JSON.stringify(p)} at [${i}]`);
		});

		strings.unshift(this.path);

		return new FilePath(path.resolve.apply(path, strings));
	}

	relative(to) {
		if (typeof to === 'string') {
			return new FilePath(path.relative(this.path, to));
		}
		if (to && typeof to.path === 'string') {
			return new FilePath(path.relative(this.path, to.path));
		}

		throw new Error(`Invalid argument ${JSON.stringify(to)}`);
	}

	static create(...paths) {
		let thisPath;

		if (paths.length > 0 && paths[0]) {
			thisPath = paths[0];
		} else if (paths.length === 0) {
			thisPath = process.cwd();
		} else {
			const filteredPaths = paths
				.filter((str) => {
					return Boolean(str);
				})
				.map((str) => {
					return String(str);
				});

			thisPath = path.join.apply(path, filteredPaths);
		}

		return new FilePath(thisPath);
	}
}

module.exports = FilePath;
<commit_msg>Resolves #48 Adds a new .stats() method.<commit_after>'use strict';

const path = require('path');
const fs = require('fs');

class FilePath {
	static get SEP() {
		return path.sep;
	}

	static get DELIMITER() {
		return path.delimiter;
	}

	constructor(p) {
		Object.defineProperties(this, {
			path: {
				enumerable: true,
				value: p
			},
			sep: {
				enumerable: true,
				value: FilePath.SEP
			},
			delimiter: {
				enumerable: true,
				value: FilePath.DELIMITER
			}
		});
	}

	stats(options = {}) {
		return fs.statSync(this.path, options);
	}

	resolve(...paths) {
		const strings = paths.map((p, i) => {
			if (typeof p === 'string') return p;
			if (p && typeof p.path === 'string') return p.path;
			throw new Error(`Invalid argument ${JSON.stringify(p)} at [${i}]`);
		});

		strings.unshift(this.path);

		return new FilePath(path.resolve.apply(path, strings));
	}

	relative(to) {
		if (typeof to === 'string') {
			return new FilePath(path.relative(this.path, to));
		}
		if (to && typeof to.path === 'string') {
			return new FilePath(path.relative(this.path, to.path));
		}

		throw new Error(`Invalid argument ${JSON.stringify(to)}`);
	}

	static create(...paths) {
		let thisPath;

		if (paths.length > 0 && paths[0]) {
			thisPath = paths[0];
		} else if (paths.length === 0) {
			thisPath = process.cwd();
		} else {
			const filteredPaths = paths
				.filter((str) => {
					return Boolean(str);
				})
				.map((str) => {
					return String(str);
				});

			thisPath = path.join.apply(path, filteredPaths);
		}

		return new FilePath(thisPath);
	}
}

module.exports = FilePath;
<|endoftext|>


<commit_before>Object.defineProperty(exports,'__esModule',{value:!0});var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,'value'in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}();var _events=require('events'),_events2=_interopRequireDefault(_events),_immutable=require('immutable'),_immutable2=_interopRequireDefault(_immutable),_lodash=require('lodash'),_lodash2=_interopRequireDefault(_lodash),_APIError=require('./errors/APIError'),_APIError2=_interopRequireDefault(_APIError);

require('whatwg-fetch');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError('Cannot call a class as a function')}function _possibleConstructorReturn(self,call){if(!self)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return call&&('object'==typeof call||'function'==typeof call)?call:self}function _inherits(subClass,superClass){if('function'!=typeof superClass&&null!==superClass)throw new TypeError('Super expression must either be null or a function, not '+typeof superClass);subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:!1,writable:!0,configurable:!0}}),superClass&&(Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass)}var

Hunter=function(_EventEmitter){

function Hunter(){return _classCallCheck(this,Hunter),_possibleConstructorReturn(this,(Hunter.__proto__||Object.getPrototypeOf(Hunter)).call(this));

}return _inherits(Hunter,_EventEmitter),_createClass(Hunter,[{key:'off',value:function off(

a,b){
this.removeListener(a,b);
}},{key:'toGQL',value:function toGQL(

a){var _this2=this;
if(_immutable2.default.Iterable.isIterable(a))
return this.toGQL(a.toJS());

if(_lodash2.default.isString(a)||Array.isArray(a))
return JSON.stringify(a);

if(_lodash2.default.isObject(a)){var _ret=function(){
a=(0,_lodash2.default)(a).omit(_lodash2.default.isUndefined).omit(_lodash2.default.isNull).value();
var b=Object.keys(a),
c=[];

b.map(function(e){
var f=a[e];

if(_lodash2.default.isPlainObject(f))
c.push(_this2.toGQL(f));else

if(_lodash2.default.isArray(f)){
var g=f.map(function(h){
return _this2.toGQL(h);
});

c.push(e+': ['+g.join(', ')+']');
}else{
var _g=JSON.stringify(f);

_g&&
c.push(e+': '+_g);

}
});

var d=c.join(', ');return(

''===d?{v:
'""'}:{v:'{'+

c.join(', ')+'}'})}();if('object'==typeof _ret)return _ret.v;

}else
return a;

}},{key:'query',value:function query(

a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:'',d=arguments[3];

return b='query '+b,this._getGraph(a,b,c,d);
}},{key:'mutation',value:function mutation(

a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:'',d=arguments[3];

return b='mutation '+b,this._getGraph(a,b,c,d);
}},{key:'_getGraph',value:function _getGraph(

a,b){var _this3=this,c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:'',d=arguments[3];

return a=a?a.trim():'',c=c||'',d=d?{}:{'Content-Type':'application/graphql','Cache-Control':'no-cache'},''!==c&&(d.Authorization='Bearer '+c),console.log('_getGraph::headers',d),console.log('_getGraph::body',b),fetch(a,{
method:'post',
headers:d,
body:b}).

then(function(e){
var f=/application\/json/i,
g=e.headers.getAll('Content-Type').some(function(h){return f.test(h)});return(

console.log('_getGraph::response',JSON.stringify(e)),
g?
e.json():

{data:{}});

}).
catch(function(e){
if('only absolute urls are supported'===(e||{}).message)
throw new _APIError2.default(_immutable2.default.fromJS([{message:'invalid_url'}]),e);

throw new _APIError2.default(_immutable2.default.fromJS([{message:'network_error'}]),e);
}).
then(function(e){

if(console.log('_getGraph::json',e),!e||e.errors){
if(!e)
e={errors:[{message:'api_error'}]};else

if((e.errors||[]).some(function(f){return'Must provide query string.'===f.message}))
throw new _APIError2.default(_immutable2.default.fromJS([{message:'required_query'}]),new Error());

throw new _APIError2.default(_immutable2.default.fromJS(e.errors),new Error());
}else
return _immutable2.default.fromJS(e.data||{});

}).
catch(function(e){

throw e.source||(e=new _APIError2.default(_immutable2.default.fromJS([{message:'network_error'}]),e)),_this3.emit('rip_hunter_error',e),e;
});
}}]),Hunter}(_events2.default);

var hunter=new Hunter;exports.default=
hunter;<commit_msg>build<commit_after>Object.defineProperty(exports,'__esModule',{value:!0});var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,'value'in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}();var _events=require('events'),_events2=_interopRequireDefault(_events),_immutable=require('immutable'),_immutable2=_interopRequireDefault(_immutable),_lodash=require('lodash'),_lodash2=_interopRequireDefault(_lodash),_APIError=require('./errors/APIError'),_APIError2=_interopRequireDefault(_APIError);

require('whatwg-fetch');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError('Cannot call a class as a function')}function _possibleConstructorReturn(self,call){if(!self)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return call&&('object'==typeof call||'function'==typeof call)?call:self}function _inherits(subClass,superClass){if('function'!=typeof superClass&&null!==superClass)throw new TypeError('Super expression must either be null or a function, not '+typeof superClass);subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:!1,writable:!0,configurable:!0}}),superClass&&(Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass)}var

Hunter=function(_EventEmitter){

function Hunter(){return _classCallCheck(this,Hunter),_possibleConstructorReturn(this,(Hunter.__proto__||Object.getPrototypeOf(Hunter)).call(this));

}return _inherits(Hunter,_EventEmitter),_createClass(Hunter,[{key:'off',value:function off(

a,b){
this.removeListener(a,b);
}},{key:'toGQL',value:function toGQL(

a){var _this2=this;
if(_immutable2.default.Iterable.isIterable(a))
return this.toGQL(a.toJS());

if(_lodash2.default.isString(a)||Array.isArray(a))
return JSON.stringify(a);

if(_lodash2.default.isObject(a)){var _ret=function(){
a=(0,_lodash2.default)(a).omit(_lodash2.default.isUndefined).omit(_lodash2.default.isNull).value();
var b=Object.keys(a),
c=[];

b.map(function(e){
var f=a[e];

if(_lodash2.default.isPlainObject(f))
c.push(_this2.toGQL(f));else

if(_lodash2.default.isArray(f)){
var g=f.map(function(h){
return _this2.toGQL(h);
});

c.push(e+': ['+g.join(', ')+']');
}else{
var _g=JSON.stringify(f);

_g&&
c.push(e+': '+_g);

}
});

var d=c.join(', ');return(

''===d?{v:
'""'}:{v:'{'+

c.join(', ')+'}'})}();if('object'==typeof _ret)return _ret.v;

}else
return a;

}},{key:'query',value:function query(

a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:'',d=arguments[3];

return b='query '+b,this._getGraph(a,b,c,d);
}},{key:'mutation',value:function mutation(

a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:'',d=arguments[3];

return b='mutation '+b,this._getGraph(a,b,c,d);
}},{key:'_getGraph',value:function _getGraph(

a,b){var _this3=this,c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:'',d=arguments[3];

return a=a?a.trim():'',c=c||'',d=d?{}:{'Content-Type':'application/graphql','Cache-Control':'no-cache'},''!==c&&(d.Authorization='Bearer '+c),fetch(a,{
method:'post',
headers:d,
body:b}).

then(function(e){
var f=/application\/json/i,
g=e.headers.getAll('Content-Type').some(function(h){return f.test(h)});return(

g?
e.json():

{data:{}});

}).
catch(function(e){
if('only absolute urls are supported'===(e||{}).message)
throw new _APIError2.default(_immutable2.default.fromJS([{message:'invalid_url'}]),e);

throw new _APIError2.default(_immutable2.default.fromJS([{message:'network_error'}]),e);
}).
then(function(e){
if(!e||e.errors){
if(!e)
e={errors:[{message:'api_error'}]};else

if((e.errors||[]).some(function(f){return'Must provide query string.'===f.message}))
throw new _APIError2.default(_immutable2.default.fromJS([{message:'required_query'}]),new Error());

throw new _APIError2.default(_immutable2.default.fromJS(e.errors),new Error());
}else
return _immutable2.default.fromJS(e.data||{});

}).
catch(function(e){

throw e.source||(e=new _APIError2.default(_immutable2.default.fromJS([{message:'network_error'}]),e)),_this3.emit('rip_hunter_error',e),e;
});
}}]),Hunter}(_events2.default);

var hunter=new Hunter;exports.default=
hunter;<|endoftext|>


<commit_before>'use strict';

const got = require('got');
let URL;

module.exports = function (user, game) {
	if (!user) {
		throw new TypeError('Please provide a user');
	}

	game = game || '730/2/';

	if (isNaN(Number(user))) {
		URL = `http://steamcommunity.com/id/${user}/inventory/json/${game}`;
	} else {
		URL = `http://steamcommunity.com/profiles/${user}/inventory/json/${game}`;
	}

	let response = [];

	return got(URL).then(res => {
		let items;
		let desc;

		try {
			desc = JSON.parse(res.body).rgDescriptions;
			items = JSON.parse(res.body).rgInventory;
		} catch (e) {
			items = [];
			desc = {};
		}

		if (!items) {
			return response;
		}

		Object.keys(items).forEach(key => {
			let temp = items[key];
			let item = desc[`${temp.classid}_${temp.instanceid}`];

			if (!item) {
				return response.push({error: true});
			}

			let data = {
				id: temp.id,
				amount: temp.amount,
				pos: temp.pos,
				name: item.name,
				appid: item.appid,
				classid: item.classid,
				instanceid: item.instanceid,
				tradable: item.tradable,
				marketable: item.marketable,
				marketTradableRestriction: item.market_tradable_restriction,
				link: item.actions ? item.actions[0].link : null,
				image: `http://steamcommunity-a.akamaihd.net/economy/image/${item.icon_url_large || item.icon_url}`,
				category: null,
				type: null,
				exterior: null,
				quality: null,
				raw: item,
			};

			// To match http://steamcommunity.com/market/ filters names
			item.tags.forEach(tag => {
				if (tag.category === 'Type') {
					data.type = tag.name;
				}
				if (tag.category === 'Weapon') {
					data.weapon = tag.name;
				}
				if (tag.category === 'Quality') {
					data.category = tag.name;
				}
				if (tag.category === 'Exterior') {
					data.exterior = tag.name;
				}
			});

			response.push(data);
		});

		return response;
	});
};
<commit_msg>Added "gotOptions" param<commit_after>'use strict';

const got = require('got');
let URL;

module.exports = function (user, game, gotOptions) {
	if (!user) {
		throw new TypeError('Please provide a user');
	}

	game = game || '730/2/';

	if (isNaN(Number(user))) {
		URL = `http://steamcommunity.com/id/${user}/inventory/json/${game}`;
	} else {
		URL = `http://steamcommunity.com/profiles/${user}/inventory/json/${game}`;
	}

	let response = [];

	return got(URL, gotOptions).then(res => {
		let items;
		let desc;

		try {
			desc = JSON.parse(res.body).rgDescriptions;
			items = JSON.parse(res.body).rgInventory;
		} catch (e) {
			items = [];
			desc = {};
		}

		if (!items) {
			return response;
		}

		Object.keys(items).forEach(key => {
			let temp = items[key];
			let item = desc[`${temp.classid}_${temp.instanceid}`];

			if (!item) {
				return response.push({error: true});
			}

			let data = {
				id: temp.id,
				amount: temp.amount,
				pos: temp.pos,
				name: item.name,
				appid: item.appid,
				classid: item.classid,
				instanceid: item.instanceid,
				tradable: item.tradable,
				marketable: item.marketable,
				marketTradableRestriction: item.market_tradable_restriction,
				link: item.actions ? item.actions[0].link : null,
				image: `http://steamcommunity-a.akamaihd.net/economy/image/${item.icon_url_large || item.icon_url}`,
				category: null,
				type: null,
				exterior: null,
				quality: null,
				raw: item,
			};

			// To match http://steamcommunity.com/market/ filters names
			item.tags.forEach(tag => {
				if (tag.category === 'Type') {
					data.type = tag.name;
				}
				if (tag.category === 'Weapon') {
					data.weapon = tag.name;
				}
				if (tag.category === 'Quality') {
					data.category = tag.name;
				}
				if (tag.category === 'Exterior') {
					data.exterior = tag.name;
				}
			});

			response.push(data);
		});

		return response;
	});
};
<|endoftext|>


<commit_before>const raven = require('raven');
const winston = require('winston');

const Config = require('../config/Config.js');

const logger = new winston.Logger();

logger.add(winston.transports.Console, {
  timestamp: true,
  stringify: true,
  colorize: true,
});

/**
 * Configure Sentry using the env vars pased if in production
 * @param {string} sentryDSN The url given by Sentry tied to your account
 * @param {string} environment dev, testing, peoduction etc.
 * @param {string} loggingLevel error, info, warn etc.
 * @return {object} return raven object if in production
 */
function setupSentry(sentryDSN, environment, loggingLevel) {
  raven
    .config(sentryDSN, {
      logger: loggingLevel,
      environment,
    })
    .install();

  if (Config.sentryDSN) {
    return raven;
  }
  return null;
}

const sentry = Config.sentryDSN
  ? setupSentry(Config.sentryDSN, Config.environment, 'error')
  : undefined;

/**
 * A function to send rejected promise errors to both winston and sentry
 * @param {string} text the promise rejection error text
 */
function logRejectedPromise(text) {
  const message = 'Promise rejected: ' + text;
  logger.log('error', message);
  if (Config.environment === 'production') {
    sentry.captureMessage(message);
  }
}

module.exports = {
  log: logger,
  logRejectedPromise,
};
<commit_msg>export a `log` function which wraps `logger.log`<commit_after>const raven = require('raven');
const winston = require('winston');

const Config = require('../config/Config.js');

const logger = new winston.Logger();

logger.add(winston.transports.Console, {
  timestamp: true,
  stringify: true,
  colorize: true,
});

/**
 * A wrapper around logger.log because we can't export log on it's own
 * meant to be used as borq.log
 * @param {string} logLevel - The logLevel like error, warn etc.
 * @param {string} message - the message to log
 */
function log(logLevel, message) {
  if (message === undefined) {
    logger.log(logLevel);
  } else {
    logger.log(logLevel, message);
  }
}

/**
 * Configure Sentry using the env vars pased if in production
 * @param {string} sentryDSN The url given by Sentry tied to your account
 * @param {string} environment dev, testing, peoduction etc.
 * @param {string} loggingLevel error, info, warn etc.
 * @return {object} return raven object if in production
 */
function setupSentry(sentryDSN, environment, loggingLevel) {
  raven
    .config(sentryDSN, {
      logger: loggingLevel,
      environment,
    })
    .install();

  if (Config.sentryDSN) {
    return raven;
  }
  return null;
}

const sentry = Config.sentryDSN
  ? setupSentry(Config.sentryDSN, Config.environment, 'error')
  : undefined;

/**
 * A function to send rejected promise errors to both winston and sentry
 * @param {string} text the promise rejection error text
 */
function logRejectedPromise(text) {
  const message = 'Promise rejected: ' + text;
  log('error', message);
  if (Config.environment === 'production') {
    sentry.captureMessage(message);
  }
}

module.exports = {
  log,
  logRejectedPromise,
};
<|endoftext|>


<commit_before>#!/usr/bin/node

const
https = require('https'),
convict = require('convict');

var conf = convict({
  asana: {
    apiKey: {
      doc: "Asana API key",
      format: 'string'
    },
    workspaceIds: {
      doc: "List of Asana workspace IDs to use, separated by commas.",
      format: 'string'
    }
  },
  github: {
    username: {
      doc: "Github username",
      format: 'string'
    }
  },
  bugzilla: {
    email: {
      doc: "Bugzilla email",
      format: 'string'
    }
  }
}).loadFile(__dirname + '/config.json').validate();

// TODO: check https certs

// Asana: http://developer.asana.com/documentation/
function fetchAsana() {
  // TODO: query all Asana workspaces
  var workspace = 'TODO';

  var options = {
    hostname: 'app.asana.com',
    port: 443,
    path: '/api/1.0/tasks?workspace=' + workspace + '&assignee=me',
    auth: conf.get('asana').apiKey + ':',
    headers: {'Content-Type': 'application/json'},
    method: 'GET'
  };
  var req = https.request(options, function (res) {
    var taskData = '';
    res.on('data', function (chunk) {
      taskData += chunk;
    });

    res.on('end', function () {
      process.stdout.write("ASANA TASKS\n");

      var tasks = JSON.parse(taskData).data;
      tasks.forEach(function (task) {
        var taskID = task.id;
        var taskName = task.name;
        process.stdout.write("  " + taskID + ": " + taskName + "\n");
      });
    });
  });
  req.end();

  req.on('error', function (e) {
    console.error(e);
  });
}

// Github: http://developer.github.com/v3/
function fetchGithub() {
  var options = {
    hostname: 'api.github.com',
    port: 443,
    path: '/repos/mozilla/browserid/issues?assignee=' + conf.get('github').username,
    method: 'GET'
  };
  var req = https.request(options, function (res) {
    var issueData = '';
    res.on('data', function (chunk) {
      issueData += chunk;
    });

    res.on('end', function () {
      process.stdout.write("GITHUB ISSUES\n");

      var issues = JSON.parse(issueData);
      issues.forEach(function (issue) {
        var issueURL = issue.url;
        var issueTitle = issue.title;
        process.stdout.write("  " + issueURL + ": " + issueTitle + "\n");
      });
    });
  });
  req.end();

  req.on('error', function (e) {
    console.error(e);
  });
}

// Bugzilla: https://wiki.mozilla.org/Bugzilla:REST_API (and https://github.com/harthur/bz.js?)
function fetchBugzilla() {
  // TODO: fetch private bugs too
  var options = {
    hostname: 'api-dev.bugzilla.mozilla.org',
    port: 443,
    path: '/1.1/bug?email1=' + conf.get('bugzilla').email + '&email1_type=equals_any&email1_assigned_to=1&email1_qa_contact=1&email1_cc=1&status=UNCONFIRMED&status=NEW&status=ASSIGNED&status=REOPENED&include_fields=id,summary',
    method: 'GET'
  };
  var req = https.request(options, function (res) {
    var bugData = '';
    res.on('data', function (chunk) {
      bugData += chunk;
    });

    res.on('end', function () {
      process.stdout.write("BUGZILLA BUGS\n");

      var bugs = JSON.parse(bugData).bugs;
      bugs.forEach(function (bug) {
        var bugID = bug.id;
        var bugSummary = bug.summary;
        process.stdout.write("  " + bugID + ": " + bugSummary + "\n");
      });
    });
  });
  req.end();

  req.on('error', function (e) {
    console.error(e);
  });
}

//fetchAsana();
fetchGithub();
fetchBugzilla();
<commit_msg>Fetch Asana tasks from multiple workspaces<commit_after>#!/usr/bin/node

const
https = require('https'),
convict = require('convict');

var conf = convict({
  asana: {
    apiKey: {
      doc: "API key",
      format: 'string'
    },
    workspaces: {
      doc: "List of workspace IDs to use, separated by commas.",
      format: 'string'
    }
  },
  github: {
    username: {
      doc: "Github username",
      format: 'string'
    }
  },
  bugzilla: {
    email: {
      doc: "Bugzilla email",
      format: 'string'
    }
  }
}).loadFile(__dirname + '/config.json').validate();

// TODO: check https certs

var asanaTasks = [];
var asanaWorkspaceCount = 0;

function fetchAsanaWorkspace(workspace, cb) {
  var options = {
    hostname: 'app.asana.com',
    port: 443,
    path: '/api/1.0/tasks?workspace=' + workspace + '&assignee=me',
    auth: conf.get('asana').apiKey + ':',
    headers: {'Content-Type': 'application/json'},
    method: 'GET'
  };
  var req = https.request(options, function (res) {
    var taskData = '';
    res.on('data', function (chunk) {
      taskData += chunk;
    });

    res.on('end', function () {
      var tasks = JSON.parse(taskData).data;
      tasks.forEach(function (task) {
        asanaTasks.push({id: task.id, name: task.name});
      });

      asanaWorkspaceCount += 1;
      return cb();
    });
  });
  req.end();

  req.on('error', function (e) {
    console.error(e);
  });
}

// Asana: http://developer.asana.com/documentation/
function fetchAsana() {
  var workspaces = conf.get('asana').workspaces.split(',');
  workspaces.forEach(function (workspace) {
    fetchAsanaWorkspace(workspace, function () {
      if (workspaces.length != asanaWorkspaceCount) {
        return;
      }

      process.stdout.write("ASANA TASKS\n");
        asanaTasks.forEach(function (task) {
        process.stdout.write("  " + task.id + ": " + task.name + "\n");
      });
    });
  });
}

// Github: http://developer.github.com/v3/
function fetchGithub() {
  var options = {
    hostname: 'api.github.com',
    port: 443,
    path: '/repos/mozilla/browserid/issues?assignee=' + conf.get('github').username,
    method: 'GET'
  };
  var req = https.request(options, function (res) {
    var issueData = '';
    res.on('data', function (chunk) {
      issueData += chunk;
    });

    res.on('end', function () {
      process.stdout.write("GITHUB ISSUES\n");

      var issues = JSON.parse(issueData);
      issues.forEach(function (issue) {
        var issueURL = issue.url;
        var issueTitle = issue.title;
        process.stdout.write("  " + issueURL + ": " + issueTitle + "\n");
      });
    });
  });
  req.end();

  req.on('error', function (e) {
    console.error(e);
  });
}

// Bugzilla: https://wiki.mozilla.org/Bugzilla:REST_API (and https://github.com/harthur/bz.js?)
function fetchBugzilla() {
  // TODO: fetch private bugs too
  var options = {
    hostname: 'api-dev.bugzilla.mozilla.org',
    port: 443,
    path: '/1.1/bug?email1=' + conf.get('bugzilla').email + '&email1_type=equals_any&email1_assigned_to=1&email1_qa_contact=1&email1_cc=1&status=UNCONFIRMED&status=NEW&status=ASSIGNED&status=REOPENED&include_fields=id,summary',
    method: 'GET'
  };
  var req = https.request(options, function (res) {
    var bugData = '';
    res.on('data', function (chunk) {
      bugData += chunk;
    });

    res.on('end', function () {
      process.stdout.write("BUGZILLA BUGS\n");

      var bugs = JSON.parse(bugData).bugs;
      bugs.forEach(function (bug) {
        var bugID = bug.id;
        var bugSummary = bug.summary;
        process.stdout.write("  " + bugID + ": " + bugSummary + "\n");
      });
    });
  });
  req.end();

  req.on('error', function (e) {
    console.error(e);
  });
}

fetchAsana();
fetchGithub();
fetchBugzilla();
<|endoftext|>


<commit_before>import makeStore from './src/store';
import startServer from './src/server';

export const store = makeStore();
startServer(store);

<commit_msg>added documentation<commit_after>import makeStore from './src/store';
import startServer from './src/server';

/* Give startServer the REDUX store so the socket.io can
 * subscribe and emit state changes
* */
export const store = makeStore();
startServer(store);

<|endoftext|>


<commit_before>var denodeify = require('es6-denodeify')(Promise)
var tough = require('tough-cookie')

module.exports = function fetchCookieDecorator (fetch, jar) {
  fetch = fetch || window.fetch
  jar = jar || new tough.CookieJar()

  var getCookieString = denodeify(jar.getCookieString.bind(jar))
  var setCookie = denodeify(jar.setCookie.bind(jar))

  async function fetchCookie (url, opts) {
    opts = opts || {}

    // Prepare request
    const cookie = await getCookieString(typeof url === 'string' ? url : url.url)

    if (url.headers && typeof url.headers.append === 'function') {
      url.headers.append('cookie', cookie)
    } else if (opts.headers && typeof opts.headers.append === 'function') {
      opts.headers.append('cookie', cookie)
    } else {
      opts.headers = Object.assign(opts.headers || {}, cookie ? { cookie: cookie } : {})
    }

    // Actual request
    const res = await fetch(url, opts)

    // Get cookie header
    var cookies = []
    if (res.headers.getAll) {
      // node-fetch v1
      cookies = res.headers.getAll('set-cookie')
      // console.warn("You are using a fetch version that supports 'Headers.getAll' which is deprecated!")
      // console.warn("In the future 'fetch-cookie-v2' may discontinue supporting that fetch implementation.")
      // console.warn('Details: https://developer.mozilla.org/en-US/docs/Web/API/Headers/getAll')
    } else {
      // node-fetch v2
      const headers = res.headers.raw()
      if (headers['set-cookie'] !== undefined) {
        cookies = headers['set-cookie']
      }
    }

    // Store all present cookies
    await Promise.all(cookies.map(cookie => setCookie(cookie, res.url)))

    return res
  }
  fetchCookie.toughCookie = tough // Export for making you own CookieJars
  return fetchCookie
}

module.exports.toughCookie = tough // Export for making you own CookieJars
<commit_msg>formatting<commit_after>var denodeify = require('es6-denodeify')(Promise)
var tough = require('tough-cookie')

module.exports = function fetchCookieDecorator (fetch, jar) {
  fetch = fetch || window.fetch
  jar = jar || new tough.CookieJar()

  var getCookieString = denodeify(jar.getCookieString.bind(jar))
  var setCookie = denodeify(jar.setCookie.bind(jar))

  async function fetchCookie (url, opts) {
    opts = opts || {}

    // Prepare request
    const cookie = await getCookieString(typeof url === 'string' ? url : url.url)

    if (url.headers && typeof url.headers.append === 'function') {
      url.headers.append('cookie', cookie)
    } else if (opts.headers && typeof opts.headers.append === 'function') {
      opts.headers.append('cookie', cookie)
    } else {
      opts.headers = Object.assign(
        opts.headers || {},
        cookie ? { cookie: cookie } : {}
      )
    }

    // Actual request
    const res = await fetch(url, opts)

    // Get cookie header
    var cookies = []
    if (res.headers.getAll) {
      // node-fetch v1
      cookies = res.headers.getAll('set-cookie')
      // console.warn("You are using a fetch version that supports 'Headers.getAll' which is deprecated!")
      // console.warn("In the future 'fetch-cookie-v2' may discontinue supporting that fetch implementation.")
      // console.warn('Details: https://developer.mozilla.org/en-US/docs/Web/API/Headers/getAll')
    } else {
      // node-fetch v2
      const headers = res.headers.raw()
      if (headers['set-cookie'] !== undefined) {
        cookies = headers['set-cookie']
      }
    }

    // Store all present cookies
    await Promise.all(cookies.map(cookie => setCookie(cookie, res.url)))

    return res
  }
  fetchCookie.toughCookie = tough // Export for making you own CookieJars
  return fetchCookie
}

module.exports.toughCookie = tough // Export for making you own CookieJars
<|endoftext|>