<commit_before>// Copyright (c) 2012 IT Agenten - http://www.it-agenten.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// This 'provider' stores the images created via mongoose-attachments in the
// local file system.
// It requires that the option 'directory' points to the absolute path of
// the directory where it will create subfolders per image type.
//
// Example:
// type is 'thumb'
// and 'directory' points to /path/to/public/images/
// When storing data into the schema, the thumbnail created by mongoose-attachemnts
// is stored as /path/to/public/images/thumb/<ObjectID>-thumb.<format>
//
// The absolute path to the image is stored in Model.image.<type>.path

var attachments = require('mongoose-attachments');
var fs = require('fs');
var path = require('path');
var util = require('util');

function LocalfsStorage(options) {
	attachments.StorageProvider.call(this, options);
}
util.inherits(LocalfsStorage, attachments.StorageProvider);

/*
 * Returns the file system path (absolute)
 * and not the actual URL.
 */
LocalfsStorage.prototype.getUrl = function( path ){
	return path;
};

LocalfsStorage.prototype.createOrReplace = function(attachment, callback) {
	var self = this;
	var dirname = path.join(path.dirname(attachment.path), attachment.style.name);
    var existsSync = fs.existsSync || path.existsSync;
	if (!existsSync(dirname)) {
		var pathParts = dirname.split(path.sep);
		var p = "/";
		for (var i=0; i<pathParts.length; i++) {
			p = path.join(p, pathParts[i]);
			if (!existsSync(p)) {
				fs.mkdirSync(p, '0750');
			}
		}
	}

	var basename = path.basename(attachment.path);
	if (path.extname(basename).length == 0) {
		basename = basename + "." + attachment.model.extension;
	}
	attachment.path = path.join(dirname, basename);
	fs.rename(attachment.filename, attachment.path, function(error) {
		if (error) {
			callback(error);
		} else {
			attachment.defaultUrl = self.getUrl( attachment.path );
			callback(null, attachment);
		}
	});
};

// register the File System Storage Provider into the registry
attachments.registerStorageProvider('localfs', LocalfsStorage);

// export attachments so there is no need for an explicit require of it
module.exports = attachments;
<commit_msg>add optional prefix to remove from absolute path<commit_after>// Copyright (c) 2012 IT Agenten - http://www.it-agenten.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// This 'provider' stores the images created via mongoose-attachments in the
// local file system.
// It requires that the option 'directory' points to the absolute path of
// the directory where it will create subfolders per image type.
//
// Example:
// type is 'thumb'
// and 'directory' points to /path/to/public/images/
// When storing data into the schema, the thumbnail created by mongoose-attachemnts
// is stored as /path/to/public/images/thumb/<ObjectID>-thumb.<format>
//
// The absolute path to the image is stored in Model.image.<type>.path

var attachments = require('mongoose-attachments');
var fs = require('fs');
var path = require('path');
var util = require('util');
var existsSync = fs.existsSync || path.existsSync;

function LocalfsStorage(options) {
	if(options.removePrefix) {
		this.prefix = options.removePrefix;
	}
	attachments.StorageProvider.call(this, options);
}
util.inherits(LocalfsStorage, attachments.StorageProvider);

/*
 * Returns the file system path (absolute)
 * and not the actual URL.
 */
LocalfsStorage.prototype.getUrl = function( path ){
		if(this.prefix) {
			// if there is a user-specified prefix, we remove it from the path
			return path.substring(this.prefix.length);
		}
		return path;
};

LocalfsStorage.prototype.createOrReplace = function(attachment, callback) {
	var self = this;
	var dirname = path.join(path.dirname(attachment.path), attachment.style.name);
	if (!existsSync(dirname)) {
		var pathParts = dirname.split(path.sep);
		var p = "/";
		for (var i=0; i<pathParts.length; i++) {
			p = path.join(p, pathParts[i]);
			if (!existsSync(p)) {
				fs.mkdirSync(p, '0750');
			}
		}
	}

	var basename = path.basename(attachment.path);
	if (path.extname(basename).length == 0) {
		basename = basename + "." + attachment.model.extension;
	}
	attachment.path = path.join(dirname, basename);
	fs.rename(attachment.filename, attachment.path, function(error) {
		if (error) {
			callback(error);
		} else {
			attachment.defaultUrl = self.getUrl(attachment.path);
			callback(null, attachment);
		}
	});
};

// register the File System Storage Provider into the registry
attachments.registerStorageProvider('localfs', LocalfsStorage);

// export attachments so there is no need for an explicit require of it
module.exports = attachments;
<|endoftext|>


<commit_before>import request from 'request';
import Promise from 'bluebird';

Promise.promisifyAll(request);

class MessengerClient {
  constructor(context) {
    this.context = context;
  }

  getUserData(id) {
    return request.getAsync({
      uri: `https://graph.facebook.com/v2.6/${id}`,
      qs: {
        fields: 'first_name,last_name,profile_pic',
        access_token: this.context.pageAccessToken
      },
      json: true
    }).then((response) => {
      return response.body;
    });
  }
}

export default MessengerClient;
<commit_msg>messenger-client: add sendData method<commit_after>import request from 'request';
import Promise from 'bluebird';

Promise.promisifyAll(request);

class MessengerClient {
  constructor(context) {
    this.context = context;
  }

  getUserData(id) {
    return request.getAsync({
      uri: `https://graph.facebook.com/v2.6/${id}`,
      qs: {
        fields: 'first_name,last_name,profile_pic',
        access_token: this.context.pageAccessToken
      },
      json: true
    }).then((response) => {
      return response.body;
    });
  }

  sendData(payload, sender) {
    return request.postAsync({
      uri: 'https://graph.facebook.com/v2.6/me/messages',
      qs: {
        access_token: this.context.pageAccessToken
      },
      json: {
        recipient: { id: sender },
        message: payload
      }
    });
  }
}

export default MessengerClient;
<|endoftext|>


<commit_before>var uri = require('url')
  , State = require('../state')
  , dispatch = require('../utils').dispatch
  , swizzleRedirect = require('../utils/swizzle-redirect')
  , swizzleRender = require('../utils/swizzle-render')
  , swizzleEnd = require('../utils/swizzle-end')
  , flatten = require('utils-flatten')
  , merge = require('utils-merge')
  , utils = require('../utils')
  , debug = require('debug')('flowstate')
  , SessionStore = require('../stores/session');

  // TIPS: Generally, push a state before redirecting (unless wizard style
  //  alternatively, handler can lazily push, for its own state, to preserve storage
  //  which could be done based on the presence of a returnTo (which is not the handlers URL)

/**
 * Load state.
 *
 * This middleware is used to load state associated with a request.  The state
 * will be made available at `req.state`.
 *
 * HTTP is a stateless protocol.  In order to support stateful interactions, the
 * client and server need to operate in coordination.  The server is responsible
 * for persisting state associated with a request.  The client is responsible for
 * indicating that state in subsequent requests to the server (via a `state`
 * parameter in the query or body, by default).  The server then loads the
 * previously persisted state in order to continue processing the transaction.
 *
 * Options:
 *
 *     name   set req.state only if the state name is equal to the option value
 *     required   require state, failing otherwise (default: false)
 *
 * Examples:
 *
 *     app.get('/login',
 *       flowstate.loadState(),
 *       function(req, res, next) {
 *         res.locals.failureCount = req.state.failureCount;
 *         res.render('login');
 *       });
 *
 *     app.get('/login/callback',
 *       flowstate.loadState({ name: 'oauth2-redirect', required: true }),
 *       function(req, res, next) {
 *         // ...
 *       });
 *
 * @return {Function}
 * @api public
 */
