<commit_before>'use strict';
const path = require('path');
const zlib = require('zlib');
const fse = require('fs-extra');
const BbPromise = require('bluebird');
const _ = require('lodash');
const async = require('async');
const mime = require('mime-types');
const fs = require('fs');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');

class SPA {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.options = options;
    this.stage = options.stage || _.get(serverless, 'service.provider.stage')
    this.region = options.region || _.get(serverless, 'service.provider.region');
    this.provider = 'aws';
    this.aws = this.serverless.getProvider(this.provider);

    this.commands = {
      spa: {
        usage: 'Bundle your spa app and deploys to S3',
        lifecycleEvents: [
          'prepare',
          'cleanup',
          'bundle',
          'deploy'
        ],
        options: {
        },
        commands: {
          clean: {
            usage: 'Cleanup deployment folder',
            lifecycleEvents: [
              'prepare',
              'cleanup'
            ],
            options: {
            },
          },
          bundle: {
            usage: 'Bundle app',
            lifecycleEvents: [
              'prepare',
              'cleanup',
              'bundle'
            ],
            options: {
            },
          },
          serve: {
            usage: 'Open local webpack server',
            lifecycleEvents: [
              'prepare',
              'cleanup',
              'serve'
            ],
            options: {
            },
          },
          deploy: {
            usage: 'Deploy app',
            lifecycleEvents: [
              'prepare',
              'cleanup',
              'bundle',
              'deploy'
            ],
            options: {
            },
          },
        }
      },
    };

    this.hooks = {
      'spa:prepare': this.prepare.bind(this),
      'spa:clean:prepare': this.prepare.bind(this),
      'spa:bundle:prepare': this.prepare.bind(this),
      'spa:serve:prepare': this.prepare.bind(this),
      'spa:deploy:prepare': this.prepare.bind(this),

      'spa:cleanup': this.cleanup.bind(this),
      'spa:clean:cleanup': this.cleanup.bind(this),
      'spa:bundle:cleanup': this.cleanup.bind(this),
      'spa:serve:cleanup': this.cleanup.bind(this),
      'spa:deploy:cleanup': this.cleanup.bind(this),

      'spa:bundle': this.bundle.bind(this),
      'spa:bundle:bundle': this.bundle.bind(this),
      'spa:deploy:bundle': this.bundle.bind(this),

      'spa:deploy': this.deploy.bind(this),
      'spa:deploy:deploy': this.deploy.bind(this),

      'spa:serve:serve': this.serve.bind(this),

      'before:offline:start': this.offline.bind(this),
      'before:offline:start:init': this.offline.bind(this),
    };
  }

  prepare() {
    const Utils = this.serverless.utils;
    const Error = this.serverless.classes.Error;

    let error = this._setWebpackFilename();
    if (error) {
      return error;
    }

    error = this._setAppFolder();
    if (error) {
      return error;
    }

    this._setDistFolder();

    error = this._setWebpackConfigPath();
    if (error) {
      return error;
    }

    this._setWebpackConfig();

    error = this._setBucketName();
    if (error) {
      return error;
    }

    this._setGZip();
  }

  cleanup() {
    this.serverless.cli.log('Removing ".spa" folder');
    fse.removeSync(this.distFolder);
  }

  bundle() {
    const Error = this.serverless.classes.Error;
    this.serverless.cli.log('Bundling with Webpack');

    const compiler = webpack(this.webpackConfig);
    return BbPromise
      .fromCallback(cb => compiler.run(cb))
      .then(stats => {

        this.serverless.cli.consoleLog(stats.toString({
          colors: true,
          hash: false,
          version: false,
          chunks: false,
          children: false
        }));
        if (stats.compilation.errors.length) {
          throw new Error('Webpack compilation error, see above');
        }
        const outputPath = stats.compilation.compiler.outputPath;
        this.webpackOutputPath = outputPath;
        this.originalServicePath = this.serverless.config.servicePath;
        this.serverless.config.servicePath = outputPath;
        return stats;
      });
  }

  _appendHmrToEntries(entry, hmrEntries) {
    let result = {};
    if (typeof (entry) === 'string') {
      /*
        entry: 'file.js'
      */
      result = hmrEntries.concat(entry);
    } else if (Array.isArray(entry)) {
      /*
        entry: ['file.js']
      */
      result = hmrEntries.concat(entry);
    } else {
      /*
        entry: {
          app: 'file.js',
          vendor: [
            'f1.js',
            'b.js'
          ]
        }
      */
      for (let key in entry) {
        result[key] = this._appendHmrToEntries(entry[key], hmrEntries);
      }
    }

    return result;
  }

  serve() {
    const Error = this.serverless.classes.Error;

    const devServerOptions = _.extend(
      {
        port: 8080
      },
      this.webpackConfig.devServer,
      {
        contentBase: '.spa',
        stats: 'errors-only'
      }
    );

    const url = this._getDevServerURL(devServerOptions.https, devServerOptions.port);

    this._setHMR(devServerOptions.hot, url);

    return new BbPromise((resolve, reject) => {
      const compiler = webpack(this.webpackConfig);
      new WebpackDevServer(compiler, devServerOptions)
        .listen(devServerOptions.port, '0.0.0.0', (err) => {
          if (err) {
            reject(err);
          }

          this.serverless.cli.log(`Serving app at ${url}`);
        });
    });
  }

  _getDevServerURL(https, port) {
    const protocol = https ? 'https' : 'http';
    return `${protocol}://localhost:${port}/`;
  }

  _setHMR(hmrEnabled, url) {
    if (!hmrEnabled) {
      return;
    }

    const hmrEntries = [
      'webpack-dev-server/client?' + url,
      'webpack/hot/dev-server',
      'react-hot-loader/patch'
    ];

    this.webpackConfig.entry = this._appendHmrToEntries(this.webpackConfig.entry, hmrEntries);

    const plugins = this.webpackConfig.plugins;
    const pluginNames = plugins.map(plugin => plugin.constructor.name);

    if (!_.includes(pluginNames, 'HotModuleReplacementPlugin')) {
      this.webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
    }

    if (!_.includes(pluginNames, 'NamedModulesPlugin')) {
      this.webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
    }

    if (!_.includes(pluginNames, 'NoEmitOnErrorsPlugin')) {
      this.webpackConfig.plugins.push(new webpack.NoEmitOnErrorsPlugin());
    }
  }

  offline() {
    BbPromise
      .bind(this)
      .then(this.prepare)
      .then(this.cleanup)
      .then(this.serve);
  }

  deploy() {
    return this.aws.request('S3', 'listBuckets', {}, this.stage, this.region)
      .bind(this)
      .then(this._listBuckets)
      .then(this._listObjectsInBucket)
      .then(this._deleteObjectsFromBucket)
      .then(this._createBucket)
      .then(this._configureBucket)
      .then(this._configurePolicyForBucket)
      .then(this._uploadDirectory);
  }

  _listBuckets(data) {
    data.Buckets.forEach((bucket) => {
      if (bucket.Name === this.bucketName) {
        this.bucketExists = true;
        this.serverless.cli.log(`Bucket ${this.bucketName} already exists`);
      }
    });
  }

  _listObjectsInBucket() {
    if (!this.bucketExists) return BbPromise.resolve();

    this.serverless.cli.log(`Listing objects in bucket ${this.bucketName}...`);

    let params = {
      Bucket: this.bucketName
    };

    return this.aws.request('S3', 'listObjectsV2', params, this.stage, this.region);
  }

  _deleteObjectsFromBucket(data) {
    if (!this.bucketExists) {
      return BbPromise.resolve();
    }

    this.serverless.cli.log(`Deleting all objects from bucket ${this.bucketName}...`);

    if (!data.Contents[0]) {
      return BbPromise.resolve();
    }

    let Objects = _.map(data.Contents, (content) => _.pick(content, 'Key'));

    let params = {
      Bucket: this.bucketName,
      Delete: { Objects: Objects }
    };

    return this.aws.request('S3', 'deleteObjects', params, this.stage, this.region);
  }

  _createBucket() {
    if (this.bucketExists) return BbPromise.resolve();
    this.serverless.cli.log(`Creating bucket ${this.bucketName}...`);

    let params = {
      Bucket: this.bucketName
    };

    return this.aws.request('S3', 'createBucket', params, this.stage, this.region);
  }

  _configureBucket() {
    this.serverless.cli.log(`Configuring website bucket ${this.bucketName}...`);

    let params = {
      Bucket: this.bucketName,
      WebsiteConfiguration: {
        IndexDocument: { Suffix: 'index.html' },
        ErrorDocument: { Key: 'index.html' }
      }
    };

    return this.aws.request('S3', 'putBucketWebsite', params, this.stage, this.region)
  }

  _configurePolicyForBucket() {
    this.serverless.cli.log(`Configuring policy for bucket ${this.bucketName}...`);

    let policy = {
      Version: '2008-10-17',
      Id: 'Policy1392681112290',
      Statement: [
        {
          Sid: 'Stmt1392681101677',
          Effect: 'Allow',
          Principal: {
            AWS: '*'
          },
          Action: 's3:GetObject',
          Resource: `arn:aws:s3:::${this.bucketName}/*`
        }
      ]
    };

    let params = {
      Bucket: this.bucketName,
      Policy: JSON.stringify(policy)
    };

    return this.aws.request('S3', 'putBucketPolicy', params, this.stage, this.region);
  }

  _uploadDirectory(recursivePath) {
    this.serverless.cli.log('Uploading files...');
    let directoryPath = this.distFolder;
    if (JSON.stringify(recursivePath) !== "{}") {
        directoryPath = recursivePath
    }
    let readDirectory = _.partial(fs.readdir, directoryPath);

    async.waterfall([readDirectory, (files) => {
      files = _.map(files, (file) => path.join(directoryPath, file));

      async.each(files, (path) => {
        fs.stat(path, (err, stats) => {
          return stats.isDirectory() ? this._uploadDirectory(path) : this._uploadFile(path);
        });
      });
    }]);
  }

  _uploadFile(filePath) {
    let fileKey = filePath.replace(this.distFolder, '').substr(1).replace(/\\/g, '/');

    this._gzipFile(filePath, fileKey, () => {
      this.serverless.cli.log(`Uploading file '${fileKey}'...`);

      fs.readFile(filePath, (err, fileBuffer) => {
        let params = {
          Bucket: this.bucketName,
          Key: fileKey,
          Body: fileBuffer,
          ContentType: mime.lookup(filePath)
        };

        if (this.gzip) {
          params.ContentEncoding = 'gzip';
        }

        // TODO: remove browser caching
        return this.aws.request('S3', 'putObject', params, this.stage, this.region);
      });
    });
  }

  _gzipFile(filePath, filename, done) {
    if (!this.gzip) {
      done();
      return;
    }

    this.serverless.cli.log(`Compressing file '${filename}'`);

    const content = fs.readFileSync(filePath);
    zlib.gzip(content, (error, result) => {
      if (error) {
        this.serverless.cli.log(`Fail to compress file '${filename}'`, error);
        done();
        return;
      }

      fs.writeFileSync(filePath, result);
      done();
    });
  }

  _setWebpackFilename() {
    this.webpackFilename = _.get(this.serverless, 'service.custom.spa.webpack');

    if (!this.webpackFilename || typeof (this.webpackFilename) !== 'string') {
      this.webpackFilename = _.get(this.serverless, `service.custom.spa.webpack.${this.stage}`);
    }

    if (!this.webpackFilename) {
      return Promise.reject(new Error('Could not find webpack config.'));
    }
  }

  _setBucketName() {
    this.bucketName = _.get(this.serverless, 'service.custom.spa.bucket');

    if (!this.bucketName || typeof (this.bucketName) !== 'string') {
      this.bucketName = _.get(this.serverless, `service.custom.spa.bucket.${this.stage}`);
    }

    if (!this.bucketName) {
      return Promise.reject(new Error(`Could not find bucket name.`));
    }
  }

  _setWebpackConfigPath() {
    const Utils = this.serverless.utils;
    this.webpackConfigPath = path.join(process.cwd(), this.webpackFilename);

    if (!Utils.fileExistsSync(this.webpackConfigPath)) {
      return Promise.reject(new Error(`Could not find '${this.webpackConfigPath}' file.`));
    }
  }

  _setWebpackConfig() {
    process.env.SERVERLESS_STAGE = this.stage;
    this.webpackConfig = require(this.webpackConfigPath);

    this.webpackConfig.output = this.webpackConfig.output || {};
    this.webpackConfig.output.path = path.join(process.cwd(), '.spa');
    this.webpackConfig.output.publicPath = this.webpackConfig.output.publicPath || '/';
  }

  _setAppFolder() {
    const Utils = this.serverless.utils;
    this.appFolder = _.get(this.serverless, 'service.custom.spa.appFolder');

    if (!Utils.dirExistsSync(this.appFolder)) {
      return Promise.reject(new Error('appFolder not defined in custom.spa.'));
    }
  }

  _setDistFolder() {
    this.distFolder = path.join(process.cwd(), '.spa');
  }

  _setGZip() {
    this.gzip = _.get(this.serverless, 'service.custom.spa.gzip');
  }
}

