<commit_before>'use strict';

app.factory('courseService', function ($http, $q, alfredStorage) {
  var base_url = "https://www.coursera.org/maestro/api/topic/list_my?user_id=";

  var getUserId = function(){
    var request = {url: 'https://www.coursera.org/*', name: 'maestro_user'};

    var deferred = Q.defer();
    var user_id;
    if(user_id = alfredStorage.getUserID() ) {
      deferred.resolve(user_id);
    } else {
      chrome.cookies.get(request, function(cookie){
        if(cookie){
          var user = JSON.parse(decodeURIComponent(cookie.value));
          deferred.resolve(user.id);
          alfredStorage.setUserID(user.id)
        } else{
          deferred.resolve();
        }
      });
    }
    return deferred.promise;
  };

  var getAllCourses = function(user_id){
    var url = base_url + user_id;
    return $.get(url)
                .then(function(response){
                  return response;
                })
  };

  var getPages = function(courses){
    var courses_promoises = courses.filter(function(item){
      return item.courses[0].home_link;
    }).map(function(item){
      var deferred = Q.defer();
      item["class_link"] = item.courses[0].home_link;
      item["home_link"] = item["class_link"] + "class/index";
      $.get(item.home_link)
           .then(function(response){
             item["html"] = response;
             deferred.resolve(item);
           });
      return deferred.promise;
    });
    return Q.all(courses_promoises);
  };

  var getEvents = function(pages){
    var events = {
      deadlines: [],
      courses: [],
    };
    var courses = pages.map(function(item){
      var raw_html = item.html;
      var body = '
' +
                 raw_html.replace(/^[\s\S]*|<\/body>[\s\S]*$/g, '') +
                 '
';

      var $sidebar = $(body).find('.course-page-sidebar');

      // deadlines and new lectures are separated by a

      var $hr = $sidebar.find("hr");
      var $deadlineCcontainer = $hr.prevAll('.course-overview-upcoming-category')
      var $newLecturesContainer = $hr.nextAll('.course-overview-upcoming-category')

      var deadlines = $deadlineCcontainer
                      .find('.course-overview-upcoming-item')
                      .toArray();
      var newLectures = $newLecturesContainer
                        .find('.course-overview-upcoming-item')
                        .toArray();

      item["new_lectures"] = newLectures;

      if($sidebar.find(".icon-calendar").length > 0) {
        item["calendar"] = item["class_link"] + "calendar/ics";
      }

      var deadlineObjects = deadlines.map(function(deadline){
        var $deadline = $(deadline);
        var $deadlineItem = $deadline.find('time');

        // remove specific timezone string in date string(like HKT, because Date() can't pase that)
        var date_array = $deadline
                          .find(".course-assignment-deadline")
                          .text()
                          .replace("\n","")
                          .split(' ')
                          .filter(function(item){
                            return item;
                          });
        date_array.splice(date_array.length-2, 1);
        var dateString = date_array.join(" ");
        var object  = {
          "html": $deadline.html(),
          "title": $deadlineItem.data("event-title"),
          "link": $deadlineItem.data("event-location"),
          "description": $deadlineItem.data("event-description"),
          "time": new Date(dateString),
          "course": {
            "name": item.name,
            "icon": item.small_icon
          }
        };
        return object;
      });

      if (deadlineObjects.length > 0) {
        events.deadlines = events.deadlines.concat(deadlineObjects);
      }

      return item;
    }).filter(function(item){
      return item["new_lectures"].length > 0;
    });

    // events.courses = courses;
    return events;
  };

  var getCourses =  function(){
    var deferred = Q.defer();
    getUserId().then(
              getAllCourses
            )
            .then(
              getPages, function(reason){
                deferred.reject(reason);
              }
            )
            .then(function(pages){
              var events = getEvents(pages);
              deferred.resolve(events);
            })
    return deferred.promise;
  }

  var updateBadge = function(){
    var len = alfredStorage.getDeadlines().length - alfredStorage.getRemoved().length;
    chrome.browserAction.setBadgeText({text: len.toString()});
  }

  var updateData = function(){
    getCourses().then(function(events){
      if(events){
        alfredStorage.signIn();
        alfredStorage.setDeadlines(events.deadlines);
        alfredStorage.removeExpiredDeadlines();

        updateBadge();
      }
    }, function(reason){
      alfredStorage.signOut();
    });
  }

  return {
    updateData: updateData,
    updateBadge: updateBadge
  }
});
<commit_msg>fix badge status<commit_after>'use strict';

app.factory('courseService', function ($http, $q, alfredStorage) {
  var base_url = "https://www.coursera.org/maestro/api/topic/list_my?user_id=";

  var getUserId = function(){
    var request = {url: 'https://www.coursera.org/*', name: 'maestro_user'};

    var deferred = Q.defer();
    var user_id;
    if(user_id = alfredStorage.getUserID() ) {
      deferred.resolve(user_id);
    } else {
      chrome.cookies.get(request, function(cookie){
        if(cookie){
          var user = JSON.parse(decodeURIComponent(cookie.value));
          deferred.resolve(user.id);
          alfredStorage.setUserID(user.id)
        } else{
          deferred.resolve();
        }
      });
    }
    return deferred.promise;
  };

  var getAllCourses = function(user_id){
    var url = base_url + user_id;
    return $.get(url)
                .then(function(response){
                  return response;
                })
  };

  var getPages = function(courses){
    var courses_promoises = courses.filter(function(item){
      return item.courses[0].home_link;
    }).map(function(item){
      var deferred = Q.defer();
      item["class_link"] = item.courses[0].home_link;
      item["home_link"] = item["class_link"] + "class/index";
      $.get(item.home_link)
           .then(function(response){
             item["html"] = response;
             deferred.resolve(item);
           });
      return deferred.promise;
    });
    return Q.all(courses_promoises);
  };

  var getEvents = function(pages){
    var events = {
      deadlines: [],
      courses: [],
    };
    var courses = pages.map(function(item){
      var raw_html = item.html;
      var body = '
' +
                 raw_html.replace(/^[\s\S]*|<\/body>[\s\S]*$/g, '') +
                 '
';

      var $sidebar = $(body).find('.course-page-sidebar');

      // deadlines and new lectures are separated by a

      var $hr = $sidebar.find("hr");
      var $deadlineCcontainer = $hr.prevAll('.course-overview-upcoming-category')
      var $newLecturesContainer = $hr.nextAll('.course-overview-upcoming-category')

      var deadlines = $deadlineCcontainer
                      .find('.course-overview-upcoming-item')
                      .toArray();
      var newLectures = $newLecturesContainer
                        .find('.course-overview-upcoming-item')
                        .toArray();

      item["new_lectures"] = newLectures;

      if($sidebar.find(".icon-calendar").length > 0) {
        item["calendar"] = item["class_link"] + "calendar/ics";
      }

      var deadlineObjects = deadlines.map(function(deadline){
        var $deadline = $(deadline);
        var $deadlineItem = $deadline.find('time');

        // remove specific timezone string in date string(like HKT, because Date() can't pase that)
        var date_array = $deadline
                          .find(".course-assignment-deadline")
                          .text()
                          .replace("\n","")
                          .split(' ')
                          .filter(function(item){
                            return item;
                          });
        date_array.splice(date_array.length-2, 1);
        var dateString = date_array.join(" ");
        var object  = {
          "html": $deadline.html(),
          "title": $deadlineItem.data("event-title"),
          "link": $deadlineItem.data("event-location"),
          "description": $deadlineItem.data("event-description"),
          "time": new Date(dateString),
          "course": {
            "name": item.name,
            "icon": item.small_icon
          }
        };
        return object;
      });

      if (deadlineObjects.length > 0) {
        events.deadlines = events.deadlines.concat(deadlineObjects);
      }

      return item;
    }).filter(function(item){
      return item["new_lectures"].length > 0;
    });

    // events.courses = courses;
    return events;
  };

  var getCourses =  function(){
    var deferred = Q.defer();
    getUserId().then(
              getAllCourses
            )
            .then(
              getPages, function(reason){
                deferred.reject(reason);
              }
            )
            .then(function(pages){
              var events = getEvents(pages);
              deferred.resolve(events);
            })
    return deferred.promise;
  }

  var updateBadge = function(){
    var len = alfredStorage.getDeadlines().length - alfredStorage.getRemoved().length;
    chrome.browserAction.setBadgeText({text: len.toString()});
    if(len == 0){
      chrome.browserAction.setBadgeText({text: ''});
    }
  }

  var updateData = function(){
    chrome.browserAction.setBadgeText({text: '...'});
    getCourses().then(function(events){
      if(events){
        alfredStorage.signIn();
        alfredStorage.setDeadlines(events.deadlines);
        alfredStorage.removeExpiredDeadlines();

        updateBadge();
      }
    }, function(reason){
      alfredStorage.signOut();
    });
  }

  return {
    updateData: updateData,
    updateBadge: updateBadge
  }
});
<|endoftext|>


