<commit_before>import _ from 'lodash';
import irc from 'irc';
import logger from 'winston';
import Slack from 'slack-client';
import { ConfigurationError } from './errors';
import emojis from '../assets/emoji.json';
import { validateChannelMapping } from './validators';
import { highlightUsername } from './helpers';

const ALLOWED_SUBTYPES = ['me_message'];
const REQUIRED_FIELDS = ['server', 'nickname', 'slackUser', 'channelMapping', 'token'];

/**
 * An IRC bot, works as a middleman for all communication
 * @param {object} options
 */
class Bot {
  constructor(options) {
    REQUIRED_FIELDS.forEach(field => {
      if (!options[field]) {
        throw new ConfigurationError('Missing configuration field ' + field);
      }
    });

    validateChannelMapping(options.channelMapping);

    this.slack = new Slack(options.token);

    this.server = options.server;
    this.nickname = options.nickname;
    this.slackUser = options.slackUser;
    this.ircOptions = options.ircOptions;
    this.ircStatusNotices = options.ircStatusNotices || {};
    this.channels = _.values(options.channelMapping);
    this.muteSlackbot = options.muteSlackbot || false;

    this.rememberRecipientsFor = +(options.rememberRecipientsFor || 1000 * 60 * 10);
    this.lastPM = {
      user: '',
      timestamp: 0
    };

    this.channelMapping = {};

    // Remove channel passwords from the mapping and lowercase IRC channel names
    _.forOwn(options.channelMapping, (ircChan, slackChan) => {
      this.channelMapping[slackChan] = ircChan.split(' ')[0].toLowerCase();
    }, this);

    this.invertedMapping = _.invert(this.channelMapping);
    this.autoSendCommands = options.autoSendCommands || [];
  }

  connect() {
    logger.debug('Connecting to IRC and Slack');
    this.slack.login();

    const ircOptions = {
      userName: this.nickname,
      realName: this.nickname,
      channels: this.channels,
      floodProtection: true,
      floodProtectionDelay: 500,
      ...this.ircOptions
    };

    this.ircClient = new irc.Client(this.server, this.nickname, ircOptions);
    this.attachListeners();
  }

  attachListeners() {
    this.slack.on('open', () => {
      logger.debug('Connected to Slack');
    });

    this.ircClient.on('registered', message => {
      logger.debug('Registered event: ', message);
      this.autoSendCommands.forEach(element => {
        this.ircClient.send(...element);
      });
    });

    this.ircClient.on('error', error => {
      logger.error('Received error event from IRC', error);
    });

    this.slack.on('error', error => {
      logger.error('Received error event from Slack', error);
    });

    this.slack.on('presenceChange', (user, presence) => {
      if (user.name === this.slackUser) {
        if (presence === 'active') {
          this.ircClient.send('AWAY');
        } else {
          this.ircClient.send('AWAY', ' ');
        }
      }
    });

    this.slack.on('message', message => {
      // Ignore bot messages and people leaving/joining
      if (message.type === 'message' &&
        (!message.subtype || ALLOWED_SUBTYPES.indexOf(message.subtype) > -1)) {
        this.sendToIRC(message);
      }
    });

    this.ircClient.on('message', this.sendToSlack.bind(this));

    this.ircClient.on('notice', (author, to, text) => {
      const formattedText = '*' + text + '*';
      this.sendToSlack(author, to, formattedText);
    });

    this.ircClient.on('action', (author, to, text) => {
      const formattedText = '_' + text + '_';
      this.sendToSlack(author, to, formattedText);
    });

    this.ircClient.on('invite', (channel, from) => {
      logger.debug('Received invite:', channel, from);
      if (!this.invertedMapping[channel]) {
        logger.debug('Channel not found in config, not joining:', channel);
      } else {
        this.ircClient.join(channel);
        logger.debug('Joining channel:', channel);
      }
    });

    if (this.ircStatusNotices.join) {
      this.ircClient.on('join', (channel, nick) => {
        if (nick !== this.nickname) {
          this.sendToSlack(false, channel, `*${nick}* has joined the IRC channel`);
        }
      });
    }

    if (this.ircStatusNotices.leave) {
      this.ircClient.on('part', (channel, nick) => {
        this.sendToSlack(false, channel, `*${nick}* has left the IRC channel`);
      });

      this.ircClient.on('quit', (nick, reason, channels) => {
        channels.forEach(channel => {
          this.sendToSlack(false, channel, `*${nick}* has quit the IRC channel`);
        });
      });
    }
  }

  parseText(text) {
    return text
      .replace(/\n|\r\n|\r/g, ' ')
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/<!channel>/g, '@channel')
      .replace(/<!group>/g, '@group')
      .replace(/<!everyone>/g, '@everyone')
      .replace(/<#(C\w+)\|?(\w+)?>/g, (match, channelId, readable) => {
        const { name } = this.slack.getChannelByID(channelId);
        return readable || `#${name}`;
      })
      .replace(/<@(U\w+)\|?(\w+)?>/g, (match, userId, readable) => {
        const { name } = this.slack.getUserByID(userId);
        return readable || `@${name}`;
      })
      .replace(/<(?!!)(\S+)>/g, (match, link) => link)
      .replace(/<!(\w+)\|?(\w+)?>/g, (match, command, label) =>
        `<${label || command}>`
      )
      .replace(/\:(\w+)\:/g, (match, emoji) => {
        if (emoji in emojis) {
          return emojis[emoji];
        }

        return match;
      });
  }

  sendDMToIRC(message, channel) {
    const text = this.parseText(message.getBody());
    const cmdMatch = (/^[A-Z]+\s\S/).test(text);
    const pmMatch = (/^(\S+):\s+(.+)/i).exec(text);

    if (cmdMatch) {
      logger.debug('Sending raw command to IRC', text);
      this.ircClient.send(...text.split(' '));
      channel.send(`_sent raw command_`);
      return;
    }

    if (pmMatch) {
      const [, user, msg] = pmMatch;
      logger.debug('Sending /msg to IRC user', user, msg);
      this.ircClient.send('PRIVMSG', user, msg);
      channel.send(`_sent to ${user}_`);
      this.lastPM.user = user;
      this.lastPM.timestamp = Date.now();
      return;
    }

    const lastMessage = Date.now() - this.lastPM.timestamp;
    if (lastMessage < this.rememberRecipientsFor) {
      const user = this.lastPM.user;
      logger.debug('Sending /msg to last messaged IRC user', user, text);
      this.ircClient.send('PRIVMSG', user, text);
      channel.send(`_sent to ${user}_`);
      this.lastPM.user = user;
      this.lastPM.timestamp = Date.now();
      return;
    }

    if (this.lastPM.user) {
      logger.debug('Not sending message', text, 'since user', this.lastPM.user, 'was messaged',
          lastMessage, 'ago, which is more than', this.rememberRecipientsFor);
      channel.send(`_it's been too long since your last message, please specify the user_`);
      return;
    }

    logger.debug('Not sending message', text, 'since no users have been messaged');
    channel.send(`_you haven't messaged anyone yet, please specify the user_`);
  }

  sendToIRC(message) {
    const channel = this.slack.getChannelGroupOrDMByID(message.channel);
    if (!channel) {
      logger.info('Received message from a channel the bot isn\'t in:',
        message.channel);
      return;
    }

    if (this.muteSlackbot && message.user === 'USLACKBOT') {
      logger.debug(`Muted message from Slackbot: "${message.text}"`);
      return;
    }

    if (channel.is_im) {
      this.sendDMToIRC(message, channel);
      return;
    }

    const channelName = channel.is_channel ? `#${channel.name}` : channel.name;
    const ircChannel = this.channelMapping[channelName];

    logger.debug('Channel Mapping', channelName, this.channelMapping[channelName]);
    if (ircChannel) {
      let text = this.parseText(message.getBody());

      if (message.subtype === 'me_message') {
        text = `\x01ACTION ${text}\x01`;
      }
      logger.debug('Sending message to IRC', channelName, text);
      this.ircClient.say(ircChannel, text);
    }
  }

  sendToSlack(author, channel, text) {
    const slackChannelName = channel === this.nickname ?
        this.slackUser :
        this.invertedMapping[channel.toLowerCase()];
    if (slackChannelName) {
      const slackChannel = this.slack.getChannelGroupOrDMByName(slackChannelName);

      // If it's a private group and the bot isn't in it, we won't find anything here.
      // If it's a channel however, we need to check is_member.
      if (!slackChannel ||
          (!slackChannel.is_member && !slackChannel.is_group && !slackChannel.is_im)) {
        logger.info('Tried to send a message to a channel the bot isn\'t in:', slackChannelName);
        return;
      }

      const currentChannelUsernames = (slackChannel.members || []).map(member =>
        this.slack.getUserByID(member).name
      );

      const mappedText = currentChannelUsernames.reduce((current, username) =>
        highlightUsername(username, current)
      , text);

      logger.debug('Sending message to Slack', mappedText, channel, '->', slackChannelName);
      if (author) {
        slackChannel.postMessage({
          text: mappedText,
          username: author,
          parse: 'full',
          icon_url: `http://api.adorable.io/avatars/48/${author}.png`
        });
      } else {
        slackChannel.send(mappedText);
      }
    }
  }
}

export default Bot;
<commit_msg>no acknowledgement for /msg sending, only in abnormal cases since I can see whether or not the bot is online in Slack, and errors will be surfaced<commit_after>import _ from 'lodash';
import irc from 'irc';
import logger from 'winston';
import Slack from 'slack-client';
import { ConfigurationError } from './errors';
import emojis from '../assets/emoji.json';
import { validateChannelMapping } from './validators';
import { highlightUsername } from './helpers';

const ALLOWED_SUBTYPES = ['me_message'];
const REQUIRED_FIELDS = ['server', 'nickname', 'slackUser', 'channelMapping', 'token'];

/**
 * An IRC bot, works as a middleman for all communication
 * @param {object} options
 */
class Bot {
  constructor(options) {
    REQUIRED_FIELDS.forEach(field => {
      if (!options[field]) {
        throw new ConfigurationError('Missing configuration field ' + field);
      }
    });

    validateChannelMapping(options.channelMapping);

    this.slack = new Slack(options.token);

    this.server = options.server;
    this.nickname = options.nickname;
    this.slackUser = options.slackUser;
    this.ircOptions = options.ircOptions;
    this.ircStatusNotices = options.ircStatusNotices || {};
    this.channels = _.values(options.channelMapping);
    this.muteSlackbot = options.muteSlackbot || false;

    this.rememberRecipientsFor = +(options.rememberRecipientsFor || 1000 * 60 * 10);
    this.lastPM = {
      user: '',
      timestamp: 0
    };

    this.channelMapping = {};

    // Remove channel passwords from the mapping and lowercase IRC channel names
    _.forOwn(options.channelMapping, (ircChan, slackChan) => {
      this.channelMapping[slackChan] = ircChan.split(' ')[0].toLowerCase();
    }, this);

    this.invertedMapping = _.invert(this.channelMapping);
    this.autoSendCommands = options.autoSendCommands || [];
  }

  connect() {
    logger.debug('Connecting to IRC and Slack');
    this.slack.login();

    const ircOptions = {
      userName: this.nickname,
      realName: this.nickname,
      channels: this.channels,
      floodProtection: true,
      floodProtectionDelay: 500,
      ...this.ircOptions
    };

    this.ircClient = new irc.Client(this.server, this.nickname, ircOptions);
    this.attachListeners();
  }