module.exports = SPA;
<commit_msg>first version<commit_after>'use strict';
const path = require('path');
const zlib = require('zlib');
const fse = require('fs-extra');
const BbPromise = require('bluebird');
const _ = require('lodash');
const async = require('async');
const mime = require('mime-types');
const fs = require('fs');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');

class SPA {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.options = options;
    this.stage = options.stage || _.get(serverless, 'service.provider.stage')
    this.region = options.region || _.get(serverless, 'service.provider.region');
    this.provider = 'aws';
    this.aws = this.serverless.getProvider(this.provider);

    this.commands = {
      spa: {
        usage: 'Bundle your spa app and deploys to S3',
        lifecycleEvents: [
          'prepare',
          'cleanup',
          'bundle',
          'deploy'
        ],
        options: {
        },
        commands: {
          clean: {
            usage: 'Cleanup deployment folder',
            lifecycleEvents: [
              'prepare',
              'cleanup'
            ],
            options: {
            },
          },
          bundle: {
            usage: 'Bundle app',
            lifecycleEvents: [
              'prepare',
              'cleanup',
              'bundle'
            ],
            options: {
            },
          },
          serve: {
            usage: 'Open local webpack server',
            lifecycleEvents: [
              'prepare',
              'cleanup',
              'serve'
            ],
            options: {
            },
          },
          deploy: {
            usage: 'Deploy app',
            lifecycleEvents: [
              'prepare',
              'cleanup',
              'bundle',
              'deploy'
            ],
            options: {
            },
          },
        }
      },
    };

