Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of git://github.com/alloy/terminal-notifier

  • Loading branch information...
commit 25adc675eeee7ce31b26c03613cd0899972b75c3 2 parents 8c8656e + 60bd116
@foxycoder foxycoder authored
View
3  .gitignore
@@ -2,3 +2,6 @@
.DS_Store
DerivedData
xcuserdata
+Ruby/*.zip
+Ruby/*.gem
+Ruby/vendor
View
117 README.markdown
@@ -20,31 +20,116 @@ status of commands which are executed due to filesystem changes. (v3.0.0)
Prebuilt binaries, which are code-signed and ready to use, are available from
the [downloads section](https://github.com/alloy/terminal-notifier/downloads).
+Or if you want to use this from
+[Ruby](https://github.com/alloy/terminal-notifier/tree/master/Ruby), you can
+install it through RubyGems:
+
+```
+$ [sudo] gem install terminal-notifier
+```
+
## Usage
```
-$ ./terminal-notifier.app/Contents/MacOS/terminal-notifier group-ID title message [bundle-ID]
+$ ./terminal-notifier.app/Contents/MacOS/terminal-notifier -[message|group|list] [VALUE|ID|ID] [options]
```
-In order to use terminal-notifier, you have to call the binary _inside_ the app
-bundle.
+In order to use terminal-notifier, you have to call the binary _inside_ the
+application bundle.
+
+The Ruby gem, which wraps this tool, _does_ have a bin wrapper. If installed
+you can simply do:
+
+```
+$ terminal-notifier -[message|group|list] [VALUE|ID|ID] [options]
+```
+
+This will obviously be a bit slower than using the tool without the wrapper.
+
+
+#### Options
+
+At a minimum, you have to specify either the `-message` , the `-remove`
+option or the `-list` option.
+
+-------------------------------------------------------------------------------
+
+`-message VALUE` **[required]**
+
+The message body of the notification.
+
+-------------------------------------------------------------------------------
+
+`-title VALUE`
+
+The title of the notification. This defaults to ‘Terminal’.
+
+-------------------------------------------------------------------------------
+
+`-subtitle VALUE`
+
+The subtitle of the notification.
+
+-------------------------------------------------------------------------------
+
+`-group ID`
+
+Specifies the ‘group’ a notification belongs to. For any ‘group’ only _one_
+notification will ever be shown, replacing previously posted notifications.
+
+A notification can be explicitely removed with the `-remove` option, describe
+below.
+
+Examples are:
+
+* The sender’s name to scope the notifications by tool.
+* The sender’s process ID to scope the notifications by a unique process.
+* The current working directory to scope notifications by project.
+
+-------------------------------------------------------------------------------
+
+`-remove ID` **[required]**
+
+Removes a notification that was previously sent with the specified ‘group’ ID,
+if one exists. If used with the special group "ALL", all message are removed.
+
+-------------------------------------------------------------------------------
+
+`-list ID` **[required]**
+
+Lists details about the specified ‘group’ ID. If used with the special group
+"ALL", details about all currently active messages are displayed.
+
+The output of this command is tab-separated, which makes it easy to parse.
+
+-------------------------------------------------------------------------------
+
+`-activate ID`
+
+Specifies which application should be activated when the user clicks the
+notification.
+
+You can find the bundle identifier of an application in its `Info.plist` file
+_inside_ the application bundle.
+
+Examples are:
+
+* `com.apple.Terminal` to activate Terminal.app
+* `com.apple.Safari` to activate Safari.app
+
+-------------------------------------------------------------------------------
+
+`-open URL`
+
+Specifies a resource to be opened when the user clicks the notification. This
+can be a web or file URL, or any custom URL scheme.
-The first argument specifies the ‘group’ a notification belongs to. For
-any ‘group’ only _one_ notification will ever be shown, replacing
-previously posted notifications. Examples are: the sender’s process ID to
-scope the notifications by a unique process, or the current working directory
-to scope notifications by a project.
+-------------------------------------------------------------------------------
-The second and third arguments describe the notification itself and are its
-‘title’ and ‘message’ respectively. For example, to communicate the sender of
-a notification to the user, you could specify the sender’s name as the title.
+`-execute COMMAND`
-The fourth and last argument is an optional one. It specifies which application
-should be activated when the user clicks the notification. By default this will
-activate Terminal.app, to launch another application instead specify the
-application’s bundle identifier. For example, to launch Safari.app use:
-`com.apple.Safari`.
+Specifies a shell command to run when the user clicks the notification.
## License
View
2  Ruby/Gemfile
@@ -0,0 +1,2 @@
+source :rubygems
+gemspec
View
23 Ruby/Gemfile.lock
@@ -0,0 +1,23 @@
+PATH
+ remote: .
+ specs:
+ terminal-notifier (1.4.1)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ bacon (1.1.0)
+ metaclass (0.0.1)
+ mocha (0.11.4)
+ metaclass (~> 0.0.1)
+ mocha-on-bacon (0.2.0)
+ mocha (>= 0.9.8)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bacon
+ mocha
+ mocha-on-bacon
+ terminal-notifier!
View
23 Ruby/LICENSE
@@ -0,0 +1,23 @@
+All the works are available under the MIT license. **Except** for
+‘Terminal.icns’, which is a copy of Apple’s Terminal.app icon and as such is
+copyright of Apple.
+
+Copyright (C) 2012 Eloy Durán <eloy.de.enige@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
View
46 Ruby/README.markdown
@@ -0,0 +1,46 @@
+# TerminalNotifier
+
+A simple Ruby wrapper around the [`terminal-notifier`][HOMEPAGE] command-line
+tool, which allows you to send User Notifications to the Notification Center on
+Mac OS X 10.8, or higher.
+
+
+## Installation
+
+```
+$ gem install terminal-notifier
+```
+
+
+## Usage
+
+For full information on all the options, see the tool’s [README][README].
+
+Examples are:
+
+```ruby
+TerminalNotifier.notify('Hello World')
+TerminalNotifier.notify('Hello World', :title => 'Ruby', :subtitle => 'Programming Language')
+TerminalNotifier.notify('Hello World', :activate => 'com.apple.Safari')
+TerminalNotifier.notify('Hello World', :open => 'http://twitter.com/alloy')
+TerminalNotifier.notify('Hello World', :execute => 'say "OMG"')
+TerminalNotifier.notify('Hello World', :group => Process.pid)
+
+TerminalNotifier.remove(Process.pid)
+
+TerminalNotifier.list(Process.pid)
+TerminalNotifier.list
+```
+
+
+## License
+
+All the works are available under the MIT license. **Except** for
+‘Terminal.icns’, which is a copy of Apple’s Terminal.app icon and as such is
+copyright of Apple.
+
+See [LICENSE][LICENSE] for details.
+
+[HOMEPAGE]: https://github.com/alloy/terminal-notifier
+[README]: https://github.com/alloy/terminal-notifier/blob/master/README.markdown
+[LICENSE]: https://github.com/alloy/terminal-notifier/blob/master/Ruby/LICENSE
View
43 Ruby/Rakefile
@@ -0,0 +1,43 @@
+def version
+ @version ||= begin
+ plist = File.expand_path('../../Terminal Notifier/Terminal Notifier-Info.plist', __FILE__)
+ `/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' '#{plist}'`.strip
+ end
+end
+
+def filename
+ "terminal-notifier_#{version}"
+end
+
+def zipfile
+ "#{filename}.zip"
+end
+
+task :clean do
+ rm zipfile
+ rm_rf "vendor"
+end
+
+task :update_build do
+ unless File.exist?(zipfile)
+ sh "curl -O 'http://cloud.github.com/downloads/alloy/terminal-notifier/terminal-notifier_#{version}.zip'"
+ end
+
+ rm_rf "vendor"
+ mkdir "vendor"
+
+ sh "unzip -o -d vendor #{zipfile}"
+ mv "vendor/#{filename}", "vendor/terminal-notifier"
+end
+
+desc "Build gem"
+task :gem => :update_build do
+ sh "gem build terminal-notifier.gemspec"
+end
+
+desc "Run specs"
+task :spec do
+ sh "bundle exec ruby spec/terminal-notifier_spec.rb"
+end
+
+task :default => :spec
View
9 Ruby/bin/terminal-notifier
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+
+if $0 == __FILE__
+ $:.unshift File.expand_path('../../lib', __FILE__)
+end
+
+require 'terminal-notifier'
+
+exec TerminalNotifier::BIN_PATH, *ARGV
View
88 Ruby/lib/terminal-notifier.rb
@@ -0,0 +1,88 @@
+module TerminalNotifier
+ BIN_PATH = File.expand_path('../../vendor/terminal-notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier', __FILE__)
+
+ # Returns wether or not the current platform is Mac OS X 10.8, or higher.
+ def self.available?
+ if @available.nil?
+ @available = `uname`.strip == 'Darwin' && `sw_vers -productVersion`.strip >= '10.8'
+ end
+ @available
+ end
+
+ def self.execute(verbose, options)
+ if available?
+ command = [BIN_PATH, *options.map { |k,v| ["-#{k}", v.to_s] }.flatten]
+ if RUBY_VERSION < '1.9'
+ require 'shellwords'
+ command = Shellwords.shelljoin(command)
+ end
+ result = ''
+ IO.popen(command) do |stdout|
+ output = stdout.read
+ STDOUT.print output if verbose
+ result << output
+ end
+ result
+ else
+ raise "terminal-notifier is only supported on Mac OS X 10.8, or higher."
+ end
+ end
+
+ # Sends a User Notification and returns wether or not it was a success.
+ #
+ # The available options are `:title`, `:group`, `:activate`, `:open`, and
+ # `:execute`. For a description of each option see:
+ #
+ # https://github.com/alloy/terminal-notifier/blob/master/README.markdown
+ #
+ # Examples are:
+ #
+ # TerminalNotifier.notify('Hello World')
+ # TerminalNotifier.notify('Hello World', :title => 'Ruby')
+ # TerminalNotifier.notify('Hello World', :group => Process.pid)
+ # TerminalNotifier.notify('Hello World', :activate => 'com.apple.Safari')
+ # TerminalNotifier.notify('Hello World', :open => 'http://twitter.com/alloy')
+ # TerminalNotifier.notify('Hello World', :execute => 'say "OMG"')
+ #
+ # Raises if not supported on the current platform.
+ def notify(message, options = {}, verbose = false)
+ TerminalNotifier.execute(verbose, options.merge(:message => message))
+ $?.success?
+ end
+ module_function :notify
+
+ # Removes a notification that was previously sent with the specified
+ # ‘group’ ID, if one exists.
+ #
+ # If no ‘group’ ID is given, all notifications are removed.
+ def remove(group = 'ALL', verbose = false)
+ TerminalNotifier.execute(verbose, :remove => group)
+ $?.success?
+ end
+ module_function :remove
+
+ LIST_FIELDS = [:group, :title, :subtitle, :message, :delivered_at].freeze
+
+ # If a ‘group’ ID is given, and a notification for that group exists,
+ # returns a hash with details about the notification.
+ #
+ # If no ‘group’ ID is given, an array of hashes describing all
+ # notifications.
+ #
+ # If no information is available this will return `nil`.
+ def list(group = 'ALL', verbose = false)
+ output = TerminalNotifier.execute(verbose, :list => group)
+ return if output.strip.empty?
+
+ require 'time'
+ notifications = output.split("\n")[1..-1].map do |line|
+ LIST_FIELDS.zip(line.split("\t")).inject({}) do |hash, (key, value)|
+ hash[key] = key == :delivered_at ? Time.parse(value) : (value unless value == '(null)')
+ hash
+ end
+ end
+
+ group == 'ALL' ? notifications : notifications.first
+ end
+ module_function :list
+end
View
75 Ruby/spec/terminal-notifier_spec.rb
@@ -0,0 +1,75 @@
+require 'rubygems'
+require 'bacon'
+require 'mocha'
+require 'mocha-on-bacon'
+
+Bacon.summary_at_exit
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+require 'terminal-notifier'
+
+describe "TerminalNotifier" do
+ it "executes the tool with the given options" do
+ command = [TerminalNotifier::BIN_PATH, '-message', 'ZOMG']
+ if RUBY_VERSION < '1.9'
+ require 'shellwords'
+ command = Shellwords.shelljoin(command)
+ end
+ IO.expects(:popen).with(command).yields(StringIO.new('output'))
+ TerminalNotifier.execute(false, :message => 'ZOMG')
+ end
+
+ it "returns the result output of the command" do
+ TerminalNotifier.execute(false, 'help' => '').should == `'#{TerminalNotifier::BIN_PATH}' -help`
+ end
+
+ it "sends a notification" do
+ TerminalNotifier.expects(:execute).with(false, :message => 'ZOMG', :group => 'important stuff')
+ TerminalNotifier.notify('ZOMG', :group => 'important stuff')
+ end
+
+ it "removes a notification" do
+ TerminalNotifier.expects(:execute).with(false, :remove => 'important stuff')
+ TerminalNotifier.remove('important stuff')
+ end
+
+ it "by default removes all the notifications" do
+ TerminalNotifier.expects(:execute).with(false, :remove => 'ALL')
+ TerminalNotifier.remove
+ end
+
+ it "returns `nil` if no notification was found to list info for" do
+ TerminalNotifier.expects(:execute).with(false, :list => 'important stuff').returns('')
+ TerminalNotifier.list('important stuff').should == nil
+ end
+
+ it "returns info about a notification posted in a specific group" do
+ TerminalNotifier.expects(:execute).with(false, :list => 'important stuff').
+ returns("GroupID\tTitle\tSubtitle\tMessage\tDelivered At\n" \
+ "important stuff\tTerminal\t(null)\tExecute: rake spec\t2012-08-06 19:45:30 +0000")
+ TerminalNotifier.list('important stuff').should == {
+ :group => 'important stuff',
+ :title => 'Terminal', :subtitle => nil, :message => 'Execute: rake spec',
+ :delivered_at => Time.parse('2012-08-06 19:45:30 +0000')
+ }
+ end
+
+ it "by default returns a list of all notification" do
+ TerminalNotifier.expects(:execute).with(false, :list => 'ALL').
+ returns("GroupID\tTitle\tSubtitle\tMessage\tDelivered At\n" \
+ "important stuff\tTerminal\t(null)\tExecute: rake spec\t2012-08-06 19:45:30 +0000\n" \
+ "(null)\t(null)\tSubtle\tBe subtle!\t2012-08-07 19:45:30 +0000")
+ TerminalNotifier.list.should == [
+ {
+ :group => 'important stuff',
+ :title => 'Terminal', :subtitle => nil, :message => 'Execute: rake spec',
+ :delivered_at => Time.parse('2012-08-06 19:45:30 +0000')
+ },
+ {
+ :group => nil,
+ :title => nil, :subtitle => 'Subtle', :message => 'Be subtle!',
+ :delivered_at => Time.parse('2012-08-07 19:45:30 +0000')
+ }
+ ]
+ end
+end
View
22 Ruby/terminal-notifier.gemspec
@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+plist = File.expand_path('../../Terminal Notifier/Terminal Notifier-Info.plist', __FILE__)
+version = `/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' '#{plist}'`.strip
+
+Gem::Specification.new do |gem|
+ gem.name = "terminal-notifier"
+ gem.version = version
+ gem.summary = 'Send User Notifications on Mac OS X 10.8.'
+ gem.authors = ["Eloy Duran"]
+ gem.email = ["eloy.de.enige@gmail.com"]
+ gem.homepage = 'https://github.com/alloy/terminal-notifier'
+
+ gem.executables = ['terminal-notifier']
+ gem.files = ['bin/terminal-notifier', 'lib/terminal-notifier.rb'] + Dir.glob('vendor/terminal-notifier/**/*')
+ gem.require_paths = ['lib']
+
+ gem.extra_rdoc_files = ['README.markdown']
+
+ gem.add_development_dependency 'bacon'
+ gem.add_development_dependency 'mocha'
+ gem.add_development_dependency 'mocha-on-bacon'
+end
View
38 Terminal Notifier.xcodeproj/xcshareddata/xcschemes/Terminal Notifier.xcscheme
@@ -59,21 +59,45 @@
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
- argument = "12345"
- isEnabled = "YES">
+ argument = "-group abcde"
+ isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
- argument = "Kicker"
- isEnabled = "YES">
+ argument = "-list ALL"
+ isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
- argument = "&apos;Execute: rake spec&apos;"
- isEnabled = "YES">
+ argument = "-list abcde"
+ isEnabled = "NO">
+ </CommandLineArgument>
+ <CommandLineArgument
+ argument = "-remove abcde"
+ isEnabled = "NO">
+ </CommandLineArgument>
+ <CommandLineArgument
+ argument = "-open &apos;http://google.com&apos;"
+ isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
- argument = "com.apple.Safari"
+ argument = "-execute &apos;say &quot;OMG&quot;&apos;"
+ isEnabled = "NO">
+ </CommandLineArgument>
+ <CommandLineArgument
+ argument = "-execute &apos;ls -l&apos;"
+ isEnabled = "NO">
+ </CommandLineArgument>
+ <CommandLineArgument
+ argument = "-title Kicker"
+ isEnabled = "NO">
+ </CommandLineArgument>
+ <CommandLineArgument
+ argument = "-message &apos;Execute: rake spec&apos;"
isEnabled = "YES">
</CommandLineArgument>
+ <CommandLineArgument
+ argument = "-activate com.apple.Safari"
+ isEnabled = "NO">
+ </CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
View
202 Terminal Notifier/AppDelegate.m
@@ -1,8 +1,50 @@
#import "AppDelegate.h"
#import <ScriptingBridge/ScriptingBridge.h>
+@interface NSUserDefaults (Subscript)
+@end
+
+@implementation NSUserDefaults (Subscript)
+- (id)objectForKeyedSubscript:(id)key;
+{
+ return [self objectForKey:key];
+}
+@end
+
+
@implementation AppDelegate
+- (void)printHelpBanner;
+{
+ const char *appName = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String];
+ const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];
+ printf("%s (%s) is a command-line tool to send OS X User Notifications.\n" \
+ "\n" \
+ "Usage: %s -[message|list|remove] [VALUE|ID|ID] [options]\n" \
+ "\n" \
+ " Either of these is required:\n" \
+ "\n" \
+ " -message VALUE The notification message.\n" \
+ " -remove ID Removes a notification with the specified ‘group’ ID.\n" \
+ " -list ID If the specified ‘group’ ID exists show when it was delivered,\n" \
+ " or use ‘ALL’ as ID to see all notifications.\n" \
+ " The output is a tab-separated list.\n"
+ "\n" \
+ " Optional:\n" \
+ "\n" \
+ " -title VALUE The notification title. Defaults to ‘Terminal’.\n" \
+ " -subtitle VALUE The notification subtitle.\n" \
+ " -group ID A string which identifies the group the notifications belong to.\n" \
+ " Old notifications with the same ID will be removed.\n" \
+ " -activate ID The bundle identifier of the application to activate when the user clicks the notification.\n" \
+ " -open URL The URL of a resource to open when the user clicks the notification.\n" \
+ " -execute COMMAND A shell command to perform when the user clicks the notification.\n" \
+ "\n" \
+ "When the user activates a notification, the results are logged to the system logs.\n" \
+ "Use Console.app to view these logs.\n",
+ appName, appVersion, appName);
+}
+
- (void)applicationDidFinishLaunching:(NSNotification *)notification;
{
NSUserNotification *userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
@@ -10,89 +52,161 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification;
[self userActivatedNotification:userNotification];
} else {
- NSArray *args = [[NSProcessInfo processInfo] arguments];
-
- if ([args count] < 4 || [args count] > 5) {
- const char *appName = [[args[0] lastPathComponent] UTF8String];
- const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];
- printf("%s (%s) is a command-line tool to send OS X User Notifications.\n" \
- "\n" \
- "Usage: %s group-ID title message [bundle-ID]\n" \
- "\n" \
- " group-ID\tA string which identifies the group the notifications belong to.\n" \
- " \tOld notifications with the same ID will be removed.\n" \
- " title\tThe notification title.\n" \
- " message\tThe notification message.\n" \
- " bundle-ID\tThe bundle identifier of the application to activate when the user\n" \
- " \tactivates (clicks) a notification. Defaults to `com.apple.Terminal'.\n" \
- "\n" \
- "When the user activates a notification, the results are logged to the system logs.\n" \
- "Use Console.app to view these logs.\n",
- appName, appVersion, appName);
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ NSString *subtitle = defaults[@"subtitle"];
+ NSString *message = defaults[@"message"];
+ NSString *remove = defaults[@"remove"];
+ NSString *list = defaults[@"list"];
+ if (message == nil && remove == nil && list == nil) {
+ [self printHelpBanner];
exit(1);
}
- NSString *bundleID = [args count] == 5 ? args[4] : @"com.apple.Terminal";
+ if (list) {
+ [self listNotificationWithGroupID:list];
+ exit(0);
+ }
+
+ if (remove) {
+ [self removeNotificationWithGroupID:remove];
+ if (message == nil) exit(0);
+ }
- [self deliverNotificationForGroupID:args[1]
- title:args[2]
- message:args[3]
- activateAppWithBundleIdentifier:bundleID];
+ if (message) {
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ if (defaults[@"activate"]) options[@"bundleID"] = defaults[@"activate"];
+ if (defaults[@"group"]) options[@"groupID"] = defaults[@"group"];
+ if (defaults[@"execute"]) options[@"command"] = defaults[@"execute"];
+ if (defaults[@"open"]) options[@"open"] = defaults[@"open"];
+
+ [self deliverNotificationWithTitle:defaults[@"title"] ?: @"Terminal"
+ subtitle:subtitle
+ message:message
+ options:options];
+ }
}
}
-- (void)deliverNotificationForGroupID:(NSString *)groupID
- title:(NSString *)title
- message:(NSString *)message
- activateAppWithBundleIdentifier:(NSString *)bundleID;
+- (void)deliverNotificationWithTitle:(NSString *)title
+ subtitle:(NSString *)subtitle
+ message:(NSString *)message
+ options:(NSDictionary *)options;
{
+ // First remove earlier notification with the same group ID.
+ if (options[@"groupID"]) [self removeNotificationWithGroupID:options[@"groupID"]];
+
+ NSUserNotification *userNotification = [NSUserNotification new];
+ userNotification.title = title;
+ userNotification.subtitle = subtitle;
+ userNotification.informativeText = message;
+ userNotification.userInfo = options;
+
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
- NSUserNotification *userNotification = nil;
+ center.delegate = self;
+ [center scheduleNotification:userNotification];
+}
- // First remove earlier notification with the same group ID.
- for (userNotification in center.deliveredNotifications) {
- if ([userNotification.userInfo[@"groupID"] isEqualToString:groupID]) {
+- (void)removeNotificationWithGroupID:(NSString *)groupID;
+{
+ NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
+ for (NSUserNotification *userNotification in center.deliveredNotifications) {
+ if ([@"ALL" isEqualToString:groupID] || [userNotification.userInfo[@"groupID"] isEqualToString:groupID]) {
NSString *deliveredAt = [userNotification.actualDeliveryDate description];
- printf("* Removing previous notification, which was delivered on: %s\n", [deliveredAt UTF8String]);
+ printf("* Removing previously sent notification, which was sent on: %s\n", [deliveredAt UTF8String]);
[center removeDeliveredNotification:userNotification];
- break;
}
}
+}
- // Now create and deliver the new notification
- userNotification = [NSUserNotification new];
- userNotification.title = title;
- userNotification.informativeText = message;
- userNotification.userInfo = @{ @"groupID":groupID, @"bundleID":bundleID };
+- (void)listNotificationWithGroupID:(NSString *)listGroupID;
+{
+ NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
- center.delegate = self;
- [center scheduleNotification:userNotification];
+ NSMutableArray *lines = [NSMutableArray array];
+ for (NSUserNotification *userNotification in center.deliveredNotifications) {
+ NSString *deliveredgroupID = userNotification.userInfo[@"groupID"];
+ NSString *title = userNotification.title;
+ NSString *subtitle = userNotification.subtitle;
+ NSString *message = userNotification.informativeText;
+ NSString *deliveredAt = [userNotification.actualDeliveryDate description];
+ if ([@"ALL" isEqualToString:listGroupID] || [deliveredgroupID isEqualToString:listGroupID]) {
+ [lines addObject:[NSString stringWithFormat:@"%@\t%@\t%@\t%@\t%@", deliveredgroupID, title, subtitle, message, deliveredAt]];
+ }
+ }
+
+ if (lines.count > 0) {
+ printf("GroupID\tTitle\tSubtitle\tMessage\tDelivered At\n");
+ for (NSString *line in lines) {
+ printf("%s\n", [line UTF8String]);
+ }
+ }
}
+
- (void)userActivatedNotification:(NSUserNotification *)userNotification;
{
[[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:userNotification];
- NSString *groupID = userNotification.userInfo[@"groupID"];
+ NSString *groupID = userNotification.userInfo[@"groupID"];
NSString *bundleID = userNotification.userInfo[@"bundleID"];
+ NSString *command = userNotification.userInfo[@"command"];
+ NSString *open = userNotification.userInfo[@"open"];
NSLog(@"User activated notification:");
NSLog(@" group ID: %@", groupID);
NSLog(@" title: %@", userNotification.title);
+ NSLog(@" subtitle: %@", userNotification.subtitle);
NSLog(@" message: %@", userNotification.informativeText);
NSLog(@"bundle ID: %@", bundleID);
+ NSLog(@" command: %@", command);
+ NSLog(@" open: %@", open);
+
+ BOOL success = YES;
+ // TODO this loses NO if a consecutive call does succeed
+ if (bundleID) success = [self activateAppWithBundleID:bundleID] && success;
+ if (command) success = [self executeShellCommand:command] && success;
+ if (open) success = [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:open]] && success;
+ exit(success ? 0 : 1);
+}
+
+- (BOOL)activateAppWithBundleID:(NSString *)bundleID;
+{
id app = [SBApplication applicationWithBundleIdentifier:bundleID];
if (app) {
[app activate];
- exit(0);
+ return YES;
} else {
NSLog(@"Unable to find an application with the specified bundle indentifier.");
- exit(1);
+ return NO;
}
}
+- (BOOL)executeShellCommand:(NSString *)command;
+{
+ NSPipe *pipe = [NSPipe pipe];
+ NSFileHandle *fileHandle = [pipe fileHandleForReading];
+
+ NSTask *task = [NSTask new];
+ task.launchPath = @"/bin/sh";
+ task.arguments = @[@"-c", command];
+ task.standardOutput = pipe;
+ task.standardError = pipe;
+ [task launch];
+
+ NSData *data = nil;
+ NSMutableData *accumulatedData = [NSMutableData data];
+ while ((data = [fileHandle availableData]) && [data length]) {
+ [accumulatedData appendData:data];
+ }
+
+ [task waitUntilExit];
+ NSLog(@"command output:\n%@", [[NSString alloc] initWithData:accumulatedData encoding:NSUTF8StringEncoding]);
+ return [task terminationStatus] == 0;
+}
+
// TODO is this really needed?
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)userNotification;
View
4 Terminal Notifier/Terminal Notifier-Info.plist
@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>1.1.0</string>
+ <string>1.4.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>1</string>
+ <string>7</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>LSUIElement</key>
Please sign in to comment.
Something went wrong with that request. Please try again.