From 07de32a44ecd4184b98a86361ec2c02fccae4f4e Mon Sep 17 00:00:00 2001 From: Carl Kittelberger Date: Thu, 18 May 2017 01:22:46 +0200 Subject: [PATCH] Update to ICSv2. ICSv2 will be used as a preparation to fully change to ES2017 only. For now the compilation will be done by transpiling from ICSv2 to ES2015+, then using lebab to upgrade some left-over code parts and then using Babel to transpile down some of the features that Node does not support yet. This way we already make sure we can theoretically use ES2017. - Use import instead of require. - Rename *.iced to *.coffee with some exceptions (see below). - Rename app.iced to index.coffee. - Rename app.js to index.js. - Wrap app code in function definition to avoid import statements not being at top-most root. - Avoid using fat arrows where unnecessary to reduce overhead in generated ES code. Known issues: - Currently the code can not be run without being built first. We need to see how we can solve that soon. --- .dockerignore | 33 +- src/.gitignore => .gitignore | 13 +- Dockerfile | 2 +- src/package.json => package.json | 31 +- src/app.coffee | 382 +++++ src/app.iced | 381 ----- src/{config.iced => config.coffee} | 8 +- src/index.coffee | 73 + src/{app.js => index.js} | 6 +- src/{logger.iced => logger.coffee} | 15 +- ...se_duration.iced => parse_duration.coffee} | 6 +- src/{require_bin.iced => require_bin.coffee} | 11 +- ...mparer.iced => service_depcomparer.coffee} | 2 +- ..._template.iced => service_template.coffee} | 14 +- src/{services.iced => services.coffee} | 20 +- .../{pulseaudio.iced => pulseaudio.coffee} | 24 +- .../{ts3client.iced => ts3client.coffee} | 39 +- src/services/{vlc.iced => vlc.coffee} | 12 +- src/services/{xvfb.iced => xvfb.coffee} | 11 +- src/services/{xwm.iced => xwm.coffee} | 17 +- ...ar_property.iced => sugar_property.coffee} | 9 +- src/{ts3query.iced => ts3query.coffee} | 258 +-- src/{ts3settings.iced => ts3settings.coffee} | 50 +- src/{x11.iced => x11.coffee} | 50 +- src/yarn.lock | 1457 ----------------- 25 files changed, 814 insertions(+), 2110 deletions(-) rename src/.gitignore => .gitignore (67%) rename src/package.json => package.json (53%) create mode 100644 src/app.coffee delete mode 100644 src/app.iced rename src/{config.iced => config.coffee} (91%) create mode 100644 src/index.coffee rename src/{app.js => index.js} (94%) rename src/{logger.iced => logger.coffee} (83%) rename src/{parse_duration.iced => parse_duration.coffee} (75%) rename src/{require_bin.iced => require_bin.coffee} (74%) rename src/{service_depcomparer.iced => service_depcomparer.coffee} (84%) rename src/{service_template.iced => service_template.coffee} (94%) rename src/{services.iced => services.coffee} (82%) rename src/services/{pulseaudio.iced => pulseaudio.coffee} (84%) rename src/services/{ts3client.iced => ts3client.coffee} (94%) rename src/services/{vlc.iced => vlc.coffee} (78%) rename src/services/{xvfb.iced => xvfb.coffee} (79%) rename src/services/{xwm.iced => xwm.coffee} (86%) rename src/{sugar_property.iced => sugar_property.coffee} (51%) rename src/{ts3query.iced => ts3query.coffee} (79%) rename src/{ts3settings.iced => ts3settings.coffee} (83%) rename src/{x11.iced => x11.coffee} (63%) delete mode 100644 src/yarn.lock diff --git a/.dockerignore b/.dockerignore index 3f978aa..e48d5ca 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,34 @@ -.git -**/.gitignore +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Deployed apps should consider commenting this line out: +# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git +node_modules + +### + +.git* +.docker* Dockerfile -src/node_modules **/*.log **/*.ini **/*.md diff --git a/src/.gitignore b/.gitignore similarity index 67% rename from src/.gitignore rename to .gitignore index dd55084..ad29cd3 100644 --- a/src/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Logs logs *.log -npm-debug.log* # Runtime data pids @@ -17,12 +16,14 @@ coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt -# node-waf configuration -.lock-wscript - # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory -# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git -node_modules \ No newline at end of file +# Deployed apps should consider commenting this line out: +# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git +node_modules + +# Built files +src.js +dist diff --git a/Dockerfile b/Dockerfile index 0090a81..62df88d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:6.3.1 +FROM node:7.10.0 ARG TS3CLIENT_VERSION=3.0.19.4 diff --git a/src/package.json b/package.json similarity index 53% rename from src/package.json rename to package.json index d1b3295..49a87e4 100644 --- a/src/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "ts3bot", - "version": "0.3.0", + "version": "0.4.0", "description": "Allows running TeamSpeak3 as a bot for all kinds of media (local music/videos/streams/YouTube/...) without the need for a real GUI to exist.", - "main": "app.js", + "main": "dist/node7/index.js", "bin": { - "ts3bot": "app.js" + "ts3bot": "dist/node7/index.js" }, "keywords": [ "teamspeak", @@ -20,15 +20,22 @@ "media", "musicbot" ], - "author": "Carl Kittelberger ", + "scripts": { + "iced2es": "iced3 -I none -b --no-header -o src.js -c src && lebab -t let,multi-var,for-each,arg-rest,arg-spread,obj-method,obj-shorthand,no-strict,exponent,template,default-param,includes,class --replace src.js", + "es2017-node7": "babel src.js --presets es2017-node7 --source-maps --out-dir ./dist/node7", + "build": "npm run iced2es && npm run es2017-node7", + "prepublish": "npm run build", + "start": "node .", + "start-coffee": "node ./src/index.js" + }, + "author": "Carl Kittelberger ", "license": "GPL-3.0+", "repository": { "type": "git", - "url": "https://github.com/icedream/ts3bot-control.git" + "url": "https://github.com/icedream/ts3bot.git" }, "dependencies": { "express": "^4.13.3", - "iced-coffee-script": "^108.0.8", "merge": "^1.2.0", "mkdirp": "^0.5.1", "named-regexp": "^0.1.1", @@ -49,7 +56,17 @@ "webchimera.js": "^0.2.7", "which": "^1.1.2", "winston": "^2.3.1", - "xvfb": "git://github.com/icedream/node-xvfb.git", + "xvfb": "^0.2.3", "youtube-dl": "^1.10.5" + }, + "devDependencies": { + "babel-cli": "^6.24.1", + "babel-preset-es2017-node7": "^0.5.2", + "iced-coffee-script-3": "^111.1.1", + "lebab": "^2.7.2" + }, + "engineStrict": true, + "engines": { + "node": ">= 7.6.0" } } diff --git a/src/app.coffee b/src/app.coffee new file mode 100644 index 0000000..55f4fbc --- /dev/null +++ b/src/app.coffee @@ -0,0 +1,382 @@ +import { track as tempTrack } from 'temp' +import fs from 'fs' +import path from 'path' +import prettyMs from 'pretty-ms' +import qs from 'querystring' +import request from 'request' +import Sync from 'sync' +import validUrl from 'valid-url' +import youtubedl from 'youtube-dl' + +import config from './config' +import getLogger from './logger' +import services from './services' +import parseDuration from './parse_duration.iced' + +isValidUrl = validUrl.isWebUri +track = tempTrack() +log = getLogger "Main" + +# http://stackoverflow.com/a/7117336 +removeBB = (str) -> str.replace /\[(\w+)[^\]]*](.*?)\[\/\1]/g, "$2" + +shutdown = (cb) -> + ts3clientService = services.find("ts3client") + if ts3clientService and ts3clientService.state == "started" + await ts3clientService.stop defer(err) + if err + cb? new Error "Could not stop TeamSpeak3" + return + + log.debug "Shutting down services..." + await services.shutdown defer(err) + if err + cb? new Error "Error while shutting down rest of services." + log.debug "Services shut down." + + cb?() + +module.exports = + shutdown: shutdown + shutdownSync: => Sync @shutdown + +run = -> + # Separate our own PulseAudio from any system one by using our own custom XDG directories. + process.env.XDG_RUNTIME_DIR = temp.mkdirSync "ts3bot-xdg" + + # Xvfb for isolated graphical interfaces! + xvfbService = services.find("xvfb") + await xvfbService.start defer(err, vlc) + if err + if not process.env.DISPLAY? or process.env.DISPLAY.trim() == "" + log.error "Xvfb could not start up and no display is available!", err + await shutdown defer() + process.exit 1 + log.warn "Xvfb could not start up - will use existing display!", err + + # PulseAudio daemon + await services.find("pulseaudio").start defer err + if err + log.warn "PulseAudio could not start up, audio may not act as expected!", err + + # VLC via WebChimera.js + vlcService = services.find("vlc") + await vlcService.start defer(err, vlc) + if err + log.warn "VLC could not start up!", err + await shutdown defer() + process.exit 1 + + # This is where we keep track of the volume + vlcVolume = 50 + + # Cached information for tracks in playlist + vlcMediaInfo = {} + + # TeamSpeak3 + ts3clientService = services.find("ts3client") + + ts3clientService.on "started", (ts3proc) -> + ts3query = ts3clientService.query + + ts3clientService.once "stopped", () -> + ts3query = undefined + + # VLC event handling + vlc.onPlaying = () -> + try + # TODO: Check why info is sometimes null, something must be wrong with the "add"/"play" commands here! + # TODO: Do not format as URL in text message if MRL points to local file + + item = vlc.playlist.items[vlc.playlist.currentItem] + info = vlcMediaInfo[item.mrl] + url = info?.originalUrl or item.mrl + title = info?.title or item.mrl + ts3query?.sendtextmessage 2, 0, "Now playing [URL=#{url}]#{title}[/URL]." + + # Restore audio volume + vlc.audio.volume = vlcVolume + catch e + log.warn "Error in VLC onPlaying handler", e + + vlc.onPaused = () -> ts3query?.sendtextmessage 2, 0, "Paused." + vlc.onForward = () -> ts3query?.sendtextmessage 2, 0, "Fast-forwarding..." + vlc.onBackward = () -> ts3query?.sendtextmessage 2, 0, "Rewinding..." + vlc.onEncounteredError = () -> log.error "VLC has encountered an error! You will need to restart the bot.", arguments + vlc.onStopped = () -> ts3query?.sendtextmessage 2, 0, "Stopped." + + ts3query.currentScHandlerID = 1 + ts3query.mydata = {} + + ts3query.on "open", -> + log.info "TS3 query now ready." + + attempts = 0 + err = null + init = true + while init or err != null + init = false + if err + attempts++ + if attempts == 10 + log.error "Could not register to TeamSpeak3 client events, giving up!" + break + else + log.warn "Could not register to TeamSpeak3 client events!", err + + [ + "notifytalkstatuschange" + "notifyconnectstatuschange" + "notifytextmessage" + "notifyclientupdated" + "notifycliententerview" + "notifyclientleftview" + "notifyclientchatclosed" + "notifyclientchatcomposing" + "notifyclientchannelgroupchanged" + "notifyclientmoved" + ].every(eventName -> + await ts3query.clientnotifyregister ts3query.currentScHandlerID, eventName, defer(err) + if err + return false + return true + ) + + ts3query.on "message.selected", (args) -> + if args["schandlerid"] + ts3query.currentScHandlerID = parseInt args["schandlerid"] + + ts3query.on "message.notifytalkstatuschange", (args) -> + await ts3query.use args.schandlerid, defer(err, data) + + ts3query.on "message.notifyconnectstatuschange", (args) -> + await ts3query.use args.schandlerid, defer(err, data) + + if args.status == "disconnected" and ts3clientService.state != "stopping" + log.warn "Disconnected from TeamSpeak server, reconnecting in a few seconds..." + ts3clientService.stopSync() + setTimeout (() -> ts3clientService.restartSync()), 8000 + + if args.status == "connecting" + log.info "Connecting to TeamSpeak server..." + + if args.status == "connection_established" + log.info "Connected to TeamSpeak server." + + ts3query.on "message.notifyclientupdated", (args) -> + await ts3query.use args.schandlerid, defer(err, data) + await ts3query.whoami defer(err, data) + if not err + ts3query.mydata = data + + ts3query.on "message.notifytextmessage", (args) -> + await ts3query.use args.schandlerid, defer(err, data) + + if not args.msg? + return + + msg = args.msg + invoker = { name: args.invokername, uid: args.invokeruid, id: args.invokerid } + targetmode = args.targetmode # 1 = private, 2 = channel + + log.info "<#{invoker.name}> #{msg}" + + # cheap argument parsing here + firstSpacePos = msg.indexOf " " + if firstSpacePos == 0 + return + if firstSpacePos > 0 + name = msg.substring 0, firstSpacePos + paramline = msg.substring firstSpacePos + 1 + params = paramline.match(/'[^']*'|"[^"]*"|[^ ]+/g) || [] + else + name = msg + paramline = "" + params = [] + + switch name.toLowerCase() + when "current" + item = vlc.playlist.items[vlc.playlist.currentItem] + if not item? + ts3query?.sendtextmessage args.targetmode, invoker.id, "Not playing anything at the moment." + return + + info = vlcMediaInfo[item.mrl] + url = info?.originalUrl or item.mrl + title = info?.title or item.mrl + ts3query?.sendtextmessage args.targetmode, invoker.id, "Currently playing [URL=#{url}]#{title}[/URL]." + + # Restore audio volume + vlc.audio.volume = vlcVolume + when "pause" + # now we can toggle-pause playback this easily! yay! + vlc.togglePause() + return + when "play" + inputBB = paramline.trim() + input = (removeBB paramline).trim() + + # we gonna interpret play without a url as an attempt to unpause the current song + if input.length <= 0 + vlc.play() + return + + # only allow playback from file if it's a preconfigured alias + if isValidUrl input + log.debug "Got input URL:", input + else + input = config.get "aliases:#{input}" + if not(isValidUrl input) and not(fs.existsSync input) + log.debug "Got neither valid URL nor valid alias:", input + ts3query.sendtextmessage args.targetmode, invoker.id, "Sorry, you're not allowed to play #{inputBB} via the bot." + return + + # TODO: permission system to check if uid is allowed to play this url or alias + + vlc.playlist.clear() + + # let's give youtube-dl a shot! + await youtubedl.getInfo input, ["--format=bestaudio"], defer(err, info) + if err or not info? + log.debug "There is no audio-only download for #{inputBB}, downloading full video instead." + await youtubedl.getInfo input, ["--format=best"], defer(err, info) + if err or not info? + info = + url: input + if not info.url? + info.url = input + info.title = input # URL as title + info.originalUrl = input + vlcMediaInfo[info.url] = info + + # play it in VLC + vlc.play info.url + when "time", "seek", "pos", "position" + inputBB = paramline.trim() + input = (removeBB paramline).trim() + + # we gonna interpret no argument as us needing to return the current position + if input.length <= 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "Currently position is #{prettyMs vlc.input.time}." + return + + ts3query.sendtextmessage args.targetmode, invoker.id, "Seeking to #{prettyMs vlc.input.time}." + vlc.input.time = parseDuration input + + return + when "stop-after" + vlc.playlist.mode = vlc.playlist.Single + ts3query.sendtextmessage args.targetmode, invoker.id, "Playback will stop after the current playlist item." + when "loop" + inputBB = paramline + input = null + switch (removeBB paramline).toLowerCase().trim() + when "" + # just show current mode + ts3query.sendtextmessage args.targetmode, invoker.id, "Playlist looping is #{if vlc.playlist.mode == vlc.playlist.Loop then "on" else "off"}." + when "on" + # enable looping + vlc.playlist.mode = vlc.playlist.Loop + ts3query.sendtextmessage args.targetmode, invoker.id, "Playlist looping is now on." + when "off" + # disable looping + vlc.playlist.mode = vlc.playlist.Normal + ts3query.sendtextmessage args.targetmode, invoker.id, "Playlist looping is now off." + else + ts3query.sendtextmessage args.targetmode, invoker.id, "[B]#{name} on|off[/B] - Turns playlist looping on or off" + return + when "next" + if vlc.playlist.items.count == 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "The playlist is empty." + return + if vlc.playlist.mode != vlc.playlist.Loop and vlc.playlist.currentItem == vlc.playlist.items.count - 1 + ts3query.sendtextmessage args.targetmode, invoker.id, "Can't jump to next playlist item, this is the last one!" + return + vlc.playlist.next() + when "prev", "previous" + if vlc.playlist.items.count == 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "The playlist is empty." + return + if vlc.playlist.mode != vlc.playlist.Loop and vlc.playlist.currentItem <= 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "Can't jump to previous playlist item, this is the first one!" + return + vlc.playlist.prev() + when "empty", "clear" + vlc.playlist.clear() + ts3query.sendtextmessage args.targetmode, invoker.id, "Cleared the playlist." + when "enqueue", "add", "append" + inputBB = paramline.trim() + input = (removeBB paramline).trim() + + if inputBB.length <= 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "[B]#{name} [/B] - Adds the specified URL to the current playlist" + return + + # only allow playback from file if it's a preconfigured alias + if isValidUrl input + log.debug "Got input URL:", input + else + input = config.get "aliases:#{input}" + if not(isValidUrl input) and not(fs.existsSync input) + log.debug "Got neither valid URL nor valid alias:", input + ts3query.sendtextmessage args.targetmode, invoker.id, "Sorry, you're not allowed to play #{inputBB} via the bot." + return + + # TODO: permission system to check if uid is allowed to play this url or alias + + # let's give youtube-dl a shot! + await youtubedl.getInfo input, ["--format=bestaudio"], defer(err, info) + if err or not info? + log.debug "There is no audio-only download for #{inputBB}, downloading full video instead." + await youtubedl.getInfo input, ["--format=best"], defer(err, info) + if err or not info? + info = + url: input + if not info.url? + info.url = input + info.title = input # URL as title + info.originalUrl = input + vlcMediaInfo[info.url] = info + + # add it in VLC + vlc.playlist.add info.url + ts3query.sendtextmessage args.targetmode, invoker.id, "Added [URL=#{input}]#{info.title}[/URL] to the playlist." + + # TODO: Do we need to make sure that vlc.playlist.mode is not set to "Single" here or is that handled automatically? + when "stop" + vlc.stop() + when "vol" + inputBB = paramline.trim() + input = (removeBB paramline).trim() + + if inputBB.length <= 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "Volume is currently set to #{vlcVolume}%." + return + + vol = parseInt input + + if paramline.trim().length <= 0 or isNaN(vol) or vol > 200 or vol < 0 + ts3query.sendtextmessage args.targetmode, invoker.id, "[B]vol [/B] - takes a number between 0 (0%) and 200 (200%) to set the volume. 100% is 100. Defaults to 50 (50%) on startup." + return + + vlc.audio.volume = vlcVolume = vol + ts3query.sendtextmessage args.targetmode, invoker.id, "Volume set to #{vol}%." + when "changenick" + nick = paramline + Sync -> + try + ts3query.clientupdate.sync ts3query, { client_nickname: nick } + catch err + log.warn "ChangeNick failed, error information:", err + switch err.id + when 513 then ts3query.sendtextmessage args.targetmode, invoker.id, "That nickname is already in use." + when 1541 then ts3query.sendtextmessage args.targetmode, invoker.id, "That nickname is too short or too long." + else ts3query.sendtextmessage args.targetmode, invoker.id, "That unfortunately didn't work out." + + await ts3clientService.start [ config.get("ts3-server") ], defer(err, ts3proc) + if err + log.error "TeamSpeak3 could not start, shutting down.", err + await shutdown defer() + process.exit 1 + +run() diff --git a/src/app.iced b/src/app.iced deleted file mode 100644 index 4ab6673..0000000 --- a/src/app.iced +++ /dev/null @@ -1,381 +0,0 @@ -Sync = require "sync" - -config = require("./config") -getLogger = require("./logger") -services = require("./services") -sync = require "sync" -request = require "request" -fs = require("fs") -path = require("path") -qs = require "querystring" -temp = require("temp").track() -youtubedl = require "youtube-dl" -isValidUrl = (require "valid-url").isWebUri -parseDuration = require "./parse_duration.iced" -prettyMs = require "pretty-ms" - -log = getLogger "Main" - -# http://stackoverflow.com/a/7117336 -removeBB = (str) -> str.replace /\[(\w+)[^\]]*](.*?)\[\/\1]/g, "$2" - -module.exports = - shutdown: (cb) => - ts3clientService = services.find("ts3client") - if ts3clientService and ts3clientService.state == "started" - await ts3clientService.stop defer(err) - if err - cb? new Error "Could not stop TeamSpeak3" - return - - log.debug "Shutting down services..." - await services.shutdown defer(err) - if err - cb? new Error "Error while shutting down rest of services." - log.debug "Services shut down." - - cb?() - shutdownSync: => Sync @shutdown - -# Separate our own PulseAudio from any system one by using our own custom XDG directories. -process.env.XDG_RUNTIME_DIR = temp.mkdirSync "ts3bot-xdg" - -# Xvfb for isolated graphical interfaces! -xvfbService = services.find("xvfb") -await xvfbService.start defer err, vlc -if err - if not process.env.DISPLAY? or process.env.DISPLAY.trim() == "" - log.error "Xvfb could not start up and no display is available!", err - await module.exports.shutdown defer() - process.exit 1 - log.warn "Xvfb could not start up - will use existing display!", err - -# PulseAudio daemon -await services.find("pulseaudio").start defer err -if err - log.warn "PulseAudio could not start up, audio may not act as expected!", err - -# VLC via WebChimera.js -vlcService = services.find("vlc") -await vlcService.start defer err, vlc -if err - log.warn "VLC could not start up!", err - await module.exports.shutdown defer() - process.exit 1 - -# This is where we keep track of the volume -vlcVolume = 50 - -# Cached information for tracks in playlist -vlcMediaInfo = {} - -# TeamSpeak3 -ts3clientService = services.find("ts3client") - -ts3clientService.on "started", (ts3proc) => - ts3query = ts3clientService.query - - ts3clientService.once "stopped", () => - ts3query = undefined - - # VLC event handling - vlc.onPlaying = () => - try - # TODO: Check why info is sometimes null, something must be wrong with the "add"/"play" commands here! - # TODO: Do not format as URL in text message if MRL points to local file - - item = vlc.playlist.items[vlc.playlist.currentItem] - info = vlcMediaInfo[item.mrl] - url = info?.originalUrl or item.mrl - title = info?.title or item.mrl - ts3query?.sendtextmessage 2, 0, "Now playing [URL=#{url}]#{title}[/URL]." - - # Restore audio volume - vlc.audio.volume = vlcVolume - catch e - log.warn "Error in VLC onPlaying handler", e - - vlc.onPaused = () => ts3query?.sendtextmessage 2, 0, "Paused." - vlc.onForward = () => ts3query?.sendtextmessage 2, 0, "Fast-forwarding..." - vlc.onBackward = () => ts3query?.sendtextmessage 2, 0, "Rewinding..." - vlc.onEncounteredError = () => log.error "VLC has encountered an error! You will need to restart the bot.", arguments - vlc.onStopped = () => ts3query?.sendtextmessage 2, 0, "Stopped." - - ts3query.currentScHandlerID = 1 - ts3query.mydata = {} - - ts3query.on "open", => - log.info "TS3 query now ready." - - attempts = 0 - err = null - init = true - while init or err != null - init = false - if err - attempts++ - if attempts == 10 - log.error "Could not register to TeamSpeak3 client events, giving up!" - break - else - log.warn "Could not register to TeamSpeak3 client events!", err - for eventName in [ - "notifytalkstatuschange" - "notifyconnectstatuschange" - "notifytextmessage" - "notifyclientupdated" - "notifycliententerview" - "notifyclientleftview" - "notifyclientchatclosed" - "notifyclientchatcomposing" - "notifyclientchannelgroupchanged" - "notifyclientmoved" - ] - await ts3query.clientnotifyregister ts3query.currentScHandlerID, eventName, defer(err) - if err - break - - ts3query.on "message.selected", (args) => - if args["schandlerid"] - ts3query.currentScHandlerID = parseInt args["schandlerid"] - - ts3query.on "message.notifytalkstatuschange", (args) => - await ts3query.use args.schandlerid, defer(err, data) - - ts3query.on "message.notifyconnectstatuschange", (args) => - await ts3query.use args.schandlerid, defer(err, data) - - if args.status == "disconnected" and ts3clientService.state != "stopping" - log.warn "Disconnected from TeamSpeak server, reconnecting in a few seconds..." - ts3clientService.stopSync() - setTimeout (() => ts3clientService.restartSync()), 8000 - - if args.status == "connecting" - log.info "Connecting to TeamSpeak server..." - - if args.status == "connection_established" - log.info "Connected to TeamSpeak server." - - ts3query.on "message.notifyclientupdated", (args) => - await ts3query.use args.schandlerid, defer(err, data) - await ts3query.whoami defer(err, data) - if not err - ts3query.mydata = data - - ts3query.on "message.notifytextmessage", (args) => - await ts3query.use args.schandlerid, defer(err, data) - - if not args.msg? - return - - msg = args.msg - invoker = { name: args.invokername, uid: args.invokeruid, id: args.invokerid } - targetmode = args.targetmode # 1 = private, 2 = channel - - log.info "<#{invoker.name}> #{msg}" - - # cheap argument parsing here - firstSpacePos = msg.indexOf " " - if firstSpacePos == 0 - return - if firstSpacePos > 0 - name = msg.substring 0, firstSpacePos - paramline = msg.substring firstSpacePos + 1 - params = paramline.match(/'[^']*'|"[^"]*"|[^ ]+/g) || []; - else - name = msg - paramline = "" - params = [] - - switch name.toLowerCase() - when "current" - item = vlc.playlist.items[vlc.playlist.currentItem] - if not item? - ts3query?.sendtextmessage args.targetmode, invoker.id, "Not playing anything at the moment." - return - - info = vlcMediaInfo[item.mrl] - url = info?.originalUrl or item.mrl - title = info?.title or item.mrl - ts3query?.sendtextmessage args.targetmode, invoker.id, "Currently playing [URL=#{url}]#{title}[/URL]." - - # Restore audio volume - vlc.audio.volume = vlcVolume - when "pause" - # now we can toggle-pause playback this easily! yay! - vlc.togglePause() - return - when "play" - inputBB = paramline.trim() - input = (removeBB paramline).trim() - - # we gonna interpret play without a url as an attempt to unpause the current song - if input.length <= 0 - vlc.play() - return - - # only allow playback from file if it's a preconfigured alias - if isValidUrl input - log.debug "Got input URL:", input - else - input = config.get "aliases:#{input}" - if not(isValidUrl input) and not(fs.existsSync input) - log.debug "Got neither valid URL nor valid alias:", input - ts3query.sendtextmessage args.targetmode, invoker.id, "Sorry, you're not allowed to play #{inputBB} via the bot." - return - - # TODO: permission system to check if uid is allowed to play this url or alias - - vlc.playlist.clear() - - # let's give youtube-dl a shot! - await youtubedl.getInfo input, [ - "--format=bestaudio" - ], defer(err, info) - if err or not info? - log.debug "There is no audio-only download for #{inputBB}, downloading full video instead." - await youtubedl.getInfo input, [ - "--format=best" - ], defer(err, info) - if err or not info? - info = - url: input - if not info.url? - info.url = input - info.title = input # URL as title - info.originalUrl = input - vlcMediaInfo[info.url] = info - - # play it in VLC - vlc.play info.url - when "time", "seek", "pos", "position" - inputBB = paramline.trim() - input = (removeBB paramline).trim() - - # we gonna interpret no argument as us needing to return the current position - if input.length <= 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "Currently position is #{prettyMs vlc.input.time}." - return - - ts3query.sendtextmessage args.targetmode, invoker.id, "Seeking to #{prettyMs vlc.input.time}." - vlc.input.time = parseDuration input - - return - when "stop-after" - vlc.playlist.mode = vlc.playlist.Single - ts3query.sendtextmessage args.targetmode, invoker.id, "Playback will stop after the current playlist item." - when "loop" - inputBB = paramline - input = null - switch (removeBB paramline).toLowerCase().trim() - when "" - # just show current mode - ts3query.sendtextmessage args.targetmode, invoker.id, "Playlist looping is #{if vlc.playlist.mode == vlc.playlist.Loop then "on" else "off"}." - when "on" - # enable looping - vlc.playlist.mode = vlc.playlist.Loop - ts3query.sendtextmessage args.targetmode, invoker.id, "Playlist looping is now on." - when "off" - # disable looping - vlc.playlist.mode = vlc.playlist.Normal - ts3query.sendtextmessage args.targetmode, invoker.id, "Playlist looping is now off." - else - ts3query.sendtextmessage args.targetmode, invoker.id, "[B]#{name} on|off[/B] - Turns playlist looping on or off" - return - when "next" - if vlc.playlist.items.count == 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "The playlist is empty." - return - if vlc.playlist.mode != vlc.playlist.Loop and vlc.playlist.currentItem == vlc.playlist.items.count - 1 - ts3query.sendtextmessage args.targetmode, invoker.id, "Can't jump to next playlist item, this is the last one!" - return - vlc.playlist.next() - when "prev", "previous" - if vlc.playlist.items.count == 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "The playlist is empty." - return - if vlc.playlist.mode != vlc.playlist.Loop and vlc.playlist.currentItem <= 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "Can't jump to previous playlist item, this is the first one!" - return - vlc.playlist.prev() - when "empty", "clear" - vlc.playlist.clear() - ts3query.sendtextmessage args.targetmode, invoker.id, "Cleared the playlist." - when "enqueue", "add", "append" - inputBB = paramline.trim() - input = (removeBB paramline).trim() - - if inputBB.length <= 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "[B]#{name} [/B] - Adds the specified URL to the current playlist" - return - - # only allow playback from file if it's a preconfigured alias - if isValidUrl input - log.debug "Got input URL:", input - else - input = config.get "aliases:#{input}" - if not(isValidUrl input) and not(fs.existsSync input) - log.debug "Got neither valid URL nor valid alias:", input - ts3query.sendtextmessage args.targetmode, invoker.id, "Sorry, you're not allowed to play #{inputBB} via the bot." - return - - # TODO: permission system to check if uid is allowed to play this url or alias - - # let's give youtube-dl a shot! - await youtubedl.getInfo input, [ - "--format=bestaudio" - ], defer(err, info) - if err or not info? - log.debug "There is no audio-only download for #{inputBB}, downloading full video instead." - await youtubedl.getInfo input, [ - "--format=best" - ], defer(err, info) - if err or not info? - info = - url: input - if not info.url? - info.url = input - info.title = input # URL as title - info.originalUrl = input - vlcMediaInfo[info.url] = info - - # add it in VLC - vlc.playlist.add info.url - ts3query.sendtextmessage args.targetmode, invoker.id, "Added [URL=#{input}]#{info.title}[/URL] to the playlist." - - # TODO: Do we need to make sure that vlc.playlist.mode is not set to "Single" here or is that handled automatically? - when "stop" - vlc.stop() - when "vol" - inputBB = paramline.trim() - input = (removeBB paramline).trim() - - if inputBB.length <= 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "Volume is currently set to #{vlcVolume}%." - return - - vol = parseInt input - - if paramline.trim().length <= 0 or isNaN(vol) or vol > 200 or vol < 0 - ts3query.sendtextmessage args.targetmode, invoker.id, "[B]vol [/B] - takes a number between 0 (0%) and 200 (200%) to set the volume. 100% is 100. Defaults to 50 (50%) on startup." - return - - vlc.audio.volume = vlcVolume = vol - ts3query.sendtextmessage args.targetmode, invoker.id, "Volume set to #{vol}%." - when "changenick" - nick = paramline - Sync () => - try - ts3query.clientupdate.sync ts3query, { client_nickname: nick } - catch err - log.warn "ChangeNick failed, error information:", err - switch err.id - when 513 then ts3query.sendtextmessage args.targetmode, invoker.id, "That nickname is already in use." - when 1541 then ts3query.sendtextmessage args.targetmode, invoker.id, "That nickname is too short or too long." - else ts3query.sendtextmessage args.targetmode, invoker.id, "That unfortunately didn't work out." - -await ts3clientService.start [ config.get("ts3-server") ], defer(err, ts3proc) -if err - log.error "TeamSpeak3 could not start, shutting down.", err - await module.exports.shutdown defer() - process.exit 1 diff --git a/src/config.iced b/src/config.coffee similarity index 91% rename from src/config.iced rename to src/config.coffee index 5cddbfd..79aa69d 100644 --- a/src/config.iced +++ b/src/config.coffee @@ -1,7 +1,7 @@ -nconf = require "nconf" -path = require "path" -merge = require "merge" -pwgen = require "password-generator" +import nconf from 'nconf' +import path from 'path' +import merge from 'merge' +import pwgen from 'password-generator' console.log "Loading configuration..." diff --git a/src/index.coffee b/src/index.coffee new file mode 100644 index 0000000..1ae56af --- /dev/null +++ b/src/index.coffee @@ -0,0 +1,73 @@ +import Sync from 'sync' +import readline from 'readline' + +import services from './services' +import getLogger from './logger' +import app from './app.iced' + +log = getLogger('app') + +# compatibility with Windows for interrupt signal +if process.platform == 'win32' + rl = readline.createInterface( + input: process.stdin + output: process.stdout) + rl.on 'SIGINT', -> + process.emit 'SIGINT' + +doShutdownAsync = (cb) -> + log.info 'App shutdown starting...' + app.shutdown -> + log.info 'Services shutdown starting...' + services.shutdown -> + if cb and typeof cb == 'function' + cb() + return + return + return + +process.on 'uncaughtException', (err) -> + log.error 'Shutting down due to an uncaught exception!', err + app.shutdownSync() + process.exit 0xFF + return + +process.on 'exit', (e) -> + log.debug 'Triggered exit', e + app.shutdownSync() + return + +process.on 'SIGTERM', (e) -> + log.debug 'Caught SIGTERM signal' + app.shutdown -> + process.exit 0 + return + return + +process.on 'SIGINT', -> + log.debug 'Caught SIGINT signal' + app.shutdown -> + process.exit 0 + return + return + +process.on 'SIGHUP', -> + log.debug 'Caught SIGHUP signal' + app.shutdown -> + process.exit 0 + return + return + +process.on 'SIGQUIT', -> + log.debug 'Caught SIGQUIT signal' + app.shutdown -> + process.exit 0 + return + return + +process.on 'SIGABRT', -> + log.debug 'Caught SIGABRT signal' + app.shutdown -> + process.exit 0 + return + return diff --git a/src/app.js b/src/index.js similarity index 94% rename from src/app.js rename to src/index.js index 26af737..9f46e85 100644 --- a/src/app.js +++ b/src/index.js @@ -1,6 +1,4 @@ -#!/usr/bin/env node - -require("iced-coffee-script/register"); +require("iced-coffee-script-3/register"); Sync = require("sync"); var services = require("./services"); @@ -19,7 +17,7 @@ if (process.platform === "win32") { }); } -app = require("./app.iced"); +app = require("./app"); doShutdownAsync = function(cb) { log.info("App shutdown starting..."); diff --git a/src/logger.iced b/src/logger.coffee similarity index 83% rename from src/logger.iced rename to src/logger.coffee index 29e72a8..4b65097 100644 --- a/src/logger.iced +++ b/src/logger.coffee @@ -1,8 +1,9 @@ -winston = require "winston" -path = require "path" -config = require "./config" -merge = require "merge" -winstonCommon = require "winston/lib/winston/common" +import winston from 'winston' +import path from 'path' +import merge from 'merge' +import winstonCommon from 'winston/lib/winston/common' + +import config from './config' winston.emitErrs = true @@ -40,10 +41,10 @@ container = new (winston.Container) initialized_loggers = [] -module.exports = (name, options) => +module.exports = (name, options) -> if not(name in initialized_loggers) logger = container.add name - logger.filters.push (level, msg, meta) => "[#{name}] #{msg}" + logger.filters.push (level, msg, meta) -> "[#{name}] #{msg}" initialized_loggers.push name return logger diff --git a/src/parse_duration.iced b/src/parse_duration.coffee similarity index 75% rename from src/parse_duration.iced rename to src/parse_duration.coffee index bf4b563..0938738 100644 --- a/src/parse_duration.iced +++ b/src/parse_duration.coffee @@ -1,5 +1,5 @@ -parseDuration = require "parse-duration" -namedRegex = require("named-regexp").named +import parseDuration from 'parse-duration' +import { named as namedRegex } from 'named-regexp' durationRegex = namedRegex /^(((:[0-9]{0,2}):)?(:[0-9]{0,2}):)?(:[0-9]{0,2})(:\.[0-9]*)?$/ @@ -9,4 +9,4 @@ module.exports = (str) -> m = durationRegex.exec(str).matches return m["ms"] + m["s"]*60 + m["m"]*(60*60) + m["h"]*(60*60*60) - parseDuration str \ No newline at end of file + parseDuration str diff --git a/src/require_bin.iced b/src/require_bin.coffee similarity index 74% rename from src/require_bin.iced rename to src/require_bin.coffee index 1a6a77f..fc75b6f 100644 --- a/src/require_bin.iced +++ b/src/require_bin.coffee @@ -1,8 +1,11 @@ -which = require("which").sync -path = require "path" -log = require("./logger")("RequireBin") +import { sync as which } from 'which' +import path from 'path' -module.exports = (binName, doErrorIfNotFound) => +import getLogger from './logger' + +log = getLogger "RequireBin" + +module.exports = (binName, doErrorIfNotFound) -> doErrorIfNotFound = true unless doErrorIfNotFound? # check if xvfb is findable from here diff --git a/src/service_depcomparer.iced b/src/service_depcomparer.coffee similarity index 84% rename from src/service_depcomparer.iced rename to src/service_depcomparer.coffee index 18621a3..bce3f69 100644 --- a/src/service_depcomparer.iced +++ b/src/service_depcomparer.coffee @@ -3,4 +3,4 @@ module.exports = (a, b) -> return -1; # a before b if b.dependencies.indexOf(a.name) >= 0 return 1; # a after b - return 0; # does not matter \ No newline at end of file + return 0 # does not matter diff --git a/src/service_template.iced b/src/service_template.coffee similarity index 94% rename from src/service_template.iced rename to src/service_template.coffee index 5ad2630..02363dd 100644 --- a/src/service_template.iced +++ b/src/service_template.coffee @@ -1,11 +1,11 @@ -Sync = require "sync" +import Sync from 'sync' +import { EventEmitter } from 'events' +import merge from 'merge' -getLogger = require "./logger" -EventEmitter = require("events").EventEmitter -merge = require "merge" -services = require "./services" +import getLogger from './logger' +import services from './services' -module.exports = class Service extends EventEmitter +class Service extends EventEmitter constructor: (@name, funcs) -> @log = getLogger @name @_funcs = funcs @@ -152,3 +152,5 @@ module.exports = class Service extends EventEmitter cb? err restartSync: () => Sync () => @restart.sync @ + +module.exports = Service diff --git a/src/services.iced b/src/services.coffee similarity index 82% rename from src/services.iced rename to src/services.coffee index f3e8399..309ae3c 100644 --- a/src/services.iced +++ b/src/services.coffee @@ -1,12 +1,16 @@ # At this point I feel like I'm writing my own init system. Phew... -merge = require "merge" -getLogger = require("./logger") -EventEmitter = require("events").EventEmitter -log = getLogger("ServiceMgr") -Sync = require "sync" +import merge from 'merge' +import { EventEmitter } from 'events' +import Sync from 'sync' -getLegacyServiceName = (serviceName) -> serviceName.toLowerCase().replace(/[^A-z0-9]/g, "_") +import getLogger from './logger' +import { Service } from './service_template' + +log = getLogger("ServiceMgr") + +getLegacyServiceName = (serviceName) -> + serviceName.toLowerCase().replace(/[^A-z0-9]/g, "_") module.exports = services: [] @@ -52,7 +56,7 @@ module.exports = shutdownSync: () -> Sync () => @shutdown.sync @ # base class for all services -module.exports.Service = require "./service_template" +module.exports.Service = Service # register services services = [ @@ -66,4 +70,4 @@ services.sort require("./service_depcomparer") # sort services by dependency for service in services module.exports.register service -module.exports.services = services \ No newline at end of file +module.exports.services = services diff --git a/src/services/pulseaudio.iced b/src/services/pulseaudio.coffee similarity index 84% rename from src/services/pulseaudio.iced rename to src/services/pulseaudio.coffee index 29139c2..9d2915c 100644 --- a/src/services/pulseaudio.iced +++ b/src/services/pulseaudio.coffee @@ -1,10 +1,12 @@ -spawn = require("child_process").spawn -log = require("../logger")("PulseAudio") -services = require("../services") -config = require("../config") -StreamSplitter = require("stream-splitter") -require_bin = require("../require_bin") +import { spawn } from 'child_process' +import StreamSplitter from 'stream-splitter' +import getLogger from '../logger' +import services from '../services' +import config from '../config' +import require_bin from '../require_bin' + +log = getLogger "PulseAudio" pulseaudioPath = require_bin config.get("PULSE_BINARY") pacmdPath = require_bin "pacmd" @@ -18,7 +20,7 @@ module.exports = class PulseAudioService extends services.Service return # logging - forwardLog = (token) => + forwardLog = (token) -> token = token.trim() # get rid of \r level = token.substring(0, 1).toUpperCase() msg = token.substring token.indexOf("]") + 2 @@ -41,7 +43,7 @@ module.exports = class PulseAudioService extends services.Service # check if there is already a daemon running proc = spawn pulseaudioPath, [ "--check" ], opts stderrTokenizer = proc.stderr.pipe StreamSplitter "\n" - stderrTokenizer.encoding = "utf8"; + stderrTokenizer.encoding = "utf8" stderrTokenizer.on "token", forwardLog await proc.once "exit", defer(code, signal) @log.silly "PulseAudio daemon check returned that #{if code == 0 then "a daemon is already running" else "no daemon is running"}" @@ -74,7 +76,7 @@ module.exports = class PulseAudioService extends services.Service stderrTokenizer.encoding = "utf8" stderrTokenizer.on "token", tokenHandler - proc.on "exit", () => + proc.on "exit", () -> if not calledCallback calledCallback = true cb? new Error "PulseAudio daemon terminated unexpectedly." @@ -89,11 +91,11 @@ module.exports = class PulseAudioService extends services.Service cb?() - findIndexForProcessId: (pid, cb) => throw new Error "Not implemented yet" + findIndexForProcessId: (pid, cb) -> throw new Error "Not implemented yet" findIndexForProcessIdSync: (pid) => Sync () => @findIndexForProcessId @, pid - setSinkInputMute: (index, value, cb) => throw new Error "Not implemented yet" + setSinkInputMute: (index, value, cb) -> throw new Error "Not implemented yet" setSinkInputMuteSync: (index, value) => Sync () => @setSinkInputMute @, index, value diff --git a/src/services/ts3client.iced b/src/services/ts3client.coffee similarity index 94% rename from src/services/ts3client.iced rename to src/services/ts3client.coffee index d9cfd9f..5cf74c3 100644 --- a/src/services/ts3client.iced +++ b/src/services/ts3client.coffee @@ -1,17 +1,20 @@ -xvfb = require("xvfb") -log = require("../logger")("TS3Client") -config = require("../config") -services = require("../services") -x11tools = require("../x11") -TS3Settings = require("../ts3settings") -TS3ClientQuery = require("../ts3query") -path = require "path" -merge = require "merge" -fs = require "fs" -url = require "url" -spawn = require("child_process").spawn -StreamSplitter = require("stream-splitter") -require_bin = require("../require_bin") +import xvfb from 'xvfb' +import path from 'path' +import merge from 'merge' +import fs from 'fs' +import url from 'url' +import { spawn } from 'child_process' +import StreamSplitter from 'stream-splitter' + +import getLogger from '../logger' +import config from '../config' +import services from '../services' +import x11tools from '../x11' +import TS3Settings from '../ts3settings' +import TS3ClientQuery from '../ts3query' +import require_bin from '../require_bin' + +log = getLogger "TS3Client" ts3client_binpath = require_bin path.join(config.get("ts3-install-path"), "ts3client_linux_" + (if process.arch == "x64" then "amd64" else process.arch)) @@ -57,7 +60,7 @@ module.exports = class TS3ClientService extends services.Service # spawn process proc = null doStart = null - forwardLog = (token) => + forwardLog = (token) -> token = token.trim() # get rid of \r if token.indexOf("|") > 0 token = token.split("|") @@ -92,7 +95,7 @@ module.exports = class TS3ClientService extends services.Service LD_LIBRARY_PATH: config.get("ts3-install-path") if process.env.LD_LIBRARY_PATH env.LD_LIBRARY_PATH += ":#{process.env.LD_LIBRARY_PATH}" - + @log.silly "Environment variables:", env @log.silly "Arguments:", JSON.stringify args @@ -106,11 +109,11 @@ module.exports = class TS3ClientService extends services.Service # logging stdoutTokenizer = proc.stdout.pipe StreamSplitter "\n" - stdoutTokenizer.encoding = "utf8"; + stdoutTokenizer.encoding = "utf8" stdoutTokenizer.on "token", forwardLog stderrTokenizer = proc.stderr.pipe StreamSplitter "\n" - stderrTokenizer.encoding = "utf8"; + stderrTokenizer.encoding = "utf8" stderrTokenizer.on "token", forwardLog # connect to client query plugin when it's loaded diff --git a/src/services/vlc.iced b/src/services/vlc.coffee similarity index 78% rename from src/services/vlc.iced rename to src/services/vlc.coffee index e5e1762..71081ba 100644 --- a/src/services/vlc.iced +++ b/src/services/vlc.coffee @@ -1,8 +1,9 @@ -spawn = require("child_process").spawn -services = require("../services") -config = require("../config") -wc = require("webchimera.js") -StreamSplitter = require("stream-splitter") +import { spawn } from 'child_process' +import wc from 'webchimera.js' +import StreamSplitter from 'stream-splitter' + +import services from '../services' +import config from '../config' module.exports = class VLCService extends services.Service dependencies: [ @@ -42,4 +43,3 @@ module.exports = class VLCService extends services.Service @_instance = null cb?() - diff --git a/src/services/xvfb.iced b/src/services/xvfb.coffee similarity index 79% rename from src/services/xvfb.iced rename to src/services/xvfb.coffee index 4edc696..a35a5b6 100644 --- a/src/services/xvfb.iced +++ b/src/services/xvfb.coffee @@ -1,9 +1,10 @@ -Xvfb = require("xvfb") -log = require("../logger")("Xvfb") -config = require("../config") -services = require("../services") -require_bin = require("../require_bin") +import Xvfb from 'xvfb' +import getLogger from '../logger' +import config from '../config' +import services from '../services' +import require_bin from '../require_bin' +log = getLogger "Xvfb" xvfbPath = require_bin "Xvfb", false module.exports = class XvfbService extends services.Service diff --git a/src/services/xwm.iced b/src/services/xwm.coffee similarity index 86% rename from src/services/xwm.iced rename to src/services/xwm.coffee index 6114e2b..a993d4d 100644 --- a/src/services/xwm.iced +++ b/src/services/xwm.coffee @@ -1,8 +1,11 @@ -spawn = require("child_process").spawn -log = require("../logger")("XWindowManager") -services = require("../services") -StreamSplitter = require("stream-splitter") -require_bin = require("../require_bin") +import { spawn } from 'child_process' +import StreamSplitter from 'stream-splitter' + +import getLogger from '../logger' +import services from '../services' +import require_bin from '../require_bin' + +log = getLogger "XWindowManager" xwmBinPath = require_bin "x-window-manager", false @@ -42,13 +45,13 @@ module.exports = class XWindowManagerService extends services.Service # logging stdoutTokenizer = proc.stdout.pipe StreamSplitter "\n" - stdoutTokenizer.encoding = "utf8"; + stdoutTokenizer.encoding = "utf8" stdoutTokenizer.on "token", (token) => token = token.trim() # get rid of \r @log.debug token stderrTokenizer = proc.stderr.pipe StreamSplitter "\n" - stderrTokenizer.encoding = "utf8"; + stderrTokenizer.encoding = "utf8" stderrTokenizer.on "token", (token) => token = token.trim() # get rid of \r @log.warn token diff --git a/src/sugar_property.iced b/src/sugar_property.coffee similarity index 51% rename from src/sugar_property.iced rename to src/sugar_property.coffee index d4790f3..290c7ab 100644 --- a/src/sugar_property.iced +++ b/src/sugar_property.coffee @@ -1,13 +1,16 @@ # @property "prop", { desc... } -Function::property = (prop, desc) -> Object.defineProperty @prototype, prop, desc +Function::property = (prop, desc) -> + Object.defineProperty @prototype, prop, desc # defineProperty "prop", { desc... } #Object::defineProperty = (prop, desc) -> Object.defineProperty @, prop, desc # propertiesof obj -#global.propertiesof = (obj) -> Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertyNames(obj.constructor.prototype or {})) +#global.propertiesof = (obj) -> +# Object.getOwnPropertyNames(obj).concat( +# Object.getOwnPropertyNames(obj.constructor.prototype or {})) # descriptorof obj, name #global.descriptorof = (obj, name) -> Object.getOwnPropertyDescriptor obj, name -module.exports = exports = {} \ No newline at end of file +module.exports = exports = {} diff --git a/src/ts3query.iced b/src/ts3query.coffee similarity index 79% rename from src/ts3query.iced rename to src/ts3query.coffee index 3f07864..c878337 100644 --- a/src/ts3query.iced +++ b/src/ts3query.coffee @@ -1,16 +1,16 @@ -require "string.prototype.startswith" +import 'string.prototype.startswith' -net = require "net" -getLogger = require "./logger" -StringDecoder = require("string_decoder").StringDecoder -StreamSplitter = require "stream-splitter" -events = require "events" -EventEmitter = events.EventEmitter -merge = require "merge" +import net from 'net' +import { StringDecoder } from 'string_decoder' +import StreamSplitter from 'stream-splitter' +import { EventEmitter } from 'events' +import merge from 'merge' + +import getLogger from './logger' parserLog = getLogger "parser" -escape = (value) => value.toString()\ +escape = (value) -> value.toString()\ .replace(/\\/g, "\\\\")\ .replace(/\//g, "\\/")\ .replace(/\|/g, "\\p")\ @@ -19,7 +19,7 @@ escape = (value) => value.toString()\ .replace(/\t/g, "\\t")\ .replace(/\ /g, "\\s") -unescape = (value) => value.toString()\ +unescape = (value) -> value.toString()\ .replace(/\\s/g, " ")\ .replace(/\\t/g, "\t")\ .replace(/\\r/g, "\r")\ @@ -28,7 +28,7 @@ unescape = (value) => value.toString()\ .replace(/\\\//g, "/")\ .replace(/\\\\/g, "\\") -buildCmd = (name, namedArgs, posArgs) => +buildCmd = (name, namedArgs, posArgs) -> # TODO: Add support for collected arguments (aka lists) if not name throw new Error "Need command name" @@ -52,7 +52,7 @@ buildCmd = (name, namedArgs, posArgs) => param += "#{escape(v)}" param + "\n\r" -parseCmd = (str) => +parseCmd = (str) -> params = str.split " " startIndex = 0 @@ -92,7 +92,7 @@ parseCmd = (str) => args: collectedArgs } -checkError = (err) => +checkError = (err) -> err.id = parseInt err.id if err.id == 0 return null @@ -150,7 +150,7 @@ module.exports = class TS3ClientQuery extends EventEmitter _sendKeepalive: (cb) => @_log.silly "Send: " - @_tcpClient.write "\n\r", "utf8", () => cb?() + @_tcpClient.write "\n\r", "utf8", () -> cb?() _stopKeepalive: () => if @_keepaliveInt? @@ -191,37 +191,37 @@ module.exports = class TS3ClientQuery extends EventEmitter @_log.silly "Send:", text.trim() - @_tcpClient.write text, "utf8", () => cb?() + @_tcpClient.write text, "utf8", () -> cb?() @_resetKeepalive() - banadd: (cb) => + banadd: (cb) -> throw new Error "Not implemented yet" - banclient: (cb) => + banclient: (cb) -> throw new Error "Not implemented yet" - bandel: (cb) => + bandel: (cb) -> throw new Error "Not implemented yet" - bandelall: (cb) => + bandelall: (cb) -> throw new Error "Not implemented yet" - banlist: (cb) => + banlist: (cb) -> throw new Error "Not implemented yet" - channeladdperm: (cb) => + channeladdperm: (cb) -> throw new Error "Not implemented yet" - channelclientaddperm: (cb) => + channelclientaddperm: (cb) -> throw new Error "Not implemented yet" - channelclientdelperm: (cb) => + channelclientdelperm: (cb) -> throw new Error "Not implemented yet" - channelclientlist: (cb) => + channelclientlist: (cb) -> throw new Error "Not implemented yet" - channelclientpermlist: (cb) => + channelclientpermlist: (cb) -> throw new Error "Not implemented yet" ### @@ -234,8 +234,8 @@ module.exports = class TS3ClientQuery extends EventEmitter cb = cid cid = null retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "channelconnectinfo", cid: cid @@ -254,70 +254,70 @@ module.exports = class TS3ClientQuery extends EventEmitter channel_properties = {} channel_properties["channel_name"] = channel_name retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "channelcreate", channel_properties - channeldelete: (cb) => + channeldelete: (cb) -> throw new Error "Not implemented yet" - channeldelperm: (cb) => + channeldelperm: (cb) -> throw new Error "Not implemented yet" ### Changes a channels configuration using given properties. ### channeledit: (cid, channel_properties, cb) => - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "channeledit", merge true, channel_properties, cid: cid - channelgroupadd: (cb) => + channelgroupadd: (cb) -> throw new Error "Not implemented yet" - channelgroupaddperm: (cb) => + channelgroupaddperm: (cb) -> throw new Error "Not implemented yet" - channelgroupclientlist: (cb) => + channelgroupclientlist: (cb) -> throw new Error "Not implemented yet" - channelgroupdel: (cb) => + channelgroupdel: (cb) -> throw new Error "Not implemented yet" - channelgroupdelperm: (cb) => + channelgroupdelperm: (cb) -> throw new Error "Not implemented yet" - channelgrouplist: (cb) => + channelgrouplist: (cb) -> throw new Error "Not implemented yet" - channelgrouppermlist: (cb) => + channelgrouppermlist: (cb) -> throw new Error "Not implemented yet" - channellist: (cb) => + channellist: (cb) -> throw new Error "Not implemented yet" - channelmove: (cb) => + channelmove: (cb) -> throw new Error "Not implemented yet" - channelpermlist: (cb) => + channelpermlist: (cb) -> throw new Error "Not implemented yet" - channelvariable: (cb) => + channelvariable: (cb) -> throw new Error "Not implemented yet" - clientaddperm: (cb) => + clientaddperm: (cb) -> throw new Error "Not implemented yet" - clientdbdelete: (cb) => + clientdbdelete: (cb) -> throw new Error "Not implemented yet" - clientdbedit: (cb) => + clientdbedit: (cb) -> throw new Error "Not implemented yet" - clientdblist: (cb) => + clientdblist: (cb) -> throw new Error "Not implemented yet" - clientdelperm: (cb) => + clientdelperm: (cb) -> throw new Error "Not implemented yet" ### @@ -325,8 +325,8 @@ module.exports = class TS3ClientQuery extends EventEmitter ### clientgetdbidfromuid: (cluid, cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientgetdbidfromuid", cluid: cluid @@ -335,30 +335,30 @@ module.exports = class TS3ClientQuery extends EventEmitter ### clientgetids: (cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientgetids", cluid: cluid ### - Displays the unique identifier and nickname matching the database ID specified + Displays the unique identifier and nickname matching the database ID specified by cldbid. ### clientgetnamefromdbid: (cldbid, cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientgetnamefromdbid", cldbid: cldbid ### - Displays the database ID and nickname matching the unique identifier specified + Displays the database ID and nickname matching the unique identifier specified by cluid. ### clientgetnamefromuid: (cluid, cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientgetnamefromuid", cluid: cluid @@ -368,15 +368,15 @@ module.exports = class TS3ClientQuery extends EventEmitter ### clientgetuidfromclid: (clid, cb) => retval = { } - @once "notifyclientuidfromclid", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "notifyclientuidfromclid", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientgetuidfromclid", clid: clid ### - Kicks one or more clients specified with clid from their currently joined - channel or from the server, depending on reasonid. The reasonmsg parameter - specifies a text message sent to the kicked clients. This parameter is optional + Kicks one or more clients specified with clid from their currently joined + channel or from the server, depending on reasonid. The reasonmsg parameter + specifies a text message sent to the kicked clients. This parameter is optional and may only have a maximum of 40 characters. Available reasonid values are: @@ -391,7 +391,7 @@ module.exports = class TS3ClientQuery extends EventEmitter if typeof clid == "function" cb = clid clid = null - @once "message.error", (args) => cb? checkError(args), retval + @once "message.error", (args) -> cb? checkError(args), retval @send "clientkick", reasonid: reasonid reasonmsg: reasonmsg @@ -449,14 +449,14 @@ module.exports = class TS3ClientQuery extends EventEmitter cleanedModifiers.push v retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientlist", cleanedModifiers - clientmove: (cb) => + clientmove: (cb) -> throw new Error "Not implemented yet" - clientmute: (cb) => + clientmute: (cb) -> throw new Error "Not implemented yet" ### @@ -501,7 +501,7 @@ module.exports = class TS3ClientQuery extends EventEmitter notifyconnectstatuschange ### clientnotifyregister: (schandlerid, event, cb) => - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "clientnotifyregister", schandlerid: schandlerid event: event @@ -510,7 +510,7 @@ module.exports = class TS3ClientQuery extends EventEmitter Unregisters from all previously registered client notifications. ### clientnotifyunregister: (cb) => - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "clientnotifyunregister" ### @@ -518,8 +518,8 @@ module.exports = class TS3ClientQuery extends EventEmitter ### clientpermlist: (cldbid, cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientpermlist", cldbid: cldbid @@ -530,12 +530,12 @@ module.exports = class TS3ClientQuery extends EventEmitter if typeof msg == "function" cb = msg msg = null - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "clientpoke", msg: msg clid: clid - clientunmute: (cb) => + clientunmute: (cb) -> throw new Error "Not implemented yet" ### @@ -558,7 +558,7 @@ module.exports = class TS3ClientQuery extends EventEmitter connect ### clientupdate: (idents, cb) => - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "clientupdate", idents ### @@ -624,20 +624,20 @@ module.exports = class TS3ClientQuery extends EventEmitter if not Array.isArray variables throw new Error "variables needs to be an array of requested client variables." retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "clientvariable", { clid: clid }, variables - complainadd: (cb) => + complainadd: (cb) -> throw new Error "Not implemented yet" - complaindel: (cb) => + complaindel: (cb) -> throw new Error "Not implemented yet" - complaindelall: (cb) => + complaindelall: (cb) -> throw new Error "Not implemented yet" - complainlist: (cb) => + complainlist: (cb) -> throw new Error "Not implemented yet" ### @@ -645,70 +645,70 @@ module.exports = class TS3ClientQuery extends EventEmitter ### currentschandlerid: (cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "currentschandlerid" - disconnect: (cb) => close(cb) + disconnect: (cb) -> close(cb) - exam: (cb) => + exam: (cb) -> throw new Error "Not implemented yet" - ftcreatedir: (cb) => + ftcreatedir: (cb) -> throw new Error "Not implemented yet" - ftdeletefile: (cb) => + ftdeletefile: (cb) -> throw new Error "Not implemented yet" - ftgetfileinfo: (cb) => + ftgetfileinfo: (cb) -> throw new Error "Not implemented yet" - ftgetfilelist: (cb) => + ftgetfilelist: (cb) -> throw new Error "Not implemented yet" - ftinitdownload: (cb) => + ftinitdownload: (cb) -> throw new Error "Not implemented yet" - ftinitupload: (cb) => + ftinitupload: (cb) -> throw new Error "Not implemented yet" - ftlist: (cb) => + ftlist: (cb) -> throw new Error "Not implemented yet" - ftrenamefile: (cb) => + ftrenamefile: (cb) -> throw new Error "Not implemented yet" - ftstop: (cb) => + ftstop: (cb) -> throw new Error "Not implemented yet" - hashpassword: (cb) => + hashpassword: (cb) -> throw new Error "Not implemented yet" - help: (cb) => + help: (cb) -> throw new Error "Not implemented yet" - messageadd: (cb) => + messageadd: (cb) -> throw new Error "Not implemented yet" - messagedel: (cb) => + messagedel: (cb) -> throw new Error "Not implemented yet" - messageget: (cb) => + messageget: (cb) -> throw new Error "Not implemented yet" - messagelist: (cb) => + messagelist: (cb) -> throw new Error "Not implemented yet" - messageupdateflag: (cb) => + messageupdateflag: (cb) -> throw new Error "Not implemented yet" - permoverview: (cb) => + permoverview: (cb) -> throw new Error "Not implemented yet" - quit: (cb) => close(cb) + quit: (cb) -> close(cb) ### - Sends a text message a specified target. The type of the target is determined + Sends a text message a specified target. The type of the target is determined by targetmode. Available targetmodes are: 1: Send private text message to a client. You must specify the target parameter @@ -720,61 +720,61 @@ module.exports = class TS3ClientQuery extends EventEmitter cb = msg msg = target target = null - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "sendtextmessage", targetmode: targetmode target: target msg: msg - serverconnectinfo: (cb) => + serverconnectinfo: (cb) -> throw new Error "Not implemented yet" - serverconnectionhandlerlist: (cb) => + serverconnectionhandlerlist: (cb) -> throw new Error "Not implemented yet" - servergroupadd: (cb) => + servergroupadd: (cb) -> throw new Error "Not implemented yet" - servergroupaddclient: (cb) => + servergroupaddclient: (cb) -> throw new Error "Not implemented yet" - servergroupaddperm: (cb) => + servergroupaddperm: (cb) -> throw new Error "Not implemented yet" - servergroupclientlist: (cb) => + servergroupclientlist: (cb) -> throw new Error "Not implemented yet" - servergroupdel: (cb) => + servergroupdel: (cb) -> throw new Error "Not implemented yet" - servergroupdelclient: (cb) => + servergroupdelclient: (cb) -> throw new Error "Not implemented yet" - servergroupdelperm: (cb) => + servergroupdelperm: (cb) -> throw new Error "Not implemented yet" - servergrouplist: (cb) => + servergrouplist: (cb) -> throw new Error "Not implemented yet" - servergrouppermlist: (cb) => + servergrouppermlist: (cb) -> throw new Error "Not implemented yet" - servergroupsbyclientid: (cb) => + servergroupsbyclientid: (cb) -> throw new Error "Not implemented yet" - servervariable: (cb) => + servervariable: (cb) -> throw new Error "Not implemented yet" - setclientchannelgroup: (cb) => + setclientchannelgroup: (cb) -> throw new Error "Not implemented yet" - tokenadd: (cb) => + tokenadd: (cb) -> throw new Error "Not implemented yet" - tokendelete: (cb) => + tokendelete: (cb) -> throw new Error "Not implemented yet" - tokenlist: (cb) => + tokenlist: (cb) -> throw new Error "Not implemented yet" ### @@ -782,7 +782,7 @@ module.exports = class TS3ClientQuery extends EventEmitter server will automatically delete the token after it has been used. ### tokenuse: (token, cb) => - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "tokenuse", token: token @@ -792,12 +792,12 @@ module.exports = class TS3ClientQuery extends EventEmitter ### use: (schandlerid, cb) => retval = { } - @once "message.selected", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "message.selected", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "use", schandlerid: schandlerid - verifychannelpassword: (cb) => + verifychannelpassword: (cb) -> throw new Error "Not implemented yet" ### @@ -805,7 +805,7 @@ module.exports = class TS3ClientQuery extends EventEmitter incorrect. ### verifyserverpassword: (password, cb) => - @once "message.error", (args) => cb? checkError(args) + @once "message.error", (args) -> cb? checkError(args) @send "verifyserverpassword", password: password @@ -818,6 +818,6 @@ module.exports = class TS3ClientQuery extends EventEmitter ### whoami: (cb) => retval = { } - @once "vars", (args) => merge retval, args - @once "message.error", (args) => cb? checkError(args), retval + @once "vars", (args) -> merge retval, args + @once "message.error", (args) -> cb? checkError(args), retval @send "whoami" diff --git a/src/ts3settings.iced b/src/ts3settings.coffee similarity index 83% rename from src/ts3settings.iced rename to src/ts3settings.coffee index db03705..7cc5c5f 100644 --- a/src/ts3settings.iced +++ b/src/ts3settings.coffee @@ -1,17 +1,17 @@ -sqlite3 = require("sqlite3") #.verbose() -SQLite3Database = sqlite3.Database -path = require "path" -mkdirp = require "mkdirp" -SimpleIni = require "simple-ini" -fs = require "fs" -merge = require "merge" -getLogger = require "./logger" +import sqlite3, { Database as SQLite3Database } from 'sqlite3' +import path from 'path' +import mkdirp from 'mkdirp' +import SimpleIni from 'simple-ini' +import fs from 'fs' +import merge from 'merge' + +import getLogger from './logger' # some properties sugar from http://bl.ocks.org/joyrexus/65cb3780a24ecd50f6df Function::getter = (prop, get) -> - Object.defineProperty @prototype, prop, {get, configurable: yes} + Object.defineProperty @prototype, prop, {get, configurable: yes} Function::setter = (prop, set) -> - Object.defineProperty @prototype, prop, {set, configurable: yes} + Object.defineProperty @prototype, prop, {set, configurable: yes} module.exports = class SettingsFile db: null @@ -26,21 +26,28 @@ module.exports = class SettingsFile catch err throw new Error "Could not create TS3 config directory." - @getter "isInitialized", -> () => fs.existsSync(path.join(@configPath, "settings.db")) and fs.existsSync(path.join(@configPath, "ts3clientui_qt.secrets.conf")) - @getter "isReady", -> () => @db != null + @getter "isInitialized", -> + () => + fs.existsSync(path.join(@configPath, "settings.db")) and + fs.existsSync(path.join(@configPath, "ts3clientui_qt.secrets.conf")) + @getter "isReady", -> + () => + @db != null open: (cb) => # settings database @db = new SQLite3Database path.join(@configPath, "settings.db") await @db.serialize defer() - await @query "CREATE TABLE IF NOT EXISTS TS3Tables (key varchar NOT NULL UNIQUE,timestamp integer unsigned NOT NULL)", defer() + await @query "CREATE TABLE IF NOT EXISTS TS3Tables "+ + "(key varchar NOT NULL UNIQUE,timestamp integer unsigned NOT NULL)", + defer() # secrets file @identities = [] @defaultIdentity = null secretsPath = path.join(@configPath, "ts3clientui_qt.secrets.conf") if fs.existsSync(secretsPath) - secrets = new SimpleIni (() => fs.readFileSync(secretsPath, "utf-8")), + secrets = new SimpleIni (-> fs.readFileSync(secretsPath, "utf-8")), quotedValues: false for i in [1 .. secrets.Identities.size] @identities.push @@ -75,7 +82,8 @@ module.exports = class SettingsFile # Generate INI content await secrets.save defer(iniText) - fs.writeFileSync path.join(@configPath, "ts3clientui_qt.secrets.conf"), iniText + fs.writeFileSync path.join(@configPath, "ts3clientui_qt.secrets.conf"), + iniText @identities = null @defaultIdentity = null @@ -98,7 +106,9 @@ module.exports = class SettingsFile if not table throw new Error "Need table" - await @query "create table if not exists #{table} (timestamp integer unsigned NOT NULL, key varchar NOT NULL UNIQUE, value varchar)", defer() + await @query "create table if not exists #{table} "+ + "(timestamp integer unsigned NOT NULL, key varchar NOT NULL UNIQUE, "+ + "value varchar)", defer() if not key return @@ -112,11 +122,13 @@ module.exports = class SettingsFile timestamp = Math.round (new Date).getTime() / 1000 - stmt = @db.prepare "insert or replace into TS3Tables (key, timestamp) values (?, ?)" + stmt = @db.prepare "insert or replace into TS3Tables (key, timestamp) "+ + "values (?, ?)" stmt.run table, timestamp await stmt.finalize defer() - stmt = @db.prepare "insert or replace into #{table} (timestamp, key, value) values (?, ?, ?)" + stmt = @db.prepare "insert or replace into #{table} (timestamp, key, value) "+ + "values (?, ?, ?)" stmt.run timestamp, key, value await stmt.finalize defer() @@ -141,7 +153,7 @@ module.exports = class SettingsFile @log.info "Importing identity from #{identityFilePath}..." # open identity file - idFile = new SimpleIni (() => fs.readFileSync(identityFilePath, "utf-8")), + idFile = new SimpleIni (-> fs.readFileSync(identityFilePath, "utf-8")), quotedValues: true importedIdentity = {} for own k, v of idFile.Identity diff --git a/src/x11.iced b/src/x11.coffee similarity index 63% rename from src/x11.iced rename to src/x11.coffee index 948916f..6e8a91c 100644 --- a/src/x11.iced +++ b/src/x11.coffee @@ -1,16 +1,17 @@ -Sync = require "sync" +import Sync from 'sync' +import { spawn } from 'child_process' +import StreamSplitter from 'stream-splitter' -log = require("./logger")("X11tools") -spawn = require("child_process").spawn -services = require("./services") -StreamSplitter = require("stream-splitter") -require_bin = require("./require_bin") +import getLogger from './logger' +import services from './services' +import require_bin from './require_bin' +log = getLogger "X11tools" xdotoolBinPath = require_bin "xdotool", false # Just some tools to work with the X11 windows module.exports = - getWindowIdByProcessId: (pid, cb) => + getWindowIdByProcessId: (pid, cb) -> wid = null # Return null instantly if xdotool is not available @@ -18,21 +19,23 @@ module.exports = cb? new Error "xdotool is not available" return - # We provide --name due to the bug mentioned at https://github.com/jordansissel/xdotool/issues/14 - xdoproc = spawn xdotoolBinPath, [ "search", "--any", "--pid", pid, "--name", "xdosearch" ], + # We provide --name due to the bug mentioned at + # https://github.com/jordansissel/xdotool/issues/14 + xdoproc = spawn xdotoolBinPath, [ + "search", "--any", "--pid", pid, "--name", "xdosearch" ], env: DISPLAY: process.env.DISPLAY XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR stdoutTokenizer = xdoproc.stdout.pipe StreamSplitter "\n" - stdoutTokenizer.encoding = "utf8"; - stdoutTokenizer.on "token", (token) => + stdoutTokenizer.encoding = "utf8" + stdoutTokenizer.on "token", (token) -> token = token.trim() # get rid of \r newWid = parseInt(token) if newWid != 0 and wid == null wid = newWid stderrTokenizer = xdoproc.stderr.pipe StreamSplitter "\n" - stderrTokenizer.encoding = "utf8"; - stderrTokenizer.on "token", (token) => + stderrTokenizer.encoding = "utf8" + stderrTokenizer.on "token", (token) -> token = token.trim() # get rid of \r log.warn token await xdoproc.on "exit", defer(e) @@ -45,9 +48,10 @@ module.exports = cb? null, parseInt(wid) - getWindowIdByProcessIdSync: (pid) => Sync() => @getWindowIdByProcessId.sync @, pid + getWindowIdByProcessIdSync: (pid) -> + Sync() -> @getWindowIdByProcessId.sync @, pid - sendKeys: (wid, keys, cb) => + sendKeys: (wid, keys, cb) -> # Do not bother trying if xdotool is not available if not xdotoolBinPath? cb? new Error "xdotool not available." @@ -61,18 +65,24 @@ module.exports = cb? new Error "Could not start a window manager." return - xdoproc = spawn xdotoolBinPath, [ "windowactivate", "--sync", wid, "key", "--clearmodifiers", "--delay", "100" ].concat(keys), + xdoproc = spawn xdotoolBinPath, [ + "windowactivate", + "--sync", + wid, + "key", + "--clearmodifiers", + "--delay", "100" ].concat(keys), env: DISPLAY: process.env.DISPLAY XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR stdoutTokenizer = xdoproc.stdout.pipe StreamSplitter "\n" - stdoutTokenizer.encoding = "utf8"; - stdoutTokenizer.on "token", (token) => + stdoutTokenizer.encoding = "utf8" + stdoutTokenizer.on "token", (token) -> token = token.trim() # get rid of \r log.debug token stderrTokenizer = xdoproc.stderr.pipe StreamSplitter "\n" - stderrTokenizer.encoding = "utf8"; - stderrTokenizer.on "token", (token) => + stderrTokenizer.encoding = "utf8" + stderrTokenizer.on "token", (token) -> token = token.trim() # get rid of \r log.warn token await xdoproc.on "exit", defer(e) diff --git a/src/yarn.lock b/src/yarn.lock deleted file mode 100644 index 4e3074b..0000000 --- a/src/yarn.lock +++ /dev/null @@ -1,1457 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -abbrev@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" - -accepts@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" - dependencies: - mime-types "~2.1.11" - negotiator "0.6.1" - -ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi@^0.3.0, ansi@~0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21" - -aproba@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" - -are-we-there-yet@~1.0.0: - version "1.0.6" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz#a2d28c93102aa6cc96245a26cb954de06ec53f0c" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.0 || ^1.1.13" - -are-we-there-yet@~1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -async@^1.4.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - -async@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws4@^1.2.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" - -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - -bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - dependencies: - tweetnacl "^0.14.3" - -"binary@>= 0.3.0 < 1": - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - -bindings@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - -bluebird@^2.9.15: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -brace-expansion@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" - dependencies: - balanced-match "^0.4.1" - concat-map "0.0.1" - -buffer-shims@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - -camelcase@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - dependencies: - traverse ">=0.3.0 <0.4" - -cliui@^3.0.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -cmake-js@*: - version "3.4.1" - resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-3.4.1.tgz#29b57d6e5417b3a395e549b86cb9dc1f3cd97f90" - dependencies: - bluebird "^2.9.15" - debug "^2.1.3" - fs-extra "1" - is-iojs "^1.0.1" - lodash "^3.6.0" - memory-stream "0" - npmconf "^2.1.2" - npmlog "^1.2.0" - request "^2.54.0" - semver "^5.0.3" - splitargs "0" - tar "^1.0.3" - traceur "0.0.x" - unzip "^0.1.11" - url-join "0" - which "^1.0.9" - yargs "^3.6.0" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -colors@1.0.x: - version "1.0.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" - -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" - dependencies: - delayed-stream "~1.0.0" - -commander@2.9.x, commander@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - dependencies: - graceful-readlink ">= 1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -config-chain@~1.1.8: - version "1.1.11" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - -content-type@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - -cycle@1.0.x: - version "1.0.3" - resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -debug@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" - dependencies: - ms "0.7.2" - -debug@2.6.4, debug@^2.1.3, debug@^2.2.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.4.tgz#7586a9b3c39741c0282ae33445c4e8ac74734fe0" - dependencies: - ms "0.7.3" - -decamelize@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -deep-extend@~0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -depd@1.1.0, depd@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - -encodeurl@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - -etag@~1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" - -express@^4.13.3: - version "4.15.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.2.tgz#af107fc148504457f2dca9a6f2571d7129b97b35" - dependencies: - accepts "~1.3.3" - array-flatten "1.1.1" - content-disposition "0.5.2" - content-type "~1.0.2" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.1" - depd "~1.1.0" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.0" - finalhandler "~1.0.0" - fresh "0.5.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.1" - path-to-regexp "0.1.7" - proxy-addr "~1.1.3" - qs "6.4.0" - range-parser "~1.2.0" - send "0.15.1" - serve-static "1.12.1" - setprototypeof "1.0.3" - statuses "~1.3.1" - type-is "~1.6.14" - utils-merge "1.0.0" - vary "~1.1.0" - -extend@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" - -eyes@0.1.x: - version "0.1.8" - resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - -fibers@>=0.6: - version "1.0.15" - resolved "https://registry.yarnpkg.com/fibers/-/fibers-1.0.15.tgz#22f039c8f18b856190fbbe4decf056154c1eae9c" - -finalhandler@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.2.tgz#d0e36f9dbc557f2de14423df6261889e9d60c93a" - dependencies: - debug "2.6.4" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.1" - statuses "~1.3.1" - unpipe "~1.0.0" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" - -fresh@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" - -fs-extra@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fstream-ignore@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" - dependencies: - fstream "^1.0.0" - inherits "2" - minimatch "^3.0.0" - -"fstream@>= 0.1.30 < 1": - version "0.1.31" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-0.1.31.tgz#7337f058fbbbbefa8c9f561a28cab0849202c988" - dependencies: - graceful-fs "~3.0.2" - inherits "~2.0.0" - mkdirp "0.5" - rimraf "2" - -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -gauge@~1.2.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" - dependencies: - ansi "^0.3.0" - has-unicode "^2.0.0" - lodash.pad "^4.1.0" - lodash.padend "^4.1.0" - lodash.padstart "^4.1.0" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - dependencies: - assert-plus "^1.0.0" - -glob@5.0.x: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.5: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -graceful-fs@~3.0.2: - version "3.0.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" - dependencies: - natives "^1.1.0" - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -hashish@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/hashish/-/hashish-0.0.4.tgz#6d60bc6ffaf711b6afd60e426d077988014e6554" - dependencies: - traverse ">=0.2.4" - -hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - -http-errors@~1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" - dependencies: - depd "1.1.0" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -iced-coffee-script@^108.0.8: - version "108.0.11" - resolved "https://registry.yarnpkg.com/iced-coffee-script/-/iced-coffee-script-108.0.11.tgz#1d71ff93c9049728a6468385aa9bc891fd74c58f" - dependencies: - iced-runtime ">=0.0.1" - -iced-runtime@>=0.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/iced-runtime/-/iced-runtime-1.0.3.tgz#2d4f4fb999ab7aa5430b193c77a7fce4118319ce" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.3, inherits@~2.0.0, inherits@~2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@^1.2.0, ini@^1.3.0, ini@^1.3.4, ini@~1.3.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - -ipaddr.js@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" - -is-finite@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-iojs@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-iojs/-/is-iojs-1.1.0.tgz#4c11033b5d5d94d6eab3775dedc9be7d008325f1" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - -isstream@0.1.x, isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsprim@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" - dependencies: - assert-plus "1.0.0" - extsprintf "1.0.2" - json-schema "0.2.3" - verror "1.3.6" - -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - optionalDependencies: - graceful-fs "^4.1.9" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -lodash.pad@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70" - -lodash.padend@^4.1.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" - -lodash.padstart@^4.1.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" - -lodash@^3.6.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - -"match-stream@>= 0.0.2 < 1": - version "0.0.2" - resolved "https://registry.yarnpkg.com/match-stream/-/match-stream-0.0.2.tgz#99eb050093b34dffade421b9ac0b410a9cfa17cf" - dependencies: - buffers "~0.1.1" - readable-stream "~1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - -memory-stream@0: - version "0.0.3" - resolved "https://registry.yarnpkg.com/memory-stream/-/memory-stream-0.0.3.tgz#ebe8dd1c3b8bc38c0e7941e9ddd5aebe6b4de83f" - dependencies: - readable-stream "~1.0.26-2" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - -merge@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - -mime-db@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" - -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: - version "2.1.15" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" - dependencies: - mime-db "~1.27.0" - -mime@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" - -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8, minimist@~0.0.1: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -mkdirp@0.5, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - -ms@0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff" - -named-regexp@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/named-regexp/-/named-regexp-0.1.1.tgz#cd9c5383245fa5cbc712a73d669b1e4e2aef590d" - -nan@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" - -natives@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.0.tgz#e9ff841418a6b2ec7a495e939984f78f163e6e31" - -nconf@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/nconf/-/nconf-0.8.4.tgz#9502234f7ad6238cab7f92d7c068c20434d3ff93" - dependencies: - async "^1.4.0" - ini "^1.3.0" - secure-keys "^1.0.0" - yargs "^3.19.0" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - -node-pre-gyp@~0.6.31: - version "0.6.34" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" - dependencies: - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.0.2" - rc "^1.1.7" - request "^2.81.0" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -nopt@~3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" - -npm-path@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.3.tgz#15cff4e1c89a38da77f56f6055b24f975dfb2bbe" - dependencies: - which "^1.2.10" - -npm-which@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" - dependencies: - commander "^2.9.0" - npm-path "^2.0.2" - which "^1.2.10" - -npmconf@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/npmconf/-/npmconf-2.1.2.tgz#66606a4a736f1e77a059aa071a79c94ab781853a" - dependencies: - config-chain "~1.1.8" - inherits "~2.0.0" - ini "^1.2.0" - mkdirp "^0.5.0" - nopt "~3.0.1" - once "~1.3.0" - osenv "^0.1.0" - semver "2 || 3 || 4" - uid-number "0.0.5" - -npmlog@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-1.2.1.tgz#28e7be619609b53f7ad1dd300a10d64d716268b6" - dependencies: - ansi "~0.3.0" - are-we-there-yet "~1.0.0" - gauge "~1.2.0" - -npmlog@^4.0.2: - version "4.1.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -oauth-sign@~0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.3: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -once@~1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" - dependencies: - wrappy "1" - -optimist@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.0, osenv@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -"over@>= 0.0.5 < 1": - version "0.0.5" - resolved "https://registry.yarnpkg.com/over/-/over-0.0.5.tgz#f29852e70fd7e25f360e013a8ec44c82aedb5708" - -parse-duration@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/parse-duration/-/parse-duration-0.1.1.tgz#13114ddc9891c1ecd280036244554de43647a226" - -parse-ms@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" - -parseurl@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" - -password-generator@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/password-generator/-/password-generator-2.1.0.tgz#d7580432951b69851e3d8d0a98fb1dc60b5c56da" - dependencies: - optimist "0.6.1" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - -plur@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/plur/-/plur-1.0.0.tgz#db85c6814f5e5e5a3b49efc28d604fec62975156" - -pretty-ms@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-2.1.0.tgz#4257c256df3fb0b451d6affaab021884126981dc" - dependencies: - is-finite "^1.0.1" - parse-ms "^1.0.0" - plur "^1.0.0" - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - -proxy-addr@~1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" - dependencies: - forwarded "~0.1.0" - ipaddr.js "1.3.0" - -"pullstream@>= 0.4.1 < 1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/pullstream/-/pullstream-0.4.1.tgz#d6fb3bf5aed697e831150eb1002c25a3f8ae1314" - dependencies: - over ">= 0.0.5 < 1" - readable-stream "~1.0.31" - setimmediate ">= 1.0.2 < 2" - slice-stream ">= 1.0.0 < 2" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -qs@6.4.0, qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - -querystring@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -rc@^1.1.7: - version "1.2.1" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.6, readable-stream@^2.1.4: - version "2.2.9" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" - dependencies: - buffer-shims "~1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~1.0.0" - util-deprecate "~1.0.1" - -readable-stream@~1.0.0, readable-stream@~1.0.26-2, readable-stream@~1.0.31: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -request@^2.54.0, request@^2.61.0, request@^2.69.0, request@^2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - -rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" - dependencies: - glob "^7.0.5" - -rimraf@~2.2.6: - version "2.2.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" - -rsvp@^3.0.13: - version "3.5.0" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.5.0.tgz#a62c573a4ae4e1dfd0697ebc6242e79c681eaa34" - -safe-buffer@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" - -secure-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/secure-keys/-/secure-keys-1.0.0.tgz#f0c82d98a3b139a8776a8808050b824431087fca" - -"semver@2 || 3 || 4", semver@^4.3.3: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - -semver@^5.0.3, semver@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - -send@0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.1.tgz#8a02354c26e6f5cca700065f5f0cdeba90ec7b5f" - dependencies: - debug "2.6.1" - depd "~1.1.0" - destroy "~1.0.4" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.0" - fresh "0.5.0" - http-errors "~1.6.1" - mime "1.3.4" - ms "0.7.2" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.3.1" - -serve-static@1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.1.tgz#7443a965e3ced647aceb5639fa06bf4d1bbe0039" - dependencies: - encodeurl "~1.0.1" - escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.15.1" - -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -"setimmediate@>= 1.0.1 < 2", "setimmediate@>= 1.0.2 < 2": - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -simple-ini@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/simple-ini/-/simple-ini-1.0.4.tgz#a5b426643f52035d6c8b594959afbd4c6fcdb0f1" - -"slice-stream@>= 1.0.0 < 2": - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-stream/-/slice-stream-1.0.0.tgz#5b33bd66f013b1a7f86460b03d463dec39ad3ea0" - dependencies: - readable-stream "~1.0.31" - -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -source-map-support@~0.2.8: - version "0.2.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" - dependencies: - source-map "0.1.32" - -source-map@0.1.32: - version "0.1.32" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" - dependencies: - amdefine ">=0.0.4" - -splitargs@0: - version "0.0.7" - resolved "https://registry.yarnpkg.com/splitargs/-/splitargs-0.0.7.tgz#fe9f7ae657371b33b10cb80da143cf8249cf6b3b" - -sqlite3@^3.1.0: - version "3.1.8" - resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-3.1.8.tgz#4cbcf965d8b901d1b1015cbc7fc415aae157dfaa" - dependencies: - nan "~2.4.0" - node-pre-gyp "~0.6.31" - -sshpk@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -stack-trace@0.0.x: - version "0.0.9" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" - -"statuses@>= 1.3.1 < 2", statuses@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" - -stream-splitter@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/stream-splitter/-/stream-splitter-0.3.2.tgz#e2f28262e53c2d8bbc492e72f2960fc19857984d" - dependencies: - buffers "~0.1.1" - -streamify@~0.2.3: - version "0.2.8" - resolved "https://registry.yarnpkg.com/streamify/-/streamify-0.2.8.tgz#aa8133ecf41f72073dc956111ef23bae1cd0fbad" - dependencies: - hashish "~0.0.4" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string.prototype.startswith@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/string.prototype.startswith/-/string.prototype.startswith-0.2.0.tgz#da68982e353a4e9ac4a43b450a2045d1c445ae7b" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" - dependencies: - buffer-shims "~1.0.0" - -stringstream@~0.0.4: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -sync@0.2.x, sync@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/sync/-/sync-0.2.5.tgz#3910bb9b66abee56542e2e70f0ce549031252df6" - dependencies: - fibers ">=0.6" - -tar-pack@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" - dependencies: - debug "^2.2.0" - fstream "^1.0.10" - fstream-ignore "^1.0.5" - once "^1.3.3" - readable-stream "^2.1.4" - rimraf "^2.5.1" - tar "^2.2.1" - uid-number "^0.0.6" - -tar@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tar/-/tar-1.0.3.tgz#15bcdab244fa4add44e4244a0176edb8aa9a2b44" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -temp@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" - dependencies: - os-tmpdir "^1.0.0" - rimraf "~2.2.6" - -tough-cookie@~2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" - dependencies: - punycode "^1.4.1" - -traceur@0.0.x: - version "0.0.111" - resolved "https://registry.yarnpkg.com/traceur/-/traceur-0.0.111.tgz#c04de74d14696c3373427de4fc08ecaf913fc3a1" - dependencies: - commander "2.9.x" - glob "5.0.x" - rsvp "^3.0.13" - semver "^4.3.3" - source-map-support "~0.2.8" - -traverse@>=0.2.4: - version "0.6.6" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" - -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-is@~1.6.14: - version "1.6.15" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" - dependencies: - media-typer "0.3.0" - mime-types "~2.1.15" - -uid-number@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.5.tgz#5a3db23ef5dbd55b81fce0ec9a2ac6fccdebb81e" - -uid-number@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - -unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - -unzip@^0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/unzip/-/unzip-0.1.11.tgz#89749c63b058d7d90d619f86b98aa1535d3b97f0" - dependencies: - binary ">= 0.3.0 < 1" - fstream ">= 0.1.30 < 1" - match-stream ">= 0.0.2 < 1" - pullstream ">= 0.4.1 < 1" - readable-stream "~1.0.31" - setimmediate ">= 1.0.1 < 2" - -url-join@0: - version "0.0.1" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-0.0.1.tgz#1db48ad422d3402469a87f7d97bdebfe4fb1e3c8" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" - -uuid@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" - -valid-url@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" - -vary@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" - -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" - dependencies: - extsprintf "1.0.2" - -webchimera.js@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/webchimera.js/-/webchimera.js-0.2.7.tgz#b254e861723dc0c072308990c48577616cc1ac54" - dependencies: - bindings "~1.2.1" - cmake-js "*" - -which@^1.0.9, which@^1.1.2, which@^1.2.10: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" - dependencies: - string-width "^1.0.1" - -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - -winston@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119" - dependencies: - async "~1.0.0" - colors "1.0.x" - cycle "1.0.x" - eyes "0.1.x" - isstream "0.1.x" - stack-trace "0.0.x" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -"xvfb@git://github.com/icedream/node-xvfb.git": - version "0.2.3" - resolved "git://github.com/icedream/node-xvfb.git#76a183c3027542fbe36e11727a5f0a42bfde09df" - dependencies: - sync "0.2.x" - -y18n@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yargs@^3.19.0, yargs@^3.6.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - -youtube-dl@^1.10.5: - version "1.11.1" - resolved "https://registry.yarnpkg.com/youtube-dl/-/youtube-dl-1.11.1.tgz#c8e3b74951182dc5c58a0d1cd5be54b356271bd2" - dependencies: - mkdirp "^0.5.1" - request "^2.69.0" - streamify "~0.2.3"