    this.hooks = {
      'spa:prepare': this.prepare.bind(this),
      'spa:clean:prepare': this.prepare.bind(this),
      'spa:bundle:prepare': this.prepare.bind(this),
      'spa:serve:prepare': this.prepare.bind(this),
      'spa:deploy:prepare': this.prepare.bind(this),

      'spa:cleanup': this.cleanup.bind(this),
      'spa:clean:cleanup': this.cleanup.bind(this),
      'spa:bundle:cleanup': this.cleanup.bind(this),
      'spa:serve:cleanup': this.cleanup.bind(this),
      'spa:deploy:cleanup': this.cleanup.bind(this),

      'spa:bundle': this.bundle.bind(this),
      'spa:bundle:bundle': this.bundle.bind(this),
      'spa:deploy:bundle': this.bundle.bind(this),

      'spa:deploy': this.deploy.bind(this),
      'spa:deploy:deploy': this.deploy.bind(this),

      'spa:serve:serve': this.serve.bind(this),

      'before:offline:start': this.offline.bind(this),
      'before:offline:start:init': this.offline.bind(this),
    };
  }

  prepare() {
    const Utils = this.serverless.utils;
    const Error = this.serverless.classes.Error;

    let error = this._setWebpackFilename();
    if (error) {
      return error;
    }

    error = this._setAppFolder();
    if (error) {
      return error;
    }

    this._setDistFolder();

    error = this._setWebpackConfigPath();
    if (error) {
      return error;
    }

    this._setWebpackConfig();

    error = this._setBucketName();
    if (error) {
      return error;
    }

    this._setGZip();
  }

  cleanup() {
    this.serverless.cli.log('Removing ".spa" folder');
    fse.removeSync(this.distFolder);
  }

  bundle() {
    const Error = this.serverless.classes.Error;
    this.serverless.cli.log('Bundling with Webpack');

    const compiler = webpack(this.webpackConfig);
    return BbPromise
      .fromCallback(cb => compiler.run(cb))
      .then(stats => {

        this.serverless.cli.consoleLog(stats.toString({
          colors: true,
          hash: false,
          version: false,
          chunks: false,
          children: false
        }));
        if (stats.compilation.errors.length) {
          throw new Error('Webpack compilation error, see above');
        }
        const outputPath = stats.compilation.compiler.outputPath;
        this.webpackOutputPath = outputPath;
        this.originalServicePath = this.serverless.config.servicePath;
        this.serverless.config.servicePath = outputPath;
        return stats;
      });
  }

  _appendHmrToEntries(entry, hmrEntries) {
    let result = {};
    if (typeof (entry) === 'string') {
      /*
        entry: 'file.js'
      */
      result = hmrEntries.concat(entry);
    } else if (Array.isArray(entry)) {
      /*
        entry: ['file.js']
      */
      result = hmrEntries.concat(entry);
    } else {
      /*
        entry: {
          app: 'file.js',
          vendor: [
            'f1.js',
            'b.js'
          ]
        }
      */
      for (let key in entry) {
        result[key] = this._appendHmrToEntries(entry[key], hmrEntries);
      }
    }

    return result;
  }

  serve() {
    const Error = this.serverless.classes.Error;

    const devServerOptions = _.extend(
      {
        port: 8080
      },
      this.webpackConfig.devServer,
      {
        contentBase: '.spa',
        stats: 'errors-only'
      }
    );

    const url = this._getDevServerURL(devServerOptions.https, devServerOptions.port);

    this._setHMR(devServerOptions.hot, url);

    return new BbPromise((resolve, reject) => {
      const compiler = webpack(this.webpackConfig);
      new WebpackDevServer(compiler, devServerOptions)
        .listen(devServerOptions.port, '0.0.0.0', (err) => {
          if (err) {
            reject(err);
          }

          this.serverless.cli.log(`Serving app at ${url}`);
        });
    });
  }

  _getDevServerURL(https, port) {
    const protocol = https ? 'https' : 'http';
    return `${protocol}://localhost:${port}/`;
  }

  _setHMR(hmrEnabled, url) {
    if (!hmrEnabled) {
      return;
    }

    const hmrEntries = [
      'webpack-dev-server/client?' + url,
      'webpack/hot/dev-server',
      'react-hot-loader/patch'
    ];

    this.webpackConfig.entry = this._appendHmrToEntries(this.webpackConfig.entry, hmrEntries);

    const plugins = this.webpackConfig.plugins;
    const pluginNames = plugins.map(plugin => plugin.constructor.name);

    if (!_.includes(pluginNames, 'HotModuleReplacementPlugin')) {
      this.webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
    }

    if (!_.includes(pluginNames, 'NamedModulesPlugin')) {
      this.webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
    }

    if (!_.includes(pluginNames, 'NoEmitOnErrorsPlugin')) {
      this.webpackConfig.plugins.push(new webpack.NoEmitOnErrorsPlugin());
    }
  }

  offline() {
    BbPromise
      .bind(this)
      .then(this.prepare)
      .then(this.cleanup)
      .then(this.serve);
  }

  deploy() {
    return this.aws.request('S3', 'listBuckets', {}, this.stage, this.region)
      .bind(this)
      .then(this._listBuckets)
      .then(this._listObjectsInBucket)
      .then(this._deleteObjectsFromBucket)
      .then(this._createBucket)
      .then(this._configureBucket)
      .then(this._configurePolicyForBucket)
      .then(this._uploadDirectory);
  }

  _listBuckets(data) {
    data.Buckets.forEach((bucket) => {
      if (bucket.Name === this.bucketName) {
        this.bucketExists = true;
        this.serverless.cli.log(`Bucket ${this.bucketName} already exists`);
      }
    });
  }

  _listObjectsInBucket() {
    if (!this.bucketExists) return BbPromise.resolve();

    this.serverless.cli.log(`Listing objects in bucket ${this.bucketName}...`);

    let params = {
      Bucket: this.bucketName
    };

    return this.aws.request('S3', 'listObjectsV2', params, this.stage, this.region);
  }

  _deleteObjectsFromBucket(data) {
    if (!this.bucketExists) {
      return BbPromise.resolve();
    }

    this.serverless.cli.log(`Deleting all objects from bucket ${this.bucketName}...`);

    if (!data.Contents[0]) {
      return BbPromise.resolve();
    }

    let Objects = _.map(data.Contents, (content) => _.pick(content, 'Key'));

    let params = {
      Bucket: this.bucketName,
      Delete: { Objects: Objects }
    };

    return this.aws.request('S3', 'deleteObjects', params, this.stage, this.region);
  }

  _createBucket() {
    if (this.bucketExists) return BbPromise.resolve();
    this.serverless.cli.log(`Creating bucket ${this.bucketName}...`);

    let params = {
      Bucket: this.bucketName
    };

    return this.aws.request('S3', 'createBucket', params, this.stage, this.region);
  }

  _configureBucket() {
    this.serverless.cli.log(`Configuring website bucket ${this.bucketName}...`);

    let params = {
      Bucket: this.bucketName,
      WebsiteConfiguration: {
        IndexDocument: { Suffix: 'index.html' },
        ErrorDocument: { Key: 'index.html' }
      }
    };

    return this.aws.request('S3', 'putBucketWebsite', params, this.stage, this.region)
  }

  _configurePolicyForBucket() {
    this.serverless.cli.log(`Configuring policy for bucket ${this.bucketName}...`);

    let policy = {
      Version: '2008-10-17',
      Id: 'Policy1392681112290',
      Statement: [
        {
          Sid: 'Stmt1392681101677',
          Effect: 'Allow',
          Principal: {
            AWS: '*'
          },
          Action: 's3:GetObject',
          Resource: `arn:aws:s3:::${this.bucketName}/*`
        }
      ]
    };

    let params = {
      Bucket: this.bucketName,
      Policy: JSON.stringify(policy)
    };

    return this.aws.request('S3', 'putBucketPolicy', params, this.stage, this.region);
  }

  _uploadDirectory(recursivePath) {
    this.serverless.cli.log('Uploading files...');
    let directoryPath = this.distFolder;
    if (JSON.stringify(recursivePath) !== "{}") {
        directoryPath = recursivePath
    }
    let files = fs.readdirSync(directoryPath)

    files = _.map(files, (file) => path.join(directoryPath, file));

    return async.each(files, (path) => {
      let stats = fs.statSync(path)
      return stats.isDirectory() ? this._uploadDirectory(path) : this._uploadFile(path);
    });
  }

  _uploadFile(filePath) {
    let fileKey = filePath.replace(this.distFolder, '').substr(1).replace(/\\/g, '/');

    this._gzipFile(filePath, fileKey, () => {
      this.serverless.cli.log(`Uploading file '${fileKey}'...`);

      const fileBuffer = fs.readFileSync(filePath)
      let params = {
        Bucket: this.bucketName,
        Key: fileKey,
        Body: fileBuffer,
        ContentType: mime.lookup(filePath)
      };

      if (this.gzip) {
        params.ContentEncoding = 'gzip';
      }

      // TODO: remove browser caching
      return this.aws.request('S3', 'putObject', params, this.stage, this.region);
    });
  }

  _gzipFile(filePath, filename, done) {
    if (!this.gzip) {
      done();
      return;
    }

    this.serverless.cli.log(`Compressing file '${filename}'`);

    const content = fs.readFileSync(filePath);
    zlib.gzip(content, (error, result) => {
      if (error) {
        this.serverless.cli.log(`Fail to compress file '${filename}'`, error);
        done();
        return;
      }

      fs.writeFileSync(filePath, result);
      done();
    });
  }

  _setWebpackFilename() {
    this.webpackFilename = _.get(this.serverless, 'service.custom.spa.webpack');

    if (!this.webpackFilename || typeof (this.webpackFilename) !== 'string') {
      this.webpackFilename = _.get(this.serverless, `service.custom.spa.webpack.${this.stage}`);
    }

    if (!this.webpackFilename) {
      return Promise.reject(new Error('Could not find webpack config.'));
    }
  }

  _setBucketName() {
    this.bucketName = _.get(this.serverless, 'service.custom.spa.bucket');

    if (!this.bucketName || typeof (this.bucketName) !== 'string') {
      this.bucketName = _.get(this.serverless, `service.custom.spa.bucket.${this.stage}`);
    }

    if (!this.bucketName) {
      return Promise.reject(new Error(`Could not find bucket name.`));
    }
  }

  _setWebpackConfigPath() {
    const Utils = this.serverless.utils;
    this.webpackConfigPath = path.join(process.cwd(), this.webpackFilename);

    if (!Utils.fileExistsSync(this.webpackConfigPath)) {
      return Promise.reject(new Error(`Could not find '${this.webpackConfigPath}' file.`));
    }
  }

  _setWebpackConfig() {
    process.env.SERVERLESS_STAGE = this.stage;
    this.webpackConfig = require(this.webpackConfigPath);

    this.webpackConfig.output = this.webpackConfig.output || {};
    this.webpackConfig.output.path = path.join(process.cwd(), '.spa');
    this.webpackConfig.output.publicPath = this.webpackConfig.output.publicPath || '/';
  }

  _setAppFolder() {
    const Utils = this.serverless.utils;
    this.appFolder = _.get(this.serverless, 'service.custom.spa.appFolder');

    if (!Utils.dirExistsSync(this.appFolder)) {
      return Promise.reject(new Error('appFolder not defined in custom.spa.'));
    }
  }

  _setDistFolder() {
    this.distFolder = path.join(process.cwd(), '.spa');
  }

  _setGZip() {
    this.gzip = _.get(this.serverless, 'service.custom.spa.gzip');
  }
}

module.exports = SPA;
<|endoftext|>


<commit_before>/*!
 * Morgan | Connect - logger
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var bytes = require('bytes');

/*!
 * Default log buffer duration.
 */

var defaultBufferDuration = 1000;

/**
 * Log requests with the given `options` or a `format` string.
 *
 * See README.md for documentation of options and formatting.
 *
 * @param {String|Function|Object} format or options
 * @return {Function} middleware
 * @api public
 */

exports = module.exports = function logger(options) {
  if (options && typeof options !== 'object') {
    options = { format: options };
  } else {
    options = options || {};
  }

  // output on request instead of response
  var immediate = options.immediate;

  // check if log entry should be skipped
  var skip = options.skip || function () { return false; };

  // format name
  var fmt = exports[options.format] || options.format || exports.default;

  // compile format
  if ('function' != typeof fmt) fmt = compile(fmt);

  // options
  var stream = options.stream || process.stdout
    , buffer = options.buffer;

  // buffering support
  if (buffer) {
    var realStream = stream
    var buf = []
    var timer = null
    var interval = 'number' == typeof buffer
      ? buffer
      : defaultBufferDuration

    // flush function
    var flush = function(){
      timer = null

      if (buf.length) {
        realStream.write(buf.join(''));
        buf.length = 0;
      }
    }

    // swap the stream
    stream = {
      write: function(str){
        if (timer === null) {
          timer = setTimeout(flush, interval)
        }

        buf.push(str);
      }
    };
  }

  return function logger(req, res, next) {
    req._startAt = process.hrtime();
    req._startTime = new Date;
    req._remoteAddress = req.connection && req.connection.remoteAddress;

    function logRequest(){
      res.removeListener('finish', logRequest);
      res.removeListener('close', logRequest);
      if (skip(req, res)) return;
      var line = fmt(exports, req, res);
      if (null == line) return;
      stream.write(line + '\n');
    };

    // immediate
    if (immediate) {
      logRequest();
    // proxy end to output logging
    } else {
      res.on('finish', logRequest);
      res.on('close', logRequest);
    }

    next();
  };
};