<commit_before>import classNames from 'classnames'
import React from 'react'
import ReactDOM from 'react-dom'

import Component from '../base-component'
import getPosition from './get-position'
import propTypes from '../prop-types'

import styles from './index.css'

// ===================================================================

let instance

export class TooltipViewer extends Component {
  constructor () {
    super()

    if (instance) {
      throw new Error('Tooltip viewer is a singleton!')
    }
    instance = this
    this.state.place = 'top'
  }

  render () {
    const {
      className,
      content,
      place,
      show,
      style
    } = this.state

    return (
      <div
        className={classNames(show ? styles.tooltipEnabled : styles.tooltipDisabled, className)}
        style={{
          marginTop: (place === 'top' && '-10px') || (place === 'bottom' && '10px'),
          marginLeft: (place === 'left' && '-10px') || (place === 'right' && '10px'),
          ...style
        }}
      >
        {content}
      </div>
    )
  }
}

// ===================================================================

@propTypes({
  children: propTypes.any.isRequired,
  className: propTypes.string,
  content: propTypes.any.isRequired,
  style: propTypes.object,
  tagName: propTypes.string
})
export default class Tooltip extends Component {
  componentDidMount () {
    this._addListeners()
  }

  componentWillUnmount () {
    this._removeListeners()
  }

  componentWillReceiveProps (props) {
    if (props.children !== this.props.children) {
      this._removeListeners()
    }
  }

  componentDidUpdate (prevProps) {
    if (prevProps.children !== this.props.children) {
      this._addListeners()
    }
  }

  _addListeners () {
    const node = this._node = ReactDOM.findDOMNode(this)

    node.addEventListener('mouseenter', this._showTooltip)
    node.addEventListener('mouseleave', this._hideTooltip)
    node.addEventListener('mousemove', this._updateTooltip)
  }

  _removeListeners () {
    const node = this._node

    if (!node) {
      return
    }

    node.removeEventListener('mouseenter', this._showTooltip)
    node.removeEventListener('mouseleave', this._hideTooltip)
    node.removeEventListener('mousemove', this._updateTooltip)

    this._node = null
  }

  _showTooltip = () => {
    const { props } = this

    instance.setState({
      className: props.className,
      content: props.content,
      show: true,
      style: props.style
    })
  }

  _hideTooltip = () => {
    instance.setState({ show: false })
  }

  _updateTooltip = event => {
    const node = ReactDOM.findDOMNode(instance)
    const result = getPosition(event, event.currentTarget, node, instance.state.place, 'float', {})

    if (result.isNewState) {
      return instance.setState(result.newState, () => this._updateTooltip(event))
    }

    const { position } = result
    node.style.left = `${position.left}px`
    node.style.top = `${position.top}px`
  }

  render () {
    return this.props.children
  }
}
<commit_msg>feat(tooltip): float → solid, do not follow cursor<commit_after>import classNames from 'classnames'
import React from 'react'
import ReactDOM from 'react-dom'

import Component from '../base-component'
import getPosition from './get-position'
import propTypes from '../prop-types'

import styles from './index.css'

// ===================================================================

let instance

export class TooltipViewer extends Component {
  constructor () {
    super()

    if (instance) {
      throw new Error('Tooltip viewer is a singleton!')
    }
    instance = this
    this.state.place = 'top'
  }

  render () {
    const {
      className,
      content,
      place,
      show,
      style
    } = this.state

    return (
      <div
        className={classNames(show ? styles.tooltipEnabled : styles.tooltipDisabled, className)}
        style={{
          marginTop: (place === 'top' && '-10px') || (place === 'bottom' && '10px'),
          marginLeft: (place === 'left' && '-10px') || (place === 'right' && '10px'),
          ...style
        }}
      >
        {content}
      </div>
    )
  }
}

// ===================================================================

@propTypes({
  children: propTypes.any.isRequired,
  className: propTypes.string,
  content: propTypes.any.isRequired,
  style: propTypes.object,
  tagName: propTypes.string
})
export default class Tooltip extends Component {
  componentDidMount () {
    this._addListeners()
  }

  componentWillUnmount () {
    this._removeListeners()
  }

  componentWillReceiveProps (props) {
    if (props.children !== this.props.children) {
      this._removeListeners()
    }
  }

  componentDidUpdate (prevProps) {
    if (prevProps.children !== this.props.children) {
      this._addListeners()
    }
  }

  _addListeners () {
    const node = this._node = ReactDOM.findDOMNode(this)

    node.addEventListener('mouseenter', this._showTooltip)
    node.addEventListener('mouseleave', this._hideTooltip)
    node.addEventListener('mousemove', this._updateTooltip)
  }

  _removeListeners () {
    const node = this._node

    if (!node) {
      return
    }

    node.removeEventListener('mouseenter', this._showTooltip)
    node.removeEventListener('mouseleave', this._hideTooltip)
    node.removeEventListener('mousemove', this._updateTooltip)

    this._node = null
  }

  _showTooltip = () => {
    const { props } = this

    instance.setState({
      className: props.className,
      content: props.content,
      show: true,
      style: props.style
    })
  }

  _hideTooltip = () => {
    instance.setState({ show: false })
  }

  _updateTooltip = event => {
    const node = ReactDOM.findDOMNode(instance)
    const result = getPosition(event, event.currentTarget, node, instance.state.place, 'solid', {})

    if (result.isNewState) {
      return instance.setState(result.newState, () => this._updateTooltip(event))
    }

    const { position } = result
    node.style.left = `${position.left}px`
    node.style.top = `${position.top}px`
  }

  render () {
    return this.props.children
  }
}
<|endoftext|>


<commit_before>var vows = require('vows');
var assert = require('assert');

var test = require('tape');
var parallel = require('../lib/parallel');

test('Parallel Processor', function (t) {
	t.plan(4);
	var p;

	t.doesNotThrow(function () {
		p = parallel({test: function (cb) { cb(null, 1); }});
	}, undefined, 'must not throw any errors');

	t.ok(p instanceof parallel.ParallelProcessor, 'must instantiate a parallel processor');
	t.ok(typeof p.exec, 'function', 'must expose an exec method');
	t.ok(typeof p.add, 'function', 'must expose an add method');

	test('... when executed', function (t) {
		t.plan(3);
		p.exec(function (e, data) {
			t.equal(e, null, 'must not return an error');
			t.equal(typeof data, 'object', 'must return an object');
			t.equal(data.test, 1, 'must return the correct value');
		})
	});

});
<commit_msg>remove traces of vows<commit_after>var test = require('tape');
var parallel = require('../lib/parallel');

test('Parallel Processor', function (t) {
	t.plan(4);
	var p;

	t.doesNotThrow(function () {
		p = parallel({test: function (cb) { cb(null, 1); }});
	}, undefined, 'must not throw any errors');

	t.ok(p instanceof parallel.ParallelProcessor, 'must instantiate a parallel processor');
	t.ok(typeof p.exec, 'function', 'must expose an exec method');
	t.ok(typeof p.add, 'function', 'must expose an add method');

	test('... when executed', function (t) {
		t.plan(3);
		p.exec(function (e, data) {
			t.equal(e, null, 'must not return an error');
			t.equal(typeof data, 'object', 'must return an object');
			t.equal(data.test, 1, 'must return the correct value');
		})
	});

});
<|endoftext|>


<commit_before>$(document).ready(function() {
  hashtagger(window.location.hash);
	$('a[href^="#"]').click(function() {
		hashtagger(this.hash);
    });
});

function hashtagger (hash) {
  // poor mans tab
	$('.hashtagger').hide();
	$(hash).show();
  // special actions
	switch (hash) {
  case "#contact": // example

    break;
  default:
  case "#":

    break;
	}
}<commit_msg>hashslicer<commit_after>$(document).ready(function() {
  hashtagger(window.location.hash);
  hashslicer(window.location.hash);
  $('a[href^="#"]').click(function() {
		hashtagger(this.hash);
		hashslicer(this.hash);
    });
});

function hashtagger (hash) {
  // poor mans tab
	$('.hashtagger').hide();
	$(hash).show();
  // special actions
	switch (hash) {
  case "#contact": // example

    break;
  default:
  case "#":

    break;
	}
}
function hashslicer (hash) { // prefill "GET parameter"-like value string to inputs
	var gets = hash.slice('1').split('&') ;

	for (var i = 0; i < gets.length; i++) {
		var getkeyparam = gets[i].split('=')
		$('#' + getkeyparam[0]).val( getkeyparam[1] )
	}
}<|endoftext|>


