<commit_before>'use strict';

var fs = require('fs');
var typeOf = require('kind-of');
var isGlob = require('is-glob');
var relative = require('relative');
var defaults = require('defaults-deep');
var extend = require('extend-shallow');
var union = require('array-union');
var glob = require('lazy-globby');
var pick = require('object.pick');
var omit = require('object.omit');

var rootKeys = [
  'path',
  'content',
  'data',
  'locals',
  'orig',
  'options'
];

function Loader(opts) {
  opts = opts || {};
  opts.rootKeys = union(opts.rootKeys || [], rootKeys);
  this.options = opts;
  this.cache = {};
}

/**
 * Load templates.
 */

Loader.prototype.load = function(key/*, value, locals, options*/) {
  switch(typeOf(key)) {
    case 'string':
      return this.loadString.apply(this, arguments);
    case 'object':
      return this.loadObject.apply(this, arguments);
    case 'array':
      return this.loadArray.apply(this, arguments);
  }
};

Loader.prototype.loadString = function(key, value/*, locals, options*/) {
  var args = [].slice.call(arguments);
  var opts = this.options;

  if (isGlob(key) && typeof value === 'string') {
    throw new Error('load-templates#loadString: invalid second argument: ' + value);
  }

  var files = glob().sync(key, opts);

  if (files.length) {
    args.shift();
    files.forEach(function (fp) {
      if (typeof opts.cwd === 'string') {
        fp = path.join(opts.cwd, fp);
      }
      var file = this.normalize.apply(this, args);
      file.content = this.readFn(fp, opts);
      file.path = this.resolve((file.path || fp), opts);
      this.cache[this.renameKey(fp, opts)] = file;
    }.bind(this));

  } else {
    var fp = args.shift();
    if (typeof opts.cwd === 'string') {
      fp = path.join(opts.cwd, fp);
    }
    var file = this.normalize.apply(this, args);
    file.path = this.resolve((file.path || fp), opts);
    this.cache[this.renameKey(fp, opts)] = file;
  }
  return this.cache;
};

Loader.prototype.loadArray = function(key/*, value, locals, options*/) {
  var opts = extend({nonull: true}, this.options);
  var files = glob().sync(key, opts);
  if (!files.length && opts.strict) {
    throw new Error('Loader#loadArray cannot find glob pattern: ' + key);
  }

  var args = [].slice.call(arguments, 1);
  var rest = this.normalize.apply(this, args);
  files.forEach(function (fp) {
    if (typeof opts.cwd === 'string') {
      fp = path.join(opts.cwd, fp);
    }
    var content = this.readFn(fp, opts);
    var file = {};
    if (fp.slice(-5) === '.json') {
      file = content;
    } else {
      file.content = content;
    }
    file = defaults({}, file, rest);
    opts = extend({}, this.options, file.options);
    file.path = this.resolve((file.path || fp), opts);
    this.cache[this.renameKey(fp, opts)] = this.sift(file, 'locals');
  }.bind(this));
  return this.cache;
};

Loader.prototype.loadObject = function(template/*, value, locals, options*/) {
  var args = [].slice.call(arguments, 1);
  var rest = this.normalize.apply(this, args);

  // if it has a `path` property, then it doesn't have a key
  if (template.hasOwnProperty('path')) {
    this.cache[template.path] = this.normalize.apply(this, arguments);
  } else {

    for (var key in template) {
      if (template.hasOwnProperty(key)) {
        var file = defaults({}, template[key], rest);
        var opts = extend({}, this.options, file.options);
        if (typeof opts.cwd === 'string') {
          file.path = path.join(opts.cwd, file.path);
        }

        // normalize file.path
        file.path = this.resolve((file.path || key), opts);
        // cache the template and normalize the template key
        this.cache[this.renameKey(key, opts)] = this.sift(file, 'locals');
      }
    }
  }
  return this.cache;
};

Loader.prototype.normalize = function(value, locals, options) {
  var args = [].slice.call(arguments);
  if (args.length === 0) return {};
  var file = {};

  if (value && typeof value === 'object' && value.content || value.path) {
    file = args.shift();
    file = extend({content: '', path: ''}, file);
  } else if (typeof value === 'string') {
    file.content = args.shift();
  }

  locals = args.shift() || {};
  options = args.shift() || {};

  extendOmit('locals', options, locals);
  extendOmit('options', locals, options);

  extendOmit('locals', locals, locals);
  extendOmit('options', options, options);

  if (Object.keys(options).length) {
    file.options = extend({}, file.options, options);
  }
  if (Object.keys(locals).length) {
    file.locals = extend({}, file.locals, locals);
  }

  file = this.sift(file, 'locals');
  return file;
};

Loader.prototype.sift = function(val, key) {
  var file = pick(val, this.options.rootKeys);
  var keys = Object.keys(file);
  if (keys.length) {
    file[key] = file[key] || {};
    var prop = omit(val, keys);
    file[key] = extend({}, file[key], prop);
    if (!Object.keys(file[key]).length) {
      delete file[key];
    }
  }
  return file;
};

Loader.prototype.readFn = function(fp, options) {
  var opts = extend({}, this.options, options);
  try {
    if (typeof opts.readFn === 'function') {
      return opts.readFn(fp);
    }
    var str = fs.readFileSync(fp, 'utf8');
    if (fp.slice(-5) === '.json') {
      var file = JSON.parse(str);
      file.orig = str;
      return file;
    }
    return str;
  } catch(err) {}
  return fp;
};

Loader.prototype.renameKey = function(fp, options) {
  var opts = extend({}, this.options, options);
  if (typeof opts.renameKey === 'function') {
    return opts.renameKey(fp);
  }
  if (opts.relative === false) return fp;
  return relative(fp);
};

Loader.prototype.resolve = function(fp, options) {
  var opts = extend({}, this.options, options);
  if (typeof opts.resolve === 'function') {
    return opts.resolve(fp);
  }
  if (opts.relative === false) return fp;
  return relative(fp);
};

function extendOmit(prop, o, target) {
  if (typeof target !== 'object') target = o;
  if (o.hasOwnProperty(prop)) {
    extend(target, o[prop]);
    delete o[prop];
  }
}

module.exports = Loader;
<commit_msg>ensure options are passed<commit_after>'use strict';

var fs = require('fs');
var typeOf = require('kind-of');
var isGlob = require('is-glob');
var relative = require('relative');
var isObject = require('is-extendable');
var defaults = require('defaults-deep');
var extend = require('extend-shallow');
var union = require('array-union');
var glob = require('lazy-globby');
var pick = require('object.pick');
var omit = require('object.omit');

var rootKeys = [
  'path',
  'content',
  'data',
  'locals',
  'orig',
  'options'
];

function Loader(opts) {
  opts = opts || {};
  opts.rootKeys = union(opts.rootKeys || [], rootKeys);
  this.options = opts;
  this.cache = {};
}

/**
 * Load templates.
 */

Loader.prototype.load = function(key/*, value, locals, options*/) {
  switch(typeOf(key)) {
    case 'string':
      return this.loadString.apply(this, arguments);
    case 'object':
      return this.loadObject.apply(this, arguments);
    case 'array':
      return this.loadArray.apply(this, arguments);
  }
};

Loader.prototype.loadString = function(key, value/*, locals, options*/) {
  var args = [].slice.call(arguments);
  var len = args.length;
  var last = args[len - 1];
  var opts = this.options;

  if (isObject(last)) {
    opts = extend({}, this.options, last);
  }

  if (isGlob(key) && typeof value === 'string') {
    throw new Error('load-templates#loadString: invalid second argument: ' + value);
  }

  var files = glob().sync(key, opts);

  if (files.length) {
    args.shift();
    files.forEach(function (fp) {
      if (typeof opts.cwd === 'string') {
        fp = path.join(opts.cwd, fp);
      }
      var file = this.normalize.apply(this, args);
      file.content = this.readFn(fp, opts);
      file.path = this.resolve((file.path || fp), opts);
      this.cache[this.renameKey(fp, opts)] = file;
    }.bind(this));

  } else {
    var fp = args.shift();
    if (typeof opts.cwd === 'string') {
      fp = path.join(opts.cwd, fp);
    }
    var file = this.normalize.apply(this, args);
    file.path = this.resolve((file.path || fp), opts);
    this.cache[this.renameKey(fp, opts)] = file;
  }
  return this.cache;
};

Loader.prototype.loadArray = function(key/*, value, locals, options*/) {
  var opts = extend({nonull: true}, this.options);
  var files = glob().sync(key, opts);
  if (!files.length && opts.strict) {
    throw new Error('Loader#loadArray cannot find glob pattern: ' + key);
  }

  var args = [].slice.call(arguments, 1);
  var rest = this.normalize.apply(this, args);
  files.forEach(function (fp) {
    if (typeof opts.cwd === 'string') {
      fp = path.join(opts.cwd, fp);
    }
    var content = this.readFn(fp, opts);
    var file = {};
    if (fp.slice(-5) === '.json') {
      file = content;
    } else {
      file.content = content;
    }
    file = defaults({}, file, rest);
    opts = extend({}, this.options, file.options);
    file.path = this.resolve((file.path || fp), opts);
    this.cache[this.renameKey(fp, opts)] = this.sift(file, 'locals');
  }.bind(this));
  return this.cache;
};

Loader.prototype.loadObject = function(template/*, value, locals, options*/) {
  var args = [].slice.call(arguments, 1);
  var rest = this.normalize.apply(this, args);

  // if it has a `path` property, then it doesn't have a key
  if (template.hasOwnProperty('path')) {
    this.cache[template.path] = this.normalize.apply(this, arguments);
  } else {

    for (var key in template) {
      if (template.hasOwnProperty(key)) {
        var file = defaults({}, template[key], rest);
        var opts = extend({}, this.options, file.options);
        if (typeof opts.cwd === 'string') {
          file.path = path.join(opts.cwd, file.path);
        }

        // normalize file.path
        file.path = this.resolve((file.path || key), opts);
        // cache the template and normalize the template key
        this.cache[this.renameKey(key, opts)] = this.sift(file, 'locals');
      }
    }
  }
  return this.cache;
};

Loader.prototype.normalize = function(value, locals, options) {
  var args = [].slice.call(arguments);
  if (args.length === 0) return {};
  var file = {};

  if (value && typeof value === 'object' && value.content || value.path) {
    file = args.shift();
    file = extend({content: '', path: ''}, file);
  } else if (typeof value === 'string') {
    file.content = args.shift();
  }

  locals = args.shift() || {};
  options = args.shift() || {};

  extendOmit('locals', options, locals);
  extendOmit('options', locals, options);

  extendOmit('locals', locals, locals);
  extendOmit('options', options, options);

  if (Object.keys(options).length) {
    file.options = extend({}, file.options, options);
  }
  if (Object.keys(locals).length) {
    file.locals = extend({}, file.locals, locals);
  }

  file = this.sift(file, 'locals');
  return file;
};

Loader.prototype.sift = function(val, key) {
  var file = pick(val, this.options.rootKeys);
  var keys = Object.keys(file);
  if (keys.length) {
    file[key] = file[key] || {};
    var prop = omit(val, keys);
    file[key] = extend({}, file[key], prop);
    if (!Object.keys(file[key]).length) {
      delete file[key];
    }
  }
  return file;
};

Loader.prototype.readFn = function(fp, options) {
  var opts = extend({}, this.options, options);
  try {
    if (typeof opts.readFn === 'function') {
      return opts.readFn(fp);
    }
    var str = fs.readFileSync(fp, 'utf8');
    if (fp.slice(-5) === '.json') {
      var file = JSON.parse(str);
      file.orig = str;
      return file;
    }
    return str;
  } catch(err) {}
  return fp;
};

Loader.prototype.renameKey = function(fp, options) {
  var opts = extend({}, this.options, options);
  if (typeof opts.renameKey === 'function') {
    return opts.renameKey(fp);
  }
  if (opts.relative === false) return fp;
  return relative(fp);
};

Loader.prototype.resolve = function(fp, options) {
  var opts = extend({}, this.options, options);
  if (typeof opts.resolve === 'function') {
    return opts.resolve(fp);
  }
  if (opts.relative === false) return fp;
  return relative(fp);
};

function extendOmit(prop, o, target) {
  if (typeof target !== 'object') target = o;
  if (o.hasOwnProperty(prop)) {
    extend(target, o[prop]);
    delete o[prop];
  }
}

module.exports = Loader;
<|endoftext|>


<commit_before>"use strict";
const Discord = require('discord.js');
var request = require('request');
const client = new Discord.Client();

client.on('ready', () => {
  console.log('I am ready!');
});

function me_irl(){
  var url = "";
  request('https://www.reddit.com/r/meirl/.rss', function (error, response, body) {
  if (!error && response.statusCode == 200) {
    url = console.log(body) // Show the HTML for the Google homepage.
  }
  })
  var re = new RegExp("([a-z\-_0-9\/\:\.]*\.(jpg|jpeg|png|gif))");
  return re[Symbol.match](url);
}

