123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- 'use strict';
- // Growl - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
- /**
- * Module dependencies.
- */
- const spawn = require('child_process').spawn;
- const fs = require('fs');
- const path = require('path');
- const os = require('os');
- const exists = fs.existsSync || path.existsSync;
- let cmd;
- function which(name) {
- const paths = process.env.PATH.split(':');
- let loc;
- for (let i = 0, len = paths.length; i < len; i += 1) {
- loc = path.join(paths[i], name);
- if (exists(loc)) return loc;
- }
- return false;
- }
- function setupCmd() {
- switch (os.type()) {
- case 'Darwin':
- if (which('terminal-notifier')) {
- cmd = {
- type: 'Darwin-NotificationCenter',
- pkg: 'terminal-notifier',
- msg: '-message',
- title: '-title',
- subtitle: '-subtitle',
- icon: '-appIcon',
- sound: '-sound',
- url: '-open',
- priority: {
- cmd: '-execute',
- range: [],
- },
- };
- } else {
- cmd = {
- type: 'Darwin-Growl',
- pkg: 'growlnotify',
- msg: '-m',
- sticky: '--sticky',
- url: '--url',
- priority: {
- cmd: '--priority',
- range: [
- -2,
- -1,
- 0,
- 1,
- 2,
- 'Very Low',
- 'Moderate',
- 'Normal',
- 'High',
- 'Emergency',
- ],
- },
- };
- }
- break;
- case 'Linux':
- if (which('growl')) {
- cmd = {
- type: 'Linux-Growl',
- pkg: 'growl',
- msg: '-m',
- title: '-title',
- subtitle: '-subtitle',
- host: {
- cmd: '-H',
- hostname: '192.168.33.1',
- },
- };
- } else {
- cmd = {
- type: 'Linux',
- pkg: 'notify-send',
- msg: '',
- sticky: '-t',
- icon: '-i',
- priority: {
- cmd: '-u',
- range: [
- 'low',
- 'normal',
- 'critical',
- ],
- },
- };
- }
- break;
- case 'Windows_NT':
- cmd = {
- type: 'Windows',
- pkg: 'growlnotify',
- msg: '',
- sticky: '/s:true',
- title: '/t:',
- icon: '/i:',
- url: '/cu:',
- priority: {
- cmd: '/p:',
- range: [
- -2,
- -1,
- 0,
- 1,
- 2,
- ],
- },
- };
- break;
- default:
- break;
- }
- }
- /**
- * Send growl notification _msg_ with _options_.
- *
- * Options:
- *
- * - title Notification title
- * - sticky Make the notification stick (defaults to false)
- * - priority Specify an int or named key (default is 0)
- * - name Application name (defaults to growlnotify)
- * - sound Sound efect ( in OSx defined in preferences -> sound -> effects)
- * works only in OSX > 10.8x
- * - image
- * - path to an icon sets --iconpath
- * - path to an image sets --image
- * - capitalized word sets --appIcon
- * - filename uses extname as --icon
- * - otherwise treated as --icon
- *
- * Examples:
- *
- * growl('New email')
- * growl('5 new emails', { title: 'Thunderbird' })
- * growl('5 new emails', { title: 'Thunderbird', sound: 'Purr' })
- * growl('Email sent', function(){
- * // ... notification sent
- * })
- *
- * @param {string} msg
- * @param {object} opts
- * @param {function} callback
- * @api public
- */
- function growl(msg, opts, callback) {
- let image;
- const options = opts || {};
- const fn = callback || function noop() {};
- setupCmd();
- if (options.exec) {
- cmd = {
- type: 'Custom',
- pkg: options.exec,
- range: [],
- };
- }
- // noop
- if (!cmd) {
- fn(new Error('growl not supported on this platform'));
- return;
- }
- const args = [cmd.pkg];
- // image
- if (image || options.image) {
- image = options.image;
- switch (cmd.type) {
- case 'Darwin-Growl': {
- let flag;
- const ext = path.extname(image).substr(1);
- flag = ext === 'icns' && 'iconpath';
- flag = flag || (/^[A-Z]/.test(image) && 'appIcon');
- flag = flag || (/^png|gif|jpe?g$/.test(ext) && 'image');
- flag = flag || (ext && (image = ext) && 'icon');
- flag = flag || 'icon';
- args.push(`--${flag}`, image);
- break;
- }
- case 'Darwin-NotificationCenter':
- args.push(cmd.icon, image);
- break;
- case 'Linux':
- args.push(cmd.icon, image);
- // libnotify defaults to sticky, set a hint for transient notifications
- if (!options.sticky) args.push('--hint=int:transient:1');
- break;
- case 'Windows':
- args.push(cmd.icon + image);
- break;
- default:
- break;
- }
- }
- // sticky
- if (options.sticky) args.push(cmd.sticky);
- if (options.sticky && cmd.type === 'Linux') args.push('0');
- // priority
- if (options.priority) {
- const priority = `${options.priority}`;
- const checkindexOf = cmd.priority.range.indexOf(priority);
- if (checkindexOf > -1) {
- args.push(cmd.priority, options.priority);
- }
- }
- // sound
- if (options.sound && cmd.type === 'Darwin-NotificationCenter') {
- args.push(cmd.sound, options.sound);
- }
- // name
- if (options.name && cmd.type === 'Darwin-Growl') {
- args.push('--name', options.name);
- }
- switch (cmd.type) {
- case 'Darwin-Growl':
- args.push(cmd.msg);
- args.push(msg.replace(/\\n/g, '\n'));
- if (options.title) args.push(options.title);
- if (options.url) {
- args.push(cmd.url);
- args.push(options.url);
- }
- break;
- case 'Darwin-NotificationCenter': {
- args.push(cmd.msg);
- const stringifiedMsg = msg;
- const escapedMsg = stringifiedMsg.replace(/\\n/g, '\n');
- args.push(escapedMsg);
- if (options.title) {
- args.push(cmd.title);
- args.push(options.title);
- }
- if (options.subtitle) {
- args.push(cmd.subtitle);
- args.push(options.subtitle);
- }
- if (options.url) {
- args.push(cmd.url);
- args.push(options.url);
- }
- break;
- }
- case 'Linux-Growl':
- args.push(cmd.msg);
- args.push(msg.replace(/\\n/g, '\n'));
- if (options.title) args.push(options.title);
- if (cmd.host) {
- args.push(cmd.host.cmd, cmd.host.hostname);
- }
- break;
- case 'Linux':
- if (options.title) args.push(options.title);
- args.push(msg.replace(/\\n/g, '\n'));
- break;
- case 'Windows':
- args.push(msg.replace(/\\n/g, '\n'));
- if (options.title) args.push(cmd.title + options.title);
- if (options.url) args.push(cmd.url + options.url);
- break;
- case 'Custom': {
- const customCmd = args[0];
- const message = options.title
- ? `${options.title}: ${msg}`
- : msg;
- let command = customCmd.replace(/(^|[^%])%s/g, `$1${message}`);
- const splitCmd = command.split(' ');
- if (splitCmd.length > 1) {
- command = splitCmd.shift();
- Array.prototype.push.apply(args, splitCmd);
- }
- if (customCmd.indexOf('%s') < 0) {
- args.push(message);
- }
- args[0] = command;
- break;
- }
- default:
- break;
- }
- const cmdToExec = args.shift();
- const child = spawn(cmdToExec, args);
- let stdout = '';
- let stderr = '';
- let error;
- const now = new Date();
- const timestamp = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}.${now.getMilliseconds()}`
- stderr += `[${timestamp}][node-growl] : Executed command '${cmdToExec}' with arguments '${args}'\n[stderr] : `;
- child.on('error', (err) => {
- console.error('An error occured.', err);
- error = err;
- });
- child.stdout.on('data', (data) => {
- stdout += data;
- });
- child.stderr.on('data', (data) => {
- stderr += data;
- });
- child.on('close', () => {
- if (typeof fn === 'function') {
- fn(error, stdout, stderr);
- }
- });
- }
- /**
- * Expose `growl`.
- */
- module.exports = growl;
|