<commit_before>// TODO:
// - Use the same DHT object for looking up multiple torrents
// - Persist the routing table for later bootstrapping
// - Use actual DHT data structure with "buckets" (follow spec)
// - Add the method that allows us to list ourselves in the DHT
// - https://github.com/czzarr/node-bitwise-xor

module.exports = DHT

var bncode = require('bncode')
var compact2string = require('compact2string')
var debug = require('debug')('bittorrent-dht')
var dgram = require('dgram')
var EventEmitter = require('events').EventEmitter
var hat = require('hat')
var inherits = require('inherits')
var portfinder = require('portfinder')

// Use random port above 1024
portfinder.basePort = Math.floor(Math.random() * 60000) + 1025

var BOOTSTRAP_NODES = [
  'dht.transmissionbt.com:6881',
  'router.bittorrent.com:6881',
  'router.utorrent.com:6881'
]
var BOOTSTRAP_TIMEOUT = 5000
var MAX_NODES = 5000
var MAX_QUERY_PER_SECOND = 200
var MAX_REQUESTS = 3
var QUEUE_QUERY_INTERVAL = Math.floor(1000 / MAX_QUERY_PER_SECOND)
var REQ_TIMEOUT = 2000

function parseNodeInfo (compact) {
  try {
    var nodes = []
    for (var i = 0; i < compact.length; i += 26) {
      nodes.push(compact2string(compact.slice(i + 20, i + 26)))
    }
    return nodes
  } catch (err) {
    debug('Invalid node info ' + compact)
    return []
  }
}

function parsePeerInfo (list) {
  try {
    return list.map(compact2string)
  } catch (err) {
    debug('Invalid peer info ' + list)
    return []
  }
}

inherits(DHT, EventEmitter)

/**
 * Create a new DHT
 * @param {string|Buffer} opts
 */
function DHT (opts) {
  if (!(this instanceof DHT)) return new DHT(opts)
  EventEmitter.call(this)

  if (!opts) opts = {}
  if (!opts.nodeId) opts.nodeId = hat(160)

  this.nodeId = typeof opts.nodeId === 'string'
    ? new Buffer(opts.nodeId, 'hex')
    : opts.nodeId

  this.nodes = {}
  this.nodesCounter = 0
  this.nodesList = null // list cache
  this.peers = {}
  this.reqs = {}
  this.queue = [].concat(BOOTSTRAP_NODES)

  this.listening = false
  this._closed = false

  this.port = 0
  this.requestId = 1
  this.pendingRequests = {}
  // Number of peers we still need to find to satisfy the last call to findPeers
  this.missingPeers = 0

  this.pendingRequests[this.requestId] = 1

  this.socket = dgram.createSocket('udp4')
  this.socket.on('message', this._onData.bind(this))
  this.socket.on('listening', this._onListening.bind(this))
  this.socket.on('error', function () {}) // throw away errors
}

DHT.prototype.close = function () {
  this.listening = false
  this._closed = true
  try {
    this.socket.close()
  } catch (err) {
    // ignore error, socket was either already closed / not yet bound
  }
}

// TODO: support setting multiple infohashes
DHT.prototype.setInfoHash = function (infoHash) {
  this.infoHash = typeof infoHash === 'string'
    ? new Buffer(infoHash, 'hex')
    : infoHash

  this.message = {
    t: this.requestId.toString(),
    y: 'q',
    q: 'get_peers',
    a: {
      id: this.nodeId,
      info_hash: this.infoHash
    }
  }
  this.message = bncode.encode(this.message)
}

// addr:string -> [host:string, port:number]
var addrDataCache = {}

/**
 * Given an address/port string, return an array where arr[0] is host, and arr[1] is port
 * (as a Number). This uses a cache, so we don't need to keep creating new arrays. This
 * helps out the GC.
 * @param  {string} addr
 * @return {Array.<*>}
 */
function getAddrData (addr) {
  if (!addrDataCache[addr]) {
    var array = addr.split(':')
    array[1] = Number(array[1])
    addrDataCache[addr] = array
  }
  return addrDataCache[addr]
}

/**
 * Get the number of nodes in the DHT. This uses a simple counter to avoid repeatedly
 * creating new arrays with Object.keys.
 * @return {number}
 */
DHT.prototype.getNodesNum = function () {
  return this.nodesCounter
}

/**
 * Get a list of all nodes in the DHT. Uses a cached list when possible.
 * @return {Array.<string>}
 */
DHT.prototype.getNodesList = function () {
  if (!this.nodesList) {
    this.nodesList = Object.keys(this.nodes)
  }
  return this.nodesList
}

DHT.prototype.query = function (addr) {
  var numNodes = this.getNodesNum()
  if (numNodes > MAX_NODES || this.missingPeers <= 0 || this._closed) return

  var host = getAddrData(addr)[0]
  var port = getAddrData(addr)[1]
  if (!(port > 0 && port < 65535)) return
  this.socket.send(this.message, 0, this.message.length, port, host, function () {
    setTimeout(function () {
      this.reqs[addr] = (this.reqs[addr] || 0) + 1
      if (!this.nodes[addr] && this.reqs[addr] < MAX_REQUESTS) {
        this.query.call(this, addr)
      }
    }.bind(this), REQ_TIMEOUT)
  }.bind(this))
}

DHT.prototype._queryQueue = function () {
  if (this.queue.length) {
    this.query(this.queue.pop())
  } else {
    clearInterval(this.queueInterval)
    this.queueInterval = null
  }
}

/* Start querying queue, if not already */
DHT.prototype.queryQueue = function () {
  if (!this.queryInterval) {
    this.queryInterval = setInterval(this._queryQueue.bind(this), QUEUE_QUERY_INTERVAL)
    this.queryInterval.unref()
  }
}

DHT.prototype.findPeers = function (num) {
  if (this._closed) return
  if (!num) num = 1

  // TODO: keep track of missing peers for each `findPeers` call separately!
  this.missingPeers += num

  // Start querying queue
  this.queryQueue()

  // If we are connected to no nodes after timeout period, then retry with
  // the bootstrap nodes.
  setTimeout(function () {
    if (this.getNodesNum() === 0) {
      debug('No DHT nodes replied, retry with bootstrap nodes')
      this.queue.push.apply(this.queue, BOOTSTRAP_NODES)
      this.missingPeers = 0
      this.findPeers(num)
    }
  }.bind(this), BOOTSTRAP_TIMEOUT)
}

DHT.prototype.listen = function (port, onlistening) {
  if (typeof port === 'function') {
    onlistening = port
    port = undefined
  }

  if (this._closed || this.listening) {
    return
  }

  if (onlistening)
    this.once('listening', onlistening)

  var onPort = function (err, port) {
    if (err)
      return this.emit('error', err)
    this.port = port
  }.bind(this)

  if (port)
    onPort(null, port)
  else
    portfinder.getPort(onPort)

  this.socket.bind(port)
}

DHT.prototype._onListening = function () {
  this.listening = true
  this.emit('listening', this.port)
}

/**
 * Called when client finds a new DHT node
 * @param  {string} addr
 */
DHT.prototype._handleNode = function (addr) {
  if (this.nodes[addr]) {
    return
  }

  // TODO: Something like this might be needed for safety. (?)
  //if (this.queue.length < 10000) this.queue.push(addr)
  this.queue.push(addr)
  this.queryQueue()

  this.emit('node', addr, this.infoHash.toString('hex'))
}

/**
 * Called when client finds a new peer
 * @param  {string} addr
 */
DHT.prototype._handlePeer = function (addr) {
  if (this.peers[addr]) return
  this.peers[addr] = true
  this.missingPeers = Math.max(0, this.missingPeers - 1)

  this.emit('peer', addr, this.infoHash.toString('hex'))
}

/**
 * Called when someone sends us a UDP message
 * @param {Buffer} data
 * @param {Object} rinfo
 */
DHT.prototype._onData = function (data, rinfo) {
  var addr = rinfo.address + ':' + rinfo.port

  var message
  try {
    debug('Got DHT response from ' + addr)
    message = bncode.decode(data)
    if (!message) throw new Error('message is undefined')
  } catch (err) {
    debug('Failed to decode DHT data from node ' + addr + ' ' + err.message)
    return
  }

  if (!message.t || (message.t.toString() !== this.requestId.toString())) {
    debug('DHT received wrong message requestId: ', message.t && message.t.toString(), this.requestId && this.requestId.toString(), addr)
    return
  }

  if (!this.nodes[addr]) {
    // If this is a new peer, then invalidate the cache (will be recalculated lazily)
    // and update the counter.
    this.nodesCounter++
    this.nodesList = null

    // Mark that we've seen this node (the one we received data from)
    this.nodes[addr] = true
  }

  // Reset outstanding req count to 0 (better than using "delete" which invalidates
  // the V8 inline cache
  this.reqs[addr] = 0

  var r = message && message.r

  if (r && Buffer.isBuffer(r.nodes)) {
    parseNodeInfo(r.nodes).forEach(this._handleNode.bind(this))
  }
  if (r && Array.isArray(r.values)) {
    parsePeerInfo(r.values).forEach(this._handlePeer.bind(this))
  }
}
<commit_msg>allow overriding bootstrap array<commit_after>// TODO:
// - Use the same DHT object for looking up multiple torrents
// - Persist the routing table for later bootstrapping
// - Use actual DHT data structure with "buckets" (follow spec)
// - Add the method that allows us to list ourselves in the DHT
// - https://github.com/czzarr/node-bitwise-xor

module.exports = DHT

var bncode = require('bncode')
var compact2string = require('compact2string')
var debug = require('debug')('bittorrent-dht')
var dgram = require('dgram')
var EventEmitter = require('events').EventEmitter
var hat = require('hat')
var inherits = require('inherits')
var portfinder = require('portfinder')

// Use random port above 1024
portfinder.basePort = Math.floor(Math.random() * 60000) + 1025

var BOOTSTRAP_NODES = [
  'router.bittorrent.com:6881',
  'router.utorrent.com:6881',
  'dht.transmissionbt.com:6881'
]
var BOOTSTRAP_TIMEOUT = 5000
var MAX_NODES = 5000
var MAX_QUERY_PER_SECOND = 200
var MAX_REQUESTS = 3
var QUEUE_QUERY_INTERVAL = Math.floor(1000 / MAX_QUERY_PER_SECOND)
var REQ_TIMEOUT = 2000