client.on('message', message => {
  if (message.content === 'ping') {
    message.reply('pong');
	}
  else if(message.content.slice(0,4) === '-fck'){
  	message.channel.sendMessage('fuck off ' + message.content.slice(5));
  }
  else if(message.content === '-meirl'){
  	message.channel.sendMessage(me_irl())
  }
});

client.login('-redacted-');<commit_msg>regex still broken lmao<commit_after>"use strict";
const Discord = require('discord.js');
var request = require('request');
const client = new Discord.Client();

client.on('ready', () => {
  console.log('I am ready!');
});

function me_irl(){
  var url = "";
  request('https://www.reddit.com/r/meirl/.rss', function (error, response, body) {
  if (!error && response.statusCode == 200) {
    url = console.log(body)
  }
  })
  var re = new RegExp("([a-z\-_0-9\/\:\.]*\.(jpg|jpeg|png|gif))");
  return re[Symbol.match](url);
}

client.on('message', message => {
  if (message.content === 'ping') {
    message.reply('pong');
	}
  else if(message.content.slice(0,4) === '-fck'){
  	message.channel.sendMessage('fuck off ' + message.content.slice(5));
  }
  else if(message.content === '-meirl'){
  	message.channel.sendMessage(me_irl())
  }
});

client.login('-redacted-');<|endoftext|>


<commit_before>//http://en.wikipedia.org/wiki/Delaunay_triangulation
//https://github.com/ironwallaby/delaunay
var polygon = require('turf-polygon');
var nearest = require('turf-nearest');
var point = require('turf-point');

/**
 * Takes a set of points and the name of a z-value property and
 * creates a [Triangulated Irregular Network](http://en.wikipedia.org/wiki/Triangulated_irregular_network),
 * or a TIN for short. These are often used
 * for developing elevation contour maps or stepped heat visualizations.
 *
 * @module turf/tin
 * @param {GeoJSONFeatureCollection} points - a GeoJSON FeatureCollection containing
 * Features with Point geometries
 * @param {string} propertyName - name of the property from which to pull z values
 * @return {GeoJSONFeatureCollection} output
 * @example
 * var fs = require('fs')
 * var z = 'elevation'
 * var pts = JSON.parse(fs.readFileSync('/path/to/pts.geojson'))
 * var tinPolys = turf.tin(pts, z)
 * console.log(tinPolys)
 */
module.exports = function(points, z){
  //break down points
  var vertices = [];
  points.features.forEach(function(p){
    vertices.push({x:p.geometry.coordinates[0], y:p.geometry.coordinates[1]});
  })

  var triangulated = triangulate(vertices);
  var triangles = {
    type: 'FeatureCollection',
    features: []
  };

  triangulated.forEach(function(triangle){
    var coords = [[[triangle.a.x, triangle.a.y], [triangle.b.x, triangle.b.y], [triangle.c.x, triangle.c.y]]];
    var poly = polygon(coords, {a: null, b: null, c: null});

    triangles.features.push(poly);
  });
  if(z){
    // add values from vertices
    triangles.features.forEach(function(tri){
      var coordinateNumber = 1;
      tri.geometry.coordinates[0].forEach(function(c){
        var closest = nearest(point(c[0], c[1]), points);

        if(coordinateNumber === 1){
          tri.properties.a = closest.properties[z];
        }
        else if(coordinateNumber === 2){
          tri.properties.b = closest.properties[z];
        }
        else if(coordinateNumber === 3){
          tri.properties.c = closest.properties[z];
        }
        coordinateNumber++;
      });
    });
  }

  triangles.features.forEach(function(tri){
    tri = correctRings(tri);
  });

  return triangles;
}

function correctRings(poly){
  poly.geometry.coordinates.forEach(function(ring){
    var isWrapped =  ring[0] === ring.slice(-1)[0];
    if(!isWrapped){
      ring.push(ring[0]);
    }
  })
  return poly;
}

function Triangle(a, b, c) {
  this.a = a
  this.b = b
  this.c = c

  var A = b.x - a.x,
      B = b.y - a.y,
      C = c.x - a.x,
      D = c.y - a.y,
      E = A * (a.x + b.x) + B * (a.y + b.y),
      F = C * (a.x + c.x) + D * (a.y + c.y),
      G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)),
      minx, miny, dx, dy

  /* If the points of the triangle are collinear, then just find the
   * extremes and use the midpoint as the center of the circumcircle. */
  if(Math.abs(G) < 0.000001) {
    minx = Math.min(a.x, b.x, c.x)
    miny = Math.min(a.y, b.y, c.y)
    dx   = (Math.max(a.x, b.x, c.x) - minx) * 0.5
    dy   = (Math.max(a.y, b.y, c.y) - miny) * 0.5

    this.x = minx + dx
    this.y = miny + dy
    this.r = dx * dx + dy * dy
  }

  else {
    this.x = (D*E - B*F) / G
    this.y = (A*F - C*E) / G
    dx = this.x - a.x
    dy = this.y - a.y
    this.r = dx * dx + dy * dy
  }
}

Triangle.prototype.draw = function(ctx) {
  ctx.beginPath()
  ctx.moveTo(this.a.x, this.a.y)
  ctx.lineTo(this.b.x, this.b.y)
  ctx.lineTo(this.c.x, this.c.y)
  ctx.closePath()
  ctx.stroke()
}

function byX(a, b) {
  return b.x - a.x
}

function dedup(edges) {
  var j = edges.length,
      a, b, i, m, n

  outer: while(j) {
    b = edges[--j]
    a = edges[--j]
    i = j
    while(i) {
      n = edges[--i]
      m = edges[--i]
      if((a === m && b === n) || (a === n && b === m)) {
        edges.splice(j, 2)
        edges.splice(i, 2)
        j -= 2
        continue outer
      }
    }
  }
}

function triangulate(vertices) {
  /* Bail if there aren't enough vertices to form any triangles. */
  if(vertices.length < 3)
    return []

  /* Ensure the vertex array is in order of descending X coordinate
   * (which is needed to ensure a subquadratic runtime), and then find
   * the bounding box around the points. */
  vertices.sort(byX)

  var i    = vertices.length - 1,
      xmin = vertices[i].x,
      xmax = vertices[0].x,
      ymin = vertices[i].y,
      ymax = ymin

  while(i--) {
    if(vertices[i].y < ymin) ymin = vertices[i].y
    if(vertices[i].y > ymax) ymax = vertices[i].y
  }

  /* Find a supertriangle, which is a triangle that surrounds all the
   * vertices. This is used like something of a sentinel value to remove
   * cases in the main algorithm, and is removed before we return any
   * results.
   *
   * Once found, put it in the "open" list. (The "open" list is for
   * triangles who may still need to be considered; the "closed" list is
   * for triangles which do not.) */
  var dx     = xmax - xmin,
      dy     = ymax - ymin,
      dmax   = (dx > dy) ? dx : dy,
      xmid   = (xmax + xmin) * 0.5,
      ymid   = (ymax + ymin) * 0.5,
      open   = [
        new Triangle(
          {x: xmid - 20 * dmax, y: ymid -      dmax, __sentinel: true},
          {x: xmid            , y: ymid + 20 * dmax, __sentinel: true},
          {x: xmid + 20 * dmax, y: ymid -      dmax, __sentinel: true}
        )
      ],
      closed = [],
      edges = [],
      j, a, b

  /* Incrementally add each vertex to the mesh. */
  i = vertices.length
  while(i--) {
    /* For each open triangle, check to see if the current point is
     * inside it's circumcircle. If it is, remove the triangle and add
     * it's edges to an edge list. */
    edges.length = 0
    j = open.length
    while(j--) {
      /* If this point is to the right of this triangle's circumcircle,
       * then this triangle should never get checked again. Remove it
       * from the open list, add it to the closed list, and skip. */
      dx = vertices[i].x - open[j].x
      if(dx > 0 && dx * dx > open[j].r) {
        closed.push(open[j])
        open.splice(j, 1)
        continue
      }

      /* If not, skip this triangle. */
      dy = vertices[i].y - open[j].y
      if(dx * dx + dy * dy > open[j].r)
        continue

      /* Remove the triangle and add it's edges to the edge list. */
      edges.push(
        open[j].a, open[j].b,
        open[j].b, open[j].c,
        open[j].c, open[j].a
      )
      open.splice(j, 1)
    }

    /* Remove any doubled edges. */
    dedup(edges)

    /* Add a new triangle for each edge. */
    j = edges.length
    while(j) {
      b = edges[--j]
      a = edges[--j]
      open.push(new Triangle(a, b, vertices[i]))
    }
  }

  /* Copy any remaining open triangles to the closed list, and then
   * remove any triangles that share a vertex with the supertriangle. */
  Array.prototype.push.apply(closed, open)

  i = closed.length
  while(i--)
    if(closed[i].a.__sentinel ||
       closed[i].b.__sentinel ||
       closed[i].c.__sentinel)
      closed.splice(i, 1)

  /* Yay, we're done! */
  return closed
}

/*if (typeof module !== 'undefined') {
    module.exports = {
        Triangle: Triangle,
        triangulate: triangulate
    }
}*/
<commit_msg>Improve docs<commit_after>//http://en.wikipedia.org/wiki/Delaunay_triangulation
//https://github.com/ironwallaby/delaunay
var polygon = require('turf-polygon');
var nearest = require('turf-nearest');
var point = require('turf-point');

/**
 * Takes a set of points and the name of a z-value property and
 * creates a [Triangulated Irregular Network](http://en.wikipedia.org/wiki/Triangulated_irregular_network),
 * or a TIN for short. These are often used
 * for developing elevation contour maps or stepped heat visualizations.
 *
 * @module turf/tin
 * @param {FeatureCollection} points - a GeoJSON FeatureCollection containing
 * Features with {@link Point} geometries
 * @param {string} propertyName - name of the property from which to pull z values
 * @return {FeatureCollection} output
 * @example
 * var fs = require('fs')
 * var z = 'elevation'
 * var pts = JSON.parse(fs.readFileSync('/path/to/pts.geojson'))
 * var tinPolys = turf.tin(pts, z)
 * console.log(tinPolys)
 */
module.exports = function(points, z){
  //break down points
  var vertices = [];
  points.features.forEach(function(p){
    vertices.push({x:p.geometry.coordinates[0], y:p.geometry.coordinates[1]});
  })

  var triangulated = triangulate(vertices);
  var triangles = {
    type: 'FeatureCollection',
    features: []
  };

  triangulated.forEach(function(triangle){
    var coords = [[[triangle.a.x, triangle.a.y], [triangle.b.x, triangle.b.y], [triangle.c.x, triangle.c.y]]];
    var poly = polygon(coords, {a: null, b: null, c: null});

    triangles.features.push(poly);
  });
  if(z){
    // add values from vertices
    triangles.features.forEach(function(tri){
      var coordinateNumber = 1;
      tri.geometry.coordinates[0].forEach(function(c){
        var closest = nearest(point(c[0], c[1]), points);

        if(coordinateNumber === 1){
          tri.properties.a = closest.properties[z];
        }
        else if(coordinateNumber === 2){
          tri.properties.b = closest.properties[z];
        }
        else if(coordinateNumber === 3){
          tri.properties.c = closest.properties[z];
        }
        coordinateNumber++;
      });
    });
  }

  triangles.features.forEach(function(tri){
    tri = correctRings(tri);
  });

  return triangles;
}

function correctRings(poly){
  poly.geometry.coordinates.forEach(function(ring){
    var isWrapped =  ring[0] === ring.slice(-1)[0];
    if(!isWrapped){
      ring.push(ring[0]);
    }
  })
  return poly;
}

function Triangle(a, b, c) {
  this.a = a
  this.b = b
  this.c = c

  var A = b.x - a.x,
      B = b.y - a.y,
      C = c.x - a.x,
      D = c.y - a.y,
      E = A * (a.x + b.x) + B * (a.y + b.y),
      F = C * (a.x + c.x) + D * (a.y + c.y),
      G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)),
      minx, miny, dx, dy

  /* If the points of the triangle are collinear, then just find the
   * extremes and use the midpoint as the center of the circumcircle. */
  if(Math.abs(G) < 0.000001) {
    minx = Math.min(a.x, b.x, c.x)
    miny = Math.min(a.y, b.y, c.y)
    dx   = (Math.max(a.x, b.x, c.x) - minx) * 0.5
    dy   = (Math.max(a.y, b.y, c.y) - miny) * 0.5

    this.x = minx + dx
    this.y = miny + dy
    this.r = dx * dx + dy * dy
  }

  else {
    this.x = (D*E - B*F) / G
    this.y = (A*F - C*E) / G
    dx = this.x - a.x
    dy = this.y - a.y
    this.r = dx * dx + dy * dy
  }
}

Triangle.prototype.draw = function(ctx) {
  ctx.beginPath()
  ctx.moveTo(this.a.x, this.a.y)
  ctx.lineTo(this.b.x, this.b.y)
  ctx.lineTo(this.c.x, this.c.y)
  ctx.closePath()
  ctx.stroke()
}

