<commit_before>var Q = require('q');
var async = require('async');
var url = require('url');
var cheerio = require('cheerio');
var fs = require('fs');
var request = require('request');
var events = require('events');

module.exports = {
    // TODO: Add documentation
    _logger: null,

    // TODO: Document result object properly
    /**
     * Iterates through a set of HTML resources, to check for matches of given
     * CSS selectors.
     *
     * @param {Array}  pages HTML pages to search through. This can be either an
     *                 array of Strings (containing html code), an array of URIs
     *                 to visit, or an array of paths to html files.
     * @param {Object} result Result object
     * @param {Object} headers Headers to send to server.
     * @param {String} cookie Cookie to use for login, on the form
     *                 "sessionid=foo". Each uri in the html parameter will
     *                 be visited both with and without the cookie set.
     * @param {Int}    timeout Request timeout
     * @param {Function} logger Custom log function
     * @returns {Promise} Result object
     */
    matchSelectors: function(pages, result, headers, cookie, timeout, logger) {
        this._logger = new events.EventEmitter();
        this._logger.on('request', logger);

        var deferred = Q.defer();
        var processed = []; // Array of processed items/pages
        var i, l;

        // Handle excludes by adding them to processed, so they won't be visited
        var excludedSubfolders = [];
        if (pages.exclude) {
            for (i=0, l=pages.exclude.length; i<l; i++) {
                var current = pages.exclude[i];
                if (current.indexOf("*") !== -1) {
                    var subfolder = current.substring(0, current.length - 1);
                    if (subfolder.indexOf("http") === 0) {
                        subfolder = url.parse(subfolder).pathname;
                    }
                    excludedSubfolders.push(subfolder);
                    continue;
                }

                processed.push(current.split("?")[0]);
            }
        }

        var queueItemHandler = this._queueItemHandler.bind(this);
        var queue = async.queue(function(item, queueCallback) {
            setImmediate(function() {
                queueItemHandler(item, processed, result, headers, timeout,
                                 cookie, excludedSubfolders, queue,
                                 queueCallback);
            });
        }, 8);

        queue.drain = function(err) { // TODO: Handle err
            if (err) {
                deferred.reject(new Error(err));
            } else {
                deferred.resolve(result);
            }
        };

        // Crawl to find all HTML links
        if (pages.crawl) {
            for (i=0, l=pages.crawl.length; i<l; i++) {
                // Strip away parameters when crawling
                var page = pages.crawl[i].split("?")[0];

                queue.push({page: page, followLinks: true});
            }
        }

        // Add pages included explicitly (e.g. in config) to queue
        if (pages.include) {
            for (i=0, l=pages.include.length; i<l; i++) {
                // Add to queue, with parameters
                queue.push({page: pages.include[i], followLinks: false});
            }
        }

        return deferred.promise;
    },

    /**
     * Handle an item in the queue of html documents to check.
     *
     * @param {Object} item HTML to process
     * @param {Object} processed Array of already processed items/pages
     * @param {Object} result Result object
     * @param {Object} headers Headers to send to server.
     * @param {String} cookie Cookie to use for login, on the form
     *                 "sessionid=foo". Each uri in the html parameter will
     *                 be visited both with and without the cookie set.
     * @param {Int}    timeout Request timeout
     * @param {Array}  excludedSubfolders List of sub domains to be excluded
     *                 when crawling.
     * @param {Object} queue Queue object from async, used to queue up new
     *                 items found during crawl.
     * @param {Function} queueCallback Callback to call when done.
     */

    _queueItemHandler: function(item, processed, result, headers, timeout,
                                cookie, excludedSubfolders, queue,
                                queueCallback) {
        // If in processed, skip (may have been in excluded list)
        if (-1 !== processed.indexOf(item.page)) {
            queueCallback();
            return;
        }

        var page = item.page;
        var uri, host;
        if (0 === page.indexOf("http")) {
            uri = page;
            host = url.parse(uri).host || "";
        }

        // Gather all html resources as strings
        var htmlToCheck = [];
        var html = this._getHtmlAsString(page, result, headers, null, timeout);
        if (html) {
            htmlToCheck.push(html); // regular visit
        }
        if (cookie) {
            html = this._getHtmlAsString(page, result, headers, cookie, timeout);
            if (html) {
                htmlToCheck.push(html); // logged in visit
            }
        }
        processed.push(page);

        // TODO: Add documentation - why spread here???
        // Process html strings to match selectors
        var queueLinks = this._queueLinks.bind(this);
        var matchSelectorsInDocument = this._matchSelectorsInDocument.bind(this);

        Q.spread(htmlToCheck, function(regularResult, loggedInResult) {
            var context = {
                uri: uri,
                followLinks: item.followLinks,
                result: result,
                excludedSubfolders: excludedSubfolders
            };

            var document;
            if (regularResult) {
                document = cheerio.load(regularResult);

                matchSelectorsInDocument(document, result);
                if (context.followLinks) { // look for links in document, add to queue
                    queueLinks(document, queue, context);
                }
            }

            if (loggedInResult) {
                document = cheerio.load(loggedInResult);

                matchSelectorsInDocument(document, result);
                if (context.followLinks) { // look for links in document, add to queue
                    queueLinks(document, queue, context);
                }
            }
        }).fail(function(error) {
            console.error('Unable to read %s: %s', uri, error);
            console.log(error.stack);
        }).done(queueCallback);
    },

    /**
     * Finds links in a HTML string and adds them to queue of pages to
     * process.
     *
     * Will also find links and queue them for later processing (if
     * context.followLinks is true)
     *
     * @private
     * @param {Object}  document HTML as Cheerio document.
     * @param {Object}  queue Queue object, as returned from the Async library.
     * @param {Object}  context An object containing the context information:
     * @param {String}  context.uri Original URI to the html string
     * @param {Boolean} context.followLinks If links in html string should be followed
     * @param {Array}   context.selectors List of selectors to match
     * @param {Array}   context.excludedSubfolders List of subfolders to ignore
     * @param {Object}  context.result Object to append results to
     * @param {Object}  context.result.used E.g. {".foo": 1 }
     * @param {Object}  context.result.ignored E.g. {"@font-face": 1}
     */
    _queueLinks: function(document, queue, context) {
        var uri = context.uri;
        var excludedSubfolders = context.excludedSubfolders;
        var followLinks = context.followLinks;

        var links = document("a");
        for (var i=0, l=links.length; i<l; i++) {
            var link = links[i];

            if (!('href' in link.attribs)) {
                continue;
            }

            var href = link.attribs.href.split("#")[0];

            var protocol = url.parse(href).protocol
                ? url.parse(href).protocol : "http:";

            // If no href, URI is parameter only, or protocol is not http,
            // then skip.
            if (!href
                || href.indexOf("?") === 0
                || protocol.indexOf("http") === -1) {
                continue;
            }

            // If under excluded domain, skip
            var handleThis = true;
            for (var j=0; j<excludedSubfolders.length; j++) {
                var excluded = excludedSubfolders[j];
                if (href.indexOf(excluded) === 0 ||
                    url.parse(href).pathname.indexOf(excluded) === 0) {

                    handleThis = false;
                    break;
                }
            }

            if (handleThis) {
                this._addLinkToQueue(link.attribs.href, queue, uri,
                                     followLinks);
            }
        }
        // Match selectors in current string
    },

    /**
     * Add link to queue (but only if it's on the same host as parentUri).
     *
     * @private
     * @param {String}  link Link to handle.
     * @param {Object}  queue Queue object, as returned from the Async library.
     * @param {String}  parentUri URL to document containing this link.
     * @param {Boolean} followLinks Should links in the document link refers to
     *                  be followed?
     */
    _addLinkToQueue: function(link, queue, parentUri, followLinks) {
        var host = url.parse(parentUri).host;

        // Strip away parameters when crawling
        link = link.split("?")[0];

        if (0 === link.indexOf("http")) { // Absolute url
            if (url.parse(link).host === host) {
                queue.push({page: link, followLinks: followLinks});
            } else {
                // Skip, another domain
            }
        } else { // Relative url?
            if (parentUri) { // Yes
                link = url.resolve(parentUri, link);
                queue.push({page: link, followLinks: followLinks});
            } else { // What Is This Thing Called Link?
                console.log("Could not resolve " + link);
            }
        }
    },

    // TODO: Document result object properly
    /**
     * Match selectors loaded into a Cheerio HTML document.
     *
     * @private
     * @param {Object} document HTML as Cheerio document.
     * @param {Object} result Result object.
     */
    _matchSelectorsInDocument: function(document, result) {
        // Loop through selectors
        for (var selector in result.selectors) {
            // If current selector is whitelisted, skip.
            if (result.selectors[selector].whitelisted) {
                continue;
            }

            if (selector) {
                if (-1 < selector.indexOf("@")) {
                    result.selectors[selector].ignored = true;

                    continue;
                }

                var oSelector = selector;
                // Remove pseudo part of selector
                selector = selector.split(":")[0];
                // Check if selector is used
                try {
                    var len = document(selector).length;
                    if (len > 0) {
                        // Increment total number of selectors used
                        if (result.selectors[oSelector].matches_html == 0) {
                            result.total_used++;
                        }

                        // Add number of matches for selector to total matches
                        result.selectors[oSelector].matches_html =
                            result.selectors[oSelector].matches_html + len;
                    }
                } catch (error) {
                    console.log("Problem with selector: "
                                + oSelector);
                }
            }
        }
        return;
    },

    /**
     * Match selectors in a html string.
     *
     * @private
     * @param {String} page URL, path or HTML as string.
     * @param {Object} result Result object
     * @param {Object} headers Headers to send to server.
     * @param {Array}  cookie Cookie to use for login, on the form
     *                 "sessionid=foo". Each uri in the html parameter will
     *                 be visited both with and without the cookie set.
     * @param {Int}    timeout Request timeout
     * @returns {Promise} HTML as string
     */
    _getHtmlAsString: function(page, result, headers, cookie, timeout) {
        var deferred = Q.defer();
        var data;

        // Get page as raw html
        // If URI is given, fetch HTML
        //
        // Note: _addLinkToQueue adds host etc. to relative URLs when crawling.
        if (0 === page.indexOf("http")) { // From URI
            var uri = page;

            var loggedIn = false;
            if (cookie) {
                headers["Cookie"] = cookie;
                headers["Referer"] = uri;

                loggedIn = true;
            }

            var options = { uri: uri,
                            headers: headers,
                            timeout: timeout || 10000,
                            pool: false };

            var self = this;

            request.get(options, function(error, res, data) {
                if (res && res.statusCode !== 200) {
                    self._logger.emit('request', res, uri, loggedIn);
                    result.load_errors.push({uri:uri, error: res.statusCode});
                } else if (error) {
                    if (error.toString().indexOf("TIMEDOUT" > -1)) {
                        self._logger.emit('request', null, uri, loggedIn, "Timeout");
                    } else {
                        self._logger.emit('request', null, uri, loggedIn, error);
                    }
                    result.load_errors.push({uri:uri, error: error});
                    data = "";
                } else {
                    // Check content type, ignore if not html (trust server).
                    // TODO: Look into ways of improving this further, as
                    // server may not be telling the truth.
                    var contentType = res.headers["content-type"];
                    if (contentType.indexOf("text/html") === -1) {
                        data = "";
                    } else {
                        self._logger.emit('request', res, uri, loggedIn);
                    }
                }

                deferred.resolve(data);
            });
        } else if (-1 === page.indexOf("<html>")) { // From file
            try {
                data = fs.readFileSync(page).toString();
            } catch (error) {
                console.error("Unable to read %s: %s", page, error.message);
            }
            deferred.resolve(data);
        } else { // From string
            deferred.resolve(page);
        }

        return deferred.promise;
    }
};
<commit_msg>Improve error message on selector error.<commit_after>var Q = require('q');
var async = require('async');
var url = require('url');
var cheerio = require('cheerio');
var fs = require('fs');
var request = require('request');
var events = require('events');

