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

'use strict';

// Import this first so we can use it to wrap other modules we import.
var promisify = require("promisify-node");

var getGitHubToken = promisify(require('get-github-token'));
var ghPages = require('gh-pages');
var gitconfiglocal = require('gitconfiglocal');
var path = require('path');
var swPrecache = require('sw-precache');
var readYaml = require('read-yaml');
var travisEncrypt = promisify(require('travis-encrypt'));
var writeYaml = require('write-yaml');

/**
 * Get the slug (GitHub username/repo combination) for the 'origin' remote
 * in the repository that contains the current working directory.
 * XXX Replace with github-slug module?
 */
function getSlug(callback) {
  gitconfiglocal('./', function(error, config) {
    if (error) {
      callback(error);
      return;
    }

    if ('remote' in config && 'origin' in config.remote && 'url' in config.remote.origin) {
      var url = config.remote.origin.url;
      var match;
      if (match = url.match(/^git@github.com:([^/]+)\/([^.]+)\.git$/) ||
                  url.match(/^https:\/\/github.com\/([^/]+)\/([^.]+)\.git$/)) {
        callback(null, match[1] + '/' + match[2]);
        return;
      }
      callback('could not parse value of origin remote URL: ' + url);
      return;
    }

    callback('repo has no origin remote');
  });
}
getSlug = promisify(getSlug);

function configure(callback) {
  process.stdout.write(
    '\n' +
    'Oghliner will configure your repository to automatically deploy your app\n' +
    'to GitHub Pages using Travis CI.\n' +
    '\n'
  );

  // Save the slug in the closure so we can use it across the promise chain
  // without having to pass it down the chain.
  var slug;

  getSlug()
  .then(function(aSlug) {
    slug = aSlug;
    process.stdout.write(
      'The "origin" remote of your repository is "' + slug + '".\n' +
      '\n' +
      'Make sure Travis knows about your repository, and your repository is active\n' +
      'in Travis, by going to https://travis-ci.org/profile, pressing the Sync button\n' +
      '(if needed), and activating the repository (if it isn\'t already active).\n' +
      '\n' +
      'Requesting a GitHub personal access token that Travis will use\n' +
      'to deploy your app.  In order to get the token, I need your username\n' +
      'and password (and two-factor authentication code, if appropriate).\n' +
      '\n' +
      'For more information about personal access tokens or to view the token\n' +
      'I create, see https://github.com/settings/tokens.\n' +
      '\n'
    );

    var note = 'Oghliner token for ' + slug;
    var url = 'https://github.com/mozilla/oghliner';
    return getGitHubToken(['public_repo'], note, url);
  },
  function(err) {
    process.stdout.write(
      '\n' +
      'I wasn\'t able to determine your GitHub username and repo.  Are you running\n' +
      'this command in Git repository whose "origin" remote is on GitHub?  The error\n' +
      'I received is: ' + err + '\n' +
      '\n'
    );
    callback(err);
  })
  .then(function(token) {
    process.stdout.write(
      '\n' +
      'I retrieved a GitHub personal access token.  Next I\'ll encrypt it\n' +
      'with Travis\'s public key so I can add the token to the Travis configuration\n' +
      'without leaking it in public build logs…\n' +
      '\n'
    );

    return travisEncrypt(slug, 'GH_TOKEN=' + token, undefined, undefined);
  })
  .then(function(blob) {
    process.stdout.write(
      'I encrypted the token. Next I\'ll write it to the Travis configuration…\n' +
      '\n'
    );

    var travisYml = readYaml.sync('.travis.yml');

    if (!('env' in travisYml)) {
      travisYml.env = {};
    }

    if (!('global' in travisYml.env)) {
      travisYml.env.global = [];
    }

    travisYml.env.global.push({ secure: blob });
    writeYaml.sync('.travis.yml', travisYml);
  })
  .then(function() {
    process.stdout.write(
      'I wrote the encrypted token to the Travis configuration.  You\'re ready\n' +
      'to auto-deploy using Travis!  Just commit the change to your "master" branch,\n' +
      'push the change back to the origin remote, and then visit\n' +
      'https://travis-ci.org/' + slug + '/builds to see the build status.\n' +
      '\n' +
      'If the build is successful, the "after_success" build step should show\n' +
      'that Travis deployed your app to GitHub Pages.  It should look like this:\n' +
      '\n' +
      '$ [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && gulp deploy\n' +
      '\n'
    );
  })
  .catch(function(err) {
    callback(err);
  })
  .done(function() {
    callback();
  });
}

function offline(config, callback) {
  var rootDir = config.rootDir || './';
  var fileGlobs = config.fileGlobs || ['**/*'];
  swPrecache.write(path.join(rootDir, 'offline-worker.js'), {
    staticFileGlobs: fileGlobs.map(function(v) { return path.join(rootDir, v) }),
    stripPrefix: rootDir,
    verbose: true,
  }, callback);
}

function deploy(config, callback) {
  config = config || {};

  var rootDir = 'rootDir' in config ? config.rootDir : '.';

  if ('GH_TOKEN' in process.env) {
    // We're using a token to authenticate with GitHub, so we have to embed
    // the token into the repo URL (if it isn't already there).
    gitconfiglocal('./', function(error, config) {
      if (error) {
        callback(error);
        return;
      }

      if ('remote' in config && 'origin' in config.remote && 'url' in config.remote.origin) {
        var url = config.remote.origin.url;
        var match;
        if (match = url.match(/^git@github.com:([^/]+)\/([^.]+)\.git$/) ||
                    url.match(/^https:\/\/github.com\/([^/]+)\/([^.]+)\.git$/)) {
          url = 'https://' + process.env.GH_TOKEN + '@github.com/' + match[1] + '/' + match[2] + '.git';
        }

        ghPages.publish(rootDir, {
          // We can't log here because it would leak the GitHub token on Travis.
          // logger: console.log,
          repo: url,
        }, callback);
      } else {
        callback('repo has no origin url');
      }
    });
  } else {
    // We aren't using a token to authenticate with GitHub, so we don't have to
    // alter the repo URL.
    ghPages.publish(rootDir, {
      logger: console.log,
    }, callback);
  }
}

module.exports = {
  configure: configure,
  deploy: deploy,
  offline: offline,
};
<commit_msg>fix syntax/semantic nits<commit_after>/**
 * Copyright 2015 Mozilla
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

// Import this first so we can use it to wrap other modules we import.
var promisify = require("promisify-node");

var getGitHubToken = promisify(require('get-github-token'));
var ghPages = require('gh-pages');
var gitconfiglocal = require('gitconfiglocal');
var path = require('path');
var swPrecache = require('sw-precache');
var readYaml = require('read-yaml');
var travisEncrypt = promisify(require('travis-encrypt'));
var writeYaml = require('write-yaml');

/**
 * Get the slug (GitHub username/repo combination) for the 'origin' remote
 * in the repository that contains the current working directory.
 * XXX Replace with github-slug module?
 */
function getSlug(callback) {
  gitconfiglocal('./', function(error, config) {
    if (error) {
      callback(error);
      return;
    }

    if ('remote' in config && 'origin' in config.remote && 'url' in config.remote.origin) {
      var url = config.remote.origin.url;
      var match;
      if (match = url.match(/^git@github.com:([^/]+)\/([^.]+)\.git$/) ||
                  url.match(/^https:\/\/github.com\/([^/]+)\/([^.]+)\.git$/)) {
        callback(null, match[1] + '/' + match[2]);
        return;
      }
      callback('could not parse value of origin remote URL: ' + url);
      return;
    }

    callback('repo has no origin remote');
  });
}
getSlug = promisify(getSlug);

function configure(callback) {
  process.stdout.write(
    '\n' +
    'Oghliner will configure your repository to automatically deploy your app\n' +
    'to GitHub Pages using Travis CI.\n' +
    '\n'
  );

  // Save the slug in the closure so we can use it across the promise chain
  // without having to pass it down the chain.
  var slug;

  getSlug()
  .then(function(aSlug) {
    slug = aSlug;
    process.stdout.write(
      'The "origin" remote of your repository is "' + slug + '".\n' +
      '\n' +
      'Make sure Travis knows about your repository, and your repository is active\n' +
      'in Travis, by going to https://travis-ci.org/profile, pressing the Sync button\n' +
      '(if needed), and activating the repository (if it isn\'t already active).\n' +
      '\n' +
      'Requesting a GitHub personal access token that Travis will use\n' +
      'to deploy your app.  In order to get the token, I need your username\n' +
      'and password (and two-factor authentication code, if appropriate).\n' +
      '\n' +
      'For more information about personal access tokens or to view the entry\n' +
      'for the token I create, see https://github.com/settings/tokens.\n' +
      '\n'
    );

    var note = 'Oghliner token for ' + slug;
    var url = 'https://github.com/mozilla/oghliner';
    return getGitHubToken(['public_repo'], note, url);
  },
  function(err) {
    process.stdout.write(
      '\n' +
      'I wasn\'t able to determine your GitHub username and repo.  Are you running\n' +
      'this command in a Git repository whose "origin" remote is on GitHub?  The error\n' +
      'I received is: ' + err + '\n' +
      '\n'
    );
    callback(err);
  })
  .then(function(token) {
    process.stdout.write(
      '\n' +
      'I retrieved a GitHub personal access token.  Next I\'ll encrypt it\n' +
      'with Travis\'s public key so I can add the token to the Travis configuration\n' +
      'without leaking it in public build logs…\n' +
      '\n'
    );

    return travisEncrypt(slug, 'GH_TOKEN=' + token, undefined, undefined);
  })
  .then(function(blob) {
    process.stdout.write(
      'I encrypted the token. Next I\'ll write it to the Travis configuration…\n' +
      '\n'
    );

    var travisYml = readYaml.sync('.travis.yml');

    if (!('env' in travisYml)) {
      travisYml.env = {};
    }

    if (!('global' in travisYml.env)) {
      travisYml.env.global = [];
    }

    travisYml.env.global.push({ secure: blob });
    writeYaml.sync('.travis.yml', travisYml);
  })
  .then(function() {
    process.stdout.write(
      'I wrote the encrypted token to the Travis configuration.  You\'re ready\n' +
      'to auto-deploy using Travis!  Just commit the change to your "master" branch,\n' +
      'push the change back to the origin remote, and then visit\n' +
      'https://travis-ci.org/' + slug + '/builds to see the build status.\n' +
      '\n' +
      'If the build is successful, the "after_success" build step should show\n' +
      'that Travis deployed your app to GitHub Pages.  It should look like this:\n' +
      '\n' +
      '$ [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && gulp deploy\n' +
      '\n'
    );
  })
  .catch(function(err) {
    callback(err);
  })
  .done(function() {
    callback();
  });
}

function offline(config, callback) {
  var rootDir = config.rootDir || './';
  var fileGlobs = config.fileGlobs || ['**/*'];
  swPrecache.write(path.join(rootDir, 'offline-worker.js'), {
    staticFileGlobs: fileGlobs.map(function(v) { return path.join(rootDir, v) }),
    stripPrefix: rootDir,
    verbose: true,
  }, callback);
}