function byX(a, b) {
  return b.x - a.x
}

function dedup(edges) {
  var j = edges.length,
      a, b, i, m, n

  outer: while(j) {
    b = edges[--j]
    a = edges[--j]
    i = j
    while(i) {
      n = edges[--i]
      m = edges[--i]
      if((a === m && b === n) || (a === n && b === m)) {
        edges.splice(j, 2)
        edges.splice(i, 2)
        j -= 2
        continue outer
      }
    }
  }
}

function triangulate(vertices) {
  /* Bail if there aren't enough vertices to form any triangles. */
  if(vertices.length < 3)
    return []

  /* Ensure the vertex array is in order of descending X coordinate
   * (which is needed to ensure a subquadratic runtime), and then find
   * the bounding box around the points. */
  vertices.sort(byX)

  var i    = vertices.length - 1,
      xmin = vertices[i].x,
      xmax = vertices[0].x,
      ymin = vertices[i].y,
      ymax = ymin

  while(i--) {
    if(vertices[i].y < ymin) ymin = vertices[i].y
    if(vertices[i].y > ymax) ymax = vertices[i].y
  }

  /* Find a supertriangle, which is a triangle that surrounds all the
   * vertices. This is used like something of a sentinel value to remove
   * cases in the main algorithm, and is removed before we return any
   * results.
   *
   * Once found, put it in the "open" list. (The "open" list is for
   * triangles who may still need to be considered; the "closed" list is
   * for triangles which do not.) */
  var dx     = xmax - xmin,
      dy     = ymax - ymin,
      dmax   = (dx > dy) ? dx : dy,
      xmid   = (xmax + xmin) * 0.5,
      ymid   = (ymax + ymin) * 0.5,
      open   = [
        new Triangle(
          {x: xmid - 20 * dmax, y: ymid -      dmax, __sentinel: true},
          {x: xmid            , y: ymid + 20 * dmax, __sentinel: true},
          {x: xmid + 20 * dmax, y: ymid -      dmax, __sentinel: true}
        )
      ],
      closed = [],
      edges = [],
      j, a, b

  /* Incrementally add each vertex to the mesh. */
  i = vertices.length
  while(i--) {
    /* For each open triangle, check to see if the current point is
     * inside it's circumcircle. If it is, remove the triangle and add
     * it's edges to an edge list. */
    edges.length = 0
    j = open.length
    while(j--) {
      /* If this point is to the right of this triangle's circumcircle,
       * then this triangle should never get checked again. Remove it
       * from the open list, add it to the closed list, and skip. */
      dx = vertices[i].x - open[j].x
      if(dx > 0 && dx * dx > open[j].r) {
        closed.push(open[j])
        open.splice(j, 1)
        continue
      }

      /* If not, skip this triangle. */
      dy = vertices[i].y - open[j].y
      if(dx * dx + dy * dy > open[j].r)
        continue

      /* Remove the triangle and add it's edges to the edge list. */
      edges.push(
        open[j].a, open[j].b,
        open[j].b, open[j].c,
        open[j].c, open[j].a
      )
      open.splice(j, 1)
    }

    /* Remove any doubled edges. */
    dedup(edges)

    /* Add a new triangle for each edge. */
    j = edges.length
    while(j) {
      b = edges[--j]
      a = edges[--j]
      open.push(new Triangle(a, b, vertices[i]))
    }
  }

  /* Copy any remaining open triangles to the closed list, and then
   * remove any triangles that share a vertex with the supertriangle. */
  Array.prototype.push.apply(closed, open)

  i = closed.length
  while(i--)
    if(closed[i].a.__sentinel ||
       closed[i].b.__sentinel ||
       closed[i].c.__sentinel)
      closed.splice(i, 1)

  /* Yay, we're done! */
  return closed
}

/*if (typeof module !== 'undefined') {
    module.exports = {
        Triangle: Triangle,
        triangulate: triangulate
    }
}*/
<|endoftext|>


<commit_before>var irc = require("irc");
var fs = require("fs");
var config = require("./config.js");
var jsonfile = require("jsonfile");
var format = require("util").format;
var inspect = require("util").inspect;
var google = require('google');
var fetch = require("node-fetch");
fetch.Promise = require("bluebird");
google.resultsPerPage = 1;
require('string.prototype.startswith');

var users;
var karmaUsers;
var karmaFile = config.karmaFile;

var actions = {
    JOIN: "join",
    PART: "part",
    MSG: "msg",
    ERROR: "error",
    INFO: "info"
};

// Probably a better way to do this
(function() {
    jsonfile.readFile(karmaFile, function(err, obj) {
        log(actions.INFO, "Read karmaFile JSON");
        karmaUsers = obj;
    })
})();

var bot = new irc.Client(config.server, config.botName, {
    channels: config.channels
});

bot.addListener("names" + config.channels[0], function(nicks) {
    //Object.keys because node-irc returns nicks as a large object with empty keys...
    users = Object.keys(nicks);
});

bot.addListener("join", function(channel, who) {
    log(actions.JOIN, who + " joined " + channel);
    if(who.indexOf("lounge-user") > -1) {
    	bot.say(who, "Hey, " + who + ", now that you've figured out how to use The Lounge, feel free to change your nickname to something more personal using the /nick <new_nickname> command so we know who you are :)");
    }
});

bot.addListener("message", function(from, to, text) {
    var splitMessage = text.split(" ");
    var message = "";
    log(actions.MSG, "(" + to + ") " + from + ": " + text);

    //It's a command!
    if (splitMessage[0].startsWith(config.commandPrefix)) {
        log(actions.INFO, "Command sent");
        //!karma command
        if (splitMessage[0] === "!karma" || splitMessage[0] === "!k") {
            if (splitMessage.length === 2) {
                userIndex = arrayObjectIndexOf(karmaUsers, splitMessage[1], "name");
                if (userInChannel(splitMessage[1])) {
                    message = splitMessage[1] + "'s karma is " + getKarma(splitMessage[1]);
                } else {
                    log(actions.ERROR, "No user named " + splitMessage[1] + " found");
                }
            } else {
                message = "Your karma is " + getKarma(from);
            }
            //!gh issue command
        } else if (splitMessage[0] === "!gh" || splitMessage[0] === "!github") {
            //just !gh
            if (splitMessage.length === 1) {
                message = format("https://github.com/%s/%s", config.githubUser, config.githubRepo);
                //<number> or repo info
            } else if (splitMessage.length === 2) {
                var arg = splitMessage[1];
                //It's a number, so it's an issue
                if (stringIsPositiveInteger(arg)) {
                    log(actions.INFO, config.githubUser);
                    message = getIssueInformation({
                        user: config.githubUser,
                        repo: config.githubRepo,
                        issue: arg
                    });
                } else {
                    var userRepo = parseUserRepoString(arg);
                    mesage = format("https://www.github.com/%s/%s", userRepo.user, userRepo.repo);
                }
            } else if (splitMessage.length === 3) {
                if (stringIsPositiveInteger(splitMessage[2])) {
                    message = getIssueInformation(withIssue(parseUserRepoString(splitMessage[1]), splitMessage[3]));
                } else {
                    message = format("https://github.com/%s/%s", splitMessage[1], splitMessage[2]);
                }
            } else if (splitMessage.length === 4) {
                message = getIssueInformation({
                    user: splitMessage[1],
                    repo: splitMessage[2],
                    issue: splitMessage[3]
                });
            }

            //Google command
        } else if ((splitMessage[0] === "!g" || splitMessage[0] === "!google") && splitMessage.length > 1) {
            //Remove the command from the message array
            var query = splitMessage.slice(1).join(' ');
            log(actions.INFO, query);
            google(query, function(err, next, links) {
            	if(err) {
            		log(actions.ERROR, err);
            	}
            	bot.say(to, links[0].title + ' - ' + links[0].link);
            });
        }

        log(actions.INFO, message);
        if(message !== "") {
	        if (message.then) {
	            message.then(function(m) {
	                return bot.say(to, m);
	            });
	        } else {
	            bot.say(to, message);
	        }
		}
    } else {
        //karma
        if (splitMessage[0].indexOf("++") > -1) {
            //remove ++
            var user = splitMessage[0].toString().slice(0, -2);
            if (userInChannel(user)) {
                incrementKarma(user);
            } else {
                bot.say(to, user + " is not in the channel.");
            }
        }
    }
});

/** Utils **/

var log = function(action, message) {
	var message = "[" + action.toUpperCase() + "]" + " " + message;
	console.log(message);
	fs.open('log.txt', 'a', 666, function(e, id) {
		fs.write(id, message + "\n", null, 'utf8', function(){
			fs.close(id);
		});
	});
}

var userInChannel = function(user) {
    if (users.indexOf(user.toString()) > -1) {
        return true;
    } else {
        return false;
    }
}

var arrayObjectIndexOf = function(array, searchTerm, property) {
    for (var i = 0, len = array.length; i < len; i++) {
        if (array[i][property] === searchTerm) {
            return i;
        }
    }
    return -1;
}

/** Karma **/

var incrementKarma = function(user) {
    var userIndex;
    if (karmaUsers) {
        if (karmaUsers.length >= 1) {
            userIndex = arrayObjectIndexOf(karmaUsers, user, "name");
            log(actions.INFO, "User index is " + userIndex);
        }
        //if the user exists, increment karma, otherwise add them
        if (userIndex > -1) {
            log(actions.INFO, "karmaUser[index]: ", karmaUsers[userIndex]);
            karmaUsers[userIndex].karma = karmaUsers[userIndex].karma + 1;
        } else {
            //they're a new user and just got karma, so it should be 1
            karmaUsers.push({
                "name": user,
                "karma": 1
            });
            log(actions.INFO, "Pushing new user to Karma object");
        }

        //no matter what, we should save the users to a file
        jsonfile.writeFile(karmaFile, karmaUsers);
    }
}

var getKarma = function(user) {
    var userIndex;
    if (karmaUsers) {
        if (karmaUsers.length >= 1) {
            userIndex = arrayObjectIndexOf(karmaUsers, user, "name");
            log(actions.INFO, "User index is ", userIndex);
        }
        if (userIndex > -1) {
            log(actions.INFO, "karmaUser[index]: " + karmaUsers[userIndex]);
            return karmaUsers[userIndex].karma;
        } else {
            return -1;
        }
    }
}

/** Github **/
//Credits to tennu-github for parts of the following.
var getIssueInformation = function(options) {
    var user = options.user;
    var repo = options.repo;
    var issueNumber = options.issue;
    var result = format("https://api.github.com/repos/%s/%s/issues/%s", user, repo, issueNumber);
    return fetch(result).then(function(res) {
        log(actions.INFO, result);
        return res.json();
    }).then(function(res) {
        if (res.message === "Not Found") {
            return "Issue does not exist.";
        }

        var type = res.pull_request === undefined ? "Issue" : "PR";
        var status = res.state;
        var title = res.title;
        var link = res.html_url;

        if (type === "PR" && status === "closed") {
            return fetch(res.pull_request.url).then(function(res) {
                return res.json();
            }).then(function(res) {
                if (res.message === "Not Found") {
                    return "PR does not exist.";
                }

                var type = "PR";
                var status = res.merged_at === null ? "closed" : "merged";
                var title = res.title;
                var link = res.html_url;

                return format("[%s %s] (%s) %s (%s)", type, issueNumber, status, title, link);
            });
        } else {
            return format("[%s %s] (%s) %s (%s)", type, issueNumber, status, title, link);
        }
    });
}

var withIssue = function(obj, issueNumber) {
    obj.issue = issueNumber;
    return obj;
}

var stringIsPositiveInteger = function(string) {
    var number = Number(string);

    if (isNaN(number)) {
        return false;
    }

    if (number === Infinity) {
        return false;
    }

    if (number < 1) {
        return false;
    }

    if (Math.floor(number) !== number) {
        return false;
    }

    return true;
}

var parseUserRepoString = function(userRepoString) {
    if (userRepoString.length === 0) {
        return {
            user: config.githubUser,
            repo: config.githubRepo
        };
    }

    if (userRepoString.indexOf("/") === -1) {
        return {
            user: config.githubUser,
            repo: config.userRepoString
        };
    }

    var split = userRepoString.split("/");
    var user = split[0].length !== 0 ? split[0] : config.githubUser;
    var repo = split[1].length !== 0 ? split[1] : config.githubRepo;

    return {
        user: user,
        repo: repo
    };
}<commit_msg>Lowercase nicknames now work for karma<commit_after>var irc = require("irc");
var fs = require("fs");
var config = require("./config.js");
var jsonfile = require("jsonfile");
var format = require("util").format;
var inspect = require("util").inspect;
var google = require('google');
var fetch = require("node-fetch");
fetch.Promise = require("bluebird");
google.resultsPerPage = 1;
require('string.prototype.startswith');

