<commit_before>var phantom = require('phantom')
  , http = require('http')
  , url = require('url')
  , _ = require('lodash');

var prerender = exports = module.exports = {};

prerender.createServer = function() {
    this.plugins = this.plugins || [];

    this.createPhantom();
};

prerender.use = function(plugin) {
    this.plugins = this.plugins || [];

    this.plugins.push(plugin);
    if (typeof plugin.init === 'function') plugin.init();
};

prerender.createPhantom = function() {
    phantom.create('--load-images=false', { binary: require('phantomjs').path }, _.bind(this.onPhantomCreate, this));
};

prerender.onPhantomCreate = function(phantom) {
    this.phantom = phantom;

    this.createWebServer();
};

prerender.createWebServer = function() {
    var _this = this;

    http.createServer(_.bind(this.onRequest, this)).listen(process.env.PORT || 3000);
    console.log('Server running on port ' + (process.env.PORT || 3000));
};

prerender.onRequest = function(req, res) {
    var _this = this;
    res.send = _.bind(this.send, res, req);

    req.prerender = {
        url: this.getUrl(req).substr(1),
        start: new Date()
    };

    console.log('getting', req.prerender.url);

    this.pluginsBeforePhantomRequest(req, res, function(){

        _this.phantom.createPage(function(page){
            req.prerender.page = page;
            _this.onPhantomPageCreate(req, res);
        });
    });
};

prerender.getUrl = function(req) {
    if (req.url.indexOf('_escaped_fragment_') === -1) return req.url;

    var parts = url.parse(req.url, true);

    if(parts.query['_escaped_fragment_']) parts.hash = '#!' + parts.query['_escaped_fragment_'];
    delete parts.query['_escaped_fragment_'];
    delete parts.search;

    return url.format(parts);
};

prerender.pluginsBeforePhantomRequest = function(req, res, callback) {
    var _this = this
      , index = 0
      , next;

    next = function() {
        var layer = _this.plugins[index++];
        if (!layer) return callback();

        if (layer.beforePhantomRequest) {
            layer.beforePhantomRequest(req, res, next);
        } else {
            next();
        }
    }
    next();
};

prerender.pluginsAfterPhantomRequest = function(req, res, callback) {
    var _this = this
      , index = 0
      , next;

    next = function() {
        var layer = _this.plugins[index++];
        if (!layer) return callback();

        if (layer.afterPhantomRequest) {
            layer.afterPhantomRequest(req, res, next);
        } else {
            next();
        }
    }
    next();
};

prerender.onPhantomPageCreate = function(req, res) {
    var _this = this;

    req.prerender.pendingRequests = 0;

    req.prerender.page.set('onResourceRequested', function (requestData) { req.prerender.pendingRequests++; });
    req.prerender.page.set('onResourceReceived', function (response) { if ('end' === response.stage) { req.prerender.pendingRequests--; } });
    req.prerender.page.set('onResourceError', function(resourceError) { req.prerender.pendingRequests--; });

    req.prerender.page.open(req.prerender.url, function(status){
        req.prerender.status = status;
        _this.onPageOpen(req, res);
    });
};

prerender.onPageOpen = function(req, res) {
    var _this = this;

    if (req.prerender.status === 'fail') {
        return res.send(404);
    }

    req.prerender.intervalStart = new Date();
    req.prerender.interval = setInterval(function(){
        _this.checkIfPageIsDoneLoading(req, res);
    }, 50);
};

prerender.send = function(req, statusCode, documentHTML) {

    this.writeHead(statusCode, {'Content-Type': 'text/html;charset=UTF-8'});
    if (documentHTML) this.write(documentHTML);
    this.end();
    if (req.prerender.page) req.prerender.page.close();
    console.log('got', statusCode, 'for', req.prerender.url, 'in', new Date().getTime() - req.prerender.start.getTime() + 'ms')
};

prerender.checkIfPageIsDoneLoading = function(req, res) {
    var _this = this
      , noPendingRequests = req.prerender.pendingRequests <= 0
      , timeout = new Date().getTime() - req.prerender.intervalStart.getTime() > 10000;

    if (noPendingRequests) {
        clearInterval(req.prerender.interval);
        req.prerender.page.evaluate(this.javascriptToExecuteOnPage, function(documentHTML){
            req.prerender.documentHTML = documentHTML;
            _this.onPageEvaluate(req, res);
        });
    } else if (timeout) {
        clearInterval(req.prerender.interval);
        res.send(408);
    }
};

prerender.javascriptToExecuteOnPage = function() {
    try {
        var html = document && document.getElementsByTagName('html');
        if (html && html[0]) {
            return html[0].outerHTML;
        }
        return '';
    } catch (e) {
        return '';
    }
};

prerender.onPageEvaluate = function(req, res) {
    var _this = this;

    if (!req.prerender.documentHTML) {
        return res.send(404);
    }

    this.pluginsAfterPhantomRequest(req, res, function() {

        res.send(200, req.prerender.documentHTML);
    });
};<commit_msg>fixing query string check for _escaped_fragment_ in bad urls<commit_after>var phantom = require('phantom')
  , http = require('http')
  , url = require('url')
  , _ = require('lodash');

var prerender = exports = module.exports = {};

prerender.createServer = function() {
    this.plugins = this.plugins || [];

    this.createPhantom();
};

prerender.use = function(plugin) {
    this.plugins = this.plugins || [];

    this.plugins.push(plugin);
    if (typeof plugin.init === 'function') plugin.init();
};

prerender.createPhantom = function() {
    phantom.create('--load-images=false', { binary: require('phantomjs').path }, _.bind(this.onPhantomCreate, this));
};

prerender.onPhantomCreate = function(phantom) {
    this.phantom = phantom;

    this.createWebServer();
};

prerender.createWebServer = function() {
    var _this = this;

    http.createServer(_.bind(this.onRequest, this)).listen(process.env.PORT || 3000);
    console.log('Server running on port ' + (process.env.PORT || 3000));
};

prerender.onRequest = function(req, res) {
    var _this = this;
    res.send = _.bind(this.send, res, req);

    req.prerender = {
        url: this.getUrl(req).substr(1),
        start: new Date()
    };

    console.log('getting', req.prerender.url);

    this.pluginsBeforePhantomRequest(req, res, function(){

        _this.phantom.createPage(function(page){
            req.prerender.page = page;
            _this.onPhantomPageCreate(req, res);
        });
    });
};

prerender.getUrl = function(req) {
    var parts = url.parse(decodeURIComponent(req.url), true);

    if (!parts.query.hasOwnProperty('_escaped_fragment_')) return req.url;

    if(parts.query['_escaped_fragment_']) parts.hash = '#!' + parts.query['_escaped_fragment_'];
    delete parts.query['_escaped_fragment_'];
    delete parts.search;

    return url.format(parts);
};

prerender.pluginsBeforePhantomRequest = function(req, res, callback) {
    var _this = this
      , index = 0
      , next;

    next = function() {
        var layer = _this.plugins[index++];
        if (!layer) return callback();

        if (layer.beforePhantomRequest) {
            layer.beforePhantomRequest(req, res, next);
        } else {
            next();
        }
    }
    next();
};

prerender.pluginsAfterPhantomRequest = function(req, res, callback) {
    var _this = this
      , index = 0
      , next;

    next = function() {
        var layer = _this.plugins[index++];
        if (!layer) return callback();

        if (layer.afterPhantomRequest) {
            layer.afterPhantomRequest(req, res, next);
        } else {
            next();
        }
    }
    next();
};

prerender.onPhantomPageCreate = function(req, res) {
    var _this = this;

    req.prerender.pendingRequests = 0;

    req.prerender.page.set('onResourceRequested', function (requestData) { req.prerender.pendingRequests++; });
    req.prerender.page.set('onResourceReceived', function (response) { if ('end' === response.stage) { req.prerender.pendingRequests--; } });
    req.prerender.page.set('onResourceError', function(resourceError) { req.prerender.pendingRequests--; });

    req.prerender.page.open(req.prerender.url, function(status){
        req.prerender.status = status;
        _this.onPageOpen(req, res);
    });
};

prerender.onPageOpen = function(req, res) {
    var _this = this;

    if (req.prerender.status === 'fail') {
        return res.send(404);
    }

    req.prerender.intervalStart = new Date();
    req.prerender.interval = setInterval(function(){
        _this.checkIfPageIsDoneLoading(req, res);
    }, 50);
};

prerender.send = function(req, statusCode, documentHTML) {

    this.writeHead(statusCode, {'Content-Type': 'text/html;charset=UTF-8'});
    if (documentHTML) this.write(documentHTML);
    this.end();
    if (req.prerender.page) req.prerender.page.close();
    console.log('got', statusCode, 'for', req.prerender.url, 'in', new Date().getTime() - req.prerender.start.getTime() + 'ms')
};

prerender.checkIfPageIsDoneLoading = function(req, res) {
    var _this = this
      , noPendingRequests = req.prerender.pendingRequests <= 0
      , timeout = new Date().getTime() - req.prerender.intervalStart.getTime() > 10000;

    if (noPendingRequests) {
        clearInterval(req.prerender.interval);
        req.prerender.page.evaluate(this.javascriptToExecuteOnPage, function(documentHTML){
            req.prerender.documentHTML = documentHTML;
            _this.onPageEvaluate(req, res);
        });
    } else if (timeout) {
        clearInterval(req.prerender.interval);
        res.send(408);
    }
};

prerender.javascriptToExecuteOnPage = function() {
    try {
        var html = document && document.getElementsByTagName('html');
        if (html && html[0]) {
            return html[0].outerHTML;
        }
        return '';
    } catch (e) {
        return '';
    }
};

prerender.onPageEvaluate = function(req, res) {
    var _this = this;

    if (!req.prerender.documentHTML) {
        return res.send(404);
    }

    this.pluginsAfterPhantomRequest(req, res, function() {

        res.send(200, req.prerender.documentHTML);
    });
};<|endoftext|>


<commit_before>// Version: 2.3.2
// Latest Source: https://github.com/Czarto/Adwords-Scripts/blob/master/device-bid-adjustments.js
//
// This Google Ads Script will incrementally change location bid adjustments
// based on conversion Value Per Click (VPC) using the Campaign's average VPC
// as a baseline.
//

/***********

MIT License

Copyright (c) 2016-2022 Alex Czartoryski

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

**********/

var LABEL_PROCESSING = "_processing_location";
var LABEL_IGNORE = '';

var BID_INCREMENT = 0.05;       // Value by which to adjust bids
var MIN_CONVERSIONS = 25;       // Minimum conversions needed to adjust bids.
var HIGH_COST = 500;            // or Adjust bids anyway if cost is above HIGH_COST
var MAX_BID_ADJUSTMENT = 2.00;  // Do not increase adjustments above this value
var MIN_BID_ADJUSTMENT = 0.10;  // Do not decrease adjustments below this value