function parseNodeInfo (compact) {
  try {
    var nodes = []
    for (var i = 0; i < compact.length; i += 26) {
      nodes.push(compact2string(compact.slice(i + 20, i + 26)))
    }
    return nodes
  } catch (err) {
    debug('Invalid node info ' + compact)
    return []
  }
}

function parsePeerInfo (list) {
  try {
    return list.map(compact2string)
  } catch (err) {
    debug('Invalid peer info ' + list)
    return []
  }
}

inherits(DHT, EventEmitter)

/**
 * Create a new DHT
 * @param {string|Buffer} opts
 */
function DHT (opts) {
  if (!(this instanceof DHT)) return new DHT(opts)
  EventEmitter.call(this)

  if (!opts) opts = {}
  if (!opts.nodeId) opts.nodeId = hat(160)

  this.nodeId = typeof opts.nodeId === 'string'
    ? new Buffer(opts.nodeId, 'hex')
    : opts.nodeId

  this.nodes = {}
  this.nodesCounter = 0
  this.nodesList = null // list cache
  this.peers = {}
  this.reqs = {}

  if (opts.bootstrap === false) {
    this.queue = []
  } else if (Array.isArray(opts.bootstrap)) {
    this.queue = [].concat(opts.bootstrap)
  } else {
    this.queue = [].concat(BOOTSTRAP_NODES)
  }

  this.listening = false
  this._closed = false

  this.port = 0
  this.requestId = 1
  this.pendingRequests = {}
  // Number of peers we still need to find to satisfy the last call to findPeers
  this.missingPeers = 0

  this.pendingRequests[this.requestId] = 1

  this.socket = dgram.createSocket('udp4')
  this.socket.on('message', this._onData.bind(this))
  this.socket.on('listening', this._onListening.bind(this))
  this.socket.on('error', function () {}) // throw away errors
}

DHT.prototype.close = function () {
  this.listening = false
  this._closed = true
  try {
    this.socket.close()
  } catch (err) {
    // ignore error, socket was either already closed / not yet bound
  }
}

// TODO: support setting multiple infohashes
DHT.prototype.setInfoHash = function (infoHash) {
  this.infoHash = typeof infoHash === 'string'
    ? new Buffer(infoHash, 'hex')
    : infoHash

  this.message = {
    t: this.requestId.toString(),
    y: 'q',
    q: 'get_peers',
    a: {
      id: this.nodeId,
      info_hash: this.infoHash
    }
  }
  this.message = bncode.encode(this.message)
}

// addr:string -> [host:string, port:number]
var addrDataCache = {}

/**
 * Given an address/port string, return an array where arr[0] is host, and arr[1] is port
 * (as a Number). This uses a cache, so we don't need to keep creating new arrays. This
 * helps out the GC.
 * @param  {string} addr
 * @return {Array.<*>}
 */
function getAddrData (addr) {
  if (!addrDataCache[addr]) {
    var array = addr.split(':')
    array[1] = Number(array[1])
    addrDataCache[addr] = array
  }
  return addrDataCache[addr]
}

/**
 * Get the number of nodes in the DHT. This uses a simple counter to avoid repeatedly
 * creating new arrays with Object.keys.
 * @return {number}
 */
DHT.prototype.getNodesNum = function () {
  return this.nodesCounter
}

/**
 * Get a list of all nodes in the DHT. Uses a cached list when possible.
 * @return {Array.<string>}
 */
DHT.prototype.getNodesList = function () {
  if (!this.nodesList) {
    this.nodesList = Object.keys(this.nodes)
  }
  return this.nodesList
}

DHT.prototype.query = function (addr) {
  var numNodes = this.getNodesNum()
  if (numNodes > MAX_NODES || this.missingPeers <= 0 || this._closed) return

  var host = getAddrData(addr)[0]
  var port = getAddrData(addr)[1]
  if (!(port > 0 && port < 65535)) return
  this.socket.send(this.message, 0, this.message.length, port, host, function () {
    setTimeout(function () {
      this.reqs[addr] = (this.reqs[addr] || 0) + 1
      if (!this.nodes[addr] && this.reqs[addr] < MAX_REQUESTS) {
        this.query.call(this, addr)
      }
    }.bind(this), REQ_TIMEOUT)
  }.bind(this))
}

DHT.prototype._queryQueue = function () {
  if (this.queue.length) {
    this.query(this.queue.pop())
  } else {
    clearInterval(this.queueInterval)
    this.queueInterval = null
  }
}

/* Start querying queue, if not already */
DHT.prototype.queryQueue = function () {
  if (!this.queryInterval) {
    this.queryInterval = setInterval(this._queryQueue.bind(this), QUEUE_QUERY_INTERVAL)
    this.queryInterval.unref()
  }
}

DHT.prototype.findPeers = function (num) {
  if (this._closed) return
  if (!num) num = 1

  // TODO: keep track of missing peers for each `findPeers` call separately!
  this.missingPeers += num

  // Start querying queue
  this.queryQueue()

  // If we are connected to no nodes after timeout period, then retry with
  // the bootstrap nodes.
  setTimeout(function () {
    if (this.getNodesNum() === 0) {
      debug('No DHT nodes replied, retry with bootstrap nodes')
      this.queue.push.apply(this.queue, BOOTSTRAP_NODES)
      this.missingPeers = 0
      this.findPeers(num)
    }
  }.bind(this), BOOTSTRAP_TIMEOUT)
}

DHT.prototype.listen = function (port, onlistening) {
  if (typeof port === 'function') {
    onlistening = port
    port = undefined
  }

  if (this._closed || this.listening) {
    return
  }

  if (onlistening)
    this.once('listening', onlistening)

  var onPort = function (err, port) {
    if (err)
      return this.emit('error', err)
    this.port = port
  }.bind(this)

  if (port)
    onPort(null, port)
  else
    portfinder.getPort(onPort)

  this.socket.bind(port)
}

DHT.prototype._onListening = function () {
  this.listening = true
  this.emit('listening', this.port)
}

/**
 * Called when client finds a new DHT node
 * @param  {string} addr
 */
DHT.prototype._handleNode = function (addr) {
  if (this.nodes[addr]) {
    return
  }

  // TODO: Something like this might be needed for safety. (?)
  //if (this.queue.length < 10000) this.queue.push(addr)
  this.queue.push(addr)
  this.queryQueue()

  this.emit('node', addr, this.infoHash.toString('hex'))
}

/**
 * Called when client finds a new peer
 * @param  {string} addr
 */
DHT.prototype._handlePeer = function (addr) {
  if (this.peers[addr]) return
  this.peers[addr] = true
  this.missingPeers = Math.max(0, this.missingPeers - 1)

  this.emit('peer', addr, this.infoHash.toString('hex'))
}

/**
 * Called when someone sends us a UDP message
 * @param {Buffer} data
 * @param {Object} rinfo
 */
DHT.prototype._onData = function (data, rinfo) {
  var addr = rinfo.address + ':' + rinfo.port

  var message
  try {
    debug('Got DHT response from ' + addr)
    message = bncode.decode(data)
    if (!message) throw new Error('message is undefined')
  } catch (err) {
    debug('Failed to decode DHT data from node ' + addr + ' ' + err.message)
    return
  }

  if (!message.t || (message.t.toString() !== this.requestId.toString())) {
    debug('DHT received wrong message requestId: ', message.t && message.t.toString(), this.requestId && this.requestId.toString(), addr)
    return
  }

  if (!this.nodes[addr]) {
    // If this is a new peer, then invalidate the cache (will be recalculated lazily)
    // and update the counter.
    this.nodesCounter++
    this.nodesList = null

    // Mark that we've seen this node (the one we received data from)
    this.nodes[addr] = true
  }

  // Reset outstanding req count to 0 (better than using "delete" which invalidates
  // the V8 inline cache
  this.reqs[addr] = 0

  var r = message && message.r

  if (r && Buffer.isBuffer(r.nodes)) {
    parseNodeInfo(r.nodes).forEach(this._handleNode.bind(this))
  }
  if (r && Array.isArray(r.values)) {
    parsePeerInfo(r.values).forEach(this._handlePeer.bind(this))
  }
}
<|endoftext|>


<commit_before>'use strict'

const self = require('sdk/self')
const cm = require('sdk/context-menu')
const sp = require('sdk/simple-prefs')
const ps = require('sdk/preferences/service')
const tabs = require('sdk/tabs')
const { debounce } = require('sdk/lang/functional')
const { translate, translateUrl } = require('./providers/google-translate')
const { getMostRecentBrowserWindow } = require('sdk/window/utils')
const addonUnload = require('sdk/system/unload')
const windows = require('sdk/windows').browserWindows
const { viewFor } = require('sdk/view/core')
const Request = require('sdk/request').Request

// Context Menu
const LABEL_LOADING = 'Fetching translation…'
const LABEL_TRANSLATE = 'Translate “{0}”'
const LABEL_CHANGE_LANGUAGES = 'Change Languages ({0} > {1})'

// Get the available languages
const getLanguages = () => {
  return new Promise((resolve, reject) => {
    Request({
      url: self.data.url('languages.json'),
      onComplete: response => resolve(response.json),
    }).get()
  })
}

// Replace params in a string à la Python str.format()
const format = str => Array.from(arguments).slice(1).reduce(
  (str, arg, i) => str.replace(new RegExp(`\\{${i}\\}`, 'g'), arg), str
)

// Get the From language from the preferences
const currentFrom = languages => {
  const langCode = sp.prefs.lang_from
  const lang = languages[langCode]
  return {
    code: langCode,
    name: (lang && (lang.fromName || lang.name)) || null,
  }
}

// Get the To language from the preferences
const currentTo = languages => {
  let langCode = sp.prefs.lang_to
  if (langCode === 'auto') {
    langCode = ps.getLocalized('general.useragent.locale', 'en')
    if (!langCode.startsWith('zh')) {
      langCode = langCode.replace(/-[a-zA-Z]+$/, '')
    }
  }
  const lang = languages[langCode]
  return {
    code: langCode,
    name: (lang && lang.name) || null,
  }
}