/**
 * Compile `fmt` into a function.
 *
 * @param {String} fmt
 * @return {Function}
 * @api private
 */

function compile(fmt) {
  fmt = fmt.replace(/"/g, '\\"');
  var js = '  return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){
    return '"\n    + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
  }) + '";'
  return new Function('tokens, req, res', js);
};

/**
 * Define a token function with the given `name`,
 * and callback `fn(req, res)`.
 *
 * @param {String} name
 * @param {Function} fn
 * @return {Object} exports for chaining
 * @api public
 */

exports.token = function(name, fn) {
  exports[name] = fn;
  return this;
};

/**
 * Define a `fmt` with the given `name`.
 *
 * @param {String} name
 * @param {String|Function} fmt
 * @return {Object} exports for chaining
 * @api public
 */

exports.format = function(name, fmt){
  exports[name] = fmt;
  return this;
};

/**
 * Default format.
 */

exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');

/**
 * Short format.
 */

exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');

/**
 * Tiny format.
 */

exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');

/**
 * dev (colored)
 */

exports.format('dev', function(tokens, req, res){
  var color = 32; // green
  var status = res.statusCode;

  if (status >= 500) color = 31; // red
  else if (status >= 400) color = 33; // yellow
  else if (status >= 300) color = 36; // cyan

  var fn = compile('\x1b[0m:method :url \x1b[' + color + 'm:status \x1b[0m:response-time ms - :res[content-length]\x1b[0m');

  return fn(tokens, req, res);
});

/**
 * request url
 */

exports.token('url', function(req){
  return req.originalUrl || req.url;
});

/**
 * request method
 */

exports.token('method', function(req){
  return req.method;
});

/**
 * response time in milliseconds
 */

exports.token('response-time', function(req, res){
  if (!res._header || !req._startAt) return '';
  var diff = process.hrtime(req._startAt);
  var ms = diff[0] * 1e3 + diff[1] * 1e-6;
  return ms.toFixed(3);
});

/**
 * UTC date
 */

exports.token('date', function(){
  return new Date().toUTCString();
});

/**
 * response status code
 */

exports.token('status', function(req, res){
  return res._header ? res.statusCode : null;
});

/**
 * normalized referrer
 */

exports.token('referrer', function(req){
  return req.headers['referer'] || req.headers['referrer'];
});

/**
 * remote address
 */

exports.token('remote-addr', function(req){
  if (req.ip) return req.ip;
  if (req._remoteAddress) return req._remoteAddress;
  if (req.connection) return req.connection.remoteAddress;
  return undefined;
});

/**
 * HTTP version
 */

exports.token('http-version', function(req){
  return req.httpVersionMajor + '.' + req.httpVersionMinor;
});

/**
 * UA string
 */

exports.token('user-agent', function(req){
  return req.headers['user-agent'];
});

/**
 * request header
 */

exports.token('req', function(req, res, field){
  return req.headers[field.toLowerCase()];
});

/**
 * response header
 */

exports.token('res', function(req, res, field){
  return (res._headers || {})[field.toLowerCase()];
});

<commit_msg>build: minor source tweaks<commit_after>/*!
 * morgan
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * Copyright(c) 2014 Jonathan Ong
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var bytes = require('bytes');

/**
 * Default log buffer duration.
 */

var defaultBufferDuration = 1000;

/**
 * Create a logger middleware.
 *
 * @param {String|Function|Object} format or options
 * @return {Function} middleware
 * @api public
 */

exports = module.exports = function morgan(options) {
  if (options && typeof options !== 'object') {
    options = { format: options };
  } else {
    options = options || {};
  }

  // output on request instead of response
  var immediate = options.immediate;

  // check if log entry should be skipped
  var skip = options.skip || function () { return false; };

  // format name
  var fmt = exports[options.format] || options.format || exports.default;

  // compile format
  if ('function' != typeof fmt) fmt = compile(fmt);

  // options
  var stream = options.stream || process.stdout
    , buffer = options.buffer;

  // buffering support
  if (buffer) {
    var realStream = stream
    var buf = []
    var timer = null
    var interval = 'number' == typeof buffer
      ? buffer
      : defaultBufferDuration

    // flush function
    var flush = function(){
      timer = null

      if (buf.length) {
        realStream.write(buf.join(''));
        buf.length = 0;
      }
    }

    // swap the stream
    stream = {
      write: function(str){
        if (timer === null) {
          timer = setTimeout(flush, interval)
        }

        buf.push(str);
      }
    };
  }

  return function logger(req, res, next) {
    req._startAt = process.hrtime();
    req._startTime = new Date;
    req._remoteAddress = req.connection && req.connection.remoteAddress;

    function logRequest(){
      res.removeListener('finish', logRequest);
      res.removeListener('close', logRequest);
      if (skip(req, res)) return;
      var line = fmt(exports, req, res);
      if (null == line) return;
      stream.write(line + '\n');
    };

    // immediate
    if (immediate) {
      logRequest();
    // proxy end to output logging
    } else {
      res.on('finish', logRequest);
      res.on('close', logRequest);
    }

    next();
  };
};

/**
 * Compile `fmt` into a function.
 *
 * @param {String} fmt
 * @return {Function}
 * @api private
 */

function compile(fmt) {
  fmt = fmt.replace(/"/g, '\\"');
  var js = '  return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){
    return '"\n    + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
  }) + '";'
  return new Function('tokens, req, res', js);
};

/**
 * Define a token function with the given `name`,
 * and callback `fn(req, res)`.
 *
 * @param {String} name
 * @param {Function} fn
 * @return {Object} exports for chaining
 * @api public
 */

exports.token = function(name, fn) {
  exports[name] = fn;
  return this;
};

/**
 * Define a `fmt` with the given `name`.
 *
 * @param {String} name
 * @param {String|Function} fmt
 * @return {Object} exports for chaining
 * @api public
 */

exports.format = function(name, fmt){
  exports[name] = fmt;
  return this;
};

/**
 * Default format.
 */

exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');

/**
 * Short format.
 */

exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');

/**
 * Tiny format.
 */

exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');

/**
 * dev (colored)
 */

exports.format('dev', function(tokens, req, res){
  var color = 32; // green
  var status = res.statusCode;

  if (status >= 500) color = 31; // red
  else if (status >= 400) color = 33; // yellow
  else if (status >= 300) color = 36; // cyan

  var fn = compile('\x1b[0m:method :url \x1b[' + color + 'm:status \x1b[0m:response-time ms - :res[content-length]\x1b[0m');

  return fn(tokens, req, res);
});

/**
 * request url
 */

exports.token('url', function(req){
  return req.originalUrl || req.url;
});

/**
 * request method
 */

exports.token('method', function(req){
  return req.method;
});

/**
 * response time in milliseconds
 */

exports.token('response-time', function(req, res){
  if (!res._header || !req._startAt) return '';
  var diff = process.hrtime(req._startAt);
  var ms = diff[0] * 1e3 + diff[1] * 1e-6;
  return ms.toFixed(3);
});

/**
 * UTC date
 */

exports.token('date', function(){
  return new Date().toUTCString();
});

/**
 * response status code
 */

exports.token('status', function(req, res){
  return res._header ? res.statusCode : null;
});

/**
 * normalized referrer
 */

exports.token('referrer', function(req){
  return req.headers['referer'] || req.headers['referrer'];
});

/**
 * remote address
 */

exports.token('remote-addr', function(req){
  if (req.ip) return req.ip;
  if (req._remoteAddress) return req._remoteAddress;
  if (req.connection) return req.connection.remoteAddress;
  return undefined;
});

/**
 * HTTP version
 */

exports.token('http-version', function(req){
  return req.httpVersionMajor + '.' + req.httpVersionMinor;
});

/**
 * UA string
 */

exports.token('user-agent', function(req){
  return req.headers['user-agent'];
});

/**
 * request header
 */

exports.token('req', function(req, res, field){
  return req.headers[field.toLowerCase()];
});

/**
 * response header
 */

exports.token('res', function(req, res, field){
  return (res._headers || {})[field.toLowerCase()];
});

<|endoftext|>


<commit_before>var Dict = require("collections/dict");
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var fs = require("fs");

app.get('/', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});

var rooms = new Dict({}, function (key) {
				 return "default: " + key;
				 });

function randexec(probas) {
	var ar = [];
	var i,sum = 0;

	for (i=0 ; i<probas.length-1 ; i++) // notice the '-1'
	{
		sum += (probas[i] / 100.0);
		ar[i] = sum;
	}

  // Then we get a random number and finds where it sits inside the probabilities
  // defined earlier

	var r = Math.random(); // returns [0,1]

	console.log('ar ' + ar);
	for (i=0 ; i<ar.length && r>=ar[i] ; i++) ;

  // Finally execute the function and return its result
	return i;
}

function setRoomOperation(roomId, operation) {
	var room = JSON.parse(rooms.get(roomId.toString()));
	rooms.set(roomId.toString(), "{\"operation\": " + operation + ", \"masterId\": \""+ room.masterId + "\"}");
}