function deploy(config, callback) {
  config = config || {};

  var rootDir = 'rootDir' in config ? config.rootDir : '.';

  if ('GH_TOKEN' in process.env) {
    // We're using a token to authenticate with GitHub, so we have to embed
    // the token into the repo URL (if it isn't already there).
    gitconfiglocal('./', function(error, config) {
      if (error) {
        callback(error);
        return;
      }

      if ('remote' in config && 'origin' in config.remote && 'url' in config.remote.origin) {
        var url = config.remote.origin.url;
        var match;
        if (match = url.match(/^git@github.com:([^/]+)\/([^.]+)\.git$/) ||
                    url.match(/^https:\/\/github.com\/([^/]+)\/([^.]+)\.git$/)) {
          url = 'https://' + process.env.GH_TOKEN + '@github.com/' + match[1] + '/' + match[2] + '.git';
        }

        ghPages.publish(rootDir, {
          // We can't log here because it would leak the GitHub token on Travis.
          // logger: console.log,
          repo: url,
        }, callback);
      } else {
        callback('repo has no origin url');
      }
    });
  } else {
    // We aren't using a token to authenticate with GitHub, so we don't have to
    // alter the repo URL.
    ghPages.publish(rootDir, {
      logger: console.log,
    }, callback);
  }
}

module.exports = {
  configure: configure,
  deploy: deploy,
  offline: offline,
};
<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

var EventEmitter = require('events').EventEmitter;
var rtc = require('rtc');
var debug = rtc.logger('rtc-quickconnect');
var signaller = require('rtc-signaller');
var defaults = require('cog/defaults');
var reTrailingSlash = /\/$/;

/**
  # rtc-quickconnect

  This is a high level helper module designed to help you get up
  an running with WebRTC really, really quickly.  By using this module you
  are trading off some flexibility, so if you need a more flexible
  configuration you should drill down into lower level components of the
  [rtc.io](http://www.rtc.io) suite.  In particular you should check out
  [rtc](https://github.com/rtc-io/rtc).

  ## Example Usage

  In the simplest case you simply call quickconnect with a single string
  argument which tells quickconnect which server to use for signaling:

  <<< examples/simple.js

  ## Example Usage (using data channels)

  When working with WebRTC data channels, you can call the `createDataChannel`
  function helper that is attached to the object returned from the
  `quickconnect` call.  The `createDataChannel` function signature matches
  the signature of the `RTCPeerConnection` `createDataChannel` function.

  At the minimum it requires a label for the channel, but you can also pass
  through a dictionary of options that can be used to fine tune the
  data channel behaviour.  For more information on these options, I'd
  recommend having a quick look at the WebRTC spec:

  http://dev.w3.org/2011/webrtc/editor/webrtc.html#dictionary-rtcdatachannelinit-members

  If in doubt, I'd recommend not passing through options.

  <<< examples/datachannel.js

  __NOTE:__ Data channel interoperability has been tested between Chrome 32
  and Firefox 26, which both make use of SCTP data channels.

  __NOTE:__ The current stable version of Chrome is 31, so interoperability
  with Firefox right now will be hard to achieve.

  ## Example Usage (using captured media)

  Another example is displayed below, and this example demonstrates how
  to use `rtc-quickconnect` to create a simple video conferencing application:

  <<< examples/conference.js

  ## Regarding Signalling and a Signalling Server

  Signaling is an important part of setting up a WebRTC connection and for
  our examples we use our own test instance of the
  [rtc-switchboard](https://github.com/rtc-io/rtc-switchboard). For your
  testing and development you are more than welcome to use this also, but
  just be aware that we use this for our testing so it may go up and down
  a little.  If you need something more stable, why not consider deploying
  an instance of the switchboard yourself - it's pretty easy :)

  ## Reference

  ```
  quickconnect(signalhost, opts?) => rtc-sigaller instance (+ helpers)
  ```

  ### Valid Quick Connect Options

  The options provided to the `rtc-quickconnect` module function influence the
  behaviour of some of the underlying components used from the rtc.io suite.

  Listed below are some of the commonly used options:

  - `ns` (default: '')

    An optional namespace for your signalling room.  While quickconnect
    will generate a unique hash for the room, this can be made to be more
    unique by providing a namespace.  Using a namespace means two demos
    that have generated the same hash but use a different namespace will be
    in different rooms.

  - `room` (default: null) _added 0.6_

    Rather than use the internal hash generation
    (plus optional namespace) for room name generation, simply use this room
    name instead.  __NOTE:__ Use of the `room` option takes precendence over
    `ns`.

  - `debug` (default: false)

  Write rtc.io suite debug output to the browser console.

  #### Options for Peer Connection Creation

  Options that are passed onto the
  [rtc.createConnection](https://github.com/rtc-io/rtc#createconnectionopts-constraints)
  function:

  - `constraints`

    Used to provide specific constraints when creating a new
    peer connection.

  #### Options for P2P negotiation

  Under the hood, quickconnect uses the
  [rtc/couple](https://github.com/rtc-io/rtc#rtccouple) logic, and the options
  passed to quickconnect are also passed onto this function.

**/
module.exports = function(signalhost, opts) {
  var hash = location.hash.slice(1);
  var signaller = require('rtc-signaller')(signalhost);

  // init configurable vars
  var ns = (opts || {}).ns || '';
  var room = (opts || {}).room;
  var debugging = (opts || {}).debug;

  // collect the local streams
  var localStreams = [];

  // create the known data channels registry
  var channels = {};

  function gotPeerChannel(channel, pc, data) {
    // create the channelOpen function
    var emitChannelOpen = signaller.emit.bind(
      signaller,
      channel.label + ':open',
      channel,
      data.id,
      data,
      pc
    );

    debug('channel ' + channel.label + ' discovered for peer: ' + data.id, channel);
    if (channel.readyState === 'open') {
      return emitChannelOpen();
    }

    channel.onopen = emitChannelOpen;
  }

  // if the room is not defined, then generate the room name
  if (! room) {
    // if the hash is not assigned, then create a random hash value
    if (! hash) {
      hash = location.hash = '' + (Math.pow(2, 53) * Math.random());
    }

    room = ns + '#' + hash;
  }

  if (debugging) {
    rtc.logger.enable.apply(rtc.logger, Array.isArray(debug) ? debugging : ['*']);
  }

  signaller.on('peer:announce', function(data) {
    var pc;
    var monitor;

    // if the room is not a match, abort
    if (data.room !== room) {
      return;
    }

    // create a peer connection
    pc = rtc.createConnection(opts, (opts || {}).constraints);

    // add the local streams
    localStreams.forEach(function(stream) {
      pc.addStream(stream);
    });

    // add the data channels
    // do this differently based on whether the connection is a
    // master or a slave connection
    if (signaller.isMaster(data.id)) {
      debug('is master, creating data channels: ', Object.keys(channels));

      // create the channels
      Object.keys(channels).forEach(function(label) {
        gotPeerChannel(pc.createDataChannel(label, channels[label]), pc, data);
      });
    }
    else {
      pc.ondatachannel = function(evt) {
        // if the data channel is a known channel monitor it for open
        if (evt && evt.channel && channels[evt.channel.label] !== undefined) {
          gotPeerChannel(evt.channel, pc, data);
        }
      };
    }

    // couple the connections
    monitor = rtc.couple(pc, data.id, signaller, opts);

    // emit the peer event as per <= rtc-quickconnect@0.7
    signaller.emit('peer', pc, data.id, data, monitor);

    // once active, trigger the peer connect event
    monitor.once('active', function() {
      signaller.emit('peer:connect', pc, data.id, data);
    });

    // if we are the master connnection, create the offer
    // NOTE: this only really for the sake of politeness, as rtc couple
    // implementation handles the slave attempting to create an offer
    if (signaller.isMaster(data.id)) {
      monitor.createOffer();
    }
  });

  // announce ourselves to our new friend
  setTimeout(function() {
    signaller.announce({ room: room });
  }, 0);

  /**
    ### Quickconnect Broadcast and Data Channel Helper Functions

    The following are functions that are patched into the `rtc-signaller`
    instance that make working with and creating functional WebRTC applications
    a lot simpler.

  **/

  /**
    #### broadcast(stream)

    Add the stream to the set of local streams that we will broadcast
    to other peers.

  **/
  signaller.broadcast = function(stream) {
    localStreams.push(stream);
    return signaller;
  };

  /**
    #### createDataChannel(label, config)

    Request that a data channel with the specified `label` is created on
    the peer connection.  When the data channel is open and available, an
    event will be triggered using the label of the data channel.

    For example, if a new data channel was requested using the following
    call:

    ```js
    var qc = quickconnect('http://rtc.io/switchboard').createDataChannel('test');
    ```

    Then when the data channel is ready for use, a `test:open` event would
    be emitted by `qc`.

  **/
  signaller.createDataChannel = function(label, opts) {
    // save the data channel opts in the local channels dictionary
    channels[label] = opts || null;
    return signaller;
  };

  // pass the signaller on
  return signaller;
};<commit_msg>Added profile data inclusion in announce message<commit_after>/* jshint node: true */
'use strict';

var EventEmitter = require('events').EventEmitter;
var rtc = require('rtc');
var debug = rtc.logger('rtc-quickconnect');
var signaller = require('rtc-signaller');
var defaults = require('cog/defaults');
var extend = require('cog/extend');
var reTrailingSlash = /\/$/;