<commit_before>/*******************************************
* js-cgi.js
* Copyright (c) 2015, Darrel Kathan
* Licensed under the MIT license.
*
* A current version and some documentation is available at
*    https://github.com/kathan/js-cgi
*
* @summary     Javascript CGI process manager
* @description js-cgi is a javascript CGI process manager, similar to php-fpm, for executing node.js compatible scripts behind NGINX or Apache.
* @file        js-cgi.js
* @version     1.1.1
* @author      Darrel Kathan
* @license     MIT
* 2/16/16 - Added cluster node cache
* 7/6/16 - Added script timeout override
* 2/16/17 - Added middleware option
* 4/13/17 - Major timeout bug fix
* 11/3/17 - Added cgi function
* 11/29/17 - Added path_info
*******************************************/
"use strict";

var cluster = require("cluster"),
  _ = require("lodash"),
  url = require("url"),
  fs = require("fs"),
  vm = require("vm"),
  os = require("os"),
  util = require("util"),
  path = require("path"),
  express = require("express"),
  app = express(),
  config_name = "js-cgi.config",
  use_name = "use.js",
  config = {};
/******************************************
* Capture console.log and console.error
* input to log file.
*******************************************/
var console_log = console.log,
  console_error = console.error;

console.log = function() {
  var d = new Date(),
    log_msg = d.toString() + " - (" + process.pid + "): ";
  if (config.output_log) {
    fs.appendFile(
      config.output_log,
      log_msg + util.format.apply(this, arguments) + "\n",
      function() {
        return;
      }
    );
  }
  return console_log.apply(this, arguments);
};

console.error = function(err) {
  var d = new Date(),
    log_msg = d.toString() + " - (" + process.pid + "): ";

  if (config.error_log) {
    fs.appendFile(
      config.error_log,
      log_msg + util.format.apply(this, arguments) + "\n",
      function() {
        return;
      }
    );
    if (err && err.stack) {
      fs.appendFile(
        config.error_log,
        err.stack + util.format.apply(this, arguments) + "\n",
        function() {
          return;
        }
      );
    }
  }
  return console_error.apply(this, arguments);
};

/*******************************************
* Prepare web server to handle cookies and
* format JSON.
*******************************************/

app.set("json spaces", 2);

//console.log(config.output_log);
if (fs.existsSync(path.join(__dirname, config_name))) {
  //Load the congfig file
  console.log("Loading " + config_name + "...");
  config = require("./" + config_name);
}

if (fs.existsSync(path.join(__dirname, use_name))) {
  //Load the congfig file
  console.log("Loading " + use_name + "...");
  var mid_ware = require("./" + use_name);
  if (typeof mid_ware === "function") {
    mid_ware(app);
  } else {
    console.error("Use file is malformed.");
  }
}

//Set default config items
!config.output_log
  ? (config.output_log = path.dirname(process.argv[1]) + "/js-cgi.log")
  : "";
!config.error_log
  ? (config.error_log = path.dirname(process.argv[1]) + "/err.log")
  : "";
!config.port ? (config.port = 3000) : "";
!config.localhostOnly ? (config.localhostOnly = true) : "";
!config.timeout ? (config.timeout = 30000) : "";
!config.workers ? (config.workers = os.cpus().length - 1) : ""; //Subtract one for the master
!config.watch_required ? (config.watch_required = false) : "";

/*******************************************
* Override the "require" function to watch
* if required files have change and expire
* them from cache when they do.
*******************************************/
var assert = require("assert").ok,
  Module = require("module");
if (typeof Module._watching !== "object") {
  Module._watching = {};
}
function watchRequired(fn) {
  /*var fs = require('fs')
    console = console;*/
  //console.log(fn);
  if (Module._watching && !Module._watching.hasOwnProperty(fn)) {
    if (fs.existsSync(fn) && !Module._watching.hasOwnProperty(fn)) {
      console.log("Watching " + fn);
      Module._watching[fn] = fs.watch(fn, (event, f) => {
        //Module._watching[fn] = fs.watchFile(fn, function(curr, prev){
        console.log(fn, event);

        if (Module._cache.hasOwnProperty(fn)) {
          console.log("Expiring " + fn);
          //fs.unwatch(fn, function(){return;});
          return delete Module._cache[fn];
        }
        return;
      });
    }
  }
}

Module.prototype.require = function(path) {
  assert(path, "missing path");
  assert(typeof path === "string", "path must be a string");
  if (config.watch_required) {
    watchRequired(Module._resolveFilename(path, this));
  }
  return Module._load(path, this);
};

/*******************************************
* Start up the server node.
*******************************************/
if (cluster.isMaster) {
  cluster.globals = {};
  cluster.fork().on("error", console.error); //At least one worker is required.
  console.log(process.versions);
  for (var w = 2; w <= config.workers; w++) {
    cluster.fork().on("error", console.error);
  }

  cluster.on("disconnect", worker => {
    cluster.fork().on("error", console.error);
  });

  cluster.on("listening", (worker, address) => {
    console.log("A worker is now connected to " + JSON.stringify(address));
  });
} else {
  // the worker
  var domain = require("domain"),
    server,
    d = domain.create();

  d.on("error", err => {
    console.error("Domain Error:", err, err.stack);
    //console.trace();
    try {
      // make sure we close down if it times out
      //var killtimer = setTimeout(() => {
      //var err = new Error('Killing process.');

      //console.error('Domain Error:', err);
      //console.trace();
      console.log("Killing process.");
      process.exit(1);
      //}, config.timeout);

      // But don't keep the process open just for that!
      //killtimer.unref();

      // stop taking new requests.
      server.close();

      // Let the master know we're dead.  This will trigger a
      // 'disconnect' in the cluster master, and then it will fork
      // a new worker.
      cluster.worker.disconnect();
    } catch (er2) {
      // oh well, not much we can do at this point.
      console.error(er2);
    }
  });

  /*******************************************
    * Now run the handler function in the domain.
    *******************************************/
  d.run(() => {
    console.log("Listening on " + config.port);

    server = app.listen(config.port);

    app.all("*", (req, res) => {
      //console.log('req');
      return handleRequest(req, res);
    });
  });
}