io.on('connection', function(socket) {
    console.log('one user connected ' + socket.id);

	function setResult(method, message, data) {
	  console.log('result to return: ' + JSON.stringify({ method: method, message: message, data: data }));
        return JSON.stringify({ method: method, message: message, data: data });
    };

    socket.on('create room', function (roomId) {

        if (roomId !== undefined) {
			console.log('room created with id ' + roomId);
			rooms.add("{\"operation\": 0, \"masterId\": \""+ socket.id + "\"}", roomId.toString());

            socket.join(roomId);
            io.sockets.in(roomId).emit('result', setResult("create-room", 'Sala criada - ' + roomId, null));
		} else {
			console.log('failed creating room with id ' + roomId);
			socket.emit('result', setResult("failed-crete-room", 'Erro ao criar sala. Tente novamente mais tarde.', null))
		}
    });

    socket.on('join room', function (json) {
        var data = JSON.parse(json);
		console.log("data received " + data)
        if (data !== undefined && data.roomId !== undefined) {
			console.log('user ' + socket.id + ' entered room with id ' + data.roomId + ', with probability ' + rooms.get(data.roomId));
            socket.join(data.roomId);
            io.sockets.in(data.roomId).emit('result', setResult("join-room", data.user.userName + ' entrou na sala', JSON.stringify(data.user)));
        }
    });

    socket.on("leave room", function (json) {

        var data = JSON.parse(json);
        if (data !== undefined && data.roomId !== undefined && data.user !== undefined) {
            //var room = JSON.parse(rooms.get(data.roomId));
            var room = rooms.get(data.roomId);

			if (socket.id == room.masterId) {
			  console.log('room ' + data.roomId + ' deleted from list');
			  rooms.delete(data.roomId);
			}

            socket.leave(data.roomId);
            io.sockets.in(data.roomId).emit('result', setResult("leave-room", data.user.userName + ' saiu da sala', JSON.stringify(data.user)));
        }
    });

    socket.on('start round', function (roomId) {

        if (roomId !== undefined) {
			setTimeout(function () {
				var room = JSON.parse(rooms.get(roomId.toString()));
				var probas = [];

				switch (room.operation) {
					case 0:
					   probas = [0, 50, 30, 20];
					   break;
					case 1:
					   probas = [100, 0, 0, 0];
					   break;
					case 2:
					   probas = [100, 0, 0, 0];
					   break;
					case 3:
					   probas = [0, 70, 20, 10];
					   break;
					case 4:
					   probas = [0, 50, 30, 20];
					   break;
				}
				var fruit = randexec(probas);

				console.log('sorted fruit ' + fruit + ' in room with id ' + roomId);

				io.sockets.in(roomId).emit('result', setResult("send-message", "selectedFruit: " + fruit, null));
				setRoomOperation(roomId, 0);
			}, 12400);
            io.sockets.in(roomId).emit('result', setResult("start-round", 'Rodada iniciada', null));
        }
    });

    socket.on("chosen players", function (json) {

        var data = JSON.parse(json);
        if (data !== undefined && data.roomId !== undefined && data.users !== undefined) {
            io.sockets.in(data.roomId).emit('result', setResult("chosen-players", '', JSON.stringify(data.users)));
        }
    });

    socket.on("exit round", function (roomId) {

        if (roomId !== undefined)
            io.sockets.in(roomId).emit('result', setResult("exit-round", 'Saiu da rodada', null));
    });

    socket.on("get players list", function (masterSocketId) {
		if (masterSocketId !== undefined) {
			console.log('id received ' + masterSocketId)
            socket.to(masterSocketId).emit('result', setResult("get-players-list", null, null));
		}
    });

    socket.on("send players list", function (json) {

        var data = JSON.parse(json);
        if (data !== undefined && data.roomId !== undefined && data.users !== undefined) {
            io.sockets.in(data.roomId).emit('result', setResult("send-players-list", '', JSON.stringify(data.users)));
        }
    });

    socket.on('send message', function (json) {

		var data = JSON.parse(json);

		console.log('message ' + data);
		if (data !== undefined && data.roomId !== undefined) {
			console.log('message received ' + data.message);
			io.sockets.in(data.roomId).emit('result', setResult("send-message", data.message, null));
		}
    });

	socket.on('fruit probability', function (json) {
		// Json - {roomId: 0, operation: 0}
		var data = JSON.parse(json);
		console.log('received operation ' + data.operation + ' from room with id ' + data.roomId);
		// Operation:
		// Case 1 - Tentou
		// Case 3 - Bloqueou
		if (data.roomId !== undefined && data.operation !== undefined) {
			var room = JSON.parse(rooms.get(data.roomId));
			var operation = parseInt(room.operation) + parseInt(data.operation);
			setRoomOperation(data.roomId, operation);
		}
    });

    socket.on('master disconnect', function (roomId) {

        if (roomId !== undefined) {
            io.sockets.in(roomId).emit('result', setResult("master-disconnect", "Lder desconectado", null));
            console.log('master user disconnected ' + socket.id);
        }
    });

    socket.on('disconnect', function() {
        console.log('one user disconnected '+ socket.id);
    });

    socket.on('pau', function () {

        fs.writeFile("/tmp/test", "Hey there!", function (err) {
            if (err) {
                return console.log(err);
            }

            console.log("The file was saved!");
        });
    });

    socket.on('error', (error) => {

        fs.writeFile("/tmp/test", "Hey there!", function (err) {
            if (err) {
                return console.log(err);
            }

            console.log("The file was saved!");
        });

        socket.emit('exception', { errorMessage: error });

        console.log(error);
    });
});

var port = process.env.PORT || 1337;
server.listen(port, function () {
    console.log('server listening on *:' + port);
});

<commit_msg>Update with Try Catach<commit_after>var Dict = require("collections/dict");
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var fs = require("fs");

app.get('/', function (req, res) {
  res.sendFile(__dirname + '/index.html');
});

var rooms = new Dict({}, function (key) {
				 return "default: " + key;
				 });

function randexec(probas) {
	var ar = [];
	var i,sum = 0;

	for (i=0 ; i<probas.length-1 ; i++) // notice the '-1'
	{
		sum += (probas[i] / 100.0);
		ar[i] = sum;
	}

  // Then we get a random number and finds where it sits inside the probabilities
  // defined earlier

	var r = Math.random(); // returns [0,1]

	console.log('ar ' + ar);
	for (i=0 ; i<ar.length && r>=ar[i] ; i++) ;

  // Finally execute the function and return its result
	return i;
}

function setRoomOperation(roomId, operation) {
	var room = JSON.parse(rooms.get(roomId.toString()));
	rooms.set(roomId.toString(), "{\"operation\": " + operation + ", \"masterId\": \""+ room.masterId + "\"}");
}

function logError(fileName, ex) {

    fs.writeFile("log_errors/" + fileName + ".txt", ex, function (err) {
        if (err) {
            return console.log(err);
        }

        console.log("The file was saved!");
    });
}