/**
  # rtc-quickconnect

  This is a high level helper module designed to help you get up
  an running with WebRTC really, really quickly.  By using this module you
  are trading off some flexibility, so if you need a more flexible
  configuration you should drill down into lower level components of the
  [rtc.io](http://www.rtc.io) suite.  In particular you should check out
  [rtc](https://github.com/rtc-io/rtc).

  ## Example Usage

  In the simplest case you simply call quickconnect with a single string
  argument which tells quickconnect which server to use for signaling:

  <<< examples/simple.js

  ## Example Usage (using data channels)

  When working with WebRTC data channels, you can call the `createDataChannel`
  function helper that is attached to the object returned from the
  `quickconnect` call.  The `createDataChannel` function signature matches
  the signature of the `RTCPeerConnection` `createDataChannel` function.

  At the minimum it requires a label for the channel, but you can also pass
  through a dictionary of options that can be used to fine tune the
  data channel behaviour.  For more information on these options, I'd
  recommend having a quick look at the WebRTC spec:

  http://dev.w3.org/2011/webrtc/editor/webrtc.html#dictionary-rtcdatachannelinit-members

  If in doubt, I'd recommend not passing through options.

  <<< examples/datachannel.js

  __NOTE:__ Data channel interoperability has been tested between Chrome 32
  and Firefox 26, which both make use of SCTP data channels.

  __NOTE:__ The current stable version of Chrome is 31, so interoperability
  with Firefox right now will be hard to achieve.

  ## Example Usage (using captured media)

  Another example is displayed below, and this example demonstrates how
  to use `rtc-quickconnect` to create a simple video conferencing application:

  <<< examples/conference.js

  ## Regarding Signalling and a Signalling Server

  Signaling is an important part of setting up a WebRTC connection and for
  our examples we use our own test instance of the
  [rtc-switchboard](https://github.com/rtc-io/rtc-switchboard). For your
  testing and development you are more than welcome to use this also, but
  just be aware that we use this for our testing so it may go up and down
  a little.  If you need something more stable, why not consider deploying
  an instance of the switchboard yourself - it's pretty easy :)

  ## Reference

  ```
  quickconnect(signalhost, opts?) => rtc-sigaller instance (+ helpers)
  ```

  ### Valid Quick Connect Options

  The options provided to the `rtc-quickconnect` module function influence the
  behaviour of some of the underlying components used from the rtc.io suite.

  Listed below are some of the commonly used options:

  - `ns` (default: '')

    An optional namespace for your signalling room.  While quickconnect
    will generate a unique hash for the room, this can be made to be more
    unique by providing a namespace.  Using a namespace means two demos
    that have generated the same hash but use a different namespace will be
    in different rooms.

  - `room` (default: null) _added 0.6_

    Rather than use the internal hash generation
    (plus optional namespace) for room name generation, simply use this room
    name instead.  __NOTE:__ Use of the `room` option takes precendence over
    `ns`.

  - `debug` (default: false)

  Write rtc.io suite debug output to the browser console.

  #### Options for Peer Connection Creation

  Options that are passed onto the
  [rtc.createConnection](https://github.com/rtc-io/rtc#createconnectionopts-constraints)
  function:

  - `constraints`

    Used to provide specific constraints when creating a new
    peer connection.

  #### Options for P2P negotiation

  Under the hood, quickconnect uses the
  [rtc/couple](https://github.com/rtc-io/rtc#rtccouple) logic, and the options
  passed to quickconnect are also passed onto this function.

**/
module.exports = function(signalhost, opts) {
  var hash = location.hash.slice(1);
  var signaller = require('rtc-signaller')(signalhost);

  // init configurable vars
  var ns = (opts || {}).ns || '';
  var room = (opts || {}).room;
  var debugging = (opts || {}).debug;
  var profile = {};

  // collect the local streams
  var localStreams = [];

  // create the known data channels registry
  var channels = {};

  function gotPeerChannel(channel, pc, data) {
    // create the channelOpen function
    var emitChannelOpen = signaller.emit.bind(
      signaller,
      channel.label + ':open',
      channel,
      data.id,
      data,
      pc
    );

    debug('channel ' + channel.label + ' discovered for peer: ' + data.id, channel);
    if (channel.readyState === 'open') {
      return emitChannelOpen();
    }

    channel.onopen = emitChannelOpen;
  }

  // if the room is not defined, then generate the room name
  if (! room) {
    // if the hash is not assigned, then create a random hash value
    if (! hash) {
      hash = location.hash = '' + (Math.pow(2, 53) * Math.random());
    }

    room = ns + '#' + hash;
  }

  if (debugging) {
    rtc.logger.enable.apply(rtc.logger, Array.isArray(debug) ? debugging : ['*']);
  }

  signaller.on('peer:announce', function(data) {
    var pc;
    var monitor;

    // if the room is not a match, abort
    if (data.room !== room) {
      return;
    }

    // create a peer connection
    pc = rtc.createConnection(opts, (opts || {}).constraints);

    // add the local streams
    localStreams.forEach(function(stream) {
      pc.addStream(stream);
    });

    // add the data channels
    // do this differently based on whether the connection is a
    // master or a slave connection
    if (signaller.isMaster(data.id)) {
      debug('is master, creating data channels: ', Object.keys(channels));

      // create the channels
      Object.keys(channels).forEach(function(label) {
        gotPeerChannel(pc.createDataChannel(label, channels[label]), pc, data);
      });
    }
    else {
      pc.ondatachannel = function(evt) {
        // if the data channel is a known channel monitor it for open
        if (evt && evt.channel && channels[evt.channel.label] !== undefined) {
          gotPeerChannel(evt.channel, pc, data);
        }
      };
    }

    // couple the connections
    monitor = rtc.couple(pc, data.id, signaller, opts);

    // emit the peer event as per <= rtc-quickconnect@0.7
    signaller.emit('peer', pc, data.id, data, monitor);

    // once active, trigger the peer connect event
    monitor.once('active', function() {
      signaller.emit('peer:connect', pc, data.id, data);
    });

    // if we are the master connnection, create the offer
    // NOTE: this only really for the sake of politeness, as rtc couple
    // implementation handles the slave attempting to create an offer
    if (signaller.isMaster(data.id)) {
      monitor.createOffer();
    }
  });

  // announce ourselves to our new friend
  setTimeout(function() {
    var data = extend({}, profile, { room: room });

    // announce and emit the local announce event
    signaller.announce(data);
    signaller.emit('local:announce', data);
  }, 0);

  /**
    ### Quickconnect Broadcast and Data Channel Helper Functions

    The following are functions that are patched into the `rtc-signaller`
    instance that make working with and creating functional WebRTC applications
    a lot simpler.

  **/

  /**
    #### broadcast(stream)

    Add the stream to the set of local streams that we will broadcast
    to other peers.

  **/
  signaller.broadcast = function(stream) {
    localStreams.push(stream);
    return signaller;
  };

  /**
    #### createDataChannel(label, config)

    Request that a data channel with the specified `label` is created on
    the peer connection.  When the data channel is open and available, an
    event will be triggered using the label of the data channel.

    For example, if a new data channel was requested using the following
    call:

    ```js
    var qc = quickconnect('http://rtc.io/switchboard').createDataChannel('test');
    ```

    Then when the data channel is ready for use, a `test:open` event would
    be emitted by `qc`.

  **/
  signaller.createDataChannel = function(label, opts) {
    // save the data channel opts in the local channels dictionary
    channels[label] = opts || null;
    return signaller;
  };

  /**
    #### profile(data)

    Update the profile data with the attached information, so when
    the signaller announces it includes this data in addition to any
    room and id information.

  **/
  signaller.profile = function(data) {
    extend(profile, data);
    return signaller;
  };

  // pass the signaller on
  return signaller;
};<|endoftext|>


<commit_before>module.exports = {
  'extends': 'standard',

  'rules': {
    'brace-style'                : [2, '1tbs'],
    'eqeqeq'                     : [2, 'smart'],
    'comma-dangle'               : [2, 'always'],
    'consistent-return'          : [2],
    'key-spacing'                : [2, {'align': 'colon'}],
    'no-extra-semi'              : [2],
    'no-use-before-define'       : [2],
    'no-multi-spaces'            : [2, {exceptions: {'VariableDeclarator': true}}],
    'no-multiple-empty-lines'    : [2, {'max': 2}],
    'semi'                       : [2, 'always'],
    'semi-spacing'               : [2, { 'before': false, 'after': true }],
    'space-before-function-paren': [2, {'anonymous' : 'always', 'named' : 'never'}],
    'spaced-comment'             : [2, 'always', { exceptions: ['-']}],
    'wrap-iife'                  : [2, 'inside']
  }
}
<commit_msg>Tweak rules, turn modules off<commit_after>module.exports = {
  'extends': 'standard',

  'rules': {
    'brace-style'                : [2, '1tbs'],
    'eqeqeq'                     : [2, 'smart'],
    'comma-dangle'               : [0],
    'consistent-return'          : [2],
    'indent'                     : [2, 2, { 'SwitchCase': 1 }],
    'key-spacing'                : [2, {'align': 'colon'}],
    'no-extra-semi'              : [2],
    'no-use-before-define'       : [2],
    'no-multi-spaces'            : [2, {exceptions: {'VariableDeclarator': true}}],
    'no-multiple-empty-lines'    : [2, {'max': 2}],
    'semi'                       : [2, 'always'],
    'semi-spacing'               : [2, { 'before': false, 'after': true }],
    'space-before-function-paren': [2, {'anonymous' : 'always', 'named' : 'never'}],
    'spaced-comment'             : [2, 'always', { exceptions: ['-']}],
    'strict'                     : [2, 'global'],
    'wrap-iife'                  : [2, 'inside']
  },

  "ecmaFeatures": {
    "modules": false
  }
}
<|endoftext|>


<commit_before>'use strict';
var gutil = require('gulp-util');
var through = require('through2');
var applySourceMap = require('vinyl-sourcemaps-apply');
var objectAssign = require('object-assign');
var babel = require('babel-core');

module.exports = function (opts) {
	opts = opts || {};

	return through.obj(function (file, enc, cb) {
		if (file.isNull()) {
			cb(null, file);
			return;
		}

		if (file.isStream()) {
			cb(new gutil.PluginError('gulp-babel', 'Streaming not supported'));
			return;
		}

		try {
			var fileOpts = objectAssign({}, opts, {
				filename: file.path,
				filenameRelative: file.relative,
				sourceMap: !!file.sourceMap
			});

			var res = babel.transform(file.contents.toString(), fileOpts);

			if (file.sourceMap && res.map) {
				applySourceMap(file, res.map);
			}

			file.contents = new Buffer(res.code);
			this.push(file);
		} catch (err) {
			this.emit('error', new gutil.PluginError('gulp-babel', err, {fileName: file.path}));
		}

		cb();
	});
};
<commit_msg>Improve `error.toString()`<commit_after>'use strict';
var gutil = require('gulp-util');
var through = require('through2');
var applySourceMap = require('vinyl-sourcemaps-apply');
var objectAssign = require('object-assign');
var babel = require('babel-core');

module.exports = function (opts) {
	opts = opts || {};

	return through.obj(function (file, enc, cb) {
		if (file.isNull()) {
			cb(null, file);
			return;
		}

		if (file.isStream()) {
			cb(new gutil.PluginError('gulp-babel', 'Streaming not supported'));
			return;
		}

		try {
			var fileOpts = objectAssign({}, opts, {
				filename: file.path,
				filenameRelative: file.relative,
				sourceMap: !!file.sourceMap
			});

			var res = babel.transform(file.contents.toString(), fileOpts);

			if (file.sourceMap && res.map) {
				applySourceMap(file, res.map);
			}

			file.contents = new Buffer(res.code);
			this.push(file);
		} catch (err) {
			this.emit('error', new gutil.PluginError('gulp-babel', err, {fileName: file.path, showProperties: false}));
		}

		cb();
	});
};
<|endoftext|>


<commit_before>'use strict';
var path = require('path');
var gutil = require('gulp-util');
var through2 = require('through2');
var Mocha = require('mocha');