// Colors
var RED = "\033[31m";
var RESET = "\033[0m";

module.exports = {
    // TODO: Add documentation
    _logger: null,

    // TODO: Document result object properly
    /**
     * Iterates through a set of HTML resources, to check for matches of given
     * CSS selectors.
     *
     * @param {Array}  pages HTML pages to search through. This can be either an
     *                 array of Strings (containing html code), an array of URIs
     *                 to visit, or an array of paths to html files.
     * @param {Object} result Result object
     * @param {Object} headers Headers to send to server.
     * @param {String} cookie Cookie to use for login, on the form
     *                 "sessionid=foo". Each uri in the html parameter will
     *                 be visited both with and without the cookie set.
     * @param {Int}    timeout Request timeout
     * @param {Function} logger Custom log function
     * @returns {Promise} Result object
     */
    matchSelectors: function(pages, result, headers, cookie, timeout, logger) {
        this._logger = new events.EventEmitter();
        this._logger.on('request', logger);

        var deferred = Q.defer();
        var processed = []; // Array of processed items/pages
        var i, l;

        // Handle excludes by adding them to processed, so they won't be visited
        var excludedSubfolders = [];
        if (pages.exclude) {
            for (i=0, l=pages.exclude.length; i<l; i++) {
                var current = pages.exclude[i];
                if (current.indexOf("*") !== -1) {
                    var subfolder = current.substring(0, current.length - 1);
                    if (subfolder.indexOf("http") === 0) {
                        subfolder = url.parse(subfolder).pathname;
                    }
                    excludedSubfolders.push(subfolder);
                    continue;
                }

                processed.push(current.split("?")[0]);
            }
        }

        var queueItemHandler = this._queueItemHandler.bind(this);
        var queue = async.queue(function(item, queueCallback) {
            setImmediate(function() {
                queueItemHandler(item, processed, result, headers, timeout,
                                 cookie, excludedSubfolders, queue,
                                 queueCallback);
            });
        }, 8);

        queue.drain = function(err) { // TODO: Handle err
            if (err) {
                deferred.reject(new Error(err));
            } else {
                deferred.resolve(result);
            }
        };

        // Crawl to find all HTML links
        if (pages.crawl) {
            for (i=0, l=pages.crawl.length; i<l; i++) {
                // Strip away parameters when crawling
                var page = pages.crawl[i].split("?")[0];

                queue.push({page: page, followLinks: true});
            }
        }

        // Add pages included explicitly (e.g. in config) to queue
        if (pages.include) {
            for (i=0, l=pages.include.length; i<l; i++) {
                // Add to queue, with parameters
                queue.push({page: pages.include[i], followLinks: false});
            }
        }

        return deferred.promise;
    },

    /**
     * Handle an item in the queue of html documents to check.
     *
     * @param {Object} item HTML to process
     * @param {Object} processed Array of already processed items/pages
     * @param {Object} result Result object
     * @param {Object} headers Headers to send to server.
     * @param {String} cookie Cookie to use for login, on the form
     *                 "sessionid=foo". Each uri in the html parameter will
     *                 be visited both with and without the cookie set.
     * @param {Int}    timeout Request timeout
     * @param {Array}  excludedSubfolders List of sub domains to be excluded
     *                 when crawling.
     * @param {Object} queue Queue object from async, used to queue up new
     *                 items found during crawl.
     * @param {Function} queueCallback Callback to call when done.
     */

    _queueItemHandler: function(item, processed, result, headers, timeout,
                                cookie, excludedSubfolders, queue,
                                queueCallback) {
        // If in processed, skip (may have been in excluded list)
        if (-1 !== processed.indexOf(item.page)) {
            queueCallback();
            return;
        }

        var page = item.page;
        var uri, host;
        if (0 === page.indexOf("http")) {
            uri = page;
            host = url.parse(uri).host || "";
        }

        // Gather all html resources as strings
        var htmlToCheck = [];
        var html = this._getHtmlAsString(page, result, headers, null, timeout);
        if (html) {
            htmlToCheck.push(html); // regular visit
        }
        if (cookie) {
            html = this._getHtmlAsString(page, result, headers, cookie, timeout);
            if (html) {
                htmlToCheck.push(html); // logged in visit
            }
        }
        processed.push(page);

        // TODO: Add documentation - why spread here???
        // Process html strings to match selectors
        var queueLinks = this._queueLinks.bind(this);
        var matchSelectorsInDocument = this._matchSelectorsInDocument.bind(this);

        Q.spread(htmlToCheck, function(regularResult, loggedInResult) {
            var context = {
                uri: uri,
                followLinks: item.followLinks,
                result: result,
                excludedSubfolders: excludedSubfolders
            };

            var document;
            if (regularResult) {
                document = cheerio.load(regularResult);

                matchSelectorsInDocument(document, result);
                if (context.followLinks) { // look for links in document, add to queue
                    queueLinks(document, queue, context);
                }
            }

            if (loggedInResult) {
                document = cheerio.load(loggedInResult);

                matchSelectorsInDocument(document, result);
                if (context.followLinks) { // look for links in document, add to queue
                    queueLinks(document, queue, context);
                }
            }
        }).fail(function(error) {
            console.error('Unable to read %s: %s', uri, error);
            console.log(error.stack);
        }).done(queueCallback);
    },

    /**
     * Finds links in a HTML string and adds them to queue of pages to
     * process.
     *
     * Will also find links and queue them for later processing (if
     * context.followLinks is true)
     *
     * @private
     * @param {Object}  document HTML as Cheerio document.
     * @param {Object}  queue Queue object, as returned from the Async library.
     * @param {Object}  context An object containing the context information:
     * @param {String}  context.uri Original URI to the html string
     * @param {Boolean} context.followLinks If links in html string should be followed
     * @param {Array}   context.selectors List of selectors to match
     * @param {Array}   context.excludedSubfolders List of subfolders to ignore
     * @param {Object}  context.result Object to append results to
     * @param {Object}  context.result.used E.g. {".foo": 1 }
     * @param {Object}  context.result.ignored E.g. {"@font-face": 1}
     */
    _queueLinks: function(document, queue, context) {
        var uri = context.uri;
        var excludedSubfolders = context.excludedSubfolders;
        var followLinks = context.followLinks;

        var links = document("a");
        for (var i=0, l=links.length; i<l; i++) {
            var link = links[i];

            if (!('href' in link.attribs)) {
                continue;
            }

            var href = link.attribs.href.split("#")[0];

            var protocol = url.parse(href).protocol
                ? url.parse(href).protocol : "http:";

            // If no href, URI is parameter only, or protocol is not http,
            // then skip.
            if (!href
                || href.indexOf("?") === 0
                || protocol.indexOf("http") === -1) {
                continue;
            }

            // If under excluded domain, skip
            var handleThis = true;
            for (var j=0; j<excludedSubfolders.length; j++) {
                var excluded = excludedSubfolders[j];
                if (href.indexOf(excluded) === 0 ||
                    url.parse(href).pathname.indexOf(excluded) === 0) {

                    handleThis = false;
                    break;
                }
            }

            if (handleThis) {
                this._addLinkToQueue(link.attribs.href, queue, uri,
                                     followLinks);
            }
        }
        // Match selectors in current string
    },

    /**
     * Add link to queue (but only if it's on the same host as parentUri).
     *
     * @private
     * @param {String}  link Link to handle.
     * @param {Object}  queue Queue object, as returned from the Async library.
     * @param {String}  parentUri URL to document containing this link.
     * @param {Boolean} followLinks Should links in the document link refers to
     *                  be followed?
     */
    _addLinkToQueue: function(link, queue, parentUri, followLinks) {
        var host = url.parse(parentUri).host;

        // Strip away parameters when crawling
        link = link.split("?")[0];

        if (0 === link.indexOf("http")) { // Absolute url
            if (url.parse(link).host === host) {
                queue.push({page: link, followLinks: followLinks});
            } else {
                // Skip, another domain
            }
        } else { // Relative url?
            if (parentUri) { // Yes
                link = url.resolve(parentUri, link);
                queue.push({page: link, followLinks: followLinks});
            } else { // What Is This Thing Called Link?
                console.log("Could not resolve " + link);
            }
        }
    },

    // TODO: Document result object properly
    /**
     * Match selectors loaded into a Cheerio HTML document.
     *
     * @private
     * @param {Object} document HTML as Cheerio document.
     * @param {Object} result Result object.
     */
    _matchSelectorsInDocument: function(document, result) {
        // Loop through selectors
        for (var selector in result.selectors) {
            // If current selector is whitelisted, skip.
            if (result.selectors[selector].whitelisted) {
                continue;
            }

            if (selector) {
                if (-1 < selector.indexOf("@")) {
                    result.selectors[selector].ignored = true;

                    continue;
                }

                var oSelector = selector;
                // Remove pseudo part of selector
                selector = selector.split(":")[0];
                // Check if selector is used
                try {
                    var len = document(selector).length;
                    if (len > 0) {
                        // Increment total number of selectors used
                        if (result.selectors[oSelector].matches_html == 0) {
                            result.total_used++;
                        }

                        // Add number of matches for selector to total matches
                        result.selectors[oSelector].matches_html =
                            result.selectors[oSelector].matches_html + len;
                    }
                } catch (error) {
                    console.log(RED + "Selector: \"" +
                                oSelector + "\" has " + error.name +
                                ". " + error.message + RESET);
                }
            }
        }
        return;
    },

    /**
     * Match selectors in a html string.
     *
     * @private
     * @param {String} page URL, path or HTML as string.
     * @param {Object} result Result object
     * @param {Object} headers Headers to send to server.
     * @param {Array}  cookie Cookie to use for login, on the form
     *                 "sessionid=foo". Each uri in the html parameter will
     *                 be visited both with and without the cookie set.
     * @param {Int}    timeout Request timeout
     * @returns {Promise} HTML as string
     */
    _getHtmlAsString: function(page, result, headers, cookie, timeout) {
        var deferred = Q.defer();
        var data;

        // Get page as raw html
        // If URI is given, fetch HTML
        //
        // Note: _addLinkToQueue adds host etc. to relative URLs when crawling.
        if (0 === page.indexOf("http")) { // From URI
            var uri = page;

            var loggedIn = false;
            if (cookie) {
                headers["Cookie"] = cookie;
                headers["Referer"] = uri;

                loggedIn = true;
            }

            var options = { uri: uri,
                            headers: headers,
                            timeout: timeout || 10000,
                            pool: false };

            var self = this;

            request.get(options, function(error, res, data) {
                if (res && res.statusCode !== 200) {
                    self._logger.emit('request', res, uri, loggedIn);
                    result.load_errors.push({uri:uri, error: res.statusCode});
                } else if (error) {
                    if (error.toString().indexOf("TIMEDOUT" > -1)) {
                        self._logger.emit('request', null, uri, loggedIn, "Timeout");
                    } else {
                        self._logger.emit('request', null, uri, loggedIn, error);
                    }
                    result.load_errors.push({uri:uri, error: error});
                    data = "";
                } else {
                    // Check content type, ignore if not html (trust server).
                    // TODO: Look into ways of improving this further, as
                    // server may not be telling the truth.
                    var contentType = res.headers["content-type"];
                    if (contentType.indexOf("text/html") === -1) {
                        data = "";
                    } else {
                        self._logger.emit('request', res, uri, loggedIn);
                    }
                }

                deferred.resolve(data);
            });
        } else if (-1 === page.indexOf("<html>")) { // From file
            try {
                data = fs.readFileSync(page).toString();
            } catch (error) {
                console.error("Unable to read %s: %s", page, error.message);
            }
            deferred.resolve(data);
        } else { // From string
            deferred.resolve(page);
        }

        return deferred.promise;
    }
};
<|endoftext|>