/*******************************************
* This is where all of the web requests are
* handled.
*******************************************/
function handleRequest(req, res) {
  //console.log('request:', req);
  var url_obj = url.parse(req.url, true),
      file_path;
  console.log(req.headers);
  /*var cache = [],
    j = JSON.stringify(req, function(key, value) {

      if (typeof value === 'object' && value !== null) {
        var idx = _.findIndex(cache, value);
        if (idx > -1) {return '[Circular '+idx+']';}
        cache.push(value);
      }
    return value;
  });

  res.send(j);*/
  //console.log('req.connection.remoteFamily:', '"'+req.connection.remoteFamily+'"');
  //console.log('req.connection.remoteAddress:', '"'+req.connection.remoteAddress+'"');
  if (config.localhostOnly) {
    if (
      (req.connection.remoteFamily === "IPv4" &&
        req.connection.remoteAddress !== "127.0.0.1") ||
      (req.connection.remoteFamily === "IPv6" &&
        (!_.endsWith(req.connection.remoteAddress, "::1") &&
          !_.endsWith(req.connection.remoteAddress, ":127.0.0.1")))
    ) {
      return res.sendStatus(401);
    }
  }

  //Set to the directory path
  if (!req.headers.path_translated) {
    req.headers.path_translated = __dirname + "/www";
    //console.log(req.headers.path_translated);
    if (!fs.existsSync(req.headers.path_translated)) {
      fs.mkdirSync(req.headers.path_translated);
    }
  }

  //
  if(req.headers.path_translated && req.headers.script_name)
    file_path = req.headers.path_translated + req.headers.script_name;
  else if (req.headers.path_translated && url_obj.pathname) {
    file_path = req.headers.path_translated + url_obj.pathname;
  } else {
    file_path = url_obj.pathname;
  }
  console.log(req.method + ": " + file_path);
  function resolveModule(module) {
    if (module.charAt(0) !== ".") {
      return module;
    }
    return path.resolve(path.dirname(file_path), module);
  }

  //If the requested file exists...
  fs.exists(file_path, exists => {
    if (exists) {
      fs.readFile(file_path, (err, source) => {
        if (err) {
          //Error reading file
          console.error(err);
          return res
            .status(500)
            .send({ error: err.toString(), stack: err.stack });
        }
        try {
          //console.log(require.cache);
          //var timer = 'not set';
          req.timeout = config.timeout;
          var timer,
              sandbox = {
                console: console,
                setImmediate: setImmediate,
                setInterval: setInterval,
                setTimeout: setTimeout,
                clearTimeout: clearTimeout,
                Buffer: Buffer,
                JSON: JSON,
                require: function(name) {
                  var mod_path = resolveModule(name);
                  //watchRequired(mod_path);
                  return require(mod_path);
                },
                req: req,
                res: res,
                process: process,
                __dirname: path.dirname(file_path)
              },
              setKilltimer = function() {
                //!req.timeout ? (req.timeout = config.timeout) : "";
                var to_start = Date.now();
                var timeout = req.timeout;
                if (timeout > 0) {
                  return setTimeout(() => {
                    var to_end = Date.now(),
                        msg =
                          "Timeout expired (" +
                          (to_end - to_start) +
                          " ms) for " +
                          file_path,
                        err;

                    //if(!res.headerSent){
                    res.status(408).send(msg);
                    //}
                    //err = new Error(msg);
                    console.error(msg);
                    return;
                  }, req.timeout);
                }
              };
          /****************************
          * Capture "res.send" and
          * "res.sendFile" calls to
          * be able to unset timeout
          * when the script ends. */
          var send = res.send;
          res.send = function(){
            if(timer){
              console.log('timeout cleared');
              clearTimeout(timer);
            }
            return send.apply(this, arguments);
          };
          var sendFile = res.sendFile;
          res.sendFile = function(){
            if(timer){
              console.log('timeout cleared');
              clearTimeout(timer);
            }
            return sendFile.apply(this, arguments);
          }
          /***************************/

          /*****************
           * Start VM Code *
           *****************/
          var c = vm.createContext(sandbox),
              code = `function runInContext() {try{${source}                          //0
                          if(typeof cgi === 'function'){                              //1
                            cgi(req, res);                                            //2
                          }                                                           //3
                        }catch(e){                                                    //4
                          console.error(e);                                           //5
                          res.status(500).send({error: e.toString(), stack: e.stack});//6
                        }                                                             //7
                      }                                                               //8
                                                                                //9
                      runInContext();`;
          /***************
          * End VM Code *
          ***************/

          /***************************************************
           * Set the kill timer after executing the script so
           * the script can set the timeout duration.
           ***************************************************/
          var result = vm.runInContext(code, c, file_path);
          timer = setKilltimer();
          return result;
        } catch (err) {
          console.error(err);
          return res
            .status(500)
            .send({ error: err.toString(), stack: err.stack });
        }
      });
    } else {
      //File does not exist
      return res.status(404).send("File not found. " + file_path);
    }
  });
}
<commit_msg>Added __filename<commit_after>/*******************************************
* js-cgi.js
* Copyright (c) 2015, Darrel Kathan
* Licensed under the MIT license.
*
* A current version and some documentation is available at
*    https://github.com/kathan/js-cgi
*
* @summary     Javascript CGI process manager
* @description js-cgi is a javascript CGI process manager, similar to php-fpm, for executing node.js compatible scripts behind NGINX or Apache.
* @file        js-cgi.js
* @version     1.1.1
* @author      Darrel Kathan
* @license     MIT
* 2/16/16 - Added cluster node cache
* 7/6/16 - Added script timeout override
* 2/16/17 - Added middleware option
* 4/13/17 - Major timeout bug fix
* 11/3/17 - Added cgi function
* 11/29/17 - Added path_info
*******************************************/
"use strict";

var cluster = require("cluster"),
  _ = require("lodash"),
  url = require("url"),
  fs = require("fs"),
  vm = require("vm"),
  os = require("os"),
  util = require("util"),
  path = require("path"),
  express = require("express"),
  app = express(),
  config_name = "js-cgi.config",
  use_name = "use.js",
  config = {};
/******************************************
* Capture console.log and console.error
* input to log file.
*******************************************/
var console_log = console.log,
  console_error = console.error;

console.log = function() {
  var d = new Date(),
    log_msg = d.toString() + " - (" + process.pid + "): ";
  if (config.output_log) {
    fs.appendFile(
      config.output_log,
      log_msg + util.format.apply(this, arguments) + "\n",
      function() {
        return;
      }
    );
  }
  return console_log.apply(this, arguments);
};

console.error = function(err) {
  var d = new Date(),
    log_msg = d.toString() + " - (" + process.pid + "): ";

  if (config.error_log) {
    fs.appendFile(
      config.error_log,
      log_msg + util.format.apply(this, arguments) + "\n",
      function() {
        return;
      }
    );
    if (err && err.stack) {
      fs.appendFile(
        config.error_log,
        err.stack + util.format.apply(this, arguments) + "\n",
        function() {
          return;
        }
      );
    }
  }
  return console_error.apply(this, arguments);
};

/*******************************************
* Prepare web server to handle cookies and
* format JSON.
*******************************************/

app.set("json spaces", 2);

//console.log(config.output_log);
if (fs.existsSync(path.join(__dirname, config_name))) {
  //Load the congfig file
  console.log("Loading " + config_name + "...");
  config = require("./" + config_name);
}

if (fs.existsSync(path.join(__dirname, use_name))) {
  //Load the congfig file
  console.log("Loading " + use_name + "...");
  var mid_ware = require("./" + use_name);
  if (typeof mid_ware === "function") {
    mid_ware(app);
  } else {
    console.error("Use file is malformed.");
  }
}

//Set default config items
!config.output_log
  ? (config.output_log = path.dirname(process.argv[1]) + "/js-cgi.log")
  : "";
!config.error_log
  ? (config.error_log = path.dirname(process.argv[1]) + "/err.log")
  : "";
!config.port ? (config.port = 3000) : "";
!config.localhostOnly ? (config.localhostOnly = true) : "";
!config.timeout ? (config.timeout = 30000) : "";
!config.workers ? (config.workers = os.cpus().length - 1) : ""; //Subtract one for the master
!config.watch_required ? (config.watch_required = false) : "";

/*******************************************
* Override the "require" function to watch
* if required files have change and expire
* them from cache when they do.
*******************************************/
var assert = require("assert").ok,
  Module = require("module");
if (typeof Module._watching !== "object") {
  Module._watching = {};
}
function watchRequired(fn) {
  /*var fs = require('fs')
    console = console;*/
  //console.log(fn);
  if (Module._watching && !Module._watching.hasOwnProperty(fn)) {
    if (fs.existsSync(fn) && !Module._watching.hasOwnProperty(fn)) {
      console.log("Watching " + fn);
      Module._watching[fn] = fs.watch(fn, (event, f) => {
        //Module._watching[fn] = fs.watchFile(fn, function(curr, prev){
        console.log(fn, event);

        if (Module._cache.hasOwnProperty(fn)) {
          console.log("Expiring " + fn);
          //fs.unwatch(fn, function(){return;});
          return delete Module._cache[fn];
        }
        return;
      });
    }
  }
}

Module.prototype.require = function(path) {
  assert(path, "missing path");
  assert(typeof path === "string", "path must be a string");
  if (config.watch_required) {
    watchRequired(Module._resolveFilename(path, this));
  }
  return Module._load(path, this);
};

/*******************************************
* Start up the server node.
*******************************************/
if (cluster.isMaster) {
  cluster.globals = {};
  cluster.fork().on("error", console.error); //At least one worker is required.
  console.log(process.versions);
  for (var w = 2; w <= config.workers; w++) {
    cluster.fork().on("error", console.error);
  }

  cluster.on("disconnect", worker => {
    cluster.fork().on("error", console.error);
  });

  cluster.on("listening", (worker, address) => {
    console.log("A worker is now connected to " + JSON.stringify(address));
  });
} else {
  // the worker
  var domain = require("domain"),
    server,
    d = domain.create();

  d.on("error", err => {
    console.error("Domain Error:", err, err.stack);
    //console.trace();
    try {
      // make sure we close down if it times out
      //var killtimer = setTimeout(() => {
      //var err = new Error('Killing process.');

      //console.error('Domain Error:', err);
      //console.trace();
      console.log("Killing process.");
      process.exit(1);
      //}, config.timeout);

      // But don't keep the process open just for that!
      //killtimer.unref();

      // stop taking new requests.
      server.close();

      // Let the master know we're dead.  This will trigger a
      // 'disconnect' in the cluster master, and then it will fork
      // a new worker.
      cluster.worker.disconnect();
    } catch (er2) {
      // oh well, not much we can do at this point.
      console.error(er2);
    }
  });

  /*******************************************
    * Now run the handler function in the domain.
    *******************************************/
  d.run(() => {
    console.log("Listening on " + config.port);

    server = app.listen(config.port);

    app.all("*", (req, res) => {
      //console.log('req');
      return handleRequest(req, res);
    });
  });
}