  attachListeners() {
    this.slack.on('open', () => {
      logger.debug('Connected to Slack');
    });

    this.ircClient.on('registered', message => {
      logger.debug('Registered event: ', message);
      this.autoSendCommands.forEach(element => {
        this.ircClient.send(...element);
      });
    });

    this.ircClient.on('error', error => {
      logger.error('Received error event from IRC', error);
    });

    this.slack.on('error', error => {
      logger.error('Received error event from Slack', error);
    });

    this.slack.on('presenceChange', (user, presence) => {
      if (user.name === this.slackUser) {
        if (presence === 'active') {
          this.ircClient.send('AWAY');
        } else {
          this.ircClient.send('AWAY', ' ');
        }
      }
    });

    this.slack.on('message', message => {
      // Ignore bot messages and people leaving/joining
      if (message.type === 'message' &&
        (!message.subtype || ALLOWED_SUBTYPES.indexOf(message.subtype) > -1)) {
        this.sendToIRC(message);
      }
    });

    this.ircClient.on('message', this.sendToSlack.bind(this));

    this.ircClient.on('notice', (author, to, text) => {
      const formattedText = '*' + text + '*';
      this.sendToSlack(author, to, formattedText);
    });

    this.ircClient.on('action', (author, to, text) => {
      const formattedText = '_' + text + '_';
      this.sendToSlack(author, to, formattedText);
    });

    this.ircClient.on('invite', (channel, from) => {
      logger.debug('Received invite:', channel, from);
      if (!this.invertedMapping[channel]) {
        logger.debug('Channel not found in config, not joining:', channel);
      } else {
        this.ircClient.join(channel);
        logger.debug('Joining channel:', channel);
      }
    });

    if (this.ircStatusNotices.join) {
      this.ircClient.on('join', (channel, nick) => {
        if (nick !== this.nickname) {
          this.sendToSlack(false, channel, `*${nick}* has joined the IRC channel`);
        }
      });
    }

    if (this.ircStatusNotices.leave) {
      this.ircClient.on('part', (channel, nick) => {
        this.sendToSlack(false, channel, `*${nick}* has left the IRC channel`);
      });

      this.ircClient.on('quit', (nick, reason, channels) => {
        channels.forEach(channel => {
          this.sendToSlack(false, channel, `*${nick}* has quit the IRC channel`);
        });
      });
    }
  }

  parseText(text) {
    return text
      .replace(/\n|\r\n|\r/g, ' ')
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/<!channel>/g, '@channel')
      .replace(/<!group>/g, '@group')
      .replace(/<!everyone>/g, '@everyone')
      .replace(/<#(C\w+)\|?(\w+)?>/g, (match, channelId, readable) => {
        const { name } = this.slack.getChannelByID(channelId);
        return readable || `#${name}`;
      })
      .replace(/<@(U\w+)\|?(\w+)?>/g, (match, userId, readable) => {
        const { name } = this.slack.getUserByID(userId);
        return readable || `@${name}`;
      })
      .replace(/<(?!!)(\S+)>/g, (match, link) => link)
      .replace(/<!(\w+)\|?(\w+)?>/g, (match, command, label) =>
        `<${label || command}>`
      )
      .replace(/\:(\w+)\:/g, (match, emoji) => {
        if (emoji in emojis) {
          return emojis[emoji];
        }

        return match;
      });
  }

  sendDMToIRC(message, channel) {
    const text = this.parseText(message.getBody());
    const cmdMatch = (/^[A-Z]+\s\S/).test(text);
    const pmMatch = (/^(\S+):\s+(.+)/i).exec(text);

    if (cmdMatch) {
      logger.debug('Sending raw command to IRC', text);
      this.ircClient.send(...text.split(' '));
      channel.send(`_sent raw command_`);
      return;
    }

    if (pmMatch) {
      const [, user, msg] = pmMatch;
      logger.debug('Sending /msg to IRC user', user, msg);
      this.ircClient.send('PRIVMSG', user, msg);
      this.lastPM.user = user;
      this.lastPM.timestamp = Date.now();
      return;
    }

    const lastMessage = Date.now() - this.lastPM.timestamp;
    if (lastMessage < this.rememberRecipientsFor) {
      const user = this.lastPM.user;
      logger.debug('Sending /msg to last messaged IRC user', user, text);
      this.ircClient.send('PRIVMSG', user, text);
      this.lastPM.user = user;
      this.lastPM.timestamp = Date.now();
      return;
    }

    if (this.lastPM.user) {
      logger.debug('Not sending message', text, 'since user', this.lastPM.user, 'was messaged',
          lastMessage, 'ago, which is more than', this.rememberRecipientsFor);
      channel.send(`_it's been too long since your last message, please specify the user_`);
      return;
    }

    logger.debug('Not sending message', text, 'since no users have been messaged');
    channel.send(`_you haven't messaged anyone yet, please specify the user_`);
  }

  sendToIRC(message) {
    const channel = this.slack.getChannelGroupOrDMByID(message.channel);
    if (!channel) {
      logger.info('Received message from a channel the bot isn\'t in:',
        message.channel);
      return;
    }

    if (this.muteSlackbot && message.user === 'USLACKBOT') {
      logger.debug(`Muted message from Slackbot: "${message.text}"`);
      return;
    }

    if (channel.is_im) {
      this.sendDMToIRC(message, channel);
      return;
    }

    const channelName = channel.is_channel ? `#${channel.name}` : channel.name;
    const ircChannel = this.channelMapping[channelName];

    logger.debug('Channel Mapping', channelName, this.channelMapping[channelName]);
    if (ircChannel) {
      let text = this.parseText(message.getBody());

      if (message.subtype === 'me_message') {
        text = `\x01ACTION ${text}\x01`;
      }
      logger.debug('Sending message to IRC', channelName, text);
      this.ircClient.say(ircChannel, text);
    }
  }

  sendToSlack(author, channel, text) {
    const slackChannelName = channel === this.nickname ?
        this.slackUser :
        this.invertedMapping[channel.toLowerCase()];
    if (slackChannelName) {
      const slackChannel = this.slack.getChannelGroupOrDMByName(slackChannelName);

      // If it's a private group and the bot isn't in it, we won't find anything here.
      // If it's a channel however, we need to check is_member.
      if (!slackChannel ||
          (!slackChannel.is_member && !slackChannel.is_group && !slackChannel.is_im)) {
        logger.info('Tried to send a message to a channel the bot isn\'t in:', slackChannelName);
        return;
      }

      const currentChannelUsernames = (slackChannel.members || []).map(member =>
        this.slack.getUserByID(member).name
      );

      const mappedText = currentChannelUsernames.reduce((current, username) =>
        highlightUsername(username, current)
      , text);

      logger.debug('Sending message to Slack', mappedText, channel, '->', slackChannelName);
      if (author) {
        slackChannel.postMessage({
          text: mappedText,
          username: author,
          parse: 'full',
          icon_url: `http://api.adorable.io/avatars/48/${author}.png`
        });
      } else {
        slackChannel.send(mappedText);
      }
    }
  }
}

export default Bot;
<|endoftext|>


<commit_before>var http = require('http'),
	URL = require('url'),
	googleSearch = require('node-googleSearch').googleSearch,
	genGadgets = require('node-gadgets').genGadgets;

var getElements = function(query,response){

	var params={}; //search-gadget-regexp-url

	var $E=encodeURIComponent;

	query.split('&').forEach(function(param){
		var parts = param.split('=');
		params[parts[0].trim()] = (decodeURIComponent(parts[1]) || '').trim();
	});

	var resp={
		end : function(res) {
			try {
				if (res.length) {
					var url=res[0].url;
					var p='url='+url+'&name='+params.name;
					url=url.split('/')[2];
					var t=params.search.toLowerCase().split(' ');
					var search=[];
					var l=t.length;
					for (var i=0;i<l;i++) {
						var re=new RegExp(t[i],"g");
						if (!re.test(url)) {
							search.push(t[i]);
						}
					};
					search=search.join(' ');
				};
				//Example : babyliss g910e cdiscount
				//becomes : babyliss g910e

				if (params.gadget) {
					p +='&fetch='+$E('{"img":"", "input":""}')+'&proc='+$E('{"img":"","input":""}')+'&gadget=true&price=true&search='+search+'&regexp='+$E(params.regexp);
				} else {
					p +='&fetch='+$E('{"img":"", "input":""}')+'&proc='+$E('{"img":"","input":""}')+'&price=true&search='+search+'&regexp='+$E(params.regexp);
				};

				//console.log(p);

				var resp2={

					end : function(res) {

						var head = {'Content-Type': 'text/javascript' };

						if (response.writeHead) {

							response.writeHead(200, head);

							response.end(params.name+'='+JSON.stringify(res)+';');

							/*var l=res.length;
							for (var i=0;i<l;i++) {
								console.log(res[i][3]+' '+res[i][6]);
							}*/

						} else {
							response.end(res);
						}
					}
				};

				genGadgets(p,resp2);

			} catch(ee) {
				response.end('Bad formatted request');
			}
		}
	};

	if (!params.url) {
		googleSearch('search='+$E(params.search),resp);
	} else {
		resp.end([{url:params.url}]);
	}

};

var handleRequest = function (request, response) {

	var qs = URL.parse(request.url);

	if (qs.pathname == '/getelements'){
		try {
			getElements(qs.query,response);
		} catch(ee) {
			response.end('Bad formatted request');
		};
	};
};

var launchServer = function(port) {
	http.createServer(handleRequest).listen(port);
};

exports.launchServer = launchServer;
exports.getElements = getElements;<commit_msg>Correction for parameters URL encoding<commit_after>var http = require('http'),
	URL = require('url'),
	googleSearch = require('node-googleSearch').googleSearch,
	genGadgets = require('node-gadgets').genGadgets;

var getElements = function(query,response){

	var params={}; //search-gadget-regexp-url

	var $E=encodeURIComponent;

	query.split('&').forEach(function(param){
		var parts = param.split('=');
		params[parts[0].trim()] = (decodeURIComponent(parts[1]) || '').trim();
	});

	var resp={
		end : function(res) {
			try {
				if (res.length) {
					var url=res[0].url;
					var p='url='+$E(url)+'&name='+$E(params.name);
					url=url.split('/')[2];
					var t=params.search?params.search.toLowerCase().split(' '):'';
					var search=[];
					var l=t.length;
					for (var i=0;i<l;i++) {
						var re=new RegExp(t[i],"g");
						if (!re.test(url)) {
							search.push(t[i]);
						}
					};
					search=search.join(' ');
				};
				//Example : babyliss g910e cdiscount
				//becomes : babyliss g910e

				if (params.gadget) {
					p +='&fetch='+$E('{"img":"", "input":""}')+'&proc='+$E('{"img":"","input":""}')+'&gadget=true&price=true&search='+$E(search)+'&regexp='+$E(params.regexp);
				} else {
					p +='&fetch='+$E('{"img":"", "input":""}')+'&proc='+$E('{"img":"","input":""}')+'&price=true&search='+$E(search)+'&regexp='+$E(params.regexp);
				};

				//console.log(p);

				var resp2={

					end : function(res) {

						var head = {'Content-Type': 'text/javascript' };

						if (response.writeHead) {

							response.writeHead(200, head);

							response.end(params.name+'='+JSON.stringify(res)+';');

							/*var l=res.length;
							for (var i=0;i<l;i++) {
								console.log(res[i][3]+' '+res[i][6]);
							}*/

						} else {
							response.end(res);
						}
					}
				};

				genGadgets(p,resp2);

			} catch(ee) {
				response.end('Bad formatted request');
			}
		}
	};

	if (!params.url) {
		googleSearch('search='+$E(params.search),resp);
	} else {
		resp.end([{url:params.url}]);
	}

};

var handleRequest = function (request, response) {

	var qs = URL.parse(request.url);

	if (qs.pathname == '/getelements'){
		try {
			getElements(qs.query,response);
		} catch(ee) {
			response.end('Bad formatted request');
		};
	};
};

var launchServer = function(port) {
	http.createServer(handleRequest).listen(port);
};

exports.launchServer = launchServer;
exports.getElements = getElements;<|endoftext|>


