<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const _ = require('lodash');
const request = require('request');
const jsonpath = require('JSONPath');
const debug = require('debug')('http');
const debugResponse = require('debug')('http:response');
const debugCapture = require('debug')('http:capture');
const VERSION = require('../package.json').version;
const USER_AGENT = 'artillery ' + VERSION + ' (https://artillery.io)';
const engineUtil = require('./engine_util');
const template = engineUtil.template;
const http = require('http');
const https = require('https');
const fs = require('fs');
const filtrex = require('filtrex');
const cheerio = require('cheerio');

let xmlCapture;
try {
  xmlCapture = require('artillery-xml-capture');
} catch (e) {
  xmlCapture = null;
}

module.exports = HttpEngine;

function HttpEngine(script) {
  this.config = script.config;

  if (script.config.http && script.config.http.pool) {
    this.pool = {
      maxSockets: Number(script.config.http.pool)
    };
  }
}

HttpEngine.prototype.createScenario = function(scenarioSpec, ee) {
  var self = this;
  let tasks = _.map(scenarioSpec.flow, function(rs) {
    if (rs.think) {
      return engineUtil.createThink(rs);
    }

    return self.step(rs, ee, {
      beforeRequest: scenarioSpec.beforeRequest || [],
      afterResponse: scenarioSpec.afterResponse || []
    });
  });

  return self.compile(tasks, scenarioSpec.flow, ee);
};

HttpEngine.prototype.step = function step(requestSpec, ee, opts) {
  opts = opts || {};
  let self = this;
  let config = this.config;

  if (requestSpec.loop) {
    let steps = _.map(requestSpec.loop, function(rs) {
      return self.step(rs, ee, opts);
    });

    return engineUtil.createLoopWithCount(requestSpec.count || -1, steps);
  }

  if (requestSpec.think) {
    return engineUtil.createThink(requestSpec);
  }

  var f = function(context, callback) {
    let method = _.keys(requestSpec)[0].toUpperCase();
    let params = requestSpec[method.toLowerCase()];
    let uri = maybePrependBase(template(params.url, context), config);
    let tls = config.tls || {};
    let timeout = config.timeout || 10;

    if (!engineUtil.isProbableEnough(params)) {
      return process.nextTick(function() {
        callback(null, context);
      });
    }

    if (!_.isUndefined(params.ifTrue)) {
      let cond;
      let result;
      try {
        cond = filtrex(params.ifTrue);
        result = cond(context.vars);
      } catch (e) {
        result = 1; // if the expression is incorrect, just proceed // TODO: debug message
      }
      if (typeof result === 'undefined' || result === 0) {
        return process.nextTick(function () {
          callback(null, context);
        });
      }
    }

    let requestParams = _.cloneDeep(params);
    requestParams = _.extend(requestParams, {
      uri: uri,
      method: method,
      headers: {
      },
      timeout: timeout * 1000,
      jar: context._jar
    });
    requestParams = _.extend(requestParams, tls);

    if (params.json) {
      requestParams.json = template(params.json, context);
    } else if (params.body) {
      requestParams.body = template(params.body, context);
    }

    // Assign default headers then overwrite as needed
    let defaultHeaders = lowcaseKeys(
      (config.defaults && config.defaults.headers) ?
        config.defaults.headers : {'user-agent': USER_AGENT});
    requestParams.headers = _.extend(defaultHeaders,
                                     lowcaseKeys(params.headers));
    let headers = _.reduce(requestParams.headers,
                          function(acc, v, k) {
                            acc[k] = template(v, context);
                            return acc;
                          }, {});
    requestParams.headers = headers;

    let defaultCookie = config.defaults ? config.defaults.cookie || {} : {};
    let cookie = _.reduce(
      params.cookie,
      function(acc, v, k) {
        acc[k] = v;
        return acc;
      },
      defaultCookie);

    if (cookie) {
      _.each(cookie, function(v, k) {
        context._jar.setCookie(k + '=' + template(v, context), uri);
      });
    }

    if (config.http2) {
      requestParams.http2 = true;
    } else {
      if (!self.pool) {
        requestParams.agent = context._agent;
      } else {

        requestParams.pool = self.pool;
      }
    }

    // Run beforeRequest processors (scenario-level ones too)
    let functionNames = _.concat(opts.beforeRequest || [], params.beforeRequest || []);

    async.eachSeries(
      functionNames,
      function iteratee(functionName, next) {

        let processFunc = config.processor[functionName];
        processFunc(requestParams, context, ee, function(err) {
          if (err) {
            debug(err);
          }
          // TODO: A warning of some kind - we don't abort the scenario though
          return next(null);
        });
      },
      function done(err) {
        if (err) {
          debug(err);
        }
        request(requestParams, function requestCallback(err, res, body) {
          debug('request: %s', JSON.stringify({
            uri: requestParams.uri,
            method: requestParams.method,
            headers: requestParams.headers,
            json: requestParams.json
          }, null, 2));

          if (err) {
            let errCode = err.code || err.message;
            ee.emit('error', errCode);
            debug(err);
            // this aborts the scenario
            return callback(err, context);
          }

          debugResponse(JSON.stringify(res.headers, null, 2));
          debugResponse(JSON.stringify(body, null, 2));

          // Run afterResponse processors (scenario-level ones too)
          let functionNames = _.concat(opts.afterResponse || [], params.afterResponse || []);
          async.eachSeries(
            functionNames,
            function iteratee(functionName, next) {
              let processFunc = config.processor[functionName];
              processFunc(requestParams, res, context, ee, function(err) {
                // TODO: warn as above
                return next(null);
              });
            },
            function done(err) {
              if (params.capture || params.match) {
                captureOrMatch(params, res, context, function(err, result) {
                  if (err) {
                    return callback(null, context);
                  }

                  let haveFailedMatches = _.some(result.matches, function(v, k) {
                    return v.success;
                  });

                  if (haveFailedMatches) {
                    // TODO: Should log the details of the match somewhere
                    ee.emit('error', 'Failed match');
                    return callback(new Error('Failed match'), context);
                  } else {
                    _.each(result.matches, function(v, k) {
                      ee.emit('match', v.success, {
                        expected: v.expected,
                        got: v.got,
                        expression: v.expression
                      });
                    });

                    _.each(result.captures, function(v, k) {
                      context.vars[k] = v;
                    });

                    context.vars.$ = res.body;
                    context._successCount++;
                    return callback(null, context);
                  }
                });
              } else {
                context.vars.$ = res.body;
                context._successCount++;
                return callback(null, context);
              }
            });
        }).on('request', function(req) {
          ee.emit('request');

          const startedAt = process.hrtime();

          req.on('response', function updateLatency(res) {
            let code = res.statusCode;
            const endedAt = process.hrtime(startedAt);
            let delta = (endedAt[0] * 1e9) + endedAt[1];
            ee.emit('response', delta, code, context._uid);
          });
        }).on('end', function() {
        });

      }); // eachSeries
  };

  return f;
};

HttpEngine.prototype.compile = function compile(tasks, scenarioSpec, ee) {
  let self = this;
  let config = this.config;
  let tls = config.tls || {};

  return function scenario(initialContext, callback) {
    //
    // Calculate the number of steps we expect to take.
    //
    initialContext._successCount = 0;

    initialContext._jar = request.jar();

    if (!config.http2) {
      if (!self.pool) {
        let agentOpts = {
          keepAlive: true,
          keepAliveMsecs: 1000,
          maxSockets: 1,
          maxFreeSockets: 1
        };

        // FIXME: This won't work if we have a pool - needs to be set in agentOptions
        // in request params
        if ((/^https/i).test(config.target)) {
          if (tls.pfx) {
            // FIXME: Path needs to be resolved relative to Artillery's cwd
            agentOpts.pfx = fs.readFileSync(tls.pfx);
          }
          initialContext._agent = new https.Agent(agentOpts);
        } else {
          initialContext._agent = new http.Agent(agentOpts);
        }
      }
    }

    let steps = _.flatten([
      function zero(cb) {
        ee.emit('started');
        return cb(null, initialContext);
      },
      tasks
    ]);

    async.waterfall(
      steps,
      function scenarioWaterfallCb(err, context) {
        // If the connection was refused we might not have a context
        if (context && context._agent) {
          context._agent.destroy();
        }
        return callback(err, context);
      });
  };
};

function maybePrependBase(uri, config) {
  if (_.startsWith(uri, '/')) {
    return config.target + uri;
  } else {
    return uri;
  }
}

/*
 * Given a dictionary, return a dictionary with all keys lowercased.
 */
function lowcaseKeys(h) {
  return _.transform(h, function(result, v, k) {
    result[k.toLowerCase()] = v;
  });
}

/*
 * Given a response object determine if it's JSON
 */
function isJSON(res) {
  debug('isJSON: content-type = %s', res.headers['content-type']);
  return (res.headers['content-type'] &&
          /^application\/json/.test(res.headers['content-type']));
}

/*
 * Given a response object determine if it's some kind of XML
 */
function isXML(res) {
  return (res.headers['content-type'] &&
          (/^[a-zA-Z]+\/xml/.test(res.headers['content-type']) ||
           /^[a-zA-Z]+\/[a-zA-Z]+\+xml/.test(res.headers['content-type'])));

}

/*
 * Wrap JSON.parse in a callback
 */
function parseJSON(body, callback) {
  let r;
  try {
    if (typeof body === 'string') {
      r = JSON.parse(body);
    } else {
      r = body;
    }
    return callback(null, r);
  } catch(err) {
    return callback(err, null);
  }
}

function dummyParser(body, callback) {
  return callback(null, body);
}