module.exports = function(options) {
  options = options || {}

  var store = options.store || new SessionStore(options);
  var getHandle = options.getHandle || function(req) {
    return (req.query && req.query.state) || (req.body && req.body.state);
  }

  // WIP: On state init, set `resume` if the state is not intended for this lcoation

  // TODO: add a "locations" option, with relative paths that will be added to location

  return function state(req, res, next) {
    // self-awareness
    if (req.state) {
      next();
      return;
    }

    // expose store
    req.stateStore = store;

    var data = {
      returnTo: (req.query && req.query.return_to) || (req.body && req.body.return_to) || (req.header ? req.header('referer') : undefined)
    }
    if (data.returnTo == utils.originalURL(req)) {
      // TODO: Maybe we do want to return to same resource?  POST to create, and then view?
      // Don't return to same resource
      delete data.returnTo;
    }

    // TODO: test case for originalUrl
    req.state = new State(req, data, undefined, options.external);
    // TODO: Delete this line once tests are fixed
    if (req.state.returnTo === undefined) { delete req.state.returnTo; }

    res.pushState = function(state, url, end) {
      end = end !== undefined ? end : true;

      // WIP: Test case where this is a relative URL
      if (url) { state.location = uri.resolve(utils.originalURL(req), url); }
      req.state.push(state);
      end && this.redirect(url);
    }

    res.resumeState = function(yields, cb) {
      if (typeof yields == 'function') {
        cb = yields;
        yields = undefined;
      }

      req.state.complete();

      if (req.state.returnTo) {
        if (!yields) {
          this.redirect(req.state.returnTo);
        } else {
          var state = new State(req, yields);
          state.location = req.state.returnTo;
          var self = this;

          state.save(function(err) {
            if (err) { return cb(err); }
            self.redirect(req.state.returnTo + '?state=' + state.handle);
          })
        }
      } else if (req.state.state) {
        var self = this;

        req.state.destroy(function(err) {
          //if (err) { return next(err); }
          //return proceed(state.state, state);
          //cb();

          store.load(req, req.state.state, function(err, data) {
            if (err) { return next(err); } // FIXME: this should call cb?

            // WIP: Need a state stack to traverse to do destroy on res.render/res.redirect

            var state = new State(req, data, req.state.state);
            merge(state, yields);

            if (!state.isModified()) {
              self.redirect(data.location + '?state=' + req.state.state);
            } else {
              state.save(function(err) {
                if (err) { return cb(err); }
                self.redirect(state.location + '?state=' + state.handle);
              })
            }

            //req.state = new State(req, data, req.state.resume);
            //merge(req.state, state);

            // TODO: Need to save this after merging

            // TODO: Better query serialization
            //self.redirect(data.location + '?state=' + req.state.resume);
          });

        });
      } else {
        cb();
      }
    }

    // swizzle redirect to commit the state
    swizzleRedirect(res, function commitState(url, cb) {
      debug('redirect %s, %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o, new: %o)', url, req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal(), req.state.isNew());

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      function proceed(v) {
        if (!v) { return cb(); }

        // Add `state` as a query parameter to the URL that the user agent is
        // being redirected to.
        var l = uri.parse(url, true);
        delete l.search;
        l.query.state = v;
        return cb(null, uri.format(l));
      }

      if (req.state.isComplete()) {
        proceed();
      } else if (req.state.isModified()) {
        // FIXME: only do this if state is incomplete?
        // TODO: Eliminate this defaulting of returnTo here, not sure what its doing
        if (!req.state.returnTo && !req.state.state && !req.yieldState)  { req.state.returnTo = req.originalUrl || req.url; }
        if (req.state.isExternal() && options.continue) {
          req.state.location = uri.resolve(utils.originalURL(req), options.continue);
          // FIXME: Don't set returnTO in this case, wherever it is set initially.  Find that and fix it there.
          delete req.state.returnTo;
        }

        req.state.save(function(err) {
          if (err) { return next(err); } // FIXME: This should call cb?
          debug('set `state` query parameter to saved state (%s)', req.state.handle);
          proceed(req.state.handle);
        });
      } else if (req.state.isNew()) {
        // newly initialized state, without any state to actually track.  Just pass on
        // the parent state.
        proceed(req.state.state);
      } else { // current state
        debug('set `state` query parameter to current state (%s)', req.state.handle);
        proceed(req.state.handle);
      }
    });

    // swizzle render to commit the state
    swizzleRender(res, function commitState(cb) {
      debug('render %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o)', req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal());

      // FIXME: Only get this from state, as in the isComplete case below
      //        as in the isNew case below.  this whole conditional block can be cleaned up
      var returnTo = (req.query && req.query.return_to) || (req.body && req.body.return_to);
      if (returnTo) {
        res.locals.returnTo = returnTo;
      }

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      if (req.state.isComplete()) {
        cb();
      } else if (req.state.isModified()) {
        req.state.save(function(err) {
          if (err) { return next(err); } // FIXME: This should call cb?
          debug('set `res.locals.state` to saved state (%s)', req.state.handle);
          res.locals.state = req.state.handle;
          cb();
        });
      } else if (req.state.isNew()) {
        if (req.state.state) {
          debug('set `res.locals.state` to parent state (%s)', req.state.state);
          res.locals.state = req.state.state;
        } else if (qstate) {
          debug('set `res.locals.state` to query state (%s)', qstate);
          res.locals.state = qstate;
        }
        if (req.state.returnTo) { res.locals.returnTo = req.state.returnTo; }
        cb();
      } else { // current state
        debug('set `res.locals.state` to current state (%s)', req.state.handle);
        res.locals.state = req.state.handle;
        cb();
      }
    });

    swizzleEnd(res, function autoDestroy(cb) {
      debug('end %O (handle: %s, complete: %o)', req.state,
        req.state.handle, req.state.isComplete());

      // WIP: figure out how to maintain incomplete states
      // probably best to mandate external states be  flagged complete ??

      // TODO: also check fo unchanged?
      //if (!req.state.isNew()) {
      if (req.state.isComplete()) {
        debug('destroying complete state (%s)', req.state.handle);
        req.state.destroy(function(err) {
          //if (err) { return next(err); }
          //return proceed(state.state, state);
          cb();
        });
      } else {
        cb();
      }
    });

    //next();
    if (options.external) { return next(); }

    var h = getHandle(req);
    if (!h) {
      return next();
    }

    store.load(req, h, function(err, state) {
      if (err) { return next(err); }

      if (!state) {
        return next();
      }

      state = new State(req, state, h);

      if (state.location && state.location !== utils.originalURLWithoutQuery(req)) {
        req.state = new State(req, { state: h });
      } else if (1) { // !name
        // TODO: Remove this
        req.state = state;
      } else {
        // The loaded state is not the expected state.  Save what was loaded,
        // as an optimization to avoid re-loading later if the state is resumed.
        req._state = state;
      }

      next();
    });
  };
};
<commit_msg>Simplify code.<commit_after>var uri = require('url')
  , State = require('../state')
  , dispatch = require('../utils').dispatch
  , swizzleRedirect = require('../utils/swizzle-redirect')
  , swizzleRender = require('../utils/swizzle-render')
  , swizzleEnd = require('../utils/swizzle-end')
  , flatten = require('utils-flatten')
  , merge = require('utils-merge')
  , utils = require('../utils')
  , debug = require('debug')('flowstate')
  , SessionStore = require('../stores/session');

  // TIPS: Generally, push a state before redirecting (unless wizard style
  //  alternatively, handler can lazily push, for its own state, to preserve storage
  //  which could be done based on the presence of a returnTo (which is not the handlers URL)

/**
 * Load state.
 *
 * This middleware is used to load state associated with a request.  The state
 * will be made available at `req.state`.
 *
 * HTTP is a stateless protocol.  In order to support stateful interactions, the
 * client and server need to operate in coordination.  The server is responsible
 * for persisting state associated with a request.  The client is responsible for
 * indicating that state in subsequent requests to the server (via a `state`
 * parameter in the query or body, by default).  The server then loads the
 * previously persisted state in order to continue processing the transaction.
 *
 * Options:
 *
 *     name   set req.state only if the state name is equal to the option value
 *     required   require state, failing otherwise (default: false)
 *
 * Examples:
 *
 *     app.get('/login',
 *       flowstate.loadState(),
 *       function(req, res, next) {
 *         res.locals.failureCount = req.state.failureCount;
 *         res.render('login');
 *       });
 *
 *     app.get('/login/callback',
 *       flowstate.loadState({ name: 'oauth2-redirect', required: true }),
 *       function(req, res, next) {
 *         // ...
 *       });
 *
 * @return {Function}
 * @api public
 */
module.exports = function(options) {
  options = options || {}

  var store = options.store || new SessionStore(options);
  var getHandle = options.getHandle || function(req) {
    return (req.query && req.query.state) || (req.body && req.body.state);
  }

  // WIP: On state init, set `resume` if the state is not intended for this lcoation

  // TODO: add a "locations" option, with relative paths that will be added to location

  return function state(req, res, next) {
    // self-awareness
    if (req.state) {
      next();
      return;
    }

    // expose store
    req.stateStore = store;

    var data = {
      returnTo: (req.query && req.query.return_to) || (req.body && req.body.return_to) || (req.header ? req.header('referer') : undefined)
    }
    if (data.returnTo == utils.originalURL(req)) {
      // TODO: Maybe we do want to return to same resource?  POST to create, and then view?
      // Don't return to same resource
      delete data.returnTo;
    }

    // TODO: test case for originalUrl
    req.state = new State(req, data, undefined, options.external);
    // TODO: Delete this line once tests are fixed
    if (req.state.returnTo === undefined) { delete req.state.returnTo; }

    res.pushState = function(state, url, end) {
      end = end !== undefined ? end : true;

      // WIP: Test case where this is a relative URL
      if (url) { state.location = uri.resolve(utils.originalURL(req), url); }
      req.state.push(state);
      end && this.redirect(url);
    }

    res.resumeState = function(yields, cb) {
      if (typeof yields == 'function') {
        cb = yields;
        yields = undefined;
      }

      req.state.complete();

      if (req.state.returnTo) {
        if (!yields) {
          this.redirect(req.state.returnTo);
        } else {
          var state = new State(req, yields);
          state.location = req.state.returnTo;
          var self = this;

          state.save(function(err) {
            if (err) { return cb(err); }
            self.redirect(req.state.returnTo + '?state=' + state.handle);
          })
        }
      } else if (req.state.state) {
        var self = this;

        req.state.destroy(function(err) {
          //if (err) { return next(err); }
          //return proceed(state.state, state);
          //cb();

          store.load(req, req.state.state, function(err, data) {
            if (err) { return next(err); } // FIXME: this should call cb?

            // WIP: Need a state stack to traverse to do destroy on res.render/res.redirect

            var state = new State(req, data, req.state.state);
            merge(state, yields);

            if (!state.isModified()) {
              self.redirect(data.location + '?state=' + req.state.state);
            } else {
              state.save(function(err) {
                if (err) { return cb(err); }
                self.redirect(state.location + '?state=' + state.handle);
              })
            }

            //req.state = new State(req, data, req.state.resume);
            //merge(req.state, state);

            // TODO: Need to save this after merging

            // TODO: Better query serialization
            //self.redirect(data.location + '?state=' + req.state.resume);
          });

        });
      } else {
        cb();
      }
    }

    // swizzle redirect to commit the state
    swizzleRedirect(res, function commitState(url, cb) {
      debug('redirect %s, %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o, new: %o)', url, req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal(), req.state.isNew());

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      function proceed(v) {
        if (!v) { return cb(); }

        // Add `state` as a query parameter to the URL that the user agent is
        // being redirected to.
        var l = uri.parse(url, true);
        delete l.search;
        l.query.state = v;
        return cb(null, uri.format(l));
      }

      if (req.state.isComplete()) {
        proceed();
      } else if (req.state.isModified()) {
        // FIXME: only do this if state is incomplete?
        // TODO: Eliminate this defaulting of returnTo here, not sure what its doing
        if (!req.state.returnTo && !req.state.state && !req.yieldState)  { req.state.returnTo = req.originalUrl || req.url; }
        if (req.state.isExternal() && options.continue) {
          req.state.location = uri.resolve(utils.originalURL(req), options.continue);
          // FIXME: Don't set returnTO in this case, wherever it is set initially.  Find that and fix it there.
          delete req.state.returnTo;
        }

        req.state.save(function(err) {
          if (err) { return next(err); } // FIXME: This should call cb?
          debug('set `state` query parameter to saved state (%s)', req.state.handle);
          proceed(req.state.handle);
        });
      } else if (req.state.isNew()) {
        // newly initialized state, without any state to actually track.  Just pass on
        // the parent state.
        proceed(req.state.state);
      } else { // current state
        debug('set `state` query parameter to current state (%s)', req.state.handle);
        proceed(req.state.handle);
      }
    });

    // swizzle render to commit the state
    swizzleRender(res, function commitState(cb) {
      debug('render %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o)', req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal());

      // FIXME: Only get this from state, as in the isComplete case below
      //        as in the isNew case below.  this whole conditional block can be cleaned up
      var returnTo = (req.query && req.query.return_to) || (req.body && req.body.return_to);
      if (returnTo) {
        res.locals.returnTo = returnTo;
      }

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      if (req.state.isComplete()) {
        cb();
      } else if (req.state.isModified()) {
        req.state.save(function(err) {
          if (err) { return next(err); } // FIXME: This should call cb?
          debug('set `res.locals.state` to saved state (%s)', req.state.handle);
          res.locals.state = req.state.handle;
          cb();
        });
      } else if (req.state.isNew()) {
        if (req.state.state) {
          debug('set `res.locals.state` to parent state (%s)', req.state.state);
          res.locals.state = req.state.state;
        } else if (qstate) {
          debug('set `res.locals.state` to query state (%s)', qstate);
          res.locals.state = qstate;
        }
        if (req.state.returnTo) { res.locals.returnTo = req.state.returnTo; }
        cb();
      } else { // current state
        debug('set `res.locals.state` to current state (%s)', req.state.handle);
        res.locals.state = req.state.handle;
        cb();
      }
    });

    swizzleEnd(res, function autoDestroy(cb) {
      debug('end %O (handle: %s, complete: %o)', req.state,
        req.state.handle, req.state.isComplete());

      // WIP: figure out how to maintain incomplete states
      // probably best to mandate external states be  flagged complete ??

      // TODO: also check fo unchanged?
      //if (!req.state.isNew()) {
      if (req.state.isComplete()) {
        debug('destroying complete state (%s)', req.state.handle);
        req.state.destroy(function(err) {
          //if (err) { return next(err); }
          //return proceed(state.state, state);
          cb();
        });
      } else {
        cb();
      }
    });

    //next();
    if (options.external) { return next(); }

    var h = getHandle(req);
    if (!h) {
      return next();
    }

    store.load(req, h, function(err, state) {
      if (err) { return next(err); }
      if (!state) { return next(); }

      if (state.location !== utils.originalURLWithoutQuery(req)) {
        req.state = new State(req, { state: h });
      } else {
        req.state = new State(req, state, h);
      }
      next();
    });
  };
};
<|endoftext|>