function main() {
    initLabels(); // Create Labels

    Logger.log('Set Location Bids: 30 days');
    setLocationBids("LAST_30_DAYS");

    Logger.log('Set Location Bids: 90 days');
    setLocationBids(LAST_90_DAYS(), TODAY());

    Logger.log('Set Location Bids: Past Year');
    setLocationBids(LAST_YEAR(), TODAY());

    //Logger.log('Set Location Bids: All Time');
    //setLocationBids("ALL_TIME");

    cleanup(); // Remove Labels
}

//
// Set the Processing label
// This keeps track of which bid adjustments have already been processed
// in the case where multiple time-lookback windows are being used
//
function initLabels() {
    checkLabelExists();
    cleanup();

    Logger.log('Initializing labels...');
    var itemsToLabel = [AdsApp.campaigns(), AdsApp.shoppingCampaigns()];

    for (i = 0; i < itemsToLabel.length; i++) {
        var iterator = itemsToLabel[i].withCondition("Status = ENABLED").get();

        while (iterator.hasNext()) {
            iterator.next().applyLabel(LABEL_PROCESSING);
        }
    }
}

//
// Create the processing label if it does not exist
//
function checkLabelExists() {
    var labelIterator = AdsApp.labels().withCondition("Name = '" + LABEL_PROCESSING + "'" ).get();

    if( !labelIterator.hasNext()) {
        AdsApp.createLabel(LABEL_PROCESSING, "AdWords Scripts label used to process bids")
    }
}

//
// Remove Processing label
//
function cleanup() {
    Logger.log('Cleaning up...');

    var cleanupList = [AdsApp.campaigns(), AdsApp.shoppingCampaigns()];

    for (i = 0; i < cleanupList.length; i++) {
      var iterator = cleanupList[i].withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']").get();

      while (iterator.hasNext()) {
        iterator.next().removeLabel(LABEL_PROCESSING);
      }
    }
}

function setLocationBids(dateRange, dateRangeEnd) {

    // Adjust for normal campaigns
    var campaignIterator = getCampaignSelector(dateRange, dateRangeEnd).get();

    Logger.log(' ')
    Logger.log('### ADJUST LOCATION TARGETING BIDS ###');
    Logger.log('Non-Shopping Campaigns');
    Logger.log('Total Campaigns found : ' + campaignIterator.totalNumEntities());

    setLocationBidsForCampaigns(campaignIterator, dateRange, dateRangeEnd);

    // Adjust for Shopping campaigns
    var campaignIterator = getCampaignSelector(dateRange, dateRangeEnd, true).get();

    Logger.log(' ')
    Logger.log('Shopping Campaigns');
    Logger.log('Total Campaigns found : ' + campaignIterator.totalNumEntities());

    setLocationBidsForCampaigns(campaignIterator, dateRange, dateRangeEnd);
}

//
// Sets the location bids for all the campaigns within the CampaignIterator.
//
function setLocationBidsForCampaigns(campaignIterator, dateRange, dateRangeEnd) {

    // TODO: Just do one loop, with the campaign performance report.
    while (campaignIterator.hasNext()) {
        var campaign = campaignIterator.next();
        var campaignId = campaign.getId();

        // Get click and revenue data for the entire campaign
        var report = AdsApp.report(
            "SELECT CampaignId, CampaignName, Clicks, ConversionValue " +
            "FROM CAMPAIGN_PERFORMANCE_REPORT " +
            "WHERE CampaignId = " + campaignId + " " +
            " AND CampaignStatus = 'ENABLED' " +
            "DURING " + dateRangeToString(dateRange, dateRangeEnd));

        var row = report.rows().next();
        var campaignId = row['CampaignId'];
        var campaignName = row['CampaignName'];
        var campaignClicks = row['Clicks'];
        var campaignRevenue = row['ConversionValue'].replace(',','');
        var campaignRevenuePerClick = (campaignClicks == 0 ? 0 : campaignRevenue/campaignClicks);

        // Get click and revenue data for each geo location
        var report = AdsApp.report(
            "SELECT Id, Clicks, Conversions, ConversionValue, Cost, BidModifier " +
            "FROM CAMPAIGN_LOCATION_TARGET_REPORT " +
            "WHERE Id > 0 AND CampaignId = " + campaignId + " " +
            "DURING " + dateRangeToString(dateRange, dateRangeEnd));
        var reportRows = report.rows();

        while(reportRows.hasNext()) {
            var row = reportRows.next();
            var locationId = [[campaignId, row["Id"]]];
            var locationClicks = row['Clicks'];
            var locationConverions = row['Conversions'];
            var locationRevenue = row['ConversionValue'].replace(',','');
            var locationCost = row['Cost'].replace(',','');
            var locationBidModifier = (parseFloat(row['BidModifier']) / 100.0) + 1;
            var locationRevenuePerClick = (locationClicks == 0 ? 0 : locationRevenue/locationClicks);

            if (locationConverions >= MIN_CONVERSIONS || locationCost >= HIGH_COST ) {
                var newBidModifier = (locationRevenuePerClick / campaignRevenuePerClick)
                var isIncreaseNeeded = (newBidModifier > locationBidModifier && locationBidModifier < MAX_BID_ADJUSTMENT);
                var isDecreaseNeeded = (newBidModifier < locationBidModifier && locationBidModifier > MIN_BID_ADJUSTMENT);

                if( isIncreaseNeeded || isDecreaseNeeded ) {
                    var locationIterator = campaign.targeting().targetedLocations().withIds(locationId).get()
                    if( locationIterator.hasNext()) {
                        var location = locationIterator.next();
                        if( isIncreaseNeeded ) {
                            newBidModifier = Math.min(locationBidModifier + BID_INCREMENT, MAX_BID_ADJUSTMENT)
                            location.setBidModifier(newBidModifier);
                        } else if( isDecreaseNeeded ) {
                            newBidModifier = Math.max(locationBidModifier - BID_INCREMENT, MIN_BID_ADJUSTMENT);
                            location.setBidModifier(newBidModifier);
                        }
                    }
                }
            }
        }
    }
}

//
// Returns the CampaignIterator object
//
function getCampaignSelector(dateRange, dateRangeEnd, isShopping) {
    var campaignSelector = isShopping ? AdWordsApp.shoppingCampaigns() : AdWordsApp.campaigns();

    campaignSelector = campaignSelector
        .forDateRange(dateRange, dateRangeEnd)
        .withCondition("Status = ENABLED")
        .withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']");

    if (LABEL_IGNORE.length > 0) {
        campaignSelector = campaignSelector
            .withCondition("LabelNames CONTAINS_NONE ['" + LABEL_IGNORE + "']");
    }

    return campaignSelector;
}

//
// Date range helper function
// Returns today's date
//
function TODAY() {
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth() + 1; // 0-11
    var yyyy = today.getFullYear();

    return { year: yyyy, month: mm, day: dd };
}

//
// Date range helper functions
// Returns date 90 days ago
//
function LAST_90_DAYS() {
    var date = new Date();
    date.setDate(date.getDate() - 90);

    var dd = date.getDate();
    var mm = date.getMonth()+1; // 0-11
    var yyyy = date.getFullYear();

    return {year: yyyy, month: mm, day: dd};
  }

//
// Date range helper functions
// Returns date 1 year ago
//
function LAST_YEAR() {
    var today = TODAY();

    today.year = today.year - 1;
    return today;
}

//
// Date range helper function - Reports
// Returns a date range that will work in the DURING clause of the reporting query langugae
//
function dateRangeToString(dateRange, dateRangeEnd) {
    if( dateRange == "LAST_7_DAYS" || dateRange == "LAST_14_DAYS" || dateRange == "LAST_30_DAYS" ) {
      return dateRange;
    } else if (dateRange == "ALL_TIME" ) {
      return "20000101," + TODAY().year.toString() + ("0" + TODAY().month).slice(-2) + ("0" + TODAY().day).slice(-2);
    } else {
      return dateRange.year.toString() + ("0" + dateRange.month).slice(-2) + ("0" + dateRange.day).slice(-2) + ","
             + dateRangeEnd.year.toString() + ("0" + dateRangeEnd.month).slice(-2) + ("0" + dateRangeEnd.day).slice(-2);
    }
  }<commit_msg>Added todo comment<commit_after>// Version: 2.3.2
// Latest Source: https://github.com/Czarto/Adwords-Scripts/blob/master/device-bid-adjustments.js
//
// This Google Ads Script will incrementally change location bid adjustments
// based on conversion Value Per Click (VPC) using the Campaign's average VPC
// as a baseline.
//

/***********

MIT License

Copyright (c) 2016-2022 Alex Czartoryski

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

**********/

var LABEL_PROCESSING = "_processing_location";
var LABEL_IGNORE = '';

var BID_INCREMENT = 0.05;       // Value by which to adjust bids
var MIN_CONVERSIONS = 25;       // Minimum conversions needed to adjust bids.
var HIGH_COST = 500;            // or Adjust bids anyway if cost is above HIGH_COST
var MAX_BID_ADJUSTMENT = 2.00;  // Do not increase adjustments above this value
var MIN_BID_ADJUSTMENT = 0.10;  // Do not decrease adjustments below this value
// TODO: Instead of "high cost" use a "high CPA". If Conversions < Min Conversions, but CPA is high, then adjust. Otherwise leave alone.

function main() {
    initLabels(); // Create Labels

    Logger.log('Set Location Bids: 30 days');
    setLocationBids("LAST_30_DAYS");

    Logger.log('Set Location Bids: 90 days');
    setLocationBids(LAST_90_DAYS(), TODAY());

    Logger.log('Set Location Bids: Past Year');
    setLocationBids(LAST_YEAR(), TODAY());

    //Logger.log('Set Location Bids: All Time');
    //setLocationBids("ALL_TIME");

    cleanup(); // Remove Labels
}

//
// Set the Processing label
// This keeps track of which bid adjustments have already been processed
// in the case where multiple time-lookback windows are being used
//
function initLabels() {
    checkLabelExists();
    cleanup();

    Logger.log('Initializing labels...');
    var itemsToLabel = [AdsApp.campaigns(), AdsApp.shoppingCampaigns()];

    for (i = 0; i < itemsToLabel.length; i++) {
        var iterator = itemsToLabel[i].withCondition("Status = ENABLED").get();

        while (iterator.hasNext()) {
            iterator.next().applyLabel(LABEL_PROCESSING);
        }
    }
}

//
// Create the processing label if it does not exist
//
function checkLabelExists() {
    var labelIterator = AdsApp.labels().withCondition("Name = '" + LABEL_PROCESSING + "'" ).get();

    if( !labelIterator.hasNext()) {
        AdsApp.createLabel(LABEL_PROCESSING, "AdWords Scripts label used to process bids")
    }
}