// doc is a JSON object
function extractJSONPath(doc, expr) {
  let results = jsonpath.eval(doc, expr);
  if (results.length > 1) {
    return results[randomInt(0, results.length - 1)];
  } else {
    return results[0];
  }
}

// doc is a string or an object (body parsed by Request when headers indicate JSON)
function extractRegExp(doc, expr, opts) {
  let group = opts.group;
  let str;
  if (typeof doc === 'string') {
    str = doc;
  } else {
    str = JSON.stringify(doc); // FIXME: not the same string as the one we got from the server
  }
  let rx = new RegExp(expr);
  let match = rx.exec(str);
  if(group && match[group]) {
    return match[group];
  } else if (match[0]) {
    return match[0];
  } else {
    return '';
  }
}

function extractCheerio(doc, expr, opts) {
  let $ = cheerio.load(doc);
  let els = $(expr);
  let i = 0;
  if (typeof opts.index !== 'undefined') {
    if (opts.index === 'random') {
      i = Math.ceil(Math.random() * els.get().length - 1);
    } else if (opts.index === 'last') {
      i = els.get().length() - 1;
    } else if (typeof Number(opts.index) === 'number') {
      i = Number(opts.index);
    }
  }
  return els.slice(i, i + 1).attr(opts.attr);
}

function extractHeader(headers, headerName) {
  return headers[headerName];
}

function dummyExtractor() {
  return '';
}

function randomInt (low, high) {
  return Math.floor(Math.random() * (high - low + 1) + low);
}

//
// Calls done() with:
// {captures: { var: value }, matches: { var: {expected: '', got: ''} }}
//
function captureOrMatch(params, response, context, done) {
  let specs = _.concat(
    _.get(params, 'capture', []),
    _.get(params, 'match', []));

  let result = {
    captures: {},
    matches: {}
  };

  async.eachSeries(
    specs,
    function(spec, next) {
      let parsedSpec = parseSpec(spec, response);
      let parser = parsedSpec.parser;
      let extractor = parsedSpec.extractor;
      let expr = parsedSpec.expr;

      // are we looking at body or headers:
      var content = response.body;
      if (spec.header) {
        content = response.headers;
      }

      parser(content, function(err, doc) {
        if (err) {
          return next(err, null);
        }

        let extractedValue = extractor(doc, expr, spec);

        if (spec.value) {
          // this is a match spec
          let expected = template(spec.value, context);
          debug('match: %s, expected: %s, got: %s', expr, expected, got);
          if (result !== expected) {
            result.matches[expr] = {
              success: false,
              expected: expected,
              got: extractedValue,
              expression: expr,
              strict: spec.strict
            };
          } else {
            result.matches.expr = {
              success: true,
              expected: expected,
              expression: expr
            };
          }
          return next(null);
        }

        if (spec.as) {
          // this is a capture
          debugCapture('capture: %s = %s', spec.as, extractedValue);
          result.captures[spec.as] = extractedValue;
          if (spec.transform) {
            let transformedValue = engineUtil.evil(
              result.captures,
              spec.transform);

            debugCapture('transform: %s = %s', spec.as, result.captures[spec.as]);
            result.captures[spec.as] = transformedValue;
          }
        }

        return next(null);
      });
    },
    function(err) {
      if (err) {
        return done(err, null);
      } else {
        return done(null, result);
      }
    });
};

function parseSpec(spec, response) {
  let parser;
  let extractor;
  let expr;

  if (spec.json) {
    parser = parseJSON;
    extractor = extractJSONPath;
    expr = spec.json;
  } else if (xmlCapture && spec.xpath) {
    parser = xmlCapture.parseXML;
    extractor = xmlCapture.extractXPath;
    expr = spec.xpath;
  } else if (spec.regexp) {
    parser = dummyParser;
    extractor = extractRegExp;
    expr = spec.regexp;
  } else if (spec.header) {
    parser = dummyParser;
    extractor = extractHeader;
    expr = spec.header;
  } else if (spec.selector) {
    parser = dummyParser;
    extractor = extractCheerio;
    expr = spec.selector;
  } else {
    if (isJSON(response)) {
      parser = parseJSON;
      extractor = extractJSONPath;
      expr = spec.json;
    } else if (xmlCapture && isXML(response)) {
      parser = xmlCapture.parseXML;
      extractor = xmlCapture.extractXPath;
      expr = spec.xpath;
    } else {
      // We really don't know what to do here.
      parser = dummyParser;
      extractor = dummyExtractor;
      expr = '';
    }
  }

  return { parser: parser, extractor: extractor, expr: expr };
}
<commit_msg>Add log command<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const _ = require('lodash');
const request = require('request');
const jsonpath = require('JSONPath');
const debug = require('debug')('http');
const debugResponse = require('debug')('http:response');
const debugCapture = require('debug')('http:capture');
const VERSION = require('../package.json').version;
const USER_AGENT = 'artillery ' + VERSION + ' (https://artillery.io)';
const engineUtil = require('./engine_util');
const template = engineUtil.template;
const http = require('http');
const https = require('https');
const fs = require('fs');
const filtrex = require('filtrex');
const cheerio = require('cheerio');

let xmlCapture;
try {
  xmlCapture = require('artillery-xml-capture');
} catch (e) {
  xmlCapture = null;
}

module.exports = HttpEngine;

function HttpEngine(script) {
  this.config = script.config;

  if (script.config.http && script.config.http.pool) {
    this.pool = {
      maxSockets: Number(script.config.http.pool)
    };
  }
}

HttpEngine.prototype.createScenario = function(scenarioSpec, ee) {
  var self = this;
  let tasks = _.map(scenarioSpec.flow, function(rs) {
    if (rs.think) {
      return engineUtil.createThink(rs);
    }

    if (rs.log) {
      return function(context, callback) {
        console.log(template(rs.log, context));
        return process.nextTick(function() { callback(null, context); });
      };
    }

    return self.step(rs, ee, {
      beforeRequest: scenarioSpec.beforeRequest || [],
      afterResponse: scenarioSpec.afterResponse || []
    });
  });

  return self.compile(tasks, scenarioSpec.flow, ee);
};

HttpEngine.prototype.step = function step(requestSpec, ee, opts) {
  opts = opts || {};
  let self = this;
  let config = this.config;

  if (requestSpec.loop) {
    let steps = _.map(requestSpec.loop, function(rs) {
      return self.step(rs, ee, opts);
    });

    return engineUtil.createLoopWithCount(requestSpec.count || -1, steps);
  }

  if (requestSpec.think) {
    return engineUtil.createThink(requestSpec);
  }

  var f = function(context, callback) {
    let method = _.keys(requestSpec)[0].toUpperCase();
    let params = requestSpec[method.toLowerCase()];
    let uri = maybePrependBase(template(params.url, context), config);
    let tls = config.tls || {};
    let timeout = config.timeout || 10;

    if (!engineUtil.isProbableEnough(params)) {
      return process.nextTick(function() {
        callback(null, context);
      });
    }

    if (!_.isUndefined(params.ifTrue)) {
      let cond;
      let result;
      try {
        cond = filtrex(params.ifTrue);
        result = cond(context.vars);
      } catch (e) {
        result = 1; // if the expression is incorrect, just proceed // TODO: debug message
      }
      if (typeof result === 'undefined' || result === 0) {
        return process.nextTick(function () {
          callback(null, context);
        });
      }
    }

    let requestParams = _.cloneDeep(params);
    requestParams = _.extend(requestParams, {
      uri: uri,
      method: method,
      headers: {
      },
      timeout: timeout * 1000,
      jar: context._jar
    });
    requestParams = _.extend(requestParams, tls);

    if (params.json) {
      requestParams.json = template(params.json, context);
    } else if (params.body) {
      requestParams.body = template(params.body, context);
    }

    // Assign default headers then overwrite as needed
    let defaultHeaders = lowcaseKeys(
      (config.defaults && config.defaults.headers) ?
        config.defaults.headers : {'user-agent': USER_AGENT});
    requestParams.headers = _.extend(defaultHeaders,
                                     lowcaseKeys(params.headers));
    let headers = _.reduce(requestParams.headers,
                          function(acc, v, k) {
                            acc[k] = template(v, context);
                            return acc;
                          }, {});
    requestParams.headers = headers;

    let defaultCookie = config.defaults ? config.defaults.cookie || {} : {};
    let cookie = _.reduce(
      params.cookie,
      function(acc, v, k) {
        acc[k] = v;
        return acc;
      },
      defaultCookie);

    if (cookie) {
      _.each(cookie, function(v, k) {
        context._jar.setCookie(k + '=' + template(v, context), uri);
      });
    }

    if (config.http2) {
      requestParams.http2 = true;
    } else {
      if (!self.pool) {
        requestParams.agent = context._agent;
      } else {

        requestParams.pool = self.pool;
      }
    }

    // Run beforeRequest processors (scenario-level ones too)
    let functionNames = _.concat(opts.beforeRequest || [], params.beforeRequest || []);

    async.eachSeries(
      functionNames,
      function iteratee(functionName, next) {

        let processFunc = config.processor[functionName];
        processFunc(requestParams, context, ee, function(err) {
          if (err) {
            debug(err);
          }
          // TODO: A warning of some kind - we don't abort the scenario though
          return next(null);
        });
      },
      function done(err) {
        if (err) {
          debug(err);
        }
        request(requestParams, function requestCallback(err, res, body) {
          debug('request: %s', JSON.stringify({
            uri: requestParams.uri,
            method: requestParams.method,
            headers: requestParams.headers,
            json: requestParams.json
          }, null, 2));

          if (err) {
            let errCode = err.code || err.message;
            ee.emit('error', errCode);
            debug(err);
            // this aborts the scenario
            return callback(err, context);
          }

          debugResponse(JSON.stringify(res.headers, null, 2));
          debugResponse(JSON.stringify(body, null, 2));

          // Run afterResponse processors (scenario-level ones too)
          let functionNames = _.concat(opts.afterResponse || [], params.afterResponse || []);
          async.eachSeries(
            functionNames,
            function iteratee(functionName, next) {
              let processFunc = config.processor[functionName];
              processFunc(requestParams, res, context, ee, function(err) {
                // TODO: warn as above
                return next(null);
              });
            },
            function done(err) {
              if (params.capture || params.match) {
                captureOrMatch(params, res, context, function(err, result) {
                  if (err) {
                    return callback(null, context);
                  }

                  let haveFailedMatches = _.some(result.matches, function(v, k) {
                    return v.success;
                  });

                  if (haveFailedMatches) {
                    // TODO: Should log the details of the match somewhere
                    ee.emit('error', 'Failed match');
                    return callback(new Error('Failed match'), context);
                  } else {
                    _.each(result.matches, function(v, k) {
                      ee.emit('match', v.success, {
                        expected: v.expected,
                        got: v.got,
                        expression: v.expression
                      });
                    });

                    _.each(result.captures, function(v, k) {
                      context.vars[k] = v;
                    });

                    context.vars.$ = res.body;
                    context._successCount++;
                    return callback(null, context);
                  }
                });
              } else {
                context.vars.$ = res.body;
                context._successCount++;
                return callback(null, context);
              }
            });
        }).on('request', function(req) {
          ee.emit('request');

          const startedAt = process.hrtime();

          req.on('response', function updateLatency(res) {
            let code = res.statusCode;
            const endedAt = process.hrtime(startedAt);
            let delta = (endedAt[0] * 1e9) + endedAt[1];
            ee.emit('response', delta, code, context._uid);
          });
        }).on('end', function() {
        });

      }); // eachSeries
  };

  return f;
};