<commit_before>var createClass = require('../utilities/createClass');
var EventTarget = require('../utilities/eventTarget');
var Lang = require('../utilities/lang');
var AttrValidators = require('../utilities/attrValidators');

var validateKey = function(key) {
  if (!Lang.isString(key)) {
    throw new Error('Key must be a string');
  }
};

var Dictionary = createClass({
  mixins: [EventTarget],
  constructor: function() {
    this._backing = {};
  },
  instance: {
    /**
     * Put or override a value in the dictionary
     * @method put
     * @fires add
     * @fires update
     * @param  {String} key
     * @param  {Any} value (non-null, defined)
     */
    put: function(key, value) {
      validateKey(key);

      if (!Lang.isValue(value)) {
        throw new Error('Value must be a value. Use remove to remove entries.');
      }

      var validCtor = this.get('validCtor');
      if (validCtor && !(value instanceof validCtor)) {
        throw new Error('Value not instanceof ' + validCtor + '.');
      }

      var eventType = this.hasKey(key) ? 'update' : 'add';

      /**
       * A value was put into the dictionary.
       * @event add
       * @property {String} key the key for the add
       * @property {Any} value the value that was added
       */
      /**
       * A value was overridden into the dictionary.
       * @event update
       * @property {String} key the key for the add
       * @property {Any} value the value that was added
       * @property {Any} prevVal old value
       */
      this.fire(eventType, function() {
        this._backing[key] = value;
      }.bind(this), {
        key: key,
        value: value,
        prevVal: this._backing[key]
      });
    },

    /**
     * Get a value from the dictionary.
     * @method fetch
     * @param  {String} key
     * @return {Any} the value
     */
    fetch: function(key) {
      validateKey(key);

      if (this.hasKey(key)) {
        return this._backing[key];

      } else {
        var defaultValueFn = this.get('defaultValueFn');
        if (defaultValueFn !== null) {
          return defaultValueFn(key, this);
        } else {
          return this.get('defaultValue');
        }

        return this._backing[key];
      }
    },

    /**
     * Delete a value from the map
     * @method remove
     * @fires remove
     * @param  {String} key
     * @return {Any} removed value
     */
    remove: function(key) {
      validateKey(key);

      var value = this.fetch(key);

      if (!this.hasKey(key)) {
        return null;
      }

      /**
       * A value was removed from the map
       * @event remove
       * @property {String} key the key that was used to remove a value
       */
      this.fire('remove', function() {
        delete this._backing[key];
      }.bind(this), {
        key: key,
        value: value
      });

      return value;
    },

    hasKey: function(key) {
      validateKey(key);

      return key in this._backing;
    }
  },

  attrs: {
    /**
     * All values must be instance of this
     * @attribute validCtor
     * @type      null|Function
     */
    validCtor: {
      value: null,
      validator: AttrValidators.isNullOr(Lang.isFunction),
      initOnly: true
    }
  }
});

module.exports = Dictionary;
<commit_msg>remove default value stuff<commit_after>var createClass = require('../utilities/createClass');
var EventTarget = require('../utilities/eventTarget');
var Lang = require('../utilities/lang');
var AttrValidators = require('../utilities/attrValidators');

var validateKey = function(key) {
  if (!Lang.isString(key)) {
    throw new Error('Key must be a string');
  }
};

var Dictionary = createClass({
  mixins: [EventTarget],
  constructor: function() {
    this._backing = {};
  },
  instance: {
    /**
     * Put or override a value in the dictionary
     * @method put
     * @fires add
     * @fires update
     * @param  {String} key
     * @param  {Any} value (non-null, defined)
     */
    put: function(key, value) {
      validateKey(key);

      if (!Lang.isValue(value)) {
        throw new Error('Value must be a value. Use remove to remove entries.');
      }

      var validCtor = this.get('validCtor');
      if (validCtor && !(value instanceof validCtor)) {
        throw new Error('Value not instanceof ' + validCtor + '.');
      }

      var eventType = this.hasKey(key) ? 'update' : 'add';

      /**
       * A value was put into the dictionary.
       * @event add
       * @property {String} key the key for the add
       * @property {Any} value the value that was added
       */
      /**
       * A value was overridden into the dictionary.
       * @event update
       * @property {String} key the key for the add
       * @property {Any} value the value that was added
       * @property {Any} prevVal old value
       */
      this.fire(eventType, function() {
        this._backing[key] = value;
      }.bind(this), {
        key: key,
        value: value,
        prevVal: this._backing[key]
      });
    },

    /**
     * Get a value from the dictionary.
     * @method fetch
     * @param  {String} key
     * @return {Any} the value
     */
    fetch: function(key) {
      validateKey(key);
      return this._backing[key];
    },

    /**
     * Delete a value from the map
     * @method remove
     * @fires remove
     * @param  {String} key
     * @return {Any} removed value
     */
    remove: function(key) {
      validateKey(key);

      var value = this.fetch(key);

      if (!this.hasKey(key)) {
        return null;
      }

      /**
       * A value was removed from the map
       * @event remove
       * @property {String} key the key that was used to remove a value
       */
      this.fire('remove', function() {
        delete this._backing[key];
      }.bind(this), {
        key: key,
        value: value
      });

      return value;
    },

    hasKey: function(key) {
      validateKey(key);

      return key in this._backing;
    }
  },

  attrs: {
    /**
     * All values must be instance of this
     * @attribute validCtor
     * @type      null|Function
     */
    validCtor: {
      value: null,
      validator: AttrValidators.isNullOr(Lang.isFunction),
      initOnly: true
    }
  }
});

module.exports = Dictionary;
<|endoftext|>


<commit_before>// Copyright (c) 2015, EMC Corporation

'use strict';

module.exports = WorkItemModelFactory;

WorkItemModelFactory.$provide = 'Models.WorkItem';
WorkItemModelFactory.$inject = [
    'Model',
    '_',
    'Promise',
    'Assert',
    'Constants'
];