// Utility function to create elements
const eltCreator = doc => (name, props, attrs, parent) => {
  const elt = doc.createElement(name)
  if (props) Object.keys(props).forEach(p => elt[p] = props[p])
  if (attrs) Object.keys(attrs).forEach(a => elt.setAttribute(a, attrs[a]))
  if (parent) parent.appendChild(elt)
  return elt
}

const langToItems = (languages, doc) => (
  Object.keys(languages)
    .filter(lang => !languages[lang].onlyFrom)
    .map(lang => {
      const item = doc.createElement('menuitem')
      item.setAttribute('label', languages[lang].name)
      item.setAttribute('data-gtranslate-to', lang)
      return item
    })
)

const langFromMenus = (languages, doc) => {
  const toItemsPopup = doc.createElement('menupopup')
  langToItems(languages, doc).forEach(item => toItemsPopup.appendChild(item))
  return Object.keys(languages)
    .filter(lang => !languages[lang].onlyTo)
    .map(lang => {
      const menu = doc.createElement('menu')
      menu.setAttribute('label', languages[lang].fromName || languages[lang].name)
      menu.setAttribute('data-gtranslate-from', lang)
      menu.appendChild(toItemsPopup.cloneNode(true))
      return menu
    })
}

// Returns the current selection based on the active node
const getSelectionFromNode = node => {
  const contentWin = node.ownerDocument.defaultView
  const name = node.nodeName.toLowerCase()
  const text = contentWin.getSelection().toString().trim()
  if (text) {
    return text
  }
  if (name === 'input' || name === 'textarea') {
    return node.value.substr(
      node.selectionStart,
      node.selectionEnd-node.selectionStart
    ) || null
  }
  if (name === 'a') {
    return node.textContent || node.title || null
  }
  if (name === 'img') {
    return node.alt || node.title || null
  }
  return null
}

// Returns the current selection from a window
const getSelectionFromWin = win => {
  const popupNode = win.gContextMenuContentData.popupNode
  return (popupNode && getSelectionFromNode(popupNode)) || ''
}

// Add a gtranslate menu on a window
const initMenu = (win, languages) => {

  const doc = win.document
  const cmNode = doc.getElementById('contentAreaContextMenu')
  const elt = eltCreator(doc)

  const translateMenu = elt(
    'menu',
    { className: 'menu-iconic', id: 'context-gtranslate' },
    { label: LABEL_TRANSLATE, image: self.data.url('menuitem.svg') }
  )
  const translatePopup = elt('menupopup', null, null, translateMenu)
  const result = elt('menuitem', null, null, translatePopup)
  const resultSeparator = elt('menuseparator', null, null, translatePopup)
  const langMenu = elt('menu', null, null, translatePopup)
  const fromPopup = elt('menupopup', null, null, langMenu)
  const fromMenus = langFromMenus(languages, doc)

  fromMenus.forEach(menu => fromPopup.appendChild(menu))

  const updateResult = translation => {
    result.setAttribute('tooltiptext', translation || '')
    result.setAttribute('label', translation || LABEL_LOADING)
  }

  // Update the menu when the preferences are updated
  sp.on('', () => {
    updateLangMenuLabel()
    updateLangMenuChecks()
  })

  // Update the languages menu label (“Change Languages […]”)
  const updateLangMenuLabel = detected => {
    const from = detected? `${languages[detected].name} - detected` : currentFrom(languages).name
    const to = currentTo(languages).name
    langMenu.setAttribute('label', format(LABEL_CHANGE_LANGUAGES, from, to))
  }

  const updateLangMenuChecks = () => {

    // Uncheck
    const checkedElts = fromPopup.querySelectorAll('[checked]')
    for (let elt of checkedElts) elt.removeAttribute('checked')

    // Check
    const from = currentFrom(languages).code
    const to = currentTo(languages).code
    const fromMenu = fromPopup.querySelector(`[data-gtranslate-from="${from}"]`)
    const toItem = fromMenu && fromMenu.querySelector(`[data-gtranslate-to="${to}"]`)
    if (fromMenu && toItem) {
      fromMenu.setAttribute('checked', true)
      toItem.setAttribute('checked', true)
    }
  }

  // Show the context menupopup
  const showContextMenu = event => {
    const selection = getSelectionFromWin(win)

    translateMenu.setAttribute('hidden', !selection)
    if (!selection) return

    translateMenu.setAttribute('label', format(LABEL_TRANSLATE,
      selection.length > 15? selection.substr(0, 15) + '…' : selection
    ))

    updateResult(null)
    updateLangMenuLabel()
  }

  // Show the results menupopup
  const showResultsMenu = event => {
    const selection = getSelectionFromWin(win)

    translate(currentFrom(languages).code, currentTo(languages).code, selection, res => {
      updateResult(res.translation)
      if (sp.prefs.lang_from === 'auto') {
        updateLangMenuLabel(res.detectedSource)
      }
    })
  }

  // Listen to popupshowing events
  const onPopupshowing = event => {
    if (event.target === cmNode) {
      return showContextMenu(event)
    }
    if (event.target === translatePopup) {
      return showResultsMenu(event)
    }
  }

  // Listen to command events
  const onContextCommand = event => {
    const target = event.target
    const parent = target.parentNode && target.parentNode.parentNode
    const selection = getSelectionFromWin(win)

    // Open the translation page
    if (target === result) {
      tabs.open(translateUrl(currentFrom(languages).code, currentTo(languages).code, selection))
      return
    }

    // Language change
    if (target.hasAttribute('data-gtranslate-to') &&
        parent && parent.hasAttribute('data-gtranslate-from')) {
      sp.prefs.lang_to = target.getAttribute('data-gtranslate-to')
      sp.prefs.lang_from = parent.getAttribute('data-gtranslate-from')
    }
  }

  cmNode.insertBefore(translateMenu, doc.getElementById('inspect-separator'))
  cmNode.addEventListener('popupshowing', onPopupshowing)
  cmNode.addEventListener('command', onContextCommand)

  updateLangMenuChecks()

  return function destroy() {
    cmNode.removeEventListener('popupshowing', onPopupshowing)
    cmNode.removeEventListener('command', onContextCommand)
    cmNode.removeChild(translateMenu)
  }
}

// Init the addon
getLanguages().then(languages => {

  const destroyFns = []
  const initWin = sdkWin => {
    const destroy = initMenu(viewFor(sdkWin), languages)
    if (destroy) destroyFns.push(destroy)
  }

  // Init an instance when a new window is opened
  windows.on('open', initWin)

  // Init new instances on startup
  Array.from(windows).forEach(initWin)

  // When the addon is unloaded, destroy all gtranslate instances
  addonUnload.when(() => destroyFns.forEach(fn => fn()))
})
<commit_msg>The arguments object is not available for arrow functions since Fx 44<commit_after>'use strict'

const self = require('sdk/self')
const cm = require('sdk/context-menu')
const sp = require('sdk/simple-prefs')
const ps = require('sdk/preferences/service')
const tabs = require('sdk/tabs')
const { debounce } = require('sdk/lang/functional')
const { translate, translateUrl } = require('./providers/google-translate')
const { getMostRecentBrowserWindow } = require('sdk/window/utils')
const addonUnload = require('sdk/system/unload')
const windows = require('sdk/windows').browserWindows
const { viewFor } = require('sdk/view/core')
const Request = require('sdk/request').Request

// Context Menu
const LABEL_LOADING = 'Fetching translation…'
const LABEL_TRANSLATE = 'Translate “{0}”'
const LABEL_CHANGE_LANGUAGES = 'Change Languages ({0} > {1})'

// Get the available languages
const getLanguages = () => {
  return new Promise((resolve, reject) => {
    Request({
      url: self.data.url('languages.json'),
      onComplete: response => resolve(response.json),
    }).get()
  })
}

// Replace params in a string à la Python str.format()
const format = (str, ...args) => Array.from(args).reduce(
  (str, arg, i) => str.replace(new RegExp(`\\{${i}\\}`, 'g'), arg), str
)

// Get the From language from the preferences
const currentFrom = languages => {
  const langCode = sp.prefs.lang_from
  const lang = languages[langCode]
  return {
    code: langCode,
    name: (lang && (lang.fromName || lang.name)) || null,
  }
}

// Get the To language from the preferences
const currentTo = languages => {
  let langCode = sp.prefs.lang_to
  if (langCode === 'auto') {
    langCode = ps.getLocalized('general.useragent.locale', 'en')
    if (!langCode.startsWith('zh')) {
      langCode = langCode.replace(/-[a-zA-Z]+$/, '')
    }
  }
  const lang = languages[langCode]
  return {
    code: langCode,
    name: (lang && lang.name) || null,
  }
}

// Utility function to create elements
const eltCreator = doc => (name, props, attrs, parent) => {
  const elt = doc.createElement(name)
  if (props) Object.keys(props).forEach(p => elt[p] = props[p])
  if (attrs) Object.keys(attrs).forEach(a => elt.setAttribute(a, attrs[a]))
  if (parent) parent.appendChild(elt)
  return elt
}

const langToItems = (languages, doc) => (
  Object.keys(languages)
    .filter(lang => !languages[lang].onlyFrom)
    .map(lang => {
      const item = doc.createElement('menuitem')
      item.setAttribute('label', languages[lang].name)
      item.setAttribute('data-gtranslate-to', lang)
      return item
    })
)

const langFromMenus = (languages, doc) => {
  const toItemsPopup = doc.createElement('menupopup')
  langToItems(languages, doc).forEach(item => toItemsPopup.appendChild(item))
  return Object.keys(languages)
    .filter(lang => !languages[lang].onlyTo)
    .map(lang => {
      const menu = doc.createElement('menu')
      menu.setAttribute('label', languages[lang].fromName || languages[lang].name)
      menu.setAttribute('data-gtranslate-from', lang)
      menu.appendChild(toItemsPopup.cloneNode(true))
      return menu
    })
}

// Returns the current selection based on the active node
const getSelectionFromNode = node => {
  const contentWin = node.ownerDocument.defaultView
  const name = node.nodeName.toLowerCase()
  const text = contentWin.getSelection().toString().trim()
  if (text) {
    return text
  }
  if (name === 'input' || name === 'textarea') {
    return node.value.substr(
      node.selectionStart,
      node.selectionEnd-node.selectionStart
    ) || null
  }
  if (name === 'a') {
    return node.textContent || node.title || null
  }
  if (name === 'img') {
    return node.alt || node.title || null
  }
  return null
}