//
// Remove Processing label
//
function cleanup() {
    Logger.log('Cleaning up...');

    var cleanupList = [AdsApp.campaigns(), AdsApp.shoppingCampaigns()];

    for (i = 0; i < cleanupList.length; i++) {
      var iterator = cleanupList[i].withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']").get();

      while (iterator.hasNext()) {
        iterator.next().removeLabel(LABEL_PROCESSING);
      }
    }
}

function setLocationBids(dateRange, dateRangeEnd) {

    // Adjust for normal campaigns
    var campaignIterator = getCampaignSelector(dateRange, dateRangeEnd).get();

    Logger.log(' ')
    Logger.log('### ADJUST LOCATION TARGETING BIDS ###');
    Logger.log('Non-Shopping Campaigns');
    Logger.log('Total Campaigns found : ' + campaignIterator.totalNumEntities());

    setLocationBidsForCampaigns(campaignIterator, dateRange, dateRangeEnd);

    // Adjust for Shopping campaigns
    var campaignIterator = getCampaignSelector(dateRange, dateRangeEnd, true).get();

    Logger.log(' ')
    Logger.log('Shopping Campaigns');
    Logger.log('Total Campaigns found : ' + campaignIterator.totalNumEntities());

    setLocationBidsForCampaigns(campaignIterator, dateRange, dateRangeEnd);
}

//
// Sets the location bids for all the campaigns within the CampaignIterator.
//
function setLocationBidsForCampaigns(campaignIterator, dateRange, dateRangeEnd) {

    // TODO: Just do one loop, with the campaign performance report.
    while (campaignIterator.hasNext()) {
        var campaign = campaignIterator.next();
        var campaignId = campaign.getId();

        // Get click and revenue data for the entire campaign
        var report = AdsApp.report(
            "SELECT CampaignId, CampaignName, Clicks, ConversionValue " +
            "FROM CAMPAIGN_PERFORMANCE_REPORT " +
            "WHERE CampaignId = " + campaignId + " " +
            " AND CampaignStatus = 'ENABLED' " +
            "DURING " + dateRangeToString(dateRange, dateRangeEnd));

        var row = report.rows().next();
        var campaignId = row['CampaignId'];
        var campaignName = row['CampaignName'];
        var campaignClicks = row['Clicks'];
        var campaignRevenue = row['ConversionValue'].replace(',','');
        var campaignRevenuePerClick = (campaignClicks == 0 ? 0 : campaignRevenue/campaignClicks);

        // Get click and revenue data for each geo location
        var report = AdsApp.report(
            "SELECT Id, Clicks, Conversions, ConversionValue, Cost, BidModifier " +
            "FROM CAMPAIGN_LOCATION_TARGET_REPORT " +
            "WHERE Id > 0 AND CampaignId = " + campaignId + " " +
            "DURING " + dateRangeToString(dateRange, dateRangeEnd));
        var reportRows = report.rows();

        while(reportRows.hasNext()) {
            var row = reportRows.next();
            var locationId = [[campaignId, row["Id"]]];
            var locationClicks = row['Clicks'];
            var locationConverions = row['Conversions'];
            var locationRevenue = row['ConversionValue'].replace(',','');
            var locationCost = row['Cost'].replace(',','');
            var locationBidModifier = (parseFloat(row['BidModifier']) / 100.0) + 1;
            var locationRevenuePerClick = (locationClicks == 0 ? 0 : locationRevenue/locationClicks);

            if (locationConverions >= MIN_CONVERSIONS || locationCost >= HIGH_COST ) {
                var newBidModifier = (locationRevenuePerClick / campaignRevenuePerClick)
                var isIncreaseNeeded = (newBidModifier > locationBidModifier && locationBidModifier < MAX_BID_ADJUSTMENT);
                var isDecreaseNeeded = (newBidModifier < locationBidModifier && locationBidModifier > MIN_BID_ADJUSTMENT);

                if( isIncreaseNeeded || isDecreaseNeeded ) {
                    var locationIterator = campaign.targeting().targetedLocations().withIds(locationId).get()
                    if( locationIterator.hasNext()) {
                        var location = locationIterator.next();
                        if( isIncreaseNeeded ) {
                            newBidModifier = Math.min(locationBidModifier + BID_INCREMENT, MAX_BID_ADJUSTMENT)
                            location.setBidModifier(newBidModifier);
                        } else if( isDecreaseNeeded ) {
                            newBidModifier = Math.max(locationBidModifier - BID_INCREMENT, MIN_BID_ADJUSTMENT);
                            location.setBidModifier(newBidModifier);
                        }
                    }
                }
            }
        }
    }
}

//
// Returns the CampaignIterator object
//
function getCampaignSelector(dateRange, dateRangeEnd, isShopping) {
    var campaignSelector = isShopping ? AdWordsApp.shoppingCampaigns() : AdWordsApp.campaigns();

    campaignSelector = campaignSelector
        .forDateRange(dateRange, dateRangeEnd)
        .withCondition("Status = ENABLED")
        .withCondition("LabelNames CONTAINS_ANY ['" + LABEL_PROCESSING + "']");

    if (LABEL_IGNORE.length > 0) {
        campaignSelector = campaignSelector
            .withCondition("LabelNames CONTAINS_NONE ['" + LABEL_IGNORE + "']");
    }

    return campaignSelector;
}

//
// Date range helper function
// Returns today's date
//
function TODAY() {
    var today = new Date();
    var dd = today.getDate();
    var mm = today.getMonth() + 1; // 0-11
    var yyyy = today.getFullYear();

    return { year: yyyy, month: mm, day: dd };
}

//
// Date range helper functions
// Returns date 90 days ago
//
function LAST_90_DAYS() {
    var date = new Date();
    date.setDate(date.getDate() - 90);

    var dd = date.getDate();
    var mm = date.getMonth()+1; // 0-11
    var yyyy = date.getFullYear();

    return {year: yyyy, month: mm, day: dd};
  }

//
// Date range helper functions
// Returns date 1 year ago
//
function LAST_YEAR() {
    var today = TODAY();

    today.year = today.year - 1;
    return today;
}

//
// Date range helper function - Reports
// Returns a date range that will work in the DURING clause of the reporting query langugae
//
function dateRangeToString(dateRange, dateRangeEnd) {
    if( dateRange == "LAST_7_DAYS" || dateRange == "LAST_14_DAYS" || dateRange == "LAST_30_DAYS" ) {
      return dateRange;
    } else if (dateRange == "ALL_TIME" ) {
      return "20000101," + TODAY().year.toString() + ("0" + TODAY().month).slice(-2) + ("0" + TODAY().day).slice(-2);
    } else {
      return dateRange.year.toString() + ("0" + dateRange.month).slice(-2) + ("0" + dateRange.day).slice(-2) + ","
             + dateRangeEnd.year.toString() + ("0" + dateRangeEnd.month).slice(-2) + ("0" + dateRangeEnd.day).slice(-2);
    }
  }<|endoftext|>


<commit_before>import * as HeightWidthDepth from "./HeightWidthDepth.js";
//import * as TensorTools from "../util/TensorTools.js";

/**
 * Test different resize implementation for CNN.
 *
 * @see {@link https://jsperf.com/colorfulcakechen-cnn-height-width-resize}
 */

globalThis.testSet_101x101x24 = new HeightWidthDepth.Base( 101, 101, 24 ); // height, width, depth
<commit_msg>Update jsPerf_CNNHeightWidthResize.js<commit_after>import * as HeightWidthDepth from "./HeightWidthDepth.js";
//import * as TensorTools from "../util/TensorTools.js";

/**
 * Test different resize implementation for CNN.
 *
 * @see {@link https://jsperf.com/colorfulcakechen-cnn-height-width-resize}
 */

globalThis.testSet_101x101x24 = new HeightWidthDepth.Base( 101, 101, 240 ); // height, width, depth
<|endoftext|>


<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/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

var EventEmitter = require('events').EventEmitter;
var mod_assert = require('assert');
var mod_backoff = require('backoff');
var mod_bunyan = require('bunyan');
var mod_libuuid = require('libuuid');
var mod_moray = require('moray');
var mod_util = require('util');
var mod_vasync = require('vasync');
var Watershed = require('watershed').Watershed;

var shed = new Watershed();
var pollTime = 2000; // Poll moray bucket every 2 seconds
var gcPollTime = 6361; // Prime number chosen to alleviate overlap with pollTime

/*
 * Publisher module constructor that takes an options object.
 *
 * Example options object:
 * var options = {
 *    backoff: {
 *       maxTimeout: Infinity,
 *       minTimeout: 10,
 *       retries: Infinity
 *    },
 *    log: new Bunyan({
 *       name: 'publisher_test',
 *       level: process.env['LOG_LEVEL'] || 'trace',
 *       stream: process.stderr
 *    }),
 *   maxAge: 28800,
 *   moray: {
 *       bucketName: 'change_feed_bucket',
 *       host: '10.99.99.17',
 *       resolvers: {
 *           resolvers: ['10.99.99.11']
 *       },
 *       timeout: 200,
 *       minTimeout: 1000,
 *       maxTimeout: 2000,
 *       port: 2020
 *   },
 *   restifyServer: server,
 *   resources: resources
 * };
 */