function WorkItemModelFactory (Model, _, Promise, assert, Constants) {

    function byPollers() {
        return {
            or: _.map(Constants.WorkItems.Pollers, function (pollerName) {
                return { name: pollerName };
            })
        };
    }
    return Model.extend({
        connection: 'mongo',
        identity: 'workitems',
        attributes: {
            name: {
                type: 'string',
                required: true
            },
            node: {
                model: 'nodes',
                defaultsTo: null
            },
            config: {
                type: 'json',
                defaultsTo: {}
            },
            pollInterval: {
                type: 'integer',
                required: true
            },
            nextScheduled: {
                type: 'datetime',
                defaultsTo: null
            },
            lastStarted: {
                type: 'datetime',
                defaultsTo: null
            },
            lastFinished: {
                type: 'datetime',
                defaultsTo: null
            },
            leaseToken: {
                type: 'string',
                uuidv4: true,
                defaultsTo: null
            },
            leaseExpires: {
                type: 'datetime',
                defaultsTo: null
            },
            failureCount: {
                type: 'integer',
                defaultsTo: 0,
                required: true
            },
            paused:{
                type: 'boolean',
                defaultsTo: false
            }
        },

        startNextScheduled: function startNextScheduled(leaseToken, criteria, leaseDuration) {
            var self = this;
            var now = new Date();
            return this.findOne({
                where: {
                    $and: [
                        criteria,
                        { paused: false },
                        {
                            leaseToken: null,
                            $or: [
                                { nextScheduled: { lessThan: now } },
                                { nextScheduled: null }
                            ]
                        }
                    ]
                },
                sort: 'nextScheduled ASC'
            }).then(function (workItem) {
                if (!workItem) {
                    return;
                }
                return self.update({
                    id: workItem.id,
                    leaseToken: null,
                    $or: [
                        { nextScheduled: { lessThan: now } },
                        { nextScheduled: null }
                    ]
                },
                {
                    lastStarted: now,
                    leaseToken: leaseToken,
                    leaseExpires: new Date(now.valueOf() + leaseDuration)
                }).then(function (workItems) {
                    /* some other worker acquired the lease, so we request another
                     * work item to process. */
                    if (!workItems.length || workItems[0].leaseToken !== leaseToken) {
                        return self.startNextScheduled(leaseToken, criteria, leaseDuration);
                    }
                    return workItems[0];
                });
            });
        },

        findExpired: function findExpired(leaseExpiry) {
            return this.find({
                leaseExpires: { lessThan: leaseExpiry }
            });
        },

        setFailed: function setFailed(leaseToken, workItems) {
            var self = this;
            if (!Array.isArray(workItems)) {
                workItems = Array.prototype.slice.call(arguments, 1);
            }
            var now = new Date();
            return Promise.all(_.map(workItems, function (workItem) {
                return self.update({
                    id: workItem.id,
                    leaseToken: leaseToken || workItem.leaseToken
                }, {
                    nextScheduled: new Date(now.valueOf() + workItem.pollInterval),
                    failureCount: workItem.failureCount + 1,
                    lastFinished: now,
                    leaseToken: null,
                    leaseExpires: null
                });
            })).then(function (workItems) {
                return _.flatten(workItems);
            });
        },

        setSucceeded: function setSucceeded(leaseToken, workItems) {
            var self = this;
            if (!Array.isArray(workItems)) {
                workItems = Array.prototype.slice.call(arguments, 1);
            }
            var now = new Date();
            return Promise.all(_.map(workItems, function (workItem) {
                return self.update({
                    id: workItem.id,
                    leaseToken: leaseToken || workItem.leaseToken
                }, {
                    nextScheduled: new Date(now.valueOf() + workItem.pollInterval),
                    failureCount: 0,
                    lastFinished: now,
                    leaseToken: null,
                    leaseExpires: null
                });
            })).then(function (workItems) {
                return _.flatten(workItems);
            });
        },

        createIpmiPollers: function createIpmiPollers(nodeId) {
            return this.create([{
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'sdr'
                },
                pollInterval: 60 * 1000
            }, {
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'power'
                },
                pollInterval: 60 * 1000
            }, {
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'sel'
                },
                pollInterval: 60 * 1000
            }]);
        },

        findPollers: function findPollers(criteria) {
            return this.find({
                where: {
                    $and: [
                        criteria,
                        byPollers()
                    ]
                }
            });
        }
    });
}
<commit_msg>MAG-55 Implement the UID LED state monitor in Rinjin<commit_after>// Copyright (c) 2015, EMC Corporation

'use strict';

module.exports = WorkItemModelFactory;

WorkItemModelFactory.$provide = 'Models.WorkItem';
WorkItemModelFactory.$inject = [
    'Model',
    '_',
    'Promise',
    'Assert',
    'Constants'
];

function WorkItemModelFactory (Model, _, Promise, assert, Constants) {

    function byPollers() {
        return {
            or: _.map(Constants.WorkItems.Pollers, function (pollerName) {
                return { name: pollerName };
            })
        };
    }
    return Model.extend({
        connection: 'mongo',
        identity: 'workitems',
        attributes: {
            name: {
                type: 'string',
                required: true
            },
            node: {
                model: 'nodes',
                defaultsTo: null
            },
            config: {
                type: 'json',
                defaultsTo: {}
            },
            pollInterval: {
                type: 'integer',
                required: true
            },
            nextScheduled: {
                type: 'datetime',
                defaultsTo: null
            },
            lastStarted: {
                type: 'datetime',
                defaultsTo: null
            },
            lastFinished: {
                type: 'datetime',
                defaultsTo: null
            },
            leaseToken: {
                type: 'string',
                uuidv4: true,
                defaultsTo: null
            },
            leaseExpires: {
                type: 'datetime',
                defaultsTo: null
            },
            failureCount: {
                type: 'integer',
                defaultsTo: 0,
                required: true
            },
            paused:{
                type: 'boolean',
                defaultsTo: false
            }
        },

        startNextScheduled: function startNextScheduled(leaseToken, criteria, leaseDuration) {
            var self = this;
            var now = new Date();
            return this.findOne({
                where: {
                    $and: [
                        criteria,
                        { paused: false },
                        {
                            leaseToken: null,
                            $or: [
                                { nextScheduled: { lessThan: now } },
                                { nextScheduled: null }
                            ]
                        }
                    ]
                },
                sort: 'nextScheduled ASC'
            }).then(function (workItem) {
                if (!workItem) {
                    return;
                }
                return self.update({
                    id: workItem.id,
                    leaseToken: null,
                    $or: [
                        { nextScheduled: { lessThan: now } },
                        { nextScheduled: null }
                    ]
                },
                {
                    lastStarted: now,
                    leaseToken: leaseToken,
                    leaseExpires: new Date(now.valueOf() + leaseDuration)
                }).then(function (workItems) {
                    /* some other worker acquired the lease, so we request another
                     * work item to process. */
                    if (!workItems.length || workItems[0].leaseToken !== leaseToken) {
                        return self.startNextScheduled(leaseToken, criteria, leaseDuration);
                    }
                    return workItems[0];
                });
            });
        },

        findExpired: function findExpired(leaseExpiry) {
            return this.find({
                leaseExpires: { lessThan: leaseExpiry }
            });
        },

        setFailed: function setFailed(leaseToken, workItems) {
            var self = this;
            if (!Array.isArray(workItems)) {
                workItems = Array.prototype.slice.call(arguments, 1);
            }
            var now = new Date();
            return Promise.all(_.map(workItems, function (workItem) {
                return self.update({
                    id: workItem.id,
                    leaseToken: leaseToken || workItem.leaseToken
                }, {
                    nextScheduled: new Date(now.valueOf() + workItem.pollInterval),
                    failureCount: workItem.failureCount + 1,
                    lastFinished: now,
                    leaseToken: null,
                    leaseExpires: null
                });
            })).then(function (workItems) {
                return _.flatten(workItems);
            });
        },

        setSucceeded: function setSucceeded(leaseToken, workItems) {
            var self = this;
            if (!Array.isArray(workItems)) {
                workItems = Array.prototype.slice.call(arguments, 1);
            }
            var now = new Date();
            return Promise.all(_.map(workItems, function (workItem) {
                return self.update({
                    id: workItem.id,
                    leaseToken: leaseToken || workItem.leaseToken
                }, {
                    nextScheduled: new Date(now.valueOf() + workItem.pollInterval),
                    failureCount: 0,
                    lastFinished: now,
                    leaseToken: null,
                    leaseExpires: null
                });
            })).then(function (workItems) {
                return _.flatten(workItems);
            });
        },

        createIpmiPollers: function createIpmiPollers(nodeId) {
            return this.create([{
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'sdr'
                },
                pollInterval: 60 * 1000
            }, {
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'power'
                },
                pollInterval: 60 * 1000
            }, {
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'sel'
                },
                pollInterval: 60 * 1000
            }, {
                name: Constants.WorkItems.Pollers.IPMI,
                node: nodeId,
                config: {
                    command: 'uid'
                },
                pollInterval: 60 * 1000
            }]);
        },

        findPollers: function findPollers(criteria) {
            return this.find({
                where: {
                    $and: [
                        criteria,
                        byPollers()
                    ]
                }
            });
        }
    });
}
<|endoftext|>


<commit_before>/* jshint shadow:true */
var Utils   = require("../utils");
var wordnet = require("../wordnet");
var replace = require("async-replace");
var _       = require("underscore");
var debug   = require("debug")("RegexReply");
var dWarn   = require("debug")("RegexReply:Warning");

// Prepares a trigger for the regular expression engine.

var emoList = ['~yes','~no','~emosad','~emohappy','~emosurprise','~emogoodbye','~emolaugh','~emothanks','~emogoodbye','~emobored','~emomisunderstand','~emomutual','~emopain','~emoskeptic','~emoignorance','~emodisgust','~emoprotest','~emoapology','~emomaybe','~emohello','~emohowzit'];

var processAlternates = function(reply) {
  // input Alternates.
  var match  = reply.match(/\((.+?)\)/g);

  if (match) {
    for(var i = 0; i < match.length;i++) {
      var altGroup = match[i];
      var altMatch  = altGroup.match(/\((.+?)\)/);
      var matchString = altMatch[0];
      var altStr = altMatch[1];
      var parts = altStr.split("|");

      var opts  = [];
      for (var n = 0; n < parts.length; n++) {
        opts.push(parts[n].trim());
      }

      opts = "(\\b" + opts.join("\\b|\\b") + "\\b)\\s?";
      reply = reply.replace(altGroup, opts);
    }
  }

  return reply;
};

