server.js

const url = require('url');
const http = require('http');
const body = require('body');
const npmlog = require('npmlog');
const dummy = require('dummy-object');

/**
 * Takes HTTP requests
 *
 * Example (in the main script):
 * ```
 * const server = new Server(somebot, someotherbot);
 * server.listen(80, 'localhost');
 * ```
 */
class Server {
  /**
   * @param {Function[]} [bots]        Bots whose handle method will be called
   *                                   on incoming requests
   * @param {object}     [options]
   * @param {object}     [options.log] npmlog-like logger; pass null to disable
   */
  constructor(bots = [], { log } = {}) {
    this.bots = bots;
    if (bots) {
      for (const bot of bots) {
        bot.setServer(this);
      }
    }

    if (log === null) {
      this.log = dummy;
    } else {
      this.log = log || npmlog;
    }

    // Creating HTTP server. Data is handled by this.request
    this.server = http.createServer((req, res) => this.request(req, res));
  }

  /**
   * Adds bots whose handle functions are called on HTTP requests
   *
   * @param {Bot} bot
   */
  addBot(bot) {
    this.bots.push(bot);
    bot.server = this;
  }

  /**
   * Executes the HTTP Server's listen method
   *
   * @param {number} port Port to listen on
   * @param {string} host Hostname for the server
   *
   * @see https://nodejs.org/api/http.html
   */
  listen(...args) {
    this.log.http('bot', `Server on app ${process.pid} started`);
    this.server.listen(...args);
  }


  /**
   * Receives HTTP requests from the API
   *
   * @param {IncomingMessage} req Request
   * @param {ServerResponse}  res Response
   */
  request(req, res) {
    if (req.method === 'POST') {
      const parts = url.parse(req.url, true);
      const route = parts.pathname;
      const passedUrl = parts.query && parts.query.url ? parts.query.url : '';

      this.log.http('server', `Request on route '${route}'`);

      body(req, (err, parsed) => {
        if (err) {
          this.log.error('server', 'Body parser error', err);
          return;
        }
        for (const bot of this.bots) {
          bot.handle(parsed, passedUrl, route, res);
        }
      });
    } else {
      res.writeHead(400, { 'Content-Type': 'text/plain' });
      res.end('Nothing to do here!');
    }
  }
}

module.exports = Server;