<commit_before>/**
 * Various HTTP helper functions and defaults.
 */

'use strict';

/**
 * Module dependencies.
 */

var crypto = require('crypto');
var debug = require('debug')('swagger-framework:http');
var fresh = require('fresh');
var http = require('http');
var querystring = require('querystring');

/**
 * Default charset.
 */

var CHARSET = 'utf8';

/**
 * Encoders/Decoderes
 */

var encoder = {
  'application/json': JSON.stringify,
  'application/x-www-form-urlencoded': querystring.stringify,
};

var decoder = {
  'application/json': JSON.parse,
  'application/x-www-form-urlencoded': querystring.parse,
};

/**
 * Reply to options request
 */

function options(req, res, methods) {
  res.setHeader('Allow', methods.join(' '));
}

/**
 * Reply to request
 */

function reply(req, res, opts) {
  opts = opts || {};

  return function(statusCode, body) {
    var call = { args: [].slice.call(arguments) };

    // handle single argument replies
    if (typeof statusCode === 'string') {
      body = { message: statusCode };
      statusCode = 200;
    } else if (statusCode instanceof Error) {
      body = statusCode;
      statusCode = 500;
    }

    // handle error objects in replies
    if (body instanceof Error) {
      if (typeof body.statusCode === 'number') {
        statusCode = body.statusCode;
      }
      if (body.expose) {
        body = body.toJSON ? body.toJSON() : { message: body.message };
      } else {
        body = undefined;
      }
    }

    // default message
    if (body === undefined) {
      body = { message: http.STATUS_CODES[statusCode] || 'unknown response' };
    }

    call.statusCode = statusCode;
    call.body = body;

    // allow user to override body before sending
    if (req.sf.responseMessage) {
      call = req.sf.responseMessage(call);

      if (!call) {
        debug('responseMessage handled reply');
        return;
      }

      statusCode = call.statusCode;
      body = call.body;
    }

    var length = 0;
    var type;

    if (body !== undefined) {
      if (Buffer.isBuffer(body)) {
        length = body.length;
      } else if (res.sf && res.sf.produce) {
        type = res.sf.produce.mime;
        body = res.sf.produce.encoder(body);
        length = Buffer.byteLength(body, CHARSET);
      } else {
        type = 'application/json';
        body = encoder[type](body);
        length = Buffer.byteLength(body, CHARSET);
      }
    }

    if (type && !res.getHeader('content-type')) {
      res.setHeader('content-type', type + '; charset=' + CHARSET);
    }

    if (!res.getHeader('content-length')) {
      res.setHeader('content-length', length);
    }

    // add etag header if not already set
    if (opts.etag && body && body.length && !res.getHeader('etag') &&
        req.method === 'GET' && Math.floor(statusCode / 100) === 2) {
      var etag = crypto.createHash('md5').update(body).digest('hex');
      res.setHeader('etag', etag);
    }

    // if cache is fresh set status code to 304
    if (fresh(req.headers, res._headers)) {
      statusCode = 304;
    }

    if (statusCode === 204 || statusCode === 304) {
      res.removeHeader('content-type');
      res.removeHeader('content-length');
      res.removeHeader('transfer-encoding');
      body = '';
    }

    res.statusCode = statusCode;

    return res.end(req.method === 'HEAD' ? null : body);
  };
}

/**
 * Strip slash
 */

function stripSlash(path) {
  if (path[path.length - 1] === '/') {
    return path.slice(0, path.length - 1);
  }

  return path;
}

/**
 * Expose http
 */

exports.CHARSET = CHARSET;
exports.STATUS_CODES = http.STATUS_CODES;
exports.decoder = decoder;
exports.encoder = encoder;
exports.options = options;
exports.reply = reply;
exports.stripSlash = stripSlash;
<commit_msg>Don't send headers if already sent<commit_after>/**
 * Various HTTP helper functions and defaults.
 */

'use strict';

/**
 * Module dependencies.
 */

var crypto = require('crypto');
var debug = require('debug')('swagger-framework:http');
var fresh = require('fresh');
var http = require('http');
var querystring = require('querystring');

/**
 * Default charset.
 */

var CHARSET = 'utf8';

/**
 * Encoders/Decoderes
 */

var encoder = {
  'application/json': JSON.stringify,
  'application/x-www-form-urlencoded': querystring.stringify,
};

var decoder = {
  'application/json': JSON.parse,
  'application/x-www-form-urlencoded': querystring.parse,
};

/**
 * Reply to options request
 */

function options(req, res, methods) {
  res.setHeader('Allow', methods.join(' '));
}

/**
 * Reply to request
 */

function reply(req, res, opts) {
  opts = opts || {};

  return function(statusCode, body) {
    var call = { args: [].slice.call(arguments) };

    // handle single argument replies
    if (typeof statusCode === 'string') {
      body = { message: statusCode };
      statusCode = 200;
    } else if (statusCode instanceof Error) {
      body = statusCode;
      statusCode = 500;
    }

    // handle error objects in replies
    if (body instanceof Error) {
      if (typeof body.statusCode === 'number') {
        statusCode = body.statusCode;
      }
      if (body.expose) {
        body = body.toJSON ? body.toJSON() : { message: body.message };
      } else {
        body = undefined;
      }
    }

    // default message
    if (body === undefined) {
      body = { message: http.STATUS_CODES[statusCode] || 'unknown response' };
    }

    call.statusCode = statusCode;
    call.body = body;

    // allow user to override body before sending
    if (req.sf.responseMessage) {
      call = req.sf.responseMessage(call);

      if (!call) {
        debug('responseMessage handled reply');
        return;
      }

      statusCode = call.statusCode;
      body = call.body;
    }

    var length = 0;
    var type;

    if (body !== undefined) {
      if (Buffer.isBuffer(body)) {
        length = body.length;
      } else if (res.sf && res.sf.produce) {
        type = res.sf.produce.mime;
        body = res.sf.produce.encoder(body);
        length = Buffer.byteLength(body, CHARSET);
      } else {
        type = 'application/json';
        body = encoder[type](body);
        length = Buffer.byteLength(body, CHARSET);
      }
    }

    var noContent = statusCode === 204 || statusCode === 304;

    if (!noContent && !res.headersSent) {
      if (type && !res.getHeader('content-type')) {
        res.setHeader('content-type', type + '; charset=' + CHARSET);
      }

      if (!res.getHeader('content-length')) {
        res.setHeader('content-length', length);
      }

      // add etag header if not already set
      if (opts.etag && body && body.length && !res.getHeader('etag') &&
          req.method === 'GET' && Math.floor(statusCode / 100) === 2) {
        var etag = crypto.createHash('md5').update(body).digest('hex');
        res.setHeader('etag', etag);
      }

      // if cache is fresh set status code to 304
      if (fresh(req.headers, res._headers)) {
        statusCode = 304;
        noContent = true;
      }

      if (noContent) {
        res.removeHeader('content-type');
        res.removeHeader('content-length');
        res.removeHeader('transfer-encoding');
      }
    }

    if (noContent) body = '';

    res.statusCode = statusCode;

    return res.end(req.method === 'HEAD' ? null : body);
  };
}