io.on('connection', function (socket) {

    console.log('one user connected ' + socket.id);

	function setResult(method, message, data) {
	  console.log('result to return: ' + JSON.stringify({ method: method, message: message, data: data }));
        return JSON.stringify({ method: method, message: message, data: data });
    };

    socket.on('create room', function (roomId) {

        try {
            if (roomId !== undefined) {
                console.log('room created with id ' + roomId);
                rooms.add("{\"operation\": 0, \"masterId\": \""+ socket.id + "\"}", roomId.toString());

                socket.join(roomId);
                io.sockets.in(roomId).emit('result', setResult("create-room", 'Sala criada - ' + roomId, null));
            } else {
                console.log('failed creating room with id ' + roomId);
                socket.emit('result', setResult("failed-crete-room", 'Erro ao criar sala. Tente novamente mais tarde.', null))
            }
        }
        catch (ex) {
            logError("create_room", ex);
        }
    });

    socket.on('join room', function (json) {

        try {
            var data = JSON.parse(json);
            console.log("data received " + data)
            if (data !== undefined && data.roomId !== undefined) {
                console.log('user ' + socket.id + ' entered room with id ' + data.roomId + ', with probability ' + rooms.get(data.roomId));
                socket.join(data.roomId);
                io.sockets.in(data.roomId).emit('result', setResult("join-room", data.user.userName + ' entrou na sala', JSON.stringify(data.user)));
            }
        }
        catch (ex) {
            logError("join_room", ex);
        }
    });

    socket.on("leave room", function (json) {

        try {

            var data = JSON.parse(json);
            if (data !== undefined && data.roomId !== undefined && data.user !== undefined) {
                var room = rooms.get(data.roomId);

                console.log(room);

                if (socket.id == room.masterId) {
                    console.log('room ' + data.roomId + ' deleted from list');
                    rooms.delete(data.roomId);
                }

                console.log("opa");

                socket.leave(data.roomId);
                io.sockets.in(data.roomId).emit('result', setResult("leave-room", data.user.userName + ' saiu da sala', JSON.stringify(data.user)));
            }
        }
        catch (ex) {

            logError("leave_room", ex);
        }
    });

    socket.on('start round', function (roomId) {

        try {
            if (roomId !== undefined) {
                setTimeout(function () {
                    var room = JSON.parse(rooms.get(roomId.toString()));
                    var probas = [];

                    switch (room.operation) {
                        case 0:
                            probas = [0, 50, 30, 20];
                            break;
                        case 1:
                            probas = [100, 0, 0, 0];
                            break;
                        case 2:
                            probas = [100, 0, 0, 0];
                            break;
                        case 3:
                            probas = [0, 70, 20, 10];
                            break;
                        case 4:
                            probas = [0, 50, 30, 20];
                            break;
                    }
                    var fruit = randexec(probas);

                    console.log('sorted fruit ' + fruit + ' in room with id ' + roomId);

                    io.sockets.in(roomId).emit('result', setResult("send-message", "selectedFruit: " + fruit, null));
                    setRoomOperation(roomId, 0);
                }, 12400);
                io.sockets.in(roomId).emit('result', setResult("start-round", 'Rodada iniciada', null));
            }
        }
        catch (ex) {
            logError("start_round", ex);
        }
    });

    socket.on("chosen players", function (json) {

        try {
            var data = JSON.parse(json);
            if (data !== undefined && data.roomId !== undefined && data.users !== undefined) {
                io.sockets.in(data.roomId).emit('result', setResult("chosen-players", '', JSON.stringify(data.users)));
            }
        }
        catch (ex) {
            logError("chosen_players", ex);
        }
    });

    socket.on("exit round", function (roomId) {

        try {
            if (roomId !== undefined)
                io.sockets.in(roomId).emit('result', setResult("exit-round", 'Saiu da rodada', null));
        }
        catch (ex) {
            logError("exit_round", ex);
        }
    });

    socket.on("get players list", function (masterSocketId) {

        try {
            if (masterSocketId !== undefined) {
                console.log('id received ' + masterSocketId)
                socket.to(masterSocketId).emit('result', setResult("get-players-list", null, null));
            }
        }
        catch (ex) {
            logError("get_players_list", ex);
        }
    });

    socket.on("send players list", function (json) {

        try {

            var data = JSON.parse(json);
            if (data !== undefined && data.roomId !== undefined && data.users !== undefined) {
                io.sockets.in(data.roomId).emit('result', setResult("send-players-list", '', JSON.stringify(data.users)));
            }
        }
        catch (ex) {
            logError("send_players_list", ex);
        }
    });

    socket.on('send message', function (json) {

        try {
            var data = JSON.parse(json);

            console.log('message ' + data);
            if (data !== undefined && data.roomId !== undefined) {
                console.log('message received ' + data.message);
                io.sockets.in(data.roomId).emit('result', setResult("send-message", data.message, null));
            }
        }
        catch (ex) {
            logError("send_message", ex);
        }
    });

	socket.on('fruit probability', function (json) {

	    try {

	        // Json - {roomId: 0, operation: 0}
	        var data = JSON.parse(json);
	        console.log('received operation ' + data.operation + ' from room with id ' + data.roomId);
	        // Operation:
	        // Case 1 - Tentou
	        // Case 3 - Bloqueou
	        if (data.roomId !== undefined && data.operation !== undefined) {
	            var room = JSON.parse(rooms.get(data.roomId));
	            var operation = parseInt(room.operation) + parseInt(data.operation);
	            setRoomOperation(data.roomId, operation);
	        }
	    }
	    catch (ex) {
	        logError("fruit_probability", ex);
	    }
    });

    socket.on('master disconnect', function (roomId) {

        try {

            if (roomId !== undefined) {
                io.sockets.in(roomId).emit('result', setResult("master-disconnect", "Lder desconectado", null));
                console.log('master user disconnected ' + socket.id);
            }
        }
        catch (ex) {
            logError("master_disconnect", ex);
        }
    });

    socket.on('disconnect', function() {
        console.log('one user disconnected '+ socket.id);
    });

    socket.on('error', (error) => {

        fs.writeFile("log_errors/error_socket.txt", error, function (err) {
            if (err) {
                return console.log(err);
            }

            console.log("The file was saved!");
        });

        console.log(error);
    });
});

var port = process.env.PORT || 1337;
server.listen(port, function () {
    console.log('server listening on *:' + port);
});

<|endoftext|>


<commit_before>'use strict';

var util = require('./lib/util');

exports = module.exports = Parth;

function Parth(){
  if(!(this instanceof Parth)){
    return new Parth();
  }

  this.store = {children: {}};
  this.regex = [];
  this.regex.master = /(?:[])/;
}

// ## parth.set(path)
// > path to regex classification
// > TODO: give support for regexp input
//
// arguments
//  - path, type `string`
//  - options, type `object` optional with all extra information
//
// returns
//  - null for non-supported types
//  - regular expression from the path
//
var noParamRE = /(^|[/. ]+)(\([^?].+?\)+)/g;
var paramRE = /(^|[ /.]):([\w_-]+)(\(.+?\)+)?/g;