exports.parse = function (regexp, facts, callback) {
  regexp = processAlternates(regexp);

  // If the trigger is simply '*' then the * needs to become (.*?)
  // to match the blank string too.
  regexp = regexp.replace(/^\*$/, "<zerowidthstar>");

  // Simple replacements.
  // This replacement must be done before the next or they will conflict.
  // * replacement is now optional by default meaning 0,n
  // Match Single * allowing *~n and *n to pass though
  // regexp = regexp.replace(/\*(?!~?\d)/g, "(.+?)");  // Convert * into (.+?)
  regexp = regexp.replace(/\s?\*(?!~?\d)\s?/g, "(?:.*\\s?)");  // Convert * into (.*)

  // Step 1 nWidthStar
  // (\s?(?:[\w]*\s?){n})
  // Here we match *n where n is the number of words to allow
  // This provides much more flexibility around matching adverbs with nouns.
  // We deliberately slurp in the trailing space to support zero or more words
  function replacer1(match, p1, offset, string){
    if (p1) debug("WIDTH STAR", p1, match);
    return "<"+parseInt(p1)+"ewidthstar>";
  }

  // Step 2 nWidthStar
  // (\s?(?:[\w]*\s?){0,n})
  function replacer2(match, p1, offset, string){
    if (p1) debug("WIDTH STAR", p1, match);
    var num = parseInt(p1.replace("~",""));
    return "<"+num+"vwidthstar>";
  }

  regexp = regexp.replace(/\*([0-9]{2})/g, replacer1);  // Convert *n into multi word EXACT match
  regexp = regexp.replace(/\*(~[0-9]{2}\s?)/g, replacer2);  // Convert *~n into multi word VARIABLE match
  regexp = regexp.replace(/<zerowidthstar>/g, "(?:.*?)");

  // Handle WordNet
  function wordnetReplace(match, sym, word, p3, offset, done) {
    // Use FactSystem first.

    facts.conceptToList(word.toLowerCase(), function(err, words){
      if (!_.isEmpty(words)) {
        words = "(\\b" + words.join("\\b|\\b") + "\\b)";
        // words = "(" + words.join("|") + ")";
        debug("Fact Replies", words);
        done(null, words);

      } else {
        wordnet.lookup(word, sym, function(err, words){
          // TODO add a space around the terms
          words = words.map(function(item) {
            return item.replace(/_/g, " ");
          });

          if (_.isEmpty(words)) {
            dWarn("Creating a trigger with a word NOT EXPANDED", match);
            done(null, match);
          } else {

            words = "(\\b" + words.join("\\b|\\b") + "\\b)";
            debug("Wordnet Replies", words);
            done(null, words);
          }
        });
      }
    });
  }

  replace(regexp, /(~)(\w[\w]+)/g, wordnetReplace, function(err, result) {
    regexp = result;

      // Optionals.
      var match  = regexp.match(/\[(.+?)\]/);
      var giveup = 0;
      while (match) {
        giveup++;
        if (giveup >= 50) {
          dWarn("Infinite loop when trying to process optionals in trigger!");
          return "";
        }

        var parts = match[1].split("|");
        var opts  = [];
        for (var i = 0, iend = parts.length; i < iend; i++) {
          var p = "\\s*" + parts[i] + "\\s*";
          opts.push(p);
        }

        opts.push("\\s*");

        // If this optional had a star or anything in it, make it non-matching.
        var pipes = opts.join("|");
        pipes = pipes.replace(new RegExp(Utils.quotemeta("(.+?)"), "g"),        "(?:.+?)");
        pipes = pipes.replace(new RegExp(Utils.quotemeta("(\\d+?)"), "g"),      "(?:\\d+?)");
        pipes = pipes.replace(new RegExp(Utils.quotemeta("([A-Za-z]+?)"), "g"), "(?:[A-Za-z]+?)");

        regexp = regexp.replace(new RegExp("\\s*\\[" + Utils.quotemeta(match[1]) + "\\]\\s*"),
          "(?:" + pipes + ")");
        match  = regexp.match(/\[(.+?)\]/); // Circle of life!
      }

      // neWidthStar
      function exactWidthReplace(match, p1, offset, string){
        return "(\\S+(:?\\s+\\S+){"+(parseInt(p1) - 1)+"})";
      }
      regexp = regexp.replace(/<(\d+)ewidthstar>/g, exactWidthReplace);

      // nvWidthStar
      function varWidthReplace(match, p1, offset, string){
        return "(\\s?(?:[\\w-]*\\s?){0,"+parseInt(p1)+"})";
      }
      regexp = regexp.replace(/<(\d+)vwidthstar>/g, varWidthReplace);
      callback(regexp);
  });
};