/**
 * Strip slash
 */

function stripSlash(path) {
  if (path[path.length - 1] === '/') {
    return path.slice(0, path.length - 1);
  }

  return path;
}

/**
 * Expose http
 */

exports.CHARSET = CHARSET;
exports.STATUS_CODES = http.STATUS_CODES;
exports.decoder = decoder;
exports.encoder = encoder;
exports.options = options;
exports.reply = reply;
exports.stripSlash = stripSlash;
<|endoftext|>


<commit_before>const http = require("http");
const https = require("https");
const parseurl = require("url").parse;
const Stream = require("./stream");
const assign = Object.assign;
const keys = Object.keys;

/**
 * Read header value from headers object.
 * @param {object} headers
 * @param {string} name
 * @returns {string}
 */
function readHeader(headers, name) {
    return keys(headers)
        .filter(key.toLowerCase() === name.toLowerCase())
        .map(key => headers[key])
        .join(",");
}

/**
 * Read content type from headers.
 * @param {object} headers
 * @returns {string}
 */
function type(headers) {
    return readHeader(headers, "content-type").split(";")[0];
}

/**
 * Read links from headers.
 * @param {object} headers
 * @returns {object}
 */
function links(headers) {
    var links = {};

    readHeader(headers, "link").split(",")
        .map(link => /<(.*)>;\s*rel="(.*)"/.exec(link).slice(1,3))
        .forEach(linkinfo => {
            var uri = linkinfo[0],
                rel = linkinfo[1];

            switch (typeof links[rel]) {
                case "undefined": links[rel] = url; break;
                case "string": links[rel] = [links[rel], url]; break;
                case "object": links[rel].push(url); break;
                default: throw new Error("unexpected link type");
            }
        });

    return links;
}

/**
 * Make HTTP request.
 * @param {string} [method]
 * @param {string} url
 * @param {object} [headers]
 * @param {object|string} [body]
 */
function request(method, url, headers, body) {
    var args = Array.prototype.slice.call(arguments);

    switch (args.length) {
        case 0: throw new TypeError("expects string");
        case 1: url = String(method), method = undefined; break;
        case 2:
        case 3: method = typeof args[1] === "string" ? args.shift() : undefined;
                url = args.shift();
                body = args.pop();
                headers = args.shift();
                break;
        case 4: break;
        default: throw new TypeError("unexpected argument");
    }

    body = body || (body === "" ? body : undefined);
    method = method || (body === undefined ? "GET" : "POST");
    url = String(url);
    headers = headers || {};

    if (typeof body === "object" && !type(headers)) {
        headers["content-type"] === "application/json";
    }

    if (typeof body === "object" && type(headers) === "application/json") {
        body = JSON.stringify(body);
    }

    return new Promise((resolve, reject) => {
        var opts = parseurl(url),
            proto = opts.protocol === "https:" ? https : http,
            req;

        opts.method = method;
        opts.headers = headers;

        req = proto.request(opts);
        req.on("error", reject);
        req.on("response", res => {
            Stream.read(res).then(body =>{
                if (type(res.headers) === "application/json") {
                    body = JSON.parse(body);
                }

                resolve(assign(links(res.headers), {
                    status: res.statusCode,
                    headers: res.headers,
                    body: body
                }));
            });
        });
        req.end(body);
    });
}

/**
 * Make HTTP GET request.
 * @param {string} uri
 * @param {object} [headers]
 * @returns {Promise}
 */
function get(uri, headers) {
    return request("GET", uri, headers, null);
}

/**
 * Make HTTP POST request.
 * @param {string} uri
 * @param {object} [headers]
 * @param {object|string} [body]
 */
function post(uri, headers, body) {
    return request("POST", uri, headers, body);
}

/**
 * Create headers object with User-Agent.
 * @param {string} agent
 * @returns {object}
 */
function ua(agent) {
    return {"User-Agent": agent ? String(agent) : ""};
}

/**
 * Create headers object with Authorization header.
 * @param {string} ident
 * @param {string} secret
 * @returns {object}
 */
function basic(ident, secret) {
    var creds = new Buffer(`${ident}:${secret}`).toString("base64");
    return {Authorization: `Basic ${creds}`};
}

module.exports = {
    request: request,
    get: get,
    post: post,
    type: type,
    links: links,
    ua: ua,
    basic: basic
};
<commit_msg>fix HTTP readHeader<commit_after>const http = require("http");
const https = require("https");
const parseurl = require("url").parse;
const Stream = require("./stream");
const assign = Object.assign;
const keys = Object.keys;

/**
 * Read header value from headers object.
 * @param {object} headers
 * @param {string} name
 * @returns {string}
 */
function readHeader(headers, name) {
    return keys(headers)
        .filter(key => key.toLowerCase() === name.toLowerCase())
        .map(key => headers[key])
        .join(",");
}

/**
 * Read content type from headers.
 * @param {object} headers
 * @returns {string}
 */
function type(headers) {
    return readHeader(headers, "content-type").split(";")[0];
}

/**
 * Read links from headers.
 * @param {object} headers
 * @returns {object}
 */
function links(headers) {
    var links = {};

    readHeader(headers, "link").split(",")
        .map(link => /<(.*)>;\s*rel="(.*)"/.exec(link).slice(1,3))
        .forEach(linkinfo => {
            var uri = linkinfo[0],
                rel = linkinfo[1];

            switch (typeof links[rel]) {
                case "undefined": links[rel] = url; break;
                case "string": links[rel] = [links[rel], url]; break;
                case "object": links[rel].push(url); break;
                default: throw new Error("unexpected link type");
            }
        });

    return links;
}

/**
 * Make HTTP request.
 * @param {string} [method]
 * @param {string} url
 * @param {object} [headers]
 * @param {object|string} [body]
 */
function request(method, url, headers, body) {
    var args = Array.prototype.slice.call(arguments);

    switch (args.length) {
        case 0: throw new TypeError("expects string");
        case 1: url = String(method), method = undefined; break;
        case 2:
        case 3: method = typeof args[1] === "string" ? args.shift() : undefined;
                url = args.shift();
                body = args.pop();
                headers = args.shift();
                break;
        case 4: break;
        default: throw new TypeError("unexpected argument");
    }

    body = body || (body === "" ? body : undefined);
    method = method || (body === undefined ? "GET" : "POST");
    url = String(url);
    headers = headers || {};

    if (typeof body === "object" && !type(headers)) {
        headers["content-type"] === "application/json";
    }

    if (typeof body === "object" && type(headers) === "application/json") {
        body = JSON.stringify(body);
    }

    return new Promise((resolve, reject) => {
        var opts = parseurl(url),
            proto = opts.protocol === "https:" ? https : http,
            req;

        opts.method = method;
        opts.headers = headers;

        req = proto.request(opts);
        req.on("error", reject);
        req.on("response", res => {
            Stream.read(res).then(body =>{
                if (type(res.headers) === "application/json") {
                    body = JSON.parse(body);
                }

                resolve(assign(links(res.headers), {
                    status: res.statusCode,
                    headers: res.headers,
                    body: body
                }));
            });
        });
        req.end(body);
    });
}

/**
 * Make HTTP GET request.
 * @param {string} uri
 * @param {object} [headers]
 * @returns {Promise}
 */
function get(uri, headers) {
    return request("GET", uri, headers, null);
}

/**
 * Make HTTP POST request.
 * @param {string} uri
 * @param {object} [headers]
 * @param {object|string} [body]
 */
function post(uri, headers, body) {
    return request("POST", uri, headers, body);
}

/**
 * Create headers object with User-Agent.
 * @param {string} agent
 * @returns {object}
 */
function ua(agent) {
    return {"User-Agent": agent ? String(agent) : ""};
}

/**
 * Create headers object with Authorization header.
 * @param {string} ident
 * @param {string} secret
 * @returns {object}
 */
function basic(ident, secret) {
    var creds = new Buffer(`${ident}:${secret}`).toString("base64");
    return {Authorization: `Basic ${creds}`};
}

module.exports = {
    request: request,
    get: get,
    post: post,
    type: type,
    links: links,
    ua: ua,
    basic: basic
};
<|endoftext|>


<commit_before>// this is a basic wrapper for making Request() requests
//  we wrap this so we can have the same debug information for all requests

// fs lib for writing/reading mock requests
const fs = require('fs');

const util = require('util');

const needle = require('needle');

// get our project Log function for writing log output
const Log = require('./debugPrint');

const fsExists = util.promisify(fs.exists);
const fsReadFile = util.promisify(fs.readFile);
const fsWriteFile = util.promisify(fs.writeFile);

function GetMockFilename(mockName) {
  return `${__dirname}/mocks/${mockName}.mock`;
}

function SetMock(mockName, response) {
  // only store mock responses if THEMEPARKS_MOCKWRITE is set
  if (!process.env.THEMEPARKS_MOCKWRITE) return Promise.resolve(response);

  return fsWriteFile(GetMockFilename(mockName), JSON.stringify(response)).then(
    () => Promise.resolve(response),
  );
}

function GetMock(mockName) {
  // return undefined result if we're not in mock mode
  if (!process.env.THEMEPARKS_MOCK) return Promise.resolve();

  if (mockName === undefined) return Promise.resolve();

  const filename = GetMockFilename(mockName);
  return fsExists(filename).then((exists) => {
    if (!exists) {
      // file doesn't exist! return undefined
      return Promise.resolve();
    }

    // read the file
    return fsReadFile(filename).then((data) => {
      let JSONData = null;
      try {
        JSONData = JSON.parse(data);
      } catch (e) {
        Log(`Failed to read mock file JSON ${mockName}`);
      }

      if (JSONData === null) {
        return Promise.resolve();
      }

      return Promise.resolve(JSONData);
    });
  });
}

/**
 * Make a network request
 * @private
 * @param parameters to pass to request library
 */