var users;
var karmaUsers;
var karmaFile = config.karmaFile;

var actions = {
    JOIN: "join",
    PART: "part",
    MSG: "msg",
    ERROR: "error",
    INFO: "info"
};

// Probably a better way to do this
(function() {
    jsonfile.readFile(karmaFile, function(err, obj) {
        log(actions.INFO, "Read karmaFile JSON");
        karmaUsers = obj;
        //Convert to lower-case
        for (var i = 0, L = karmaUsers.length; i < L; i++) {
          karmaUsers[i] = karmaUsers[i].name.toLowerCase();
        }

    })
})();

var bot = new irc.Client(config.server, config.botName, {
    channels: config.channels
});

bot.addListener("names" + config.channels[0], function(nicks) {
    //Object.keys because node-irc returns nicks as a large object with empty keys...
    users = Object.keys(nicks);
    //Convert to lower-case
    for (var i = 0, L = users.length; i < L; i++) {
      users[i] = users[i].toLowerCase();
    }

});

bot.addListener("join", function(channel, who) {
    log(actions.JOIN, who + " joined " + channel);
    if(who.indexOf("lounge-user") > -1) {
    	bot.say(who, "Hey, " + who + ", now that you've figured out how to use The Lounge, feel free to change your nickname to something more personal using the /nick <new_nickname> command so we know who you are :)");
    }
});

bot.addListener("message", function(from, to, text) {
    var splitMessage = text.split(" ");
    var message = "";
    log(actions.MSG, "(" + to + ") " + from + ": " + text);

    //It's a command!
    if (splitMessage[0].startsWith(config.commandPrefix)) {
        log(actions.INFO, "Command sent");
        //!karma command
        if (splitMessage[0] === "!karma" || splitMessage[0] === "!k") {
            if (splitMessage.length === 2) {
                userIndex = arrayObjectIndexOf(karmaUsers, splitMessage[1], "name");
                if (userInChannel(splitMessage[1])) {
                    message = splitMessage[1] + "'s karma is " + getKarma(splitMessage[1]);
                } else {
                    log(actions.ERROR, "No user named " + splitMessage[1] + " found");
                }
            } else {
                message = "Your karma is " + getKarma(from);
            }
            //!gh issue command
        } else if (splitMessage[0] === "!gh" || splitMessage[0] === "!github") {
            //just !gh
            if (splitMessage.length === 1) {
                message = format("https://github.com/%s/%s", config.githubUser, config.githubRepo);
                //<number> or repo info
            } else if (splitMessage.length === 2) {
                var arg = splitMessage[1];
                //It's a number, so it's an issue
                if (stringIsPositiveInteger(arg)) {
                    log(actions.INFO, config.githubUser);
                    message = getIssueInformation({
                        user: config.githubUser,
                        repo: config.githubRepo,
                        issue: arg
                    });
                } else {
                    var userRepo = parseUserRepoString(arg);
                    mesage = format("https://www.github.com/%s/%s", userRepo.user, userRepo.repo);
                }
            } else if (splitMessage.length === 3) {
                if (stringIsPositiveInteger(splitMessage[2])) {
                    message = getIssueInformation(withIssue(parseUserRepoString(splitMessage[1]), splitMessage[3]));
                } else {
                    message = format("https://github.com/%s/%s", splitMessage[1], splitMessage[2]);
                }
            } else if (splitMessage.length === 4) {
                message = getIssueInformation({
                    user: splitMessage[1],
                    repo: splitMessage[2],
                    issue: splitMessage[3]
                });
            }

            //Google command
        } else if ((splitMessage[0] === "!g" || splitMessage[0] === "!google") && splitMessage.length > 1) {
            //Remove the command from the message array
            var query = splitMessage.slice(1).join(' ');
            log(actions.INFO, query);
            google(query, function(err, next, links) {
            	if(err) {
            		log(actions.ERROR, err);
            	}
            	bot.say(to, links[0].title + ' - ' + links[0].link);
            });
        }

        log(actions.INFO, message);
        if(message !== "") {
	        if (message.then) {
	            message.then(function(m) {
	                return bot.say(to, m);
	            });
	        } else {
	            bot.say(to, message);
	        }
		}
    } else {
        //karma
        if (splitMessage[0].indexOf("++") > -1) {
            //remove ++
            var user = splitMessage[0].toString().slice(0, -2);
            if (userInChannel(user)) {
                incrementKarma(user);
            } else {
                bot.say(to, user + " is not in the channel.");
            }
        }
    }
});

/** Utils **/

var log = function(action, message) {
	var message = "[" + action.toUpperCase() + "]" + " " + message;
	console.log(message);
	fs.open('log.txt', 'a', 666, function(e, id) {
		fs.write(id, message + "\n", null, 'utf8', function(){
			fs.close(id);
		});
	});
}

var userInChannel = function(user) {
    if (users.indexOf(user.toString().toLowerCase()) > -1) {
        return true;
    } else {
        return false;
    }
}

var arrayObjectIndexOf = function(array, searchTerm, property) {
    for (var i = 0, len = array.length; i < len; i++) {
        if (array[i][property] === searchTerm) {
            return i;
        }
    }
    return -1;
}

/** Karma **/

var incrementKarma = function(user) {
    var userIndex;
    if (karmaUsers) {
        if (karmaUsers.length >= 1) {
            userIndex = arrayObjectIndexOf(karmaUsers, user.toLowerCase(), "name");
            log(actions.INFO, "User index is " + userIndex);
        }
        //if the user exists, increment karma, otherwise add them
        if (userIndex > -1) {
            log(actions.INFO, "karmaUser[index]: ", karmaUsers[userIndex]);
            karmaUsers[userIndex].karma = karmaUsers[userIndex].karma + 1;
        } else {
            //they're a new user and just got karma, so it should be 1
            karmaUsers.push({
                "name": user,
                "karma": 1
            });
            log(actions.INFO, "Pushing new user to Karma object");
        }

        //no matter what, we should save the users to a file
        jsonfile.writeFile(karmaFile, karmaUsers);
    }
}

var getKarma = function(user) {
    var userIndex;
    if (karmaUsers) {
        if (karmaUsers.length >= 1) {
            userIndex = arrayObjectIndexOf(karmaUsers, user.toLowerCase(), "name");
            log(actions.INFO, "User index is ", userIndex);
        }
        if (userIndex > -1) {
            log(actions.INFO, "karmaUser[index]: " + karmaUsers[userIndex]);
            return karmaUsers[userIndex].karma;
        } else {
            return -1;
        }
    }
}

/** Github **/
//Credits to tennu-github for parts of the following.
var getIssueInformation = function(options) {
    var user = options.user;
    var repo = options.repo;
    var issueNumber = options.issue;
    var result = format("https://api.github.com/repos/%s/%s/issues/%s", user, repo, issueNumber);
    return fetch(result).then(function(res) {
        log(actions.INFO, result);
        return res.json();
    }).then(function(res) {
        if (res.message === "Not Found") {
            return "Issue does not exist.";
        }

        var type = res.pull_request === undefined ? "Issue" : "PR";
        var status = res.state;
        var title = res.title;
        var link = res.html_url;

        if (type === "PR" && status === "closed") {
            return fetch(res.pull_request.url).then(function(res) {
                return res.json();
            }).then(function(res) {
                if (res.message === "Not Found") {
                    return "PR does not exist.";
                }

                var type = "PR";
                var status = res.merged_at === null ? "closed" : "merged";
                var title = res.title;
                var link = res.html_url;

                return format("[%s %s] (%s) %s (%s)", type, issueNumber, status, title, link);
            });
        } else {
            return format("[%s %s] (%s) %s (%s)", type, issueNumber, status, title, link);
        }
    });
}

var withIssue = function(obj, issueNumber) {
    obj.issue = issueNumber;
    return obj;
}

var stringIsPositiveInteger = function(string) {
    var number = Number(string);

    if (isNaN(number)) {
        return false;
    }

    if (number === Infinity) {
        return false;
    }

    if (number < 1) {
        return false;
    }

    if (Math.floor(number) !== number) {
        return false;
    }

    return true;
}

var parseUserRepoString = function(userRepoString) {
    if (userRepoString.length === 0) {
        return {
            user: config.githubUser,
            repo: config.githubRepo
        };
    }

    if (userRepoString.indexOf("/") === -1) {
        return {
            user: config.githubUser,
            repo: config.userRepoString
        };
    }

    var split = userRepoString.split("/");
    var user = split[0].length !== 0 ? split[0] : config.githubUser;
    var repo = split[1].length !== 0 ? split[1] : config.githubRepo;

    return {
        user: user,
        repo: repo
    };
}<|endoftext|>


<commit_before>var difflet = require('difflet');
var deepEqual = require('deep-equal');
var expect = require("expect.js");
var util = require('util');
var urlgrey = require('urlgrey');
var request = require('request');
var Reaper = require('reaper');

var isString = function(str){
  return toString.call(str) == '[object String]';
};

// checks a response holistically, rather than in parts,
// which results in better error output.
var Verity = function(uri, method){
  if (!(this instanceof Verity)) {
    return new Verity(uri, method);
  }
  this.uri = urlgrey(uri || 'http://localhost:80');
  this.method = method || 'GET';
  this.body = '';
  this.client = request.defaults({jar:true, timeout:3000});
  this.headers = {};
  this.expectedBody = '';
  this.expectedStatus = 200;
  this.expectedHeaders = {};
  this.befores = [];
  this.jsonModeOn = false;
  this.message = '';
  this.log = function(){
    var addition = '';
    for (var k in arguments){
      if (isString(arguments[k])){
        addition += arguments[k];
      } else {
        addition += util.inspect(arguments[k]) + " ";
      }
    }
    addition += "\n";
    this.message += addition;
    //process.stdout.write(addition);
  };
  this.onerror = function(err, method, uri, headers, body){
    this.log("");
    this.log("unexpected error: ", (err.message || err));
    this.log(util.inspect(err));
    this.log("method: ", method);
    this.log("uri: ", uri);
    this.log("body: ", body);
    this.log("headers: ", headers);
    this.log("");
    throw "unexpected error: " + JSON.stringify((err.message || err));
  };
};

Verity.prototype.onError = function(cb){
  this.onerror = cb;
};

Verity.prototype.expectBody = function(body){
  this.expectedBody = body;
};
Verity.prototype.expectStatus = function(code){
  this.expectedStatus = code;
};
Verity.prototype.expectHeader = function(name, value){
  this.expectedHeaders[name] = value;
};
Verity.prototype.jsonMode = function(){
  this.expectHeader('content-type', 'application/json');
  this.jsonModeOn = true;
};
Verity.prototype.before = function(cb){
  this.befores.push(cb);
};
Verity.prototype.authStrategy = function(creds, cb){
  // replace this with a function that logs the user in
  return cb(null);
};
Verity.prototype.login = function(creds){
  this.creds = creds;
};
Verity.prototype.test = function(cb){
  this.message = '';
  // TODO make this all one assertion error message
  var options = { headers : this.headers};
  options.method = this.method.toLowerCase();
  options.url = this.uri.toString();
  if (!!this.body){
    console.log("got a body!!: ", this.body);
    if (this.jsonModeOn){
      options.headers['content-type'] = 'application/json';
      if (!isString(this.body)){
        options.body = JSON.stringify(this.body);
      }
    } else {
      options.body = this.body;
    }
  }
  var that = this;
  // TODO: if this.creds, log the user in!!!
  console.log("about to request: ", options);
  if (this.creds){
    this.authStrategy(this.creds, function(){
      makeRequest(that, options, cb);
    });
  } else {
    makeRequest(that, options, cb);
  }
};

var makeRequest = function(that, options, cb){
    that.client(options, function(err, response, body){
      if (err) {
        return that.onerror(err,
                            options.method,
                            options.url,
                            options.headers,
                            options.body);
      }
      if (that.jsonModeOn){
        try {
          body = JSON.parse(body);
        } catch(ex){
          // do nothing if it doesn't parse.
          // it will stay as a string
        }
      }
      var failed = false;
      that.log("STATUS: ");
      try {
        that.log('actual: ', response.statusCode);
        that.log('expected: ', that.expectedStatus);
        expect(response.statusCode).to.equal(that.expectedStatus);
      } catch(ex) {
        that.log("Failure: ");
        that.log(ex);
        failed = true;
      }
      that.log('');
      for(var k in that.expectedHeaders){
        var v = that.expectedHeaders[k];
        if (k == 'content-type'){
          var reaper = new Reaper();
          reaper.register(v);
          expect(reaper.isAcceptable('application/json')).to.equal(true);
        } else {
          expect(response.headers[k]).to.equal(v);
        }
      }
      that.log("BODY: ");

      if (!deepEqual(body, that.expectedBody)){
        that.log("Failure: ");
        that.logObjectDiff(body, that.expectedBody);
        failed = true;
      }
      if (failed){
        throw that.message;
      }
      return cb();
    });

};