// Returns the current selection from a window
const getSelectionFromWin = win => {
  const popupNode = win.gContextMenuContentData.popupNode
  return (popupNode && getSelectionFromNode(popupNode)) || ''
}

// Add a gtranslate menu on a window
const initMenu = (win, languages) => {

  const doc = win.document
  const cmNode = doc.getElementById('contentAreaContextMenu')
  const elt = eltCreator(doc)

  const translateMenu = elt(
    'menu',
    { className: 'menu-iconic', id: 'context-gtranslate' },
    { label: LABEL_TRANSLATE, image: self.data.url('menuitem.svg') }
  )
  const translatePopup = elt('menupopup', null, null, translateMenu)
  const result = elt('menuitem', null, null, translatePopup)
  const resultSeparator = elt('menuseparator', null, null, translatePopup)
  const langMenu = elt('menu', null, null, translatePopup)
  const fromPopup = elt('menupopup', null, null, langMenu)
  const fromMenus = langFromMenus(languages, doc)

  fromMenus.forEach(menu => fromPopup.appendChild(menu))

  const updateResult = translation => {
    result.setAttribute('tooltiptext', translation || '')
    result.setAttribute('label', translation || LABEL_LOADING)
  }

  // Update the menu when the preferences are updated
  sp.on('', () => {
    updateLangMenuLabel()
    updateLangMenuChecks()
  })

  // Update the languages menu label (“Change Languages […]”)
  const updateLangMenuLabel = detected => {
    const from = detected? `${languages[detected].name} - detected` : currentFrom(languages).name
    const to = currentTo(languages).name
    langMenu.setAttribute('label', format(LABEL_CHANGE_LANGUAGES, from, to))
  }

  const updateLangMenuChecks = () => {

    // Uncheck
    const checkedElts = fromPopup.querySelectorAll('[checked]')
    for (let elt of checkedElts) elt.removeAttribute('checked')

    // Check
    const from = currentFrom(languages).code
    const to = currentTo(languages).code
    const fromMenu = fromPopup.querySelector(`[data-gtranslate-from="${from}"]`)
    const toItem = fromMenu && fromMenu.querySelector(`[data-gtranslate-to="${to}"]`)
    if (fromMenu && toItem) {
      fromMenu.setAttribute('checked', true)
      toItem.setAttribute('checked', true)
    }
  }

  // Show the context menupopup
  const showContextMenu = event => {
    const selection = getSelectionFromWin(win)

    translateMenu.setAttribute('hidden', !selection)
    if (!selection) return

    translateMenu.setAttribute('label', format(LABEL_TRANSLATE,
      selection.length > 15? selection.substr(0, 15) + '…' : selection
    ))

    updateResult(null)
    updateLangMenuLabel()
  }

  // Show the results menupopup
  const showResultsMenu = event => {
    const selection = getSelectionFromWin(win)

    translate(currentFrom(languages).code, currentTo(languages).code, selection, res => {
      updateResult(res.translation)
      if (sp.prefs.lang_from === 'auto') {
        updateLangMenuLabel(res.detectedSource)
      }
    })
  }

  // Listen to popupshowing events
  const onPopupshowing = event => {
    if (event.target === cmNode) {
      return showContextMenu(event)
    }
    if (event.target === translatePopup) {
      return showResultsMenu(event)
    }
  }

  // Listen to command events
  const onContextCommand = event => {
    const target = event.target
    const parent = target.parentNode && target.parentNode.parentNode
    const selection = getSelectionFromWin(win)

    // Open the translation page
    if (target === result) {
      tabs.open(translateUrl(currentFrom(languages).code, currentTo(languages).code, selection))
      return
    }

    // Language change
    if (target.hasAttribute('data-gtranslate-to') &&
        parent && parent.hasAttribute('data-gtranslate-from')) {
      sp.prefs.lang_to = target.getAttribute('data-gtranslate-to')
      sp.prefs.lang_from = parent.getAttribute('data-gtranslate-from')
    }
  }

  cmNode.insertBefore(translateMenu, doc.getElementById('inspect-separator'))
  cmNode.addEventListener('popupshowing', onPopupshowing)
  cmNode.addEventListener('command', onContextCommand)

  updateLangMenuChecks()

  return function destroy() {
    cmNode.removeEventListener('popupshowing', onPopupshowing)
    cmNode.removeEventListener('command', onContextCommand)
    cmNode.removeChild(translateMenu)
  }
}

// Init the addon
getLanguages().then(languages => {

  const destroyFns = []
  const initWin = sdkWin => {
    const destroy = initMenu(viewFor(sdkWin), languages)
    if (destroy) destroyFns.push(destroy)
  }

  // Init an instance when a new window is opened
  windows.on('open', initWin)

  // Init new instances on startup
  Array.from(windows).forEach(initWin)

  // When the addon is unloaded, destroy all gtranslate instances
  addonUnload.when(() => destroyFns.forEach(fn => fn()))
})
<|endoftext|>


<commit_before>var creds = require(process.env.HOME + '/.googlePlayCreds.json');
var PlayMusic = require('playmusic');
var mkdirp = require('mkdirp');
var https = require('https');
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
var stream = require('stream');

var config, player, logger;

var gmusicBackend = {};
gmusicBackend.name = 'gmusic';

// TODO: seeking
var encodeSong = function(origStream, seek, songID, progCallback, errCallback) {
    var incompletePath = config.songCachePath + '/gmusic/incomplete/' + songID + '.opus';
    var incompleteStream = fs.createWriteStream(incompletePath, {flags: 'w'});
    var encodedPath = config.songCachePath + '/gmusic/' + songID + '.opus';

    var command = ffmpeg(origStream)
        .noVideo()
        //.inputFormat('mp3')
        //.inputOption('-ac 2')
        .audioCodec('libopus')
        .audioBitrate('192')
        .format('opus')
        .on('error', function(err) {
            logger.error('error while transcoding ' + songID + ': ' + err);
            if(fs.existsSync(incompletePath))
                fs.unlinkSync(incompletePath);
            errCallback(err);
        })

    var opusStream = command.pipe(null, {end: true});
    opusStream.on('data', function(chunk) {
        incompleteStream.write(chunk, undefined, function() {
            progCallback(chunk.length, false);
        });
    });
    opusStream.on('end', function() {
        incompleteStream.end(undefined, undefined, function() {
            logger.verbose('transcoding ended for ' + songID);

            // TODO: we don't know if transcoding ended successfully or not,
            // and there might be a race condition between errCallback deleting
            // the file and us trying to move it to the songCache

            // atomically move result to encodedPath
            if(fs.existsSync(incompletePath)) {
                fs.renameSync(incompletePath, encodedPath);
                progCallback(0, true);
            } else {
                progCallback(0, false);
            }
        });
    });

    logger.verbose('transcoding ' + songID + '...');
    return function(err) {
        command.kill();
        logger.verbose('canceled preparing: ' + songID + ': ' + err);
        if(fs.existsSync(incompletePath))
            fs.unlinkSync(incompletePath);
        errCallback('canceled preparing: ' + songID + ': ' + err);
    };
};

var gmusicDownload = function(songID, progCallback, errCallback) {
    var req = null;
    var gmusicStream = new stream.PassThrough();

    var doDownload = function(streamUrl) {
        if(streamUrl) {
            logger.debug('downloading song ' + songID);

            req = https.request(streamUrl, function(res) {
                res.pipe(gmusicStream, {end: false});

                res.on('end', function() {
                    if(res.statusCode === 302) { // redirect
                        logger.debug('redirected. retrying with new URL');
                        res.unpipe();
                        doDownload(res.headers.location);
                    } else if (res.statusCode === 200) {
                        logger.debug('download finished');
                        gmusicStream.end();
                    } else {
                        gmusicStream.end();
                        logger.error('unknown status code ' + res.statusCode);
                        if(errCallback)
                            errCallback('unknown status code ' + res.statusCode);
                    }
                });
            });
            req.on('error', function(e) {
                logger.error(e + ' while fetching! reconnecting in 5s...');
                setTimeout(function() {
                    gmusicBackend.init(function() {
                        logger.error('error while fetching! now reconnected to gmusic');
                        gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                            doDownload(streamUrl);
                        }, function(err) {
                            errCallback(err);
                        });
                    });
                }, 5000);
            });
            req.end();
        } else {
            gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                doDownload(streamUrl);
            }, function(err) {
                errCallback(err);
            });
        }
    };

    /*
    return function(err) {
        // TODO: this doesn't seem to work very well...
        if(command)
            command.kill();
        if(req)
            req.abort();

        if(fs.existsSync(filePath))
            fs.unlinkSync(filePath);
        if(fs.existsSync(incompleteFilePath))
            fs.unlinkSync(incompleteFilePath);

        logger.verbose('canceled preparing: ' + songID + ': ' + err);

        errCallback();
    };
    */

    doDownload();

    var cancelEncoding = encodeSong(gmusicStream, 0, songID, progCallback, errCallback);
    return function(err) {
        if(req)
            req.abort();
        cancelEncoding(err);
    };
};

// cache songID to disk.
// on success: progCallback must be called with true as argument
// on failure: errCallback must be called with error message
// returns a function that cancels preparing
gmusicBackend.prepareSong = function(songID, progCallback, errCallback) {
    var filePath = config.songCachePath + '/gmusic/' + songID + '.opus';

    if(fs.existsSync(filePath)) {
        // true as first argument because there is song data
        progCallback(true, true);
    } else {
        return gmusicDownload(songID, progCallback, errCallback);
    }
};

gmusicBackend.isPrepared = function(songID) {
    var filePath = config.songCachePath + '/gmusic/' + songID + '.opus';
    return fs.existsSync(filePath);
};

// search for music from the backend
// on success: callback must be called with a list of song objects
// on failure: errCallback must be called with error message
gmusicBackend.search = function(query, callback, errCallback) {
    gmusicBackend.pm.search(query.terms, config.searchResultCnt, function(data) {
        var songs;
        var results = {};
        results.songs = {};

        if(data.entries) {
            songs = data.entries.filter(function(entry) {
                return entry.type === '1'; // songs only, no albums/artists
            });

            for(var i = 0; i < songs.length; i++) {
                results.songs[songs[i].track.nid] = {
                    artist: songs[i].track.artist,
                    title: songs[i].track.title,
                    album: songs[i].track.album,
                    albumArt: null, // TODO: can we add this?
                    duration: songs[i].track.durationMillis,
                    songID: songs[i].track.nid,
                    score: songs[i].score,
                    backendName: 'gmusic',
                    format: 'opus'
                };
            }
        }

        callback(results);
    }, function(err) {
        errCallback('error while searching gmusic: ' + err);
    });
};