/*******************************************
* This is where all of the web requests are
* handled.
*******************************************/
function handleRequest(req, res) {
  //console.log('request:', req);
  var url_obj = url.parse(req.url, true),
      file_path;
  console.log(req.headers);
  /*var cache = [],
    j = JSON.stringify(req, function(key, value) {

      if (typeof value === 'object' && value !== null) {
        var idx = _.findIndex(cache, value);
        if (idx > -1) {return '[Circular '+idx+']';}
        cache.push(value);
      }
    return value;
  });

  res.send(j);*/
  //console.log('req.connection.remoteFamily:', '"'+req.connection.remoteFamily+'"');
  //console.log('req.connection.remoteAddress:', '"'+req.connection.remoteAddress+'"');
  if (config.localhostOnly) {
    if (
      (req.connection.remoteFamily === "IPv4" &&
        req.connection.remoteAddress !== "127.0.0.1") ||
      (req.connection.remoteFamily === "IPv6" &&
        (!_.endsWith(req.connection.remoteAddress, "::1") &&
          !_.endsWith(req.connection.remoteAddress, ":127.0.0.1")))
    ) {
      return res.sendStatus(401);
    }
  }

  //Set to the directory path
  if (!req.headers.path_translated) {
    req.headers.path_translated = __dirname + "/www";
    //console.log(req.headers.path_translated);
    if (!fs.existsSync(req.headers.path_translated)) {
      fs.mkdirSync(req.headers.path_translated);
    }
  }

  //
  if(req.headers.path_translated && req.headers.script_name)
    file_path = req.headers.path_translated + req.headers.script_name;
  else if (req.headers.path_translated && url_obj.pathname) {
    file_path = req.headers.path_translated + url_obj.pathname;
  } else {
    file_path = url_obj.pathname;
  }
  console.log(req.method + ": " + file_path);
  function resolveModule(module) {
    if (module.charAt(0) !== ".") {
      return module;
    }
    return path.resolve(path.dirname(file_path), module);
  }

  //If the requested file exists...
  fs.exists(file_path, exists => {
    if (exists) {
      fs.readFile(file_path, (err, source) => {
        if (err) {
          //Error reading file
          console.error(err);
          return res
            .status(500)
            .send({ error: err.toString(), stack: err.stack });
        }
        try {
          //console.log(require.cache);
          //var timer = 'not set';
          req.timeout = config.timeout;
          var timer,
              sandbox = {
                console: console,
                setImmediate: setImmediate,
                setInterval: setInterval,
                setTimeout: setTimeout,
                clearTimeout: clearTimeout,
                Buffer: Buffer,
                JSON: JSON,
                require: function(name) {
                  var mod_path = resolveModule(name);
                  //watchRequired(mod_path);
                  return require(mod_path);
                },
                req: req,
                res: res,
                process: process,
                __dirname: path.dirname(file_path),
                __filename: path.basename(file_path)
              },
              setKilltimer = function() {
                //!req.timeout ? (req.timeout = config.timeout) : "";
                var to_start = Date.now();
                var timeout = req.timeout;
                if (timeout > 0) {
                  return setTimeout(() => {
                    var to_end = Date.now(),
                        msg =
                          "Timeout expired (" +
                          (to_end - to_start) +
                          " ms) for " +
                          file_path,
                        err;

                    //if(!res.headerSent){
                    res.status(408).send(msg);
                    //}
                    //err = new Error(msg);
                    console.error(msg);
                    return;
                  }, req.timeout);
                }
              };
          /****************************
          * Capture "res.send" and
          * "res.sendFile" calls to
          * be able to unset timeout
          * when the script ends. */
          var send = res.send;
          res.send = function(){
            if(timer){
              console.log('timeout cleared');
              clearTimeout(timer);
            }
            return send.apply(this, arguments);
          };
          var sendFile = res.sendFile;
          res.sendFile = function(){
            if(timer){
              console.log('timeout cleared');
              clearTimeout(timer);
            }
            return sendFile.apply(this, arguments);
          }
          /***************************/

          /*****************
           * Start VM Code *
           *****************/
          var c = vm.createContext(sandbox),
              code = `function runInContext() {try{${source}                          //0
                          if(typeof cgi === 'function'){                              //1
                            cgi(req, res);                                            //2
                          }                                                           //3
                        }catch(e){                                                    //4
                          console.error(e);                                           //5
                          res.status(500).send({error: e.toString(), stack: e.stack});//6
                        }                                                             //7
                      }                                                               //8
                                                                                //9
                      runInContext();`;
          /***************
          * End VM Code *
          ***************/

          /***************************************************
           * Set the kill timer after executing the script so
           * the script can set the timeout duration.
           ***************************************************/
          var result = vm.runInContext(code, c, file_path);
          timer = setKilltimer();
          return result;
        } catch (err) {
          console.error(err);
          return res
            .status(500)
            .send({ error: err.toString(), stack: err.stack });
        }
      });
    } else {
      //File does not exist
      return res.status(404).send("File not found. " + file_path);
    }
  });
}
<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { InsufficientFunds, OrderNotFound } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class acx extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'acx',
            'name': 'ACX',
            'countries': [ 'AU' ],
            'rateLimit': 1000,
            'version': 'v2',
            'has': {
                'CORS': true,
                'fetchTickers': true,
                'fetchOHLCV': true,
                'withdraw': true,
                'fetchOrder': true,
            },
            'timeframes': {
                '1m': '1',
                '5m': '5',
                '15m': '15',
                '30m': '30',
                '1h': '60',
                '2h': '120',
                '4h': '240',
                '12h': '720',
                '1d': '1440',
                '3d': '4320',
                '1w': '10080',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg',
                'extension': '.json',
                'api': 'https://acx.io/api',
                'www': 'https://acx.io',
                'doc': 'https://acx.io/documents/api_v2',
            },
            'api': {
                'public': {
                    'get': [
                        'depth', // Get depth or specified market Both asks and bids are sorted from highest price to lowest.
                        'k_with_pending_trades', // Get K data with pending trades, which are the trades not included in K data yet, because there's delay between trade generated and processed by K data generator
                        'k', // Get OHLC(k line) of specific market
                        'markets', // Get all available markets
                        'order_book', // Get the order book of specified market
                        'order_book/{market}',
                        'tickers', // Get ticker of all markets
                        'tickers/{market}', // Get ticker of specific market
                        'timestamp', // Get server current time, in seconds since Unix epoch
                        'trades', // Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order.
                        'trades/{market}',
                    ],
                },
                'private': {
                    'get': [
                        'members/me', // Get your profile and accounts info
                        'deposits', // Get your deposits history
                        'deposit', // Get details of specific deposit
                        'deposit_address', // Where to deposit The address field could be empty when a new address is generating (e.g. for bitcoin), you should try again later in that case.
                        'orders', // Get your orders, results is paginated
                        'order', // Get information of specified order
                        'trades/my', // Get your executed trades Trades are sorted in reverse creation order.
                        'withdraws', // Get your cryptocurrency withdraws
                        'withdraw', // Get your cryptocurrency withdraw
                    ],
                    'post': [
                        'orders', // Create a Sell/Buy order
                        'orders/multi', // Create multiple sell/buy orders
                        'orders/clear', // Cancel all my orders
                        'order/delete', // Cancel an order
                        'withdraw', // Create a withdraw
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': false,
                    'percentage': true,
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'tierBased': false,
                    'percentage': true,
                    'withdraw': {}, // There is only 1% fee on withdrawals to your bank account.
                },
            },
            'exceptions': {
                '2002': InsufficientFunds,
                '2003': OrderNotFound,
            },
        });
    }

    async fetchMarkets () {
        let markets = await this.publicGetMarkets ();
        let result = [];
        for (let p = 0; p < markets.length; p++) {
            let market = markets[p];
            let id = market['id'];
            let symbol = market['name'];
            let [ base, quote ] = symbol.split ('/');
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetMembersMe ();
        let balances = response['accounts'];
        let result = { 'info': balances };
        for (let b = 0; b < balances.length; b++) {
            let balance = balances[b];
            let currency = balance['currency'];
            let uppercase = currency.toUpperCase ();
            let account = {
                'free': parseFloat (balance['balance']),
                'used': parseFloat (balance['locked']),
                'total': 0.0,
            };
            account['total'] = this.sum (account['free'], account['used']);
            result[uppercase] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'market': market['id'],
        };
        if (limit !== undefined)
            request['limit'] = limit; // default = 300
        let orderbook = await this.publicGetDepth (this.extend (request, params));
        let timestamp = orderbook['timestamp'] * 1000;
        return this.parseOrderBook (orderbook, timestamp);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['at'] * 1000;
        ticker = ticker['ticker'];
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = this.safeFloat (ticker, 'last');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': this.safeFloat (ticker, 'buy'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'sell'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'vol'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTickers (params);
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = undefined;
            let symbol = id;
            if (id in this.markets_by_id) {
                market = this.markets_by_id[id];
                symbol = market['symbol'];
            } else {
                let base = id.slice (0, 3);
                let quote = id.slice (3, 6);
                base = base.toUpperCase ();
                quote = quote.toUpperCase ();
                base = this.commonCurrencyCode (base);
                quote = this.commonCurrencyCode (quote);
                symbol = base + '/' + quote;
            }
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTickersMarket (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseTicker (response, market);
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.parse8601 (trade['created_at']);
        return {
            'id': trade['id'].toString (),
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': undefined,
            'price': this.safeFloat (trade, 'price'),
            'amount': this.safeFloat (trade, 'volume'),
            'cost': this.safeFloat (trade, 'funds'),
            'info': trade,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTrades (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseTrades (response, market, since, limit);
    }

    parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
        return [
            ohlcv[0] * 1000,
            ohlcv[1],
            ohlcv[2],
            ohlcv[3],
            ohlcv[4],
            ohlcv[5],
        ];
    }

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        if (limit === undefined)
            limit = 500; // default is 30
        let request = {
            'market': market['id'],
            'period': this.timeframes[timeframe],
            'limit': limit,
        };
        if (since !== undefined)
            request['timestamp'] = since;
        let response = await this.publicGetK (this.extend (request, params));
        return this.parseOHLCVs (response, market, timeframe, since, limit);
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        } else {
            let marketId = order['market'];
            symbol = this.markets_by_id[marketId]['symbol'];
        }
        let timestamp = this.parse8601 (order['created_at']);
        let state = order['state'];
        let status = undefined;
        if (state === 'done') {
            status = 'closed';
        } else if (state === 'wait') {
            status = 'open';
        } else if (state === 'cancel') {
            status = 'canceled';
        }
        return {
            'id': order['id'].toString (),
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'status': status,
            'symbol': symbol,
            'type': order['ord_type'],
            'side': order['side'],
            'price': this.safeFloat (order, 'price'),
            'amount': this.safeFloat (order, 'volume'),
            'filled': this.safeFloat (order, 'executed_volume'),
            'remaining': this.safeFloat (order, 'remaining_volume'),
            'trades': undefined,
            'fee': undefined,
            'info': order,
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetOrder (this.extend ({
            'id': parseInt (id),
        }, params));
        return this.parseOrder (response);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let order = {
            'market': this.marketId (symbol),
            'side': side,
            'volume': amount.toString (),
            'ord_type': type,
        };
        if (type === 'limit') {
            order['price'] = price.toString ();
        }
        let response = await this.privatePostOrders (this.extend (order, params));
        let market = this.markets_by_id[response['market']];
        return this.parseOrder (response, market);
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let result = await this.privatePostOrderDelete ({ 'id': id });
        let order = this.parseOrder (result);
        let status = order['status'];
        if (status === 'closed' || status === 'canceled') {
            throw new OrderNotFound (this.id + ' ' + this.json (order));
        }
        return order;
    }

    async withdraw (currency, amount, address, tag = undefined, params = {}) {
        this.checkAddress (address);
        await this.loadMarkets ();
        let result = await this.privatePostWithdraw (this.extend ({
            'currency': currency.toLowerCase (),
            'sum': amount,
            'address': address,
        }, params));
        return {
            'info': result,
            'id': undefined,
        };
    }

    nonce () {
        return this.milliseconds ();
    }

    encodeParams (params) {
        if ('orders' in params) {
            let orders = params['orders'];
            let query = this.urlencode (this.keysort (this.omit (params, 'orders')));
            for (let i = 0; i < orders.length; i++) {
                let order = orders[i];
                let keys = Object.keys (order);
                for (let k = 0; k < keys.length; k++) {
                    let key = keys[k];
                    let value = order[key];
                    query += '&orders%5B%5D%5B' + key + '%5D=' + value.toString ();
                }
            }
            return query;
        }
        return this.urlencode (this.keysort (params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let request = '/api' + '/' + this.version + '/' + this.implodeParams (path, params);
        if ('extension' in this.urls)
            request += this.urls['extension'];
        let query = this.omit (params, this.extractParams (path));
        let url = this.urls['api'] + request;
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let query = this.encodeParams (this.extend ({
                'access_key': this.apiKey,
                'tonce': nonce,
            }, params));
            let auth = method + '|' + request + '|' + query;
            let signed = this.hmac (this.encode (auth), this.encode (this.secret));
            let suffix = query + '&signature=' + signed;
            if (method === 'GET') {
                url += '?' + suffix;
            } else {
                body = suffix;
                headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
            }
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if (code === 400) {
            const response = JSON.parse (body);
            const error = this.safeValue (response, 'error');
            const errorCode = this.safeString (error, 'code');
            const feedback = this.id + ' ' + this.json (response);
            const exceptions = this.exceptions;
            if (errorCode in exceptions) {
                throw new exceptions[errorCode] (feedback);
            }
            // fallback to default error handler
        }
    }
};
<commit_msg>acx withdraw() signature unified<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { InsufficientFunds, OrderNotFound } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class acx extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'acx',
            'name': 'ACX',
            'countries': [ 'AU' ],
            'rateLimit': 1000,
            'version': 'v2',
            'has': {
                'CORS': true,
                'fetchTickers': true,
                'fetchOHLCV': true,
                'withdraw': true,
                'fetchOrder': true,
            },
            'timeframes': {
                '1m': '1',
                '5m': '5',
                '15m': '15',
                '30m': '30',
                '1h': '60',
                '2h': '120',
                '4h': '240',
                '12h': '720',
                '1d': '1440',
                '3d': '4320',
                '1w': '10080',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg',
                'extension': '.json',
                'api': 'https://acx.io/api',
                'www': 'https://acx.io',
                'doc': 'https://acx.io/documents/api_v2',
            },
            'api': {
                'public': {
                    'get': [
                        'depth', // Get depth or specified market Both asks and bids are sorted from highest price to lowest.
                        'k_with_pending_trades', // Get K data with pending trades, which are the trades not included in K data yet, because there's delay between trade generated and processed by K data generator
                        'k', // Get OHLC(k line) of specific market
                        'markets', // Get all available markets
                        'order_book', // Get the order book of specified market
                        'order_book/{market}',
                        'tickers', // Get ticker of all markets
                        'tickers/{market}', // Get ticker of specific market
                        'timestamp', // Get server current time, in seconds since Unix epoch
                        'trades', // Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order.
                        'trades/{market}',
                    ],
                },
                'private': {
                    'get': [
                        'members/me', // Get your profile and accounts info
                        'deposits', // Get your deposits history
                        'deposit', // Get details of specific deposit
                        'deposit_address', // Where to deposit The address field could be empty when a new address is generating (e.g. for bitcoin), you should try again later in that case.
                        'orders', // Get your orders, results is paginated
                        'order', // Get information of specified order
                        'trades/my', // Get your executed trades Trades are sorted in reverse creation order.
                        'withdraws', // Get your cryptocurrency withdraws
                        'withdraw', // Get your cryptocurrency withdraw
                    ],
                    'post': [
                        'orders', // Create a Sell/Buy order
                        'orders/multi', // Create multiple sell/buy orders
                        'orders/clear', // Cancel all my orders
                        'order/delete', // Cancel an order
                        'withdraw', // Create a withdraw
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': false,
                    'percentage': true,
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'tierBased': false,
                    'percentage': true,
                    'withdraw': {}, // There is only 1% fee on withdrawals to your bank account.
                },
            },
            'exceptions': {
                '2002': InsufficientFunds,
                '2003': OrderNotFound,
            },
        });
    }

    async fetchMarkets () {
        let markets = await this.publicGetMarkets ();
        let result = [];
        for (let p = 0; p < markets.length; p++) {
            let market = markets[p];
            let id = market['id'];
            let symbol = market['name'];
            let baseId = this.safeString (market, 'base_unit');
            let quoteId = this.safeString (market, 'quote_unit');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            // todo: find out their undocumented precision and limits
            let precision = {
                'amount': 8,
                'price': 8,
            };
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'precision': precision,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetMembersMe ();
        let balances = response['accounts'];
        let result = { 'info': balances };
        for (let b = 0; b < balances.length; b++) {
            let balance = balances[b];
            let currency = balance['currency'];
            let uppercase = currency.toUpperCase ();
            let account = {
                'free': parseFloat (balance['balance']),
                'used': parseFloat (balance['locked']),
                'total': 0.0,
            };
            account['total'] = this.sum (account['free'], account['used']);
            result[uppercase] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'market': market['id'],
        };
        if (limit !== undefined)
            request['limit'] = limit; // default = 300
        let orderbook = await this.publicGetDepth (this.extend (request, params));
        let timestamp = orderbook['timestamp'] * 1000;
        return this.parseOrderBook (orderbook, timestamp);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['at'] * 1000;
        ticker = ticker['ticker'];
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = this.safeFloat (ticker, 'last');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high'),
            'low': this.safeFloat (ticker, 'low'),
            'bid': this.safeFloat (ticker, 'buy'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'sell'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': this.safeFloat (ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'vol'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTickers (params);
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = undefined;
            let symbol = id;
            if (id in this.markets_by_id) {
                market = this.markets_by_id[id];
                symbol = market['symbol'];
            } else {
                let base = id.slice (0, 3);
                let quote = id.slice (3, 6);
                base = base.toUpperCase ();
                quote = quote.toUpperCase ();
                base = this.commonCurrencyCode (base);
                quote = this.commonCurrencyCode (quote);
                symbol = base + '/' + quote;
            }
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTickersMarket (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseTicker (response, market);
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.parse8601 (trade['created_at']);
        return {
            'id': trade['id'].toString (),
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': undefined,
            'price': this.safeFloat (trade, 'price'),
            'amount': this.safeFloat (trade, 'volume'),
            'cost': this.safeFloat (trade, 'funds'),
            'info': trade,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTrades (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseTrades (response, market, since, limit);
    }

    parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
        return [
            ohlcv[0] * 1000,
            ohlcv[1],
            ohlcv[2],
            ohlcv[3],
            ohlcv[4],
            ohlcv[5],
        ];
    }

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        if (limit === undefined)
            limit = 500; // default is 30
        let request = {
            'market': market['id'],
            'period': this.timeframes[timeframe],
            'limit': limit,
        };
        if (since !== undefined)
            request['timestamp'] = since;
        let response = await this.publicGetK (this.extend (request, params));
        return this.parseOHLCVs (response, market, timeframe, since, limit);
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        } else {
            let marketId = order['market'];
            symbol = this.markets_by_id[marketId]['symbol'];
        }
        let timestamp = this.parse8601 (order['created_at']);
        let state = order['state'];
        let status = undefined;
        if (state === 'done') {
            status = 'closed';
        } else if (state === 'wait') {
            status = 'open';
        } else if (state === 'cancel') {
            status = 'canceled';
        }
        return {
            'id': order['id'].toString (),
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'status': status,
            'symbol': symbol,
            'type': order['ord_type'],
            'side': order['side'],
            'price': this.safeFloat (order, 'price'),
            'amount': this.safeFloat (order, 'volume'),
            'filled': this.safeFloat (order, 'executed_volume'),
            'remaining': this.safeFloat (order, 'remaining_volume'),
            'trades': undefined,
            'fee': undefined,
            'info': order,
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetOrder (this.extend ({
            'id': parseInt (id),
        }, params));
        return this.parseOrder (response);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let order = {
            'market': this.marketId (symbol),
            'side': side,
            'volume': amount.toString (),
            'ord_type': type,
        };
        if (type === 'limit') {
            order['price'] = price.toString ();
        }
        let response = await this.privatePostOrders (this.extend (order, params));
        let market = this.markets_by_id[response['market']];
        return this.parseOrder (response, market);
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let result = await this.privatePostOrderDelete ({ 'id': id });
        let order = this.parseOrder (result);
        let status = order['status'];
        if (status === 'closed' || status === 'canceled') {
            throw new OrderNotFound (this.id + ' ' + this.json (order));
        }
        return order;
    }

    async withdraw (code, amount, address, tag = undefined, params = {}) {
        this.checkAddress (address);
        await this.loadMarkets ();
        let currency = this.currency (code);
        // they have XRP but no docs on memo/tag
        let request = {
            'currency': currency['id'],
            'sum': amount,
            'address': address,
        };
        let result = await this.privatePostWithdraw (this.extend (request, params));
        // withdrawal response is undocumented
        return {
            'info': result,
            'id': undefined,
        };
    }

    nonce () {
        return this.milliseconds ();
    }

    encodeParams (params) {
        if ('orders' in params) {
            let orders = params['orders'];
            let query = this.urlencode (this.keysort (this.omit (params, 'orders')));
            for (let i = 0; i < orders.length; i++) {
                let order = orders[i];
                let keys = Object.keys (order);
                for (let k = 0; k < keys.length; k++) {
                    let key = keys[k];
                    let value = order[key];
                    query += '&orders%5B%5D%5B' + key + '%5D=' + value.toString ();
                }
            }
            return query;
        }
        return this.urlencode (this.keysort (params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let request = '/api' + '/' + this.version + '/' + this.implodeParams (path, params);
        if ('extension' in this.urls)
            request += this.urls['extension'];
        let query = this.omit (params, this.extractParams (path));
        let url = this.urls['api'] + request;
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ().toString ();
            let query = this.encodeParams (this.extend ({
                'access_key': this.apiKey,
                'tonce': nonce,
            }, params));
            let auth = method + '|' + request + '|' + query;
            let signed = this.hmac (this.encode (auth), this.encode (this.secret));
            let suffix = query + '&signature=' + signed;
            if (method === 'GET') {
                url += '?' + suffix;
            } else {
                body = suffix;
                headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
            }
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if (code === 400) {
            const response = JSON.parse (body);
            const error = this.safeValue (response, 'error');
            const errorCode = this.safeString (error, 'code');
            const feedback = this.id + ' ' + this.json (response);
            const exceptions = this.exceptions;
            if (errorCode in exceptions) {
                throw new exceptions[errorCode] (feedback);
            }
            // fallback to default error handler
        }
    }
};
<|endoftext|>


<commit_before>//Vars
var db = require('./dm.js').db;

exports.identifyPlayer = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  playersColl.update({ name: name }, {$set: { identified: true }}, { multi: false }, function(err){
    if(!err){
      return res.send(JSON.stringify([{ channel: name, message: "You are now identified! You can now execute sensitive commands."}]));
    } else {
      return res.send(JSON.stringify([{ channel: name, message: "You are identified, but there were errors writing data" }]));
    }
  });
};

exports.playerIdentified = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  var player = playersColl.find({ name: name }).limit(1);
  player.count(function(err, count){
    if(count == 0){
      return res.send(true);
    } else {
      player.toArray(function(err, arr){
        if(arr[0].identified){
          return res.send(true);
        } else {
          return res.send(false);
        }
      });
    }
  });
};

//Generate a new character
exports.newPlayer = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  var player = playersColl.find({ name: name }).limit(1);
  player.count(function(err, count){
    if(count > 0){
      console.log("Found existing player");
      return res.send(JSON.stringify([{channel: name, message: "You already have an existing character. If you would like to delete this character please type .delete"}]));
    } else {
      console.log("Creating new player");
      playersColl.insert({ name: name, identified: false, ready: false }, {w: 1}, function(err, obj){
        return res.send(JSON.stringify([{channel: name, message: "Character created for " + name + "! Please choose your character's class: 1: Warrior, 2: Archer, 3: Mage, 4: Cleric, 5: Rogue (e.g. '.choose 3')"}]));
      });
    }
  });
};

//Choose player class
exports.choosePlayerClass = function(req, res){
  var name = req.body.name;
  var clazz = req.body.clazz;
  var playersColl = db.collection('players');
  playersColl.find({ name: name }).limit(1).toArray(function(err, arr){
    if(arr.length == 0){
      return res.send(JSON.stringify([{channel: name, message: "No character found!"}]));
    }
    else if(arr[0].ready == false) {
      switch(clazz){
        case: "1":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "1", str: 6, def: 10, intell: 2, dex: 4 }}, { multi: false });
          break;
        case: "2":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "2", str: 8, def: 6, intell: 2, dex: 6 }}, { multi: false });
          break;
        case: "3":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "3", str: 2, def: 4, intell: 10, dex: 6 }}, { multi: false });
          break;
        case: "4":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "4", str: 2, def: 8, intell: 8, dex: 4 }}, { multi: false });
          break;
        case: "5":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "5", str: 8, def: 2, intell: 2, dex: 10 }}, { multi: false });
          break;
      }
      return res.send(JSON.stringify([{channel: name, message: "Your character is ready for adventure!"}]));
    } else {
      return res.send(JSON.stringify([{channel: name, message: "Your character's class has already been chosen."}]))
    }
  });
};