<commit_before>var commander = require('commander')
var diff = require('diff')
var fixmyjs = require('../')
var fs = require('fs')
var fu = require('fu')
var jshint = require('jshint').JSHINT
var minimatch = require('minimatch')
var path = require('path')

function removeJsComments(str) {
  return (str || '')
    .replace(/\/\*[\s\S]*(?:\*\/)/g, '') //everything between '/* */'
    .replace(/\/\/[^\n\r]*/g, '') //everything after '//'
}

function loadAndParseConfig(filePath) {
  if (typeof filePath === 'object') {
    return filePath
  }

  try {
    return fs.existsSync(filePath)
      ? JSON.parse(removeJsComments(fs.readFileSync(filePath, 'utf-8')))
      : {}
  } catch (ex) {
    console.error('Error opening config file ' + filePath)
    process.exit(1)
  }
}

function mergeConfig(a, b) {
  var config = fu.merge({}, a)
  Object.keys(b).forEach(function (key) {
    if (key == 'predef') {
      config.predef = fu.concat(config.predef || [], b.predef)
    } else {
      config[key] = b[key]
    }
  })
  return config
}

function getConfig(dir) {
  return loadAndParseConfig(path.join(dir, '.jshintrc'))
}

function getIgnore(dir) {
  var PATH_TO_IGNORE = path.join(dir, '.jshintignore')
  var ignoreRules = fs.existsSync(PATH_TO_IGNORE)
    ? fu.compact(fs.readFileSync(PATH_TO_IGNORE, 'utf-8').split('\n'))
    : []

  return fu.map(function (ignoreRule) {
    return path.join(dir, ignoreRule)
  }, ignoreRules)
}

function printDiff(a, b) {
  if (a == b) {
    return
  }

  var DARK = '\x1b[90m'
  var GREEN = '\x1b[32m'
  var RED = '\x1b[31m'
  var RESET = '\x1b[39m'

  var df = diff.diffLines(a, b)
  var content = fu.map(function (n) {
    var line = df[n]
    if (line.removed) {
      return RED + line.value
    } else if (line.added) {
      return GREEN + line.value
    } else {
      return DARK + line.value
    }
  }, Object.keys(df))
  console.log(content.join(RESET + '\n'))
}

function createPatch(fileName, a, b) {
  console.log(diff.createPatch(fileName, a, b, '', ''))
}

function isDir(fullpath) {
  try {
    return fs.statSync(fullpath).isDirectory()
  } catch (ex) {
    return null
  }
}

function shouldIgnorePath(fullpath, ignore) {
  return fu.any(function (ignoreRule) {
    var fnmatch = minimatch(fullpath, ignoreRule, { nocase: true })
    var lsmatch = Boolean(
      isDir(ignoreRule) &&
      ignoreRule.match(/^[^\/]*\/?$/) &&
      fullpath.match(new RegExp('^' + ignoreRule + '.*'))
    )
    return !!(fnmatch || lsmatch)
  }, ignore)
}

function shouldLintFile(fileName, ignore) {
  return /\.js$/.test(fileName) && !shouldIgnorePath(fileName, ignore)
}

function genFixForFile(file, config) {
  return function () {
    var content = fs.readFileSync(file).toString()
    var fixed = ''

    var fmjOptions = commander.indentPref
      ? fu.merge(config, { indentpref: commander.indentPref })
      : config

    try {
      if (commander.legacy) {
        jshint(content, config)
        fixed = fixmyjs(jshint.data(), content, fmjOptions).run()
      } else {
        fixed = fixmyjs.fix(content, fmjOptions)
      }
    } catch (ex) {
      ex.stack = 'File: ' + file + '\n' + ex.stack
      throw ex
    }

    if (commander.silent) {
      return true
    } else if (commander.dryRun || commander.diff) {
      printDiff(content, fixed)
    } else if (commander.patch) {
      createPatch(file, content, fixed)
    } else {
      fs.writeFileSync(file, fixed, 'utf8')
    }

    return true
  }
}

function traverseFiles(_, fileName) {
  var fullpath = path.resolve(fileName)

  switch (isDir(fullpath)) {
    case true:
      if (shouldIgnorePath(fullpath, _.ignore)) {
        return []
      }
      var ignore = fu.concat(_.ignore, getIgnore(fullpath))
      var config = mergeConfig(_.config, getConfig(fullpath))
      return fu.concatMap(function (x) {
        return traverseFiles({
          ignore: ignore,
          config: config
        }, path.join(fileName, x))
      }, fs.readdirSync(fullpath))
    case false:
      return shouldLintFile(fullpath, _.ignore)
        ? [genFixForFile(fullpath, _.config)]
        : []
    case null:
      return []
  }
}

function getUserHome() {
  return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE
}

function cli() {
  var findFiles = fu.curry(traverseFiles, {
    ignore: [],
    config: mergeConfig(
      getConfig(getUserHome()),
      commander.config ? getConfig(commander.config) : {}
    )
  })
  var filesToLint = fu.concatMap(findFiles, commander.args)
  return fu.all(function (fn) { return fn() }, filesToLint)
}

commander
  .option('-c, --config [.jshintrc]', 'Load your own config file')
  .option('-d, --diff', 'Similar to dry-run')
  .option('-l, --legacy', 'Use legacy fixmyjs')
  .option('-n, --indent-pref [tabs|spaces]', 'Your indentation preference')
  .option('-p, --patch', 'Output a patch file to stdout')
  .option('-r, --dry-run', 'Performs a dry-run and shows you a diff')
  .option('-s, --silent', 'A useless option')
  .parse(process.argv)

if (commander.args.length === 0) {
  commander.emit('help')
} else {
  cli()
}
<commit_msg>Fixes loading of config files via cli args<commit_after>var commander = require('commander')
var diff = require('diff')
var fixmyjs = require('../')
var fs = require('fs')
var fu = require('fu')
var jshint = require('jshint').JSHINT
var minimatch = require('minimatch')
var path = require('path')

function removeJsComments(str) {
  return (str || '')
    .replace(/\/\*[\s\S]*(?:\*\/)/g, '') //everything between '/* */'
    .replace(/\/\/[^\n\r]*/g, '') //everything after '//'
}

function loadAndParseConfig(filePath) {
  if (typeof filePath === 'object') {
    return filePath
  }

  try {
    return fs.existsSync(filePath)
      ? JSON.parse(removeJsComments(fs.readFileSync(filePath, 'utf-8')))
      : {}
  } catch (ex) {
    console.error('Error opening config file ' + filePath)
    process.exit(1)
  }
}

function mergeConfig(a, b) {
  var config = fu.merge({}, a)
  Object.keys(b).forEach(function (key) {
    if (key == 'predef') {
      config.predef = fu.concat(config.predef || [], b.predef)
    } else {
      config[key] = b[key]
    }
  })
  return config
}

function getConfig(dir) {
  return loadAndParseConfig(path.join(dir, '.jshintrc'))
}

function getIgnore(dir) {
  var PATH_TO_IGNORE = path.join(dir, '.jshintignore')
  var ignoreRules = fs.existsSync(PATH_TO_IGNORE)
    ? fu.compact(fs.readFileSync(PATH_TO_IGNORE, 'utf-8').split('\n'))
    : []

  return fu.map(function (ignoreRule) {
    return path.join(dir, ignoreRule)
  }, ignoreRules)
}

function printDiff(a, b) {
  if (a == b) {
    return
  }

  var DARK = '\x1b[90m'
  var GREEN = '\x1b[32m'
  var RED = '\x1b[31m'
  var RESET = '\x1b[39m'

  var df = diff.diffLines(a, b)
  var content = fu.map(function (n) {
    var line = df[n]
    if (line.removed) {
      return RED + line.value
    } else if (line.added) {
      return GREEN + line.value
    } else {
      return DARK + line.value
    }
  }, Object.keys(df))
  console.log(content.join(RESET + '\n'))
}

function createPatch(fileName, a, b) {
  console.log(diff.createPatch(fileName, a, b, '', ''))
}

function isDir(fullpath) {
  try {
    return fs.statSync(fullpath).isDirectory()
  } catch (ex) {
    return null
  }
}

function shouldIgnorePath(fullpath, ignore) {
  return fu.any(function (ignoreRule) {
    var fnmatch = minimatch(fullpath, ignoreRule, { nocase: true })
    var lsmatch = Boolean(
      isDir(ignoreRule) &&
      ignoreRule.match(/^[^\/]*\/?$/) &&
      fullpath.match(new RegExp('^' + ignoreRule + '.*'))
    )
    return !!(fnmatch || lsmatch)
  }, ignore)
}

function shouldLintFile(fileName, ignore) {
  return /\.js$/.test(fileName) && !shouldIgnorePath(fileName, ignore)
}

function genFixForFile(file, config) {
  return function () {
    var content = fs.readFileSync(file).toString()
    var fixed = ''

    var fmjOptions = commander.indentPref
      ? fu.merge(config, { indentpref: commander.indentPref })
      : config

    try {
      if (commander.legacy) {
        jshint(content, config)
        fixed = fixmyjs(jshint.data(), content, fmjOptions).run()
      } else {
        fixed = fixmyjs.fix(content, fmjOptions)
      }
    } catch (ex) {
      ex.stack = 'File: ' + file + '\n' + ex.stack
      throw ex
    }

    if (commander.silent) {
      return true
    } else if (commander.dryRun || commander.diff) {
      printDiff(content, fixed)
    } else if (commander.patch) {
      createPatch(file, content, fixed)
    } else {
      fs.writeFileSync(file, fixed, 'utf8')
    }

    return true
  }
}

function traverseFiles(_, fileName) {
  var fullpath = path.resolve(fileName)

  switch (isDir(fullpath)) {
    case true:
      if (shouldIgnorePath(fullpath, _.ignore)) {
        return []
      }
      var ignore = fu.concat(_.ignore, getIgnore(fullpath))
      var config = mergeConfig(_.config, getConfig(fullpath))
      return fu.concatMap(function (x) {
        return traverseFiles({
          ignore: ignore,
          config: config
        }, path.join(fileName, x))
      }, fs.readdirSync(fullpath))
    case false:
      return shouldLintFile(fullpath, _.ignore)
        ? [genFixForFile(fullpath, _.config)]
        : []
    case null:
      return []
  }
}

function getUserHome() {
  return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE
}

function cli() {
  var findFiles = fu.curry(traverseFiles, {
    ignore: [],
    config: mergeConfig(
      getConfig(getUserHome()),
      commander.config
        ? getConfig(path.dirname(commander.config))
        : {}
    )
  })
  var filesToLint = fu.concatMap(findFiles, commander.args)
  return fu.all(function (fn) { return fn() }, filesToLint)
}

commander
  .option('-c, --config [.jshintrc]', 'Load your own config file')
  .option('-d, --diff', 'Similar to dry-run')
  .option('-l, --legacy', 'Use legacy fixmyjs')
  .option('-n, --indent-pref [tabs|spaces]', 'Your indentation preference')
  .option('-p, --patch', 'Output a patch file to stdout')
  .option('-r, --dry-run', 'Performs a dry-run and shows you a diff')
  .option('-s, --silent', 'A useless option')
  .parse(process.argv)

if (commander.args.length === 0) {
  commander.emit('help')
} else {
  cli()
}
<|endoftext|>


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

function listAllUsages(){
	console.log('Print the entire usage here');
}

var configFile = process.cwd() + '/pg-events.json';

if (process.argv.length == 2){
	listAllUsages();
	return;

}

var commands = {
	seed: function(args){
		console.log('seed');
	},

	config: function(options){
		var contents = JSON.stringify(options, null, 4);

		fs.writeFile(configFile, contents, function (err) {
		  if (err) throw err;
		  console.log('Successfully wrote configuration data');
		  console.log(contents);
		  console.log('-- to file ' + configFile);
		});
	},

	list: function(options){
		var contents = JSON.stringify(options, null, 4);
		console.log('The configured options in file ' + configFile + ' are:');
		console.log(contents);
	},

	rebuild: function(args){
		console.log('rebuild');
	}

};