// called when partyplay is started to initialize the backend
// do any necessary initialization here
gmusicBackend.init = function(_player, _logger, callback) {
    player = _player;
    config = _player.config;
    logger = _logger;

    mkdirp(config.songCachePath + '/gmusic/incomplete');

    // initialize google play music backend
    gmusicBackend.pm = new PlayMusic();
    gmusicBackend.pm.init(creds, callback);
};

module.exports = gmusicBackend;
<commit_msg>limit search result cnt to something fairly sane<commit_after>var creds = require(process.env.HOME + '/.googlePlayCreds.json');
var PlayMusic = require('playmusic');
var mkdirp = require('mkdirp');
var https = require('https');
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
var stream = require('stream');

var config, player, logger;

var gmusicBackend = {};
gmusicBackend.name = 'gmusic';

// TODO: seeking
var encodeSong = function(origStream, seek, songID, progCallback, errCallback) {
    var incompletePath = config.songCachePath + '/gmusic/incomplete/' + songID + '.opus';
    var incompleteStream = fs.createWriteStream(incompletePath, {flags: 'w'});
    var encodedPath = config.songCachePath + '/gmusic/' + songID + '.opus';

    var command = ffmpeg(origStream)
        .noVideo()
        //.inputFormat('mp3')
        //.inputOption('-ac 2')
        .audioCodec('libopus')
        .audioBitrate('192')
        .format('opus')
        .on('error', function(err) {
            logger.error('error while transcoding ' + songID + ': ' + err);
            if(fs.existsSync(incompletePath))
                fs.unlinkSync(incompletePath);
            errCallback(err);
        })

    var opusStream = command.pipe(null, {end: true});
    opusStream.on('data', function(chunk) {
        incompleteStream.write(chunk, undefined, function() {
            progCallback(chunk.length, false);
        });
    });
    opusStream.on('end', function() {
        incompleteStream.end(undefined, undefined, function() {
            logger.verbose('transcoding ended for ' + songID);

            // TODO: we don't know if transcoding ended successfully or not,
            // and there might be a race condition between errCallback deleting
            // the file and us trying to move it to the songCache

            // atomically move result to encodedPath
            if(fs.existsSync(incompletePath)) {
                fs.renameSync(incompletePath, encodedPath);
                progCallback(0, true);
            } else {
                progCallback(0, false);
            }
        });
    });

    logger.verbose('transcoding ' + songID + '...');
    return function(err) {
        command.kill();
        logger.verbose('canceled preparing: ' + songID + ': ' + err);
        if(fs.existsSync(incompletePath))
            fs.unlinkSync(incompletePath);
        errCallback('canceled preparing: ' + songID + ': ' + err);
    };
};

var gmusicDownload = function(songID, progCallback, errCallback) {
    var req = null;
    var gmusicStream = new stream.PassThrough();

    var doDownload = function(streamUrl) {
        if(streamUrl) {
            logger.debug('downloading song ' + songID);

            req = https.request(streamUrl, function(res) {
                res.pipe(gmusicStream, {end: false});

                res.on('end', function() {
                    if(res.statusCode === 302) { // redirect
                        logger.debug('redirected. retrying with new URL');
                        res.unpipe();
                        doDownload(res.headers.location);
                    } else if (res.statusCode === 200) {
                        logger.debug('download finished');
                        gmusicStream.end();
                    } else {
                        gmusicStream.end();
                        logger.error('unknown status code ' + res.statusCode);
                        if(errCallback)
                            errCallback('unknown status code ' + res.statusCode);
                    }
                });
            });
            req.on('error', function(e) {
                logger.error(e + ' while fetching! reconnecting in 5s...');
                setTimeout(function() {
                    gmusicBackend.init(function() {
                        logger.error('error while fetching! now reconnected to gmusic');
                        gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                            doDownload(streamUrl);
                        }, function(err) {
                            errCallback(err);
                        });
                    });
                }, 5000);
            });
            req.end();
        } else {
            gmusicBackend.pm.getStreamUrl(songID, function(streamUrl) {
                doDownload(streamUrl);
            }, function(err) {
                errCallback(err);
            });
        }
    };

    /*
    return function(err) {
        // TODO: this doesn't seem to work very well...
        if(command)
            command.kill();
        if(req)
            req.abort();

        if(fs.existsSync(filePath))
            fs.unlinkSync(filePath);
        if(fs.existsSync(incompleteFilePath))
            fs.unlinkSync(incompleteFilePath);

        logger.verbose('canceled preparing: ' + songID + ': ' + err);

        errCallback();
    };
    */

    doDownload();

    var cancelEncoding = encodeSong(gmusicStream, 0, songID, progCallback, errCallback);
    return function(err) {
        if(req)
            req.abort();
        cancelEncoding(err);
    };
};

// cache songID to disk.
// on success: progCallback must be called with true as argument
// on failure: errCallback must be called with error message
// returns a function that cancels preparing
gmusicBackend.prepareSong = function(songID, progCallback, errCallback) {
    var filePath = config.songCachePath + '/gmusic/' + songID + '.opus';

    if(fs.existsSync(filePath)) {
        // true as first argument because there is song data
        progCallback(true, true);
    } else {
        return gmusicDownload(songID, progCallback, errCallback);
    }
};

gmusicBackend.isPrepared = function(songID) {
    var filePath = config.songCachePath + '/gmusic/' + songID + '.opus';
    return fs.existsSync(filePath);
};

// search for music from the backend
// on success: callback must be called with a list of song objects
// on failure: errCallback must be called with error message
gmusicBackend.search = function(query, callback, errCallback) {
    gmusicBackend.pm.search(query.terms, Math.min(100, config.searchResultCnt), function(data) {
        var songs;
        var results = {};
        results.songs = {};

        if(data.entries) {
            songs = data.entries.filter(function(entry) {
                return entry.type === '1'; // songs only, no albums/artists
            });

            for(var i = 0; i < songs.length; i++) {
                results.songs[songs[i].track.nid] = {
                    artist: songs[i].track.artist,
                    title: songs[i].track.title,
                    album: songs[i].track.album,
                    albumArt: null, // TODO: can we add this?
                    duration: songs[i].track.durationMillis,
                    songID: songs[i].track.nid,
                    score: songs[i].score,
                    backendName: 'gmusic',
                    format: 'opus'
                };
            }
        }

        callback(results);
    }, function(err) {
        errCallback('error while searching gmusic: ' + err);
    });
};

// called when partyplay is started to initialize the backend
// do any necessary initialization here
gmusicBackend.init = function(_player, _logger, callback) {
    player = _player;
    config = _player.config;
    logger = _logger;

    mkdirp(config.songCachePath + '/gmusic/incomplete');

    // initialize google play music backend
    gmusicBackend.pm = new PlayMusic();
    gmusicBackend.pm.init(creds, callback);
};

module.exports = gmusicBackend;
<|endoftext|>


<commit_before>var browsersync  = require('browser-sync');
var reload       = browsersync.reload;
var gulp         = require('gulp');
var autoprefixer = require('gulp-autoprefixer');
var filter       = require('gulp-filter');
var header       = require('gulp-header');
var gulpIf       = require('gulp-if');
var rubysass     = require('gulp-ruby-sass');
var postcss      = require('gulp-postcss');
var pixrem       = require('pixrem');
var colorHex     = require('postcss-color-hex');
var opacity      = require('postcss-opacity');
var sourcemaps   = require('gulp-sourcemaps');
var size         = require('gulp-size');
var config       = require('../../../gulpfile').rubysassBasic;
// var config       = require('../test/gulpfile').rubysassBasic; //test

module.exports = function() {

  config.onError = browsersync.notify;
  browsersync.notify(config.notify);

  return rubysass(config.src, config.options )
    .on('error', function (err) {
      console.error('Error', err.message);
    })
    .pipe(autoprefixer(config.autoprefixer))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest(config.dest))
    .pipe(filter(config.filter))
    .pipe(reload({stream: true}))
    .pipe(size({
      gzip: false,
      showFiles: true,
      title:'after rubysass'
    }));

};
<commit_msg>cleanup<commit_after>var browsersync  = require('browser-sync');
var reload       = browsersync.reload;
var gulp         = require('gulp');
var autoprefixer = require('gulp-autoprefixer');
var filter       = require('gulp-filter');
var rubysass     = require('gulp-ruby-sass');
var sourcemaps   = require('gulp-sourcemaps');
var size         = require('gulp-size');
var config       = require('../../../gulpfile').rubysassBasic;
// var config       = require('../test/gulpfile').rubysassBasic; //test

module.exports = function() {

  config.onError = browsersync.notify;
  browsersync.notify(config.notify);

  return rubysass(config.src, config.options )
    .on('error', function (err) {
      console.error('Error', err.message);
    })
    .pipe(autoprefixer(config.autoprefixer))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest(config.dest))
    .pipe(filter(config.filter))
    .pipe(reload({stream: true}))
    .pipe(size({
      gzip: false,
      showFiles: true,
      title:'after rubysass'
    }));

};
<|endoftext|>


<commit_before>var app = require("express")()
var http = require("http").Server(app)
var io = require('socket.io')(http)
var db = {
	users: [],
	bankMoney: 300000
}

app.get("/", function(req, res) {
	res.sendFile(__dirname + '/www/index.html')

})

app.get(/^(.+)$/, function(req, res){
    console.log('static file request : ' + req.params);
    res.sendfile( __dirname + req.params[0])
})

io.on('connection', function(socket){
  console.log('a user connected');
	socket.on('newUser', function (data) {
		db.users.push(data)
		console.log(data.name)
	})
})

setInterval(function () {
	var spin = Math.floor(Math.random() * 2) + 1;
	console.log(spin);
	io.emit('spin', spin)
},20000)