//Delete a character
exports.deletePlayer = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  playersColl.count({ name: name }, function(err, count){
    if(count == 0){
      console.log("No player found for deletion");
      return res.send(JSON.stringify([{channel: name, message: "You don't have a character for your account! Type .generate to make one."}]));
    } else {
      console.log("Removing player " + name);
      playersColl.remove({ name: name }, { justOne: true }, function(err, callback){
        return res.send(JSON.stringify([{channel: name, message: "Your character has been deleted. Type .generate to make a new one!"}]));
      });
    }
  });
};

//Create a new adventure
exports.joinGame = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  var gpColl = db.collection('gpRouting');
  var gamesColl = db.collection('games');
  var player = playersColl.find({ name: name });

  playersColl.count({ name: name }, function(err, count){
    if(count > 0){
      //Use player in database
      player.toArray(function(err, pArr){
        playerId = pArr[0]._id;
        var recentGames = gamesColl.find({}).sort({_id:-1});
        recentGames.toArray(function(err, arr){
          //If there is an active game waiting for joins
          if(arr.length > 0 && arr[0].active){
            //Insert player into database
            //Check if party is full or not
            gpColl.count({ game: arr[0]._id }, function(err, party){
              if(party == 5){
                return res.send(JSON.stringify([{channel: req.body.channel, message: "This adventure is full! :("}]));
              } else {
                gpColl.count({ player: playerId, game: arr[0]._id }, function(err, num){
                  if(num > 0){
                  console.log(name + " already in adventure");
                  return res.send(JSON.stringify([{channel: req.body.channel, message: name + " you are already a part of this adventure!"}]));
                  }
                  else{
                    console.log("adding " + name + " to adventure");
                    gpColl.insert({ player: playerId, game: arr[0]._id });
                    return res.send(JSON.stringify([{channel: req.body.channel, message: name + " rises for the adventure!"}]));
                  }
                });
              }
            });
          } else {
            //Create game and...
            gamesColl.insert({ active: true }, {w: 1}, function(err, obj){
              //insert player into database
              console.log("Creating new adventure");
              gpColl.insert({ player: playerId, game: obj[0]._id });
              return res.send(JSON.stringify([{channel: req.body.channel, message: name + " has decided to lead an adventure, which will embark when 4 more join! Anyone wishing to join type .adventure"}]));
            });
          }
        });
      });
    } else {
      //Create new player
      console.log("Sending player gen request back");
      return res.send(JSON.stringify([{channel: req.body.channel, message: "Please create your character before joining. Check PM for more info."}, {channel: req.body.name, message: "To begin character generation, please type .generate"}]));
    }
  });
};<commit_msg>character choosing catch<commit_after>//Vars
var db = require('./dm.js').db;