module.exports = function (options) {
	var mocha = new Mocha(options);
	var cache = {};

	for (var key in require.cache)
		cache[key] = true;

	return through2.obj(function (file, enc, cb) {
		mocha.addFile(file.path);
		this.push(file);
		cb();
	}, function (cb) {
		try {
			mocha.run(function (errCount) {
				if (errCount > 0) {
					this.emit('error', new gutil.PluginError('gulp-mocha', errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
				}

				for (var key in require.cache)
					if (!cache[key])
						delete require.cache[key];

				cb();
			}.bind(this));
		} catch (err) {
			this.emit('error', new gutil.PluginError('gulp-mocha', err));
			cb();
		}
	});
};
<commit_msg>code style<commit_after>'use strict';
var path = require('path');
var gutil = require('gulp-util');
var through2 = require('through2');
var Mocha = require('mocha');

module.exports = function (options) {
	var mocha = new Mocha(options);
	var cache = {};

	for (var key in require.cache) {
		cache[key] = true;
	}

	return through2.obj(function (file, enc, cb) {
		mocha.addFile(file.path);
		this.push(file);
		cb();
	}, function (cb) {
		try {
			mocha.run(function (errCount) {
				if (errCount > 0) {
					this.emit('error', new gutil.PluginError('gulp-mocha', errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
				}

				for (var key in require.cache) {
					if (!cache[key]) {
						delete require.cache[key];
					}
				}

				cb();
			}.bind(this));
		} catch (err) {
			this.emit('error', new gutil.PluginError('gulp-mocha', err));
			cb();
		}
	});
};
<|endoftext|>


<commit_before>"use strict";

const IS_PRODUCTION = process.env.NODE_ENV === 'production';

var ObjectID = require('mongodb').ObjectID;
var babel = require('babel');
var fs = require('fs');
var uglify = require('uglify-js');

var Common = require('./lib/Common');
var Document = require('./lib/Document');
var Structure = require('./lib/Structure');
var Property = require('./lib/Property');
var Validators = require('./lib/validators').Validators;

var Types = require('./lib/Types');
var utils = require('./lib/utils');

var userDefinition = require('./models/User').User;

exports = module.exports = Models;

function Models(model) {
  return Document.models[model];
}

//TODO: Probably a better way to do this.
Models.setMongoConnection = function(mongo) {
  Models.mongo = mongo;
  Document.setMongoConnection(mongo);

  //Ensure that all the required users have been created.
  require('./lib/requiredUsers')(Models);
};

//TODO: Implement.  Allow new validation functions to be registered
Models.registerValidator = function() {

};

Models.api = function() {
  return function *(next) {
    function enforcePost() {
      if (this.method !== 'POST') {
        throw new Error(`API call must be POST, you sent ${this.method}`);
      }
    }

    //First get setup some user helpers in the context
    this.user = (this.session && this.session.user) ? this.session.user : null;
    this.userId = this.user ? this.user._id : null;
    this.roles = (this.user && this.user.roles) ? this.user.roles : ['Anonymous'];

    if (/\/api\/v1\//.test(this.path)) {
      var call = this.path.replace('/api/v1/', '').split('/');

      switch (call[0]) {
        case 'models.js':
          this.body = Models.clientJS();
          break;
      }
      if (call[0] === 'models.js') {
      }

      var model = Document.models[call[0]];
      var query = (this.request.body && this.request.body.query) || {};
      var options = (this.request.body && this.request.body.options) || {};

      switch (call[1]) {
        case 'count':
          this.body = yield model.count(this, query, options);
          break;

        case 'find':
          this.body = yield model.find(this, query, options);
          break;

        case 'create':
          enforcePost.call(this);
          var obj = yield model.create(this, this.request.body);
          this.body = model.secureByAction(this, obj, 'read');
          break;

        default: {
          let id = call[1];
          let action = call[2];

          if (!id) {
            throw new Error('No ID provided');
          }

          try {
            id = new ObjectID(id);
          } catch(e) {
            throw new Error('Invalid ID supplied : ' + id);
          }

          switch (action) {
            case 'get':
              this.body = yield model.get(this, id);
              break;

            case 'remove':
              this.body = yield model.remove(this, id);
              break;

            case 'update':
              enforcePost.call(this);
              this.body = yield model.update(this, id, this.request.body);
              break;

            default:
              throw new Error('Unrecognized command: ' + action);
          }
        }
      }
    }

    yield next;
  }
};

var clientJS;
Models.clientJS = function() {
  if (!clientJS) {
    var str = 'var ObjectID = String;\n'
    //TODO: refactor this later.  Could be a separate client side Types library
    str += `var utils = {
      union: ${utils.union},
      guid: ${utils.guid},
      RESERVED_WORDS: ${JSON.stringify(utils.RESERVED_WORDS)},
      RESERVED_ROLES: ${JSON.stringify(utils.RESERVED_ROLES)}
    };\n`;

    str += 'var Validators = {';
    for (var validator in Validators) {
      str += `${validator}: ${Validators[validator]},`;
    }
    str += '};\n';

    str += `var Types = {
      MiniGuid: ${Types.MiniGuid},
      Guid: ${Types.Guid}
    };\n`;

    str += 'var Models = window.Models = {};\n';
    str += Models.Property.toString();
    str += `Models.Property = Property;\n`;
    str += Models.Common.toString();
    str += `Models.Common = Common;\n`;
    str += Models.Structure.toString();
    str += `Models.Structure = Structure;\n`;
    str += `Models.structures = {};\n`;
    str += 'var Extend = Structure;';

    for (let structure in Models.structures) {
      str += Models.structures[structure].toString();
      str += `Models.structures.${structure} = ${structure};\n`;
      str += `Models.structures.${structure}._clientInit();`;
    }

    str += Models.Document.toString();
    str += `Models.Document = Document;\n`;
    str += `Models.models = {};\n`;
    str += 'Extend = Document;';
    for (let model in Models.models) {
      str += Models.models[model].toString();
      str += `Models.models.${model} = ${model};\n`;
      str += `Models.models.${model}._clientInit();`;
    }

    clientJS = babel.transform(str).code;

    if (IS_PRODUCTION) {
      clientJS = uglify.minify(clientJS, {fromString: true}).code;
    }
  }

  return '(function() {\n' + clientJS + '\n})();';
};

Models.Property = Property;
Models.Common = Common;
Models.Document = Document;
Models.Structure = Structure;
Models.Types = Types;
Models.structure = Structure.registerDefinition;
Models.structures = Structure.structures;
Models.model = Document.registerDefinition;
Models.models = Document.models;
Models.utils = utils;

Models.model(userDefinition);<commit_msg>Updates<commit_after>"use strict";

const IS_PRODUCTION = process.env.NODE_ENV === 'production';

var ObjectID = require('mongodb').ObjectID;
var babel = require('babel');
var fs = require('fs');
var uglify = require('uglify-js');

var Common = require('./lib/Common');
var Document = require('./lib/Document');
var Structure = require('./lib/Structure');
var Property = require('./lib/Property');
var Validators = require('./lib/validators').Validators;

var Types = require('./lib/Types');
var utils = require('./lib/utils');

var userDefinition = require('./models/User').User;

exports = module.exports = Models;

function Models(model) {
  return Document.models[model];
}

//TODO: Probably a better way to do this.
Models.setMongoConnection = function(mongo) {
  Models.mongo = mongo;
  Document.setMongoConnection(mongo);

  //Ensure that all the required users have been created.
  require('./lib/requiredUsers')(Models);
};

//TODO: Implement.  Allow new validation functions to be registered
Models.registerValidator = function() {

};

Models.api = function() {
  return function *(next) {
    function enforcePost() {
      if (this.method !== 'POST') {
        throw new Error(`API call must be POST, you sent ${this.method}`);
      }
    }

    //First get setup some user helpers in the context
    this.user = (this.session && this.session.user) ? this.session.user : null;
    this.userId = this.user ? this.user._id : null;
    this.roles = (this.user && this.user.roles) ? this.user.roles : ['Anonymous'];

    if (/\/api\/v1\//.test(this.path)) {
      var call = this.path.replace('/api/v1/', '').split('/');

      switch (call[0]) {
        case 'models.js':
          this.type = 'application/javascript';
          this.body = Models.clientJS();
          break;
        case 'me':
          if (this.query.format === 'jsonp') {
            this.type = 'application/javascript';
            this.body = `var user = ${JSON.stringify(this.user)};`;
            break;
          }
          this.body = this.user;
          break;
        default:
        {
          let model = Document.models[call[0]];
          let query = (this.request.body && this.request.body.query) || {};
          let options = (this.request.body && this.request.body.options) || {};

          switch (call[1]) {
            case 'count':
              this.body = yield model.count(this, query, options);
              break;

            case 'find':
              this.body = yield model.find(this, query, options);
              break;

            case 'create':
              enforcePost.call(this);
              var obj = yield model.create(this, this.request.body);
              this.body = model.secureByAction(this, obj, 'read');
              break;

            default:
            {
              let id = call[1];
              let action = call[2];

              if (!id) {
                throw new Error('No ID provided');
              }

              try {
                id = new ObjectID(id);
              } catch (e) {
                throw new Error('Invalid ID supplied : ' + id);
              }

              switch (action) {
                case 'get':
                  this.body = yield model.get(this, id);
                  break;

                case 'remove':
                  this.body = yield model.remove(this, id);
                  break;

                case 'update':
                  enforcePost.call(this);
                  this.body = yield model.update(this, id, this.request.body);
                  break;

                default:
                  throw new Error('Unrecognized command: ' + action);
              }
            }
          }
        }
      }
    }

    yield next;
  }
};

var clientJS;
Models.clientJS = function() {
  if (!clientJS) {
    var str = 'var ObjectID = String;\n'
    //TODO: refactor this later.  Could be a separate client side Types library
    str += `var utils = {
      union: ${utils.union},
      guid: ${utils.guid},
      RESERVED_WORDS: ${JSON.stringify(utils.RESERVED_WORDS)},
      RESERVED_ROLES: ${JSON.stringify(utils.RESERVED_ROLES)}
    };\n`;

    str += 'var Validators = {';
    for (var validator in Validators) {
      str += `${validator}: ${Validators[validator]},`;
    }
    str += '};\n';

    str += `var Types = {
      MiniGuid: ${Types.MiniGuid},
      Guid: ${Types.Guid}
    };\n`;

    str += 'var Models = window.Models = {};\n';
    str += Models.Property.toString();
    str += `Models.Property = Property;\n`;
    str += Models.Common.toString();
    str += `Models.Common = Common;\n`;
    str += Models.Structure.toString();
    str += `Models.Structure = Structure;\n`;
    str += `Models.structures = {};\n`;
    str += 'var Extend = Structure;';

    for (let structure in Models.structures) {
      str += Models.structures[structure].toString();
      str += `Models.structures.${structure} = ${structure};\n`;
      str += `Models.structures.${structure}._clientInit();`;
    }

    str += Models.Document.toString();
    str += `Models.Document = Document;\n`;
    str += `Models.models = {};\n`;
    str += 'Extend = Document;';
    for (let model in Models.models) {
      str += Models.models[model].toString();
      str += `Models.models.${model} = ${model};\n`;
      str += `Models.models.${model}._clientInit();`;
    }

    clientJS = babel.transform(str).code;

    if (IS_PRODUCTION) {
      clientJS = uglify.minify(clientJS, {fromString: true}).code;
    }
  }

  return '(function() {\n' + clientJS + '\n})();';
};

Models.Property = Property;
Models.Common = Common;
Models.Document = Document;
Models.Structure = Structure;
Models.Types = Types;
Models.structure = Structure.registerDefinition;
Models.structures = Structure.structures;
Models.model = Document.registerDefinition;
Models.models = Document.models;
Models.utils = utils;

Models.model(userDefinition);<|endoftext|>


<commit_before>var async           = require('async');
var path            = require('path');
var filesystem      = require('fs');

var GrimmFramework = function() {
  var self = this;

  // Configuration Object
  var configuration = {};

  // Rood directory for the framework. Here we assume Grimm is a normally located module.
  var root_directory = path.resolve(__dirname, '../../');

  // The environment level (think dev, test, prod).
  var environment = 'dev';

  // A pointer to the web object, e.g. express()
  var web = null;

  // A pointer to express, e.g. require('express')
  var express = null;

  // A pointer to a server, e.g. http.createServer()
  var server = null;

  // A pointer to socket.io, e.g. require('socket.io').listen()
  var websockets = null;

  // A pointer to whiskers, e.g. require('whiskers')
  var template_engine = null;

  /**
   * Sets some configuration settings used by some modules
   */
  self.configure = function(config) {
    configuration = config;

    return self;
  };

  /**
   * Loads configuration from a file
   */
  self.loadConfig = function(env, callback) {
    self.log('config', 'Using config/' + env + '.json');
    filesystem.readFile('config/' + environment + '.json', function(err, contents) {
      if (err) {
        callback(err);
        return;
      }
      var config = JSON.parse(contents);
      callback(null, config);
    });

    return self;
  };

  /**
   * Sets the environment. `dev` is assumed for a local dev machine, use w/e others you want
   */
  self.setEnv = function(env) {
    environment = env;
    return self;
  };

  /**
   * Sets the websocket engine. Right now we only support the `socket.io` module, but one day more
   */
  self.setSockets = function(engine) {
    websockets = engine;
    return self;
  };

  /**
   * Sets the web server. Right now we only support the `express` module, but one day more
   * This is starting to look ugly... Perhaps I should just load the modules from within grimm?
   */
  self.setWeb = function(engine, exp, srvr) {
    web = engine;
    express = exp;
    server = srvr;

    return self;
  };

  /**
   * Sets the template engine. Right now we only support the `whiskers` module, but one day more
   */
  self.setTemplate = function(engine) {
    template_engine = engine;
    return self;
  };

  /**
   * Sets the root directory, you probably want to just use __dirname from the loading script
   */
  self.setRoot = function(directory) {
    root_directory = directory;
    return self;
  };

  /**
   * Returns the root directory
   */
  self.getRoot = function() {
    return root_directory;
  };

  /**
   * Initializes some Grimm conventions
   */
  self.initialize = function() {
    // Provides /styles, /scripts, /images, etc. Mapped to the public/ dir
    web.use('/', express.static(root_directory + '/public'));
    self.log('public', '/* -> public/*');

    // Cache compiled HTML to make it faster. Unless we're in dev.
    if (environment !== 'dev') {
      web.use(function(req, res, next) {
        res.locals.cache = true;
        next();
      });
    }

    // Enable templating support for Express
    web.set('views', root_directory + '/views');
    web.engine('html', template_engine.__express);

    return self;
  };

  /**
   * Looks in our bundles directory, and attempts to load them all
   * Note that _errors is a special bundle and is loaded last (for now)
   */
  self.loadModules = function() {
    filesystem.readdir(root_directory + '/bundles', function(err, modules) {
      if (err) {
        self.log('module', 'Error reading modules directory (bundles)');
        self.log('module', err);
        process.exit(2);
      }

      self.log('modload', 'Loading Modules...');

      async.forEach(modules, attemptLoadController, function(err) {
        if (err) {
          self.log('module', err);
          process.exit(4);
          return;
        }
        loadController('_errors', function() {
          self.log('modload', 'All Modules Loaded.');
        });
      });
    });

    return self;
  }

  /**
   * Checks to see if the specified controller can be loaded, then passes
   * it off to loadController()
   * PRIVATE
   */
  function attemptLoadController(module_name, callback) {
    filesystem.stat(root_directory + '/bundles/' + module_name, function(err, stats) {
      // _errors is a special bundle that we don't want to load until the end
      if (module_name == '_errors') {
        callback();
        return;
      }

      if (err) {
        self.log('modload', 'Encountered an error whilst loading a module, ' + module_name);
        callback(err);
        return;
      }

      if (stats.isDirectory()) {
        loadController(module_name, callback);
      } else {
        // nothing to do...
        if (typeof callback === 'function') {
          callback();
        }
      }
    });
  }

  /**
   * Loads the specified controller, and assigns a public directory is applicable
   * PRIVATE
   */
  function loadController(module_name, callback) {
    self.log('modload', 'Loading: ' + module_name);
    require(root_directory + '/bundles/' + module_name)({
      "web": web,
      "config": configuration,
      "websockets": websockets,
      "handlebars": template_engine,
      "log": self.log
    });

    // Does this bundle contain a public/ directory?
    var public_dir = root_directory + '/bundles/' + module_name + '/public';
    filesystem.stat(public_dir, function(err, stats) {
      if (err) {
        return;
      };
      // bundles/:name/public* -> http://site/:name/*
      web.use('/' + module_name, express.static(public_dir));
      self.log('public', '/' + module_name + '/* -> bundles/' + module_name + '/public/*');
    });

    if (typeof callback === 'function') {
      callback();
    }
  }

  /**
   * Begins our web server
   */
  self.listen = function() {
    server.listen(configuration.web.port, configuration.web.host, null, function() {
      self.log('http', 'Listening on ' + (configuration.web.host || '*') + ':' + configuration.web.port);
      try {
        self.log('perms', 'Old User ID: ' + process.getuid() + ', Old Group ID: ' + process.getgid());
        process.setgid(configuration.permissions.group);
        process.setuid(configuration.permissions.user);
        self.log('perms', 'New User ID: ' + process.getuid() + ', New Group ID: ' + process.getgid());
      } catch (err) {
        self.log('perms', 'Cowardly refusing to keep the process alive as root.');
        process.exit(4);
      }
    });

      return self;
  };

  /**
   * Logs a message into our specified format. Will need to beef this bad boy up one day.
   */
  self.log = function(level, message) {
    var LEVEL_LEN = 8;
    var title = level.toString().substr(0,LEVEL_LEN).toUpperCase();
    while (title.length < LEVEL_LEN) {
      title = ' ' + title;
    }

    console.log('[' + title + '] ' + message);
  };
};

module.exports = new GrimmFramework();
<commit_msg>configurable logger<commit_after>var async           = require('async');
var path            = require('path');
var filesystem      = require('fs');

var GrimmFramework = function() {
  var self = this;

  // Configuration Object
  var configuration = {};

  // Rood directory for the framework. Here we assume Grimm is a normally located module.
  var root_directory = path.resolve(__dirname, '../../');

  // The environment level (think dev, test, prod).
  var environment = 'dev';

  // A pointer to the web object, e.g. express()
  var web = null;

  // A pointer to express, e.g. require('express')
  var express = null;

  // A pointer to a server, e.g. http.createServer()
  var server = null;

  // A pointer to socket.io, e.g. require('socket.io').listen()
  var websockets = null;

  // A pointer to whiskers, e.g. require('whiskers')
  var template_engine = null;

  // A function to do our logging. Arguments include title, message, request
  var logger = null;

  /**
   * Sets some configuration settings used by some modules
   */
  self.configure = function(config) {
    configuration = config;

    return self;
  };

  /**
   * Loads configuration from a file
   */
  self.loadConfig = function(env, callback) {
    self.log('config', 'Using config/' + env + '.json');
    filesystem.readFile('config/' + environment + '.json', function(err, contents) {
      if (err) {
        callback(err);
        return;
      }
      var config = JSON.parse(contents);
      callback(null, config);
    });

    return self;
  };

  /**
   * Sets the environment. `dev` is assumed for a local dev machine, use w/e others you want
   */
  self.setEnv = function(env) {
    environment = env;
    return self;
  };

  /**
   * Sets the websocket engine. Right now we only support the `socket.io` module, but one day more
   */
  self.setSockets = function(engine) {
    websockets = engine;
    return self;
  };

  /**
   * Sets the web server. Right now we only support the `express` module, but one day more
   * This is starting to look ugly... Perhaps I should just load the modules from within grimm?
   */
  self.setWeb = function(engine, exp, srvr) {
    web = engine;
    express = exp;
    server = srvr;

    return self;
  };

  /**
   * Sets the template engine. Right now we only support the `whiskers` module, but one day more
   */
  self.setTemplate = function(engine) {
    template_engine = engine;
    return self;
  };

  /**
   * Sets the root directory, you probably want to just use __dirname from the loading script
   */
  self.setRoot = function(directory) {
    root_directory = directory;
    return self;
  };

  /**
   * Returns the root directory
   */
  self.getRoot = function() {
    return root_directory;
  };

  /**
   * Initializes some Grimm conventions
   */
  self.initialize = function() {
    // Provides /styles, /scripts, /images, etc. Mapped to the public/ dir
    web.use('/', express.static(root_directory + '/public'));
    self.log('public', '/* -> public/*');

    // Cache compiled HTML to make it faster. Unless we're in dev.
    if (environment !== 'dev') {
      web.use(function(req, res, next) {
        res.locals.cache = true;
        next();
      });
    }

    // Enable templating support for Express
    web.set('views', root_directory + '/views');
    web.engine('html', template_engine.__express);

    return self;
  };

  /**
   * Looks in our bundles directory, and attempts to load them all
   * Note that _errors is a special bundle and is loaded last (for now)
   */
  self.loadModules = function() {
    filesystem.readdir(root_directory + '/bundles', function(err, modules) {
      if (err) {
        self.log('module', 'Error reading modules directory (bundles)');
        self.log('module', err);
        process.exit(2);
      }

      self.log('modload', 'Loading Modules...');

      async.forEach(modules, attemptLoadController, function(err) {
        if (err) {
          self.log('module', err);
          process.exit(4);
          return;
        }
        loadController('_errors', function() {
          self.log('modload', 'All Modules Loaded.');
        });
      });
    });

    return self;
  }

  /**
   * Checks to see if the specified controller can be loaded, then passes
   * it off to loadController()
   * PRIVATE
   */
  function attemptLoadController(module_name, callback) {
    filesystem.stat(root_directory + '/bundles/' + module_name, function(err, stats) {
      // _errors is a special bundle that we don't want to load until the end
      if (module_name == '_errors') {
        callback();
        return;
      }

      if (err) {
        self.log('modload', 'Encountered an error whilst loading a module, ' + module_name);
        callback(err);
        return;
      }

      if (stats.isDirectory()) {
        loadController(module_name, callback);
      } else {
        // nothing to do...
        if (typeof callback === 'function') {
          callback();
        }
      }
    });
  }

  /**
   * Loads the specified controller, and assigns a public directory is applicable
   * PRIVATE
   */
  function loadController(module_name, callback) {
    self.log('modload', 'Loading: ' + module_name);
    require(root_directory + '/bundles/' + module_name)({
      "web": web,
      "config": configuration,
      "websockets": websockets,
      "handlebars": template_engine,
      "log": self.log
    });

    // Does this bundle contain a public/ directory?
    var public_dir = root_directory + '/bundles/' + module_name + '/public';
    filesystem.stat(public_dir, function(err, stats) {
      if (err) {
        return;
      };
      // bundles/:name/public* -> http://site/:name/*
      web.use('/' + module_name, express.static(public_dir));
      self.log('public', '/' + module_name + '/* -> bundles/' + module_name + '/public/*');
    });

    if (typeof callback === 'function') {
      callback();
    }
  }

  /**
   * Begins our web server
   */
  self.listen = function() {
    server.listen(configuration.web.port, configuration.web.host, null, function() {
      self.log('http', 'Listening on ' + (configuration.web.host || '*') + ':' + configuration.web.port);
      try {
        self.log('perms', 'Old User ID: ' + process.getuid() + ', Old Group ID: ' + process.getgid());
        process.setgid(configuration.permissions.group);
        process.setuid(configuration.permissions.user);
        self.log('perms', 'New User ID: ' + process.getuid() + ', New Group ID: ' + process.getgid());
      } catch (err) {
        self.log('perms', 'Cowardly refusing to keep the process alive as root.');
        process.exit(4);
      }
    });

      return self;
  };

  /**
   * Sets a function to be used for logging, instead of the built in one.
   */
  self.setLogger = function(logger) {
    self.logger = logger;
  };

  /**
   * Logs a message into our specified format. Will need to beef this bad boy up one day.
   */
  self.log = function(level, message, details) {
    if (typeof self.logger === 'function') {
      logger(level, message, details);
      return;
    }
    var LEVEL_LEN = 8;
    var title = level.toString().substr(0,LEVEL_LEN).toUpperCase();
    while (title.length < LEVEL_LEN) {
      title = ' ' + title;
    }

    console.log('[' + title + '] ' + message);
  };
};

module.exports = new GrimmFramework();
<|endoftext|>


<commit_before>var connectLeg = module.exports = function connectLeg(log) {
  return function connectLeg(req, res, next) {
    req._leg_requestId = [Date.now(), [0,0,0,0,0,0,0,0].map(function(e) { return String.fromCharCode(Math.floor(Math.random() * 25) + 97); }).join("")].join("-");
    req._leg_requestTime = Date.now();

    log.info("request", {
      request: req._leg_requestId,
      method: req.method,
      host: req.host,
      path: req.url,
    });

    var _end = res.end;
    res.end = function() {
      var level;

      if (res.statusCode >= 400 && res.statusCode <= 499) {
        level = "warn";
      } else if (res.statusCode >= 500) {
        level = "error";
      } else {
        level = "info";
      }

      log[level]("response", {
        request: req._leg_requestId,
        method: req.method,
        host: req.host,
        path: req.url,
        status: res.statusCode,
        took: Date.now() - req._leg_requestTime,
      });

      return _end.apply(res, arguments);
    };

    return next();
  };
};
<commit_msg>add referrer/origin stuff<commit_after>var connectLeg = module.exports = function connectLeg(log) {
  return function connectLeg(req, res, next) {
    req._leg_requestId = [Date.now(), [0,0,0,0,0,0,0,0].map(function(e) { return String.fromCharCode(Math.floor(Math.random() * 25) + 97); }).join("")].join("-");
    req._leg_requestTime = Date.now();

    log.info("request", {
      request: req._leg_requestId,
      method: req.method,
      host: req.host,
      path: req.url,
      origin: req.headers.origin,
      referer: req.headers.referer,
    });

    var _end = res.end;
    res.end = function() {
      var level;

      if (res.statusCode >= 400 && res.statusCode <= 499) {
        level = "warn";
      } else if (res.statusCode >= 500) {
        level = "error";
      } else {
        level = "info";
      }

      log[level]("response", {
        request: req._leg_requestId,
        method: req.method,
        host: req.host,
        path: req.url,
        status: res.statusCode,
        took: Date.now() - req._leg_requestTime,
      });

      return _end.apply(res, arguments);
    };

    return next();
  };
};
<|endoftext|>


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

'use strict';

process.env.DEBUG = 'actions-on-google:*';

// We need the Assistant client for all the magic here
const Assistant = require('actions-on-google').ApiAiAssistant;
// To make our http request (a bit nicer)
const request = require('request');

// Some variables we will use in this example
const ACTION_PRICE = 'price';
const ACTION_TOTAL = 'total';
const ACTION_BLOCK = 'block';
const ACTION_MARKET = 'marketcap';

const EXT_BITCOIN_API_URL = "https://blockchain.info";
const EXT_PRICE = "/q/24hrprice";
const EXT_TOTAL = "/q/totalbc";
const EXT_BLOCK_COUNT = "/q/getblockcount";
const EXT_MARKET_CAP = "/q/marketcap";

// [START Bitcoin Info]
exports.bitcoinInfo = (req, res) => {
  const assistant = new Assistant({request: req, response: res});
  console.log('bitcoinInfoAction Request headers: ' + JSON.stringify(req.headers));
  console.log('bitcoinInfoAction Request body: ' + JSON.stringify(req.body));

  // Fulfill price action business logic
  function priceHandler (assistant) {
  	request(EXT_BITCOIN_API_URL + EXT_PRICE, function(error, response, body) {
  		// The fulfillment logic for returning the bitcoin current price
  		console.log("priceHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
    	const msg = "Right now the price of a bitcoin is " + body + " USD. What else would you like to know?";
    	assistant.ask(msg);
  	});
  }

  // Fulfill total bitcoin action
  function totalHandler (assistant) {
    request(EXT_BITCOIN_API_URL + EXT_TOTAL, function(error, response, body) {
  		console.log("totalHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
    	// The fulfillment logic for returning the amount of bitcoins in the world
    	const billionsBitcoins = body / 1000000000;
    	const msg = "Right now there are " + billionsBitcoins + " billion bitcoins around the world. What else would you like to know?";
    	assistant.ask(msg);
  	});
  }

  // Fulfill block count action
  function blockCountHandler (assistant) {
  	request(EXT_BITCOIN_API_URL + EXT_BLOCK_COUNT, function(error, response, body) {
  		console.log("blockCountHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
    	const msg = "Right now there are " + body + " blocks. What else would you like to know? the price?";
    	assistant.ask(msg);
  	});
  }

// Fulfill block count action
  function marketCaptHandler (assistant) {
  	request(EXT_BITCOIN_API_URL + EXT_MARKET_CAP, function(error, response, body) {
  		console.log("marketCaptHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
  		const marketCapB = Math.round((body / 1000000000) * 100) / 100
    	const msg = "Right now market cap is " + marketCapB + " billions. What else would you like to know?";
    	assistant.ask(msg);
  	});
  }

  // The Entry point to all our actions
  const actionMap = new Map();
  actionMap.set(ACTION_PRICE, priceHandler);
  actionMap.set(ACTION_TOTAL, totalHandler);
  actionMap.set(ACTION_BLOCK, blockCountHandler);
  actionMap.set(ACTION_MARKET, marketCaptHandler);

  assistant.handleRequest(actionMap);
};
// [END Bitcoin Info]
<commit_msg>Add the interval between blocks<commit_after>// Copyright 2016, Google, Inc.
// Licensed under the Apache License, Version 2.0 (the 'License');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

process.env.DEBUG = 'actions-on-google:*';

// We need the Assistant client for all the magic here
const Assistant = require('actions-on-google').ApiAiAssistant;
// To make our http request (a bit nicer)
const request = require('request');

// the actions we are supporting (get them from api.ai)
const ACTION_PRICE = 'price';
const ACTION_TOTAL = 'total';
const ACTION_BLOCK = 'block';
const ACTION_MARKET = 'marketcap';
const ACTION_INTERVAL = 'interval';

// The end-points to our calls
const EXT_BITCOIN_API_URL = "https://blockchain.info";
const EXT_PRICE = "/q/24hrprice";
const EXT_TOTAL = "/q/totalbc";
const EXT_BLOCK_COUNT = "/q/getblockcount";
const EXT_MARKET_CAP = "/q/marketcap";
const EXT_INTERVAL = "/q/interval";

// [START Bitcoin Info]
exports.bitcoinInfo = (req, res) => {
  const assistant = new Assistant({request: req, response: res});
  console.log('bitcoinInfoAction Request headers: ' + JSON.stringify(req.headers));
  console.log('bitcoinInfoAction Request body: ' + JSON.stringify(req.body));

  // Fulfill price action business logic
  function priceHandler (assistant) {
    request(EXT_BITCOIN_API_URL + EXT_PRICE, function(error, response, body) {
      // The fulfillment logic for returning the bitcoin current price
      console.log("priceHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
      const msg = "Right now the price of a bitcoin is " + body + " USD. What else would you like to know?";
      assistant.ask(msg);
    });
  }

  // Fulfill total bitcoin action
  function totalHandler (assistant) {
    request(EXT_BITCOIN_API_URL + EXT_TOTAL, function(error, response, body) {
      console.log("totalHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
      // The fulfillment logic for returning the amount of bitcoins in the world
      const billionsBitcoins = body / 1000000000;
      const msg = "Right now there are " + billionsBitcoins + " billion bitcoins around the world. What else would you like to know?";
      assistant.ask(msg);
    });
  }

  // Fulfill block count action
  function blockCountHandler (assistant) {
    request(EXT_BITCOIN_API_URL + EXT_BLOCK_COUNT, function(error, response, body) {
      console.log("blockCountHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
      const msg = "Right now there are " + body + " blocks. What else would you like to know? the price?";
      assistant.ask(msg);
    });
  }

  // Fulfill market cap action
  function marketCaptHandler (assistant) {
    request(EXT_BITCOIN_API_URL + EXT_MARKET_CAP, function(error, response, body) {
      console.log("marketCaptHandler response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
      const marketCapB = Math.round((body / 1000000000) * 100) / 100
      const msg = "Right now market cap is " + marketCapB + " billions. What else would you like to know?";
      assistant.ask(msg);
    });
  }

  // Fulfill interval action
  function intervalHandler (assistant) {
    request(EXT_BITCOIN_API_URL + EXT_INTERVAL, function(error, response, body) {
      console.log("interval response: " + JSON.stringify(response) + " Body: " + body + " | Error: " + error);
      const msg = "Right now the interval between blocks is " + body + " seconds. What else would you like to know?";
      assistant.ask(msg);
    });
  }

  // The Entry point to all our actions
  const actionMap = new Map();
  actionMap.set(ACTION_PRICE, priceHandler);
  actionMap.set(ACTION_TOTAL, totalHandler);
  actionMap.set(ACTION_BLOCK, blockCountHandler);
  actionMap.set(ACTION_MARKET, marketCaptHandler);
  actionMap.set(ACTION_INTERVAL, intervalHandler);

  assistant.handleRequest(actionMap);
};
// [END Bitcoin Info]
<|endoftext|>


<commit_before>const Eris = require('eris'), //The bot's api library
    chalk = require('chalk'), //Used to make the console have pretty colours
    fs = require('fs'), //For reading/writing to a file
    axios = require('axios'), //HTTP client for requests to and from websites
    options = require('./options/options.json'),
    commandLoader = require('./utils/commandLoader.js'),
    utils = require('./utils/utils.js'),
    database = require('./utils/database.js'),
    processCmd = require('./utils/commandHandler.js'),
    usageChecker = require('./utils/usageChecker.js'),
    //This isn't normally needed but PM2 doesn't work with chalk unless I do this
    colour = new chalk.constructor({
        enabled: true
    }),
    //Unflipped tables for use with the auto-table-unfipper
    unflippedTables = ["┬─┬ ︵ /(.□. \\\\)", "┬─┬ノ( º _ ºノ)", "┬─┬ ノ( ゜-゜ノ)", "┬─┬ ノ( ^_^ノ)", "┬──┬ ¯\\\\_(ツ)", "(╯°□°）╯︵ /(.□. \\\\)"];

let urls = ['https://www.twitch.tv/winningthewaronpants'], //Twitch URLS the bot pulls from to link to in the Streaming Status
    //Bot Constructor Creation check https://abal.moe/Eris/Client.html for more info
    bot = new Eris(options.token, {
        getAllUsers: true,
        messageLimit: 0,
        maxShards: 1,
        autoReconnect: true,
        disableEveryone: true,
        disabledEvents: {
            VOICE_STATE_UPDATE: true,
            TYPING_START: true,
            GUILD_EMOJI_UPDATE: true,
            GUILD_INTEGRATIONS_UPDATE: true,
            GUILD_BAN_ADD: true,
            GUILD_BAN_REMOVE: true,
            MESSAGE_UPDATE: true,
            CHANNEL_CREATE: true,
            CHANNEL_DELETE: true
        }
    });

//Make Promises faster and more efficent by using BlueBirds Implmentation of them
global.Promise = require('bluebird'),
//Console Log Colours
botC = colour.magenta.bold,
userC = colour.cyan.bold,
guildC = colour.black.bold,
channelC = colour.green.bold,
miscC = colour.blue.bold,
warningC = colour.yellow.bold,
errorC = colour.red.bold;

//Ready Event
bot.on("ready", () => {
    //Sets the status's of every shard seperately
    utils.setRandomStatus(bot, urls)
    //This stuff below is sent to the console when the bot is ready
    console.log(botC(bot.user.username + " is now Ready."));
    console.log('Current # of Commands Loaded: ' + warningC(Object.keys(commands).length))
    console.log("Users: " + userC(bot.users.size) + " | Channels: " + channelC(Object.keys(bot.channelGuildMap).length) + " | Servers: " + guildC(bot.guilds.size))
    //Run inactivity checker and output the number of inactive servers
    usageChecker.checkInactivity(bot).then(response => console.log(botC(response))).catch(err => console.log(errorC(err)));
})

//On Message Creation Event
bot.on("messageCreate", msg => {
    //If bot isn't ready or if the message author is a bot who isn't Kimi do nothing with the message
    if (!bot.ready || (msg.author.bot && msg.author.id !== "174669219659513856")) return;
    //else if (msg.author.id !== '87600987040120832') return; //Used only if I want to disable the bot for everyone but me while testing/debugging
    else {
        //If used in guild and the guild has a custom prefix set the msgPrefix as such otherwise grab the default prefix
        let msgPrefix = msg.channel.guild && database.getPrefix(msg.channel.guild.id) !== undefined ? database.getPrefix(msg.channel.guild.id) : options.prefix;
        //Use Eval on the message if it starts with sudo and used by Mei
        if (msg.content.split(" ")[0] === "sudo" && msg.author.id === "87600987040120832") {
            evalText(msg, msg.content.substring((msg.content.split(" ")[0].substring(1)).length + 2));
            return;
        }
        //If stuff that isn't a command is used in a PM treat it as using cleverbot by adding the correct prefix as well as the 'chat' command text to the message
        if (!msg.channel.guild && !msg.content.startsWith(options.prefix)) msg.content = msgPrefix + "chat " + msg.content;
        //If used in a Guild
        if (msg.channel.guild) {
            //If Message is a tableFlip and the Guild has tableflip(tableunflip) on return an unflipped table
            if (msg.content === "(╯°□°）╯︵ ┻━┻") database.checkSetting(msg.channel.guild.id, 'tableflip').then(() => bot.createMessage(msg.channel.id, unflippedTables[~~(Math.random() * (unflippedTables.length))])).catch(err => utils.fileLog(err))
                //Check if message starts with a bot user mention and if so replace with the correct prefix and the 'chat' command text
            if (msg.content.replace(/<@!/, "<@").startsWith(bot.user.mention)) msg.content = msg.content.replace(/<@!/g, "<@").replace(bot.user.mention, msgPrefix + "chat");
            //Prefix command override so that prefix can be used with the default command prefix to prevent forgotten prefixes
            if ((msg.content.startsWith(options.prefix + 'prefix') || msg.content.startsWith(options.prefix + 'checkprefix')) && msgPrefix !== options.prefix) msg.content = msg.content.replace(options.prefix, msgPrefix)
        }
        //If the message stats with the set prefix
        if (msg.content.startsWith(msgPrefix)) {
            let formatedMsg = msg.content.substring(msgPrefix.length, msg.content.length), //Format message to remove command prefix
                cmdTxt = formatedMsg.split(" ")[0].toLowerCase(), //Get command from the formated message
                args = formatedMsg.substring((formatedMsg.split(" ")[0]).length + 1); //Get arguments from the formated message
            if (commandAliases.hasOwnProperty(cmdTxt)) cmdTxt = commandAliases[cmdTxt]; //If the cmdTxt is an alias of the command
            if (cmdTxt === 'channelmute') processCmd(msg, args, commands[cmdTxt], bot); //Override channelCheck if cmd is channelmute to unmute a muted channel
            //Check if a Command was used and runs the corresponding code depending on if it was used in a Guild or not, if in guild checks for muted channel and disabled command
            else if (commands.hasOwnProperty(cmdTxt)) database.checkChannel(msg.channel.id).then(() => database.checkCommand(msg.channel.guild, cmdTxt).then(() => processCmd(msg, args, commands[cmdTxt], bot)))
        }
    }
});

function evalText(msg, args) {
    let result;
    //Trys to run eval on the text and output either an error or the result if applicable
    try {
        result = eval("try{" + args + "}catch(err){console.log(err);msg.channel.createMessage(\"```\"+err+\"```\");}");
    } catch (e) {
        console.log(e)
        msg.channel.createMessage("```" + e + "```");
    }
    //If result isn't undefined and it isn't an object return to channel
    if (result && typeof result !== "object") msg.channel.createMessage(result);
}

//New Guild Member Event
bot.on("guildMemberAdd", (guild, member) => {
    //Checks to make sure guild and a member was sent
    if (guild && member) {
        //Checks to see if the guild has a welcome set
        database.checkSetting(guild.id, 'welcome').then(response => {
            sendGuildMessage(response, guild, member);
        }).catch(err => utils.fileLog(err));
    }
})

//Guild Member Left Event
bot.on("guildMemberRemove", (guild, member) => {
    //Checks to make sure guild and a member was sent
    if (guild && member) {
        //Checks to see if the guild has a leave set
        database.checkSetting(guild.id, 'leave').then(response => {
            sendGuildMessage(response, guild, member);
        }).catch(err => utils.fileLog(err))
    }
})

//Replaces the correct strings with the correct variables then sends the message to the channel
function sendGuildMessage(response, guild, member) {
    bot.createMessage(response.channel, response.response.replace(/\[GuildName]/g, guild.name).replace(/\[ChannelName]/g, guild.channels.get(response.channel.toString()).name).replace(/\[ChannelMention]/g, guild.channels.get(response.channel.toString()).mention).replace(/\[UserName]/g, member.user.username).replace(/\[UserMention]/g, member.user.mention));
}

//Guild Joined Event
bot.on('guildCreate', guild => {
    //Post Guild Count
    postGuildCount()
    //Add guild to usageChecker database
    usageChecker.addToUsageCheck(guild.id);
})

//Guild Left Event
bot.on('guildDelete', guild => {
    //Post Guild Count
    postGuildCount()
    //Remove Guild from database and log if error
    database.removeGuild(guild.id).catch(err => utils.fileLog(err))
    //Remove guild from guildPrefix array if it exists in it
    database.removeGuildfromJson(guild.id)
    //Remove from usageChecker database
    usageChecker.removeFromUsageCheck(guild.id);
})

//Load Commands then Connect(Logs any errors to console and file)
commandLoader.load().then(() => {
    bot.connect().then(console.log(warningC("Logged in using Token")))
}).catch(err => utils.fileLog(err));

//Posts Guild Count to Discord Bots and Carbonitex
function postGuildCount() {
    //Check that the bot is ready so premature guild creates won't cause a crash
    if (bot.ready) {
        //Post Guild Count to Discord Bots and if error log to file and console
        axios({
            method: 'post',
            url: "https://bots.discord.pw/api/bots/" + bot.user.id + "/stats",
            headers: {
                "Authorization": options.bot_key,
                "content-type": "application/json"
            },
            data: {
                "server_count": bot.guilds.size
            }
        }).catch(err => utils.fileLog(err));
        //Post Guild Count to Carbonitex and if error log to file and console
        axios({
            method: 'post',
            url: "https://www.carbonitex.net/discord/data/botdata.php",
            headers: {
                "content-type": "application/json"
            },
            data: {
                "key": options.carbon_key,
                "servercount": bot.guilds.size
            }
        }).catch(err => utils.fileLog(err));
    }
}

//Checks to see if any shard is disconnected every 10s and trys to reconnect it if so
setInterval(() => {
    bot.shards.forEach(shard => {
        if (shard.status === 'disconnected') shard.connect();
    })
}, 10000)

//Changes the bots status every 10mins
setInterval(() => utils.setRandomStatus(bot, urls), 6e+5);

//Changes the bots avatar every 2hrs
setInterval(() => {
    //Reads avatar directory and randomly picks an avatar to switch to
    fs.readdir(`${__dirname}/avatars/`, (err, files) => {
        if (err) utils.fileLog(err)
        else {
            let avatar = files[~~(Math.random() * (files.length))];
            //Reads the avatar image file and changes the bots avatar to it
            fs.readFile(`${__dirname}/avatars/${avatar}`, (err, image) => {
                if (err) utils.fileLog(err)
                else {
                    bot.editSelf({
                        avatar: `data:image/jpg;base64,${image.toString('base64')}`
                    }).then(() => console.log(botC('Changed avatar to ' + avatar))).catch(err => utils.fileLog(err));
                }
            })
        }
    });
}, 7.2e+6);

//Bot Error Event
bot.on("error", err => utils.fileLog(err)) //Logs error to file and console

//Bot Warn Event(Outputs issues that aren't major)
bot.on("warn", (warn, id) => {
    console.log(warningC(warn, id));
})

//Debug event only used to find errors and usually disabled
//bot.on("debug", console.log)

//Shard Resume Event
bot.on('shardResume', id => {
    console.log(botC("@" + bot.user.username) + " - " + warningC("SHARD #" + id + "RECONNECTED"));
})

//Shard Disconnect Event
bot.on("shardDisconnect", (err, id) => {
    console.log(botC("@" + bot.user.username) + " - " + warningC("SHARD #" + id + "DISCONNECTED"));
    utils.fileLog(err); //Logs reason/error to file and console
})

//Whole Bot Disconnect Event
bot.on("disconnect", err => {
    console.log(botC("@" + bot.user.username) + " - " + errorC("DISCONNECTED"));
    utils.fileLog(err); //Logs Disconnect reason/error to file and console
    throw 'Bot Disconnected'
})<commit_msg>Added check for carbon/bot key<commit_after>const Eris = require('eris'), //The bot's api library
    chalk = require('chalk'), //Used to make the console have pretty colours
    fs = require('fs'), //For reading/writing to a file
    axios = require('axios'), //HTTP client for requests to and from websites
    options = require('./options/options.json'),
    commandLoader = require('./utils/commandLoader.js'),
    utils = require('./utils/utils.js'),
    database = require('./utils/database.js'),
    processCmd = require('./utils/commandHandler.js'),
    usageChecker = require('./utils/usageChecker.js'),
    //This isn't normally needed but PM2 doesn't work with chalk unless I do this
    colour = new chalk.constructor({
        enabled: true
    }),
    //Unflipped tables for use with the auto-table-unfipper
    unflippedTables = ["┬─┬ ︵ /(.□. \\\\)", "┬─┬ノ( º _ ºノ)", "┬─┬ ノ( ゜-゜ノ)", "┬─┬ ノ( ^_^ノ)", "┬──┬ ¯\\\\_(ツ)", "(╯°□°）╯︵ /(.□. \\\\)"];

let urls = ['https://www.twitch.tv/winningthewaronpants'], //Twitch URLS the bot pulls from to link to in the Streaming Status
    //Bot Constructor Creation check https://abal.moe/Eris/Client.html for more info
    bot = new Eris(options.token, {
        getAllUsers: true,
        messageLimit: 0,
        maxShards: 1,
        autoReconnect: true,
        disableEveryone: true,
        disabledEvents: {
            VOICE_STATE_UPDATE: true,
            TYPING_START: true,
            GUILD_EMOJI_UPDATE: true,
            GUILD_INTEGRATIONS_UPDATE: true,
            GUILD_BAN_ADD: true,
            GUILD_BAN_REMOVE: true,
            MESSAGE_UPDATE: true,
            CHANNEL_CREATE: true,
            CHANNEL_DELETE: true
        }
    });

//Make Promises faster and more efficent by using BlueBirds Implmentation of them
global.Promise = require('bluebird'),
//Console Log Colours
botC = colour.magenta.bold,
userC = colour.cyan.bold,
guildC = colour.black.bold,
channelC = colour.green.bold,
miscC = colour.blue.bold,
warningC = colour.yellow.bold,
errorC = colour.red.bold;

//Ready Event
bot.on("ready", () => {
    //Sets the status's of every shard seperately
    utils.setRandomStatus(bot, urls)
    //This stuff below is sent to the console when the bot is ready
    console.log(botC(bot.user.username + " is now Ready."));
    console.log('Current # of Commands Loaded: ' + warningC(Object.keys(commands).length))
    console.log("Users: " + userC(bot.users.size) + " | Channels: " + channelC(Object.keys(bot.channelGuildMap).length) + " | Servers: " + guildC(bot.guilds.size))
    //Run inactivity checker and output the number of inactive servers
    usageChecker.checkInactivity(bot).then(response => console.log(botC(response))).catch(err => console.log(errorC(err)));
})

//On Message Creation Event
bot.on("messageCreate", msg => {
    //If bot isn't ready or if the message author is a bot who isn't Kimi do nothing with the message
    if (!bot.ready || (msg.author.bot && msg.author.id !== "174669219659513856")) return;
    //else if (msg.author.id !== '87600987040120832') return; //Used only if I want to disable the bot for everyone but me while testing/debugging
    else {
        //If used in guild and the guild has a custom prefix set the msgPrefix as such otherwise grab the default prefix
        let msgPrefix = msg.channel.guild && database.getPrefix(msg.channel.guild.id) !== undefined ? database.getPrefix(msg.channel.guild.id) : options.prefix;
        //Use Eval on the message if it starts with sudo and used by Mei
        if (msg.content.split(" ")[0] === "sudo" && msg.author.id === "87600987040120832") {
            evalText(msg, msg.content.substring((msg.content.split(" ")[0].substring(1)).length + 2));
            return;
        }
        //If stuff that isn't a command is used in a PM treat it as using cleverbot by adding the correct prefix as well as the 'chat' command text to the message
        if (!msg.channel.guild && !msg.content.startsWith(options.prefix)) msg.content = msgPrefix + "chat " + msg.content;
        //If used in a Guild
        if (msg.channel.guild) {
            //If Message is a tableFlip and the Guild has tableflip(tableunflip) on return an unflipped table
            if (msg.content === "(╯°□°）╯︵ ┻━┻") database.checkSetting(msg.channel.guild.id, 'tableflip').then(() => bot.createMessage(msg.channel.id, unflippedTables[~~(Math.random() * (unflippedTables.length))])).catch(err => utils.fileLog(err))
                //Check if message starts with a bot user mention and if so replace with the correct prefix and the 'chat' command text
            if (msg.content.replace(/<@!/, "<@").startsWith(bot.user.mention)) msg.content = msg.content.replace(/<@!/g, "<@").replace(bot.user.mention, msgPrefix + "chat");
            //Prefix command override so that prefix can be used with the default command prefix to prevent forgotten prefixes
            if ((msg.content.startsWith(options.prefix + 'prefix') || msg.content.startsWith(options.prefix + 'checkprefix')) && msgPrefix !== options.prefix) msg.content = msg.content.replace(options.prefix, msgPrefix)
        }
        //If the message stats with the set prefix
        if (msg.content.startsWith(msgPrefix)) {
            let formatedMsg = msg.content.substring(msgPrefix.length, msg.content.length), //Format message to remove command prefix
                cmdTxt = formatedMsg.split(" ")[0].toLowerCase(), //Get command from the formated message
                args = formatedMsg.substring((formatedMsg.split(" ")[0]).length + 1); //Get arguments from the formated message
            if (commandAliases.hasOwnProperty(cmdTxt)) cmdTxt = commandAliases[cmdTxt]; //If the cmdTxt is an alias of the command
            if (cmdTxt === 'channelmute') processCmd(msg, args, commands[cmdTxt], bot); //Override channelCheck if cmd is channelmute to unmute a muted channel
            //Check if a Command was used and runs the corresponding code depending on if it was used in a Guild or not, if in guild checks for muted channel and disabled command
            else if (commands.hasOwnProperty(cmdTxt)) database.checkChannel(msg.channel.id).then(() => database.checkCommand(msg.channel.guild, cmdTxt).then(() => processCmd(msg, args, commands[cmdTxt], bot)))
        }
    }
});

function evalText(msg, args) {
    let result;
    //Trys to run eval on the text and output either an error or the result if applicable
    try {
        result = eval("try{" + args + "}catch(err){console.log(err);msg.channel.createMessage(\"```\"+err+\"```\");}");
    } catch (e) {
        console.log(e)
        msg.channel.createMessage("```" + e + "```");
    }
    //If result isn't undefined and it isn't an object return to channel
    if (result && typeof result !== "object") msg.channel.createMessage(result);
}

//New Guild Member Event
bot.on("guildMemberAdd", (guild, member) => {
    //Checks to make sure guild and a member was sent
    if (guild && member) {
        //Checks to see if the guild has a welcome set
        database.checkSetting(guild.id, 'welcome').then(response => {
            sendGuildMessage(response, guild, member);
        }).catch(err => utils.fileLog(err));
    }
})

//Guild Member Left Event
bot.on("guildMemberRemove", (guild, member) => {
    //Checks to make sure guild and a member was sent
    if (guild && member) {
        //Checks to see if the guild has a leave set
        database.checkSetting(guild.id, 'leave').then(response => {
            sendGuildMessage(response, guild, member);
        }).catch(err => utils.fileLog(err))
    }
})

//Replaces the correct strings with the correct variables then sends the message to the channel
function sendGuildMessage(response, guild, member) {
    bot.createMessage(response.channel, response.response.replace(/\[GuildName]/g, guild.name).replace(/\[ChannelName]/g, guild.channels.get(response.channel.toString()).name).replace(/\[ChannelMention]/g, guild.channels.get(response.channel.toString()).mention).replace(/\[UserName]/g, member.user.username).replace(/\[UserMention]/g, member.user.mention));
}

//Guild Joined Event
bot.on('guildCreate', guild => {
    //Post Guild Count
    postGuildCount()
    //Add guild to usageChecker database
    usageChecker.addToUsageCheck(guild.id);
})

//Guild Left Event
bot.on('guildDelete', guild => {
    //Post Guild Count
    postGuildCount()
    //Remove Guild from database and log if error
    database.removeGuild(guild.id).catch(err => utils.fileLog(err))
    //Remove guild from guildPrefix array if it exists in it
    database.removeGuildfromJson(guild.id)
    //Remove from usageChecker database
    usageChecker.removeFromUsageCheck(guild.id);
})

//Load Commands then Connect(Logs any errors to console and file)
commandLoader.load().then(() => {
    bot.connect().then(console.log(warningC("Logged in using Token")))
}).catch(err => utils.fileLog(err));

//Posts Guild Count to Discord Bots and Carbonitex
function postGuildCount() {
    //Check that the bot is ready so premature guild creates won't cause a crash as well as checking if theres a bot/carbon key
    if (bot.ready && options.bot_key !== '' && options.carbon_key !== '') {
        //Post Guild Count to Discord Bots and if error log to file and console
        axios({
            method: 'post',
            url: "https://bots.discord.pw/api/bots/" + bot.user.id + "/stats",
            headers: {
                "Authorization": options.bot_key,
                "content-type": "application/json"
            },
            data: {
                "server_count": bot.guilds.size
            }
        }).catch(err => utils.fileLog(err));
        //Post Guild Count to Carbonitex and if error log to file and console
        axios({
            method: 'post',
            url: "https://www.carbonitex.net/discord/data/botdata.php",
            headers: {
                "content-type": "application/json"
            },
            data: {
                "key": options.carbon_key,
                "servercount": bot.guilds.size
            }
        }).catch(err => utils.fileLog(err));
    }
}

//Checks to see if any shard is disconnected every 10s and trys to reconnect it if so
setInterval(() => {
    bot.shards.forEach(shard => {
        if (shard.status === 'disconnected') shard.connect();
    })
}, 10000)

//Changes the bots status every 10mins
setInterval(() => utils.setRandomStatus(bot, urls), 6e+5);

//Changes the bots avatar every 2hrs
setInterval(() => {
    //Reads avatar directory and randomly picks an avatar to switch to
    fs.readdir(`${__dirname}/avatars/`, (err, files) => {
        if (err) utils.fileLog(err)
        else {
            let avatar = files[~~(Math.random() * (files.length))];
            //Reads the avatar image file and changes the bots avatar to it
            fs.readFile(`${__dirname}/avatars/${avatar}`, (err, image) => {
                if (err) utils.fileLog(err)
                else {
                    bot.editSelf({
                        avatar: `data:image/jpg;base64,${image.toString('base64')}`
                    }).then(() => console.log(botC('Changed avatar to ' + avatar))).catch(err => utils.fileLog(err));
                }
            })
        }
    });
}, 7.2e+6);

//Bot Error Event
bot.on("error", err => utils.fileLog(err)) //Logs error to file and console

//Bot Warn Event(Outputs issues that aren't major)
bot.on("warn", (warn, id) => {
    console.log(warningC(warn, id));
})

//Debug event only used to find errors and usually disabled
//bot.on("debug", console.log)

//Shard Resume Event
bot.on('shardResume', id => {
    console.log(botC("@" + bot.user.username) + " - " + warningC("SHARD #" + id + "RECONNECTED"));
})

//Shard Disconnect Event
bot.on("shardDisconnect", (err, id) => {
    console.log(botC("@" + bot.user.username) + " - " + warningC("SHARD #" + id + "DISCONNECTED"));
    utils.fileLog(err); //Logs reason/error to file and console
})

//Whole Bot Disconnect Event
bot.on("disconnect", err => {
    console.log(botC("@" + bot.user.username) + " - " + errorC("DISCONNECTED"));
    utils.fileLog(err); //Logs Disconnect reason/error to file and console
    throw 'Bot Disconnected'
})<|endoftext|>