function Publisher(options) {
    var self = this;
    self.pollInterval = null;
    self.gcPollInterval = null;
    EventEmitter.call(self);

    // registrations and websockets track listener details, and are used for GC.
    self.registrations = {};
    self.websockets = {};

    // Users specify maxAge in seconds, but it is easier to work with
    // milliseconds from here on out. We need to also make sure this number is
    // safe for JavaScript.
    self.maxAge = options.maxAge * 1000;

    if (options.backoff) {
        self.minTimeout = options.backoff.minTimeout || 10;
        self.maxTimeout = options.backoff.maxTimeout || Infinity;
        self.retries = options.backoff.retries || Infinity;
    } else {
        self.minTimeout = 10;
        self.maxTimeout = Infinity;
        self.retries = Infinity;
    }

    var morayOptions = options.moray;
    self.morayHost = morayOptions.host;
    self.morayResovlers = morayOptions.resolvers;
    self.morayTimeout = morayOptions.timeout;
    self.morayMinTimeout = morayOptions.minTimeout;
    self.morayMaxTimeout = morayOptions.maxTimeout;
    self.morayPort = morayOptions.port;
    self.morayBucket = {
        name: morayOptions.bucketName,
        config: {
            index: {
                published: { type: 'string' }
            }
        }
    };

    self.resources = options.resources;

    var log = this.log = options.log;
    var morayClient = this.morayClient = mod_moray.createClient({
        dns: this.morayResolvers,
        connectTimeout: this.morayTimeout || 200,
        log: this.log,
        host: this.morayHost,
        port: this.morayPort,
        reconnect: false,
        retry: {
            retries: Infinity,
            minTimeout: this.morayMinTimeout || 1000,
            maxTimeout: this.morayMaxTimeout || 16000
        }
    });

    var expBackoff = mod_backoff.exponential({
        initialDelay: self.minTimeout,
        maxDelay: self.maxTimeout
    });

    expBackoff.failAfter(self.retries);

    morayClient.on('connect', function _morayConnect() {
        log.trace('cf: _morayConnect: started');
        log.info({ moray: morayClient.toString() }, 'cf: moray: connected');
        self.emit('moray-connected');

        // Handles bucket initilization and backoff on failure
        function _bucketInit() {
            var error;
            morayClient.getBucket(self.morayBucket.name, function _gb(err) {
                if (err && err.name === 'BucketNotFoundError') {
                    var name = self.morayBucket.name;
                    var config = self.morayBucket.config;
                    morayClient.createBucket(name, config, function _cb(err2) {
                        log.info({ n: name, c: config }, 'cf: creating bucket');
                        if (err2) {
                            log.error({ cErr: err2 }, 'cf: Bucket not created');
                            expBackoff.backoff();
                        } else {
                            log.info('cf: Bucket successfully setup');
                            self.emit('moray-ready');
                            expBackoff.reset();
                        }
                    });
                } else if (err) {
                    log.error({ cErr: err }, 'cf: Bucket was not loaded');
                    expBackoff.backoff();
                } else {
                    log.info('cf: Bucket successfully setup');
                    self.emit('moray-ready');
                    expBackoff.reset();
                }
            });
        }

        morayClient.on('error', function _morayError(err) {
            log.error(err, 'cf: moray client error');
        });

        expBackoff.on('backoff', function _backoff(number, delay) {
            log.warn('cf: Backoff -- retry count: %s delay: %s', number, delay);
        });

        expBackoff.on('ready', function _ready(number, delay) {
            log.info('cf: Backoff ready -- retry count: %s:', number);
            _bucketInit();
        });

        expBackoff.on('fail', function _fail() {
            log.error('cf: backoff failed');
            self.emit('moray-fail');
        });

        _bucketInit();
    });

    var server = options.restifyServer;
    server.get({
        name: 'changefeeds',
        path: '/changefeeds'
    }, self._getResources.bind(this));
    server.get({
        name: 'changefeeds_stats',
        path: '/changefeeds/stats'
    }, self._getStats.bind(this));
    server.on('upgrade', function _upgrade(req, socket, head) {
        log.trace('cf: upgrade: start');
        var websocket = null;
        try {
            websocket = shed.accept(req, socket, head);
        } catch (ex) {
            log.error('error: ' + ex.message);
            return socket.end();
        }

        // The use of once is deliberate. This should accept no data from the
        // listener after bootstrap.
        websocket.once('text', function _register(text) {
            log.trace('cf: _register: start');
            var registration = JSON.parse(text);
            self.websockets[registration.instance] = websocket;
            self.registrations[registration.instance] = registration;
            var response = null;
            for (var i = 0; i < self.resources.length; i++) {
                var resource = self.resources[i];
                if (resource.resource === registration.changeKind.resource) {
                    log.info('Accepting valid registration response');
                    self.emit('registration');
                    response = resource;
                }
            }
            // If the registration was valid, send the bootstrap response
            if (response) {
                websocket.send(JSON.stringify(response));
            } else {
                var regResource = registration.changeKind.resource;
                log.warn('Invalid registration resource: %s', regResource);
            }
        });

        // When a listener disconnects, for any reason, clean up listeners
        websocket.on('end', function _end() {
            log.trace('cf: _end: start');
            for (var instance in self.registrations) {
                if (this._id === self.websockets[instance]._id) {
                    log.info('Collecting instance: %s', instance);
                    delete self.websockets[instance];
                    delete self.registrations[instance];
                }
            }
        });
        websocket.on('connectionReset', function _connectionReset() {
            log.trace('cf: _connectionReset: start');
            for (var instance in self.registrations) {
                if (this._id === self.websockets[instance]._id) {
                    log.info('Collecting instance: %s', instance);
                    delete self.websockets[instance];
                    delete self.registrations[instance];
                }
            }
        });

        return null;
    });
}

mod_util.inherits(Publisher, EventEmitter);

/*
 * Halts all publishing operations including Moray polling and WebSocket push
 */
Publisher.prototype.stop = function stop() {
    var self = this;
    self.log.trace('cf: stop: start');
    clearInterval(self.pollInterval);
    clearInterval(self.gcPollInterval);
    self.morayClient.close();
};

/*
 * This causes the publisher module to begin polling its moray bucket and push
 * change feed events to registered listeners. Items in the moray bucket are
 * initially marked with `published=no`, and subsequently updated with a
 * published value of date when they were sent to listeners.
 */
Publisher.prototype.start = function start() {
    var self = this;
    self.log.trace('cf: start: start');
    var client = self.morayClient;
    var bucketName = self.morayBucket.name;
    var log = self.log;

    // Start the gc process
    self._gcItems();

    // Start the publishing poller
    self.pollInterval = setInterval(function _poll() {
        self.log.trace('cf: _poll: start');
        var req = client.findObjects(bucketName, '(published=no)');
        req.on('error', function _reqErr(err) {
            log.warn(err);
        });

        req.on('record', function _record(record) {
            self.log.trace({ cfRecord: record }, 'cf: record: start');
            var value = record.value;
            value.published = Date.now().toString();
            var strValue = null;
            try {
                strValue = JSON.stringify(value);
            } catch (ex) {
                log.error('Error serializing value: %s', ex.message);
            }

            // The double for loop is not the most efficent choice, however
            // in practice it shouldn't see enough iterations to matter at this
            // point. The simplicity out weighs the complexity of implementing
            // a more sophisticated structure at this point.
            for (var instance in self.registrations) {
                var regWebsocket = self.websockets[instance];
                var regChangeKind = self.registrations[instance].changeKind;
                var resource = value.changeKind.resource;
                if (regChangeKind.resource === resource) {
                    var regSubResources = regChangeKind.subResources;
                    var subResources = value.changeKind.subResources;
                    for (var i = 0; i < subResources.length; i++) {
                        if (regSubResources.indexOf(subResources[i]) !== -1) {
                            regWebsocket.send(strValue);
                            log.info('Published: %s', strValue);
                            self.emit('item-published');
                            break;
                        }
                    }
                } else {
                    log.info('No registrations for value: %j', value);
                }
            }

            // Mark each change feed item as published so that they aren't
            // re-sent to registered listeners.
            var key = record.key;
            client.putObject(bucketName, key, value, function _mark(err) {
                if (err) {
                    log.warn('Error putting object to moray. Error: %j', err);
                } else {
                    log.info('marking %s published', key);
                }
            });
        });

        req.on('end', function _noItems() {
            self.emit('no-items');
            log.info('findObjects ended');
        });
    }, pollTime);
};

/*
 * Add item to change feed Moray bucket so that it can be picked up and fully
 * published by the polling mechanism in start().
 *
 * Example item:
 * {
 *    changeKind: {
 *        resource: 'vm',
 *        subResources: ['alias']
 *    },
 *    changedResourceId: 'uuid_of_changed_resource'
 * };
 */
Publisher.prototype.publish = function publish(item, cb) {
    mod_assert.ok(item.hasOwnProperty('changeKind'),
        'changeKind object required on item to publish');
    mod_assert.ok(item.hasOwnProperty('changedResourceId'),
        'changedResourceId required on item to publish');

    // Tack on published property for tracking purposes
    item.published = 'no';

    this._putObject(item, function (err) {
        cb(err);
    });
};

Publisher.prototype._getBucket = function _getBucket(cb) {
    this.morayClient.getBucket(this.morayBucket.name, cb);
};

Publisher.prototype._gcItems = function () {
    var self = this;
    var client = self.morayClient;
    var bucketName = self.morayBucket.name;
    var log = self.log;

    self.gcPollInterval = setInterval(function _gcPoll() {
        var sweepDate = Date.now() - self.maxAge;
        var req = client.findObjects(bucketName, '(!(published=no))');
        req.on('error', function _gcReqErr(err) {
            log.warn(err);
        });

        req.on('record', function _gcRecord(record) {
            var value = record.value.published;
            if (parseInt(value, 10) <= sweepDate) {
                client.delObject(bucketName, record.key, function _gcItem(err) {
                    if (err) {
                        log.warn({ gcError: err }, 'Error collecting record');
                    } else {
                        log.info('%s deleted', record.key);
                    }
                });
            }
        });

        req.on('end', function _noGcItems(err) {
            if (err) {
                log.warn('Error deleting object from moray. Error: %j', err);
            } else {
                log.info('No more items to GC');
            }
        });
    }, gcPollTime);
};

Publisher.prototype._getStats = function _getStats(req, res, next) {
    var listenerCount = 0;
    var listenerRegistrations = null;
    if (this.registrations) {
        listenerCount = Object.keys(this.registrations).length;
        listenerRegistrations = this.registrations;
    }

    var stats = {
        listeners: listenerCount,
        registrations: listenerRegistrations
    };
    res.send(stats);
    next();
};

Publisher.prototype._getResources = function _getResources(req, res, next) {
    res.send(this.resources);
    next();
};

Publisher.prototype._putObject = function _putObject(item, cb) {
    var bucket = this.morayBucket;
    this.morayClient.putObject(bucket.name, mod_libuuid.create(), item, cb);
};

module.exports = Publisher;
<commit_msg>Remove unused variable<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/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

var EventEmitter = require('events').EventEmitter;
var mod_assert = require('assert');
var mod_backoff = require('backoff');
var mod_bunyan = require('bunyan');
var mod_libuuid = require('libuuid');
var mod_moray = require('moray');
var mod_util = require('util');
var mod_vasync = require('vasync');
var Watershed = require('watershed').Watershed;

var shed = new Watershed();
var pollTime = 2000; // Poll moray bucket every 2 seconds
var gcPollTime = 6361; // Prime number chosen to alleviate overlap with pollTime

/*
 * Publisher module constructor that takes an options object.
 *
 * Example options object:
 * var options = {
 *    backoff: {
 *       maxTimeout: Infinity,
 *       minTimeout: 10,
 *       retries: Infinity
 *    },
 *    log: new Bunyan({
 *       name: 'publisher_test',
 *       level: process.env['LOG_LEVEL'] || 'trace',
 *       stream: process.stderr
 *    }),
 *   maxAge: 28800,
 *   moray: {
 *       bucketName: 'change_feed_bucket',
 *       host: '10.99.99.17',
 *       resolvers: {
 *           resolvers: ['10.99.99.11']
 *       },
 *       timeout: 200,
 *       minTimeout: 1000,
 *       maxTimeout: 2000,
 *       port: 2020
 *   },
 *   restifyServer: server,
 *   resources: resources
 * };
 */
