<commit_before>var request = require('request'),
    _ = require('lodash'),
    crypto = require('crypto'),
    url = require('url');

// TODO: Check validity of callback functions before using.

function RTMClient(options) {
    if (options == null) {
        options = {};
    }

    this.apiKey = options.apiKey || null;
    this.secret = options.secret || null;
}

RTMClient.prototype.toJSON = function() {
    return JSON.stringify({
        apiKey: this.apiKey,
        secret: this.secret
    });
};

RTMClient.prototype.getAPISig = function(method, options) {
    if (this.secret == null) {
        return null;
    }

    if (typeof method === 'object') {
        options = method;
        method = null;
    }

    if (options == null) {
        options = {};
    }

    var md5 = crypto.createHash('md5'),
        input = this.secret + Object.keys(options).sort().reduce(function(hash, key) {
            return hash + key + options[key];
        }, ''),
        hash = md5.update(input).digest('hex');

    // console.log('Input:', input);

    return hash;
};

RTMClient.prototype.makeRequest = function(method, options, callback) {
    if (options == null) {
        options = {};
    }

    var format = options.format || (options.json === false ? 'xml' : 'json'),
        qs = _.extend({}, options, {
            method: method,
            format: format,
            api_key: this.apiKey
        });

    // console.log('QueryString:', qs);

    qs.api_sig = this.getAPISig(qs);

    request.post({
        url: 'https://api.rememberthemilk.com/services/rest/',
        qs: qs
    }, function (err, response, body) {
        body = JSON.parse(body).rsp;

        if (body.stat === 'fail') {
            callback(new Error(body.err.msg), null);
        } else {
            callback(err, body);
        }
    });
};

RTMClient.prototype.makePrivRequest = function(method, token, options, callback) {
    return this.makeRequest(method, _.extend({}, options, {
        auth_token: token
    }), callback);
};

RTMClient.prototype.makeTimelineRequest = function(method, token, timeline, options, callback) {
    return this.makePrivRequest(method, token, _.extend({}, options, {
        timeline: timeline
    }), callback);
};

RTMClient.prototype.echo = function(options, callback) {
    this.makeRequest('rtm.test.echo', options, callback);
};

RTMClient.prototype.getFrob = function(callback) {
    this.makeRequest('rtm.auth.getFrob', {}, function (err, data) {
        if (err) {
            callback(err, null);
        } else {<commit_msg>Catching bad response data from requests.<commit_after>var request = require('request'),
    _ = require('lodash'),
    crypto = require('crypto'),
    url = require('url');

// TODO: Check validity of callback functions before using.

function RTMClient(options) {
    if (options == null) {
        options = {};
    }

    this.apiKey = options.apiKey || null;
    this.secret = options.secret || null;
}

RTMClient.prototype.toJSON = function() {
    return JSON.stringify({
        apiKey: this.apiKey,
        secret: this.secret
    });
};

RTMClient.prototype.getAPISig = function(method, options) {
    if (this.secret == null) {
        return null;
    }

    if (typeof method === 'object') {
        options = method;
        method = null;
    }

    if (options == null) {
        options = {};
    }

    var md5 = crypto.createHash('md5'),
        input = this.secret + Object.keys(options).sort().reduce(function(hash, key) {
            return hash + key + options[key];
        }, ''),
        hash = md5.update(input).digest('hex');

    // console.log('Input:', input);

    return hash;
};

RTMClient.prototype.makeRequest = function(method, options, callback) {
    if (options == null) {
        options = {};
    }

    var format = options.format || (options.json === false ? 'xml' : 'json'),
        qs = _.extend({}, options, {
            method: method,
            format: format,
            api_key: this.apiKey
        });

    // console.log('QueryString:', qs);

    qs.api_sig = this.getAPISig(qs);

    request.post({
        url: 'https://api.rememberthemilk.com/services/rest/',
        qs: qs
    }, function (err, response, body) {
        if (err) {
            callback(err, null);
            return;
        }

        try {
            body = JSON.parse(body).rsp;
        } catch (e) {
            // TODO: Do something with e.
            callback(null, body);
            return;
        }

        if (body.stat === 'fail') {
            callback(new Error(body.err.msg), null);
        } else {
            callback(null, body);
        }
    });
};

RTMClient.prototype.makePrivRequest = function(method, token, options, callback) {
    return this.makeRequest(method, _.extend({}, options, {
        auth_token: token
    }), callback);
};

RTMClient.prototype.makeTimelineRequest = function(method, token, timeline, options, callback) {
    return this.makePrivRequest(method, token, _.extend({}, options, {
        timeline: timeline
    }), callback);
};

RTMClient.prototype.echo = function(options, callback) {
    this.makeRequest('rtm.test.echo', options, callback);
};

RTMClient.prototype.getFrob = function(callback) {
    this.makeRequest('rtm.auth.getFrob', {}, function (err, data) {
        if (err) {
            callback(err, null);
        } else {
{'fraction_non_alphanumeric': 0.15328874024526198, 'fraction_numerical': 0.0011148272017837235, 'mean_word_length': 2.326946847960445, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1916516', 'n_tokens_mistral': 1638, 'n_tokens_neox': 1597, 'n_words': 349}