function MakeRequest(networkRequest) {
  if (arguments.length !== 1) {
    return Promise.reject(new Error('HTTP requires 1 argument. The network object configuration'));
  }

  const options = networkRequest;

  return GetMock(options.mock).then((mockData) => {
    if (mockData !== undefined) {
      Log(`Returning mock request for ${options.mock}`);
      return Promise.resolve(mockData);
    }

    // debug log if we're in debug mode
    Log(`Making request to ${options.url}`);

    // grab method from the request (we'll call .[method] directly using the needle library)
    const requestMethod = options.method || 'get';
    delete options.method;

    // extract the required URL
    const requestURL = options.url;
    if (!requestURL) return Promise.reject(new Error(`No URL defined for ${requestMethod} request`));
    delete options.url;

    const requestData = options.data || options.body || {};
    delete options.data;
    delete options.body;

    // build-in retires into this wrapper (default 3)
    const retries = options.retries || 3;
    // un-set retries in-case request suddenly supports this or something!
    delete options.retries;

    // default delay of 2 seconds for each retry attempt
    const retryDelay = options.retryDelay || 2000;
    delete options.retryDelay;

    // we will default to returning the body, but can return the full response object if we want
    const returnFullResponse = options.returnFullResponse || false;
    delete options.returnFullResponse;

    // add ability to force responses into JSON objects
    //  even if they don't return application/json content header
    let forceJSON = options.forceJSON || false;
    delete options.forceJSON;

    // return result as a Promise!
    return new Promise((resolve, reject) => {
      let attempt = 0;

      // make request in an anonymous function so we can make multiple requests to it easily
      const attemptRequest = () => {
        Log(`Calling ${requestMethod}:${requestURL}`);

        // build Needle request
        needle.request(requestMethod, requestURL, requestData, options, (err, resp) => {
          if (err || resp.statusCode >= 400 || (resp.statusCode === 200 && !resp.body)) {
            Log(`Network request failed attempt ${attempt}/${retries} for URL ${requestURL}`);
            Log(err || (`${resp.statusCode}: ${JSON.stringify(resp.body, null, 2)}`));

            // if we have retires left, try again!
            attempt += 1;
            if (attempt < retries) {
              // try again after retryDelay milliseconds
              setTimeout(attemptRequest, retryDelay);
              return;
            }

            reject(err || (`${resp.statusCode}: ${JSON.stringify(resp.body, null, 2)}`));
          }

          // no error! return the result
          if (returnFullResponse) {
            Log(`Successfully fetched response for URL ${requestURL}`);
            SetMock(options.mock, resp).then(resolve);
          } else {
            // enable "forceJSON" if the return header type is "application/json"
            if (resp.headers && resp.headers['content-type'] && resp.headers['content-type'].indexOf('application/json') >= 0) {
              Log("Found 'application/json' header from in HTTP request, parsing JSON data");
              forceJSON = true;
            }

            // if we want to force JSON (and we're not already a JSON object!)
            if (forceJSON && resp.body.constructor !== {}.constructor && resp.body.constructor !== [].constructor) {
              let JSONData;
              try {
                JSONData = JSON.parse(resp.body);
              } catch (e) {
                reject(new Error(`Unable to parse ${resp.body} into a JSON object: ${e}`));
                return;
              }
              Log(`Successfully fetched and parsed JSON from response at ${requestURL}`);
              SetMock(options.mock, JSONData).then(resolve);
            } else {
              Log(`Successfully fetched body for URL ${requestURL}`);
              SetMock(options.mock, resp.body).then(resolve);
            }
          }
        });
      };

      // make first request attempt
      process.nextTick(attemptRequest);
    });
  });
}

module.exports = MakeRequest;
<commit_msg>[!] Fix yet another NodeJS syntax error<commit_after>// this is a basic wrapper for making Request() requests
//  we wrap this so we can have the same debug information for all requests

// fs lib for writing/reading mock requests
const fs = require('fs');

const util = require('util');

const needle = require('needle');

// get our project Log function for writing log output
const Log = require('./debugPrint');

const fsExists = util.promisify(fs.exists);
const fsReadFile = util.promisify(fs.readFile);
const fsWriteFile = util.promisify(fs.writeFile);

function GetMockFilename(mockName) {
  return `${__dirname}/mocks/${mockName}.mock`;
}

function SetMock(mockName, response) {
  // only store mock responses if THEMEPARKS_MOCKWRITE is set
  if (!process.env.THEMEPARKS_MOCKWRITE) return Promise.resolve(response);

  return fsWriteFile(GetMockFilename(mockName), JSON.stringify(response)).then(
    () => Promise.resolve(response)
  );
}

function GetMock(mockName) {
  // return undefined result if we're not in mock mode
  if (!process.env.THEMEPARKS_MOCK) return Promise.resolve();

  if (mockName === undefined) return Promise.resolve();

  const filename = GetMockFilename(mockName);
  return fsExists(filename).then((exists) => {
    if (!exists) {
      // file doesn't exist! return undefined
      return Promise.resolve();
    }

    // read the file
    return fsReadFile(filename).then((data) => {
      let JSONData = null;
      try {
        JSONData = JSON.parse(data);
      } catch (e) {
        Log(`Failed to read mock file JSON ${mockName}`);
      }

      if (JSONData === null) {
        return Promise.resolve();
      }

      return Promise.resolve(JSONData);
    });
  });
}

/**
 * Make a network request
 * @private
 * @param parameters to pass to request library
 */
function MakeRequest(networkRequest) {
  if (arguments.length !== 1) {
    return Promise.reject(new Error('HTTP requires 1 argument. The network object configuration'));
  }

  const options = networkRequest;

  return GetMock(options.mock).then((mockData) => {
    if (mockData !== undefined) {
      Log(`Returning mock request for ${options.mock}`);
      return Promise.resolve(mockData);
    }

    // debug log if we're in debug mode
    Log(`Making request to ${options.url}`);

    // grab method from the request (we'll call .[method] directly using the needle library)
    const requestMethod = options.method || 'get';
    delete options.method;

    // extract the required URL
    const requestURL = options.url;
    if (!requestURL) return Promise.reject(new Error(`No URL defined for ${requestMethod} request`));
    delete options.url;

    const requestData = options.data || options.body || {};
    delete options.data;
    delete options.body;

    // build-in retires into this wrapper (default 3)
    const retries = options.retries || 3;
    // un-set retries in-case request suddenly supports this or something!
    delete options.retries;

    // default delay of 2 seconds for each retry attempt
    const retryDelay = options.retryDelay || 2000;
    delete options.retryDelay;

    // we will default to returning the body, but can return the full response object if we want
    const returnFullResponse = options.returnFullResponse || false;
    delete options.returnFullResponse;

    // add ability to force responses into JSON objects
    //  even if they don't return application/json content header
    let forceJSON = options.forceJSON || false;
    delete options.forceJSON;

    // return result as a Promise!
    return new Promise((resolve, reject) => {
      let attempt = 0;

      // make request in an anonymous function so we can make multiple requests to it easily
      const attemptRequest = () => {
        Log(`Calling ${requestMethod}:${requestURL}`);

        // build Needle request
        needle.request(requestMethod, requestURL, requestData, options, (err, resp) => {
          if (err || resp.statusCode >= 400 || (resp.statusCode === 200 && !resp.body)) {
            Log(`Network request failed attempt ${attempt}/${retries} for URL ${requestURL}`);
            Log(err || (`${resp.statusCode}: ${JSON.stringify(resp.body, null, 2)}`));

            // if we have retires left, try again!
            attempt += 1;
            if (attempt < retries) {
              // try again after retryDelay milliseconds
              setTimeout(attemptRequest, retryDelay);
              return;
            }

            reject(err || (`${resp.statusCode}: ${JSON.stringify(resp.body, null, 2)}`));
          }

          // no error! return the result
          if (returnFullResponse) {
            Log(`Successfully fetched response for URL ${requestURL}`);
            SetMock(options.mock, resp).then(resolve);
          } else {
            // enable "forceJSON" if the return header type is "application/json"
            if (resp.headers && resp.headers['content-type'] && resp.headers['content-type'].indexOf('application/json') >= 0) {
              Log("Found 'application/json' header from in HTTP request, parsing JSON data");
              forceJSON = true;
            }

            // if we want to force JSON (and we're not already a JSON object!)
            if (forceJSON && resp.body.constructor !== {}.constructor && resp.body.constructor !== [].constructor) {
              let JSONData;
              try {
                JSONData = JSON.parse(resp.body);
              } catch (e) {
                reject(new Error(`Unable to parse ${resp.body} into a JSON object: ${e}`));
                return;
              }
              Log(`Successfully fetched and parsed JSON from response at ${requestURL}`);
              SetMock(options.mock, JSONData).then(resolve);
            } else {
              Log(`Successfully fetched body for URL ${requestURL}`);
              SetMock(options.mock, resp.body).then(resolve);
            }
          }
        });
      };

      // make first request attempt
      process.nextTick(attemptRequest);
    });
  });
}

module.exports = MakeRequest;
<|endoftext|>


<commit_before>const micro = require('micro');
const Promise = require('bluebird');

module.exports = ({ port = 0 }, router) => {
  return new Promise((resolve, reject) => {
    const server = micro(router);
    server.listen(port, () => {
      let { address, family, port } = server.address();
      if (family === 'IPv6' && address === '::') {
        address = '0.0.0.0';
      }
      console.log(`Listening to ${address}:${port}`);
      resolve({ address, port });
    });

    server.on('error', err => {
      console.error('micro:', err.stack);
      reject(err);
      // process.exit(1);
    });
  });
};
<commit_msg>port from env<commit_after>const micro = require('micro');
const Promise = require('bluebird');
const { PORT } = process.env;

module.exports = ({ port = PORT || 0 }, router) => {
  return new Promise((resolve, reject) => {
    const server = micro(router);
    server.listen(port, () => {
      let { address, family, port } = server.address();
      if (family === 'IPv6' && address === '::') {
        address = '0.0.0.0';
      }
      console.log(`Listening to ${address}:${port}`);
      resolve({ address, port });
    });

    server.on('error', err => {
      console.error('micro:', err.stack);
      reject(err);
      // process.exit(1);
    });
  });
};
<|endoftext|>


<commit_before>"use strict";

function eid(x){
    return document.getElementById(x);
}

var AjaxBestPromise={};