Parth.prototype.set = function(path, opt){
  var o = util.boil(path, opt); if(!o){ return null; }

  var sep, index = -1;
  var stem = o.path.replace(noParamRE, function($0, $1, $2){
    return $1 + ':' + (++index) + $2;
  });

  var parsed = new RegExp('^' +
    stem.replace(/\S+/g, function(s){
      sep = (s.match(/\//) || s.match(/\./) || ' ')[0].trim();
      return s.replace(paramRE, function($0, $1, $2, $3){
        return $1 + ($3 || '([^'+sep+' ]+)');
      });
    }).replace(/[^?( )+*$]+(?=\(|$)/g, util.escapeRegExp)
  );

  parsed.path = o.path;
  if(index > -1){ parsed.stem = stem; }
  parsed.depth = util.boil.argv(o.path).length;

  // avoid mutation of main object
  if(this.store.children[o.path]){ return parsed; }

  this.regex.push(parsed);

  // ## order regexes according to
  // - depth (number of separation tokens, [ /.])
  // - if that fails, use localCompare

  this.regex.sort(function(x, y){
    return (y.depth - x.depth) || y.path.localeCompare(x.path);
  });

  // ## sum up all learned
  // - void all groups
  // - make a giant regex

  this.regex.master = new RegExp(
    '(' + this.regex.map(util.voidRE).join(')|(') + ')'
  );

  if(/:/.test(o.path)){ o.regex = new RegExp(parsed.source); }
  this.store.children[o.path] = o;
  return parsed;
};

// ## parth.get(path[, options])
// > take a string or array, return the matching path
//
// arguments
//  - path, type `string`
//  - options, type `object` optional holding all extra information
//
// return
//  - null for non-supported types or not matching path
//  - regex with for the matching path
//
Parth.prototype.get = function(path, opt){
  var o = opt || { }; o.notFound = true;
  o = util.boil(path, o); if(!o){ return null; }

  var found = this.regex.master.exec(o.path);
  if(!found){ return null; }

  o.match = found[0].trim();
  var regex = this.regex[found.indexOf(found.shift())];
  if(!opt){ return regex; }

  var index = -1, stem = regex.stem || regex.path;
  var params = {_: o.path.match(regex).slice(1)};
  stem.replace(paramRE, function($0, $1, $2){
    params[$2] = params._[++index];
    params._[index] = $2;
  });

  o.notFound = o.path.replace(o.match, '') || false;
  if(index < 0){ return regex; }
  o.params = params;
  return regex;
};
<commit_msg>minor: white space<commit_after>'use strict';

var util = require('./lib/util');

exports = module.exports = Parth;

function Parth(){
  if(!(this instanceof Parth)){
    return new Parth();
  }

  this.store = {children: {}};
  this.regex = [];
  this.regex.master = /(?:[])/;
}

// ## parth.set(path)
// > path to regex classification
// > TODO: give support for regexp input
//
// arguments
//  - path, type `string`
//  - options, type `object` optional with all extra information
//
// returns
//  - null for non-supported types
//  - regular expression from the path
//
var noParamRE = /(^|[/. ]+)(\([^?].+?\)+)/g;
var paramRE = /(^|[ /.]):([\w_-]+)(\(.+?\)+)?/g;

Parth.prototype.set = function(path, opt){
  var o = util.boil(path, opt); if(!o){ return null; }
  var sep, index = -1;

  var stem = o.path.replace(noParamRE, function($0, $1, $2){
    return $1 + ':' + (++index) + $2;
  });

  var parsed = new RegExp('^' +
    stem.replace(/\S+/g, function(s){
      sep = (s.match(/\//) || s.match(/\./) || ' ')[0].trim();
      return s.replace(paramRE, function($0, $1, $2, $3){
        return $1 + ($3 || '([^'+sep+' ]+)');
      });
    }).replace(/[^?( )+*$]+(?=\(|$)/g, util.escapeRegExp)
  );

  parsed.path = o.path;
  if(index > -1){ parsed.stem = stem; }
  parsed.depth = util.boil.argv(o.path).length;

  // avoid mutation of main object
  if(this.store.children[o.path]){ return parsed; }

  this.regex.push(parsed);

  // ## order regexes according to
  // - depth (number of separation tokens, [ /.])
  // - if that fails, use localCompare

  this.regex.sort(function(x, y){
    return (y.depth - x.depth) || y.path.localeCompare(x.path);
  });

  // ## sum up all learned
  // - void all groups
  // - make a giant regex

  this.regex.master = new RegExp(
    '(' + this.regex.map(util.voidRE).join(')|(') + ')'
  );

  if(/:/.test(o.path)){ o.regex = new RegExp(parsed.source); }
  this.store.children[o.path] = o;
  return parsed;
};

// ## parth.get(path[, options])
// > take a string or array, return the matching path
//
// arguments
//  - path, type `string`
//  - options, type `object` optional holding all extra information
//
// return
//  - null for non-supported types or not matching path
//  - regex with for the matching path
//
Parth.prototype.get = function(path, opt){
  var o = opt || { }; o.notFound = true;
  o = util.boil(path, o); if(!o){ return null; }

  var found = this.regex.master.exec(o.path);
  if(!found){ return null; }

  o.match = found[0].trim();
  var regex = this.regex[found.indexOf(found.shift())];
  if(!opt){ return regex; }

  var index = -1, stem = regex.stem || regex.path;
  var params = {_: o.path.match(regex).slice(1)};
  stem.replace(paramRE, function($0, $1, $2){
    params[$2] = params._[++index];
    params._[index] = $2;
  });

  o.notFound = o.path.replace(o.match, '') || false;
  if(index < 0){ return regex; }
  o.params = params;
  return regex;
};
<|endoftext|>


<commit_before>'use strict'

var delay = 5 // ms delay to wait before scheduling a GC after an element should be cleared

var ExpireArray = module.exports = function (timeout) {
  if (!(this instanceof ExpireArray)) return new ExpireArray(timeout)
  if (!Number.isFinite(timeout)) throw new Error('Timeout must be a number')
  this._timeout = timeout
  this._db = []
}

ExpireArray.prototype._scheduleGC = function (wait) {
  if (this._nextGC) return
  wait = wait === undefined ? this._timeout : wait
  this._nextGC = setTimeout(this._gc.bind(this), wait)
  if (this._nextGC.unref) this._nextGC.unref()
}

ExpireArray.prototype._gc = function () {
  var now = Date.now()
  while (this._db.length && this._db[0][0] < now) {
    this._db.shift()
  }
  this._nextGC = null
  if (this._db.length) this._scheduleGC(this._db[0][0] - Date.now() + delay)
}

ExpireArray.prototype.push = function (elm) {
  if (!this._db.length) this._scheduleGC()
  this._db.push([Date.now() + this._timeout, elm])
}

ExpireArray.prototype.all = function () {
  return this._db.map(function (elm) {
    return elm[1]
  })
}

/**
 * forwarded functions
 */

ExpireArray.prototype.every = function (fn, self) {
  return this._db.every(function (elm, index) {
    return fn.call(self, elm[1], index)
  })
}

ExpireArray.prototype.forEach = function (fn, self) {
  this._db.forEach(function (elm, index) {
    fn.call(self, elm[1], index)
  })
}

ExpireArray.prototype.map = function (fn, self) {
  return this._db.map(function (elm, index) {
    return fn.call(self, elm[1], index)
  })
}

ExpireArray.prototype.some = function (fn, self) {
  return this._db.some(function (elm, index) {
    return fn.call(self, elm[1], index)
  })
}
<commit_msg>Hide GC scheduling algorithm<commit_after>'use strict'

var delay = 5 // ms delay to wait before scheduling a GC after an element should be cleared

var ExpireArray = module.exports = function (timeout) {
  if (!(this instanceof ExpireArray)) return new ExpireArray(timeout)
  if (!Number.isFinite(timeout)) throw new Error('Timeout must be a number')
  this._timeout = timeout
  this._db = []
}

ExpireArray.prototype._scheduleGC = function () {
  if (this._nextGC) return
  var ts = this._db.length ? this._db[0][0] - Date.now() + delay : this._timeout
  this._nextGC = setTimeout(this._gc.bind(this), ts)
  if (this._nextGC.unref) this._nextGC.unref()
}

ExpireArray.prototype._gc = function () {
  var now = Date.now()
  while (this._db.length && this._db[0][0] < now) {
    this._db.shift()
  }
  this._nextGC = null
  this._scheduleGC()
}

ExpireArray.prototype.push = function (elm) {
  if (!this._db.length) this._scheduleGC()
  this._db.push([Date.now() + this._timeout, elm])
}

ExpireArray.prototype.all = function () {
  return this._db.map(function (elm) {
    return elm[1]
  })
}

/**
 * forwarded functions
 */

ExpireArray.prototype.every = function (fn, self) {
  return this._db.every(function (elm, index) {
    return fn.call(self, elm[1], index)
  })
}

ExpireArray.prototype.forEach = function (fn, self) {
  this._db.forEach(function (elm, index) {
    fn.call(self, elm[1], index)
  })
}

ExpireArray.prototype.map = function (fn, self) {
  return this._db.map(function (elm, index) {
    return fn.call(self, elm[1], index)
  })
}

ExpireArray.prototype.some = function (fn, self) {
  return this._db.some(function (elm, index) {
    return fn.call(self, elm[1], index)
  })
}
<|endoftext|>


<commit_before>// Example express application adding the parse-server module to expose Parse
// compatible API routes.

var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var path = require('path');

var databaseUri = process.env.DATABASE_URI || process.env.MONGODB_URI;

if (!databaseUri) {
  console.log('DATABASE_URI not specified, falling back to localhost.');
}

var api = new ParseServer({
  databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
  cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
  appId: process.env.APP_ID || 'myAppId',
  masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
  serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',  // Don't forget to change to https if needed
  liveQuery: {
    classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
  }
});
// Client-keys like the javascript key or the .NET key are not necessary with parse-server
// If you wish you require them, you can set them as options in the initialization above:
// javascriptKey, restAPIKey, dotNetKey, clientKey

var app = express();

// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));

// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || '/parse';
app.use(mountPath, api);

// Parse Server plays nicely with the rest of your web routes
app.get('/', function(req, res) {
  res.status(200).send('Make sure to star the parse-server repo on GitHub!');
});

// There will be a test page available on the /test path of your server url
// Remove this before launching your app
app.get('/test', function(req, res) {
  res.sendFile(path.join(__dirname, '/public/test.html'));
});

var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function() {
    console.log('parse-server-example running on port ' + port + '.');
});

// This will enable the Live Query real-time server
ParseServer.createLiveQueryServer(httpServer);
<commit_msg>Added cheeky line back to default route.<commit_after>// Example express application adding the parse-server module to expose Parse
// compatible API routes.

var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var path = require('path');

var databaseUri = process.env.DATABASE_URI || process.env.MONGODB_URI;

if (!databaseUri) {
  console.log('DATABASE_URI not specified, falling back to localhost.');
}

var api = new ParseServer({
  databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
  cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
  appId: process.env.APP_ID || 'myAppId',
  masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
  serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',  // Don't forget to change to https if needed
  liveQuery: {
    classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
  }
});
// Client-keys like the javascript key or the .NET key are not necessary with parse-server
// If you wish you require them, you can set them as options in the initialization above:
// javascriptKey, restAPIKey, dotNetKey, clientKey

var app = express();

// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));

// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || '/parse';
app.use(mountPath, api);

// Parse Server plays nicely with the rest of your web routes
app.get('/', function(req, res) {
  res.status(200).send('I dream of being a website.  Please star the parse-server repo on GitHub!');
});

// There will be a test page available on the /test path of your server url
// Remove this before launching your app
app.get('/test', function(req, res) {
  res.sendFile(path.join(__dirname, '/public/test.html'));
});

var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function() {
    console.log('parse-server-example running on port ' + port + '.');
});

// This will enable the Live Query real-time server
ParseServer.createLiveQueryServer(httpServer);
<|endoftext|>


<commit_before>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack bot built with Botkit.

This bot demonstrates many of the core features of Botkit:

* Connect to Slack using the real time API
* Receive messages based on "spoken" patterns
* Reply to messages
* Use the conversation system to ask questions
* Use the built in storage system to store and retrieve information
  for a user.

# RUN THE BOT:

  Get a Bot token from Slack:

    -> http://my.slack.com/services/new/bot

  Run your bot from the command line:

    token=<MY TOKEN> node slack_bot.js

# USE THE BOT:

  Find your bot inside Slack to send it a direct message.

  Say: "Hello"

  The bot will reply "Hello!"

  Say: "who are you?"

  The bot will tell you its name, where it running, and for how long.

  Say: "Call me <nickname>"

  Tell the bot your nickname. Now you are friends.

  Say: "who am I?"

  The bot will tell you your nickname, if it knows one for you.

  Say: "shutdown"

  The bot will ask if you are sure, and then shut itself down.

  Make sure to invite your bot into other channels using /invite @<my bot>!

# EXTEND THE BOT:

  Botkit has many features for building cool and useful bots!

  Read all about it here:

    -> http://howdy.ai/botkit

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

var http = require('http');
var https = require('https');

var allContactTypes = ['direct_message', 'direct_mention', 'mention'];

if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('botkit/lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true,
    logLevel: 2
});

var bot = controller.spawn({
    token: process.env.token
}).startRTM();

controller.hears(['that conference'], ['ambient'], function(bot, message) {
    bot.startConversation(message, function(err, convo) {
        if (!err) {
            var stop = {
                pattern: ['stop', 'no', 'OMG'],
                callback: function(response, convo) {
                    convo.say('well, you asked...');
                    convo.stop();
                }
            };

            var what = {
                pattern: ['what?', 'ummm...'],
                callback: function(response, convo) {
                    convo.repeat();
                    convo.next();
                }
            };

            convo.ask('which conference?', [
                {
                    pattern: ['that conference'],
                    callback: function(response, convo) {
                        convo.ask('this conference?', [
                            {
                                pattern: 'yes',
                                callback: function(response, convo) {
                                    convo.ask('yes conference?', [
                                        {
                                            pattern: 'no',
                                            callback: function(response, convo) {
                                                convo.say("well now I'm confused");
                                                convo.stop();
                                            }
                                        },
                                        stop,
                                        what
                                    ]);
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'no, that conference',
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            },
                            stop,
                            what
                        ]);
                        convo.next();
                    }
                },
                stop,
                what
            ]);

            convo.on('end', function(convo) {
                if (convo.status == 'completed') {
                    bot.reply(message, 'OK! That Conference');
                } else {
                    // this happens if the conversation ended prematurely for some reason
                    bot.reply(message, 'yes, That Conference!');
                }
            });
        }
    });
});

controller.hears(['hello', 'hi', 'yo'], allContactTypes, function(bot, message) {

    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    }, function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(', err);
        }
    });

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'Hello ' + user.name + '!!');
        } else {
            bot.reply(message, 'Hello.');
        }
    });
});

controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {
    var name = message.match[1];
    controller.storage.users.get(message.user, function(err, user) {
        if (!user) {
            user = {
                id: message.user,
            };
        }
        user.name = name;
        controller.storage.users.save(user, function(err, id) {
            bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
        });
    });
});

controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention', function(bot, message) {

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'Your name is ' + user.name);
        } else {
            bot.startConversation(message, function(err, convo) {
                if (!err) {
                    convo.say('I do not know your name yet!');
                    convo.ask('What should I call you?', function(response, convo) {
                        convo.ask('You want me to call you `' + response.text + '`?', [
                            {
                                pattern: 'yes',
                                callback: function(response, convo) {
                                    // since no further messages are queued after this,
                                    // the conversation will end naturally with status == 'completed'
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'no',
                                callback: function(response, convo) {
                                    // stop the conversation. this will cause it to end with status == 'stopped'
                                    convo.stop();
                                }
                            },
                            {
                                default: true,
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            }
                        ]);

                        convo.next();

                    }, {'key': 'nickname'}); // store the results in a field called nickname

                    convo.on('end', function(convo) {
                        if (convo.status == 'completed') {
                            bot.reply(message, 'OK! I will update my dossier...');

                            controller.storage.users.get(message.user, function(err, user) {
                                if (!user) {
                                    user = {
                                        id: message.user,
                                    };
                                }
                                user.name = convo.extractResponse('nickname');
                                controller.storage.users.save(user, function(err, id) {
                                    bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
                                });
                            });

                        } else {
                            // this happens if the conversation ended prematurely for some reason
                            bot.reply(message, 'OK, nevermind!');
                        }
                    });
                }
            });
        }
    });
});

controller.hears(['about (.*)', 'tell me about (.*)', 'speaker (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {

    var speakerName = message.match[1];

    var options = {
        host: 'www.thatconference.com',
        port: 443,
        path: '/api3/Speakers/GetSpeakers?year=2016',
        method: 'GET'
    };

    https.request(options, function(res) {
        console.log('STATUS: ' + res.statusCode);
        console.log('HEADERS: ' + JSON.stringify(res.headers));
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            var speakers = JSON.parse(chunk);
            for (var speaker in speakers) {
                if (speaker.FirstName == speakerName || speaker.LastName == speakerName ){
                    bot.reply(message, speaker.Biography);
                }
            }
        });
    }).end();
});

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

});

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}

/*
controller.hears(['shutdown'], 'direct_message,direct_mention,mention', function(bot, message) {

    bot.startConversation(message, function(err, convo) {

        convo.ask('Are you sure you want me to shutdown?', [
            {
                pattern: bot.utterances.yes,
                callback: function(response, convo) {
                    convo.say('Bye!');
                    convo.next();
                    setTimeout(function() {
                        process.exit();
                    }, 3000);
                }
            },
        {
            pattern: bot.utterances.no,
            default: true,
            callback: function(response, convo) {
                convo.say('*Phew!*');
                convo.next();
            }
        }
        ]);
    });
});
*/<commit_msg>clean up @csell5 auful codez<commit_after>var http = require('http');
var https = require('https');

var allContactTypes = ['direct_message', 'direct_mention', 'mention'];

if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('botkit/lib/Botkit.js');
var os = require('os');

var controller = Botkit.slackbot({
    debug: true,
    logLevel: 2
});

var bot = controller.spawn({
    token: process.env.token
}).startRTM();

controller.hears(['that conference'], ['ambient'], function(bot, message) {
    bot.startConversation(message, function(err, convo) {
        if (!err) {
            var stop = {
                pattern: ['stop', 'no', 'OMG'],
                callback: function(response, convo) {
                    convo.say('well, you asked...');
                    convo.stop();
                }
            };

            var what = {
                pattern: ['what?', 'ummm...'],
                callback: function(response, convo) {
                    convo.repeat();
                    convo.next();
                }
            };

            convo.ask('which conference?', [
                {
                    pattern: ['that conference'],
                    callback: function(response, convo) {
                        convo.ask('this conference?', [
                            {
                                pattern: 'yes',
                                callback: function(response, convo) {
                                    convo.ask('yes conference?', [
                                        {
                                            pattern: 'no',
                                            callback: function(response, convo) {
                                                convo.say("well now I'm confused");
                                                convo.stop();
                                            }
                                        },
                                        stop,
                                        what
                                    ]);
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'no, that conference',
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            },
                            stop,
                            what
                        ]);
                        convo.next();
                    }
                },
                stop,
                what
            ]);

            convo.on('end', function(convo) {
                if (convo.status == 'completed') {
                    bot.reply(message, 'OK! That Conference');
                } else {
                    // this happens if the conversation ended prematurely for some reason
                    bot.reply(message, 'yes, That Conference!');
                }
            });
        }
    });
});

controller.hears(['hello', 'hi', 'yo'], allContactTypes, function(bot, message) {

    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    }, function(err, res) {
        if (err) {
            bot.botkit.log('Failed to add emoji reaction :(', err);
        }
    });

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'Hello ' + user.name + '!!');
        } else {
            bot.reply(message, 'Hello.');
        }
    });
});

controller.hears(['call me (.*)', 'my name is (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {
    var name = message.match[1];
    controller.storage.users.get(message.user, function(err, user) {
        if (!user) {
            user = {
                id: message.user,
            };
        }
        user.name = name;
        controller.storage.users.save(user, function(err, id) {
            bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
        });
    });
});

controller.hears(['what is my name', 'who am i'], 'direct_message,direct_mention,mention', function(bot, message) {

    controller.storage.users.get(message.user, function(err, user) {
        if (user && user.name) {
            bot.reply(message, 'Your name is ' + user.name);
        } else {
            bot.startConversation(message, function(err, convo) {
                if (!err) {
                    convo.say('I do not know your name yet!');
                    convo.ask('What should I call you?', function(response, convo) {
                        convo.ask('You want me to call you `' + response.text + '`?', [
                            {
                                pattern: 'yes',
                                callback: function(response, convo) {
                                    // since no further messages are queued after this,
                                    // the conversation will end naturally with status == 'completed'
                                    convo.next();
                                }
                            },
                            {
                                pattern: 'no',
                                callback: function(response, convo) {
                                    // stop the conversation. this will cause it to end with status == 'stopped'
                                    convo.stop();
                                }
                            },
                            {
                                default: true,
                                callback: function(response, convo) {
                                    convo.repeat();
                                    convo.next();
                                }
                            }
                        ]);

                        convo.next();

                    }, {'key': 'nickname'}); // store the results in a field called nickname

                    convo.on('end', function(convo) {
                        if (convo.status == 'completed') {
                            bot.reply(message, 'OK! I will update my dossier...');

                            controller.storage.users.get(message.user, function(err, user) {
                                if (!user) {
                                    user = {
                                        id: message.user,
                                    };
                                }
                                user.name = convo.extractResponse('nickname');
                                controller.storage.users.save(user, function(err, id) {
                                    bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.');
                                });
                            });

                        } else {
                            // this happens if the conversation ended prematurely for some reason
                            bot.reply(message, 'OK, nevermind!');
                        }
                    });
                }
            });
        }
    });
});

controller.hears(['about (.*)', 'tell me about (.*)', 'speaker (.*)'], 'direct_message,direct_mention,mention', function(bot, message) {

    var speakerName = message.match[1];

    var options = {
        host: 'www.thatconference.com',
        port: 443,
        path: '/api3/Speakers/GetSpeakers?year=2016',
        method: 'GET'
    };

    https.request(options, function(res) {
        console.log('STATUS: ' + res.statusCode);
        console.log('HEADERS: ' + JSON.stringify(res.headers));
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            var speakers = JSON.parse(chunk);
            for (var speaker in speakers) {
                if (speaker.FirstName == speakerName || speaker.LastName == speakerName ){
                    bot.reply(message, speaker.Biography);
                }
            }
        });
    }).end();
});

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

});

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}

/*
controller.hears(['shutdown'], 'direct_message,direct_mention,mention', function(bot, message) {

    bot.startConversation(message, function(err, convo) {

        convo.ask('Are you sure you want me to shutdown?', [
            {
                pattern: bot.utterances.yes,
                callback: function(response, convo) {
                    convo.say('Bye!');
                    convo.next();
                    setTimeout(function() {
                        process.exit();
                    }, 3000);
                }
            },
        {
            pattern: bot.utterances.no,
            default: true,
            callback: function(response, convo) {
                convo.say('*Phew!*');
                convo.next();
            }
        }
        ]);
    });
});
*/<|endoftext|>