Verity.prototype.logObjectDiff = function (actual, expected){
    this.log(difflet({indent:2}).compare(actual, expected));
    this.log("\n\nactual");
    this.log(JSON.stringify(actual, null, 2));
    this.log("\n\nexpected");
    this.log(JSON.stringify(expected, null, 2));
    this.log("\n\n");
};

exports.Verity = Verity;

<commit_msg>get the export right.<commit_after>var difflet = require('difflet');
var deepEqual = require('deep-equal');
var expect = require("expect.js");
var util = require('util');
var urlgrey = require('urlgrey');
var request = require('request');
var Reaper = require('reaper');

var isString = function(str){
  return toString.call(str) == '[object String]';
};

// checks a response holistically, rather than in parts,
// which results in better error output.
var Verity = function(uri, method){
  if (!(this instanceof Verity)) {
    return new Verity(uri, method);
  }
  this.uri = urlgrey(uri || 'http://localhost:80');
  this.method = method || 'GET';
  this.body = '';
  this.client = request.defaults({jar:true, timeout:3000});
  this.headers = {};
  this.expectedBody = '';
  this.expectedStatus = 200;
  this.expectedHeaders = {};
  this.befores = [];
  this.jsonModeOn = false;
  this.message = '';
  this.log = function(){
    var addition = '';
    for (var k in arguments){
      if (isString(arguments[k])){
        addition += arguments[k];
      } else {
        addition += util.inspect(arguments[k]) + " ";
      }
    }
    addition += "\n";
    this.message += addition;
    //process.stdout.write(addition);
  };
  this.onerror = function(err, method, uri, headers, body){
    this.log("");
    this.log("unexpected error: ", (err.message || err));
    this.log(util.inspect(err));
    this.log("method: ", method);
    this.log("uri: ", uri);
    this.log("body: ", body);
    this.log("headers: ", headers);
    this.log("");
    throw "unexpected error: " + JSON.stringify((err.message || err));
  };
};

Verity.prototype.onError = function(cb){
  this.onerror = cb;
};

Verity.prototype.expectBody = function(body){
  this.expectedBody = body;
};
Verity.prototype.expectStatus = function(code){
  this.expectedStatus = code;
};
Verity.prototype.expectHeader = function(name, value){
  this.expectedHeaders[name] = value;
};
Verity.prototype.jsonMode = function(){
  this.expectHeader('content-type', 'application/json');
  this.jsonModeOn = true;
};
Verity.prototype.before = function(cb){
  this.befores.push(cb);
};
Verity.prototype.authStrategy = function(creds, cb){
  // replace this with a function that logs the user in
  return cb(null);
};
Verity.prototype.login = function(creds){
  this.creds = creds;
};
Verity.prototype.test = function(cb){
  this.message = '';
  // TODO make this all one assertion error message
  var options = { headers : this.headers};
  options.method = this.method.toLowerCase();
  options.url = this.uri.toString();
  if (!!this.body){
    console.log("got a body!!: ", this.body);
    if (this.jsonModeOn){
      options.headers['content-type'] = 'application/json';
      if (!isString(this.body)){
        options.body = JSON.stringify(this.body);
      }
    } else {
      options.body = this.body;
    }
  }
  var that = this;
  // TODO: if this.creds, log the user in!!!
  console.log("about to request: ", options);
  if (this.creds){
    this.authStrategy(this.creds, function(){
      makeRequest(that, options, cb);
    });
  } else {
    makeRequest(that, options, cb);
  }
};

var makeRequest = function(that, options, cb){
    that.client(options, function(err, response, body){
      if (err) {
        return that.onerror(err,
                            options.method,
                            options.url,
                            options.headers,
                            options.body);
      }
      if (that.jsonModeOn){
        try {
          body = JSON.parse(body);
        } catch(ex){
          // do nothing if it doesn't parse.
          // it will stay as a string
        }
      }
      var failed = false;
      that.log("STATUS: ");
      try {
        that.log('actual: ', response.statusCode);
        that.log('expected: ', that.expectedStatus);
        expect(response.statusCode).to.equal(that.expectedStatus);
      } catch(ex) {
        that.log("Failure: ");
        that.log(ex);
        failed = true;
      }
      that.log('');
      for(var k in that.expectedHeaders){
        var v = that.expectedHeaders[k];
        if (k == 'content-type'){
          var reaper = new Reaper();
          reaper.register(v);
          expect(reaper.isAcceptable('application/json')).to.equal(true);
        } else {
          expect(response.headers[k]).to.equal(v);
        }
      }
      that.log("BODY: ");

      if (!deepEqual(body, that.expectedBody)){
        that.log("Failure: ");
        that.logObjectDiff(body, that.expectedBody);
        failed = true;
      }
      if (failed){
        throw that.message;
      }
      return cb();
    });

};

Verity.prototype.logObjectDiff = function (actual, expected){
    this.log(difflet({indent:2}).compare(actual, expected));
    this.log("\n\nactual");
    this.log(JSON.stringify(actual, null, 2));
    this.log("\n\nexpected");
    this.log(JSON.stringify(expected, null, 2));
    this.log("\n\n");
};

module.exports = Verity;

<|endoftext|>


<commit_before>const TelegramBot = require('node-telegram-bot-api');
const request = require('request');
const schedule = require('node-schedule');
const fs = require('fs');
var config = require('./config');

const token = config.bottoken;
const bot = new TelegramBot(token, {polling: true})
var dailyloc = config.dailyLocation;

bot.onText(/\/greet (.+)/, (msg, input) => {
	const chatId = msg.chat.id;
	var blub = input[1];
	var resp ='Hello, '+blub;

	bot.sendMessage(chatId, resp);
});

var loremSettings = {
	url: 'http://lorempixel.com/400/200',
	encoding: null
};

bot.onText(/\/lorem/||/\/lorem (.*)/, (msg) =>{
	const chatId = msg.chat.id;
	request(loremSettings, function (error, response, buffer) {
		if (!error && response.statusCode == 200){
			bot.sendPhoto(chatId, buffer);
		}
	})
});

var doit = [
	'http://i.imgur.com/RPbK0fZ.png',
	'http://i.imgur.com/TgUQfNI.jpg',
	'http://i.imgur.com/WSm116p.jpg',
]

bot.onText(/\/doit/,(msg) =>{
	const chatId = msg.chat.id;
	bot.sendPhoto(chatId, doit[Math.floor(Math.random()*doit.length)]);
});

var ship = [
	'http://i.imgur.com/b7bzy0C.png',
	'http://i.imgur.com/hse69ui.jpg',
	'http://i.imgur.com/pyjpvhM.gif',
	'http://i.imgur.com/66j50Xz.jpg',
	'http://i.imgur.com/tCiQSlg.jpg',
	'http://i.imgur.com/DDLLoL8.gif',
	'http://i.imgur.com/LDzcDY3.jpg',
	'http://i.imgur.com/nkeMpLe.jpg',
	'http://i.imgur.com/ACaARFw.jpg',
	'http://i.imgur.com/aJkhUrE.jpg',
	'http://i.imgur.com/YBdJsaS.jpg',
	'http://i.imgur.com/KmZrtgd.jpg',
	'http://i.imgur.com/rmgCY2Q.jpg',
	'http://i.imgur.com/g00lVs9.gif',
	'http://i.imgur.com/LIIXWmD.png',
	'http://i.imgur.com/c6K9rP3.jpg',
	'http://i.imgur.com/2M8iyPt.jpg',
	'http://i.imgur.com/7XxnrTu.jpg',
	'http://i.imgur.com/AJN04yl.jpg',
	'http://i.imgur.com/rcIMpM0.gif',
	'http://i.imgur.com/60u8kye.jpg',
	'http://i.imgur.com/yUypmaC.jpg',
	'http://i.imgur.com/IflZleY.jpg',
	'http://i.imgur.com/h3mlvCq.png',
	'http://i.imgur.com/dXzZETc.png',
	'https://media.giphy.com/media/143vPc6b08locw/giphy.gif',
	'https://media0.giphy.com/media/14tFXhhNurdGp2/giphy.gif',
	'https://media4.giphy.com/media/ta83CqOoRwfwQ/giphy.gif',
	'https://media2.giphy.com/media/tDafHUBVrRKtq/giphy.gif',
	'https://media2.giphy.com/media/tDafHUBVrRKtq/giphy.gif',
]

bot.onText(/\/ship/,(msg) =>{
	const chatId = msg.chat.id;
	bot.sendMessage(chatId, ship[Math.floor(Math.random()*ship.length)]);
});

var editions = [
	{short:'an', count:92},
	{short:'lg', count:310},
	{short:'dk', count:119},
	{short:'fe', count:187},
	{short:'hl', count:140},
	{short:'mr', count:350},
	{short:'vi', count:167},
	{short:'wl', count:167},
	{short:'tp', count:350},
	{short:'sh', count:167},
	{short:'ex', count:167},
	{short:'us', count:350},
	{short:'ul', count:143},
	{short:'ud', count:143},
	{short:'mm', count:350},
	{short:'ne', count:143},
	{short:'pr', count:143},
	{short:'in', count:355},
	{short:'ps', count:146},
	{short:'ap', count:148},
	{short:'od', count:350},
	{short:'tr', count:143},
	{short:'ju', count:143},
	{short:'on', count:350},
	{short:'le', count:145},
	{short:'sc', count:143},
	{short:'be', count:302},
	{short:'ug', count:88},
	{short:'po2', count:165},
	{short:'po', count:222},
	{short:'p3k', count:180},
	{short:'ced', count:302},
	{short:'cedi', count:302},
]

bot.onText(/\/draw/, (msg) =>{
	const chatId = msg.chat.id;
	let edition = editions[Math.floor(Math.random()*editions.length)];
	bot.sendMessage(chatId,`http://magiccards.info/scans/en/${edition.short}/${Math.floor(Math.random()*edition.count)}.jpg`);
});

var merge = [
	'https://i.imgur.com/X9zNSkM.gif',
	'https://cdn.meme.am/cache/instances/folder86/500x/64007086/disaster-girl-push-rejected-rebase-or-merge-git-push-force.jpg',
	'https://www.mememaker.net/static/images/memes/3629643.jpg',
	'http://s.quickmeme.com/img/58/58cdc50dcb04a16438e9759f2db7a0ac23442e718f69af7b2ac0a3761915e92a.jpg',
	'https://i.stack.imgur.com/nEfIm.jpg',
]

bot.onText(/\/💩/,(msg) => {
	const chatId = msg.chat.id;
	bot.sendMessage(chatId, 'Kothaufen');
});

bot.onText(/\/merge/,(msg) => {
	const chatId = msg.chat.id;
	bot.sendMessage(chatId, merge[Math.floor(Math.random()*merge.length)]);
});

var j = schedule.scheduleJob('30 5 * * *', function(){
	downloader('http://wttr.in/'+dailyloc+'.png?1', 'dailywetter.png',function(){
		sendingWeather(config.dailyChatId,dailyloc,__dirname+'/dailywetter.png');
	});
});

var downloader = function(uri, filename, callback){
	request.head(uri, function(err, res, body){
		request(uri).pipe(fs.createWriteStream(filename)).on('finish',callback);
	});
};

bot.onText(/\/weather (.+)/, (msg, input) => {
	const chatId = msg.chat.id;
	var loc = input[1];
	downloader('http://wttr.in/'+loc+'.png?1', 'wetter.png',function(){
		sendingWeather(chatId,loc,__dirname+'/wetter.png');
	});
});

bot.onText(/\/decide (.+)/, (msg, input) => {
	const chatID = msg.chat.id;
	var answer = Math.random() >= 0.5 ? "Yes" : "No";
	bot.sendMessage(chatID, input[1] + " " + answer);
});

bot.onText(/\/marius/, (msg) => {
	const chatID = msg.chat.id;
	bot.sendMessage(chatID, "https://i.giphy.com/media/l2YWsiql5xGPIbnzy/giphy.gif");
});
bot.onText(/\/slap (.+)/, (msg,input) => {
	const chatID = msg.chat.id;
	bot.sendMessage(chatID, "" + msg.from.first_name + " slaps " + input[1] + " around a bit with a large trout",{parse_mode : "HTML"});
});
function sendingWeather(chatId,loc, photo){
	bot.sendPhoto(chatId, photo, {caption: "Todays weather forecast for: "+loc});
};

bot.onText(/\/hype/, (msg) =>{
	bot.sendMessage(msg.chat.id, msg.from.first_name+' started a HYPE-Train!');
	bot.sendPhoto(msg.chat.id, 'http://imgur.com/Ibx2NJs');
});

var geste = [
	'Schere',
	'Stein',
	'Papier',
	]

bot.onText(/\/schere/, (msg) =>{
	let geste = geste[Math.floor(Math.random()*geste.length)];
	if (geste == 'Schere') {
    		bot.sendMessage(msg.chat.id, 'Schere, '+msg.from.first_name+' hat ein Unentschieden erzielt');
	} else if (geste == 'Stein') {
    		bot.sendMessage(msg.chat.id, 'Stein, '+msg.from.first_name+' hat verloren');
	} else {
		bot.sendMessage(msg.chat.id, 'Papier, '+msg.from.first_name+' hat GEWONNEN');
	}
});