HttpEngine.prototype.compile = function compile(tasks, scenarioSpec, ee) {
  let self = this;
  let config = this.config;
  let tls = config.tls || {};

  return function scenario(initialContext, callback) {
    //
    // Calculate the number of steps we expect to take.
    //
    initialContext._successCount = 0;

    initialContext._jar = request.jar();

    if (!config.http2) {
      if (!self.pool) {
        let agentOpts = {
          keepAlive: true,
          keepAliveMsecs: 1000,
          maxSockets: 1,
          maxFreeSockets: 1
        };

        // FIXME: This won't work if we have a pool - needs to be set in agentOptions
        // in request params
        if ((/^https/i).test(config.target)) {
          if (tls.pfx) {
            // FIXME: Path needs to be resolved relative to Artillery's cwd
            agentOpts.pfx = fs.readFileSync(tls.pfx);
          }
          initialContext._agent = new https.Agent(agentOpts);
        } else {
          initialContext._agent = new http.Agent(agentOpts);
        }
      }
    }

    let steps = _.flatten([
      function zero(cb) {
        ee.emit('started');
        return cb(null, initialContext);
      },
      tasks
    ]);

    async.waterfall(
      steps,
      function scenarioWaterfallCb(err, context) {
        // If the connection was refused we might not have a context
        if (context && context._agent) {
          context._agent.destroy();
        }
        return callback(err, context);
      });
  };
};

function maybePrependBase(uri, config) {
  if (_.startsWith(uri, '/')) {
    return config.target + uri;
  } else {
    return uri;
  }
}

/*
 * Given a dictionary, return a dictionary with all keys lowercased.
 */
function lowcaseKeys(h) {
  return _.transform(h, function(result, v, k) {
    result[k.toLowerCase()] = v;
  });
}

/*
 * Given a response object determine if it's JSON
 */
function isJSON(res) {
  debug('isJSON: content-type = %s', res.headers['content-type']);
  return (res.headers['content-type'] &&
          /^application\/json/.test(res.headers['content-type']));
}

/*
 * Given a response object determine if it's some kind of XML
 */
function isXML(res) {
  return (res.headers['content-type'] &&
          (/^[a-zA-Z]+\/xml/.test(res.headers['content-type']) ||
           /^[a-zA-Z]+\/[a-zA-Z]+\+xml/.test(res.headers['content-type'])));

}

/*
 * Wrap JSON.parse in a callback
 */
function parseJSON(body, callback) {
  let r;
  try {
    if (typeof body === 'string') {
      r = JSON.parse(body);
    } else {
      r = body;
    }
    return callback(null, r);
  } catch(err) {
    return callback(err, null);
  }
}

function dummyParser(body, callback) {
  return callback(null, body);
}

// doc is a JSON object
function extractJSONPath(doc, expr) {
  let results = jsonpath.eval(doc, expr);
  if (results.length > 1) {
    return results[randomInt(0, results.length - 1)];
  } else {
    return results[0];
  }
}

// doc is a string or an object (body parsed by Request when headers indicate JSON)
function extractRegExp(doc, expr, opts) {
  let group = opts.group;
  let str;
  if (typeof doc === 'string') {
    str = doc;
  } else {
    str = JSON.stringify(doc); // FIXME: not the same string as the one we got from the server
  }
  let rx = new RegExp(expr);
  let match = rx.exec(str);
  if(group && match[group]) {
    return match[group];
  } else if (match[0]) {
    return match[0];
  } else {
    return '';
  }
}

function extractCheerio(doc, expr, opts) {
  let $ = cheerio.load(doc);
  let els = $(expr);
  let i = 0;
  if (typeof opts.index !== 'undefined') {
    if (opts.index === 'random') {
      i = Math.ceil(Math.random() * els.get().length - 1);
    } else if (opts.index === 'last') {
      i = els.get().length() - 1;
    } else if (typeof Number(opts.index) === 'number') {
      i = Number(opts.index);
    }
  }
  return els.slice(i, i + 1).attr(opts.attr);
}

function extractHeader(headers, headerName) {
  return headers[headerName];
}

function dummyExtractor() {
  return '';
}

function randomInt (low, high) {
  return Math.floor(Math.random() * (high - low + 1) + low);
}

//
// Calls done() with:
// {captures: { var: value }, matches: { var: {expected: '', got: ''} }}
//
function captureOrMatch(params, response, context, done) {
  let specs = _.concat(
    _.get(params, 'capture', []),
    _.get(params, 'match', []));

  let result = {
    captures: {},
    matches: {}
  };

  async.eachSeries(
    specs,
    function(spec, next) {
      let parsedSpec = parseSpec(spec, response);
      let parser = parsedSpec.parser;
      let extractor = parsedSpec.extractor;
      let expr = parsedSpec.expr;

      // are we looking at body or headers:
      var content = response.body;
      if (spec.header) {
        content = response.headers;
      }

      parser(content, function(err, doc) {
        if (err) {
          return next(err, null);
        }

        let extractedValue = extractor(doc, expr, spec);

        if (spec.value) {
          // this is a match spec
          let expected = template(spec.value, context);
          debug('match: %s, expected: %s, got: %s', expr, expected, got);
          if (result !== expected) {
            result.matches[expr] = {
              success: false,
              expected: expected,
              got: extractedValue,
              expression: expr,
              strict: spec.strict
            };
          } else {
            result.matches.expr = {
              success: true,
              expected: expected,
              expression: expr
            };
          }
          return next(null);
        }

        if (spec.as) {
          // this is a capture
          debugCapture('capture: %s = %s', spec.as, extractedValue);
          result.captures[spec.as] = extractedValue;
          if (spec.transform) {
            let transformedValue = engineUtil.evil(
              result.captures,
              spec.transform);

            debugCapture('transform: %s = %s', spec.as, result.captures[spec.as]);
            result.captures[spec.as] = transformedValue;
          }
        }

        return next(null);
      });
    },
    function(err) {
      if (err) {
        return done(err, null);
      } else {
        return done(null, result);
      }
    });
};

function parseSpec(spec, response) {
  let parser;
  let extractor;
  let expr;

  if (spec.json) {
    parser = parseJSON;
    extractor = extractJSONPath;
    expr = spec.json;
  } else if (xmlCapture && spec.xpath) {
    parser = xmlCapture.parseXML;
    extractor = xmlCapture.extractXPath;
    expr = spec.xpath;
  } else if (spec.regexp) {
    parser = dummyParser;
    extractor = extractRegExp;
    expr = spec.regexp;
  } else if (spec.header) {
    parser = dummyParser;
    extractor = extractHeader;
    expr = spec.header;
  } else if (spec.selector) {
    parser = dummyParser;
    extractor = extractCheerio;
    expr = spec.selector;
  } else {
    if (isJSON(response)) {
      parser = parseJSON;
      extractor = extractJSONPath;
      expr = spec.json;
    } else if (xmlCapture && isXML(response)) {
      parser = xmlCapture.parseXML;
      extractor = xmlCapture.extractXPath;
      expr = spec.xpath;
    } else {
      // We really don't know what to do here.
      parser = dummyParser;
      extractor = dummyExtractor;
      expr = '';
    }
  }

  return { parser: parser, extractor: extractor, expr: expr };
}
<|endoftext|>


<commit_before>"use strict";
import React from "react";
import ReactDOM from "react-dom";
import debugFactory from "debug";
const debug = debugFactory('app:components:MapPane');
const stateData = require('./states');
import {Grid, Row, Col, Button, PageHeader, Input, Glyphicon, DropdownButton, MenuItem, Panel} from "react-bootstrap";

import Map from "./map";
import YearSelector from "./YearSelector";
import StateSelector from "./StateSelector";
import ActivitiesPerformed from "./ActivitiesPerformed";