var fs = require('fs');

var readOptions = function(data){
	var options = {};

	if (data){
		options = JSON.parse(data);
	}

	var parseArgs = require('minimist');
	var args = parseArgs(process.argv.slice(3));
	if (args['d']){
		options.database = args['d'];
	}

	if (args['database']){
		options.database = args['database'];
	}

	if (args['p']){
		options.projections = args['p'];
	}

	if (args['projections']){
		options.projections = args['projections'];
	}

	return options;
}

var commandName = process.argv[2];
var command = commands[commandName];
if (command){

	fs.readFile(configFile, function(err, data){
		var options = readOptions(data);
		if (options.database == null || options.projections == null){
			console.log('Insufficient information:');
			console.log('database = ' + options.database);
			console.log('projections = ' + options.projections);
			console.log('');
			listAllUsages();
		}
		else{
			command(options);
		}
	});
}
else {
	listAllUsages();
}

<commit_msg>doing the list usages thing<commit_after>#! /usr/bin/env node

function listAllUsages(){
	console.log('The command line tool for pg-events');
	console.log('See the website at https://github.com/jeremydmiller/pg-events for more information');
	console.log('Commands:');
	console.log('    config -- saves the database connection and projections folder name to the file pg-events.json');
	console.log('      list -- lists the configuration in the file pg-events.json, but can be overriden by flags');
	console.log('      seed -- seed the postgresql database schema with all the pg-events tables, including projection tables');
	console.log('   rebuild -- rebuilds all the persisted projections in the schema');
	console.log();
	console.log('Flags:');
	console.log('    --database or -d => the database connection as required by the pg library');
	console.log(' --projections or -p => the folder holding the projection definition scripts');
	console.log('');
}

var configFile = process.cwd() + '/pg-events.json';

if (process.argv.length == 2){
	listAllUsages();
	return;

}

var commands = {
	seed: function(args){
		console.log('seed');
	},

	config: function(options){
		var contents = JSON.stringify(options, null, 4);

		fs.writeFile(configFile, contents, function (err) {
		  if (err) throw err;
		  console.log('Successfully wrote configuration data');
		  console.log(contents);
		  console.log('-- to file ' + configFile);
		});
	},

	list: function(options){
		var contents = JSON.stringify(options, null, 4);
		console.log('The configured options in file ' + configFile + ' are:');
		console.log(contents);
	},

	rebuild: function(args){
		console.log('rebuild');
	}

};

var fs = require('fs');

var readOptions = function(data){
	var options = {};

	if (data){
		options = JSON.parse(data);
	}

	var parseArgs = require('minimist');
	var args = parseArgs(process.argv.slice(3));
	if (args['d']){
		options.database = args['d'];
	}

	if (args['database']){
		options.database = args['database'];
	}

	if (args['p']){
		options.projections = args['p'];
	}

	if (args['projections']){
		options.projections = args['projections'];
	}

	return options;
}

var commandName = process.argv[2];
var command = commands[commandName];
if (command){

	fs.readFile(configFile, function(err, data){
		var options = readOptions(data);
		if (options.database == null || options.projections == null){
			console.log('Insufficient information:');
			console.log('database = ' + options.database);
			console.log('projections = ' + options.projections);
			console.log('');
			listAllUsages();
		}
		else{
			command(options);
		}
	});
}
else {
	listAllUsages();
}

<|endoftext|>


<commit_before>var fs     = require('fs'),
	path   = require('path'),
	spawn  = require('child_process').spawn,
	argv   = require('optimist').argv,
	colors = require('colors')
	api    = require('./core').initCLI()

var globalConfigPath = process.env.HOME + '/.podrc'

var cli    = {

	help: function () {
	    console.log(fs.readFileSync(__dirname + '/../help/usage', 'utf-8'))
	},

	config: function () {
	    edit(globalConfigPath)
	},

	create: function (appname) {
	    if (!appname) return
	    api.createApp(appname, output)
	},

	rm: function (appname) {
	    if (!appname) return
	    api.removeApp(appname, output)
	},

	start: function (appname) {
		if (!appname) return
		api.startApp(appname, output)
	},

	stop: function (appname) {
	    if (!appname) return
	    api.stopApp(appname, output)
	},

	restart: function (appname) {
	    if (!appname) return
	    api.restartApp(appname, output)
	},

	startall: function () {
	    api.startAllApps(output)
	},

	stopall: function () {
		api.stopAllApps(output)
	},

	restartall: function () {
		api.restartAllApps(output)
	},

	edit: function (appname) {
	    if (!appname) return
        edit(api.getAppInfo(appname).repoPath + '/hooks/post-receive')
	},

	list: function () {
	    api.listApps(function (err, apps) {
	        if (!apps.length) {
	        	log('no apps found.')
	        } else {
	        	console.log()
	        	apps.forEach(function (app) {
	        		var tabs = app.name.length < 8 ? '\t\t' : '\t',
	        			status = app.isRunning ? 'ON '.green : 'OFF'.grey
	        	    console.log('  ' + app.name.yellow + tabs + ' - ' + status + ' : ' + app.port)
	        	})
	        	console.log()
	        }
	    })
	},

	procs: function () {
	    api.listProcs(output)
	}

}

if (!api) {
	console.log('Hello! It seems it\'s your first time running pod on this machine.')
	prompt('Please specify a root directory for pod to store all the files (repos, logs, apps):', function (dir) {
	    dir = path.resolve(process.cwd(), dir)
	    if (fs.existsSync(dir)) {
	    	if (!fs.statSync(dir).isDirectory()) {
	    		return warn('target path ' + dir.grey + ' is not a directory.')
	    	} else {
	    		api = core.initConfig(dir)
	    		parseCommand()
	    	}
	    } else {
	    	prompt('target path ' + dir.grey + ' doesn\'t exist. create it? y/n', function (reply) {
                if (reply.toLowerCase() === 'y') {
                    fs.mkdirSync(dir)
                    api = core.initConfig(dir)
                    parseCommand()
                } else {
                	process.exit(0)
                }
            })
	    }
	})
} else {
	parseCommand()
}

function parseCommand () {
	var command = argv._[0] || 'help'
    if (cli[command]) {
        cli[command](argv._[1])
    } else {
        if (command) {
            warn('unknown command ' + command.red)
        }
    }
}

function prompt (msg, callback) {
    console.log(msg)
    process.stdin.resume()
    process.stdin.setEncoding('utf8')
    process.stdin.on('data', function (input) {
        process.stdin.pause()
        callback(input.replace(/\n/g, ''))
    })
}

function edit (file, callback) {
    process.stdin.setRawMode(true)
    var p = spawn(api.getConfig().editor, [file], {
        customFds: [ 0, 1, 2 ]
    })
    p.on('exit', function () {
        process.stdin.setRawMode(false)
        callback && callback()
    })
}

function output (err, msg) {
    err ? warn(err) : log(msg)
}

function log (msg) {
    if (!Array.isArray(msg)) {
        console.log('pod '.green + msg)
    } else {
        msg.forEach(function (m) {
            console.log('pod '.green + m)
        })
    }
}

function warn (err) {
    err = err.toString().replace('Error: ', '')
    console.warn('pod '.green + 'ERR '.red + err)
}<commit_msg>tab adjustment<commit_after>var fs     = require('fs'),
	path   = require('path'),
	spawn  = require('child_process').spawn,
	argv   = require('optimist').argv,
	colors = require('colors')
	api    = require('./core').initCLI()

var globalConfigPath = process.env.HOME + '/.podrc'

var cli    = {

	help: function () {
	    console.log(fs.readFileSync(__dirname + '/../help/usage', 'utf-8'))
	},

	config: function () {
	    edit(globalConfigPath)
	},

	create: function (appname) {
	    if (!appname) return
	    api.createApp(appname, output)
	},

	rm: function (appname) {
	    if (!appname) return
	    api.removeApp(appname, output)
	},

	start: function (appname) {
		if (!appname) return
		api.startApp(appname, output)
	},

	stop: function (appname) {
	    if (!appname) return
	    api.stopApp(appname, output)
	},

	restart: function (appname) {
	    if (!appname) return
	    api.restartApp(appname, output)
	},

	startall: function () {
	    api.startAllApps(output)
	},

	stopall: function () {
		api.stopAllApps(output)
	},

	restartall: function () {
		api.restartAllApps(output)
	},

	edit: function (appname) {
	    if (!appname) return
        edit(api.getAppInfo(appname).repoPath + '/hooks/post-receive')
	},

	list: function () {
	    api.listApps(function (err, apps) {
	        if (!apps.length) {
	        	log('no apps found.')
	        } else {
	        	console.log()
	        	apps.forEach(function (app) {
	        		var tabs = app.name.length < 6 ? '\t\t' : '\t',
	        			status = app.isRunning ? 'ON '.green : 'OFF'.grey
	        	    console.log('  ' + app.name.yellow + tabs + ' - ' + status + ' : ' + app.port)
	        	})
	        	console.log()
	        }
	    })
	},

	procs: function () {
	    api.listProcs(output)
	}

}

if (!api) {
	console.log('Hello! It seems it\'s your first time running pod on this machine.')
	prompt('Please specify a root directory for pod to store all the files (repos, logs, apps):', function (dir) {
	    dir = path.resolve(process.cwd(), dir)
	    if (fs.existsSync(dir)) {
	    	if (!fs.statSync(dir).isDirectory()) {
	    		return warn('target path ' + dir.grey + ' is not a directory.')
	    	} else {
	    		api = core.initConfig(dir)
	    		parseCommand()
	    	}
	    } else {
	    	prompt('target path ' + dir.grey + ' doesn\'t exist. create it? y/n', function (reply) {
                if (reply.toLowerCase() === 'y') {
                    fs.mkdirSync(dir)
                    api = core.initConfig(dir)
                    parseCommand()
                } else {
                	process.exit(0)
                }
            })
	    }
	})
} else {
	parseCommand()
}

function parseCommand () {
	var command = argv._[0] || 'help'
    if (cli[command]) {
        cli[command](argv._[1])
    } else {
        if (command) {
            warn('unknown command ' + command.red)
        }
    }
}

function prompt (msg, callback) {
    console.log(msg)
    process.stdin.resume()
    process.stdin.setEncoding('utf8')
    process.stdin.on('data', function (input) {
        process.stdin.pause()
        callback(input.replace(/\n/g, ''))
    })
}

function edit (file, callback) {
    process.stdin.setRawMode(true)
    var p = spawn(api.getConfig().editor, [file], {
        customFds: [ 0, 1, 2 ]
    })
    p.on('exit', function () {
        process.stdin.setRawMode(false)
        callback && callback()
    })
}

function output (err, msg) {
    err ? warn(err) : log(msg)
}

function log (msg) {
    if (!Array.isArray(msg)) {
        console.log('pod '.green + msg)
    } else {
        msg.forEach(function (m) {
            console.log('pod '.green + m)
        })
    }
}

function warn (err) {
    err = err.toString().replace('Error: ', '')
    console.warn('pod '.green + 'ERR '.red + err)
}<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var command = process.argv[2];

switch (command) {
  case 'translate':
    var version = process.argv[3];
    require('./translate').translate(version);
    break;

  case 'readme':
    require('./update_readme').update();
    break;

  case 'check':
    var maxVersions = require('./check_update').check();
    for (var ver in maxVersions) {
      console.log('Patch for v' + max + ' should be created.');
    }
    break;

  case 'update':
    require('./check_update').update();
    break;

  default:
    console.log('Unknown command ' + command);
    process.exit(1);
    break;
}
<commit_msg>バージョンチェックスクリプトの不備を修正<commit_after>#!/usr/bin/env node
'use strict';