exports.identifyPlayer = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  playersColl.update({ name: name }, {$set: { identified: true }}, { multi: false }, function(err){
    if(!err){
      return res.send(JSON.stringify([{ channel: name, message: "You are now identified! You can now execute sensitive commands."}]));
    } else {
      return res.send(JSON.stringify([{ channel: name, message: "You are identified, but there were errors writing data" }]));
    }
  });
};

exports.playerIdentified = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  var player = playersColl.find({ name: name }).limit(1);
  player.count(function(err, count){
    if(count == 0){
      return res.send(true);
    } else {
      player.toArray(function(err, arr){
        if(arr[0].identified){
          return res.send(true);
        } else {
          return res.send(false);
        }
      });
    }
  });
};

//Generate a new character
exports.newPlayer = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  var player = playersColl.find({ name: name }).limit(1);
  player.count(function(err, count){
    if(count > 0){
      console.log("Found existing player");
      return res.send(JSON.stringify([{channel: name, message: "You already have an existing character. If you would like to delete this character please type .delete"}]));
    } else {
      console.log("Creating new player");
      playersColl.insert({ name: name, identified: false, ready: false }, {w: 1}, function(err, obj){
        return res.send(JSON.stringify([{channel: name, message: "Character created for " + name + "! Please choose your character's class: 1: Warrior, 2: Archer, 3: Mage, 4: Cleric, 5: Rogue (e.g. '.choose 3')"}]));
      });
    }
  });
};