function Publisher(options) {
    var self = this;
    self.pollInterval = null;
    self.gcPollInterval = null;
    EventEmitter.call(self);

    // registrations and websockets track listener details, and are used for GC.
    self.registrations = {};
    self.websockets = {};

    // Users specify maxAge in seconds, but it is easier to work with
    // milliseconds from here on out. We need to also make sure this number is
    // safe for JavaScript.
    self.maxAge = options.maxAge * 1000;

    if (options.backoff) {
        self.minTimeout = options.backoff.minTimeout || 10;
        self.maxTimeout = options.backoff.maxTimeout || Infinity;
        self.retries = options.backoff.retries || Infinity;
    } else {
        self.minTimeout = 10;
        self.maxTimeout = Infinity;
        self.retries = Infinity;
    }

    var morayOptions = options.moray;
    self.morayHost = morayOptions.host;
    self.morayResovlers = morayOptions.resolvers;
    self.morayTimeout = morayOptions.timeout;
    self.morayMinTimeout = morayOptions.minTimeout;
    self.morayMaxTimeout = morayOptions.maxTimeout;
    self.morayPort = morayOptions.port;
    self.morayBucket = {
        name: morayOptions.bucketName,
        config: {
            index: {
                published: { type: 'string' }
            }
        }
    };

    self.resources = options.resources;

    var log = this.log = options.log;
    var morayClient = this.morayClient = mod_moray.createClient({
        dns: this.morayResolvers,
        connectTimeout: this.morayTimeout || 200,
        log: this.log,
        host: this.morayHost,
        port: this.morayPort,
        reconnect: false,
        retry: {
            retries: Infinity,
            minTimeout: this.morayMinTimeout || 1000,
            maxTimeout: this.morayMaxTimeout || 16000
        }
    });

    var expBackoff = mod_backoff.exponential({
        initialDelay: self.minTimeout,
        maxDelay: self.maxTimeout
    });

    expBackoff.failAfter(self.retries);

    morayClient.on('connect', function _morayConnect() {
        log.trace('cf: _morayConnect: started');
        log.info({ moray: morayClient.toString() }, 'cf: moray: connected');
        self.emit('moray-connected');

        // Handles bucket initilization and backoff on failure
        function _bucketInit() {
            morayClient.getBucket(self.morayBucket.name, function _gb(err) {
                if (err && err.name === 'BucketNotFoundError') {
                    var name = self.morayBucket.name;
                    var config = self.morayBucket.config;
                    morayClient.createBucket(name, config, function _cb(err2) {
                        log.info({ n: name, c: config }, 'cf: creating bucket');
                        if (err2) {
                            log.error({ cErr: err2 }, 'cf: Bucket not created');
                            expBackoff.backoff();
                        } else {
                            log.info('cf: Bucket successfully setup');
                            self.emit('moray-ready');
                            expBackoff.reset();
                        }
                    });
                } else if (err) {
                    log.error({ cErr: err }, 'cf: Bucket was not loaded');
                    expBackoff.backoff();
                } else {
                    log.info('cf: Bucket successfully setup');
                    self.emit('moray-ready');
                    expBackoff.reset();
                }
            });
        }

        morayClient.on('error', function _morayError(err) {
            log.error(err, 'cf: moray client error');
        });

        expBackoff.on('backoff', function _backoff(number, delay) {
            log.warn('cf: Backoff -- retry count: %s delay: %s', number, delay);
        });

        expBackoff.on('ready', function _ready(number, delay) {
            log.info('cf: Backoff ready -- retry count: %s:', number);
            _bucketInit();
        });

        expBackoff.on('fail', function _fail() {
            log.error('cf: backoff failed');
            self.emit('moray-fail');
        });

        _bucketInit();
    });

    var server = options.restifyServer;
    server.get({
        name: 'changefeeds',
        path: '/changefeeds'
    }, self._getResources.bind(this));
    server.get({
        name: 'changefeeds_stats',
        path: '/changefeeds/stats'
    }, self._getStats.bind(this));
    server.on('upgrade', function _upgrade(req, socket, head) {
        log.trace('cf: upgrade: start');
        var websocket = null;
        try {
            websocket = shed.accept(req, socket, head);
        } catch (ex) {
            log.error('error: ' + ex.message);
            return socket.end();
        }

        // The use of once is deliberate. This should accept no data from the
        // listener after bootstrap.
        websocket.once('text', function _register(text) {
            log.trace('cf: _register: start');
            var registration = JSON.parse(text);
            self.websockets[registration.instance] = websocket;
            self.registrations[registration.instance] = registration;
            var response = null;
            for (var i = 0; i < self.resources.length; i++) {
                var resource = self.resources[i];
                if (resource.resource === registration.changeKind.resource) {
                    log.info('Accepting valid registration response');
                    self.emit('registration');
                    response = resource;
                }
            }
            // If the registration was valid, send the bootstrap response
            if (response) {
                websocket.send(JSON.stringify(response));
            } else {
                var regResource = registration.changeKind.resource;
                log.warn('Invalid registration resource: %s', regResource);
            }
        });

        // When a listener disconnects, for any reason, clean up listeners
        websocket.on('end', function _end() {
            log.trace('cf: _end: start');
            for (var instance in self.registrations) {
                if (this._id === self.websockets[instance]._id) {
                    log.info('Collecting instance: %s', instance);
                    delete self.websockets[instance];
                    delete self.registrations[instance];
                }
            }
        });
        websocket.on('connectionReset', function _connectionReset() {
            log.trace('cf: _connectionReset: start');
            for (var instance in self.registrations) {
                if (this._id === self.websockets[instance]._id) {
                    log.info('Collecting instance: %s', instance);
                    delete self.websockets[instance];
                    delete self.registrations[instance];
                }
            }
        });

        return null;
    });
}

mod_util.inherits(Publisher, EventEmitter);

/*
 * Halts all publishing operations including Moray polling and WebSocket push
 */
Publisher.prototype.stop = function stop() {
    var self = this;
    self.log.trace('cf: stop: start');
    clearInterval(self.pollInterval);
    clearInterval(self.gcPollInterval);
    self.morayClient.close();
};

/*
 * This causes the publisher module to begin polling its moray bucket and push
 * change feed events to registered listeners. Items in the moray bucket are
 * initially marked with `published=no`, and subsequently updated with a
 * published value of date when they were sent to listeners.
 */
Publisher.prototype.start = function start() {
    var self = this;
    self.log.trace('cf: start: start');
    var client = self.morayClient;
    var bucketName = self.morayBucket.name;
    var log = self.log;

    // Start the gc process
    self._gcItems();

    // Start the publishing poller
    self.pollInterval = setInterval(function _poll() {
        self.log.trace('cf: _poll: start');
        var req = client.findObjects(bucketName, '(published=no)');
        req.on('error', function _reqErr(err) {
            log.warn(err);
        });

        req.on('record', function _record(record) {
            self.log.trace({ cfRecord: record }, 'cf: record: start');
            var value = record.value;
            value.published = Date.now().toString();
            var strValue = null;
            try {
                strValue = JSON.stringify(value);
            } catch (ex) {
                log.error('Error serializing value: %s', ex.message);
            }

            // The double for loop is not the most efficent choice, however
            // in practice it shouldn't see enough iterations to matter at this
            // point. The simplicity out weighs the complexity of implementing
            // a more sophisticated structure at this point.
            for (var instance in self.registrations) {
                var regWebsocket = self.websockets[instance];
                var regChangeKind = self.registrations[instance].changeKind;
                var resource = value.changeKind.resource;
                if (regChangeKind.resource === resource) {
                    var regSubResources = regChangeKind.subResources;
                    var subResources = value.changeKind.subResources;
                    for (var i = 0; i < subResources.length; i++) {
                        if (regSubResources.indexOf(subResources[i]) !== -1) {
                            regWebsocket.send(strValue);
                            log.info('Published: %s', strValue);
                            self.emit('item-published');
                            break;
                        }
                    }
                } else {
                    log.info('No registrations for value: %j', value);
                }
            }

            // Mark each change feed item as published so that they aren't
            // re-sent to registered listeners.
            var key = record.key;
            client.putObject(bucketName, key, value, function _mark(err) {
                if (err) {
                    log.warn('Error putting object to moray. Error: %j', err);
                } else {
                    log.info('marking %s published', key);
                }
            });
        });

        req.on('end', function _noItems() {
            self.emit('no-items');
            log.info('findObjects ended');
        });
    }, pollTime);
};

/*
 * Add item to change feed Moray bucket so that it can be picked up and fully
 * published by the polling mechanism in start().
 *
 * Example item:
 * {
 *    changeKind: {
 *        resource: 'vm',
 *        subResources: ['alias']
 *    },
 *    changedResourceId: 'uuid_of_changed_resource'
 * };
 */
Publisher.prototype.publish = function publish(item, cb) {
    mod_assert.ok(item.hasOwnProperty('changeKind'),
        'changeKind object required on item to publish');
    mod_assert.ok(item.hasOwnProperty('changedResourceId'),
        'changedResourceId required on item to publish');

    // Tack on published property for tracking purposes
    item.published = 'no';

    this._putObject(item, function (err) {
        cb(err);
    });
};

Publisher.prototype._getBucket = function _getBucket(cb) {
    this.morayClient.getBucket(this.morayBucket.name, cb);
};

Publisher.prototype._gcItems = function () {
    var self = this;
    var client = self.morayClient;
    var bucketName = self.morayBucket.name;
    var log = self.log;

    self.gcPollInterval = setInterval(function _gcPoll() {
        var sweepDate = Date.now() - self.maxAge;
        var req = client.findObjects(bucketName, '(!(published=no))');
        req.on('error', function _gcReqErr(err) {
            log.warn(err);
        });

        req.on('record', function _gcRecord(record) {
            var value = record.value.published;
            if (parseInt(value, 10) <= sweepDate) {
                client.delObject(bucketName, record.key, function _gcItem(err) {
                    if (err) {
                        log.warn({ gcError: err }, 'Error collecting record');
                    } else {
                        log.info('%s deleted', record.key);
                    }
                });
            }
        });

        req.on('end', function _noGcItems(err) {
            if (err) {
                log.warn('Error deleting object from moray. Error: %j', err);
            } else {
                log.info('No more items to GC');
            }
        });
    }, gcPollTime);
};

Publisher.prototype._getStats = function _getStats(req, res, next) {
    var listenerCount = 0;
    var listenerRegistrations = null;
    if (this.registrations) {
        listenerCount = Object.keys(this.registrations).length;
        listenerRegistrations = this.registrations;
    }

    var stats = {
        listeners: listenerCount,
        registrations: listenerRegistrations
    };
    res.send(stats);
    next();
};

Publisher.prototype._getResources = function _getResources(req, res, next) {
    res.send(this.resources);
    next();
};