var command = process.argv[2];

switch (command) {
  case 'translate':
    var version = process.argv[3];
    require('./translate').translate(version);
    break;

  case 'readme':
    require('./update_readme').update();
    break;

  case 'check':
    var maxVersions = require('./check_update').check();
    maxVersions.forEach(function(e) {
      console.log('Patch for v' + e + ' should be created.');
    });
    break;

  case 'update':
    require('./check_update').update();
    break;

  default:
    console.log('Unknown command ' + command);
    process.exit(1);
    break;
}
<|endoftext|>


<commit_before>var RtmClient = require('@slack/client').RtmClient
var WebClient = require('@slack/client').WebClient
var Mopidy = require('mopidy')
var SpotifyWebApi = require('spotify-web-api-node')
var winston = require('winston')
var attachments = require('./attachments')
var DopeBot = require('./dopebot').DopeBot
var yargs = require('yargs')

'use strict'

module.exports = function () {
  var argv = yargs
    .usage('Usage: dopebot [options]')
    .option('token', {
      alias: 't',
      demand: true,
      type: 'string',
      describe: 'Set token for the Slack API'
    })
    .option('channel', {
      alias: 'c',
      demand: true,
      type: 'string',
      describe: 'Set the Slack channel name to listen for input'
    })
    .option('mopidy', {
      alias: 'm',
      type: 'string',
      describe: 'Set the WS-URL of Mopidy',
      default: 'ws://localhost:6680/mopidy/ws'
    })
    .option('mopidy', {
      alias: 'm',
      type: 'string',
      describe: 'Set the WS-URL of Mopidy',
      default: 'ws://localhost:6680/mopidy/ws'
    })
    .option('dialog', {
      alias: 'd',
      type: 'boolean',
      describe: 'Only respond to <@dopebot>',
      default: false
    })
    .option('brain', {
      alias: 'b',
      type: 'boolean',
      describe: 'Remember user commands',
      default: false
    })
    .option('unfurl', {
      alias: 'u',
      type: 'boolean',
      describe: 'Unfold Spotify URIs in Slack',
      default: false
    })
    .option('emoji', {
      alias: 'e',
      type: 'string',
      describe: 'Dopebot emoji icon in Slack',
      default: ':loud_sound:'
    })
    .option('limit', {
      alias: 'l',
      type: 'number',
      describe: 'Set limit for Spotify API result lists',
      default: 20
    })
    .option('id', {
      alias: 'i',
      demand: true,
      type: 'string',
      describe: 'Set the Spotify client Id'
    })
    .option('secret', {
      alias: 's',
      demand: true,
      type: 'string',
      describe: 'Set the Spotify client secret'
    })
    .option('limit', {
      alias: 'l',
      type: 'number',
      describe: 'Set limit for Spotify API result lists',
      default: 20
    })
    .option('verbose', {
      alias: 'v',
      type: 'count',
      describe: 'Increase verbosity'
    })
    .help('help')
    .alias('help', 'h')
    .locale('en')
    .argv

  if (argv.help) {
    // show help and exit
    yargs.showHelp()
  }

  var logger = new (winston.Logger)()

  logger.add(winston.transports.Console, {
    level: Object.keys(winston.config.npm.levels).find(function (level) {
      return winston.levels[level] === Math.min(argv.verbose + 2, Object.keys(winston.levels).length - 1)
    }),
    prettyPrint: true,
    colorize: true,
    silent: false,
    timestamp: true
  })

  var slack = {
    rtm: new RtmClient(argv.token, {
      logger: logger.log.bind(logger)
    }),
    web: new WebClient(argv.token, {
      logger: logger.log.bind(logger)
    })
  }

  var mopidy = new Mopidy({
    webSocketUrl: argv.mopidy,
    console: logger,
    autoConnect: false,
    callingConvention: 'by-position-only'
  })

  var spotifyApi = new SpotifyWebApi({
    clientId : argv.id,
    clientSecret : argv.secret,
    redirectUri: 'https://github.com/100hz/dopebot'
  })

  spotifyApi.clientCredentialsGrant()
    .then(function(data) {
      logger.debug('Spotify: Token expires in: ' + data.body['expires_in'])
      spotifyApi.setAccessToken(data.body['access_token'])
    }, function(err) {
      logger.error('Access token error:' + err.message)
    })

  var db = new DopeBot(logger, slack, mopidy, spotifyApi, {
    channel: argv.channel,
    dialog: argv.dialog === true,
    history: argv.brain === true,
    limit: parseInt(argv.limit),
    attachments: attachments({
      color: '#ff9800',
      formatTrack: function (track) {
        return {
          track: `<${track.uri}|${track.name}>`,
          artists: track.artists.map(artist => `<${artist.uri}|${artist.name}>`).join(', '),
          album: `<${track.album.uri}|${track.album.name}>`
        }
      }
    }),
    params: {
      username: 'dopebot',
      icon_emoji: argv.emoji,
      unfurl_links: argv.unfurl === true,
      unfurl_media: argv.unfurl === true
    }
  })

  db.run()
}
<commit_msg>Changed authentication strategy<commit_after>var RtmClient = require('@slack/client').RtmClient
var WebClient = require('@slack/client').WebClient
var Mopidy = require('mopidy')
var SpotifyWebApi = require('spotify-web-api-node')
var winston = require('winston')
var attachments = require('./attachments')
var DopeBot = require('./dopebot').DopeBot
var yargs = require('yargs')

'use strict'

module.exports = function () {
  var argv = yargs
    .usage('Usage: dopebot [options]')
    .option('token', {
      alias: 't',
      demand: true,
      type: 'string',
      describe: 'Set token for the Slack API'
    })
    .option('channel', {
      alias: 'c',
      demand: true,
      type: 'string',
      describe: 'Set the Slack channel name to listen for input'
    })
    .option('mopidy', {
      alias: 'm',
      type: 'string',
      describe: 'Set the WS-URL of Mopidy',
      default: 'ws://localhost:6680/mopidy/ws'
    })
    .option('mopidy', {
      alias: 'm',
      type: 'string',
      describe: 'Set the WS-URL of Mopidy',
      default: 'ws://localhost:6680/mopidy/ws'
    })
    .option('dialog', {
      alias: 'd',
      type: 'boolean',
      describe: 'Only respond to <@dopebot>',
      default: false
    })
    .option('brain', {
      alias: 'b',
      type: 'boolean',
      describe: 'Remember user commands',
      default: false
    })
    .option('unfurl', {
      alias: 'u',
      type: 'boolean',
      describe: 'Unfold Spotify URIs in Slack',
      default: false
    })
    .option('emoji', {
      alias: 'e',
      type: 'string',
      describe: 'Dopebot emoji icon in Slack',
      default: ':loud_sound:'
    })
    .option('limit', {
      alias: 'l',
      type: 'number',
      describe: 'Set limit for Spotify API result lists',
      default: 20
    })
    .option('id', {
      alias: 'i',
      demand: true,
      type: 'string',
      describe: 'Set the Spotify client Id'
    })
    .option('secret', {
      alias: 's',
      demand: true,
      type: 'string',
      describe: 'Set the Spotify client secret'
    })
    .option('code', {
      alias: 'x',
      demand: true,
      type: 'string',
      describe: 'Set the Spotify code'
    })
    .option('limit', {
      alias: 'l',
      type: 'number',
      describe: 'Set limit for Spotify API result lists',
      default: 20
    })
    .option('verbose', {
      alias: 'v',
      type: 'count',
      describe: 'Increase verbosity'
    })
    .help('help')
    .alias('help', 'h')
    .locale('en')
    .argv

  if (argv.help) {
    // show help and exit
    yargs.showHelp()
  }

  var logger = new (winston.Logger)()

  logger.add(winston.transports.Console, {
    level: Object.keys(winston.config.npm.levels).find(function (level) {
      return winston.levels[level] === Math.min(argv.verbose + 2, Object.keys(winston.levels).length - 1)
    }),
    prettyPrint: true,
    colorize: true,
    silent: false,
    timestamp: true
  })

  var slack = {
    rtm: new RtmClient(argv.token, {
      logger: logger.log.bind(logger)
    }),
    web: new WebClient(argv.token, {
      logger: logger.log.bind(logger)
    })
  }

  var mopidy = new Mopidy({
    webSocketUrl: argv.mopidy,
    console: logger,
    autoConnect: false,
    callingConvention: 'by-position-only'
  })

  var spotifyApi = new SpotifyWebApi({
    clientId : argv.id,
    clientSecret : argv.secret,
    redirectUri: 'https://example.com/callback'
  })

  // Retrieve an access token and a refresh token
  spotifyApi.authorizationCodeGrant(argv.code)
    .then(function(data) {
      logger.debug('Spotify: The token expires in ' + data.body['expires_in'])
      logger.debug('Spotify: The access token is ' + data.body['access_token'])
      logger.debug('Spotify: The refresh token is ' + data.body['refresh_token'])

      // Set the access token on the API object to use it in later calls
      spotifyApi.setAccessToken(data.body['access_token'])
      spotifyApi.setRefreshToken(data.body['refresh_token'])
    }, function(err) {
      logger.error('Access token error:' + err.message)
    });

  var db = new DopeBot(logger, slack, mopidy, spotifyApi, {
    channel: argv.channel,
    dialog: argv.dialog === true,
    history: argv.brain === true,
    limit: parseInt(argv.limit),
    attachments: attachments({
      color: '#ff9800',
      formatTrack: function (track) {
        return {
          track: `<${track.uri}|${track.name}>`,
          artists: track.artists.map(artist => `<${artist.uri}|${artist.name}>`).join(', '),
          album: `<${track.album.uri}|${track.album.name}>`
        }
      }
    }),
    params: {
      username: 'dopebot',
      icon_emoji: argv.emoji,
      unfurl_links: argv.unfurl === true,
      unfurl_media: argv.unfurl === true
    }
  })

  db.run()
}
<|endoftext|>


<commit_before>var async = require('async');
var fs = require('fs');
var path = require('path');

var _ = require('lodash');

var argv = require('./argv');

var Formatter = require('./formatter');
var Logger = require('content-logger');

var contentLogger = Logger.create({});

var MAP_OMIT = {
	'$0': true,
	'_': true
};

var flags = _.reduce(
	argv,
	function(res, item, index) {
		if (index.length > 1 && !MAP_OMIT[index]) {
			index = _.camelCase(index);

			res[index] = item;
		}

		return res;
	},
	{}
);

_.mixin(
	{
		bindRight: function(fn, context) {
			var args = _.toArray(arguments).slice(2);

			args.unshift(_.bind(fn, context));

			return _.partialRight.apply(_, args);
		}
	}
);

var CLI = function(config) {
	config = config || {};

	this.flags = config.flags || flags;

	this._args = config.args || argv._;
	this._async = config.async || async;
	this._cwd = config.cwd || process.cwd();
	this._log = config.log || console.log.bind(console);
	this._logger = config.logger || new contentLogger();
	this._read = config.read || fs.readFile.bind(fs);
	this._write = config.write || fs.writeFile.bind(fs);
};