let MapPaneComponent = React.createClass({

    propTypes: {
        onCenterChange: React.PropTypes.func.isRequired,
        onStateChange: React.PropTypes.func.isRequired,
        onZoomChange: React.PropTypes.func.isRequired,
        onYearChange: React.PropTypes.func.isRequired,
        onBoundsChange: React.PropTypes.func.isRequired,
        state: React.PropTypes.string.isRequired,
        lat: React.PropTypes.number.isRequired,
        lng: React.PropTypes.number.isRequired,
        zoom: React.PropTypes.number.isRequired,
        year: React.PropTypes.number.isRequired,
    },

    getInitialState() {
        return {
            acquiringLocation: false,
        };
    },

    componentDidMount() {
        this.initialLoadState(stateData.statesByCode[this.props.state]);
    },

    onLocateMe() {
        //TODO: need to use geocoding to lookup state from coords
        this.setState({selectedState: null});
        if (navigator.geolocation) {
            this.setState({acquiringLocation: true});
            navigator.geolocation.getCurrentPosition((position) => {
                this.setState({acquiringLocation: false});
                const center = {
                    lat: position.coords.latitude,
                    lng: position.coords.longitude,
                };
                this.refs.map.setCenter(center);
                // TODO: signal state change using geocoding
                this.props.onCenterChange(center);
            });
        } else {
            this.setState({alert: "Geolocation is not supported by this browser."});
        }
    },

    initialLoadState(state) {
        debug('loading initial state', state);
        this.setState({selectedState: state});
        this.refs.map.enable(state.polygon);
        this.props.onStateChange(state.code);
    },

    loadState(state, requestedBounds) {
        debug('loading state', state);

        // Default to the state bounds
        let bounds = {
            sw: {
                lat: state.bounds.minLat,
                lng: state.bounds.minLng,
            },
            ne: {
                lat: state.bounds.maxLat,
                lng: state.bounds.maxLng,
            }
        };
        // If we have some bounds that have been requested by the state selector
        if (requestedBounds) {
            // We only have a center point, do some finagling to get it to center better
            if (requestedBounds.c) {
                bounds = {
                    // Some magic numbers to get a closer view on a given item
                    sw: {
                        lat: requestedBounds.c.lat * .9999,
                        lng: requestedBounds.c.lng * 1.0001
                    },
                    ne: {
                        lat: requestedBounds.c.lat * 1.0001,
                        lng: requestedBounds.c.lng * .9999
                    }
                }
            } else {

                // We have a rectangular bounds use that
                bounds = requestedBounds;
            }
        }

        if (this.state.selectedState) {
            this.refs.map.disable(this.state.selectedState.polygon);
        }

        this.setState({selectedState: state});
        this.refs.map.enable(state.polygon);
        this.refs.map.setBounds(bounds);

        this.props.onStateChange(state.code);
    },

    onSelectState(state, bounds) {
        if (state) {
            debug('Selected State', state);
            this.loadState(state, bounds);
            this.onStateChange(state);
        }
    },

    render() {
        return (

Where's your farm?

                    <StateSelector onStateSelected={this.onSelectState} />

                    <YearSelector
                        year={this.props.year}
                        onYearChange={this.props.onYearChange}
                    />

                    <Map ref="map"
                         year={this.props.year}
                         lat={this.props.lat}
                         lng={this.props.lng}
                         zoom={this.props.zoom}
                         onCenterChange={this.props.onCenterChange}
                         onZoomChange={this.props.onZoomChange}
                         onBoundsChange={this.props.onBoundsChange}
                    />

                <ActivitiesPerformed state={this.props.state} year={this.props.year} />

        );
    }
});
module.exports = MapPaneComponent;<commit_msg>Adding pesticide data source and notes<commit_after>"use strict";
import React from "react";
import ReactDOM from "react-dom";
import debugFactory from "debug";
const debug = debugFactory('app:components:MapPane');
const stateData = require('./states');
import {Grid, Row, Col, Button, PageHeader, Input, Glyphicon, DropdownButton, MenuItem, Panel} from "react-bootstrap";

import Map from "./map";
import YearSelector from "./YearSelector";
import StateSelector from "./StateSelector";
import ActivitiesPerformed from "./ActivitiesPerformed";

let MapPaneComponent = React.createClass({

    propTypes: {
        onCenterChange: React.PropTypes.func.isRequired,
        onStateChange: React.PropTypes.func.isRequired,
        onZoomChange: React.PropTypes.func.isRequired,
        onYearChange: React.PropTypes.func.isRequired,
        onBoundsChange: React.PropTypes.func.isRequired,
        state: React.PropTypes.string.isRequired,
        lat: React.PropTypes.number.isRequired,
        lng: React.PropTypes.number.isRequired,
        zoom: React.PropTypes.number.isRequired,
        year: React.PropTypes.number.isRequired,
    },

    getInitialState() {
        return {
            acquiringLocation: false,
        };
    },

    componentDidMount() {
        this.initialLoadState(stateData.statesByCode[this.props.state]);
    },

    onLocateMe() {
        //TODO: need to use geocoding to lookup state from coords
        this.setState({selectedState: null});
        if (navigator.geolocation) {
            this.setState({acquiringLocation: true});
            navigator.geolocation.getCurrentPosition((position) => {
                this.setState({acquiringLocation: false});
                const center = {
                    lat: position.coords.latitude,
                    lng: position.coords.longitude,
                };
                this.refs.map.setCenter(center);
                // TODO: signal state change using geocoding
                this.props.onCenterChange(center);
            });
        } else {
            this.setState({alert: "Geolocation is not supported by this browser."});
        }
    },

    initialLoadState(state) {
        debug('loading initial state', state);
        this.setState({selectedState: state});
        this.refs.map.enable(state.polygon);
        this.props.onStateChange(state.code);
    },

    loadState(state, requestedBounds) {
        debug('loading state', state);

        // Default to the state bounds
        let bounds = {
            sw: {
                lat: state.bounds.minLat,
                lng: state.bounds.minLng,
            },
            ne: {
                lat: state.bounds.maxLat,
                lng: state.bounds.maxLng,
            }
        };
        // If we have some bounds that have been requested by the state selector
        if (requestedBounds) {
            // We only have a center point, do some finagling to get it to center better
            if (requestedBounds.c) {
                bounds = {
                    // Some magic numbers to get a closer view on a given item
                    sw: {
                        lat: requestedBounds.c.lat * .9999,
                        lng: requestedBounds.c.lng * 1.0001
                    },
                    ne: {
                        lat: requestedBounds.c.lat * 1.0001,
                        lng: requestedBounds.c.lng * .9999
                    }
                }
            } else {

                // We have a rectangular bounds use that
                bounds = requestedBounds;
            }
        }

        if (this.state.selectedState) {
            this.refs.map.disable(this.state.selectedState.polygon);
        }

        this.setState({selectedState: state});
        this.refs.map.enable(state.polygon);
        this.refs.map.setBounds(bounds);

        this.props.onStateChange(state.code);
    },

    onSelectState(state, bounds) {
        if (state) {
            debug('Selected State', state);
            this.loadState(state, bounds);
            this.onStateChange(state);
        }
    },

    render() {
        return (

Where's your farm?

                    <StateSelector onStateSelected={this.onSelectState} />

                    <YearSelector
                        year={this.props.year}
                        onYearChange={this.props.onYearChange}
                    />

                    <Map ref="map"
                         year={this.props.year}
                         lat={this.props.lat}
                         lng={this.props.lng}
                         zoom={this.props.zoom}
                         onCenterChange={this.props.onCenterChange}
                         onZoomChange={this.props.onZoomChange}
                         onBoundsChange={this.props.onBoundsChange}
                    />

                <ActivitiesPerformed state={this.props.state} year={this.props.year} />

Pesticide Data: USGS Estimated Annual Agricultural Pesticide Use for Counties of the Conterminous United States

Currently displays data for Atrazine.

Clicking on a county will display the amount of pesticides used for a given year in kilograms.

        );
    }
});
module.exports = MapPaneComponent;
<|endoftext|>


<commit_before>import { init, captureException, configureScope } from '@sentry/browser';

import { LOG_ERRORS, RAVEN_ENDPT, RELEASE } from '../constants';

export const setup = () => {
  if (LOG_ERRORS) {
    /* eslint-disable no-underscore-dangle */
    if (!window.__SENTRY_READY__) {
      init({ dsn: RAVEN_ENDPT });
      window.__SENTRY_READY__ = true;
    }
    /* eslint-enable no-underscore-dangle */
  }
};

export default (ex, context) => {
  if (LOG_ERRORS) {
    setup(); // memoized, it is fine to call this on every log

    configureScope(scope => scope.setExtra('release', RELEASE));

    if (context) {
      configureScope(scope => scope.setExtra('context', context));
    }

    captureException(ex);
  }

  return window.console && console.error && console.error(ex);
};
<commit_msg>Set release attr in correct place<commit_after>import { init, captureException, configureScope } from '@sentry/browser';

import { LOG_ERRORS, RAVEN_ENDPT, RELEASE } from '../constants';

export const setup = () => {
  if (LOG_ERRORS) {
    /* eslint-disable no-underscore-dangle */
    if (!window.__SENTRY_READY__) {
      init({ dsn: RAVEN_ENDPT, release: RELEASE });
      window.__SENTRY_READY__ = true;
    }
    /* eslint-enable no-underscore-dangle */
  }
};

export default (ex, context) => {
  if (LOG_ERRORS) {
    setup(); // memoized, it is fine to call this on every log

    if (context) {
      configureScope(scope => scope.setExtra('context', context));
    }

    captureException(ex);
  }

  return window.console && console.error && console.error(ex);
};
<|endoftext|>