http.listen(3000, function(){
  console.log('listening on *:3000');
})
<commit_msg>started work on giving users bits<commit_after>var app = require("express")()
var http = require("http").Server(app)
var io = require('socket.io')(http)
var db = {
	users: [],
	bankBits: 300000
}

var banker = {
	giveBits: function (amount, name) {
		db.users.forEach(function (user) {
			if (user.name == name){
				user.bits += amount
				db.bankBits -= amount
				console.log('Gave '+amount+'bits to '+user.name)
				console.log(db.bankBits)
				console.log(user.bits)
			}
		})
	}
}

app.get("/", function(req, res) {
	res.sendFile(__dirname + '/www/index.html')

})

app.get(/^(.+)$/, function(req, res){
    console.log('static file request : ' + req.params);
    res.sendfile( __dirname + req.params[0])
})

io.on('connection', function(socket){
  console.log('a user connected');
	socket.on('newUser', function (data) {
		db.users.push(data)
		console.log(data.name)
		banker.giveBits(30, data.name)
	})
})

setInterval(function () {
	var spin = Math.floor(Math.random() * 2) + 1;
	console.log(spin);
	io.emit('spin', spin)
},20000)

http.listen(3000, function(){
  console.log('listening on *:3000');
})
<|endoftext|>


<commit_before>'use strict';

var Duplex = require('stream').Duplex,
    batch = require('gulp-batch'),
    Gaze = require('gaze'),
    fs = require('vinyl-fs'),
    path = require('path'),
    gutil = require('gulp-util'),
    minimatch = require('minimatch'),
    glob2base = require('glob2base');

module.exports = function (opts, cb) {
    if (typeof opts !== 'object') {
        cb = opts;
        opts = { };
    }

    opts.emit = opts.emit || 'one';
    opts.glob = opts.glob || [];
    if (typeof opts.glob === 'string') { opts.glob = [ opts.glob ]; }

    var duplex = new Duplex({ objectMode: true, allowHalfOpen: true });

    if (cb) {
        if (typeof cb !== 'function') { throw new Error('Provided callback is not a function: ' + cb); }
        cb = batch(opts, cb.bind(duplex));
    } else {
        cb = function () { };
    }

    duplex.gaze = new Gaze(opts.glob);
    duplex._write = function _write(file, encoding, done) {
        duplex.gaze.add(file.path, function () {
            if (!opts.silent && opts.verbose) { logEvent('added to watch', file.path, opts); }
            done();
        });
        memorizeProperties(file);
        if (opts.passThrough !== false) { passThrough(file); }
    };
    duplex._read = function _read() { };

    duplex.on('finish', function () {
        if (!opts.silent) { fileCount(duplex.gaze, function (err, count) { logEvent('added from pipe', count, opts); }); }
    });

    duplex.close = function () {
        duplex.gaze.on('end', duplex.emit.bind(duplex, 'end'));
        duplex.gaze.close();
    };

    duplex.gaze.on('error', duplex.emit.bind(duplex, 'error'));
    duplex.gaze.on('ready', duplex.emit.bind(duplex, 'ready'));

    duplex.gaze.on('all', function (event, filepath) {
        if (!opts.silent) { logEvent(event, filepath, opts); }
        var glob = [ filepath ];
        if (opts.emit === 'all') {
            glob = glob.concat(opts.glob ? opts.glob : []);
            glob = glob.concat(duplex.gaze._patterns);
        }
        if (event === 'deleted') { passThrough(voidFile(filepath, event)); }
        fs.src(glob, opts).on('data', function (file) {
            if (file.path === filepath) { file.event = event; }
            passThrough(file);
        });
    });

    if (opts.glob.length && opts.emitOnGlob !== false) {
        fs.src(opts.glob, opts).on('data', passThrough);
    }

    function voidFile(filepath, event) {
        return {
            path: filepath,
            event: event,
            isNull: function () { return true; }
        };
    }

    function passThrough(file) {
        restoreProperties(file, opts);
        cb(file);
        duplex.push(file);
    }

    var pathCache = {};

    function restoreProperties(file) {
        if (pathCache[file.path]) {
            if (file.base) { file.base = pathCache[file.path].base; }
            if (file.cwd) { file.cwd = pathCache[file.path].cwd; }
        } else {
            file.base = opts.base || calculateBase(opts.glob, file) || file.base;
            memorizeProperties(file);
        }
    }

    function memorizeProperties(file) {
        pathCache[file.path] = {
            base: file.base,
            cwd: file.cwd || process.cwd()
        };
    }

    return duplex;
};

function logEvent(event, filepath, opts) {
    var msg = [gutil.colors.magenta(path.basename(filepath)), 'was', event];
    if (opts.name) { msg.unshift(gutil.colors.cyan(opts.name) + ' saw'); }
    gutil.log.apply(gutil, msg);
}

function getWatchedFiles(gaze, cb) {
    gaze.watched(function (err, dirs) {
        if (err) { return cb(err); }
        var files = [];
        Object.keys(dirs).forEach(function (dir) {
            dirs[dir].forEach(function (file) {
                if (file[file.length - 1] !== '/') {
                    files.push(file);
                }
            });
        });
        cb(null, files);
    });
}

function fileCount(gaze, cb) {
    getWatchedFiles(gaze, function (err, files) {
        if (err) { return cb(err); }
        var count = files.length;
        cb(null, count + (count === 1 ? ' file' : ' files'));
    });
}

function calculateBase(globs, file) {
    if (typeof globs === 'string') { globs = [ globs ]; }
    for (var globIdx in globs) {
        if (globs.hasOwnProperty(globIdx)) {
            var p = path.relative(file.cwd || process.cwd(), file.path);
            if (!minimatch(p, globs[globIdx])) { continue; }
            return glob2base({
                minimatch: new minimatch.Minimatch(globs[globIdx])
            });
        }
    }
}

module.exports.calculateBase = calculateBase;
<commit_msg>Move gaze require into exported function<commit_after>'use strict';

var Duplex = require('stream').Duplex,
    batch = require('gulp-batch'),
    fs = require('vinyl-fs'),
    path = require('path'),
    gutil = require('gulp-util'),
    minimatch = require('minimatch'),
    glob2base = require('glob2base');

module.exports = function (opts, cb) {
    var Gaze = require('gaze');

    if (typeof opts !== 'object') {
        cb = opts;
        opts = { };
    }

    opts.emit = opts.emit || 'one';
    opts.glob = opts.glob || [];
    if (typeof opts.glob === 'string') { opts.glob = [ opts.glob ]; }

    var duplex = new Duplex({ objectMode: true, allowHalfOpen: true });

    if (cb) {
        if (typeof cb !== 'function') { throw new Error('Provided callback is not a function: ' + cb); }
        cb = batch(opts, cb.bind(duplex));
    } else {
        cb = function () { };
    }

    duplex.gaze = new Gaze(opts.glob);
    duplex._write = function _write(file, encoding, done) {
        duplex.gaze.add(file.path, function () {
            if (!opts.silent && opts.verbose) { logEvent('added to watch', file.path, opts); }
            done();
        });
        memorizeProperties(file);
        if (opts.passThrough !== false) { passThrough(file); }
    };
    duplex._read = function _read() { };

    duplex.on('finish', function () {
        if (!opts.silent) { fileCount(duplex.gaze, function (err, count) { logEvent('added from pipe', count, opts); }); }
    });

    duplex.close = function () {
        duplex.gaze.on('end', duplex.emit.bind(duplex, 'end'));
        duplex.gaze.close();
    };

    duplex.gaze.on('error', duplex.emit.bind(duplex, 'error'));
    duplex.gaze.on('ready', duplex.emit.bind(duplex, 'ready'));

    duplex.gaze.on('all', function (event, filepath) {
        if (!opts.silent) { logEvent(event, filepath, opts); }
        var glob = [ filepath ];
        if (opts.emit === 'all') {
            glob = glob.concat(opts.glob ? opts.glob : []);
            glob = glob.concat(duplex.gaze._patterns);
        }
        if (event === 'deleted') { passThrough(voidFile(filepath, event)); }
        fs.src(glob, opts).on('data', function (file) {
            if (file.path === filepath) { file.event = event; }
            passThrough(file);
        });
    });

    if (opts.glob.length && opts.emitOnGlob !== false) {
        fs.src(opts.glob, opts).on('data', passThrough);
    }

    function voidFile(filepath, event) {
        return {
            path: filepath,
            event: event,
            isNull: function () { return true; }
        };
    }

    function passThrough(file) {
        restoreProperties(file, opts);
        cb(file);
        duplex.push(file);
    }

    var pathCache = {};

    function restoreProperties(file) {
        if (pathCache[file.path]) {
            if (file.base) { file.base = pathCache[file.path].base; }
            if (file.cwd) { file.cwd = pathCache[file.path].cwd; }
        } else {
            file.base = opts.base || calculateBase(opts.glob, file) || file.base;
            memorizeProperties(file);
        }
    }

    function memorizeProperties(file) {
        pathCache[file.path] = {
            base: file.base,
            cwd: file.cwd || process.cwd()
        };
    }

    return duplex;
};

function logEvent(event, filepath, opts) {
    var msg = [gutil.colors.magenta(path.basename(filepath)), 'was', event];
    if (opts.name) { msg.unshift(gutil.colors.cyan(opts.name) + ' saw'); }
    gutil.log.apply(gutil, msg);
}

function getWatchedFiles(gaze, cb) {
    gaze.watched(function (err, dirs) {
        if (err) { return cb(err); }
        var files = [];
        Object.keys(dirs).forEach(function (dir) {
            dirs[dir].forEach(function (file) {
                if (file[file.length - 1] !== '/') {
                    files.push(file);
                }
            });
        });
        cb(null, files);
    });
}

function fileCount(gaze, cb) {
    getWatchedFiles(gaze, function (err, files) {
        if (err) { return cb(err); }
        var count = files.length;
        cb(null, count + (count === 1 ? ' file' : ' files'));
    });
}

function calculateBase(globs, file) {
    if (typeof globs === 'string') { globs = [ globs ]; }
    for (var globIdx in globs) {
        if (globs.hasOwnProperty(globIdx)) {
            var p = path.relative(file.cwd || process.cwd(), file.path);
            if (!minimatch(p, globs[globIdx])) { continue; }
            return glob2base({
                minimatch: new minimatch.Minimatch(globs[globIdx])
            });
        }
    }
}

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


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