CLI.prototype = {
	constructor: CLI,

	init: function() {
		var instance = this;

		var series = instance._args.map(
			function(file) {
				return instance.processFile.bind(instance, file);
			}
		);

		instance._async.series(series, instance.onFinish.bind(instance));
	},

	formatFile: function(file, data, done) {
		var formatter = Formatter.get(file, this._logger, this.flags);

		if (formatter) {
			this.processFileData(data, formatter, done);
		}
	},

	logResults: function(out, file) {
		if (out) {
			this._log(out);
		}

		var verboseDetails = this._logger.verboseDetails[file];

		if (verboseDetails) {
			this._log(verboseDetails);
		}
	},

	onFinish: function() {

	},

	onRead: function(err, data, file, done) {
		if (err) {
			done = file;
			file = data;

			this._log(err);
		}
		else {
			this.formatFile(file, data, done);
		}

		done();
	},

	processFile: function(file, done) {
		this._read(file, 'utf-8', _.bindRight(this.onRead, this, file, done));
	},

	processFileData: function(data, formatter, done) {
		var file = formatter.file;

		var contents = formatter.format(data);

		this.logResults(this.renderOutput(file), file);

		if (contents !== data && this.flags.inlineEdit) {
			this.writeFile(file, contents, done);
		}
	},

	renderOutput: function(file) {
		var flags = this.flags;

		var config = {
			showColumns: flags.showColumns
		};

		var out;

		if (flags.relative) {
			config.relative = this._cwd;
		}

		if (flags.filenames) {
			out = this._logger.renderFileNames(file, config);
		}
		else {
			config.showBanner = flags.quiet;
			config.showLintIds = flags.lintIds;

			out = this._logger.render(file, config);
		}

		return out;
	},

	writeFile: function(file, contents, done) {
		this._write(file, contents, _.bindRight(this.onWrite, this, file, done));
	}
};

var cliInstance = new CLI();

cliInstance.CLI = CLI;

module.exports = cliInstance;<commit_msg>inlineEdit/open file functionality<commit_after>var async = require('async');
var fs = require('fs');
var path = require('path');
var util = require('util');

var _ = require('lodash');

var argv = require('./argv');

var Formatter = require('./formatter');
var Logger = require('content-logger');

var contentLogger = Logger.create({});

var MAP_OMIT = {
	'$0': true,
	'_': true
};

var flags = _.reduce(
	argv,
	function(res, item, index) {
		if (index.length > 1 && !MAP_OMIT[index]) {
			index = _.camelCase(index);

			res[index] = item;
		}

		return res;
	},
	{}
);

_.mixin(
	{
		bindRight: function(fn, context) {
			var args = _.toArray(arguments).slice(2);

			args.unshift(_.bind(fn, context));

			return _.partialRight.apply(_, args);
		}
	}
);

var CLI = function(config) {
	config = config || {};

	this.flags = config.flags || flags;

	this._args = config.args || argv._;
	this._async = config.async || async;
	this._cwd = config.cwd || process.cwd();
	this._log = config.log || console.log.bind(console);
	this._logger = config.logger || new contentLogger();
	this._read = config.read || fs.readFile.bind(fs);
	this._write = config.write || fs.writeFile.bind(fs);

	this._exec = require('child_process').exec;
};

CLI.prototype = {
	constructor: CLI,

	init: function() {
		var instance = this;

		var series = instance._args.map(
			function(file) {
				return instance.processFile.bind(instance, file);
			}
		);

		instance._async.series(series, instance.onFinish.bind(instance));
	},

	formatFile: function(file, data, done) {
		var formatter = Formatter.get(file, this._logger, this.flags);

		if (formatter) {
			this.processFileData(data, formatter, done);
		}
	},

	logGeneralError: function(err) {
		var msg = '';

		if (err) {
			msg = util.format(colors.error('Something went wrong.\nDetails below:') + '\n%s', err.stack);

			this._log(msg);
		}

		return msg;
	},

	logResults: function(out, file) {
		if (out) {
			this._log(out);
		}

		var verboseDetails = this._logger.verboseDetails[file];

		if (verboseDetails) {
			this._log(verboseDetails);
		}
	},

	onFinish: function(err, results) {
		this.logGeneralError(err);

		if (this.flags.open) {
			this.openFiles(err, results);
		}
	},

	onRead: function(err, data, file, done) {
		if (err) {
			done = file;
			file = data;

			this._log(err);
		}
		else {
			this.formatFile(file, data, done);
		}

		done();
	},

	onWrite: function(err, file, done) {
		var writeResults = '';

		if (err) {
			writeResults = err;
		}
		else {
			writeResults = util.format('Wrote file: %s', file);
		}

		this._log(writeResults);

		if (_.isFunction(done)) {
			done();
		}
	},

	openFiles: function(err, result) {
		var instance = this;

		var errorFiles = Object.keys(instance._logger.getErrors());

		if (errorFiles.length) {
			instance._exec(
				'git config --get user.editor',
				function(res) {
					instance._exec(
						'open -a "' + res[0] + '" "' + errorFiles.join('" "') + '"'
					);
				}
			);
		}
	},

	processFile: function(file, done) {
		this._read(file, 'utf-8', _.bindRight(this.onRead, this, file, done));
	},

	processFileData: function(data, formatter, done) {
		var file = formatter.file;

		var contents = formatter.format(data);

		this.logResults(this.renderOutput(file), file);

		if (contents !== data && this.flags.inlineEdit) {
			this.writeFile(file, contents, done);
		}
	},

	renderOutput: function(file) {
		var flags = this.flags;

		var config = {
			showColumns: flags.showColumns
		};

		var out;

		if (flags.relative) {
			config.relative = this._cwd;
		}

		if (flags.filenames) {
			out = this._logger.renderFileNames(file, config);
		}
		else {
			config.showBanner = flags.quiet;
			config.showLintIds = flags.lintIds;

			out = this._logger.render(file, config);
		}

		return out;
	},

	writeFile: function(file, contents, done) {
		this._write(file, contents, _.bindRight(this.onWrite, this, file, done));
	}
};

var cliInstance = new CLI();

cliInstance.CLI = CLI;

module.exports = cliInstance;<|endoftext|>


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

import meow from 'meow';
import fs from 'fs-promise';
import path from 'path';
import {buildTheme} from './';
import logger from './logger';
import openInBrowser from 'open';

require('babel-polyfill');

(async function() {
  async function resolvePath(path) {
    let stat = await fs.lstat(path);
    if (stat.isFile()) {
      return path;
    }
  }

  const options = `Usage:
    $ base16-builder [-s <scheme>] [-t <template>] [-b <light|dark>]
    $ base16-builder [-s <scheme path>] [-t <template path>]

  Options:
    -s, --scheme     Build theme using this color scheme
    -t, --template   Build theme using this template
    -b, --brightness Build theme using this brightness

  Examples:
    $ base16-builder -s oceanicnext -t rxvt-unicode -b dark
    $ base16-builder --scheme oceanicnext --template rxvt-unicode --brightness dark
    $ base16-builder --scheme schemes/customScheme.yml --template templs/customTempl.nunjucks`;
  const cli = meow(options, {
    alias: {
      h: 'help',
      t: 'template',
      s: 'scheme',
      b: 'brightness'
    }
  });

  if (cli.input[0] && cli.input[0] === 'preview') {
    const previewPath = path.join(__dirname, 'index.html');
    openInBrowser(previewPath);
    return;
  }

  if (Object.keys(cli.flags).length === 0) {
    cli.showHelp();
    return;
  }

  let {template: templNameOrPath, scheme: schemeNameOrPath, brightness} = cli.flags;

  if (!templNameOrPath || !schemeNameOrPath) {
    logger.error('fatal: You did not supply valid arguments. Run \'base16-builder --help\' for guidance.');
    return;
  }

  schemeNameOrPath = schemeNameOrPath.toLowerCase();
  templNameOrPath = templNameOrPath.toLowerCase();

  let templPath;
  try {
    templPath = await resolvePath(templNameOrPath);
  } catch (error) {
  }

  if (!templPath) {
    if (!brightness) {
      logger.error('fatal: You did not supply valid arguments. Run \'base16-builder --help\' for guidance.');
      return;
    }

    brightness = brightness.toLowerCase();
    if (brightness !== 'light' && brightness !== 'dark') {
      logger.error(
        'fatal: You did not supply valid arguments. The value for brightness must be \'light\' or \'dark\'.');
      return;
    }

    templPath = path.join(__dirname, `db/templates/${templNameOrPath}/${brightness}.nunjucks`);
  }

  let schemePath;
  try {
    schemePath = await resolvePath(schemeNameOrPath);
  } catch (error) {
  }

  if (!schemePath) {
    schemePath = path.join(__dirname, `db/schemes/${schemeNameOrPath}.yml`);
  }

  let templ;
  try {
    templ = await fs.readFile(templPath, 'utf8');
  } catch (err) {
    if (err.code === 'ENOENT') {
      logger.error(`Could not find template ${templNameOrPath}.`);
      return;
    }
    logger.error(`Unexpected error: ${JSON.stringify(err)}`);
    return;
  }

  let scheme;
  try {
    scheme = await fs.readFile(schemePath, 'utf8');
  } catch (err) {
    if (err.code === 'ENOENT') {
      logger.error(`Could not find scheme ${schemeNameOrPath}.`);
      return;
    }
    logger.error(`Unexpected error: ${JSON.stringify(err)}`);
  }

  let theme;
  try {
    theme = buildTheme(scheme, templ);
  } catch (err) {
    logger.error(`Unexpected error: ${JSON.stringify(err)}`);
  }

  logger.log(theme);
})();
<commit_msg>feat(cli): Rename preview command to ls schemes<commit_after>#!/usr/bin/env node

import meow from 'meow';
import fs from 'fs-promise';
import path from 'path';
import {buildTheme} from './';
import logger from './logger';
import openInBrowser from 'open';

require('babel-polyfill');

(async function() {
  async function resolvePath(path) {
    let stat = await fs.lstat(path);
    if (stat.isFile()) {
      return path;
    }
  }

  const options = `Usage:
    $ base16-builder [-s <scheme>] [-t <template>] [-b <light|dark>]
    $ base16-builder [-s <scheme path>] [-t <template path>]

  Options:
    -s, --scheme     Build theme using this color scheme
    -t, --template   Build theme using this template
    -b, --brightness Build theme using this brightness

  Examples:
    $ base16-builder -s oceanicnext -t rxvt-unicode -b dark
    $ base16-builder --scheme oceanicnext --template rxvt-unicode --brightness dark
    $ base16-builder --scheme schemes/customScheme.yml --template templs/customTempl.nunjucks`;
  const cli = meow(options, {
    alias: {
      h: 'help',
      t: 'template',
      s: 'scheme',
      b: 'brightness'
    }
  });

  if (cli.input[0] &&
      cli.input[0] === 'ls' &&
      cli.input[1] === 'schemes') {
    const schemesHtmlPath = path.join(__dirname, 'index.html');
    openInBrowser(schemesHtmlPath);
    return;
  }

  if (Object.keys(cli.flags).length === 0) {
    cli.showHelp();
    return;
  }

  let {template: templNameOrPath, scheme: schemeNameOrPath, brightness} = cli.flags;

  if (!templNameOrPath || !schemeNameOrPath) {
    logger.error('fatal: You did not supply valid arguments. Run \'base16-builder --help\' for guidance.');
    return;
  }

  schemeNameOrPath = schemeNameOrPath.toLowerCase();
  templNameOrPath = templNameOrPath.toLowerCase();

  let templPath;
  try {
    templPath = await resolvePath(templNameOrPath);
  } catch (error) {
  }

  if (!templPath) {
    if (!brightness) {
      logger.error('fatal: You did not supply valid arguments. Run \'base16-builder --help\' for guidance.');
      return;
    }

    brightness = brightness.toLowerCase();
    if (brightness !== 'light' && brightness !== 'dark') {
      logger.error(
        'fatal: You did not supply valid arguments. The value for brightness must be \'light\' or \'dark\'.');
      return;
    }

    templPath = path.join(__dirname, `db/templates/${templNameOrPath}/${brightness}.nunjucks`);
  }

  let schemePath;
  try {
    schemePath = await resolvePath(schemeNameOrPath);
  } catch (error) {
  }

  if (!schemePath) {
    schemePath = path.join(__dirname, `db/schemes/${schemeNameOrPath}.yml`);
  }

  let templ;
  try {
    templ = await fs.readFile(templPath, 'utf8');
  } catch (err) {
    if (err.code === 'ENOENT') {
      logger.error(`Could not find template ${templNameOrPath}.`);
      return;
    }
    logger.error(`Unexpected error: ${JSON.stringify(err)}`);
    return;
  }

  let scheme;
  try {
    scheme = await fs.readFile(schemePath, 'utf8');
  } catch (err) {
    if (err.code === 'ENOENT') {
      logger.error(`Could not find scheme ${schemeNameOrPath}.`);
      return;
    }
    logger.error(`Unexpected error: ${JSON.stringify(err)}`);
  }

  let theme;
  try {
    theme = buildTheme(scheme, templ);
  } catch (err) {
    logger.error(`Unexpected error: ${JSON.stringify(err)}`);
  }

  logger.log(theme);
})();
<|endoftext|>