<commit_before>angular.module('Shazam2Spotify').factory('ChromeHelper', function() {
	var ChromeHelper = {
		focusOrCreateTab: function(url) {
		  chrome.windows.getAll({"populate":true}, function(windows) {
		    var existing_tab = null;
		    for (var i in windows) {
		      var tabs = windows[i].tabs;
		      for (var j in tabs) {
		        var tab = tabs[j];
		        if (tab.url == url) {
		          existing_tab = tab;
		          break;
		        }
		      }
		    }
		    if (existing_tab) {
		      chrome.tabs.update(existing_tab.id, {"selected":true});
		    } else {
		      chrome.tabs.create({"url":url, "selected":true});
		    }
		  });
		},

		exportData: function(fileName, data) {
			window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;

			window.requestFileSystem(window.TEMPORARY, 5*1024*1024, function(fs) {
				fs.root.getFile(fileName, {create: true}, function(fileEntry) { // test.bin is filename
				    fileEntry.createWriter(function(fileWriter) {

				        var truncated = false;
				        fileWriter.onwriteend = function(err) {
				        	if (!truncated) {
					            truncated = true;
					            this.truncate(this.position);
					            return;
					        }

				            ChromeHelper.focusOrCreateTab(fileEntry.toURL());
				        };

				        fileWriter.onerror = function(error) {
					        console.error(error);
					    };

				        var blob = new Blob([data], {type: 'text/plain'});
				        fileWriter.write(blob);

				    }, function(error) {
						console.error(error);
					});
				}, function(error) {
					console.error(error);
				});
			}, function(error) {
				console.error(error);
			});
		}
	};

	return ChromeHelper;
});<commit_msg>Exported logs always open in new tab<commit_after>angular.module('Shazam2Spotify').factory('ChromeHelper', function() {
	var ChromeHelper = {
		findExistingTab: function(url, callback) {
			chrome.windows.getAll({'populate': true}, function(windows) {
				var existing_tab = null;
				for (var i in windows) {
					var tabs = windows[i].tabs;
					for (var j in tabs) {
						var tab = tabs[j];
						if (tab.url == url) {
							existing_tab = tab;
							break;
						}
					}
				}

				callback(existing_tab);
			});
		},

		// Remove existing tab, and recreate it
		removeAndCreateTab: function(url) {
			ChromeHelper.findExistingTab(url, function(existing_tab) {
				if (existing_tab) {
					chrome.tabs.remove(existing_tab.id);
				}

				chrome.tabs.create({'url': url, 'selected': true});
			});
		},

		// Focus existing tab or create it
		focusOrCreateTab: function(url) {
			ChromeHelper.findExistingTab(url, function(existing_tab) {
				if (existing_tab) {
					chrome.tabs.reload(existing_tab.id, {'bypassCache': true});
					chrome.tabs.update(existing_tab.id, {'selected': true});
				} else {
					chrome.tabs.create({'url': url, 'selected': true});
				}
			});
		},

		exportData: function(fileName, data) {
			window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;

			window.requestFileSystem(window.TEMPORARY, 5*1024*1024, function(fs) {
				fs.root.getFile(fileName, {create: true}, function(fileEntry) { // test.bin is filename
				    fileEntry.createWriter(function(fileWriter) {

				        var truncated = false;
				        fileWriter.onwriteend = function(err) {
				        	if (!truncated) {
					            truncated = true;
					            this.truncate(this.position);
					            return;
					        }

				            ChromeHelper.removeAndCreateTab(fileEntry.toURL());
				        };

				        fileWriter.onerror = function(error) {
					        console.error(error);
					    };

				        var blob = new Blob([data], {type: 'text/plain'});
				        fileWriter.write(blob);

				    }, function(error) {
						console.error(error);
					});
				}, function(error) {
					console.error(error);
				});
			}, function(error) {
				console.error(error);
			});
		}
	};

	return ChromeHelper;
});<|endoftext|>


<commit_before>'use strict'
var isNode = require('vigour-js/lib/util/is/node')
if (!isNode) {
  var Base = require('vigour-js/lib/base')
  var Emitter = require('./emitter')
  var Event = require('vigour-js/lib/event')
  // Event.prototype.properties

  module.exports = new Emitter({
    properties: {
      type: new Base({
        inject: require('../cases/inject')
      })
    },
    define: {
      generateConstructor () {
        return function DerivedEmitter (val, ev, parent, key) {
          if (parent) {
            let type = this.type.val
            if (!type) {
              console.warn('No event type defined on', this.path)
              return
            }
            parent.setKey(type, {
              basic (e, event) {
                this.emit(key, void 0, event)
              }
            }, ev)
          }
          return Emitter.apply(this, arguments)
        }
      }
    }
  }).Constructor
}
<commit_msg>clean up<commit_after>'use strict'
var isNode = require('vigour-js/lib/util/is/node')
if (!isNode) {
  var Base = require('vigour-js/lib/base')
  var Emitter = require('./emitter')
  var Event = require('vigour-js/lib/event')
  // Event.prototype.properties

  module.exports = new Emitter({
    properties: {
      type: new Base({
        inject: require('../cases/inject')
      })
    },
    define: {
      generateConstructor () {
        return function DerivedEmitter (val, ev, parent, key) {
          if (parent) {
            let type = this.type.val
            if (!type) {
              console.warn('No event type defined on', this.path)
              return
            }
            parent.setKey(type, {
              basic (e, event) {
                this.emit(key, e, event)
              }
            }, ev)
          }
          return Emitter.apply(this, arguments)
        }
      }
    }
  }).Constructor
}
<|endoftext|>


<commit_before>'use strict';

angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService) {

  var HISTORY_SHOW_LIMIT = 10;
  var currentTxHistoryPage = 0;
  var listeners = [];
  $scope.txps = [];
  $scope.completeTxHistory = [];
  $scope.openTxpModal = txpModalService.open;
  $scope.isCordova = platformInfo.isCordova;
  $scope.isAndroid = platformInfo.isAndroid;
  $scope.isIOS = platformInfo.isIOS;

  $scope.amountIsCollapsible = !$scope.isAndroid;

  $scope.openExternalLink = function(url, target) {
    externalLinkService.open(url, target);
  };

  var setPendingTxps = function(txps) {

    /* Uncomment to test multiple outputs */

    // var txp = {
    //   message: 'test multi-output',
    //   fee: 1000,
    //   createdOn: new Date() / 1000,
    //   outputs: [],
    //   wallet: $scope.wallet
    // };
    //
    // function addOutput(n) {
    //   txp.outputs.push({
    //     amount: 600,
    //     toAddress: '2N8bhEwbKtMvR2jqMRcTCQqzHP6zXGToXcK',
    //     message: 'output #' + (Number(n) + 1)
    //   });
    // };
    // lodash.times(15, addOutput);
    // txps.push(txp);

    if (!txps) {
      $scope.txps = [];
      return;
    }
    $scope.txps = lodash.sortBy(txps, 'createdOn').reverse();
  };

  var updateStatus = function(force) {
    $scope.updatingStatus = true;
    $scope.updateStatusError = null;
    $scope.walletNotRegistered = false;

    walletService.getStatus($scope.wallet, {
      force: !!force,
    }, function(err, status) {
      $scope.updatingStatus = false;
      if (err) {
        if (err === 'WALLET_NOT_REGISTERED') {
          $scope.walletNotRegistered = true;
        } else {
          $scope.updateStatusError = bwcError.msg(err, gettextCatalog.getString('Could not update wallet'));
        }
        $scope.status = null;
      } else {
        setPendingTxps(status.pendingTxps);
        $scope.status = status;
      }
      refreshAmountSection();
      $timeout(function() {
        $scope.$apply();
      });

    });
  };

  $scope.openSearchModal = function() {
    $scope.color = $scope.wallet.color;
    $scope.isSearching = true;
    $scope.txHistorySearchResults = [];
    $scope.filteredTxHistory = [];

    $ionicModal.fromTemplateUrl('views/modals/search.html', {
      scope: $scope,
      focusFirstInput: true
    }).then(function(modal) {
      $scope.searchModal = modal;
      $scope.searchModal.show();
    });

    $scope.close = function() {
      $scope.isSearching = false;
      $scope.searchModal.hide();
    };

    $scope.openTx = function(tx) {
      $ionicHistory.nextViewOptions({
        disableAnimate: true
      });
      $scope.close();
      $scope.openTxModal(tx);
    };
  };

  $scope.openTxModal = function(btx) {
    $scope.btx = lodash.cloneDeep(btx);
    $scope.walletId = $scope.wallet.id;
    $state.transitionTo('tabs.wallet.tx-details', {
      txid: $scope.btx.txid,
      walletId: $scope.walletId
    });
  };

  $scope.openBalanceModal = function() {
    $ionicModal.fromTemplateUrl('views/modals/wallet-balance.html', {
      scope: $scope
    }).then(function(modal) {
      $scope.walletBalanceModal = modal;
      $scope.walletBalanceModal.show();
    });

    $scope.close = function() {
      $scope.walletBalanceModal.hide();
    };
  };

  $scope.recreate = function() {
    walletService.recreate($scope.wallet, function(err) {
      if (err) return;
      $timeout(function() {
        walletService.startScan($scope.wallet, function() {
          $scope.updateAll();
          $scope.$apply();
        });
      });
    });
  };

  var updateTxHistory = function(cb) {
    if (!cb) cb = function() {};
    if ($scope.updatingTxHistory) return;

    $scope.updatingTxHistory = true;
    $scope.updateTxHistoryError = false;
    $scope.updatingTxHistoryProgress = 0;

    var progressFn = function(txs, newTxs) {
      $scope.updatingTxHistoryProgress = newTxs;
      $scope.completeTxHistory = txs;
      $scope.showHistory();
      $timeout(function() {
        $scope.$apply();
      });
    };

    $timeout(function() {
      walletService.getTxHistory($scope.wallet, {
        progressFn: progressFn,
      }, function(err, txHistory) {
        $scope.updatingTxHistory = false;
        if (err) {
          $scope.txHistory = null;
          $scope.updateTxHistoryError = true;
          return;
        }
        $scope.completeTxHistory = txHistory;
        $scope.showHistory();
        $scope.$apply();
        return cb();
      });
    });
  };

  $scope.showHistory = function() {
    if ($scope.completeTxHistory) {
      $scope.txHistory = $scope.completeTxHistory.slice(0, (currentTxHistoryPage + 1) * HISTORY_SHOW_LIMIT);
      $scope.txHistoryShowMore = $scope.completeTxHistory.length > $scope.txHistory.length;
    }
  };

  $scope.getDate = function(txCreated) {
    var date = new Date(txCreated * 1000);
    return date;
  };

  $scope.isFirstInGroup = function(index) {
    if (index === 0) {
      return true;
    }
    var curTx = $scope.txHistory[index];
    var prevTx = $scope.txHistory[index - 1];
    return !createdDuringSameMonth(curTx, prevTx);
  };

  $scope.isLastInGroup = function(index) {
    if (index === $scope.txHistory.length - 1) {
      return true;
    }
    return $scope.isFirstInGroup(index + 1);
  };

  $scope.createdDuringSameMonth = function(curTx, prevTx) {
    return timeService.withinSameMonth(curTx.time * 1000, prevTx.time * 1000);
  };

  $scope.createdWithinPastDay = function(time) {
    return timeService.withinPastDay(time);
  };

  $scope.isDateInCurrentMonth = function(date) {
    return timeService.isDateInCurrentMonth(date);
  };

  $scope.isUnconfirmed = function(tx) {
    return !tx.confirmations || tx.confirmations === 0;
  };

  $scope.showMore = function() {
    $timeout(function() {
      currentTxHistoryPage++;
      $scope.showHistory();
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }, 100);
  };

  $scope.onRefresh = function() {
    $timeout(function() {
      $scope.$broadcast('scroll.refreshComplete');
    }, 300);
    $scope.updateAll(true);
  };

  $scope.updateAll = function(force, cb)  {
    updateStatus(force);
    updateTxHistory(cb);
  };

  $scope.hideToggle = function() {
    profileService.toggleHideBalanceFlag($scope.wallet.credentials.walletId, function(err) {
      if (err) $log.error(err);
    });
  };

  var prevPos;

  function getScrollPosition() {
    var scrollPosition = $ionicScrollDelegate.getScrollPosition();
    if (!scrollPosition) {
      $window.requestAnimationFrame(function() {
        getScrollPosition();
      });
      return;
    }
    var pos = scrollPosition.top;
    if (pos === prevPos) {
      $window.requestAnimationFrame(function() {
        getScrollPosition();
      });
      return;
    }
    prevPos = pos;
    refreshAmountSection(pos);
  };

  function refreshAmountSection(scrollPos) {
    $scope.showBalanceButton = false;
    if ($scope.status) {
      $scope.showBalanceButton = ($scope.status.totalBalanceSat != $scope.status.spendableAmount);
    }
    if (!$scope.amountIsCollapsible) {
      var t = ($scope.showBalanceButton ? 15 : 45);
      $scope.amountScale = 'translateY(' + t + 'px)';
      return;
    }

    scrollPos = scrollPos || 0;
    var amountHeight = 210 - scrollPos;
    if (amountHeight < 80) {
      amountHeight = 80;
    }
    var contentMargin = amountHeight;
    if (contentMargin > 210) {
      contentMargin = 210;
    }

    var amountScale = (amountHeight / 210);
    if (amountScale < 0.5) {
      amountScale = 0.5;
    }
    if (amountScale > 1.1) {
      amountScale = 1.1;
    }

    var s = amountScale;

    // Make space for the balance button when it needs to display.
    var TOP_NO_BALANCE_BUTTON = 115;
    var TOP_BALANCE_BUTTON = 30;
    var top = TOP_NO_BALANCE_BUTTON;
    if ($scope.showBalanceButton) {
      top = TOP_BALANCE_BUTTON;
    }

    var amountTop = ((amountScale - 0.7) / 0.7) * top;
    if (amountTop < -10) {
      amountTop = -10;
    }
    if (amountTop > top) {
      amountTop = top;
    }

    var t = amountTop;

    $scope.altAmountOpacity = (amountHeight - 100) / 80;
    $window.requestAnimationFrame(function() {
      $scope.amountHeight = amountHeight + 'px';
      $scope.contentMargin = contentMargin + 'px';
      $scope.amountScale = 'scale3d(' + s + ',' + s + ',' + s + ') translateY(' + t + 'px)';
      $scope.$digest();
      getScrollPosition();
    });
  }

  var scrollWatcherInitialized;

  $scope.$on("$ionicView.enter", function(event, data) {
    if ($scope.isCordova && $scope.isAndroid) setAndroidStatusBarColor();
    if (scrollWatcherInitialized || !$scope.amountIsCollapsible) {
      return;
    }
    scrollWatcherInitialized = true;
  });

  $scope.$on("$ionicView.beforeEnter", function(event, data) {
    $scope.walletId = data.stateParams.walletId;
    $scope.wallet = profileService.getWallet($scope.walletId);
    if (!$scope.wallet) return;
    $scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;

    addressbookService.list(function(err, ab) {
      if (err) $log.error(err);
      $scope.addressbook = ab || {};
    });

    listeners = [
      $rootScope.$on('bwsEvent', function(e, walletId) {
        if (walletId == $scope.wallet.id && e.type != 'NewAddress')
          $scope.updateAll();
      }),
      $rootScope.$on('Local/TxAction', function(e, walletId) {
        if (walletId == $scope.wallet.id)
          $scope.updateAll();
      }),
    ];
  });

  $scope.$on("$ionicView.afterEnter", function(event, data) {
    $scope.updateAll();
    refreshAmountSection();
  });

  $scope.$on("$ionicView.beforeLeave", function(event, data) {
    if ($window.StatusBar) {
      $window.StatusBar.backgroundColorByHexString('#1e3186');
    }
  });

  $scope.$on("$ionicView.leave", function(event, data) {
    lodash.each(listeners, function(x) {
      x();
    });
  });

  function setAndroidStatusBarColor() {
    var SUBTRACT_AMOUNT = 15;
    var rgb = hexToRgb($scope.wallet.color);
    var keys = Object.keys(rgb);
    keys.forEach(function(k) {
      if (rgb[k] - SUBTRACT_AMOUNT < 0) {
        rgb[k] = 0;
      } else {
        rgb[k] -= SUBTRACT_AMOUNT;
      }
    });
    var statusBarColorHexString = rgbToHex(rgb.r, rgb.g, rgb.b);
    if ($window.StatusBar)
      $window.StatusBar.backgroundColorByHexString(statusBarColorHexString);
  }

  function hexToRgb(hex) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shorthandRegex, function(m, r, g, b) {
      return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
      r: parseInt(result[1], 16),
      g: parseInt(result[2], 16),
      b: parseInt(result[3], 16)
    } : null;
  }

  function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
  }

  function rgbToHex(r, g, b) {
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
  }
});
<commit_msg>Fix function reference.<commit_after>'use strict';

angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService) {

  var HISTORY_SHOW_LIMIT = 10;
  var currentTxHistoryPage = 0;
  var listeners = [];
  $scope.txps = [];
  $scope.completeTxHistory = [];
  $scope.openTxpModal = txpModalService.open;
  $scope.isCordova = platformInfo.isCordova;
  $scope.isAndroid = platformInfo.isAndroid;
  $scope.isIOS = platformInfo.isIOS;

  $scope.amountIsCollapsible = !$scope.isAndroid;

  $scope.openExternalLink = function(url, target) {
    externalLinkService.open(url, target);
  };

  var setPendingTxps = function(txps) {

    /* Uncomment to test multiple outputs */

    // var txp = {
    //   message: 'test multi-output',
    //   fee: 1000,
    //   createdOn: new Date() / 1000,
    //   outputs: [],
    //   wallet: $scope.wallet
    // };
    //
    // function addOutput(n) {
    //   txp.outputs.push({
    //     amount: 600,
    //     toAddress: '2N8bhEwbKtMvR2jqMRcTCQqzHP6zXGToXcK',
    //     message: 'output #' + (Number(n) + 1)
    //   });
    // };
    // lodash.times(15, addOutput);
    // txps.push(txp);

    if (!txps) {
      $scope.txps = [];
      return;
    }
    $scope.txps = lodash.sortBy(txps, 'createdOn').reverse();
  };

  var updateStatus = function(force) {
    $scope.updatingStatus = true;
    $scope.updateStatusError = null;
    $scope.walletNotRegistered = false;

    walletService.getStatus($scope.wallet, {
      force: !!force,
    }, function(err, status) {
      $scope.updatingStatus = false;
      if (err) {
        if (err === 'WALLET_NOT_REGISTERED') {
          $scope.walletNotRegistered = true;
        } else {
          $scope.updateStatusError = bwcError.msg(err, gettextCatalog.getString('Could not update wallet'));
        }
        $scope.status = null;
      } else {
        setPendingTxps(status.pendingTxps);
        $scope.status = status;
      }
      refreshAmountSection();
      $timeout(function() {
        $scope.$apply();
      });

    });
  };

  $scope.openSearchModal = function() {
    $scope.color = $scope.wallet.color;
    $scope.isSearching = true;
    $scope.txHistorySearchResults = [];
    $scope.filteredTxHistory = [];

    $ionicModal.fromTemplateUrl('views/modals/search.html', {
      scope: $scope,
      focusFirstInput: true
    }).then(function(modal) {
      $scope.searchModal = modal;
      $scope.searchModal.show();
    });

    $scope.close = function() {
      $scope.isSearching = false;
      $scope.searchModal.hide();
    };

    $scope.openTx = function(tx) {
      $ionicHistory.nextViewOptions({
        disableAnimate: true
      });
      $scope.close();
      $scope.openTxModal(tx);
    };
  };

  $scope.openTxModal = function(btx) {
    $scope.btx = lodash.cloneDeep(btx);
    $scope.walletId = $scope.wallet.id;
    $state.transitionTo('tabs.wallet.tx-details', {
      txid: $scope.btx.txid,
      walletId: $scope.walletId
    });
  };

  $scope.openBalanceModal = function() {
    $ionicModal.fromTemplateUrl('views/modals/wallet-balance.html', {
      scope: $scope
    }).then(function(modal) {
      $scope.walletBalanceModal = modal;
      $scope.walletBalanceModal.show();
    });

    $scope.close = function() {
      $scope.walletBalanceModal.hide();
    };
  };

  $scope.recreate = function() {
    walletService.recreate($scope.wallet, function(err) {
      if (err) return;
      $timeout(function() {
        walletService.startScan($scope.wallet, function() {
          $scope.updateAll();
          $scope.$apply();
        });
      });
    });
  };

  var updateTxHistory = function(cb) {
    if (!cb) cb = function() {};
    if ($scope.updatingTxHistory) return;

    $scope.updatingTxHistory = true;
    $scope.updateTxHistoryError = false;
    $scope.updatingTxHistoryProgress = 0;

    var progressFn = function(txs, newTxs) {
      $scope.updatingTxHistoryProgress = newTxs;
      $scope.completeTxHistory = txs;
      $scope.showHistory();
      $timeout(function() {
        $scope.$apply();
      });
    };

    $timeout(function() {
      walletService.getTxHistory($scope.wallet, {
        progressFn: progressFn,
      }, function(err, txHistory) {
        $scope.updatingTxHistory = false;
        if (err) {
          $scope.txHistory = null;
          $scope.updateTxHistoryError = true;
          return;
        }
        $scope.completeTxHistory = txHistory;
        $scope.showHistory();
        $scope.$apply();
        return cb();
      });
    });
  };

  $scope.showHistory = function() {
    if ($scope.completeTxHistory) {
      $scope.txHistory = $scope.completeTxHistory.slice(0, (currentTxHistoryPage + 1) * HISTORY_SHOW_LIMIT);
      $scope.txHistoryShowMore = $scope.completeTxHistory.length > $scope.txHistory.length;
    }
  };

  $scope.getDate = function(txCreated) {
    var date = new Date(txCreated * 1000);
    return date;
  };

  $scope.isFirstInGroup = function(index) {
    if (index === 0) {
      return true;
    }
    var curTx = $scope.txHistory[index];
    var prevTx = $scope.txHistory[index - 1];
    return !$scope.createdDuringSameMonth(curTx, prevTx);
  };

  $scope.isLastInGroup = function(index) {
    if (index === $scope.txHistory.length - 1) {
      return true;
    }
    return $scope.isFirstInGroup(index + 1);
  };

  $scope.createdDuringSameMonth = function(curTx, prevTx) {
    return timeService.withinSameMonth(curTx.time * 1000, prevTx.time * 1000);
  };

  $scope.createdWithinPastDay = function(time) {
    return timeService.withinPastDay(time);
  };

  $scope.isDateInCurrentMonth = function(date) {
    return timeService.isDateInCurrentMonth(date);
  };

  $scope.isUnconfirmed = function(tx) {
    return !tx.confirmations || tx.confirmations === 0;
  };

  $scope.showMore = function() {
    $timeout(function() {
      currentTxHistoryPage++;
      $scope.showHistory();
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }, 100);
  };

  $scope.onRefresh = function() {
    $timeout(function() {
      $scope.$broadcast('scroll.refreshComplete');
    }, 300);
    $scope.updateAll(true);
  };

  $scope.updateAll = function(force, cb)  {
    updateStatus(force);
    updateTxHistory(cb);
  };

  $scope.hideToggle = function() {
    profileService.toggleHideBalanceFlag($scope.wallet.credentials.walletId, function(err) {
      if (err) $log.error(err);
    });
  };

  var prevPos;

  function getScrollPosition() {
    var scrollPosition = $ionicScrollDelegate.getScrollPosition();
    if (!scrollPosition) {
      $window.requestAnimationFrame(function() {
        getScrollPosition();
      });
      return;
    }
    var pos = scrollPosition.top;
    if (pos === prevPos) {
      $window.requestAnimationFrame(function() {
        getScrollPosition();
      });
      return;
    }
    prevPos = pos;
    refreshAmountSection(pos);
  };

  function refreshAmountSection(scrollPos) {
    $scope.showBalanceButton = false;
    if ($scope.status) {
      $scope.showBalanceButton = ($scope.status.totalBalanceSat != $scope.status.spendableAmount);
    }
    if (!$scope.amountIsCollapsible) {
      var t = ($scope.showBalanceButton ? 15 : 45);
      $scope.amountScale = 'translateY(' + t + 'px)';
      return;
    }

    scrollPos = scrollPos || 0;
    var amountHeight = 210 - scrollPos;
    if (amountHeight < 80) {
      amountHeight = 80;
    }
    var contentMargin = amountHeight;
    if (contentMargin > 210) {
      contentMargin = 210;
    }

    var amountScale = (amountHeight / 210);
    if (amountScale < 0.5) {
      amountScale = 0.5;
    }
    if (amountScale > 1.1) {
      amountScale = 1.1;
    }

    var s = amountScale;

    // Make space for the balance button when it needs to display.
    var TOP_NO_BALANCE_BUTTON = 115;
    var TOP_BALANCE_BUTTON = 30;
    var top = TOP_NO_BALANCE_BUTTON;
    if ($scope.showBalanceButton) {
      top = TOP_BALANCE_BUTTON;
    }

    var amountTop = ((amountScale - 0.7) / 0.7) * top;
    if (amountTop < -10) {
      amountTop = -10;
    }
    if (amountTop > top) {
      amountTop = top;
    }

    var t = amountTop;

    $scope.altAmountOpacity = (amountHeight - 100) / 80;
    $window.requestAnimationFrame(function() {
      $scope.amountHeight = amountHeight + 'px';
      $scope.contentMargin = contentMargin + 'px';
      $scope.amountScale = 'scale3d(' + s + ',' + s + ',' + s + ') translateY(' + t + 'px)';
      $scope.$digest();
      getScrollPosition();
    });
  }

  var scrollWatcherInitialized;

  $scope.$on("$ionicView.enter", function(event, data) {
    if ($scope.isCordova && $scope.isAndroid) setAndroidStatusBarColor();
    if (scrollWatcherInitialized || !$scope.amountIsCollapsible) {
      return;
    }
    scrollWatcherInitialized = true;
  });

  $scope.$on("$ionicView.beforeEnter", function(event, data) {
    $scope.walletId = data.stateParams.walletId;
    $scope.wallet = profileService.getWallet($scope.walletId);
    if (!$scope.wallet) return;
    $scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;

    addressbookService.list(function(err, ab) {
      if (err) $log.error(err);
      $scope.addressbook = ab || {};
    });

    listeners = [
      $rootScope.$on('bwsEvent', function(e, walletId) {
        if (walletId == $scope.wallet.id && e.type != 'NewAddress')
          $scope.updateAll();
      }),
      $rootScope.$on('Local/TxAction', function(e, walletId) {
        if (walletId == $scope.wallet.id)
          $scope.updateAll();
      }),
    ];
  });

  $scope.$on("$ionicView.afterEnter", function(event, data) {
    $scope.updateAll();
    refreshAmountSection();
  });

  $scope.$on("$ionicView.beforeLeave", function(event, data) {
    if ($window.StatusBar) {
      $window.StatusBar.backgroundColorByHexString('#1e3186');
    }
  });

  $scope.$on("$ionicView.leave", function(event, data) {
    lodash.each(listeners, function(x) {
      x();
    });
  });

  function setAndroidStatusBarColor() {
    var SUBTRACT_AMOUNT = 15;
    var rgb = hexToRgb($scope.wallet.color);
    var keys = Object.keys(rgb);
    keys.forEach(function(k) {
      if (rgb[k] - SUBTRACT_AMOUNT < 0) {
        rgb[k] = 0;
      } else {
        rgb[k] -= SUBTRACT_AMOUNT;
      }
    });
    var statusBarColorHexString = rgbToHex(rgb.r, rgb.g, rgb.b);
    if ($window.StatusBar)
      $window.StatusBar.backgroundColorByHexString(statusBarColorHexString);
  }

  function hexToRgb(hex) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shorthandRegex, function(m, r, g, b) {
      return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
      r: parseInt(result[1], 16),
      g: parseInt(result[2], 16),
      b: parseInt(result[3], 16)
    } : null;
  }

  function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
  }

  function rgbToHex(r, g, b) {
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
  }
});
<|endoftext|>


<commit_before>// \\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
//   /$$      /$$  /$$$$$$  /$$   /$$ /$$   /$$ /$$$$$$$$ /$$$$$$  /$$$$$$ /$$   /$$ /$$$$$$$$
//  | $$$    /$$$ /$$__  $$| $$  | $$| $$$ | $$|__  $$__//$$__  $$|_  $$_/| $$$ | $$|_____ $$
//  | $$$$  /$$$$| $$  \ $$| $$  | $$| $$$$| $$   | $$  | $$  \ $$  | $$  | $$$$| $$     /$$/
//  | $$ $$/$$ $$| $$  | $$| $$  | $$| $$ $$ $$   | $$  | $$$$$$$$  | $$  | $$ $$ $$    /$$/
//  | $$  $$$| $$| $$  | $$| $$  | $$| $$  $$$$   | $$  | $$__  $$  | $$  | $$  $$$$   /$$/
//  | $$\  $ | $$| $$  | $$| $$  | $$| $$\  $$$   | $$  | $$  | $$  | $$  | $$\  $$$  /$$/
//  | $$ \/  | $$|  $$$$$$/|  $$$$$$/| $$ \  $$   | $$  | $$  | $$ /$$$$$$| $$ \  $$ /$$$$$$$$
//  |__/     |__/ \______/  \______/ |__/  \__/   |__/  |__/  |__/|______/|__/  \__/|________/
//
// \\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// File: verify.js
// Purpose: Verification service.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, v. 2.0.