AjaxBestPromise.createMethodFunction=function(method){
    return function(params){
        return new Promise(function(resolve,reject){
            var ajax = new XMLHttpRequest();
            if(!method){
                return reject(new Error('debe indicar el method en ajax'));
            }
            ajax.onload=function(e){
                if(ajax.status!=200){
                    reject(new Error(ajax.status+' '+ajax.responseText));
                }else{
                    resolve(ajax.responseText);
                }
            }
            ajax.onerror=reject;
            var paqueteAEnviar=Object.keys(params.data).map(function(key){
                return key+'='+encodeURIComponent(params.data[key]);
            }).join('&');
            if(method==='POST'){
                ajax.open(method,params.url);
                ajax.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                ajax.send(paqueteAEnviar);
            }else{
                var url=params.url+(paqueteAEnviar?'?'+paqueteAEnviar:'');
                ajax.open(method,url);
                ajax.send();
            }
        });
    }
}

AjaxBestPromise.post=AjaxBestPromise.createMethodFunction('POST');
AjaxBestPromise.get=AjaxBestPromise.createMethodFunction('GET');

window.addEventListener('load',function(){
    eid('status').textContent='listo para empezar';
    eid('get').onclick=function(){
        AjaxBestPromise.get({
            url:'/ejemplo/suma',
            data:{alfa:eid('alfa').value, beta:eid('beta').value},
        }).then(function(resultado){
            eid('resultado').textContent=resultado;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('post').onclick=function(){
        AjaxBestPromise.post({
            url:'/ejemplo/suma',
            data:{alfa:eid('alfa').value, beta:eid('beta').value},
        }).then(function(resultado){
            eid('resultado').textContent=resultado;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('traer').onclick=function(){
        eid('traer').disabled = true;
        AjaxBestPromise.get({
            url:'/persona/load',
            data:{dni:eid('dni').value},
        }).then(function(resultado){
            var persona=JSON.parse(resultado);
            var tabla=document.createElement('table');
            eid('datos_persona').appendChild(tabla);
            for(var campo in persona){
                var row=tabla.insertRow(-1);
                var cell=row.insertCell(-1);
                cell.textContent=campo;
                var cell=row.insertCell(-1);
                cell.textContent=persona[campo];
            }
        }).then( function(){
            eid('traer').disabled = false;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
});<commit_msg>Agregamos servicio para los botones anterior, siguiente, primero, ultimo<commit_after>"use strict";

function eid(x){
    return document.getElementById(x);
}

var AjaxBestPromise={};

AjaxBestPromise.createMethodFunction=function(method){
    return function(params){
        return new Promise(function(resolve,reject){
            var ajax = new XMLHttpRequest();
            if(!method){
                return reject(new Error('debe indicar el method en ajax'));
            }
            ajax.onload=function(e){
                if(ajax.status!=200){
                    reject(new Error(ajax.status+' '+ajax.responseText));
                }else{
                    resolve(ajax.responseText);
                }
            }
            ajax.onerror=reject;
            var paqueteAEnviar=Object.keys(params.data).map(function(key){
                return key+'='+encodeURIComponent(params.data[key]);
            }).join('&');
            if(method==='POST'){
                ajax.open(method,params.url);
                ajax.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                ajax.send(paqueteAEnviar);
            }else{
                var url=params.url+(paqueteAEnviar?'?'+paqueteAEnviar:'');
                ajax.open(method,url);
                ajax.send();
            }
        });
    }
}

AjaxBestPromise.post=AjaxBestPromise.createMethodFunction('POST');
AjaxBestPromise.get=AjaxBestPromise.createMethodFunction('GET');

window.addEventListener('load',function(){
    eid('status').textContent='listo para empezar';
    eid('get').onclick=function(){
        AjaxBestPromise.get({
            url:'/ejemplo/suma',
            data:{alfa:eid('alfa').value, beta:eid('beta').value},
        }).then(function(resultado){
            eid('resultado').textContent=resultado;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('post').onclick=function(){
        AjaxBestPromise.post({
            url:'/ejemplo/suma',
            data:{alfa:eid('alfa').value, beta:eid('beta').value},
        }).then(function(resultado){
            eid('resultado').textContent=resultado;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('traer').onclick=function(){
        this.disabled = true;
        AjaxBestPromise.get({
            url:'/persona/load',
            data:{dni:eid('dni').value},
        }).then(function(resultado){
            if(!resultado){
                alert('No existe DNI');
            }else{
                var persona=JSON.parse(resultado);
                var tabla=document.createElement('table');
                eid('datos_persona').appendChild(tabla);
                for(var campo in persona){
                    var row=tabla.insertRow(-1);
                    var cell=row.insertCell(-1);
                    cell.textContent=campo;
                    var cell=row.insertCell(-1);
                    cell.textContent=persona[campo];
                }
            }
        }).then( function(){
            eid('traer').disabled = false;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('siguiente').onclick=function(){
        this.disabled = true;
        AjaxBestPromise.get({
            url:'/persona/siguiente',
            data:{dni:eid('dni').value},
        }).then(function(resultado){
            if(!resultado){
                alert('No hay mas registros');
            }else{
                var persona=JSON.parse(resultado);
                var tabla=document.createElement('table');
                eid('datos_persona').appendChild(tabla);
                for(var campo in persona){
                    var row=tabla.insertRow(-1);
                    var cell=row.insertCell(-1);
                    cell.textContent=campo;
                    var cell=row.insertCell(-1);
                    cell.textContent=persona[campo];
                }
            }
        }).then( function(){
            eid('siguiente').disabled = false;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('anterior').onclick=function(){
        this.disabled = true;
        AjaxBestPromise.get({
            url:'/persona/anterior',
            data:{dni:eid('dni').value},
        }).then(function(resultado){
            if(!resultado){
                alert('No hay mas registros');
            }else{
                var persona=JSON.parse(resultado);
                var tabla=document.createElement('table');
                eid('datos_persona').appendChild(tabla);
                for(var campo in persona){
                    var row=tabla.insertRow(-1);
                    var cell=row.insertCell(-1);
                    cell.textContent=campo;
                    var cell=row.insertCell(-1);
                    cell.textContent=persona[campo];
                }
            }
        }).then( function(){
            eid('anterior').disabled = false;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('primero').onclick=function(){
        this.disabled = true;
        AjaxBestPromise.get({
            url:'/persona/primero',
            data:{dni:eid('dni').value},
        }).then(function(resultado){
            if(!resultado){
                alert('No hay registros');
            }else{
                var persona=JSON.parse(resultado);
                var tabla=document.createElement('table');
                eid('datos_persona').appendChild(tabla);
                for(var campo in persona){
                    var row=tabla.insertRow(-1);
                    var cell=row.insertCell(-1);
                    cell.textContent=campo;
                    var cell=row.insertCell(-1);
                    cell.textContent=persona[campo];
                }
            }
        }).then( function(){
            eid('primero').disabled = false;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
    eid('ultimo').onclick=function(){
        this.disabled = true;
        AjaxBestPromise.get({
            url:'/persona/ultimo',
            data:{dni:eid('dni').value},
        }).then(function(resultado){
            if(!resultado){
                alert('No hay registros');
            }else{
                var persona=JSON.parse(resultado);
                var tabla=document.createElement('table');
                eid('datos_persona').appendChild(tabla);
                for(var campo in persona){
                    var row=tabla.insertRow(-1);
                    var cell=row.insertCell(-1);
                    cell.textContent=campo;
                    var cell=row.insertCell(-1);
                    cell.textContent=persona[campo];
                }
            }
        }).then( function(){
            eid('ultimo').disabled = false;
        }).catch(function(error){
            eid('status').textContent=''+error;
        });
    }
});<|endoftext|>


<commit_before>const Discord = require('discord.js');
const yt = require('ytdl-core');
const fs = require('fs');
const now = require('performance-now');
const tokens = require('./tokens.json');
const YouTube = require('youtube-node');
const youTube = new YouTube();
youTube.setKey(tokens.yt_token);
const client = new Discord.Client({fetchAllMembers: true})
const userData = JSON.parse(fs.readFileSync('./songs/spotify.json'));
const oauth = 'YOUR_DISCORD_BOT_OUATH_LINK';

let queue = {};

const commands = {
 'play': (msg) => {
  	if (userData[msg.author.id] === undefined) return msg.reply("You don't have any songs in your playlist! Add some with `" + tokens.prefix + "add`");
  	if (!msg.guild.voiceConnection) return commands.join(msg).then(() => commands.play(msg));

	  let dispatcher;

  (function play(song) {
   if (song === undefined) return msg.channel.sendMessage("You don't have any songs in your playlist! Add some with `" + tokens.prefix + "add`").then(() => {

    msg.member.voiceChannel.leave();
   });
   msg.channel.sendMessage(`Playing: **${song.title}** from user: **${msg.author.username}**'s playlist.`);
   dispatcher = msg.guild.voiceConnection.playStream(yt(song.url, {
    audioonly: true
   }) /*, { passes : tokens.passes }*/ );
   let collector = msg.channel.createCollector(m => m);
   collector.on('message', m => {
if (!m.content.startsWith(`${tokens.prefix}`)) {
  msg.channel.sendMessage(" ");
	   }
 if (m.author.bot) {
msg.channel.sendMessage(" ");
	}
if(m.author === client.user) {
msg.channel.sendMessage(" ");
	}
    if (m.content.startsWith(tokens.prefix + 'pause')) {
     msg.channel.sendMessage(':pause_button: Paused.').then(() => {
      dispatcher.pause();
     });
    } else if (m.content.startsWith(tokens.prefix + 'resume')) {
     msg.channel.sendMessage(':play_pause: Resumed.').then(() => {
      dispatcher.resume();
     });
    } else if (m.content.startsWith(tokens.prefix + 'skip')) {
     msg.channel.sendMessage(':arrow_forward: Skipped.').then(() => {
      dispatcher.end();
     });
    } else if (m.content.startsWith(tokens.prefix + 'volume+')) {
     if (Math.round(dispatcher.volume * 50) >= 100) return msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
     dispatcher.setVolume(Math.min((dispatcher.volume * 50 + (2 * (m.content.split('+').length - 1))) / 50, 2));
     msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
    } else if (m.content.startsWith(tokens.prefix + 'volume-')) {
     if (Math.round(dispatcher.volume * 50) <= 0) return msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
     dispatcher.setVolume(Math.max((dispatcher.volume * 50 - (2 * (m.content.split('-').length - 1))) / 50, 0));
     msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
    } else if (m.content.startsWith(tokens.prefix + 'time')) {
     msg.channel.sendMessage(`:clock1: Time: ${Math.floor(dispatcher.time / 60000)}:${Math.floor((dispatcher.time % 60000)/1000) <10 ? '0'+Math.floor((dispatcher.time % 60000)/1000) : Math.floor((dispatcher.time % 60000)/1000)}`);
    }
   });
   dispatcher.on('end', () => {
    collector.stop();
    play(userData[msg.author.id].songs[Math.floor(Math.random() * userData[msg.author.id].songs.length)]);
   });
   dispatcher.on('error', (err) => {
    return msg.channel.sendMessage('error: ' + err).then(() => {
     collector.stop();
     play(userData[msg.author.id].songs[Math.floor(Math.random() * userData[msg.author.id].songs.length)]);
    });
   });
  })(userData[msg.author.id].songs[Math.floor(Math.random() * userData[msg.author.id].songs.length)]);
 },
 'join': (msg) => {
  return new Promise((resolve, reject) => {
  const voiceChannel = msg.member.voiceChannel;
   if (!voiceChannel || voiceChannel.type !== 'voice') return msg.reply(':no_entry_sign: I couldn\'t connect to your voice channel.');
   voiceChannel.join().then(connection => resolve(connection)).catch(err => reject(err));
  });
  msg.channel.sendMessage(":white_check_mark: I've successfully joined your voice channel.");
 },
 'add': (msg) => {
  function addFromUrl(url) {
   msg.channel.sendMessage("*Adding...*");
   if (url == '' || url === undefined) return msg.channel.sendMessage(`You must add a YouTube video url after ${tokens.prefix}add`);
   yt.getInfo(url, (err, info) => {
    if (err) return msg.channel.sendMessage(':no_entry_sign: Invalid YouTube Link: ' + err);
    if (!userData[msg.author.id]) userData[msg.author.id] = {
     songs: []
    };
    userData[msg.author.id].songs.push({
     url: url,
     title: info.title
    });
    let updateValue = JSON.stringify(userData, null, 2);
    fs.writeFileSync('./songs/spotify.json', updateValue);
    msg.channel.sendMessage(`:white_check_mark: Added **${info.title}** to your playlist.`);
   });
  }

  function addFromQuery(query) {
   youTube.search(query, 2, function(error, result) {
    if (error) {
     msg.channel.sendMessage(`:no_entry_sign: **Error:**\n${error}`);
    } else {
     var url = `https://www.youtube.com/watch?v=${result.items[0]["id"].videoId}`;
     addFromUrl(url);
    }
   });
  }

  if (msg.content === tokens.prefix + "add") return;
  var url = msg.content.split(' ')[1];
  if(url.includes("https://youtube.com/playlist") || url.includes("https://www.youtube.com/playlist") || url.includes("http://youtube.com/playlist") || url.includes("http://www.youtube.com/playlist")) return msg.channel.sendMessage(":no_entry_sign: You can't add a Youtube playlist a song.");
  if (url.includes("https://youtube.com/watch") || url.includes("https://www.youtube.com/watch") || url.includes("http://youtube.com/watch") || url.includes("http://www.youtube.com/watch") || url.includes("https://youtu.be/") || url.includes("https://www.youtu.be/") || url.includes("http://youtu.be/") || url.includes("http://www.youtu.be/")) {
   addFromUrl(url);
  } else {
   var query = msg.content.replace(tokens.prefix + 'add', '');
   addFromQuery(query);
  }
 },
 'songs': (msg) => {
  if (!userData[msg.author.id] === undefined) return msg.channel.sendMessage("You don't have any songs in your playlist! Add some with `" + tokens.prefix + "add`");
  let tosend = [];
  userData[msg.author.id].songs.forEach((song, i) => {
   tosend.push(`${i+1}. ${song.title}`);
  });
  msg.channel.sendMessage(`__**${msg.author.username}'s Music Playlist:**__ Currently **${tosend.length}** songs in it ${(tosend.length > 15 ? '*[Only next 15 shown]*' : '')}\n\`\`\`${tosend.slice(0,15).join('\n')}\`\`\``);
 },
 'ping': (msg) => {
  var start = now();
  msg.channel.sendMessage("*Pinging...*").then(msg => {
   var end = now();msg.edit(`Pong! **${(end - start).toFixed(0)}ms**`);
  });
 },
 'avatar': (msg) => {
msg.reply("", {embed: {
    color: 0x1C226B,
    title: msg.author.avatarURL,

  }});
 },
 'help': (msg) => {
  msg.reply("I've sent a list of commands to you. Check your DM's.");
  var help = [
   "Prefix is `s.`",
   "**help** = Sends this message.",
   "**join** = Joins the voice channel you're connected to.",
   "**play** = Starts playing a song in your playlist.",
   "**skip** = Skips the currently playing song.",
   "**pause** = Pauses the currently playing song.",
   "**resume** = Resumes a paused song.",
   "**stop** = Stops the currently playing song and leaves the voice channel.",
   "**volume+[+]** = Increases the volume of a song.",
   "**volume-[-]** = Decreases the volume of a song.",
   "**time** = Displays the play time of the song that's playing.",
   "**add <song>** = Adds the specified song to your custom playlist. *(YouTube videos only)*",
   "**songs** = List's all of your songs.",
   "**invite** = Sends the OAuth link to invite me to your server.",
   "**ping** = Pings the bot.",
   "**connections** = List's the amount of voice channels Spotify is connected to.",
   "**servers** = List's the amount of servers Spotify is on.",
  ];
  msg.author.sendMessage(help);
 },
 'connections': (msg) => {
  msg.channel.sendMessage(`Currently playing music in **${client.voiceConnections.size}** voice channels!`);
 },
 'servers': (msg) => {
	 msg.channel.sendMessage("", {embed: {
    color: 0x39EA81,
    title: `On **${client.guilds.size}** servers!`,
  }});
 },
 'invite': (msg) => {
  msg.channel.sendMessage(`Feel free to invite me using this: ${oauth}`)
 },
 'restart': (msg) => {
  if (!msg.author.id === '193378071141810176') return;
  if (!msg.author.id === '138431969418543104') return;
  msg.channel.sendMessage("```css\nRestarting...```");
  console.log("Restarting...");
  setTimeout(function() {
   console.log(process.exit(0));
  }, 400);
 },
 'stats': (msg) => {
  var start = now();
  let embed = new Discord.RichEmbed();
  msg.channel.sendMessage("```+ Fetching...```")
  .then(msg => {
   var end = now();
   var milliseconds = parseInt((client.uptime % 1000) / 100),
    seconds = parseInt((client.uptime / 1000) % 60),
    minutes = parseInt((client.uptime / (1000 * 60)) % 60),
    hours = parseInt((client.uptime / (1000 * 60 * 60)) % 24);
   hours = (hours < 10) ? "0" + hours : hours;
   minutes = (minutes < 10) ? "0" + minutes : minutes;
   seconds = (seconds < 10) ? "0" + seconds : seconds;
   var uptime = "" + hours + " hours, " + minutes + " minutes and " + seconds + "." + milliseconds + " seconds";
   embed.setColor(0x00FFE1)
   .setAuthor(client.user.username, client.user.avatarURL)
   .setTitle("Spotify Stats:")
   .addField('• Uptime: ', `${uptime}`, true)
   .addField('• Servers: ', `${client.guilds.size}`, true)
   .addField('• Channels: ', `${client.channels.size.toLocaleString()}`, true)
   .addField('• Users: ', `${client.users.size.toLocaleString()}`, true)
   .addField('• Ping: ', `${(end - start).toFixed(0)} MS`, true)
   .addField('• CPU Speed: ', '2.4GHz', true)
   .addField(`• Mem. Usage: `, `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`, true)
   msg.channel.sendEmbed(embed);
   return msg.edit('```Done!```');
  }).catch(e => {
   msg.channel.sendMessage(":no_entry_sign: There was an error! Report this please:\n\n" + e);
  });
 }
};

client.on('ready', () => {
 client.user.setGame(`Custom playlists! s.help | s.invite`, 'https://www.twitch.tv/imaqtpie');
 console.log('Spotify bot online and ready!');
});

client.on('message', msg => {
 if (!msg.content.startsWith(tokens.prefix)) return;
 if (commands.hasOwnProperty(msg.content.toLowerCase().slice(tokens.prefix.length).split(' ')[0])) commands[msg.content.toLowerCase().slice(tokens.prefix.length).split(' ')[0]](msg);
});
client.login(tokens.d_token);
<commit_msg>Update app.js<commit_after>const Discord = require('discord.js');
const yt = require('ytdl-core');
const fs = require('fs');
const now = require('performance-now');
const tokens = require('./tokens.json');
const YouTube = require('youtube-node');
const youTube = new YouTube();
youTube.setKey(tokens.yt_token);
const client = new Discord.Client({fetchAllMembers: true})
const userData = JSON.parse(fs.readFileSync('./songs/spotify.json'));
const oauth = 'YOUR_DISCORD_BOT_OUATH_LINK';
let name = 'YOUR_BOT_NAME_FILL_HERE';
let queue = {};

const commands = {
 'play': (msg) => {
  	if (userData[msg.author.id] === undefined) return msg.reply("You don't have any songs in your playlist! Add some with `" + tokens.prefix + "add`");
  	if (!msg.guild.voiceConnection) return commands.join(msg).then(() => commands.play(msg));

	  let dispatcher;

  (function play(song) {
   if (song === undefined) return msg.channel.sendMessage("You don't have any songs in your playlist! Add some with `" + tokens.prefix + "add`").then(() => {

    msg.member.voiceChannel.leave();
   });
   msg.channel.sendMessage(`Playing: **${song.title}** from user: **${msg.author.username}**'s playlist.`);
   dispatcher = msg.guild.voiceConnection.playStream(yt(song.url, {
    audioonly: true
   }) /*, { passes : tokens.passes }*/ );
   let collector = msg.channel.createCollector(m => m);
   collector.on('message', m => {
if (!m.content.startsWith(`${tokens.prefix}`)) {
  msg.channel.sendMessage(" ");
	   }
 if (m.author.bot) {
msg.channel.sendMessage(" ");
	}
if(m.author === client.user) {
msg.channel.sendMessage(" ");
	}
    if (m.content.startsWith(tokens.prefix + 'pause')) {
     msg.channel.sendMessage(':pause_button: Paused.').then(() => {
      dispatcher.pause();
     });
    } else if (m.content.startsWith(tokens.prefix + 'resume')) {
     msg.channel.sendMessage(':play_pause: Resumed.').then(() => {
      dispatcher.resume();
     });
    } else if (m.content.startsWith(tokens.prefix + 'skip')) {
     msg.channel.sendMessage(':arrow_forward: Skipped.').then(() => {
      dispatcher.end();
     });
    } else if (m.content.startsWith(tokens.prefix + 'volume+')) {
     if (Math.round(dispatcher.volume * 50) >= 100) return msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
     dispatcher.setVolume(Math.min((dispatcher.volume * 50 + (2 * (m.content.split('+').length - 1))) / 50, 2));
     msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
    } else if (m.content.startsWith(tokens.prefix + 'volume-')) {
     if (Math.round(dispatcher.volume * 50) <= 0) return msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
     dispatcher.setVolume(Math.max((dispatcher.volume * 50 - (2 * (m.content.split('-').length - 1))) / 50, 0));
     msg.channel.sendMessage(`:speaker: Volume: ${Math.round(dispatcher.volume*50)}%`);
    } else if (m.content.startsWith(tokens.prefix + 'time')) {
     msg.channel.sendMessage(`:clock1: Time: ${Math.floor(dispatcher.time / 60000)}:${Math.floor((dispatcher.time % 60000)/1000) <10 ? '0'+Math.floor((dispatcher.time % 60000)/1000) : Math.floor((dispatcher.time % 60000)/1000)}`);
    }
   });
   dispatcher.on('end', () => {
    collector.stop();
    play(userData[msg.author.id].songs[Math.floor(Math.random() * userData[msg.author.id].songs.length)]);
   });
   dispatcher.on('error', (err) => {
    return msg.channel.sendMessage('error: ' + err).then(() => {
     collector.stop();
     play(userData[msg.author.id].songs[Math.floor(Math.random() * userData[msg.author.id].songs.length)]);
    });
   });
  })(userData[msg.author.id].songs[Math.floor(Math.random() * userData[msg.author.id].songs.length)]);
 },
 'join': (msg) => {
  return new Promise((resolve, reject) => {
  const voiceChannel = msg.member.voiceChannel;
   if (!voiceChannel || voiceChannel.type !== 'voice') return msg.reply(':no_entry_sign: I couldn\'t connect to your voice channel.');
   voiceChannel.join().then(connection => resolve(connection)).catch(err => reject(err));
  });
  msg.channel.sendMessage(":white_check_mark: I've successfully joined your voice channel.");
 },
 'add': (msg) => {
  function addFromUrl(url) {
   msg.channel.sendMessage("*Adding...*");
   if (url == '' || url === undefined) return msg.channel.sendMessage(`You must add a YouTube video url after ${tokens.prefix}add`);
   yt.getInfo(url, (err, info) => {
    if (err) return msg.channel.sendMessage(':no_entry_sign: Invalid YouTube Link: ' + err);
    if (!userData[msg.author.id]) userData[msg.author.id] = {
     songs: []
    };
    userData[msg.author.id].songs.push({
     url: url,
     title: info.title
    });
    let updateValue = JSON.stringify(userData, null, 2);
    fs.writeFileSync('./songs/spotify.json', updateValue);
    msg.channel.sendMessage(`:white_check_mark: Added **${info.title}** to your playlist.`);
   });
  }

  function addFromQuery(query) {
   youTube.search(query, 2, function(error, result) {
    if (error) {
     msg.channel.sendMessage(`:no_entry_sign: **Error:**\n${error}`);
    } else {
     var url = `https://www.youtube.com/watch?v=${result.items[0]["id"].videoId}`;
     addFromUrl(url);
    }
   });
  }

  if (msg.content === tokens.prefix + "add") return;
  var url = msg.content.split(' ')[1];
  if(url.includes("https://youtube.com/playlist") || url.includes("https://www.youtube.com/playlist") || url.includes("http://youtube.com/playlist") || url.includes("http://www.youtube.com/playlist")) return msg.channel.sendMessage(":no_entry_sign: You can't add a Youtube playlist a song.");
  if (url.includes("https://youtube.com/watch") || url.includes("https://www.youtube.com/watch") || url.includes("http://youtube.com/watch") || url.includes("http://www.youtube.com/watch") || url.includes("https://youtu.be/") || url.includes("https://www.youtu.be/") || url.includes("http://youtu.be/") || url.includes("http://www.youtu.be/")) {
   addFromUrl(url);
  } else {
   var query = msg.content.replace(tokens.prefix + 'add', '');
   addFromQuery(query);
  }
 },
 'songs': (msg) => {
  if (!userData[msg.author.id] === undefined) return msg.channel.sendMessage("You don't have any songs in your playlist! Add some with `" + tokens.prefix + "add`");
  let tosend = [];
  userData[msg.author.id].songs.forEach((song, i) => {
   tosend.push(`${i+1}. ${song.title}`);
  });
  msg.channel.sendMessage(`__**${msg.author.username}'s Music Playlist:**__ Currently **${tosend.length}** songs in it ${(tosend.length > 15 ? '*[Only next 15 shown]*' : '')}\n\`\`\`${tosend.slice(0,15).join('\n')}\`\`\``);
 },
 'ping': (msg) => {
  var start = now();
  msg.channel.sendMessage("*Pinging...*").then(msg => {
   var end = now();msg.edit(`Pong! **${(end - start).toFixed(0)}ms**`);
  });
 },
 'avatar': (msg) => {
msg.reply("", {embed: {
    color: 0x1C226B,
    title: msg.author.avatarURL,

  }});
 },
 'help': (msg) => {
  msg.reply("I've sent a list of commands to you. Check your DM's.");
  var help = [
   "Prefix is `s.`",
   "**help** = Sends this message.",
   "**join** = Joins the voice channel you're connected to.",
   "**play** = Starts playing a song in your playlist.",
   "**skip** = Skips the currently playing song.",
   "**pause** = Pauses the currently playing song.",
   "**resume** = Resumes a paused song.",
   "**stop** = Stops the currently playing song and leaves the voice channel.",
   "**volume+[+]** = Increases the volume of a song.",
   "**volume-[-]** = Decreases the volume of a song.",
   "**time** = Displays the play time of the song that's playing.",
   "**add <song>** = Adds the specified song to your custom playlist. *(YouTube videos only)*",
   "**songs** = List's all of your songs.",
   "**invite** = Sends the OAuth link to invite me to your server.",
   "**ping** = Pings the bot.",
   "**connections** = List's the amount of voice channels Spotify is connected to.",
   "**servers** = List's the amount of servers Spotify is on.",
  ];
  msg.author.sendMessage(help);
 },
 'connections': (msg) => {
  msg.channel.sendMessage(`Currently playing music in **${client.voiceConnections.size}** voice channels!`);
 },
 'servers': (msg) => {
	 msg.channel.sendMessage("", {embed: {
    color: 0x39EA81,
    title: `On **${client.guilds.size}** servers!`,
  }});
 },
 'invite': (msg) => {
  msg.channel.sendMessage(`Feel free to invite me using this: ${oauth}`)
 },
 'restart': (msg) => {
  if (!msg.author.id === '193378071141810176') return;
  if (!msg.author.id === '138431969418543104') return;
  msg.channel.sendMessage("```css\nRestarting...```");
  console.log("Restarting...");
  setTimeout(function() {
   console.log(process.exit(0));
  }, 400);
 },
 'stats': (msg) => {
  var start = now();
  let embed = new Discord.RichEmbed();
  msg.channel.sendMessage("```+ Fetching...```")
  .then(msg => {
   var end = now();
   var milliseconds = parseInt((client.uptime % 1000) / 100),
    seconds = parseInt((client.uptime / 1000) % 60),
    minutes = parseInt((client.uptime / (1000 * 60)) % 60),
    hours = parseInt((client.uptime / (1000 * 60 * 60)) % 24);
   hours = (hours < 10) ? "0" + hours : hours;
   minutes = (minutes < 10) ? "0" + minutes : minutes;
   seconds = (seconds < 10) ? "0" + seconds : seconds;
   var uptime = "" + hours + " hours, " + minutes + " minutes and " + seconds + "." + milliseconds + " seconds";
   embed.setColor(0x00FFE1)
   .setAuthor(client.user.username, client.user.avatarURL)
   .setTitle("Spotify Stats:")
   .addField('• Uptime: ', `${uptime}`, true)
   .addField('• Servers: ', `${client.guilds.size}`, true)
   .addField('• Channels: ', `${client.channels.size.toLocaleString()}`, true)
   .addField('• Users: ', `${client.users.size.toLocaleString()}`, true)
   .addField('• Ping: ', `${(end - start).toFixed(0)} MS`, true)
   .addField('• CPU Speed: ', '2.4GHz', true)
   .addField(`• Mem. Usage: `, `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`, true)
   msg.channel.sendEmbed(embed);
   return msg.edit('```Done!```');
  }).catch(e => {
   msg.channel.sendMessage(":no_entry_sign: There was an error! Report this please:\n\n" + e);
  });
 }
};

client.on('ready', () => {
 client.user.setGame(`Custom playlists! s.help | s.invite`, 'https://www.twitch.tv/imaqtpie');
 console.log(`${name} bot online and ready!`);
});

client.on('message', msg => {
 if (!msg.content.startsWith(tokens.prefix)) return;
 if (commands.hasOwnProperty(msg.content.toLowerCase().slice(tokens.prefix.length).split(' ')[0])) commands[msg.content.toLowerCase().slice(tokens.prefix.length).split(' ')[0]](msg);
});
client.login(tokens.d_token);
<|endoftext|>