<commit_before>#! /usr/bin/env node
'use strict'

const fs = require('fs')
const path = require('path')
const os = require('os')

const Bronze = require('../')
const packageInfo = require('../package')

const defaultSequencePath = path._makeLong(path.join(os.homedir(), '.bronze', 'sequence'))
let output = console.log

function displayHelp () {
  const helpText = `
  bronze ${packageInfo.version}

  ${packageInfo.description}

  Usage: bronze [options]

  Options:
    --sequence INT          Set the counter for the number of ids generated
                            By default will use sequence file (sequence path)
                            If set sequence file will not be updated
    --pid INT               Set the process id for generated ids
    --name STRING           A unique name for the generator
                            Any slashes will be replaced with underscores
    --spec STRING           Set the spec

    --gen, -g INT           The number of ids to create. Must be >= 0.
                            Default = 1
    --list-specs            Get the specs available from this version of bronze
    --sequence-dir STRING   Set the sequence directory
                            Will attempt to create if not exist
    --sequence-dir-reset    Sets the sequence back to 0
                            File isn't created if it doesn't exist

    --help, -h
    --version, -v

  Notes:
    - Same name + same time + different machines = bad idea (collisions)
    - If any option is invalid will fallback to default
    - Any unknown arguments will cause help to print
    - Subsequent options of the same type will override existing:
      - Ex: \`bronze -g 1 -g 3\` will generate 3 ids
    - Options can be set in 2 styles if they require a value:
      - arg, arg    Ex: \`--sequence 1\`
      - equals      Ex: \`--sequence=1\`
    - Option Resolution:
      - If Help needs to print or requested, print. Process ends.
      - If List Specs requested, print. Process ends.
      - If version requested, print. Process ends.
      - Reset Sequence File, if requested.
      - If sequence option, use. Else read/create sequence directory
      - Generate requested ids with given options

  Available Specs:
    - ${Object.keys(Bronze.specs).join(' ')}

  Default Sequence Path:
    - ${defaultSequencePath}

  README.md:
    - ${path.join(__dirname, '..', 'README.md')}

  Bugs:
    - Email: ${packageInfo.bugs.email}
    - URL: ${packageInfo.bugs.url}

  License: ${packageInfo.license}`

  output(helpText)
}

function displaySpecs () {
  output(Object.keys(Bronze.specs).join(' '))
}
function displayVersion () {
  output(packageInfo.version)
}

function updateSequenceFile (sequencePath = '', sequenceCount = 0) {
  fs.writeFileSync(sequencePath, `${sequenceCount}`, 'utf8')
}

function makeIfDirDoesntExist (givenPath = '') {
  if (fs.existsSync(givenPath) === true) return

  try {
    fs.mkdirSync(givenPath)
  } catch (e) {
    if (e.code === 'ENOENT') {
      makeIfDirDoesntExist(path.parse(givenPath).dir)
      fs.mkdirSync(givenPath)
    } else {
      throw e
    }
  }
}

function readFromSequenceFile (sequencePath = '') {
  if (fs.existsSync(sequencePath) === false) {
    makeIfDirDoesntExist(path.parse(sequencePath).dir)
    return 0
  } else {
    const sequenceCountCandidate = Number(fs.readFileSync(sequencePath, 'utf8'))

    if (Number.isSafeInteger(sequenceCountCandidate) === true) {
      return sequenceCountCandidate
    }
  }
}

function generateIds (options = {}, count = 0, sequencePath = '') {
  const idGen = new Bronze(options)

  for (let i = 0; i < count; i++) {
    output(idGen.generate())
  }

  if (typeof sequencePath === 'string') {
    updateSequenceFile(sequencePath, idGen.sequence)
  }
}