var shell = require('shelljs');
var chalk = require('chalk');
var exec = require('child_process').execSync;
var path = require('path');
var fs = require('fs');
var program = require('commander');

var localPath = path.join(__dirname, '.bin');
localPath = path.join(localPath, 'sequelize-auto');

var converter = function(fileContent, filename, mode){
    var fname = filename;
    if(fname.includes('.js')){
        fname = fname.substring(0, fname.indexOf('.js'));
    }

    var fileLines = fileContent.split('\n');
    var objects = fileLines.slice(4, fileLines.length - 5);

    var file = ""

    var object = objects.join('\n').replace(/DataTypes/g, "Sequelize").replace(/sequelize/g, "db");
    var segments = fname.split('_');
    var modelName = segments.map(function(element){
        return element.replace(element[0], element[0].toUpperCase());
    })
    modelName = modelName.join("") + "Model";
    file = "import Sequelize from 'sequelize'\n";
    file += "import {db} from '../../sequelize_connection.js'\n";
    file += "db.define('"+fname+"', {\n";
    file += object + "\n";
    file += "\t}, {\n";
    file += "\t\ttableName: '"+fname+"'\n";
    file += "\t})\n";
    file += "const "+modelName+" = db.models."+fname+";\n";
    file += "export {"+modelName+"};";

    return file;
}

shell.echo(chalk.green("Generating models, please be patient..."));

program
    .option('-d, --database <database>', 'The name of the database')
    .option('-o, --output <output>', 'The path where the models will be place after generation')
    .option('-h, --host <host>', 'The domain hosting the database')
    .option('-u, --username <username>', 'The username of the database')
    .option('-p, --port <port>', 'The port being used by the database in the domain')
    .option('-x, --password <password>', 'The password of the database')
    .parse(process.argv);

const executableCommand = `${localPath} -o ${program.output} -h ${program.host} -d ${program.database} -u ${program.username} -p ${program.port} -x ${program.password} -e mysql`;
shell.exec(executableCommand);

var modelsPath = path.join(__dirname, program.output);
var modelsDir = fs.readdirSync(modelsPath);

modelsDir.forEach(function(filename){
    var segments = filename.replace('.js', '').split('_');
    var modelName = segments.map(function(element){
        return element.replace(element[0], element[0].toUpperCase())
    });
    modelName = modelName.join('') + 'Model.js';
    fs.renameSync(path.join(modelsPath, filename), path.join(modelsPath, modelName));

    var fileContent = fs.readFileSync(path.join(modelsPath, modelName), 'utf-8');
    fileContent = converter(fileContent, filename, 'd');
    console.log(chalk.yellow(`converting ${filename}...`));

    fs.writeFileSync(path.join(modelsPath, modelName), fileContent, {encoding: 'utf-8'});
});
<commit_msg>Change model directory construction<commit_after>#!/usr/bin/env node

var shell = require('shelljs');
var chalk = require('chalk');
var exec = require('child_process').execSync;
var path = require('path');
var fs = require('fs');
var program = require('commander');

var localPath = path.join(__dirname, '.bin');
localPath = path.join(localPath, 'sequelize-auto');

var converter = function(fileContent, filename, mode){
    var fname = filename;
    if(fname.includes('.js')){
        fname = fname.substring(0, fname.indexOf('.js'));
    }

    var fileLines = fileContent.split('\n');
    var objects = fileLines.slice(4, fileLines.length - 5);

    var file = ""

    var object = objects.join('\n').replace(/DataTypes/g, "Sequelize").replace(/sequelize/g, "db");
    var segments = fname.split('_');
    var modelName = segments.map(function(element){
        return element.replace(element[0], element[0].toUpperCase());
    })
    modelName = modelName.join("") + "Model";
    file = "import Sequelize from 'sequelize'\n";
    file += "import {db} from '../../sequelize_connection.js'\n";
    file += "db.define('"+fname+"', {\n";
    file += object + "\n";
    file += "\t}, {\n";
    file += "\t\ttableName: '"+fname+"'\n";
    file += "\t})\n";
    file += "const "+modelName+" = db.models."+fname+";\n";
    file += "export {"+modelName+"};";

    return file;
}

shell.echo(chalk.green("Generating models, please be patient..."));

program
    .option('-d, --database <database>', 'The name of the database')
    .option('-o, --output <output>', 'The path where the models will be place after generation')
    .option('-h, --host <host>', 'The domain hosting the database')
    .option('-u, --username <username>', 'The username of the database')
    .option('-p, --port <port>', 'The port being used by the database in the domain')
    .option('-x, --password <password>', 'The password of the database')
    .parse(process.argv);

var modelsPath = path.join(process.cwd(), program.output);

const executableCommand = `${localPath} -o ${program.output} -h ${program.host} -d ${program.database} -u ${program.username} -p ${program.port} -x ${program.password} -e mysql`;
shell.exec(executableCommand);

var modelsDir = fs.readdirSync(modelsPath);

modelsDir.forEach(function(filename){
    var segments = filename.replace('.js', '').split('_');
    var modelName = segments.map(function(element){
        return element.replace(element[0], element[0].toUpperCase())
    });
    modelName = modelName.join('') + 'Model.js';
    fs.renameSync(path.join(modelsPath, filename), path.join(modelsPath, modelName));

    var fileContent = fs.readFileSync(path.join(modelsPath, modelName), 'utf-8');
    fileContent = converter(fileContent, filename, 'd');
    console.log(chalk.yellow(`converting ${filename}...`));

    fs.writeFileSync(path.join(modelsPath, modelName), fileContent, {encoding: 'utf-8'});
});
<|endoftext|>


<commit_before>/**
 * A Spot for Slack!
 */

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function (err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am a bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

/**
 * Configure the persistence options
 */

var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_slack_bot_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

/**
 * Are being run as an app or a custom integration? The initialization will differ, depending
 */

if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

var apiai = require('botkit-middleware-apiai')({
   token: process.env.APIAI_TOKEN
});
controller.middleware.receive.use(apiai.receive);

/**
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function (bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function (bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/**
 * Core bot logic goes here!
 */
// BEGIN EDITING HERE!

controller.on('bot_channel_join', function (bot, message) {
    bot.reply(message, "I'm here!")
});

/*controller.hears('hello', ['mention', 'direct_mention','direct_message'], function (bot, message) {
    bot.reply(message, 'Hello!');
});*/

controller.hears(['hello'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'Hello!');
});

controller.hears(['goodbye'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'See you later alligator!');
});

controller.hears(['flights'], 'direct_message', apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   } else {
       bot.reply(message, "You requested to fly to " + message.entities['geo-city'] + " on " + message.entities['date']+".");
   }
});

controller.hears(['calculate'], ['direct_mention', 'mention', 'direct_message'], apiai.hears, function (bot,message) {
  var userID = message.user
  var user = "<@"+userID+">"
  if(message.fulfillment.speech !== '') {
    bot.reply(message, message.fulfillment.speech);
  } else if (message.entities['keyword'] == 'client') {
    var total = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var day_rate = (total / (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client is paying: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nThis is made up of: \nContractor day rate: *$"+day_rate+"* :tada: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead margin: *$"+margin+"* :moneybag:");
  } else if (message.entities['keyword'] == 'perm') {
    var salary = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var pkg = (salary * 1.095).toFixed(2);
    var fee = (pkg * client_rate).toFixed(2);
    bot.reply(message, user+"\nThe candidate package is *$"+pkg+"*. :the_horns: \nLookahead fee: *$"+fee+"* :moneybag:");
  } else {
    var day_rate = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var total = (day_rate * (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client will be charged: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead earn: *$"+margin+"* :moneybag:");
  }
});

controller.hears(['Dog'], ['direct_message','mention', 'direct_mention'], apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   }
});

controller.hears(['help'], ['direct_message','direct_mention','mention'], apiai.hears, function (bot, message) {
   bot.reply(message, 'Here are some things you can ask me: \ncalculate candidate 1000 20: outputs the cost to the client and Lookahead\'s margin \ncalculate client 1300 20: reverses the prior calculation and gives you the day rate and margin \n calculate perm 100000 20: gives you the package total and placement fee \n');
});

/**
 * AN example of what could be:
 * Any un-handled direct mention gets a reaction and a pat response!
 */
//controller.on('direct_message,mention,direct_mention', function (bot, message) {
//    bot.api.reactions.add({
//        timestamp: message.ts,
//        channel: message.channel,
//        name: 'robot_face',
//    }, function (err) {
//        if (err) {
//            console.log(err)
//        }
//        bot.reply(message, 'I heard you loud and clear boss.');
//    });
//});
<commit_msg>Remove API.AI middleware for help command<commit_after>/**
 * A Spot for Slack!
 */

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function (err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am a bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

/**
 * Configure the persistence options
 */

var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_slack_bot_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

/**
 * Are being run as an app or a custom integration? The initialization will differ, depending
 */

if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

var apiai = require('botkit-middleware-apiai')({
   token: process.env.APIAI_TOKEN
});
controller.middleware.receive.use(apiai.receive);

/**
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function (bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function (bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/**
 * Core bot logic goes here!
 */
// BEGIN EDITING HERE!

controller.on('bot_channel_join', function (bot, message) {
    bot.reply(message, "I'm here!")
});

/*controller.hears('hello', ['mention', 'direct_mention','direct_message'], function (bot, message) {
    bot.reply(message, 'Hello!');
});*/

controller.hears(['hello'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'Hello!');
});

controller.hears(['goodbye'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'See you later alligator!');
});

controller.hears(['flights'], 'direct_message', apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   } else {
       bot.reply(message, "You requested to fly to " + message.entities['geo-city'] + " on " + message.entities['date']+".");
   }
});