Publisher.prototype._putObject = function _putObject(item, cb) {
    var bucket = this.morayBucket;
    this.morayClient.putObject(bucket.name, mod_libuuid.create(), item, cb);
};

module.exports = Publisher;
<|endoftext|>


<commit_before>exports.run = (client, message, args) => {
    let day = String(args[0]).toLowerCase();

    switch (day) {
        case 'sun':
        case 'sunday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nComplete Arena Battles (5) \nSave Cantina Energy \nSave Normal Energy
                                 \n== After Reset == \nSpend Cantina Energy \nSave Normal Energy`);
            break;
        case 'mon':
        case 'monday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nSpend Cantina Energy \nSave Normal Energy \nSave Galactic War (unless reset available)
                                 \n== After Reset == \nSpend Normal Energy on Light Side Battles \nSave Galactic War (unless reset available)`);
            break;
        case 'tue':
        case 'tuesday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nSpend Normal Energy on Light Side Battles \nSave Galactic War
                                 \n== After Reset == \nComplete Galactic War Battles (24 with restart) \nSave Normal Energy`);
            break;
        case 'wed':
        case 'wednesday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nComplete Galactic War Battles \nSave Normal Energy
                                 \n== After Reset == \nSpend Normal Energy on Hard Mode Battles`);
            break;
        case 'thu':
        case 'thursday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nSpend Normal Energy on Hard Mode Battles \nSave Challenges (Dailies are fine)
                                 \n== After Reset == \nComplete Challenges \nSave Normal Energy`);
            break;
        case 'fri':
        case 'friday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nComplete Challenges \nSave Normal Energy
                                 \n== After Reset == \nSpend Normal Energy on Dark Side Battles`);
            break;
        case 'sat':
        case 'saturday':
            message.channel.sendCode('asciidoc', `== Before Reset: == \nSpend Normal Energy on Dark Side Battles \nSave Arena Battles \nSave Cantina Energy
                                 \n== After Reset == \nComplete Arena Battles \nSave Cantina Energy`);
            break;
    }
};

exports.conf = {
    enabled: true,
    guildOnly: false,
    aliases: ['a', 'act'],
    permLevel: 0
};

exports.help = {
    name: 'activities',
    description: 'Shows the daily activites',
    usage: 'activities [day]'
};
<commit_msg>Fixed a few things<commit_after>exports.run = (client, message, args) => {
    let day = String(args[0]).toLowerCase();

    switch (day) {
        case 'sun':
        case 'sunday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nComplete Arena Battles \nSave Cantina Energy \nSave Normal Energy
                                 \n== After Reset == \nSpend Cantina Energy \nSave Normal Energy`);
            break;
        case 'mon':
        case 'monday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nSpend Cantina Energy \nSave Normal Energy \nSave Galactic War (unless reset available)
                                 \n== After Reset == \nSpend Normal Energy on Light Side Battles \nSave Galactic War (unless reset available)`);
            break;
        case 'tue':
        case 'tuesday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nSpend Normal Energy on Light Side Battles \nSave Galactic War
                                 \n== After Reset == \nComplete Galactic War Battles \nSave Normal Energy`);
            break;
        case 'wed':
        case 'wednesday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nComplete Galactic War Battles \nSave Normal Energy
                                 \n== After Reset == \nSpend Normal Energy on Hard Mode Battles`);
            break;
        case 'thu':
        case 'thursday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nSpend Normal Energy on Hard Mode Battles \nSave Challenges
                                 \n== After Reset == \nComplete Challenges \nSave Normal Energy`);
            break;
        case 'fri':
        case 'friday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nComplete Challenges \nSave Normal Energy
                                 \n== After Reset == \nSpend Normal Energy on Dark Side Battles`);
            break;
        case 'sat':
        case 'saturday':
            message.channel.sendCode('asciidoc', `== Before Reset == \nSpend Normal Energy on Dark Side Battles \nSave Arena Battles \nSave Cantina Energy
                                 \n== After Reset == \nComplete Arena Battles \nSave Cantina Energy`);
            break;
    }
};

exports.conf = {
    enabled: true,
    guildOnly: false,
    aliases: ['a', 'act'],
    permLevel: 0
};

exports.help = {
    name: 'activities',
    description: 'Shows the daily activites',
    usage: 'activities [day]'
};
<|endoftext|>


<commit_before>/*
	Basic Commands
*/

Settings.addPermissions(['say']);

exports.commands = {
	credits: 'about',
	bot: 'about',
	about: function () {
		this.restrictReply(this.trad('about') + ". " + this.trad('author') + ": " + Settings.package.author.name + ". (" + Settings.package.homepage + ")", 'info');
	},

	git: 'github',
	github: function () {
		if (Settings.package.repository) this.restrictReply(Tools.stripCommands(Settings.package.repository.url), 'info');
	},

	botversion: 'version',
	version: function () {
		this.restrictReply(Tools.stripCommands(Settings.package.version), 'info');
	},

	guide: 'help',
	botguide: 'help',
	help: function () {
		this.restrictReply(this.trad('guide') + ': ' + (Config.botguide || (Settings.package.homepage + "/blob/master/commands/README.md")), 'info');
	},

	bottime: 'time',
	time: function () {
		var f = new Date();
		this.restrictReply("**" + this.trad('time') + ":** __" + f.toString() + "__", 'info');
	},

	uptime: function () {
		var text = '';
		text += '**Uptime:** ';
		var divisors = [52, 7, 24, 60, 60];
		var units = [this.trad('week'), this.trad('day'), this.trad('hour'), this.trad('minute'), this.trad('second')];
		var buffer = [];
		var uptime = ~~(process.uptime());
		do {
			var divisor = divisors.pop();
			var unit = uptime % divisor;
			if (!unit) {
				units.pop();
				uptime = ~~(uptime / divisor);
				continue;
			}
			buffer.push(unit > 1 ? unit + ' ' + units.pop() + 's' : unit + ' ' + units.pop());
			uptime = ~~(uptime / divisor);
		} while (uptime);

		switch (buffer.length) {
		case 5:
			text += buffer[4] + ', ';
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 4:
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 3:
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 2:
			text += buffer[1] + ' ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 1:
			text += buffer[0];
			break;
		}
		this.restrictReply(text, 'info');
	},

	seen: function (arg, by, room, cmd) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		if (arg === toId(Bot.status.nickName)) return this.pmReply(this.trad('bot'));
		if (arg === toId(by)) return this.pmReply(this.trad('self'));
		var dSeen = Settings.userManager.getSeen(arg);
		if (dSeen) {
			text += '**' + (dSeen.name || arg).trim() + '** ' + this.trad('s1') + ' __' + Tools.getTimeAgo(dSeen.time, this.language).trim() + (this.trad('s2') ? ('__ ' + this.trad('s2')) : '__');
			if (dSeen.room) {
				switch (dSeen.action) {
					case 'j':
						text += ', ' + this.trad('j') + ' <<' + dSeen.room + '>>';
						break;
					case 'l':
						text += ', ' + this.trad('l') + ' <<' + dSeen.room + '>>';
						break;
					case 'c':
						text += ', ' + this.trad('c') + ' <<' + dSeen.room + '>>';
						break;
					case 'n':
						text += ', ' + this.trad('n') + ' **' + dSeen.args[0] + '**';
						break;
				}
			}
		} else {
			text += this.trad('n1') + ' ' + arg + ' ' + this.trad('n2');
		}
		this.pmReply(text);
	},

	publicalts: 'alts',
	alts: function (arg) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		var alts = Settings.userManager.getAlts(arg);
		if (alts && alts.length) {
			if (this.can("alts")) {
				var cmds = [];
				var toAdd;
				text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": ";
				for (var i = 0; i < alts.length; i++) {
					toAdd = alts[i] + (i < alts.length - 1 ? ", " : "");
					if ((text + toAdd).length > 300) {
						cmds.push(text);
						text = "";
					}
					text += toAdd;
				}
				if (text.length) cmds.push(text);
				this.pmReply(cmds);
				return;
			} else {
				if (alts.length <= 10) {
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + alts.join(", ");
				} else {
					var fAlts = [];
					for (var i = alts.length - 1; i >= 0 && i > alts.length - 10; i--) {
						fAlts.push(alts[i]);
					}
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + fAlts.join(", ") + ", (" + (alts.length - 10) + this.trad('more') + ")";
				}
			}
		} else {
			text += this.trad('n') + ' ' +  Settings.userManager.getName(arg);
		}
		this.pmReply(text);
	},

	say: function (arg) {
		if (!arg) return;
		if (!this.can('say')) return;
		this.reply(Tools.stripCommands(arg));
	}
};
<commit_msg>Update base-basic.js<commit_after>/*
	Basic Commands
*/

Settings.addPermissions(['say']);