const CryptoJS = require('crypto-js')
const Discord = require('discord.js')
const firebase = require('firebase')
const requestSync = require('sync-request')
const settings = JSON.parse(requestSync('GET', process.env.settingsJSON).getBody('utf8'))

module.exports = {
  name: 'verify',
  description: 'Verification service for guilds.',
  enabled: true,
  process: async function (serviceExports) {
    const client = serviceExports.client
    const app = serviceExports.app

    firebase.initializeApp(settings.firebaseConfig)

    client.on('guildMemberAdd', member => {
      firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
        if (!snapshot.val()) return
        firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (snapshot) {
          if (!snapshot.val()) member.send(`https://kalissaac.github.io/mountainz/verfico/user?status=add&id=${CryptoJS.AES.encrypt(member.id, 'secret key 123')}`, new Discord.Attachment('./assets/verfico.png'))
          else if (snapshot.val().blacklist === true) member.kick('Blacklisted from Mountainz.')
          else updateMemberRoles(member)
        })
      })
    })

    client.on('message', message => {
      if (!message.guild && message.author.id === '233760849381163010' && message.content === '*updateAllRoles') {
        client.guilds.forEach(guild => {
          firebase.database().ref('hsrr/guild/' + guild.id).once('value').then(function (guildSnapshot) {
            if (!guildSnapshot.val()) return
            guild.members.forEach(member => {
              firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (memberSnapshot) {
                if (!memberSnapshot.val() && member.user.bot === false) member.send(`https://kalissaac.github.io/mountainz/verfico/user?status=add&id=${CryptoJS.AES.encrypt(member.id, 'secret key 123')}`, new Discord.Attachment('./assets/verfico.png'))
                member.guild.roles.findAll('name', guildSnapshot.val().approvedRole).forEach(role => member.addRole(role).catch(console.error))
              })
            })
          })
        })
      }
    })

    app.get('/verify', function (request, response) {
      if (request.query.refresh === 'true') updateAllRoles(client)
      response.send('Thanks. It\'s not healthy to leave a request unresponded to.')
    })
  },
  restricted: false
}

function updateMemberRoles (member) {
  firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
    member.guild.roles.findAll('name', snapshot.val().approvedRole).forEach(role => member.addRole(role).catch(console.error))
  })
}

function updateAllRoles (client) {
  client.guilds.forEach(guild => {
    firebase.database().ref('hsrr/guild/' + guild.id).once('value').then(function (guildSnapshot) {
      if (guildSnapshot.val()) {
        guild.members.forEach(member => {
          firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (memberSnapshot) {
            if (memberSnapshot.val()) {
              member.guild.roles.findAll('name', guildSnapshot.val().approvedRole).forEach(role => member.addRole(role).catch(console.error))
            }
          })
        })
      }
    })
  })
}

// verify module

// firebase.initializeApp(settings['firebase_config'])

// client.on('guildMemberAdd', member => {
//   firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
//     if (snapshot.val()) {
//       firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (snapshot) {
//         if (!snapshot.val()) { // not registered
//           member.send('Welcome to the server! \n Before you begin to participate, we ask that you verify that you\'re a human. All you gotta do is just go to https://kalissaac.github.io/verify/?id=' + CryptoJS.AES.encrypt(member.id, 'secret key 123'))
//         } else if (snapshot.val().blacklist === true) {
//           member.kick('Blacklisted from Mountainz.')
//         } else {
//           firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
//             var roles = member.guild.roles.findAll('name', snapshot.val().approvedRole)
//             for (var i = 0; i < roles.length; i++) {
//               member.addRole(roles[i])
//             }
//           })
//         }
//       })
//     }
//   })
// })
<commit_msg>Ya know just some updates<commit_after>// \\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
//   /$$      /$$  /$$$$$$  /$$   /$$ /$$   /$$ /$$$$$$$$ /$$$$$$  /$$$$$$ /$$   /$$ /$$$$$$$$
//  | $$$    /$$$ /$$__  $$| $$  | $$| $$$ | $$|__  $$__//$$__  $$|_  $$_/| $$$ | $$|_____ $$
//  | $$$$  /$$$$| $$  \ $$| $$  | $$| $$$$| $$   | $$  | $$  \ $$  | $$  | $$$$| $$     /$$/
//  | $$ $$/$$ $$| $$  | $$| $$  | $$| $$ $$ $$   | $$  | $$$$$$$$  | $$  | $$ $$ $$    /$$/
//  | $$  $$$| $$| $$  | $$| $$  | $$| $$  $$$$   | $$  | $$__  $$  | $$  | $$  $$$$   /$$/
//  | $$\  $ | $$| $$  | $$| $$  | $$| $$\  $$$   | $$  | $$  | $$  | $$  | $$\  $$$  /$$/
//  | $$ \/  | $$|  $$$$$$/|  $$$$$$/| $$ \  $$   | $$  | $$  | $$ /$$$$$$| $$ \  $$ /$$$$$$$$
//  |__/     |__/ \______/  \______/ |__/  \__/   |__/  |__/  |__/|______/|__/  \__/|________/
//
// \\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// File: verify.js
// Purpose: Verification service.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, v. 2.0.

const CryptoJS = require('crypto-js')
const Discord = require('discord.js')
const firebase = require('firebase')
const requestSync = require('sync-request')
const settings = JSON.parse(requestSync('GET', process.env.settingsJSON).getBody('utf8'))

module.exports = {
  name: 'verify',
  description: 'Verification service for guilds.',
  enabled: true,
  process: async function (serviceExports) {
    const client = serviceExports.client
    const app = serviceExports.app

    firebase.initializeApp(settings.firebaseConfig)

    client.on('guildMemberAdd', member => {
      firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
        if (!snapshot.val()) return
        firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (snapshot) {
          if (!snapshot.val()) member.send(`https://kalissaac.github.io/mountainz/verfico/user?status=add&id=${CryptoJS.AES.encrypt(member.id, 'secret key 123')}`, new Discord.Attachment('./assets/verfico.png'))
          else if (snapshot.val().blacklist === true) member.kick('Blacklisted from Mountainz.')
          else updateMemberRoles(member)
        })
      })
    })

    client.on('message', message => {
      if (!message.guild && message.author.id === '233760849381163010' && message.content === 'hey do a verfico update') {
        client.guilds.forEach(guild => {
          firebase.database().ref('hsrr/guild/' + guild.id).once('value').then(function (guildSnapshot) {
            if (guildSnapshot.val()) {
              guild.members.forEach(member => {
                firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (memberSnapshot) {
                  if (memberSnapshot.val()) {
                    member.guild.roles.findAll('name', guildSnapshot.val().approvedRole).forEach(role => member.addRole(role).catch(console.error))
                  } else if (!member.user.bot) {
                    member.send(`https://kalissaac.github.io/mountainz/verfico/user?status=add&id=${CryptoJS.AES.encrypt(member.id, 'secret key 123')}`, new Discord.Attachment('./assets/verfico.png'))
                  }
                })
              })
            }
          })
        })
      }
    })

    app.get('/verify', function (request, response) {
      if (request.query.refresh === 'true') updateAllRoles(client)
      response.send(JSON.parse('{"response": "Thanks. It\'s not healthy to leave a request unresponded to."}'))
    })
  },
  restricted: false
}

function updateMemberRoles (member) {
  firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
    member.guild.roles.findAll('name', snapshot.val().approvedRole).forEach(role => member.addRole(role).catch(console.error))
  })
}

function updateAllRoles (client) {
  client.guilds.forEach(guild => {
    firebase.database().ref('hsrr/guild/' + guild.id).once('value').then(function (guildSnapshot) {
      if (guildSnapshot.val()) {
        guild.members.forEach(member => {
          firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (memberSnapshot) {
            if (memberSnapshot.val()) {
              member.guild.roles.findAll('name', guildSnapshot.val().approvedRole).forEach(role => member.addRole(role).catch(console.error))
            }
          })
        })
      }
    })
  })
}

// verify module

// firebase.initializeApp(settings['firebase_config'])

// client.on('guildMemberAdd', member => {
//   firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
//     if (snapshot.val()) {
//       firebase.database().ref('hsrr/id/' + member.id).once('value').then(function (snapshot) {
//         if (!snapshot.val()) { // not registered
//           member.send('Welcome to the server! \n Before you begin to participate, we ask that you verify that you\'re a human. All you gotta do is just go to https://kalissaac.github.io/verify/?id=' + CryptoJS.AES.encrypt(member.id, 'secret key 123'))
//         } else if (snapshot.val().blacklist === true) {
//           member.kick('Blacklisted from Mountainz.')
//         } else {
//           firebase.database().ref('hsrr/guild/' + member.guild.id).once('value').then(function (snapshot) {
//             var roles = member.guild.roles.findAll('name', snapshot.val().approvedRole)
//             for (var i = 0; i < roles.length; i++) {
//               member.addRole(roles[i])
//             }
//           })
//         }
//       })
//     }
//   })
// })
<|endoftext|>