//Choose player class
exports.choosePlayerClass = function(req, res){
  var name = req.body.name;
  var clazz = req.body.clazz;
  var playersColl = db.collection('players');
  playersColl.find({ name: name }).limit(1).toArray(function(err, arr){
    if(arr.length == 0){
      return res.send(JSON.stringify([{channel: name, message: "No character found!"}]));
    }
    else if(arr[0].ready == false) {
      switch(clazz){
        case: "1":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "1", str: 6, def: 10, intell: 2, dex: 4 }}, { multi: false });
          break;
        case: "2":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "2", str: 8, def: 6, intell: 2, dex: 6 }}, { multi: false });
          break;
        case: "3":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "3", str: 2, def: 4, intell: 10, dex: 6 }}, { multi: false });
          break;
        case: "4":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "4", str: 2, def: 8, intell: 8, dex: 4 }}, { multi: false });
          break;
        case: "5":
          playersColl.update({ name: name }, {$set: { ready: true, clazz: "5", str: 8, def: 2, intell: 2, dex: 10 }}, { multi: false });
          break;
      }
      return res.send(JSON.stringify([{channel: name, message: "Your character is ready for adventure!"}]));
    } else {
      return res.send(JSON.stringify([{channel: name, message: "Your character's class has already been chosen."}]))
    }
  });
};

//Delete a character
exports.deletePlayer = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  playersColl.count({ name: name }, function(err, count){
    if(count == 0){
      console.log("No player found for deletion");
      return res.send(JSON.stringify([{channel: name, message: "You don't have a character for your account! Type .generate to make one."}]));
    } else {
      console.log("Removing player " + name);
      playersColl.remove({ name: name }, { justOne: true }, function(err, callback){
        return res.send(JSON.stringify([{channel: name, message: "Your character has been deleted. Type .generate to make a new one!"}]));
      });
    }
  });
};

//Create a new adventure
exports.joinGame = function(req, res){
  var name = req.body.name;
  var playersColl = db.collection('players');
  var gpColl = db.collection('gpRouting');
  var gamesColl = db.collection('games');
  var player = playersColl.find({ name: name });

  playersColl.count({ name: name, ready: true }, function(err, count){
    if(count > 0){
      //Use player in database
      player.toArray(function(err, pArr){
        playerId = pArr[0]._id;
        var recentGames = gamesColl.find({}).sort({_id:-1});
        recentGames.toArray(function(err, arr){
          //If there is an active game waiting for joins
          if(arr.length > 0 && arr[0].active){
            //Insert player into database
            //Check if party is full or not
            gpColl.count({ game: arr[0]._id }, function(err, party){
              if(party == 5){
                return res.send(JSON.stringify([{channel: req.body.channel, message: "This adventure is full! :("}]));
              } else {
                gpColl.count({ player: playerId, game: arr[0]._id }, function(err, num){
                  if(num > 0){
                  console.log(name + " already in adventure");
                  return res.send(JSON.stringify([{channel: req.body.channel, message: name + " you are already a part of this adventure!"}]));
                  }
                  else{
                    console.log("adding " + name + " to adventure");
                    gpColl.insert({ player: playerId, game: arr[0]._id });
                    return res.send(JSON.stringify([{channel: req.body.channel, message: name + " rises for the adventure!"}]));
                  }
                });
              }
            });
          } else {
            //Create game and...
            gamesColl.insert({ active: true }, {w: 1}, function(err, obj){
              //insert player into database
              console.log("Creating new adventure");
              gpColl.insert({ player: playerId, game: obj[0]._id });
              return res.send(JSON.stringify([{channel: req.body.channel, message: name + " has decided to lead an adventure, which will embark when 4 more join! Anyone wishing to join type .adventure"}]));
            });
          }
        });
      });
    } else {
      //Create new player
      console.log("Sending player gen request back");
      player.toArray(function(err, pArray){
        if (pArray.length != 0 && pArray[0].ready == false){
          return res.send(JSON.stringify([{channel: req.body.channel, message: "Please choose your character's class before joining. Check PM for more info."}, {channel: req.body.name, message: "Please choose your character's class: 1: Warrior, 2: Archer, 3: Mage, 4: Cleric, 5: Rogue (e.g. '.choose 3')"}]));
        } else {
          return res.send(JSON.stringify([{channel: req.body.channel, message: "Please create your character before joining. Check PM for more info."}, {channel: req.body.name, message: "To begin character generation, please type .generate"}]));
        }
      });
    }
  });
};<|endoftext|>