// This function can be done after the first and contains the
// user object so it may be contextual to this user.
exports.postParse = function(regexp, message, user, callback) {

  if (_.isNull(regexp)) {
    callback(null);
  } else {
    if (regexp.indexOf("<name") > -1 && message.names.length !== 0) {
      // TODO - Scan ahead to detect the highest
      for (var i = 0 ; i < message.names.length; i++) {
        regexp = regexp.replace(new RegExp("<name"+(i+1)+">","g"), "(" + message.names[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<name>","g"), "(" + message.names[0] +")") ;
      regexp = regexp.replace(new RegExp("<names>","g"), "(" + message.names.join("|") + ")");
    }

    if (regexp.indexOf("<noun") > -1 && message.nouns.length !== 0) {
      for (var i = 0 ; i < message.nouns.length; i++) {
        regexp = regexp.replace(new RegExp("<noun"+(i+1)+">","g"), "(" + message.nouns[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<noun>","g"), "(" + message.nouns[0] +")") ;
      regexp = regexp.replace(new RegExp("<nouns>","g"), "(" + message.nouns.join("|") + ")");
    }

    if (regexp.indexOf("<adverb") > -1 && message.adverbs.length !== 0) {
      for (var i = 0 ; i < message.adverbs.length; i++) {
        regexp = regexp.replace(new RegExp("<adverb"+(i+1)+">","g"), "(" + message.adverbs[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<adverb>","g"), "(" + message.adverbs[0] +")") ;
      regexp = regexp.replace(new RegExp("<adverbs>","g"), "(" + message.adverbs.join("|") + ")");
    }

    if (regexp.indexOf("<verb") > -1 && message.verbs.length !== 0) {
      for (var i = 0 ; i < message.verbs.length; i++) {
        regexp = regexp.replace(new RegExp("<verb"+(i+1)+">","g"), "(" + message.verbs[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<verb>","g"), "(" + message.verbs[0] +")") ;
      regexp = regexp.replace(new RegExp("<verbs>","g"), "(" + message.verbs.join("|") + ")");
    }

    if (regexp.indexOf("<pronoun") > -1 && message.pronouns.length !== 0) {
      for (var i = 0 ; i < message.pronouns.length; i++) {
        regexp = regexp.replace(new RegExp("<pronoun"+(i+1)+">","g"), "(" + message.pronouns[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<pronoun>","g"), "(" + message.pronouns[0] +")") ;
      regexp = regexp.replace(new RegExp("<pronouns>","g"), "(" + message.pronouns.join("|") + ")");
    }

    if (regexp.indexOf("<adjective") > -1 && message.adjectives.length !== 0) {
      for (var i = 0 ; i < message.adjectives.length; i++) {
        regexp = regexp.replace(new RegExp("<adjective"+(i+1)+">","g"), "(" + message.adjectives[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<adjective>","g"), "(" + message.adjectives[0] +")") ;
      regexp = regexp.replace(new RegExp("<adjectives>","g"), "(" + message.adjectives.join("|") + ")");
    }

    if (regexp.indexOf("<input") > -1 || regexp.indexOf("<reply") > -1) {
      // Filter in <input> and <reply> tags.
      debug("Input, Reply Match Found");
      var types = ["input", "reply"];
      for (var i = 0; i < 2; i++) {
        var type = types[i];
        // Numbered Replies/Inputs 1 - 9
        for (var j = 1; j <= 9; j++) {
          if (regexp.indexOf("<" + type + j + ">") && user.__history__[type][j]) {
            regexp = regexp.replace(new RegExp("<" + type + j + ">","g"), user.__history__[type][j].raw);
          }
        }

        // Generic Reply/Input (first one)
        if (user.__history__[type][0]) {
          regexp = regexp.replace(new RegExp("<" + type + ">","g"), user.__history__[type][0].raw);
        }
      }
    }
  }

  callback(regexp);
};
<commit_msg>update regex for fit with the correct behaviour<commit_after>/* jshint shadow:true */
var Utils   = require("../utils");
var wordnet = require("../wordnet");
var replace = require("async-replace");
var _       = require("underscore");
var debug   = require("debug")("RegexReply");
var dWarn   = require("debug")("RegexReply:Warning");

// Prepares a trigger for the regular expression engine.

var emoList = ['~yes','~no','~emosad','~emohappy','~emosurprise','~emogoodbye','~emolaugh','~emothanks','~emogoodbye','~emobored','~emomisunderstand','~emomutual','~emopain','~emoskeptic','~emoignorance','~emodisgust','~emoprotest','~emoapology','~emomaybe','~emohello','~emohowzit'];

var processAlternates = function(reply) {
  // input Alternates.
  var match  = reply.match(/\((.+?)\)/g);

  if (match) {
    for(var i = 0; i < match.length;i++) {
      var altGroup = match[i];
      var altMatch  = altGroup.match(/\((.+?)\)/);
      var matchString = altMatch[0];
      var altStr = altMatch[1];
      var parts = altStr.split("|");

      var opts  = [];
      for (var n = 0; n < parts.length; n++) {
        opts.push(parts[n].trim());
      }

      opts = "(\\b" + opts.join("\\b|\\b") + "\\b)\\s?";
      reply = reply.replace(altGroup, opts);
    }
  }

  return reply;
};

exports.parse = function (regexp, facts, callback) {
  regexp = processAlternates(regexp);

  // If the trigger is simply '*' then the * needs to become (.*?)
  // to match the blank string too.
  regexp = regexp.replace(/^\*$/, "<zerowidthstar>");

  // Simple replacements.
  // This replacement must be done before the next or they will conflict.
  // * replacement is now optional by default meaning 0,n
  // Match Single * allowing *~n and *n to pass though
  // regexp = regexp.replace(/\*(?!~?\d)/g, "(.+?)");  // Convert * into (.+?)
  regexp = regexp.replace(/\s?\*(?!~?\d)\s?/g, "(?:.*\\s?)");  // Convert * into (.*)

  // Step 1 nWidthStar
  // (\s?(?:[\w]*\s?){n})
  // Here we match *n where n is the number of words to allow
  // This provides much more flexibility around matching adverbs with nouns.
  // We deliberately slurp in the trailing space to support zero or more words
  function replacer1(match, p1, offset, string){
    if (p1) debug("WIDTH STAR", p1, match);
    return "<"+parseInt(p1)+"ewidthstar>";
  }

  // Step 2 nWidthStar
  // (\s?(?:[\w]*\s?){0,n})
  function replacer2(match, p1, offset, string){
    if (p1) debug("WIDTH STAR", p1, match);
    var num = parseInt(p1.replace("~",""));
    return "<"+num+"vwidthstar>";
  }

  regexp = regexp.replace(/\*([0-9]{1,2})/g, replacer1);  // Convert *n into multi word EXACT match
  regexp = regexp.replace(/\*(~[0-9]{1,2}\s?)/g, replacer2);  // Convert *~n into multi word VARIABLE match
  regexp = regexp.replace(/<zerowidthstar>/g, "(?:.*?)");

  // Handle WordNet
  function wordnetReplace(match, sym, word, p3, offset, done) {
    // Use FactSystem first.

    facts.conceptToList(word.toLowerCase(), function(err, words){
      if (!_.isEmpty(words)) {
        words = "(\\b" + words.join("\\b|\\b") + "\\b)";
        // words = "(" + words.join("|") + ")";
        debug("Fact Replies", words);
        done(null, words);

      } else {
        wordnet.lookup(word, sym, function(err, words){
          // TODO add a space around the terms
          words = words.map(function(item) {
            return item.replace(/_/g, " ");
          });

          if (_.isEmpty(words)) {
            dWarn("Creating a trigger with a word NOT EXPANDED", match);
            done(null, match);
          } else {

            words = "(\\b" + words.join("\\b|\\b") + "\\b)";
            debug("Wordnet Replies", words);
            done(null, words);
          }
        });
      }
    });
  }

  replace(regexp, /(~)(\w[\w]+)/g, wordnetReplace, function(err, result) {
    regexp = result;

      // Optionals.
      var match  = regexp.match(/\[(.+?)\]/);
      var giveup = 0;
      while (match) {
        giveup++;
        if (giveup >= 50) {
          dWarn("Infinite loop when trying to process optionals in trigger!");
          return "";
        }

        var parts = match[1].split("|");
        var opts  = [];
        for (var i = 0, iend = parts.length; i < iend; i++) {
          var p = "\\s*" + parts[i] + "\\s*";
          opts.push(p);
        }

        opts.push("\\s*");

        // If this optional had a star or anything in it, make it non-matching.
        var pipes = opts.join("|");
        pipes = pipes.replace(new RegExp(Utils.quotemeta("(.+?)"), "g"),        "(?:.+?)");
        pipes = pipes.replace(new RegExp(Utils.quotemeta("(\\d+?)"), "g"),      "(?:\\d+?)");
        pipes = pipes.replace(new RegExp(Utils.quotemeta("([A-Za-z]+?)"), "g"), "(?:[A-Za-z]+?)");

        regexp = regexp.replace(new RegExp("\\s*\\[" + Utils.quotemeta(match[1]) + "\\]\\s*"),
          "(?:" + pipes + ")");
        match  = regexp.match(/\[(.+?)\]/); // Circle of life!
      }

      // neWidthStar
      function exactWidthReplace(match, p1, offset, string){
        return "(\\S+(:?\\s+\\S+){"+(parseInt(p1) - 1)+"})";
      }
      regexp = regexp.replace(/<(\d+)ewidthstar>/g, exactWidthReplace);

      // nvWidthStar
      function varWidthReplace(match, p1, offset, string){
        return "(\\s?(?:[\\w-]*\\s?){0,"+parseInt(p1)+"})";
      }
      regexp = regexp.replace(/<(\d+)vwidthstar>/g, varWidthReplace);
      callback(regexp);
  });
};

// This function can be done after the first and contains the
// user object so it may be contextual to this user.
exports.postParse = function(regexp, message, user, callback) {

  if (_.isNull(regexp)) {
    callback(null);
  } else {
    if (regexp.indexOf("<name") > -1 && message.names.length !== 0) {
      // TODO - Scan ahead to detect the highest
      for (var i = 0 ; i < message.names.length; i++) {
        regexp = regexp.replace(new RegExp("<name"+(i+1)+">","g"), "(" + message.names[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<name>","g"), "(" + message.names[0] +")") ;
      regexp = regexp.replace(new RegExp("<names>","g"), "(" + message.names.join("|") + ")");
    }

    if (regexp.indexOf("<noun") > -1 && message.nouns.length !== 0) {
      for (var i = 0 ; i < message.nouns.length; i++) {
        regexp = regexp.replace(new RegExp("<noun"+(i+1)+">","g"), "(" + message.nouns[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<noun>","g"), "(" + message.nouns[0] +")") ;
      regexp = regexp.replace(new RegExp("<nouns>","g"), "(" + message.nouns.join("|") + ")");
    }

    if (regexp.indexOf("<adverb") > -1 && message.adverbs.length !== 0) {
      for (var i = 0 ; i < message.adverbs.length; i++) {
        regexp = regexp.replace(new RegExp("<adverb"+(i+1)+">","g"), "(" + message.adverbs[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<adverb>","g"), "(" + message.adverbs[0] +")") ;
      regexp = regexp.replace(new RegExp("<adverbs>","g"), "(" + message.adverbs.join("|") + ")");
    }

    if (regexp.indexOf("<verb") > -1 && message.verbs.length !== 0) {
      for (var i = 0 ; i < message.verbs.length; i++) {
        regexp = regexp.replace(new RegExp("<verb"+(i+1)+">","g"), "(" + message.verbs[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<verb>","g"), "(" + message.verbs[0] +")") ;
      regexp = regexp.replace(new RegExp("<verbs>","g"), "(" + message.verbs.join("|") + ")");
    }

    if (regexp.indexOf("<pronoun") > -1 && message.pronouns.length !== 0) {
      for (var i = 0 ; i < message.pronouns.length; i++) {
        regexp = regexp.replace(new RegExp("<pronoun"+(i+1)+">","g"), "(" + message.pronouns[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<pronoun>","g"), "(" + message.pronouns[0] +")") ;
      regexp = regexp.replace(new RegExp("<pronouns>","g"), "(" + message.pronouns.join("|") + ")");
    }

    if (regexp.indexOf("<adjective") > -1 && message.adjectives.length !== 0) {
      for (var i = 0 ; i < message.adjectives.length; i++) {
        regexp = regexp.replace(new RegExp("<adjective"+(i+1)+">","g"), "(" + message.adjectives[i] +")") ;
      }
      regexp = regexp.replace(new RegExp("<adjective>","g"), "(" + message.adjectives[0] +")") ;
      regexp = regexp.replace(new RegExp("<adjectives>","g"), "(" + message.adjectives.join("|") + ")");
    }

    if (regexp.indexOf("<input") > -1 || regexp.indexOf("<reply") > -1) {
      // Filter in <input> and <reply> tags.
      debug("Input, Reply Match Found");
      var types = ["input", "reply"];
      for (var i = 0; i < 2; i++) {
        var type = types[i];
        // Numbered Replies/Inputs 1 - 9
        for (var j = 1; j <= 9; j++) {
          if (regexp.indexOf("<" + type + j + ">") && user.__history__[type][j]) {
            regexp = regexp.replace(new RegExp("<" + type + j + ">","g"), user.__history__[type][j].raw);
          }
        }

        // Generic Reply/Input (first one)
        if (user.__history__[type][0]) {
          regexp = regexp.replace(new RegExp("<" + type + ">","g"), user.__history__[type][0].raw);
        }
      }
    }
  }

  callback(regexp);
};
<|endoftext|>


<commit_before>var Vec3 = require('vec3').Vec3;
var _ = require('lodash');
var Entity = require('prismarine-entity');
var conv = require('../conversions');
var NAMED_ENTITY_HEIGHT = 1.62;
var CROUCH_HEIGHT = NAMED_ENTITY_HEIGHT - 0.08;
var entitiesData = require('../minecraft-data').entities;
var version=require("../version");
var Item = require("prismarine-item")(version);

module.exports = inject;

var animationEvents = {
  0: 'entitySwingArm',
  1: 'entityHurt',
  2: 'entityWake',
  3: 'entityEat',
  4: 'entityCriticalEffect',
  5: 'entityMagicCriticalEffect'
};

var entityStatusEvents = {
  2: 'entityHurt',
  3: 'entityDead',
  6: 'entityTaming',
  7: 'entityTamed',
  8: 'entityShakingOffWater',
  10: 'entityEatingGrass',
};

function inject(bot) {

  bot.findPlayer = bot.findPlayers = function(filter) {
    var filterFn = function(entity) {
      if(entity.type !== 'player') return false;
      if(filter == null) return true;
      if(typeof filter === 'object' && filter instanceof RegExp) {
        return entity.username.search(filter) !== -1;
      } else if(typeof filter === 'function') {
        return filter(entity);
      } else if(typeof filter === 'string') {
        return entity.username.toLowerCase() === filter.toLowerCase();
      }
      return false;
    };
    var resultSet = _.transform(bot.entities, function(players, entity) {
      if(filterFn(entity)) players.push(entity);
    }, []);

    if(typeof filter === 'string') {
      switch(resultSet.length) {
        case 0:
          return null;
        case 1:
          return resultSet[0];
        default:
          return resultSet;
      }
    }
    return resultSet;
  };

  bot.players = {};
  bot.uuidToUsername = {};
  bot.entities = {};

  bot._client.once('login', function(packet) {
    // login
    bot.entity = fetchEntity(packet.entityId);
    bot.entity.username = bot.username;
    bot.entity.type = 'player';
  });

  bot._client.on('entity_equipment', function(packet) {
    // entity equipment
    var entity = fetchEntity(packet.entityId);
    entity.setEquipment(packet.slot, packet.item ? Item.fromNotch(packet.item) : null);
    bot.emit('entityEquip', entity);
  });

  bot._client.on('bed', function(packet) {
    // use bed
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.location.x, packet.location.y, packet.location.z);
    bot.emit('entitySleep', entity);
  });

  bot._client.on('animation', function(packet) {
    // animation
    var entity = fetchEntity(packet.entityId);
    var eventName = animationEvents[packet.animation];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('named_entity_spawn', function(packet) {
    // in case player_info packet was not sent before named_entity_spawn : ignore named_entity_spawn (see #213)
    if(packet.playerUUID in bot.uuidToUsername) {
      // spawn named entity
      var entity = fetchEntity(packet.entityId);
      entity.type = 'player';
      entity.username = bot.uuidToUsername[packet.playerUUID];
      entity.uuid = packet.playerUUID;
      entity.dataBlobs = packet.data;
      entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
      entity.yaw = conv.fromNotchianYawByte(packet.yaw);
      entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
      entity.height = NAMED_ENTITY_HEIGHT;
      entity.metadata = parseMetadata(packet.metadata);
      if(!bot.players[entity.username].entity) {
        bot.players[entity.username].entity = entity;
      }
      bot.emit('entitySpawn', entity);
    }
  });
  bot.on("entityCrouch", function(entity) {
    entity.height = CROUCH_HEIGHT;
  });
  bot.on("entityUncrouch", function(entity) {
    entity.height = NAMED_ENTITY_HEIGHT;
  });

  bot._client.on('collect', function(packet) {
    // collect item
    var collector = fetchEntity(packet.collectorEntityId);
    var collected = fetchEntity(packet.collectedEntityId);
    bot.emit('playerCollect', collector, collected);
  });

  bot._client.on('spawn_entity', function(packet) {
    // spawn object/vehicle
    var entity = fetchEntity(packet.entityId);
    var entityData = entitiesData[packet.type];
    if(entityData) {
      entity.type = 'object';
      entity.objectType = entityData.displayName;
      entity.displayName = entityData.displayName;
      entity.entityType = entityData.id;
      entity.name = entityData.name;
      entity.kind = entityData.type;
    } else {
      // unknown entity
      entity.type = 'other';
      entity.entityType = packet.type;
    }
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.objectData = packet.objectData;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_experience_orb', function(packet) {
    var entity = fetchEntity(packet.entityId);
    entity.type = 'orb';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.count = packet.count;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_living', function(packet) {
    // spawn mob
    var entity = fetchEntity(packet.entityId);
    entity.type = 'mob';
    var entityData = entitiesData[packet.type];
    entity.mobType = entityData.displayName;
    entity.displayName = entityData.displayName;
    entity.entityType = entityData.id;
    entity.name = entityData.name;
    entity.kind = entityData.type;
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.headPitch = conv.fromNotchianPitchByte(packet.headPitch);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('entity_velocity', function(packet) {
    // entity velocity
    var entity = fetchEntity(packet.entityId);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
  });

  bot._client.on('entity_destroy', function(packet) {
    // destroy entity
    packet.entityIds.forEach(function(id) {
      var entity = fetchEntity(id);
      bot.emit('entityGone', entity);
      entity.isValid = false;
      if(entity.username && bot.players[entity.username]) {
        bot.players[entity.username].entity = null;
      }
      delete bot.entities[id];
    });
  });

  bot._client.on('rel_entity_move', function(packet) {
    // entity relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_look', function(packet) {
    // entity look
    var entity = fetchEntity(packet.entityId);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_move_look', function(packet) {
    // entity look and relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_teleport', function(packet) {
    // entity teleport
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_head_rotation', function(packet) {
    // entity head look
    var entity = fetchEntity(packet.entityId);
    entity.headYaw = conv.fromNotchianYawByte(packet.headYaw);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_status', function(packet) {
    // entity status
    var entity = fetchEntity(packet.entityId);
    var eventName = entityStatusEvents[packet.entityStatus];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('attach_entity', function(packet) {
    // attach entity
    var entity = fetchEntity(packet.entityId);
    if(packet.vehicleId === -1) {
      var vehicle = entity.vehicle;
      delete entity.vehicle;
      bot.emit('entityDetach', entity, vehicle);
    } else {
      entity.vehicle = fetchEntity(packet.vehicleId);
      bot.emit('entityAttach', entity, entity.vehicle);
    }
  });

  bot._client.on('entity_metadata', function(packet) {
    // entity metadata
    var entity = fetchEntity(packet.entityId);
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entityUpdate', entity);
  });

  bot._client.on('entity_effect', function(packet) {
    // entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = {
      id: packet.effectId,
      amplifier: packet.amplifier,
      duration: packet.duration,
    };
    entity.effects[effect.id] = effect;
    bot.emit('entityEffect', entity, effect);
  });

  bot._client.on('remove_entity_effect', function(packet) {
    // remove entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = entity.effects[packet.effectId];
    if(effect) {
      delete entity.effects[effect.id];
    }
    else {
      // unknown effect
      effect = {
        id: packet.effectId,
        amplifier: -1,
        duration: -1,
      };
    }
    bot.emit('entityEffectEnd', entity, effect);
  });

  bot._client.on('spawn_entity_weather', function(packet) {
    // spawn global entity
    var entity = fetchEntity(packet.entityId);
    entity.type = 'global';
    entity.globalType = 'thunderbolt';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    bot.emit('entitySpawn', entity);
  });

  bot.on('spawn', function() {
    bot.emit('entitySpawn', bot.entity);
  });
  bot._client.on('player_info', function(packet) {
    // player list item(s)
    packet.data.forEach(function(item) {
      var playerEntity = bot.findPlayers(item.name);
      var player = bot.uuidToUsername[item.UUID] ? bot.players[bot.uuidToUsername[item.UUID]] : null;
      if(packet.action === 0) {
        // New Player
        if(!player) {
          player = bot.players[item.name] = {username: item.name, ping: item.ping, uuid: item.UUID};
          bot.uuidToUsername[item.UUID] = item.name;
          bot.emit('playerJoined', player);
          // Just an Update
        } else player.ping = item.ping;

        player.entity = playerEntity;
      } else if(packet.action === 1) {
        // TODO: update gamemode
      } else if(packet.action === 2) {
        // TODO: update latency
      } else if(packet.action === 3) {
        // TODO: update display name
      } else if(packet.action === 4) {
        // Player has parted
        if(player) {
          player.entity = null;
          delete bot.players[player.username];
          delete bot.uuidToUsername[item.UUID];
          bot.emit('playerLeft', player);
        }
      }
    });
  });

  // attaching to a vehicle
  bot._client.on('attach_entity', function(packet) {
    if(packet.entityId !== bot.entity.id) return;
    var vehicle = bot.vehicle;
    if(packet.vehicleId === -1) {
      bot.vehicle = null;
      bot.emit("dismount", vehicle);
    } else {
      bot.vehicle = bot.entities[packet.vehicleId];
      bot.emit("mount");
    }
  });

  bot.attack = attack;
  bot.mount = mount;
  bot.dismount = dismount;
  bot.useOn = useOn;
  bot.moveVehicle = moveVehicle;

  function useOn(target) {
    // TODO: check if not crouching will make make this action always use the item
    useEntity(target, 0);
  }

  function attack(target, swing) {
    if(swing) {
      bot._client.write('arm_animation', {});
    }
    useEntity(target, 1);
  }

  function mount(target) {
    // TODO: check if crouching will make make this action always mount
    useEntity(target, 0);
  }

  function moveVehicle(left, forward) {
    bot._client.write('steer_vehicle', {
      "sideways": left,
      "forward": forward,
      "jump": 0x01
    });
  }

  function dismount() {
    if(bot.vehicle) {
      bot._client.write('steer_vehicle', {
        "sideways": 0.0,
        "forward": 0.0,
        "jump": 0x02
      });
    } else {
      bot.emit("error", new Error("dismount: not mounted"));
    }
  }

  function useEntity(target, leftClick, x, y, z) {
    if(x && y && z) {
      bot._client.write('use_entity', {
        target: target.id,
        mouse: leftClick,
        x: x,
        y: y,
        z: z
      });
    }
    else bot._client.write('use_entity', {
      target: target.id,
      mouse: leftClick
    });
  }

  function fetchEntity(id) {
    return bot.entities[id] || (bot.entities[id] = new Entity(id));
  }
}

function parseMetadata(metadata) {
  var o = {};
  metadata.forEach(function(pair) {
    o[pair.key] = pair.value;
  });
  return o;
}
<commit_msg>adapt to new minecraft data entities format<commit_after>var Vec3 = require('vec3').Vec3;
var _ = require('lodash');
var Entity = require('prismarine-entity');
var conv = require('../conversions');
var NAMED_ENTITY_HEIGHT = 1.62;
var CROUCH_HEIGHT = NAMED_ENTITY_HEIGHT - 0.08;
var objects = require('../minecraft-data').objects;
var mobs = require('../minecraft-data').mobs;
var version=require("../version");
var Item = require("prismarine-item")(version);

module.exports = inject;

var animationEvents = {
  0: 'entitySwingArm',
  1: 'entityHurt',
  2: 'entityWake',
  3: 'entityEat',
  4: 'entityCriticalEffect',
  5: 'entityMagicCriticalEffect'
};

var entityStatusEvents = {
  2: 'entityHurt',
  3: 'entityDead',
  6: 'entityTaming',
  7: 'entityTamed',
  8: 'entityShakingOffWater',
  10: 'entityEatingGrass',
};

function inject(bot) {

  bot.findPlayer = bot.findPlayers = function(filter) {
    var filterFn = function(entity) {
      if(entity.type !== 'player') return false;
      if(filter == null) return true;
      if(typeof filter === 'object' && filter instanceof RegExp) {
        return entity.username.search(filter) !== -1;
      } else if(typeof filter === 'function') {
        return filter(entity);
      } else if(typeof filter === 'string') {
        return entity.username.toLowerCase() === filter.toLowerCase();
      }
      return false;
    };
    var resultSet = _.transform(bot.entities, function(players, entity) {
      if(filterFn(entity)) players.push(entity);
    }, []);

    if(typeof filter === 'string') {
      switch(resultSet.length) {
        case 0:
          return null;
        case 1:
          return resultSet[0];
        default:
          return resultSet;
      }
    }
    return resultSet;
  };

  bot.players = {};
  bot.uuidToUsername = {};
  bot.entities = {};

  bot._client.once('login', function(packet) {
    // login
    bot.entity = fetchEntity(packet.entityId);
    bot.entity.username = bot.username;
    bot.entity.type = 'player';
  });

  bot._client.on('entity_equipment', function(packet) {
    // entity equipment
    var entity = fetchEntity(packet.entityId);
    entity.setEquipment(packet.slot, packet.item ? Item.fromNotch(packet.item) : null);
    bot.emit('entityEquip', entity);
  });

  bot._client.on('bed', function(packet) {
    // use bed
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.location.x, packet.location.y, packet.location.z);
    bot.emit('entitySleep', entity);
  });

  bot._client.on('animation', function(packet) {
    // animation
    var entity = fetchEntity(packet.entityId);
    var eventName = animationEvents[packet.animation];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('named_entity_spawn', function(packet) {
    // in case player_info packet was not sent before named_entity_spawn : ignore named_entity_spawn (see #213)
    if(packet.playerUUID in bot.uuidToUsername) {
      // spawn named entity
      var entity = fetchEntity(packet.entityId);
      entity.type = 'player';
      entity.username = bot.uuidToUsername[packet.playerUUID];
      entity.uuid = packet.playerUUID;
      entity.dataBlobs = packet.data;
      entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
      entity.yaw = conv.fromNotchianYawByte(packet.yaw);
      entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
      entity.height = NAMED_ENTITY_HEIGHT;
      entity.metadata = parseMetadata(packet.metadata);
      if(!bot.players[entity.username].entity) {
        bot.players[entity.username].entity = entity;
      }
      bot.emit('entitySpawn', entity);
    }
  });
  bot.on("entityCrouch", function(entity) {
    entity.height = CROUCH_HEIGHT;
  });
  bot.on("entityUncrouch", function(entity) {
    entity.height = NAMED_ENTITY_HEIGHT;
  });

  bot._client.on('collect', function(packet) {
    // collect item
    var collector = fetchEntity(packet.collectorEntityId);
    var collected = fetchEntity(packet.collectedEntityId);
    bot.emit('playerCollect', collector, collected);
  });

  bot._client.on('spawn_entity', function(packet) {
    // spawn object/vehicle
    var entity = fetchEntity(packet.entityId);
    var entityData = objects[packet.type];
    if(entityData) {
      entity.type = 'object';
      entity.objectType = entityData.displayName;
      entity.displayName = entityData.displayName;
      entity.entityType = entityData.id;
      entity.name = entityData.name;
      entity.kind = entityData.category;
    } else {
      // unknown entity
      entity.type = 'other';
      entity.entityType = packet.type;
    }
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.objectData = packet.objectData;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_experience_orb', function(packet) {
    var entity = fetchEntity(packet.entityId);
    entity.type = 'orb';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.count = packet.count;
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('spawn_entity_living', function(packet) {
    // spawn mob
    var entity = fetchEntity(packet.entityId);
    entity.type = 'mob';
    var entityData = mobs[packet.type];
    entity.mobType = entityData.displayName;
    entity.displayName = entityData.displayName;
    entity.entityType = entityData.id;
    entity.name = entityData.name;
    entity.kind = entityData.category;
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    entity.headPitch = conv.fromNotchianPitchByte(packet.headPitch);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entitySpawn', entity);
  });

  bot._client.on('entity_velocity', function(packet) {
    // entity velocity
    var entity = fetchEntity(packet.entityId);
    var notchVel = new Vec3(packet.velocityX, packet.velocityY, packet.velocityZ);
    entity.velocity.update(conv.fromNotchVelocity(notchVel));
  });

  bot._client.on('entity_destroy', function(packet) {
    // destroy entity
    packet.entityIds.forEach(function(id) {
      var entity = fetchEntity(id);
      bot.emit('entityGone', entity);
      entity.isValid = false;
      if(entity.username && bot.players[entity.username]) {
        bot.players[entity.username].entity = null;
      }
      delete bot.entities[id];
    });
  });

  bot._client.on('rel_entity_move', function(packet) {
    // entity relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_look', function(packet) {
    // entity look
    var entity = fetchEntity(packet.entityId);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_move_look', function(packet) {
    // entity look and relative move
    var entity = fetchEntity(packet.entityId);
    entity.position.translate(packet.dX / 32, packet.dY / 32, packet.dZ / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_teleport', function(packet) {
    // entity teleport
    var entity = fetchEntity(packet.entityId);
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    entity.yaw = conv.fromNotchianYawByte(packet.yaw);
    entity.pitch = conv.fromNotchianPitchByte(packet.pitch);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_head_rotation', function(packet) {
    // entity head look
    var entity = fetchEntity(packet.entityId);
    entity.headYaw = conv.fromNotchianYawByte(packet.headYaw);
    bot.emit('entityMoved', entity);
  });

  bot._client.on('entity_status', function(packet) {
    // entity status
    var entity = fetchEntity(packet.entityId);
    var eventName = entityStatusEvents[packet.entityStatus];
    if(eventName) bot.emit(eventName, entity);
  });

  bot._client.on('attach_entity', function(packet) {
    // attach entity
    var entity = fetchEntity(packet.entityId);
    if(packet.vehicleId === -1) {
      var vehicle = entity.vehicle;
      delete entity.vehicle;
      bot.emit('entityDetach', entity, vehicle);
    } else {
      entity.vehicle = fetchEntity(packet.vehicleId);
      bot.emit('entityAttach', entity, entity.vehicle);
    }
  });

  bot._client.on('entity_metadata', function(packet) {
    // entity metadata
    var entity = fetchEntity(packet.entityId);
    entity.metadata = parseMetadata(packet.metadata);
    bot.emit('entityUpdate', entity);
  });

  bot._client.on('entity_effect', function(packet) {
    // entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = {
      id: packet.effectId,
      amplifier: packet.amplifier,
      duration: packet.duration,
    };
    entity.effects[effect.id] = effect;
    bot.emit('entityEffect', entity, effect);
  });

  bot._client.on('remove_entity_effect', function(packet) {
    // remove entity effect
    var entity = fetchEntity(packet.entityId);
    var effect = entity.effects[packet.effectId];
    if(effect) {
      delete entity.effects[effect.id];
    }
    else {
      // unknown effect
      effect = {
        id: packet.effectId,
        amplifier: -1,
        duration: -1,
      };
    }
    bot.emit('entityEffectEnd', entity, effect);
  });

  bot._client.on('spawn_entity_weather', function(packet) {
    // spawn global entity
    var entity = fetchEntity(packet.entityId);
    entity.type = 'global';
    entity.globalType = 'thunderbolt';
    entity.position.set(packet.x / 32, packet.y / 32, packet.z / 32);
    bot.emit('entitySpawn', entity);
  });

  bot.on('spawn', function() {
    bot.emit('entitySpawn', bot.entity);
  });
  bot._client.on('player_info', function(packet) {
    // player list item(s)
    packet.data.forEach(function(item) {
      var playerEntity = bot.findPlayers(item.name);
      var player = bot.uuidToUsername[item.UUID] ? bot.players[bot.uuidToUsername[item.UUID]] : null;
      if(packet.action === 0) {
        // New Player
        if(!player) {
          player = bot.players[item.name] = {username: item.name, ping: item.ping, uuid: item.UUID};
          bot.uuidToUsername[item.UUID] = item.name;
          bot.emit('playerJoined', player);
          // Just an Update
        } else player.ping = item.ping;

        player.entity = playerEntity;
      } else if(packet.action === 1) {
        // TODO: update gamemode
      } else if(packet.action === 2) {
        // TODO: update latency
      } else if(packet.action === 3) {
        // TODO: update display name
      } else if(packet.action === 4) {
        // Player has parted
        if(player) {
          player.entity = null;
          delete bot.players[player.username];
          delete bot.uuidToUsername[item.UUID];
          bot.emit('playerLeft', player);
        }
      }
    });
  });

  // attaching to a vehicle
  bot._client.on('attach_entity', function(packet) {
    if(packet.entityId !== bot.entity.id) return;
    var vehicle = bot.vehicle;
    if(packet.vehicleId === -1) {
      bot.vehicle = null;
      bot.emit("dismount", vehicle);
    } else {
      bot.vehicle = bot.entities[packet.vehicleId];
      bot.emit("mount");
    }
  });

  bot.attack = attack;
  bot.mount = mount;
  bot.dismount = dismount;
  bot.useOn = useOn;
  bot.moveVehicle = moveVehicle;

  function useOn(target) {
    // TODO: check if not crouching will make make this action always use the item
    useEntity(target, 0);
  }

  function attack(target, swing) {
    if(swing) {
      bot._client.write('arm_animation', {});
    }
    useEntity(target, 1);
  }

  function mount(target) {
    // TODO: check if crouching will make make this action always mount
    useEntity(target, 0);
  }

  function moveVehicle(left, forward) {
    bot._client.write('steer_vehicle', {
      "sideways": left,
      "forward": forward,
      "jump": 0x01
    });
  }

  function dismount() {
    if(bot.vehicle) {
      bot._client.write('steer_vehicle', {
        "sideways": 0.0,
        "forward": 0.0,
        "jump": 0x02
      });
    } else {
      bot.emit("error", new Error("dismount: not mounted"));
    }
  }

  function useEntity(target, leftClick, x, y, z) {
    if(x && y && z) {
      bot._client.write('use_entity', {
        target: target.id,
        mouse: leftClick,
        x: x,
        y: y,
        z: z
      });
    }
    else bot._client.write('use_entity', {
      target: target.id,
      mouse: leftClick
    });
  }

  function fetchEntity(id) {
    return bot.entities[id] || (bot.entities[id] = new Entity(id));
  }
}

function parseMetadata(metadata) {
  var o = {};
  metadata.forEach(function(pair) {
    o[pair.key] = pair.value;
  });
  return o;
}
<|endoftext|>