bot.onText(/\/stein/, (msg) =>{
	let geste = geste[Math.floor(Math.random()*geste.length)];
	if (geste == 'Schere') {
    		bot.sendMessage(msg.chat.id, 'Schere, '+msg.from.first_name+' hat GEWONNEN');
	} else if (geste == 'Stein') {
    		bot.sendMessage(msg.chat.id, 'Stein, '+msg.from.first_name+' hat ein Unentschieden erzielt');
	} else {
		bot.sendMessage(msg.chat.id, 'Papier, '+msg.from.first_name+' hat verloren');
	}
});

bot.onText(/\/papier/, (msg) =>{
	let geste = geste[Math.floor(Math.random()*geste.length)];
	if (geste == 'Schere') {
    		bot.sendMessage(msg.chat.id, 'Schere, '+msg.from.first_name+' hat verloren');
	} else if (geste == 'Stein') {
    		bot.sendMessage(msg.chat.id, 'Stein, '+msg.from.first_name+' hat GEWONNEN');
	} else {
		bot.sendMessage(msg.chat.id, 'Papier, '+msg.from.first_name+' hat ein Unentschieden erzielt');
	}
});

var userdieichkenne = [
	'Laumi',
	'xAndy',
	'Hahniel',
	'Fabian',
	'Anna',
	'Marius',
	'Florian',
	'Michael',
	'Manuel',
	'Konstantin',
	'anon-kun',
	'Dennis',
	'Jonas nicht-M.',
	'Jonas M.',
	'Christian',
	'M3T4',
	'ShipIt-Bot',
	'Darkpilot',
]
bot.onText(/\/wer/, (msg) => {
	let name = userdieichkenne[Math.floor(Math.random()*userdieichkenne.length)];
	bot.sendMessage(msg.chat.id, name+' '+input[1]);
});
<commit_msg>Alles gefixt<commit_after>const TelegramBot = require('node-telegram-bot-api');
const request = require('request');
const schedule = require('node-schedule');
const fs = require('fs');
var config = require('./config');

const token = config.bottoken;
const bot = new TelegramBot(token, {polling: true})
var dailyloc = config.dailyLocation;

bot.onText(/\/greet (.+)/, (msg, input) => {
	const chatId = msg.chat.id;
	var blub = input[1];
	var resp ='Hello, '+blub;

	bot.sendMessage(chatId, resp);
});

var loremSettings = {
	url: 'http://lorempixel.com/400/200',
	encoding: null
};

bot.onText(/\/lorem/||/\/lorem (.*)/, (msg) =>{
	const chatId = msg.chat.id;
	request(loremSettings, function (error, response, buffer) {
		if (!error && response.statusCode == 200){
			bot.sendPhoto(chatId, buffer);
		}
	})
});

var doit = [
	'http://i.imgur.com/RPbK0fZ.png',
	'http://i.imgur.com/TgUQfNI.jpg',
	'http://i.imgur.com/WSm116p.jpg',
]

bot.onText(/\/doit/,(msg) =>{
	const chatId = msg.chat.id;
	bot.sendPhoto(chatId, doit[Math.floor(Math.random()*doit.length)]);
});

var ship = [
	'http://i.imgur.com/b7bzy0C.png',
	'http://i.imgur.com/hse69ui.jpg',
	'http://i.imgur.com/pyjpvhM.gif',
	'http://i.imgur.com/66j50Xz.jpg',
	'http://i.imgur.com/tCiQSlg.jpg',
	'http://i.imgur.com/DDLLoL8.gif',
	'http://i.imgur.com/LDzcDY3.jpg',
	'http://i.imgur.com/nkeMpLe.jpg',
	'http://i.imgur.com/ACaARFw.jpg',
	'http://i.imgur.com/aJkhUrE.jpg',
	'http://i.imgur.com/YBdJsaS.jpg',
	'http://i.imgur.com/KmZrtgd.jpg',
	'http://i.imgur.com/rmgCY2Q.jpg',
	'http://i.imgur.com/g00lVs9.gif',
	'http://i.imgur.com/LIIXWmD.png',
	'http://i.imgur.com/c6K9rP3.jpg',
	'http://i.imgur.com/2M8iyPt.jpg',
	'http://i.imgur.com/7XxnrTu.jpg',
	'http://i.imgur.com/AJN04yl.jpg',
	'http://i.imgur.com/rcIMpM0.gif',
	'http://i.imgur.com/60u8kye.jpg',
	'http://i.imgur.com/yUypmaC.jpg',
	'http://i.imgur.com/IflZleY.jpg',
	'http://i.imgur.com/h3mlvCq.png',
	'http://i.imgur.com/dXzZETc.png',
	'https://media.giphy.com/media/143vPc6b08locw/giphy.gif',
	'https://media0.giphy.com/media/14tFXhhNurdGp2/giphy.gif',
	'https://media4.giphy.com/media/ta83CqOoRwfwQ/giphy.gif',
	'https://media2.giphy.com/media/tDafHUBVrRKtq/giphy.gif',
	'https://media2.giphy.com/media/tDafHUBVrRKtq/giphy.gif',
]

bot.onText(/\/ship/,(msg) =>{
	const chatId = msg.chat.id;
	bot.sendMessage(chatId, ship[Math.floor(Math.random()*ship.length)]);
});

var editions = [
	{short:'an', count:92},
	{short:'lg', count:310},
	{short:'dk', count:119},
	{short:'fe', count:187},
	{short:'hl', count:140},
	{short:'mr', count:350},
	{short:'vi', count:167},
	{short:'wl', count:167},
	{short:'tp', count:350},
	{short:'sh', count:167},
	{short:'ex', count:167},
	{short:'us', count:350},
	{short:'ul', count:143},
	{short:'ud', count:143},
	{short:'mm', count:350},
	{short:'ne', count:143},
	{short:'pr', count:143},
	{short:'in', count:355},
	{short:'ps', count:146},
	{short:'ap', count:148},
	{short:'od', count:350},
	{short:'tr', count:143},
	{short:'ju', count:143},
	{short:'on', count:350},
	{short:'le', count:145},
	{short:'sc', count:143},
	{short:'be', count:302},
	{short:'ug', count:88},
	{short:'po2', count:165},
	{short:'po', count:222},
	{short:'p3k', count:180},
	{short:'ced', count:302},
	{short:'cedi', count:302},
]

bot.onText(/\/draw/, (msg) =>{
	const chatId = msg.chat.id;
	let edition = editions[Math.floor(Math.random()*editions.length)];
	bot.sendMessage(chatId,`http://magiccards.info/scans/en/${edition.short}/${Math.floor(Math.random()*edition.count)}.jpg`);
});

var merge = [
	'https://i.imgur.com/X9zNSkM.gif',
	'https://cdn.meme.am/cache/instances/folder86/500x/64007086/disaster-girl-push-rejected-rebase-or-merge-git-push-force.jpg',
	'https://www.mememaker.net/static/images/memes/3629643.jpg',
	'http://s.quickmeme.com/img/58/58cdc50dcb04a16438e9759f2db7a0ac23442e718f69af7b2ac0a3761915e92a.jpg',
	'https://i.stack.imgur.com/nEfIm.jpg',
]

bot.onText(/\/💩/,(msg) => {
	const chatId = msg.chat.id;
	bot.sendMessage(chatId, 'Kothaufen');
});

bot.onText(/\/merge/,(msg) => {
	const chatId = msg.chat.id;
	bot.sendMessage(chatId, merge[Math.floor(Math.random()*merge.length)]);
});

var j = schedule.scheduleJob('30 5 * * *', function(){
	downloader('http://wttr.in/'+dailyloc+'.png?1', 'dailywetter.png',function(){
		sendingWeather(config.dailyChatId,dailyloc,__dirname+'/dailywetter.png');
	});
});

var downloader = function(uri, filename, callback){
	request.head(uri, function(err, res, body){
		request(uri).pipe(fs.createWriteStream(filename)).on('finish',callback);
	});
};

bot.onText(/\/weather (.+)/, (msg, input) => {
	const chatId = msg.chat.id;
	var loc = input[1];
	downloader('http://wttr.in/'+loc+'.png?1', 'wetter.png',function(){
		sendingWeather(chatId,loc,__dirname+'/wetter.png');
	});
});

bot.onText(/\/decide (.+)/, (msg, input) => {
	const chatID = msg.chat.id;
	var answer = Math.random() >= 0.5 ? "Yes" : "No";
	bot.sendMessage(chatID, input[1] + " " + answer);
});

bot.onText(/\/marius/, (msg) => {
	const chatID = msg.chat.id;
	bot.sendMessage(chatID, "https://i.giphy.com/media/l2YWsiql5xGPIbnzy/giphy.gif");
});
bot.onText(/\/slap (.+)/, (msg,input) => {
	const chatID = msg.chat.id;
	bot.sendMessage(chatID, "" + msg.from.first_name + " slaps " + input[1] + " around a bit with a large trout",{parse_mode : "HTML"});
});
function sendingWeather(chatId,loc, photo){
	bot.sendPhoto(chatId, photo, {caption: "Todays weather forecast for: "+loc});
};

bot.onText(/\/hype/, (msg) =>{
	bot.sendMessage(msg.chat.id, msg.from.first_name+' started a HYPE-Train!');
	bot.sendPhoto(msg.chat.id, 'http://imgur.com/Ibx2NJs');
});

var geste = [
	'Schere',
	'Stein',
	'Papier',
]

bot.onText(/\/schere/, (msg) =>{
	let gestenzeichen = geste[Math.floor(Math.random()*geste.length)];
	if (gestenzeichen == 'Schere') {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht eine Schere, '+msg.from.first_name+' hat ein Unentschieden erzielt');
	} else if (gestenzeichen == 'Stein') {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht einen Stein, '+msg.from.first_name+' hat verloren');
	} else {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht Papier, '+msg.from.first_name+' hat GEWONNEN');
	}
});

bot.onText(/\/stein/, (msg) =>{
	let gestenzeichen = geste[Math.floor(Math.random()*geste.length)];
	if (gestenzeichen == 'Schere') {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht eine Schere, '+msg.from.first_name+' hat GEWONNEN');
	} else if (gestenzeichen == 'Stein') {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht einen Stein, '+msg.from.first_name+' hat ein Unentschieden erzielt');
	} else {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht Papier, '+msg.from.first_name+' hat verloren');
	}
});

bot.onText(/\/papier/, (msg) =>{
	let gestenzeichen = geste[Math.floor(Math.random()*geste.length)];
	if (gestenzeichen == 'Schere') {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht eine Schere, '+msg.from.first_name+' hat verloren');
	} else if (gestenzeichen == 'Stein') {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht einen Stein, '+msg.from.first_name+' hat GEWONNEN');
	} else {
		bot.sendMessage(msg.chat.id, 'ShipIt-Bot macht Papier, '+msg.from.first_name+' hat ein Unentschieden erzielt');
	}
});

var userdieichkenne =[
	'Laumi',
	'xAndy',
	'Hahniel',
	'Fabian',
	'Anna',
	'Marius',
	'Florian',
	'Michael',
	'Manuel',
	'Konstantin',
	'anon-kun',
	'Dennis',
	'Jonas nicht-M.',
	'Jonas M.',
	'Christian',
	'M3T4',
	'ShipIt-Bot',
	'Darkpilot',
]
bot.onText(/\/wer (.+)/, (msg, input) =>{
	let usernamedenichkenne = userdieichkenne[Math.floor(Math.random()*userdieichkenne.length)];
	bot.sendMessage(msg.chat.id, ''+usernamedenichkenne+' '+input[1]);
});
<|endoftext|>


<commit_before>var async = require("async"),
	AWS = require("aws-sdk"),
	fs = require("fs"),
	im = require("gm").subClass({imageMagick: true}),
	s3 = new AWS.S3(),
	spawn = require('child_process').spawn,
	temp = require('temp');
	piexif = require("piexifjs");

//temp.track();

const FORMAT_JPEG = "jpeg";
const FORMAT_PNG = "png";

var CONFIG = require("./config.json");

function getImageType(objectContentType) {
	if (objectContentType === "image/jpeg") {
		return FORMAT_JPEG;
	} else if (objectContentType === "image/png") {
		return FORMAT_PNG;
	} else {
		throw new Error("unsupported objectContentType " + objectContentType);
	}
}

function cross(left, right) {
	var res = [];
	left.forEach(function(l) {
		right.forEach(function(r) {
			res.push([l, r]);
		});
	});
	return res;
}

/**
 * remove double quoutes at the beginning and the end of an etag
 *
 * @param $etag
 * @returns {void|XML|string|*}
 */