function parseCommand (givenArgs = process.argv.slice(2)) {
  let bronzeOptions = {}
  let generateIdCount = 1
  let sequencePath = defaultSequencePath
  let isHelp = false
  let isListSpecs = false
  let isVersion = false
  let resetSequenceFile = false

  for (let i = 0, len = givenArgs.length; i < len; i++) {
    const arg = givenArgs[i].trim()
    let advanceAdditional = false
    let advanceAdditionalCandidate = false
    let valueCandidate = null

    if (arg.indexOf('--pid') === 0) {
      if (arg.indexOf('--pid=') === 0) {
        valueCandidate = Number(arg.slice('--pid='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true) {
        bronzeOptions.pid = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--name') === 0) {
      if (arg.indexOf('--name=') === 0) {
        valueCandidate = arg.slice('--name='.length)
      } else {
        valueCandidate = givenArgs[i + 1]
        advanceAdditionalCandidate = true
      }

      if (typeof valueCandidate === 'string') {
        bronzeOptions.name = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--spec') === 0) {
      if (arg.indexOf('--spec=') === 0) {
        valueCandidate = arg.slice('--spec='.length)
      } else {
        valueCandidate = givenArgs[i + 1]
        advanceAdditionalCandidate = true
      }

      if (typeof valueCandidate === 'string') {
        bronzeOptions.spec = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--gen') === 0) {
      if (arg.indexOf('--gen=') === 0) {
        valueCandidate = Number(arg.slice('--gen='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true && valueCandidate >= 0) {
        generateIdCount = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('-g') === 0) {
      if (arg.indexOf('-g=') === 0) {
        valueCandidate = Number(arg.slice('-g='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true && valueCandidate >= 0) {
        generateIdCount = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--list-specs') === 0) {
      isListSpecs = true
    } else if (arg.indexOf('--sequence-dir-reset') === 0) {
      resetSequenceFile = true
    } else if (arg.indexOf('--sequence-dir') === 0) {
      if (arg.indexOf('--sequence-dir=') === 0) {
        valueCandidate = arg.slice('--sequence-dir='.length)
      } else {
        valueCandidate = givenArgs[i + 1]
        advanceAdditionalCandidate = true
      }

      if (typeof valueCandidate === 'string') {
        sequencePath = path._makeLong(path.join(valueCandidate, 'sequence'))
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--sequence') === 0) {
      if (arg.indexOf('--sequence=') === 0) {
        valueCandidate = Number(arg.slice('--sequence='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true) {
        bronzeOptions.sequence = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--help') === 0 || arg.indexOf('-h') === 0) {
      isHelp = true
    } else if (arg.indexOf('--version') === 0 || arg.indexOf('-v') === 0) {
      isVersion = true
    } else {
      isHelp = true
    }

    if (advanceAdditional === true) {
      i++
    }
  }

  if (isHelp === true) {
    displayHelp()
  } else if (isListSpecs === true) {
    displaySpecs()
  } else if (isVersion === true) {
    displayVersion()
  } else {
    if (resetSequenceFile === true) {
      if (fs.existsSync(sequencePath) === true) {
        updateSequenceFile(sequencePath)
      }
    }

    if (typeof bronzeOptions.sequence === 'number') {
      generateIds(bronzeOptions, generateIdCount, null)
    } else {
      bronzeOptions.sequence = readFromSequenceFile(sequencePath)
      generateIds(bronzeOptions, generateIdCount, sequencePath)
    }
  }
}

if (module.parent === null) {
  parseCommand()
} else {
  module.exports = {
    displayHelp,
    displaySpecs,
    displayVersion,
    updateSequenceFile,
    makeIfDirDoesntExist,
    readFromSequenceFile,
    generateIds,
    parseCommand,
    get output () {
      return output
    },
    set output (o = console.log) {
      output = o
      return output
    }
  }
}
<commit_msg>`makeIfDirDoesntExist` -> `makeNestedDirectory`<commit_after>#! /usr/bin/env node
'use strict'

const fs = require('fs')
const path = require('path')
const os = require('os')

const Bronze = require('../')
const packageInfo = require('../package')

const defaultSequencePath = path._makeLong(path.join(os.homedir(), '.bronze', 'sequence'))
let output = console.log

function displayHelp () {
  const helpText = `
  bronze ${packageInfo.version}

  ${packageInfo.description}

  Usage: bronze [options]

  Options:
    --sequence INT          Set the counter for the number of ids generated
                            By default will use sequence file (sequence path)
                            If set sequence file will not be updated
    --pid INT               Set the process id for generated ids
    --name STRING           A unique name for the generator
                            Any slashes will be replaced with underscores
    --spec STRING           Set the spec

    --gen, -g INT           The number of ids to create. Must be >= 0.
                            Default = 1
    --list-specs            Get the specs available from this version of bronze
    --sequence-dir STRING   Set the sequence directory
                            Will attempt to create if not exist
    --sequence-dir-reset    Sets the sequence back to 0
                            File isn't created if it doesn't exist

    --help, -h
    --version, -v

  Notes:
    - Same name + same time + different machines = bad idea (collisions)
    - If any option is invalid will fallback to default
    - Any unknown arguments will cause help to print
    - Subsequent options of the same type will override existing:
      - Ex: \`bronze -g 1 -g 3\` will generate 3 ids
    - Options can be set in 2 styles if they require a value:
      - arg, arg    Ex: \`--sequence 1\`
      - equals      Ex: \`--sequence=1\`
    - Option Resolution:
      - If Help needs to print or requested, print. Process ends.
      - If List Specs requested, print. Process ends.
      - If version requested, print. Process ends.
      - Reset Sequence File, if requested.
      - If sequence option, use. Else read/create sequence directory
      - Generate requested ids with given options

  Available Specs:
    - ${Object.keys(Bronze.specs).join(' ')}

  Default Sequence Path:
    - ${defaultSequencePath}

  README.md:
    - ${path.join(__dirname, '..', 'README.md')}

  Bugs:
    - Email: ${packageInfo.bugs.email}
    - URL: ${packageInfo.bugs.url}

  License: ${packageInfo.license}`

  output(helpText)
}

function displaySpecs () {
  output(Object.keys(Bronze.specs).join(' '))
}
function displayVersion () {
  output(packageInfo.version)
}

function updateSequenceFile (sequencePath = '', sequenceCount = 0) {
  fs.writeFileSync(sequencePath, `${sequenceCount}`, 'utf8')
}

function makeNestedDirectory (givenPath = '') {
  if (fs.existsSync(givenPath) === true) return

  try {
    fs.mkdirSync(givenPath)
  } catch (e) {
    if (e.code === 'ENOENT') {
      makeNestedDirectory(path.parse(givenPath).dir)
      fs.mkdirSync(givenPath)
    } else {
      throw e
    }
  }
}

function readFromSequenceFile (sequencePath = '') {
  if (fs.existsSync(sequencePath) === false) {
    makeNestedDirectory(path.parse(sequencePath).dir)
    return 0
  } else {
    const sequenceCountCandidate = Number(fs.readFileSync(sequencePath, 'utf8'))

    if (Number.isSafeInteger(sequenceCountCandidate) === true) {
      return sequenceCountCandidate
    }
  }
}

function generateIds (options = {}, count = 0, sequencePath = '') {
  const idGen = new Bronze(options)

  for (let i = 0; i < count; i++) {
    output(idGen.generate())
  }

  if (typeof sequencePath === 'string') {
    updateSequenceFile(sequencePath, idGen.sequence)
  }
}

function parseCommand (givenArgs = process.argv.slice(2)) {
  let bronzeOptions = {}
  let generateIdCount = 1
  let sequencePath = defaultSequencePath
  let isHelp = false
  let isListSpecs = false
  let isVersion = false
  let resetSequenceFile = false

  for (let i = 0, len = givenArgs.length; i < len; i++) {
    const arg = givenArgs[i].trim()
    let advanceAdditional = false
    let advanceAdditionalCandidate = false
    let valueCandidate = null

    if (arg.indexOf('--pid') === 0) {
      if (arg.indexOf('--pid=') === 0) {
        valueCandidate = Number(arg.slice('--pid='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true) {
        bronzeOptions.pid = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--name') === 0) {
      if (arg.indexOf('--name=') === 0) {
        valueCandidate = arg.slice('--name='.length)
      } else {
        valueCandidate = givenArgs[i + 1]
        advanceAdditionalCandidate = true
      }

      if (typeof valueCandidate === 'string') {
        bronzeOptions.name = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--spec') === 0) {
      if (arg.indexOf('--spec=') === 0) {
        valueCandidate = arg.slice('--spec='.length)
      } else {
        valueCandidate = givenArgs[i + 1]
        advanceAdditionalCandidate = true
      }

      if (typeof valueCandidate === 'string') {
        bronzeOptions.spec = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--gen') === 0) {
      if (arg.indexOf('--gen=') === 0) {
        valueCandidate = Number(arg.slice('--gen='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true && valueCandidate >= 0) {
        generateIdCount = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('-g') === 0) {
      if (arg.indexOf('-g=') === 0) {
        valueCandidate = Number(arg.slice('-g='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true && valueCandidate >= 0) {
        generateIdCount = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--list-specs') === 0) {
      isListSpecs = true
    } else if (arg.indexOf('--sequence-dir-reset') === 0) {
      resetSequenceFile = true
    } else if (arg.indexOf('--sequence-dir') === 0) {
      if (arg.indexOf('--sequence-dir=') === 0) {
        valueCandidate = arg.slice('--sequence-dir='.length)
      } else {
        valueCandidate = givenArgs[i + 1]
        advanceAdditionalCandidate = true
      }

      if (typeof valueCandidate === 'string') {
        sequencePath = path._makeLong(path.join(valueCandidate, 'sequence'))
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--sequence') === 0) {
      if (arg.indexOf('--sequence=') === 0) {
        valueCandidate = Number(arg.slice('--sequence='.length))
      } else {
        valueCandidate = Number(givenArgs[i + 1])
        advanceAdditionalCandidate = true
      }

      if (Number.isSafeInteger(valueCandidate) === true) {
        bronzeOptions.sequence = valueCandidate
        if (advanceAdditionalCandidate === true) {
          advanceAdditional = true
        }
      }
    } else if (arg.indexOf('--help') === 0 || arg.indexOf('-h') === 0) {
      isHelp = true
    } else if (arg.indexOf('--version') === 0 || arg.indexOf('-v') === 0) {
      isVersion = true
    } else {
      isHelp = true
    }

    if (advanceAdditional === true) {
      i++
    }
  }

  if (isHelp === true) {
    displayHelp()
  } else if (isListSpecs === true) {
    displaySpecs()
  } else if (isVersion === true) {
    displayVersion()
  } else {
    if (resetSequenceFile === true) {
      if (fs.existsSync(sequencePath) === true) {
        updateSequenceFile(sequencePath)
      }
    }

    if (typeof bronzeOptions.sequence === 'number') {
      generateIds(bronzeOptions, generateIdCount, null)
    } else {
      bronzeOptions.sequence = readFromSequenceFile(sequencePath)
      generateIds(bronzeOptions, generateIdCount, sequencePath)
    }
  }
}

if (module.parent === null) {
  parseCommand()
} else {
  module.exports = {
    displayHelp,
    displaySpecs,
    displayVersion,
    updateSequenceFile,
    makeIfDirDoesntExist,
    readFromSequenceFile,
    generateIds,
    parseCommand,
    get output () {
      return output
    },
    set output (o = console.log) {
      output = o
      return output
    }
  }
}
<|endoftext|>


<commit_before>var	util = require('util');
var path = require('path');
var fs = require('fs');
var cp = require('child_process');
var parseJSON = require('./util').parseJSON;
var BOOTSTRAP_PATH = path.join(__dirname, 'bootstrap.js');

function isRunning(pid, callback) {

	pid ? cp.exec(util.format(process.platform === 'win32' ?
			'tasklist /fi "PID eq %s" | findstr /i "node.exe"'
			: 'ps -f -p %s | grep "node"', pid),
			function (err, stdout, stderr) {
				callback(!err && !!stdout.toString().trim());
			})
			: callback(false);
}

exports.isRunning = isRunning;

function run(main, options, callback) {
	if (typeof options == 'function') {
		var tmp = options;
		options = callback;
		callback = tmp;
	}

	execCmd(main, options, {
		stdio: [ 0, 1, 2 ]
	});
	callback && callback();
}

exports.run = run;

function start(config, options, callback) {
	if (typeof options == 'function') {
		var tmp = options;
		options = callback;
		callback = tmp;
	}

	if (typeof config == 'string') {
		config = {main: config};
	}
	var now = new Date();
	var log = config.log || util.format('log/'
			+ 'starting-%s-%s-%s.log',
			now.getFullYear(), now.getMonth() + 1, now.getDate());
	var running = config.running || '.running';

	var pid;
	try {
		pid = fs.readFileSync(running, 'utf-8').split('\n')[0];
	} catch(e) {}

	isRunning(pid, function (isruning) {
		if (isruning) {
			return callback && callback(true);
		}
		//再次kill进程，确保kill掉
		stop(config.running, function () {
			!config.log && !fs.existsSync('log') && fs.mkdirSync('log');

			child = execCmd(config.main, options, {
				detached: true,
				stdio: ['ignore', 'ignore', fs.openSync(log, 'a+')]
			});

			fs.writeFileSync(running,  child.pid
					+ '\n' + JSON.stringify(options || {})
					+ '\n' + JSON.stringify(config));
			child.unref();

			callback && callback();
		});
	});
}

exports.start = start;

function restart(config, options, callback) {
	if (typeof options == 'function') {
		var tmp = options;
		options = callback;
		callback = tmp;
	}

	stop(config.running, function (isrunning, conf, opts) {
		setTimeout(function () {
			start(util._extend(conf, config), util._extend(opts, options), callback);
		}, 1000);
	});
}

exports.restart = restart;

function stop(running, callback) {
	if (typeof running == 'function') {
		var tmp = running;
		running = callback;
		callback = tmp;
	}

	var data, pid, config, options;
	running = running || '.running';
	try {
		data = fs.readFileSync(running, 'utf-8').split('\n');
		pid = data[0];
		options = parseJSON(data[1]);
		config = parseJSON(data[2]);
	} catch(e) {}

	isRunning(pid, function (isruning) {
		if (isruning) {
			try {
				process.kill(pid);
				fs.unlinkSync(running);
			} catch (err) {
				isruning = err;
			}
		}

		callback && callback(isruning, config || {}, options || {});
	});
}

exports.stop = stop;

function execCmd(main, data, options) {
	var args = [BOOTSTRAP_PATH, 'run', main];
	var cmd;

	if (data) {
		if (data.command) {
			cmd = data.command.trim().split(/\s+/g);
			args.unshift.apply(args, cmd.slice(1));
			cmd = cmd[0];
		}

		delete data.command;
		args.push('--data');
		args.push(encodeURIComponent(JSON.stringify(data)));
	}

	return cp.spawn(cmd || 'node', args, options);
}

<commit_msg>show start errors<commit_after>var	util = require('util');
var path = require('path');
var fs = require('fs');
var cp = require('child_process');
var parseJSON = require('./util').parseJSON;
var BOOTSTRAP_PATH = path.join(__dirname, 'bootstrap.js');
var LOG_DIR = path.join(__dirname, '.log');

function isRunning(pid, callback) {

	pid ? cp.exec(util.format(process.platform === 'win32' ?
			'tasklist /fi "PID eq %s" | findstr /i "node.exe"'
			: 'ps -f -p %s | grep "node"', pid),
			function (err, stdout, stderr) {
				callback(!err && !!stdout.toString().trim());
			})
			: callback(false);
}

exports.isRunning = isRunning;

function run(main, options, callback) {
	if (typeof options == 'function') {
		var tmp = options;
		options = callback;
		callback = tmp;
	}

	execCmd(main, options, {
		stdio: [ 0, 1, 2 ]
	});
	callback && callback();
}

exports.run = run;

function start(config, options, callback) {
	if (typeof options == 'function') {
		var tmp = options;
		options = callback;
		callback = tmp;
	}

	if (typeof config == 'string') {
		config = {main: config};
	}

	var running = config.running || '.running';

	var pid;
	try {
		pid = fs.readFileSync(running, 'utf-8').split('\n')[0];
	} catch(e) {}

	isRunning(pid, function (isruning) {
		if (isruning) {
			return callback && callback(true);
		}
		//再次kill进程，确保kill掉
		stop(config.running, function () {
			if (!fs.existsSync(LOG_DIR)) {
				fs.mkdirSync(LOG_DIR);
			}

			var errorFile = path.join(LOG_DIR, '.error')
			try {
				fs.unlinkSync(errorFile);
			} catch(e) {
				errorFile = path.join(LOG_DIR, '.error.' + Date.now());
			}
			child = execCmd(config.main, options, {
				detached: true,
				stdio: ['ignore', 'ignore', fs.openSync(errorFile, 'a+')]
			});

			var startTime = Date.now();
			(function execCallback() {
				var error;
				try {
					error = fs.readFileSync(errorFile, {encoding: 'utf8'});
				} catch(e) {}

				if (error) {
					try {
						console.log(error);
						process.kill(child.pid);
					} catch(e) {}
					return;
				}

				if (Date.now() - startTime < 3600) {

					return setTimeout(execCallback, 200);
				}

				fs.writeFileSync(running,  child.pid
						+ '\n' + JSON.stringify(options || {})
						+ '\n' + JSON.stringify(config));
				child.unref();

				callback && callback();
			})();
		});
	});
}

exports.start = start;

function restart(config, options, callback) {
	if (typeof options == 'function') {
		var tmp = options;
		options = callback;
		callback = tmp;
	}

	stop(config.running, function (isrunning, conf, opts) {
		setTimeout(function () {
			start(util._extend(conf, config), util._extend(opts, options), callback);
		}, 1000);
	});
}

exports.restart = restart;

function stop(running, callback) {
	if (typeof running == 'function') {
		var tmp = running;
		running = callback;
		callback = tmp;
	}

	var data, pid, config, options;
	running = running || '.running';
	try {
		data = fs.readFileSync(running, 'utf-8').split('\n');
		pid = data[0];
		options = parseJSON(data[1]);
		config = parseJSON(data[2]);
	} catch(e) {}

	isRunning(pid, function (isruning) {
		if (isruning) {
			try {
				process.kill(pid);
				fs.unlinkSync(running);
			} catch (err) {
				isruning = err;
			}
		}

		callback && callback(isruning, config || {}, options || {});
	});
}

exports.stop = stop;

function execCmd(main, data, options) {
	var args = [BOOTSTRAP_PATH, 'run', main];
	var cmd;

	if (data) {
		if (data.command) {
			cmd = data.command.trim().split(/\s+/g);
			args.unshift.apply(args, cmd.slice(1));
			cmd = cmd[0];
		}

		delete data.command;
		args.push('--data');
		args.push(encodeURIComponent(JSON.stringify(data)));
	}

	return cp.spawn(cmd || 'node', args, options);
}

<|endoftext|>