controller.hears(['calculate'], ['direct_mention', 'mention', 'direct_message'], apiai.hears, function (bot,message) {
  var userID = message.user
  var user = "<@"+userID+">"
  if(message.fulfillment.speech !== '') {
    bot.reply(message, message.fulfillment.speech);
  } else if (message.entities['keyword'] == 'client') {
    var total = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var day_rate = (total / (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client is paying: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nThis is made up of: \nContractor day rate: *$"+day_rate+"* :tada: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead margin: *$"+margin+"* :moneybag:");
  } else if (message.entities['keyword'] == 'perm') {
    var salary = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var pkg = (salary * 1.095).toFixed(2);
    var fee = (pkg * client_rate).toFixed(2);
    bot.reply(message, user+"\nThe candidate package is *$"+pkg+"*. :the_horns: \nLookahead fee: *$"+fee+"* :moneybag:");
  } else {
    var day_rate = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var total = (day_rate * (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client will be charged: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead earn: *$"+margin+"* :moneybag:");
  }
});

controller.hears(['Dog'], ['direct_message','mention', 'direct_mention'], apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   }
});

controller.hears(['help'], ['direct_message','direct_mention','mention'], function (bot, message) {
   bot.reply(message, 'Here are some things you can ask me: \ncalculate candidate 1000 20: outputs the cost to the client and Lookahead\'s margin \ncalculate client 1300 20: reverses the prior calculation and gives you the day rate and margin \n calculate perm 100000 20: gives you the package total and placement fee \n');
});

/**
 * AN example of what could be:
 * Any un-handled direct mention gets a reaction and a pat response!
 */
//controller.on('direct_message,mention,direct_mention', function (bot, message) {
//    bot.api.reactions.add({
//        timestamp: message.ts,
//        channel: message.channel,
//        name: 'robot_face',
//    }, function (err) {
//        if (err) {
//            console.log(err)
//        }
//        bot.reply(message, 'I heard you loud and clear boss.');
//    });
//});
<|endoftext|>


<commit_before>var _ = require('lodash')
  , volume = require('./lib/volume')
  , rooms = require('./lib/rooms')
  , responses = require('./lib/responses')
  , helptext = require('./lib/helptext');

module.exports = function(robot) {
  robot.brain.on('loaded', function() {
    robot.brain.snoozer = robot.brain.snoozer || {};
    volume.init(robot.brain.snoozer);
    rooms.init(robot.brain.snoozer);
  });

  robot.respond(/cinco help/, helptext);

  robot.respond(/cinco (facetime )?(party )?snoozer/i, function(msg) {
    var room = msg.message.room;

    if (rooms.has(room)) {
      rooms.remove(room);
      msg.send('Cinco Facetime Party Snoozer is powered off! You\'ll have to make your own conversation now');
    } else {
      rooms.add(room);
      msg.send('Cinco Facetime Party Snoozer is on! Now get to talking about whatever boring thing you want!');
    }
  });

  robot.respond(/cinco status/i, function(msg) {
    var room = msg.message.room;
    if (rooms.has(room)) {
      msg.send('Cinco Facetime Party Snoozer is on and ready to talk!');
    } else {
      msg.send('Cinco Facetime Party Snoozer is off, maybe you should turn it on so this place feels lively!');
    }
  });

  robot.respond(/cinco louder/i, function(msg) {
    var room = msg.message.room;
    volume.louder(room);
    msg.respond('Volume set to ' + volume.value(room));
  });

  robot.respond(/cinco loudest/i, function(msg) {
    var room = msg.message.room;
    volume.loudest(room);
    msg.respond('Volume set to ' + volume.value(room));
  });

  robot.respond(/cinco quieter/i, function(msg) {
    var room = msg.message.room;
    volume.quieter(room);
    msg.respond('Volume set to ' + volume.value(room));
  });

  robot.respond(/cinco quietest/i, function(msg) {
    var room = msg.message.room;
    volume.quieter(room);
    msg.respond('Volume set to ' + volume.value(room));
  });

  // the magic
  robot.hear(/.*/i, function(msg) {
    var room = msg.message.room;

    if (rooms.has(room) && _.random(volume.min, volume.max) <= volume.value()) {
      msg.send(responses.random());
    }
  });

  robot.respond(/cinco debug/i, function(msg) {
    msg.send(JSON.stringify(robot.brain.snoozer, null, 2));
  });
};
<commit_msg>fixed volume controls<commit_after>var _ = require('lodash')
  , volume = require('./lib/volume')
  , rooms = require('./lib/rooms')
  , responses = require('./lib/responses')
  , helptext = require('./lib/helptext');

module.exports = function(robot) {
  robot.brain.on('loaded', function() {
    robot.brain.snoozer = robot.brain.snoozer || {};
    volume.init(robot.brain.snoozer);
    rooms.init(robot.brain.snoozer);
  });

  robot.respond(/cinco help/, helptext);

  robot.respond(/cinco (facetime )?(party )?snoozer/i, function(msg) {
    var room = msg.message.room;

    if (rooms.has(room)) {
      rooms.remove(room);
      msg.send('Cinco Facetime Party Snoozer is powered off! You\'ll have to make your own conversation now');
    } else {
      rooms.add(room);
      msg.send('Cinco Facetime Party Snoozer is on! Now get to talking about whatever boring thing you want!');
    }
  });

  robot.respond(/cinco status/i, function(msg) {
    var room = msg.message.room;
    if (rooms.has(room)) {
      msg.send('Cinco Facetime Party Snoozer is on and ready to talk!');
    } else {
      msg.send('Cinco Facetime Party Snoozer is off, maybe you should turn it on so this place feels lively!');
    }
  });

  robot.respond(/cinco louder/i, function(msg) {
    var room = msg.message.room;
    volume.louder(room);
    msg.send('Volume set to ' + volume.value(room));
  });

  robot.respond(/cinco loudest/i, function(msg) {
    var room = msg.message.room;
    volume.loudest(room);
    msg.send('Volume set to ' + volume.value(room));
  });

  robot.respond(/cinco quieter/i, function(msg) {
    var room = msg.message.room;
    volume.quieter(room);
    msg.send('Volume set to ' + volume.value(room));
  });

  robot.respond(/cinco quietest/i, function(msg) {
    var room = msg.message.room;
    volume.quieter(room);
    msg.send('Volume set to ' + volume.value(room));
  });

  // the magic
  robot.hear(/.*/i, function(msg) {
    var room = msg.message.room;

    if (rooms.has(room) && _.random(volume.min, volume.max) <= volume.value()) {
      msg.send(responses.random());
    }
  });

  robot.respond(/cinco debug/i, function(msg) {
    msg.send(JSON.stringify(robot.brain.snoozer, null, 2));
  });
};
<|endoftext|>


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

module.exports = function (level, naming) {
    return function (item, add, walk, next) {
        var basename = item.basename,
            filename = item.filename,
            depth = item.depth,
            splited = basename.split('.'),
            dirNotation, dirType;

        if (splited.length === 1) {
            var dirname = item.dirname,
                parent;

            if (depth === 1) {
                dirNotation = naming.parse(basename);
                dirType = naming.typeOf(dirNotation);

                if (dirType === 'block') {
                    return walk(filename, { notation: dirNotation, type: dirType });
                }
            } else if (depth === 2) {
                parent = path.basename(dirname);
                dirNotation = naming.parse(parent + basename);
                dirType = naming.typeOf(dirNotation);

                if (dirType === 'blockMod' || dirType === 'elem') {
                    return walk(filename, { notation: dirNotation, type: dirType });
                }
            } else if (depth === 3) {
                var grandParent = path.basename(path.dirname(dirname));

                parent = path.basename(dirname);
                dirNotation = naming.parse(grandParent + parent + basename);
                dirType = naming.typeOf(dirNotation);

                if (dirType === 'elemMod') {
                    return walk(filename, { notation: dirNotation, type: dirType });
                }
            }
        } else {
            if (depth > 1) {
                var data = item.data,
                    notation = naming.parse(splited.shift());

                dirNotation = data.notation;
                dirType = data.type;

                if (notation &&
                    (depth === 2 && dirNotation.block === notation.block) ||
                    (depth === 3 && dirType === 'elem' && dirNotation.block === notation.block &&
                        dirNotation.elem === notation.elem) ||
                    (depth === 3 && dirType === 'blockMod' && dirNotation.block === notation.block &&
                        dirNotation.modName === notation.modName) ||
                    (depth === 4 && dirNotation.block === notation.block && dirNotation.elem === notation.elem &&
                        dirNotation.modName === notation.modName)
                ) {
                    notation.level = level;
                    notation.tech = splited.join('.');
                    notation.path = filename;

                    add(notation);
                }
            }
        }

        next();
    };
};
<commit_msg>Fix walker<commit_after>var path = require('path');

module.exports = function (level, naming) {
    return function (item, add, walk, next) {
        var basename = item.basename,
            filename = item.filename,
            depth = item.depth,
            splited = basename.split('.'),
            dirNotation, dirType;

        if (splited.length === 1) {
            var dirname = item.dirname,
                parent;

            if (depth === 1) {
                dirNotation = naming.parse(basename);
                dirType = naming.typeOf(dirNotation);

                if (dirType === 'block') {
                    return walk(filename, { notation: dirNotation, type: dirType });
                }
            } else if (depth === 2) {
                parent = path.basename(dirname);
                dirNotation = naming.parse(parent + basename);
                dirType = naming.typeOf(dirNotation);

                if (dirType === 'blockMod' || dirType === 'elem') {
                    return walk(filename, { notation: dirNotation, type: dirType });
                }
            } else if (depth === 3) {
                var grandParent = path.basename(path.dirname(dirname));

                parent = path.basename(dirname);
                dirNotation = naming.parse(grandParent + parent + basename);
                dirType = naming.typeOf(dirNotation);

                if (dirType === 'elemMod') {
                    return walk(filename, { notation: dirNotation, type: dirType });
                }
            }
        } else {
            if (depth > 1) {
                var data = item.data,
                    notation = naming.parse(splited.shift());

                dirNotation = data.notation;
                dirType = data.type;

                if (notation &&
                    ((depth === 2 && dirNotation.block === notation.block) ||
                    (depth === 3 && dirType === 'elem' && dirNotation.block === notation.block &&
                        dirNotation.elem === notation.elem) ||
                    (depth === 3 && dirType === 'blockMod' && dirNotation.block === notation.block &&
                        dirNotation.modName === notation.modName) ||
                    (depth === 4 && dirNotation.block === notation.block && dirNotation.elem === notation.elem &&
                        dirNotation.modName === notation.modName)
                )) {
                    notation.level = level;
                    notation.tech = splited.join('.');
                    notation.path = filename;

                    add(notation);
                }
            }
        }

        next();
    };
};
<|endoftext|>