function cleanETag($etag) {
	return $etag.replace(/"/g, "");
}

exports.handler = function(event, context) {
	var defaults =  {
			bucketOriginals : "",
			bucketThumbnails : "",
			autorotate: true,
			sharpImage: true,
			sharpOriginalImage: false,
			doWatermark: false,
			watermark : {
				src : ""
			},
			sizes: [],
			folder: "",
			files: [],
			concurrency: 1,
			savelocal: false
		},
		settings = Object.assign({}, defaults, event),
		resultETags = {original: {}, thumbnails: {}};

	console.log(JSON.stringify(settings, null, 2));

	async.mapLimit(settings.files, settings.concurrency,
		function(file, cb) {

			async.waterfall(
				[
					function(callback) {
						s3.getObject({
								"Bucket": settings.bucketOriginals,
								"Key": settings.folder + '/' + file
							}, function(err, dataS3) {
								callback(err, dataS3);
							}
						);
					},
					function(dataS3, callback) {
						var img = im(dataS3.Body);
						img.identify(function(err, identifyData){
							callback(err, img, dataS3, identifyData);
						});
					},
					function(image, dataS3, identifyData, callback) {
						// save the ICC profile
						var iccProfile = {path: temp.path({prefix: file, suffix: ".icc"})},
							convert;

						// fixes strange windows issue with double backslash
						iccProfile['path'] = iccProfile['path'].replace(/\\/g,"\\");

						convert = spawn('convert', [
							"-",
							"-verbose",
							"icc:"+ iccProfile['path']

						]);

						convert.stdin.write(dataS3.Body);
						convert.stdin.end();

						var b1 = [];
						var b2 = [];
						convert.stdout.on('data', function(data){b1.push(data)})
						convert.stderr.on('data', function(data){b2.push(data)})

						convert.on('close', function (code) {

							console.log(Buffer.concat(b1).toString('ascii'));
							console.log(Buffer.concat(b2).toString('ascii'));

							if (code !== 0) {
								callback(null, image, dataS3, identifyData, undefined);
							} else {
								console.log(code, iccProfile['path']);
								callback(null, image, dataS3, identifyData, iccProfile);
							}
						});
					},
					function(image, dataS3, identifyData, iccProfile, callback) {
						var imageType = getImageType(dataS3.ContentType),
							exifBytes = null;

						resultETags.original[file] = cleanETag(dataS3.ETag);
						console.log("Identified the file " + file);

						// save the metadata
						{
							if (imageType === FORMAT_JPEG) {
								var jpeg = dataS3.Body,
									data = jpeg.toString("binary"),
									exifObj = piexif.load(data);

								delete exifObj['thumbnail'];
								exifBytes = piexif.dump(exifObj);

								jpeg = null;
								data = null;
								exifObj = null;
							}
						}

						callback(null, {
							buffer: dataS3.Body,
							file: file,
							width: identifyData.size.width,
							height: identifyData.size.height,
							folder: settings.folder,
							imageType: imageType,
							contentType: dataS3.ContentType,
							jpegExifBytes: exifBytes,
							iccProfile: iccProfile
						});
					}
				],function (err, result) {
					cb(err, result);
				}
			);
		},
		function(err, images){
			if (err) {
				context.fail(err);
				return;
			}

			console.log("Start the resizing process now");

			var resizePairs = cross(settings.sizes, images);
			async.eachLimit(resizePairs, settings.concurrency, function(resizePair, cb) {
				var width = resizePair[0],
					newWidth = width,
				 	image = resizePair[1],
				 	imagepath = `${image.folder}/${image.file}`,
					img;

				img = im(image.buffer);

				if (newWidth>image.width) {
					newWidth = image.width;
				}
				console.log(`Resizing ${imagepath} to size ${newWidth}.`);

				img.resize(newWidth);

				if (settings.autorotate) {
					img.autoOrient();
				}

				if (settings.sharpImage) {
					var doSharp = true;

					if (newWidth >= image.width && !settings.sharpOriginalImage) {
						doSharp = false;
					}

					if (doSharp) {
						img.unsharp(CONFIG.unsharp.radius, CONFIG.unsharp.sigma, CONFIG.unsharp.amount, CONFIG.unsharp.threshold );
						console.log(`Sharping ${imagepath} with size ` + JSON.stringify(CONFIG.unsharp));
					}
				}

				img.strip();

				async.waterfall([
					function(callback) {
						img.toBuffer(image.imageType, function(err, buffer) {
								callback(err, buffer);
						});
					},
					function(buffer, callback) {
						// Watermarking
						if (!settings.doWatermark) {
							callback(null, buffer);
							return;
						}
						console.log("do watermarking now");
						im(buffer)
							.composite(settings.watermark.src)
							.geometry('+100+150')
							.toBuffer(function(err, buffer) {
								callback(err, buffer);
							});
					},
					function(buffer, callback) {

						if (image.iccProfile !== undefined) {

								var commandsOptions = [
									"-",
									"-profile",
									image.iccProfile['path'],
									"-"],
								convert = spawn('convert', commandsOptions),
								newBuffer = [];

							convert.stdio[1].on('data', (data) => {
								newBuffer.push(data);
							});

							convert.stdin.write(buffer);
							convert.stdin.end();

							convert.on('close', function (code) {
								console.log('Finished writing ICC profile for ' + image.file);
								var result = Buffer.concat(newBuffer);

								if (code === 0) {
									callback(null, result);
								} else {
									callback("error code: " + code);
								}
							});
						} else {
							callback(null, buffer);
						}
					},
					function(buffer, callback) {

						var newJpeg = buffer,
							key = `${image.folder}/s${width}/${image.file}`;

						if (image.imageType == FORMAT_JPEG) {
							newData = piexif.insert(image.jpegExifBytes, buffer.toString("binary"));
							newJpeg = new Buffer(newData, "binary");
							newData = null;
						}

						console.log(`Uploading now to ${key}`);
						s3.putObject({
							"Bucket": settings.bucketThumbnails,
							"Key": key,
							"Body": newJpeg,
							"ContentType": image.contentType,
							"ACL": "public-read"
						}, function(err, data) {
							console.log(`Uploading now to ${key} DONE. ${err}`);

							if (!resultETags.thumbnails[image.file]) {
								resultETags.thumbnails[image.file] = {};
							}
							resultETags.thumbnails[image.file][key] = cleanETag(data.ETag);

							if (settings.savelocal) {
								try { fs.mkdirSync(`tmp`);} catch (e) {}
								try { fs.mkdirSync(`tmp/${image.folder}`);} catch (e) {}
								try { fs.mkdirSync(`tmp/${image.folder}/s${width}`);} catch (e) {}

								fs.writeFile(`tmp/${image.folder}/s${width}/${image.file}`, newJpeg, function (err) {
									callback(err);
								});
							} else {
								img = null;
								newJpeg = null;
								callback(err, "Done");
							}
						});

					}
				], function (err, result) {

					cb(err, result);
				});

			}, function(err) {

				if (err) {
					console.log(err);
					context.fail(err);
				} else {
					context.succeed(resultETags);
				}
			});
		}
	);

	/*

	async.mapLimit(event.Records, CONFIG.concurrency, function(record, cb) {
		var originalKey = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));
		s3.getObject({
			"Bucket": record.s3.bucket.name,
			"Key": originalKey
		}, function(err, data) {
			if (err) {
				cb(err);
			} else {
				cb(null, {
					"originalKey": originalKey,
					"contentType": data.ContentType,
					"imageType": getImageType(data.ContentType),
					"buffer": data.Body,
					"record": record
				});
			}
		});
	}, function(err, images) {
		if (err) {
			context.fail(err);
		} else {
			var resizePairs = cross(CONFIG.sizes, images);
			async.eachLimit(resizePairs, CONFIG.concurrency, function(resizePair, cb) {
				var config = resizePair[0];
				var image = resizePair[1];
				im(image.buffer).resize(config).toBuffer(image.imageType, function(err, buffer) {
					if (err) {
						cb(err);
					} else {
						s3.putObject({
							"Bucket": image.record.s3.bucket.name.replace("-original", "-resized"),
							"Key": config + "/" + image.originalKey,
							"Body": buffer,
							"ContentType": image.contentType
						}, function(err) {
							cb(err);
						});
					}
				});
			}, function(err) {
				if (err) {
					context.fail(err);
				} else {
					context.succeed();
				}
			});
		}
	});
	*/

};<commit_msg>added watermark option to the API<commit_after>var async = require("async"),
	AWS = require("aws-sdk"),
	fs = require("fs"),
	im = require("gm").subClass({imageMagick: true}),
	s3 = new AWS.S3(),
	spawn = require('child_process').spawn,
	temp = require('temp');
	piexif = require("piexifjs");

//temp.track();

const FORMAT_JPEG = "jpeg";
const FORMAT_PNG = "png";

var CONFIG = require("./config.json");

function getImageType(objectContentType) {
	if (objectContentType === "image/jpeg") {
		return FORMAT_JPEG;
	} else if (objectContentType === "image/png") {
		return FORMAT_PNG;
	} else {
		throw new Error("unsupported objectContentType " + objectContentType);
	}
}

function cross(left, right) {
	var res = [];
	left.forEach(function(l) {
		right.forEach(function(r) {
			res.push([l, r]);
		});
	});
	return res;
}

/**
 * remove double quoutes at the beginning and the end of an etag
 *
 * @param $etag
 * @returns {void|XML|string|*}
 */
function cleanETag($etag) {
	return $etag.replace(/"/g, "");
}

exports.handler = function(event, context) {
	var defaults =  {
			bucketOriginals : "",
			bucketThumbnails : "",
			autorotate: true,
			sharpImage: true,
			sharpOriginalImage: false,
			doWatermark: false,
			watermark : {
				src : "",
				margin : {
					horizontal: 0,
					vertical: 0
				},
				opacity: 0,
				position: "mc", //top middle bottom, left center right
				mode: "prop", //prop, fill, fit
				mode_prop: 50, // 50% of the image
				thumbnailThreshold: 0 // don't apply a watermarkt to images smaller than this value
			},
			sizes: [],
			folder: "",
			files: [],
			concurrency: 1,
			savelocal: false
		},
		settings = Object.assign({}, defaults, event),
		resultETags = {original: {}, thumbnails: {}};

	console.log(JSON.stringify(settings, null, 2));

	async.mapLimit(settings.files, settings.concurrency,
		function(file, cb) {

			async.waterfall(
				[
					function(callback) {
						s3.getObject({
								"Bucket": settings.bucketOriginals,
								"Key": settings.folder + '/' + file
							}, function(err, dataS3) {
								callback(err, dataS3);
							}
						);
					},
					function(dataS3, callback) {
						var img = im(dataS3.Body);
						img.identify(function(err, identifyData){
							callback(err, img, dataS3, identifyData);
						});
					},
					function(image, dataS3, identifyData, callback) {
						// save the ICC profile
						var iccProfile = {path: temp.path({prefix: file, suffix: ".icc"})},
							convert;

						// fixes strange windows issue with double backslash
						iccProfile['path'] = iccProfile['path'].replace(/\\/g,"\\");

						convert = spawn('convert', [
							"-",
							"-verbose",
							"icc:"+ iccProfile['path']

						]);

						convert.stdin.write(dataS3.Body);
						convert.stdin.end();

						var b1 = [];
						var b2 = [];
						convert.stdout.on('data', function(data){b1.push(data)})
						convert.stderr.on('data', function(data){b2.push(data)})

						convert.on('close', function (code) {

							console.log(Buffer.concat(b1).toString('ascii'));
							console.log(Buffer.concat(b2).toString('ascii'));

							if (code !== 0) {
								callback(null, image, dataS3, identifyData, undefined);
							} else {
								console.log(code, iccProfile['path']);
								callback(null, image, dataS3, identifyData, iccProfile);
							}
						});
					},
					function(image, dataS3, identifyData, iccProfile, callback) {
						var imageType = getImageType(dataS3.ContentType),
							exifBytes = null;

						resultETags.original[file] = cleanETag(dataS3.ETag);
						console.log("Identified the file " + file);

						// save the metadata
						{
							if (imageType === FORMAT_JPEG) {
								var jpeg = dataS3.Body,
									data = jpeg.toString("binary"),
									exifObj = piexif.load(data);

								delete exifObj['thumbnail'];
								exifBytes = piexif.dump(exifObj);

								jpeg = null;
								data = null;
								exifObj = null;
							}
						}

						callback(null, {
							buffer: dataS3.Body,
							file: file,
							width: identifyData.size.width,
							height: identifyData.size.height,
							folder: settings.folder,
							imageType: imageType,
							contentType: dataS3.ContentType,
							jpegExifBytes: exifBytes,
							iccProfile: iccProfile
						});
					}
				],function (err, result) {
					cb(err, result);
				}
			);
		},
		function(err, images){
			if (err) {
				context.fail(err);
				return;
			}

			console.log("Start the resizing process now");

			var resizePairs = cross(settings.sizes, images);
			async.eachLimit(resizePairs, settings.concurrency, function(resizePair, cb) {
				var width = resizePair[0],
					newWidth = width,
				 	image = resizePair[1],
				 	imagepath = `${image.folder}/${image.file}`,
					img;

				img = im(image.buffer);

				if (newWidth>image.width) {
					newWidth = image.width;
				}
				console.log(`Resizing ${imagepath} to size ${newWidth}.`);

				img.resize(newWidth);

				if (settings.autorotate) {
					img.autoOrient();
				}

				if (settings.sharpImage) {
					var doSharp = true;

					if (newWidth >= image.width && !settings.sharpOriginalImage) {
						doSharp = false;
					}

					if (doSharp) {
						img.unsharp(CONFIG.unsharp.radius, CONFIG.unsharp.sigma, CONFIG.unsharp.amount, CONFIG.unsharp.threshold );
						console.log(`Sharping ${imagepath} with size ` + JSON.stringify(CONFIG.unsharp));
					}
				}

				img.strip();

				async.waterfall([
					function(callback) {
						img.toBuffer(image.imageType, function(err, buffer) {
								callback(err, buffer);
						});
					},
					function(buffer, callback) {
						// Watermarking
						if (!settings.doWatermark) {
							callback(null, buffer);
							return;
						}
						console.log("do watermarking now");
						im(buffer)
							.composite(settings.watermark.src)
							.geometry('+100+150')
							.toBuffer(function(err, buffer) {
								callback(err, buffer);
							});
					},
					function(buffer, callback) {

						if (image.iccProfile !== undefined) {

								var commandsOptions = [
									"-",
									"-profile",
									image.iccProfile['path'],
									"-"],
								convert = spawn('convert', commandsOptions),
								newBuffer = [];

							convert.stdio[1].on('data', (data) => {
								newBuffer.push(data);
							});

							convert.stdin.write(buffer);
							convert.stdin.end();

							convert.on('close', function (code) {
								console.log('Finished writing ICC profile for ' + image.file);
								var result = Buffer.concat(newBuffer);

								if (code === 0) {
									callback(null, result);
								} else {
									callback("error code: " + code);
								}
							});
						} else {
							callback(null, buffer);
						}
					},
					function(buffer, callback) {

						var newJpeg = buffer,
							key = `${image.folder}/s${width}/${image.file}`;

						if (image.imageType == FORMAT_JPEG) {
							newData = piexif.insert(image.jpegExifBytes, buffer.toString("binary"));
							newJpeg = new Buffer(newData, "binary");
							newData = null;
						}

						console.log(`Uploading now to ${key}`);
						s3.putObject({
							"Bucket": settings.bucketThumbnails,
							"Key": key,
							"Body": newJpeg,
							"ContentType": image.contentType,
							"ACL": "public-read"
						}, function(err, data) {
							console.log(`Uploading now to ${key} DONE. ${err}`);

							if (!resultETags.thumbnails[image.file]) {
								resultETags.thumbnails[image.file] = {};
							}
							resultETags.thumbnails[image.file][key] = cleanETag(data.ETag);

							if (settings.savelocal) {
								try { fs.mkdirSync(`tmp`);} catch (e) {}
								try { fs.mkdirSync(`tmp/${image.folder}`);} catch (e) {}
								try { fs.mkdirSync(`tmp/${image.folder}/s${width}`);} catch (e) {}

								fs.writeFile(`tmp/${image.folder}/s${width}/${image.file}`, newJpeg, function (err) {
									callback(err);
								});
							} else {
								img = null;
								newJpeg = null;
								callback(err, "Done");
							}
						});

					}
				], function (err, result) {

					cb(err, result);
				});

			}, function(err) {

				if (err) {
					console.log(err);
					context.fail(err);
				} else {
					context.succeed(resultETags);
				}
			});
		}
	);

	/*

	async.mapLimit(event.Records, CONFIG.concurrency, function(record, cb) {
		var originalKey = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));
		s3.getObject({
			"Bucket": record.s3.bucket.name,
			"Key": originalKey
		}, function(err, data) {
			if (err) {
				cb(err);
			} else {
				cb(null, {
					"originalKey": originalKey,
					"contentType": data.ContentType,
					"imageType": getImageType(data.ContentType),
					"buffer": data.Body,
					"record": record
				});
			}
		});
	}, function(err, images) {
		if (err) {
			context.fail(err);
		} else {
			var resizePairs = cross(CONFIG.sizes, images);
			async.eachLimit(resizePairs, CONFIG.concurrency, function(resizePair, cb) {
				var config = resizePair[0];
				var image = resizePair[1];
				im(image.buffer).resize(config).toBuffer(image.imageType, function(err, buffer) {
					if (err) {
						cb(err);
					} else {
						s3.putObject({
							"Bucket": image.record.s3.bucket.name.replace("-original", "-resized"),
							"Key": config + "/" + image.originalKey,
							"Body": buffer,
							"ContentType": image.contentType
						}, function(err) {
							cb(err);
						});
					}
				});
			}, function(err) {
				if (err) {
					context.fail(err);
				} else {
					context.succeed();
				}
			});
		}
	});
	*/

};<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

require('dotenv').config({'path': './.environment'});

const BotKit       = require('botkit');
const storage      = require('botkit-storage-redis');
const Promise      = require('bluebird');
const Path         = require('path');
const File         = Promise.promisifyAll(require('fs'));
const Program      = require('commander');
const GitHubAPI    = require('github');
const Calendar     = require(__dirname + '/lib/google-calendar.js');
const CalendarUtil = require(__dirname + '/lib/google-calendar-util.js');
const CronJob      = require(__dirname + '/lib/cron.js');
const Util         = require(__dirname + '/lib/botkit-util.js');

Program
	.version('1.0.0')
	.option('-d, --debug', 'Botkit debug mode')
	.option('-t, --token [token]', 'Slack Token')
	.option('-c, --channel [channel]', 'Debug info throw channel')
	.option('-g, --github [github_token]', 'GitHub OAuth token')
	.option('-r, --redis [redis_url]', 'Using strage Redis URL')
	.option('-g. --google [path]', 'Google OAuth client secret file')
	.parse(process.argv);

// Main exec code
getGoogleAuthCode().done((auth) => startAobaBot(auth));

// Google OAuth
function getGoogleAuthCode() {
	const GOOGLE_CLIENT_SECRET_PATH = process.env.GOOGLE_CLIENT_SECRET_PATH || Program.google;
	return new Promise((resolve, reject) => {
		if (GOOGLE_CLIENT_SECRET_PATH) {
			let calendar = new Calendar(GOOGLE_CLIENT_SECRET_PATH);
			calendar.auth()
				.then(auth => {
					resolve(auth);
				})
				.catch(err => {
					reject(err);
				});
		}
		else {
			resolve(null);
		}
	});
}

function loadScripts(dirName, botkit, bot) {
	return new Promise((resolve, reject) => {
		File.readdirAsync(Path.join(__dirname, dirName))
			.then(files => {
				files.forEach(file => {
					if (file.match(/\.js$/)) {
						try {
							require(Path.join(__dirname, dirName, file))(botkit, bot);
						}
						catch (err) {
							console.error(err);
						}
					}
				});
			})
			.done(() => {
				return resolve();
			});
	});
}

function startAobaBot(googleAuth) {
	// Redis
	let botkit = BotKit.slackbot({
		'debug': Program.debug,
		'storage': storage({'url': process.env.REDIS_URL || 'redis://localhost:6379'}),
		'google': googleAuth,
		'debug_channel': process.env.DEBUG_INFO_CHANNEL || Program.channel
	});

	// Botkit connection
	const BOTKIT_SLACK_TOKEN = process.env.BOTKIT_SLACK_TOKEN || Program.token;
	if (!BOTKIT_SLACK_TOKEN) {
		console.error('Error: Specify token in environment or command run option `--token [token]`');
		process.exit(1);
	}
	botkit.spawn({
		'token': BOTKIT_SLACK_TOKEN,
	}).startRTM((err, bot, payload) => {
		// Append botkit cron job function
		botkit.cron = new CronJob(bot);

		// Append GitHub api function
		botkit.github = new GitHubAPI();
		const GITHUB_TOKEN = process.env.GITHUB_TOKEN || Program.github;
		if (GITHUB_TOKEN) {
			botkit.github.authenticate({
				type: 'oauth',
				token: GITHUB_TOKEN
			});
		}

		botkit.calendar = new CalendarUtil(botkit.config.google);

		botkit.util = new Util(bot);

		// load script file
		Promise.all([
			botkit.util.channel.getChannelList(),
			loadScripts('scripts', botkit),
			loadScripts('commands', botkit),
			loadScripts('original', botkit)
		])
		.then(() => {
			if (botkit.config.debug_channel !== undefined) {
				bot.say({
					'channel': botkit.util.channel.getChannelID(botkit.config.debug_channel),
					'text': '<!here|@here> おはよう♪'
				});
			}
		});
	});
}
<commit_msg>RTMが死んだ時に再接続させるようにしてみる<commit_after>#!/usr/bin/env node

'use strict';

require('dotenv').config({'path': './.environment'});

const BotKit       = require('botkit');
const storage      = require('botkit-storage-redis');
const Promise      = require('bluebird');
const Path         = require('path');
const File         = Promise.promisifyAll(require('fs'));
const Program      = require('commander');
const GitHubAPI    = require('github');
const Calendar     = require(__dirname + '/lib/google-calendar.js');
const CalendarUtil = require(__dirname + '/lib/google-calendar-util.js');
const CronJob      = require(__dirname + '/lib/cron.js');
const Util         = require(__dirname + '/lib/botkit-util.js');

Program
	.version('1.0.0')
	.option('-d, --debug', 'Botkit debug mode')
	.option('-t, --token [token]', 'Slack Token')
	.option('-c, --channel [channel]', 'Debug info throw channel')
	.option('-g, --github [github_token]', 'GitHub OAuth token')
	.option('-r, --redis [redis_url]', 'Using strage Redis URL')
	.option('-g. --google [path]', 'Google OAuth client secret file')
	.parse(process.argv);

// Main exec code
getGoogleAuthCode().done((auth) => startAobaBot(auth));

// Google OAuth
function getGoogleAuthCode() {
	const GOOGLE_CLIENT_SECRET_PATH = process.env.GOOGLE_CLIENT_SECRET_PATH || Program.google;
	return new Promise((resolve, reject) => {
		if (GOOGLE_CLIENT_SECRET_PATH) {
			let calendar = new Calendar(GOOGLE_CLIENT_SECRET_PATH);
			calendar.auth()
				.then(auth => {
					resolve(auth);
				})
				.catch(err => {
					reject(err);
				});
		}
		else {
			resolve(null);
		}
	});
}

function loadScripts(dirName, botkit, bot) {
	return new Promise((resolve, reject) => {
		File.readdirAsync(Path.join(__dirname, dirName))
			.then(files => {
				files.forEach(file => {
					if (file.match(/\.js$/)) {
						try {
							require(Path.join(__dirname, dirName, file))(botkit, bot);
						}
						catch (err) {
							console.error(err);
						}
					}
				});
			})
			.done(() => {
				return resolve();
			});
	});
}

function startAobaBot(googleAuth) {
	// Redis
	let botkit = BotKit.slackbot({
		'debug': Program.debug,
		'storage': storage({'url': process.env.REDIS_URL || 'redis://localhost:6379'}),
		'google': googleAuth,
		'debug_channel': process.env.DEBUG_INFO_CHANNEL || Program.channel
	});

	// Botkit connection
	const BOTKIT_SLACK_TOKEN = process.env.BOTKIT_SLACK_TOKEN || Program.token;
	if (!BOTKIT_SLACK_TOKEN) {
		console.error('Error: Specify token in environment or command run option `--token [token]`');
		process.exit(1);
	}
	botkit.spawn({
		'token': BOTKIT_SLACK_TOKEN,
	}).startRTM((err, bot, payload) => {
		// Append botkit cron job function
		botkit.cron = new CronJob(bot);

		// Append GitHub api function
		botkit.github = new GitHubAPI();
		const GITHUB_TOKEN = process.env.GITHUB_TOKEN || Program.github;
		if (GITHUB_TOKEN) {
			botkit.github.authenticate({
				type: 'oauth',
				token: GITHUB_TOKEN
			});
		}

		botkit.calendar = new CalendarUtil(botkit.config.google);

		botkit.util = new Util(bot);

		// load script file
		Promise.all([
			botkit.util.channel.getChannelList(),
			loadScripts('scripts', botkit),
			loadScripts('commands', botkit),
			loadScripts('original', botkit)
		])
		.then(() => {
			if (botkit.config.debug_channel !== undefined) {
				bot.say({
					'channel': botkit.util.channel.getChannelID(botkit.config.debug_channel),
					'text': '<!here|@here> おはよう♪'
				});
			}
		});
	});

	// RTM接続が死んだ時に自分自身を呼び出し直す
	botkit.on('rtm_close', (bot, err) => {
		startAobaBot(googleAuth);
	});
}
<|endoftext|>