exports.commands = {
	credits: 'about',
	bot: 'about',
	about: function () {
		Bot.say('Hi im bottonee');
		this.restrictReply(this.trad('about') + ". " + this.trad('author') + ": " + Settings.package.author.name + ". (" + Settings.package.homepage + ")", 'info');
	},

	git: 'github',
	github: function () {
		if (Settings.package.repository) this.restrictReply(Tools.stripCommands(Settings.package.repository.url), 'info');
	},

	botversion: 'version',
	version: function () {
		this.restrictReply(Tools.stripCommands(Settings.package.version), 'info');
	},

	guide: 'help',
	botguide: 'help',
	help: function () {
		this.restrictReply(this.trad('guide') + ': ' + (Config.botguide || (Settings.package.homepage + "/blob/master/commands/README.md")), 'info');
	},

	bottime: 'time',
	time: function () {
		var f = new Date();
		this.restrictReply("**" + this.trad('time') + ":** __" + f.toString() + "__", 'info');
	},

	uptime: function () {
		var text = '';
		text += '**Uptime:** ';
		var divisors = [52, 7, 24, 60, 60];
		var units = [this.trad('week'), this.trad('day'), this.trad('hour'), this.trad('minute'), this.trad('second')];
		var buffer = [];
		var uptime = ~~(process.uptime());
		do {
			var divisor = divisors.pop();
			var unit = uptime % divisor;
			if (!unit) {
				units.pop();
				uptime = ~~(uptime / divisor);
				continue;
			}
			buffer.push(unit > 1 ? unit + ' ' + units.pop() + 's' : unit + ' ' + units.pop());
			uptime = ~~(uptime / divisor);
		} while (uptime);

		switch (buffer.length) {
		case 5:
			text += buffer[4] + ', ';
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 4:
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 3:
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 2:
			text += buffer[1] + ' ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 1:
			text += buffer[0];
			break;
		}
		this.restrictReply(text, 'info');
	},

	seen: function (arg, by, room, cmd) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		if (arg === toId(Bot.status.nickName)) return this.pmReply(this.trad('bot'));
		if (arg === toId(by)) return this.pmReply(this.trad('self'));
		var dSeen = Settings.userManager.getSeen(arg);
		if (dSeen) {
			text += '**' + (dSeen.name || arg).trim() + '** ' + this.trad('s1') + ' __' + Tools.getTimeAgo(dSeen.time, this.language).trim() + (this.trad('s2') ? ('__ ' + this.trad('s2')) : '__');
			if (dSeen.room) {
				switch (dSeen.action) {
					case 'j':
						text += ', ' + this.trad('j') + ' <<' + dSeen.room + '>>';
						break;
					case 'l':
						text += ', ' + this.trad('l') + ' <<' + dSeen.room + '>>';
						break;
					case 'c':
						text += ', ' + this.trad('c') + ' <<' + dSeen.room + '>>';
						break;
					case 'n':
						text += ', ' + this.trad('n') + ' **' + dSeen.args[0] + '**';
						break;
				}
			}
		} else {
			text += this.trad('n1') + ' ' + arg + ' ' + this.trad('n2');
		}
		this.pmReply(text);
	},

	publicalts: 'alts',
	alts: function (arg) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		var alts = Settings.userManager.getAlts(arg);
		if (alts && alts.length) {
			if (this.can("alts")) {
				var cmds = [];
				var toAdd;
				text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": ";
				for (var i = 0; i < alts.length; i++) {
					toAdd = alts[i] + (i < alts.length - 1 ? ", " : "");
					if ((text + toAdd).length > 300) {
						cmds.push(text);
						text = "";
					}
					text += toAdd;
				}
				if (text.length) cmds.push(text);
				this.pmReply(cmds);
				return;
			} else {
				if (alts.length <= 10) {
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + alts.join(", ");
				} else {
					var fAlts = [];
					for (var i = alts.length - 1; i >= 0 && i > alts.length - 10; i--) {
						fAlts.push(alts[i]);
					}
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + fAlts.join(", ") + ", (" + (alts.length - 10) + this.trad('more') + ")";
				}
			}
		} else {
			text += this.trad('n') + ' ' +  Settings.userManager.getName(arg);
		}
		this.pmReply(text);
	},

	say: function (arg) {
		if (!arg) return;
		if (!this.can('say')) return;
		this.reply(Tools.stripCommands(arg));
	}
};
<|endoftext|>


<commit_before>const searchMembers = require("../../util/searchMembers");

const subcommands = {
    start: async function(ctx) {
        if (ctx.gcfg.trivia == ctx.channel.id && !ctx.client.trivia.channels.includes(ctx.channel.id)) {
            ctx.client.trivia.init(ctx.client, ctx.channel.id);
            return Promise.resolve();
        } else if (ctx.gcfg.trivia != 0 && ctx.gcfg.gtrivia != ctx.channel.id) {
            return ctx.send(ctx.strings.get("trivia_wrong_channel", ctx.gcfg.trivia));
        } else if (ctx.gcfg.trivia == 0 || ctx.gcfg.trivia == undefined) {
            return ctx.send(ctx.strings.get("trivia_no_channel", ctx.gcfg.prefix));
        }

        return Promise.resolve();
    },
    stop: async function(ctx) {
        if (ctx.client.trivia.channels.includes(ctx.channel.id)) {
            ctx.client.trivia.channels.splice(ctx.client.trivia.channels.indexOf(ctx.gcfg.trivia), 1);
            return ctx.success(ctx.strings.get("trivia_stopped"));
        }
    },
    top: async function(ctx) {
        let res;
        try {
            res = await ctx.client.pg.query("SELECT * FROM scores ORDER BY score DESC;");
        } catch (err) {
            console.error(err);
            return ctx.failure(ctx.strings.get("bot_generic_error"));
        }

        let msg = [];
        let rows = res.rows;
        if (ctx.options[0] == "all") {
            msg.push(ctx.strings.get("trivia_top_ten_bot"));
        } else {
            msg.push(ctx.strings.get("trivia_top_ten_server"));
            rows = rows.filter((row) => ctx.guild.members.get(row.id));
        }

        rows = rows.slice(0, 10);

        msg = msg.concat(rows.map((row) => {
            let user = ctx.client.users.find((user) => user.id == row.id);
            return `${user ? user.username : ctx.strings.get("trivia_unkown_user")}: ${row.score}`;
        }));

        return ctx.send(msg.join("\n"));
    },
    stats: async function(ctx) {
        let res;
        try {
            res = await ctx.client.pg.query("SELECT * FROM scores ORDER BY score DESC LIMIT 1;")
        } catch (err) {
            console.error(err);
            return ctx.failure(ctx.strings.get("bot_generic_error"));
        }

        let user = ctx.client.users.get(res.rows[0].id);

        return ctx.send([
            `**${ctx.strings.get("trivia_total_questions")}** ${ctx.client.trivia.questions.length}`,
            `**${ctx.strings.get("trivia_first_place")}** ${user ? user.username : ctx.strings.get("trivia_unkown_user")} - ${res.rows[0].score}`,
            `**${ctx.strings.get("trivia_concurrent_games")}** ${ctx.client.trivia.channels.length}`
        ].join("\n"));
    },
    points: async function(ctx) {
        let ID = ctx.author.id;

        if (ctx.options.length) {
            let result = await searchMembers(ctx.guild.members, ctx.options);
            if (result) ID = result.all[0];
        }

        let res;
        try {
            res = await ctx.client.pg.query("SELECT * FROM scores ORDER BY score DESC;");
        } catch (err) {
            console.error(err);
            return ctx.failure(ctx.strings.get("bot_generic_error"));
        }

        let user = ctx.client.users.get(ID);
        let row = res.rows.find((row) => row.id == ID);
        let guild = res.rows.filter((row) => ctx.guild.members.get(row.id));

        if (!row) {
            return ctx.send(ctx.strings.get("trivia_has_not_played", user.username));
        }

        let embed = {
            author: {
                name: user.username,
                icon_url: user.iconURL
            },
            description: [
                `**${ctx.strings.get("trivia_points")}** ${row.score}`,
                `**${ctx.strings.get("trivia_highest_streak")}** ${row.streak}`,
                `**${ctx.strings.get("trivia_server_rank")}** ${guild.indexOf(row) + 1}/${guild.length}`,
                `**${ctx.strings.get("trivia_global_rank")}** ${res.rows.indexOf(row) + 1}/${res.rows.length}`
            ].join("\n")
        }

        return ctx.embed(embed);
    }
}

async function exec(ctx) {
    const subcommand = ctx.options[0];

    if (subcommands.hasOwnProperty(subcommand)) {
        ctx.options = ctx.options.slice(1);
        return subcommands[subcommand](ctx);
    } else {
        return ctx.send(ctx.strings.get("bot_available_subcommands", Object.keys(subcommands).map((cmd) => `\`${cmd}\``).join(", ")));
    }
}

module.exports = {
    name: "trivia",
    category: "fun",
    exec
};
<commit_msg>learn eris<commit_after>const searchMembers = require("../../util/searchMembers");

const subcommands = {
    start: async function(ctx) {
        if (ctx.gcfg.trivia == ctx.channel.id && !ctx.client.trivia.channels.includes(ctx.channel.id)) {
            ctx.client.trivia.init(ctx.client, ctx.channel.id);
            return Promise.resolve();
        } else if (ctx.gcfg.trivia != 0 && ctx.gcfg.gtrivia != ctx.channel.id) {
            return ctx.send(ctx.strings.get("trivia_wrong_channel", ctx.gcfg.trivia));
        } else if (ctx.gcfg.trivia == 0 || ctx.gcfg.trivia == undefined) {
            return ctx.send(ctx.strings.get("trivia_no_channel", ctx.gcfg.prefix));
        }

        return Promise.resolve();
    },
    stop: async function(ctx) {
        if (ctx.client.trivia.channels.includes(ctx.channel.id)) {
            ctx.client.trivia.channels.splice(ctx.client.trivia.channels.indexOf(ctx.gcfg.trivia), 1);
            return ctx.success(ctx.strings.get("trivia_stopped"));
        }
    },
    top: async function(ctx) {
        let res;
        try {
            res = await ctx.client.pg.query("SELECT * FROM scores ORDER BY score DESC;");
        } catch (err) {
            console.error(err);
            return ctx.failure(ctx.strings.get("bot_generic_error"));
        }

        let msg = [];
        let rows = res.rows;
        if (ctx.options[0] == "all") {
            msg.push(ctx.strings.get("trivia_top_ten_bot"));
        } else {
            msg.push(ctx.strings.get("trivia_top_ten_server"));
            rows = rows.filter((row) => ctx.guild.members.get(row.id));
        }

        rows = rows.slice(0, 10);

        msg = msg.concat(rows.map((row) => {
            let user = ctx.client.users.find((user) => user.id == row.id);
            return `${user ? user.username : ctx.strings.get("trivia_unkown_user")}: ${row.score}`;
        }));

        return ctx.send(msg.join("\n"));
    },
    stats: async function(ctx) {
        let res;
        try {
            res = await ctx.client.pg.query("SELECT * FROM scores ORDER BY score DESC LIMIT 1;")
        } catch (err) {
            console.error(err);
            return ctx.failure(ctx.strings.get("bot_generic_error"));
        }

        let user = ctx.client.users.get(res.rows[0].id);

        return ctx.send([
            `**${ctx.strings.get("trivia_total_questions")}** ${ctx.client.trivia.questions.length}`,
            `**${ctx.strings.get("trivia_first_place")}** ${user ? user.username : ctx.strings.get("trivia_unkown_user")} - ${res.rows[0].score}`,
            `**${ctx.strings.get("trivia_concurrent_games")}** ${ctx.client.trivia.channels.length}`
        ].join("\n"));
    },
    points: async function(ctx) {
        let ID = ctx.author.id;

        if (ctx.options.length) {
            let result = await searchMembers(ctx.guild.members, ctx.options);
            if (result) ID = result.all[0];
        }

        let res;
        try {
            res = await ctx.client.pg.query("SELECT * FROM scores ORDER BY score DESC;");
        } catch (err) {
            console.error(err);
            return ctx.failure(ctx.strings.get("bot_generic_error"));
        }

        let user = ctx.client.users.get(ID);
        let row = res.rows.find((row) => row.id == ID);
        let guild = res.rows.filter((row) => ctx.guild.members.get(row.id));

        if (!row) {
            return ctx.send(ctx.strings.get("trivia_has_not_played", user.username));
        }

        let embed = {
            author: {
                name: user.username,
                icon_url: user.avatarURL
            },
            description: [
                `**${ctx.strings.get("trivia_points")}** ${row.score}`,
                `**${ctx.strings.get("trivia_highest_streak")}** ${row.streak}`,
                `**${ctx.strings.get("trivia_server_rank")}** ${guild.indexOf(row) + 1}/${guild.length}`,
                `**${ctx.strings.get("trivia_global_rank")}** ${res.rows.indexOf(row) + 1}/${res.rows.length}`
            ].join("\n")
        }

        return ctx.embed(embed);
    }
}

async function exec(ctx) {
    const subcommand = ctx.options[0];

    if (subcommands.hasOwnProperty(subcommand)) {
        ctx.options = ctx.options.slice(1);
        return subcommands[subcommand](ctx);
    } else {
        return ctx.send(ctx.strings.get("bot_available_subcommands", Object.keys(subcommands).map((cmd) => `\`${cmd}\``).join(", ")));
    }
}

module.exports = {
    name: "trivia",
    category: "fun",
    exec
};
<|endoftext|>


<commit_before>"use strict";

const chalk = require("chalk");
const columnify = require("columnify");

const Command = require("@lerna/command");
const output = require("@lerna/output");

module.exports = factory;

function factory(argv) {
  return new ListCommand(argv);
}

class ListCommand extends Command {
  get requiresGit() {
    return false;
  }

  initialize() {
    this.resultList = this.filteredPackages.map(pkg => ({
      name: pkg.name,
      version: pkg.version,
      private: pkg.private,
    }));
  }

  execute() {
    let result;

    if (this.options.json) {
      result = this.formatJSON();
    } else {
      result = this.formatColumns();
    }

    output(result);
  }

  formatJSON() {
    return JSON.stringify(this.resultList, null, 2);
  }

  formatColumns() {
    const formattedResults = this.resultList.map(result => {
      const formatted = {
        name: result.name,
      };

      if (result.version) {
        formatted.version = chalk.grey(`v${result.version}`);
      } else {
        formatted.version = chalk.yellow("MISSING");
      }

      if (result.private) {
        formatted.private = `(${chalk.red("private")})`;
      }

      return formatted;
    });

    return columnify(formattedResults, {
      showHeaders: false,
      config: {
        version: {
          align: "right",
        },
      },
    });
  }
}

module.exports.ListCommand = ListCommand;
<commit_msg>feat(ls): Log number of packages listed<commit_after>"use strict";

const chalk = require("chalk");
const columnify = require("columnify");

const Command = require("@lerna/command");
const output = require("@lerna/output");

module.exports = factory;

function factory(argv) {
  return new ListCommand(argv);
}

class ListCommand extends Command {
  get requiresGit() {
    return false;
  }

  initialize() {
    this.resultList = this.filteredPackages.map(pkg => ({
      name: pkg.name,
      version: pkg.version,
      private: pkg.private,
    }));

    // logged after output
    this.length = this.resultList.length;
  }

  execute() {
    let result;

    if (this.options.json) {
      result = this.formatJSON();
    } else {
      result = this.formatColumns();
    }

    output(result);

    this.logger.info("list", `found ${this.length} package${this.length === 1 ? "" : "s"}`);
  }

  formatJSON() {
    return JSON.stringify(this.resultList, null, 2);
  }

  formatColumns() {
    const formattedResults = this.resultList.map(result => {
      const formatted = {
        name: result.name,
      };

      if (result.version) {
        formatted.version = chalk.grey(`v${result.version}`);
      } else {
        formatted.version = chalk.yellow("MISSING");
      }

      if (result.private) {
        formatted.private = `(${chalk.red("private")})`;
      }

      return formatted;
    });

    return columnify(formattedResults, {
      showHeaders: false,
      config: {
        version: {
          align: "right",
        },
      },
    });
  }
}

module.exports.ListCommand = ListCommand;
<|endoftext|>


<commit_before>const Command = require("../base/Command");

class ReloadData extends Command {
    constructor(Bot) {
        super(Bot, {
            name: "reloaddata",
            category: "Dev",
            enabled: true,
            aliases: ["rdata", "rd"],
            permLevel: 10
        });
    }

    async run(Bot, message, [action, ...args], options) { // eslint-disable-line no-unused-vars
        const id = message.channel.id;
        const client = message.client;
        switch (action) {
            case "com":
            case "commands": // Reloads all the commands,
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadAllCommands('${id}'); `);
                } else {
                    client.reloadAllCommands(id);
                }
                break;
            case "ev":
            case "events": // Reload the events
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadAllEvents('${id}'); `);
                } else {
                    client.reloadAllEvents(id);
                }
                break;
            case "func":
            case "funct":
            case "functs":
            case "function":
            case "functions": // Reload the functions file
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadFunctions('${id}'); `);
                } else {
                    client.reloadFunctions(id);
                }
                break;
            case "api":
            case "swapi": // Reload the swapi file
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadSwapi('${id}'); `);
                } else {
                    client.reloadSwapi(id);
                }
                break;
            case "data": // Reload the character/ ship data files
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadDataFiles('${id}'); `);
                } else {
                    client.reloadDataFiles(id);
                }
                break;
            case "lang":
            case "language":
            case "languages":
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadLanguages('${id}'); `);
                } else {
                    client.reloadLanguages(id);
                }
                break;
            case "swlang":
                // Do this first since it's just the basic skeleton
                await message.client.swgohAPI.character(null, true);
                for (const lang of message.client.swgohLangList) {
                    if (!args[0]) {
                        await Bot.swgohAPI.abilities([], lang, true);
                        message.channel.send(`Updated abilities for ${lang}`);
                        await Bot.swgohAPI.gear([], lang, true);
                        message.channel.send(`Updated gear for ${lang}`);
                        await Bot.swgohAPI.recipes([], lang, true);
                        message.channel.send(`Updated recipes for ${lang}`);
                        await Bot.swgohAPI.materials([], lang, true);
                        message.channel.send(`Updated mats for ${lang}`);
                        await Bot.swgohAPI.units("", lang, true);
                        message.channel.send(`Updated units for ${lang}`);
                        message.channel.send("Updated all local data for " + lang);
                    } else {
                        switch (args[0]) {
                            case "abilities":
                                await Bot.swgohAPI.abilities([], lang, true);
                                message.channel.send(`Updated abilities for ${lang}`);
                                break;
                            case "gear":
                                await Bot.swgohAPI.gear([], lang, true);
                                message.channel.send(`Updated gear for ${lang}`);
                                break;
                            case "materials":
                                await Bot.swgohAPI.materials([], lang, true);
                                message.channel.send(`Updated mats for ${lang}`);
                                break;
                            case "recipes":
                                await Bot.swgohAPI.recipes([], lang, true);
                                message.channel.send(`Updated recipes for ${lang}`);
                                break;
                            case "units":
                                await Bot.swgohAPI.units("", lang, true);
                                message.channel.send(`Updated units for ${lang}`);
                                break;
                            default:
                                return message.channel.send("Invalid choice - `Abilities, gear, materials, recipes, units`");
                        }
                    }
                }
                message.channel.send("API Language update complete");
                break;
            case "users": // Reload the users file
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadUserReg('${id}');`);
                } else {
                    client.reloadUserReg(id);
                }
                break;
            default:
                return super.error(message, "You can only choose `api, commands, events, functions, languages, swlang, or data.`");
        }
    }
}

module.exports = ReloadData;

<commit_msg>Update some missed client/Bot changes<commit_after>const Command = require("../base/Command");

class ReloadData extends Command {
    constructor(Bot) {
        super(Bot, {
            name: "reloaddata",
            category: "Dev",
            enabled: true,
            aliases: ["rdata", "rd"],
            permLevel: 10
        });
    }

    async run(Bot, message, [action, ...args], options) { // eslint-disable-line no-unused-vars
        const id = message.channel.id;
        const client = message.client;
        switch (action) {
            case "com":
            case "commands": // Reloads all the commands,
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadAllCommands('${id}'); `);
                } else {
                    client.reloadAllCommands(id);
                }
                break;
            case "ev":
            case "events": // Reload the events
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadAllEvents('${id}'); `);
                } else {
                    client.reloadAllEvents(id);
                }
                break;
            case "func":
            case "funct":
            case "functs":
            case "function":
            case "functions": // Reload the functions file
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadFunctions('${id}'); `);
                } else {
                    client.reloadFunctions(id);
                }
                break;
            case "api":
            case "swapi": // Reload the swapi file
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadSwapi('${id}'); `);
                } else {
                    client.reloadSwapi(id);
                }
                break;
            case "data": // Reload the character/ ship data files
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadDataFiles('${id}'); `);
                } else {
                    client.reloadDataFiles(id);
                }
                break;
            case "lang":
            case "language":
            case "languages":
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadLanguages('${id}'); `);
                } else {
                    client.reloadLanguages(id);
                }
                break;
            case "swlang":
                // Do this first since it's just the basic skeleton
                await Bot.swgohAPI.character(null, true);
                for (const lang of Bot.swgohLangList) {
                    if (!args[0]) {
                        await Bot.swgohAPI.abilities([], lang, true);
                        message.channel.send(`Updated abilities for ${lang}`);
                        await Bot.swgohAPI.gear([], lang, true);
                        message.channel.send(`Updated gear for ${lang}`);
                        await Bot.swgohAPI.recipes([], lang, true);
                        message.channel.send(`Updated recipes for ${lang}`);
                        await Bot.swgohAPI.materials([], lang, true);
                        message.channel.send(`Updated mats for ${lang}`);
                        await Bot.swgohAPI.units("", lang, true);
                        message.channel.send(`Updated units for ${lang}`);
                        message.channel.send("Updated all local data for " + lang);
                    } else {
                        switch (args[0]) {
                            case "abilities":
                                await Bot.swgohAPI.abilities([], lang, true);
                                message.channel.send(`Updated abilities for ${lang}`);
                                break;
                            case "gear":
                                await Bot.swgohAPI.gear([], lang, true);
                                message.channel.send(`Updated gear for ${lang}`);
                                break;
                            case "materials":
                                await Bot.swgohAPI.materials([], lang, true);
                                message.channel.send(`Updated mats for ${lang}`);
                                break;
                            case "recipes":
                                await Bot.swgohAPI.recipes([], lang, true);
                                message.channel.send(`Updated recipes for ${lang}`);
                                break;
                            case "units":
                                await Bot.swgohAPI.units("", lang, true);
                                message.channel.send(`Updated units for ${lang}`);
                                break;
                            default:
                                return message.channel.send("Invalid choice - `Abilities, gear, materials, recipes, units`");
                        }
                    }
                }
                message.channel.send("API Language update complete");
                break;
            case "users": // Reload the users file
                if (message.client.shard && message.client.shard.count > 0) {
                    message.client.shard.broadcastEval(`this.reloadUserReg('${id}');`);
                } else {
                    client.reloadUserReg(id);
                }
                break;
            default:
                return super.error(message, "You can only choose `api, commands, events, functions, languages, swlang, or data.`");
        }
    }
}

module.exports = ReloadData;

<|endoftext|>