terminal-notifier 1.7.2 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +1 -1
- data/lib/terminal-notifier.rb +5 -4
- data/vendor/terminal-notifier +0 -0
- metadata +6 -44
- data/vendor/terminal-notifier/LICENSE.md +0 -9
- data/vendor/terminal-notifier/README.markdown +0 -301
- data/vendor/terminal-notifier/Ruby/Gemfile +0 -2
- data/vendor/terminal-notifier/Ruby/Gemfile.lock +0 -23
- data/vendor/terminal-notifier/Ruby/LICENSE +0 -24
- data/vendor/terminal-notifier/Ruby/README.markdown +0 -48
- data/vendor/terminal-notifier/Ruby/Rakefile +0 -48
- data/vendor/terminal-notifier/Ruby/bin/terminal-notifier +0 -13
- data/vendor/terminal-notifier/Ruby/lib/terminal-notifier.rb +0 -92
- data/vendor/terminal-notifier/Ruby/spec/terminal-notifier_spec.rb +0 -79
- data/vendor/terminal-notifier/Ruby/terminal-notifier.gemspec +0 -26
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.pbxproj +0 -315
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.xcworkspace/xcshareddata/Terminal Notifier.xccheckout +0 -41
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/xcshareddata/xcschemes/Terminal Notifier.xcscheme +0 -140
- data/vendor/terminal-notifier/Terminal Notifier/AppDelegate.h +0 -4
- data/vendor/terminal-notifier/Terminal Notifier/AppDelegate.m +0 -594
- data/vendor/terminal-notifier/Terminal Notifier/Terminal Notifier-Info.plist +0 -43
- data/vendor/terminal-notifier/Terminal Notifier/Terminal Notifier-Prefix.pch +0 -7
- data/vendor/terminal-notifier/Terminal Notifier/en.lproj/Credits.rtf +0 -29
- data/vendor/terminal-notifier/Terminal Notifier/en.lproj/InfoPlist.strings +0 -2
- data/vendor/terminal-notifier/Terminal Notifier/en.lproj/MainMenu.xib +0 -3387
- data/vendor/terminal-notifier/Terminal Notifier/main.m +0 -6
- data/vendor/terminal-notifier/Terminal.icns +0 -0
- data/vendor/terminal-notifier/assets/Example_1.png +0 -0
- data/vendor/terminal-notifier/assets/Example_2.png +0 -0
- data/vendor/terminal-notifier/assets/Example_3.png +0 -0
- data/vendor/terminal-notifier/assets/Example_4.png +0 -0
- data/vendor/terminal-notifier/assets/Example_5.png +0 -0
- data/vendor/terminal-notifier/assets/System_prefs.png +0 -0
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/Info.plist +0 -63
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier +0 -0
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/PkgInfo +0 -1
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/Terminal.icns +0 -0
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/en.lproj/Credits.rtf +0 -29
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
@@ -1,41 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
-
<plist version="1.0">
|
4
|
-
<dict>
|
5
|
-
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
6
|
-
<false/>
|
7
|
-
<key>IDESourceControlProjectIdentifier</key>
|
8
|
-
<string>A55309C1-2ABD-460A-B080-5A322DCAF4B5</string>
|
9
|
-
<key>IDESourceControlProjectName</key>
|
10
|
-
<string>Terminal Notifier</string>
|
11
|
-
<key>IDESourceControlProjectOriginsDictionary</key>
|
12
|
-
<dict>
|
13
|
-
<key>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</key>
|
14
|
-
<string>github.com:alloy/terminal-notifier.git</string>
|
15
|
-
</dict>
|
16
|
-
<key>IDESourceControlProjectPath</key>
|
17
|
-
<string>Terminal Notifier.xcodeproj</string>
|
18
|
-
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
19
|
-
<dict>
|
20
|
-
<key>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</key>
|
21
|
-
<string>../..</string>
|
22
|
-
</dict>
|
23
|
-
<key>IDESourceControlProjectURL</key>
|
24
|
-
<string>github.com:alloy/terminal-notifier.git</string>
|
25
|
-
<key>IDESourceControlProjectVersion</key>
|
26
|
-
<integer>111</integer>
|
27
|
-
<key>IDESourceControlProjectWCCIdentifier</key>
|
28
|
-
<string>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</string>
|
29
|
-
<key>IDESourceControlProjectWCConfigurations</key>
|
30
|
-
<array>
|
31
|
-
<dict>
|
32
|
-
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
33
|
-
<string>public.vcs.git</string>
|
34
|
-
<key>IDESourceControlWCCIdentifierKey</key>
|
35
|
-
<string>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</string>
|
36
|
-
<key>IDESourceControlWCCName</key>
|
37
|
-
<string>terminal-notifier</string>
|
38
|
-
</dict>
|
39
|
-
</array>
|
40
|
-
</dict>
|
41
|
-
</plist>
|
@@ -1,140 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<Scheme
|
3
|
-
LastUpgradeVersion = "0440"
|
4
|
-
version = "1.3">
|
5
|
-
<BuildAction
|
6
|
-
parallelizeBuildables = "YES"
|
7
|
-
buildImplicitDependencies = "YES">
|
8
|
-
<BuildActionEntries>
|
9
|
-
<BuildActionEntry
|
10
|
-
buildForTesting = "YES"
|
11
|
-
buildForRunning = "YES"
|
12
|
-
buildForProfiling = "YES"
|
13
|
-
buildForArchiving = "YES"
|
14
|
-
buildForAnalyzing = "YES">
|
15
|
-
<BuildableReference
|
16
|
-
BuildableIdentifier = "primary"
|
17
|
-
BlueprintIdentifier = "5199791315B1F92B003AFC57"
|
18
|
-
BuildableName = "terminal-notifier.app"
|
19
|
-
BlueprintName = "terminal-notifier"
|
20
|
-
ReferencedContainer = "container:Terminal Notifier.xcodeproj">
|
21
|
-
</BuildableReference>
|
22
|
-
</BuildActionEntry>
|
23
|
-
</BuildActionEntries>
|
24
|
-
</BuildAction>
|
25
|
-
<TestAction
|
26
|
-
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
27
|
-
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
28
|
-
shouldUseLaunchSchemeArgsEnv = "YES"
|
29
|
-
buildConfiguration = "Debug">
|
30
|
-
<Testables>
|
31
|
-
</Testables>
|
32
|
-
<MacroExpansion>
|
33
|
-
<BuildableReference
|
34
|
-
BuildableIdentifier = "primary"
|
35
|
-
BlueprintIdentifier = "5199791315B1F92B003AFC57"
|
36
|
-
BuildableName = "terminal-notifier.app"
|
37
|
-
BlueprintName = "terminal-notifier"
|
38
|
-
ReferencedContainer = "container:Terminal Notifier.xcodeproj">
|
39
|
-
</BuildableReference>
|
40
|
-
</MacroExpansion>
|
41
|
-
</TestAction>
|
42
|
-
<LaunchAction
|
43
|
-
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
44
|
-
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
45
|
-
launchStyle = "0"
|
46
|
-
useCustomWorkingDirectory = "NO"
|
47
|
-
buildConfiguration = "Release"
|
48
|
-
ignoresPersistentStateOnLaunch = "NO"
|
49
|
-
debugDocumentVersioning = "NO"
|
50
|
-
allowLocationSimulation = "YES">
|
51
|
-
<BuildableProductRunnable>
|
52
|
-
<BuildableReference
|
53
|
-
BuildableIdentifier = "primary"
|
54
|
-
BlueprintIdentifier = "5199791315B1F92B003AFC57"
|
55
|
-
BuildableName = "terminal-notifier.app"
|
56
|
-
BlueprintName = "terminal-notifier"
|
57
|
-
ReferencedContainer = "container:Terminal Notifier.xcodeproj">
|
58
|
-
</BuildableReference>
|
59
|
-
</BuildableProductRunnable>
|
60
|
-
<CommandLineArguments>
|
61
|
-
<CommandLineArgument
|
62
|
-
argument = "-group abcde"
|
63
|
-
isEnabled = "NO">
|
64
|
-
</CommandLineArgument>
|
65
|
-
<CommandLineArgument
|
66
|
-
argument = "-help"
|
67
|
-
isEnabled = "NO">
|
68
|
-
</CommandLineArgument>
|
69
|
-
<CommandLineArgument
|
70
|
-
argument = "-sound default"
|
71
|
-
isEnabled = "NO">
|
72
|
-
</CommandLineArgument>
|
73
|
-
<CommandLineArgument
|
74
|
-
argument = "-list ALL"
|
75
|
-
isEnabled = "NO">
|
76
|
-
</CommandLineArgument>
|
77
|
-
<CommandLineArgument
|
78
|
-
argument = "-list abcde"
|
79
|
-
isEnabled = "NO">
|
80
|
-
</CommandLineArgument>
|
81
|
-
<CommandLineArgument
|
82
|
-
argument = "-remove abcde"
|
83
|
-
isEnabled = "NO">
|
84
|
-
</CommandLineArgument>
|
85
|
-
<CommandLineArgument
|
86
|
-
argument = "-open 'http://google.com/search?q=http status codes'"
|
87
|
-
isEnabled = "YES">
|
88
|
-
</CommandLineArgument>
|
89
|
-
<CommandLineArgument
|
90
|
-
argument = "-execute 'say "OMG"'"
|
91
|
-
isEnabled = "NO">
|
92
|
-
</CommandLineArgument>
|
93
|
-
<CommandLineArgument
|
94
|
-
argument = "-execute 'ls -l'"
|
95
|
-
isEnabled = "NO">
|
96
|
-
</CommandLineArgument>
|
97
|
-
<CommandLineArgument
|
98
|
-
argument = "-title Kicker"
|
99
|
-
isEnabled = "NO">
|
100
|
-
</CommandLineArgument>
|
101
|
-
<CommandLineArgument
|
102
|
-
argument = "-message 'Execute: rake spec'"
|
103
|
-
isEnabled = "NO">
|
104
|
-
</CommandLineArgument>
|
105
|
-
<CommandLineArgument
|
106
|
-
argument = "-activate com.apple.Safari"
|
107
|
-
isEnabled = "NO">
|
108
|
-
</CommandLineArgument>
|
109
|
-
<CommandLineArgument
|
110
|
-
argument = "-sender com.apple.Safari"
|
111
|
-
isEnabled = "NO">
|
112
|
-
</CommandLineArgument>
|
113
|
-
</CommandLineArguments>
|
114
|
-
<AdditionalOptions>
|
115
|
-
</AdditionalOptions>
|
116
|
-
</LaunchAction>
|
117
|
-
<ProfileAction
|
118
|
-
shouldUseLaunchSchemeArgsEnv = "YES"
|
119
|
-
savedToolIdentifier = ""
|
120
|
-
useCustomWorkingDirectory = "NO"
|
121
|
-
buildConfiguration = "Release"
|
122
|
-
debugDocumentVersioning = "YES">
|
123
|
-
<BuildableProductRunnable>
|
124
|
-
<BuildableReference
|
125
|
-
BuildableIdentifier = "primary"
|
126
|
-
BlueprintIdentifier = "5199791315B1F92B003AFC57"
|
127
|
-
BuildableName = "terminal-notifier.app"
|
128
|
-
BlueprintName = "terminal-notifier"
|
129
|
-
ReferencedContainer = "container:Terminal Notifier.xcodeproj">
|
130
|
-
</BuildableReference>
|
131
|
-
</BuildableProductRunnable>
|
132
|
-
</ProfileAction>
|
133
|
-
<AnalyzeAction
|
134
|
-
buildConfiguration = "Debug">
|
135
|
-
</AnalyzeAction>
|
136
|
-
<ArchiveAction
|
137
|
-
buildConfiguration = "Release"
|
138
|
-
revealArchiveInOrganizer = "YES">
|
139
|
-
</ArchiveAction>
|
140
|
-
</Scheme>
|
@@ -1,594 +0,0 @@
|
|
1
|
-
#import "AppDelegate.h"
|
2
|
-
#import <ScriptingBridge/ScriptingBridge.h>
|
3
|
-
#import <objc/runtime.h>
|
4
|
-
|
5
|
-
NSString * const TerminalNotifierBundleID = @"nl.superalloy.oss.terminal-notifier";
|
6
|
-
NSString * const NotificationCenterUIBundleID = @"com.apple.notificationcenterui";
|
7
|
-
|
8
|
-
// Set OS Params
|
9
|
-
#define NSAppKitVersionNumber10_8 1187
|
10
|
-
#define NSAppKitVersionNumber10_9 1265
|
11
|
-
|
12
|
-
#define contains(str1, str2) ([str1 rangeOfString: str2 ].location != NSNotFound)
|
13
|
-
|
14
|
-
NSString *_fakeBundleIdentifier = nil;
|
15
|
-
NSUserNotification *currentNotification = nil;
|
16
|
-
|
17
|
-
@implementation NSBundle (FakeBundleIdentifier)
|
18
|
-
|
19
|
-
// Overriding bundleIdentifier works, but overriding NSUserNotificationAlertStyle does not work.
|
20
|
-
|
21
|
-
- (NSString *)__bundleIdentifier;
|
22
|
-
{
|
23
|
-
if (self == [NSBundle mainBundle]) {
|
24
|
-
return _fakeBundleIdentifier ? _fakeBundleIdentifier : TerminalNotifierBundleID;
|
25
|
-
} else {
|
26
|
-
return [self __bundleIdentifier];
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
|
-
@end
|
31
|
-
|
32
|
-
static BOOL
|
33
|
-
InstallFakeBundleIdentifierHook()
|
34
|
-
{
|
35
|
-
Class class = objc_getClass("NSBundle");
|
36
|
-
if (class) {
|
37
|
-
method_exchangeImplementations(class_getInstanceMethod(class, @selector(bundleIdentifier)),
|
38
|
-
class_getInstanceMethod(class, @selector(__bundleIdentifier)));
|
39
|
-
return YES;
|
40
|
-
}
|
41
|
-
return NO;
|
42
|
-
}
|
43
|
-
|
44
|
-
static BOOL
|
45
|
-
isMavericks()
|
46
|
-
{
|
47
|
-
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) {
|
48
|
-
/* On a 10.8 - 10.8.x system */
|
49
|
-
return NO;
|
50
|
-
} else {
|
51
|
-
/* 10.9 or later system */
|
52
|
-
return YES;
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
|
-
@implementation NSUserDefaults (SubscriptAndUnescape)
|
57
|
-
- (id)objectForKeyedSubscript:(id)key;
|
58
|
-
{
|
59
|
-
id obj = [self objectForKey:key];
|
60
|
-
if ([obj isKindOfClass:[NSString class]] && [(NSString *)obj hasPrefix:@"\\"]) {
|
61
|
-
obj = [(NSString *)obj substringFromIndex:1];
|
62
|
-
}
|
63
|
-
return obj;
|
64
|
-
}
|
65
|
-
@end
|
66
|
-
|
67
|
-
|
68
|
-
@implementation AppDelegate
|
69
|
-
|
70
|
-
+(void)initializeUserDefaults
|
71
|
-
{
|
72
|
-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
73
|
-
|
74
|
-
// initialize the dictionary with default values depending on OS level
|
75
|
-
NSDictionary *appDefaults;
|
76
|
-
|
77
|
-
if (isMavericks()) {
|
78
|
-
//10.9
|
79
|
-
appDefaults = @{@"sender": @"com.apple.Terminal"};
|
80
|
-
} else {
|
81
|
-
//10.8
|
82
|
-
appDefaults = @{@"": @"message"};
|
83
|
-
}
|
84
|
-
|
85
|
-
// and set them appropriately
|
86
|
-
[defaults registerDefaults:appDefaults];
|
87
|
-
}
|
88
|
-
|
89
|
-
- (void)printHelpBanner;
|
90
|
-
{
|
91
|
-
const char *appName = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String];
|
92
|
-
const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];
|
93
|
-
printf("%s (%s) is a command-line tool to send OS X User Notifications.\n" \
|
94
|
-
"\n" \
|
95
|
-
"Usage: %s -[message|list|remove] [VALUE|ID|ID] [options]\n" \
|
96
|
-
"\n" \
|
97
|
-
" Either of these is required (unless message data is piped to the tool):\n" \
|
98
|
-
"\n" \
|
99
|
-
" -help Display this help banner.\n" \
|
100
|
-
" -message VALUE The notification message.\n" \
|
101
|
-
" -remove ID Removes a notification with the specified ‘group’ ID.\n" \
|
102
|
-
" -list ID If the specified ‘group’ ID exists show when it was delivered,\n" \
|
103
|
-
" or use ‘ALL’ as ID to see all notifications.\n" \
|
104
|
-
" The output is a tab-separated list.\n"
|
105
|
-
"\n" \
|
106
|
-
" Reply type notification:\n" \
|
107
|
-
"\n" \
|
108
|
-
" -reply VALUE The notification will be displayed as a reply type alert, VALUE used as placeholder.\n" \
|
109
|
-
"\n" \
|
110
|
-
" Actions type notification:\n" \
|
111
|
-
"\n" \
|
112
|
-
" -actions VALUE1,VALUE2.\n" \
|
113
|
-
" The notification actions avalaible.\n" \
|
114
|
-
" When you provide more than one value, a dropdown will be displayed.\n" \
|
115
|
-
" You can customize this dropdown label with the next option.\n" \
|
116
|
-
" -dropdownLabel VALUE\n" \
|
117
|
-
" The notification actions dropdown title (only when multiples actions are provided).\n" \
|
118
|
-
" Notification style must be set to Alert.\n" \
|
119
|
-
"\n" \
|
120
|
-
" Optional:\n" \
|
121
|
-
"\n" \
|
122
|
-
" -title VALUE The notification title. Defaults to ‘Terminal’.\n" \
|
123
|
-
" -subtitle VALUE The notification subtitle.\n" \
|
124
|
-
" -closeLabel VALUE The notification close button label.\n" \
|
125
|
-
" -sound NAME The name of a sound to play when the notification appears. The names are listed\n" \
|
126
|
-
" in Sound Preferences. Use 'default' for the default notification sound.\n" \
|
127
|
-
" -group ID A string which identifies the group the notifications belong to.\n" \
|
128
|
-
" Old notifications with the same ID will be removed.\n" \
|
129
|
-
" -activate ID The bundle identifier of the application to activate when the user clicks the notification.\n" \
|
130
|
-
" -sender ID The bundle identifier of the application that should be shown as the sender, including its icon.\n" \
|
131
|
-
" -appIcon URL The URL of a image to display instead of the application icon (Mavericks+ only)\n" \
|
132
|
-
" -contentImage URL The URL of a image to display attached to the notification (Mavericks+ only)\n" \
|
133
|
-
" -open URL The URL of a resource to open when the user clicks the notification.\n" \
|
134
|
-
" -execute COMMAND A shell command to perform when the user clicks the notification.\n" \
|
135
|
-
" -timeout NUMBER Close the notification after NUMBER seconds.\n" \
|
136
|
-
" -json Output event or value to stdout as JSON \n" \
|
137
|
-
"\n" \
|
138
|
-
"When the user activates a notification, the results are logged to the system logs.\n" \
|
139
|
-
"Use Console.app to view these logs.\n" \
|
140
|
-
"\n" \
|
141
|
-
"Note that in some circumstances the first character of a message has to be escaped in order to be recognized.\n" \
|
142
|
-
"An example of this is when using an open bracket, which has to be escaped like so: ‘\\[’.\n" \
|
143
|
-
"\n" \
|
144
|
-
"For more information see https://github.com/julienXX/terminal-notifier.\n",
|
145
|
-
appName, appVersion, appName);
|
146
|
-
}
|
147
|
-
|
148
|
-
- (void)applicationDidFinishLaunching:(NSNotification *)notification;
|
149
|
-
{
|
150
|
-
NSUserNotification *userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
|
151
|
-
if (userNotification) {
|
152
|
-
[self userActivatedNotification:userNotification];
|
153
|
-
|
154
|
-
} else {
|
155
|
-
if ([[[NSProcessInfo processInfo] arguments] indexOfObject:@"-help"] != NSNotFound) {
|
156
|
-
[self printHelpBanner];
|
157
|
-
exit(0);
|
158
|
-
}
|
159
|
-
|
160
|
-
NSArray *runningProcesses = [[[NSWorkspace sharedWorkspace] runningApplications] valueForKey:@"bundleIdentifier"];
|
161
|
-
if ([runningProcesses indexOfObject:NotificationCenterUIBundleID] == NSNotFound) {
|
162
|
-
NSLog(@"[!] Unable to post a notification for the current user (%@), as it has no running NotificationCenter instance.", NSUserName());
|
163
|
-
exit(1);
|
164
|
-
}
|
165
|
-
|
166
|
-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
167
|
-
|
168
|
-
NSString *subtitle = defaults[@"subtitle"];
|
169
|
-
NSString *message = defaults[@"message"];
|
170
|
-
NSString *remove = defaults[@"remove"];
|
171
|
-
NSString *list = defaults[@"list"];
|
172
|
-
NSString *sound = defaults[@"sound"];
|
173
|
-
|
174
|
-
// If there is no message and data is piped to the application, use that
|
175
|
-
// instead.
|
176
|
-
if (message == nil && !isatty(STDIN_FILENO)) {
|
177
|
-
NSData *inputData = [NSData dataWithData:[[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile]];
|
178
|
-
message = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
|
179
|
-
}
|
180
|
-
|
181
|
-
if (message == nil && remove == nil && list == nil) {
|
182
|
-
[self printHelpBanner];
|
183
|
-
exit(1);
|
184
|
-
}
|
185
|
-
|
186
|
-
if (list) {
|
187
|
-
[self listNotificationWithGroupID:list];
|
188
|
-
exit(0);
|
189
|
-
}
|
190
|
-
|
191
|
-
// Install the fake bundle ID hook so we can fake the sender. This also
|
192
|
-
// needs to be done to be able to remove a message.
|
193
|
-
if (defaults[@"sender"]) {
|
194
|
-
@autoreleasepool {
|
195
|
-
if (InstallFakeBundleIdentifierHook()) {
|
196
|
-
_fakeBundleIdentifier = defaults[@"sender"];
|
197
|
-
}
|
198
|
-
}
|
199
|
-
}
|
200
|
-
|
201
|
-
if (remove) {
|
202
|
-
[self removeNotificationWithGroupID:remove];
|
203
|
-
if (message == nil) exit(0);
|
204
|
-
}
|
205
|
-
|
206
|
-
if (message) {
|
207
|
-
NSMutableDictionary *options = [NSMutableDictionary dictionary];
|
208
|
-
if (defaults[@"activate"]) options[@"bundleID"] = defaults[@"activate"];
|
209
|
-
if (defaults[@"group"]) options[@"groupID"] = defaults[@"group"];
|
210
|
-
if (defaults[@"execute"]) options[@"command"] = defaults[@"execute"];
|
211
|
-
if (defaults[@"appIcon"]) options[@"appIcon"] = defaults[@"appIcon"];
|
212
|
-
if (defaults[@"contentImage"]) options[@"contentImage"] = defaults[@"contentImage"];
|
213
|
-
if (defaults[@"closeLabel"]) options[@"closeLabel"] = defaults[@"closeLabel"];
|
214
|
-
if (defaults[@"dropdownLabel"]) options[@"dropdownLabel"] = defaults[@"dropdownLabel"];
|
215
|
-
if (defaults[@"actions"]) options[@"actions"] = defaults[@"actions"];
|
216
|
-
|
217
|
-
if([[[NSProcessInfo processInfo] arguments] containsObject:@"-reply"] == true) {
|
218
|
-
options[@"reply"] = @"Reply";
|
219
|
-
if (defaults[@"reply"]) options[@"reply"] = defaults[@"reply"];
|
220
|
-
}
|
221
|
-
|
222
|
-
options[@"output"] = @"outputEvent";
|
223
|
-
if([[[NSProcessInfo processInfo] arguments] containsObject:@"-json"] == true) {
|
224
|
-
options[@"output"] = @"json";
|
225
|
-
}
|
226
|
-
|
227
|
-
options[@"uuid"] = [NSString stringWithFormat:@"%ld", self.hash];
|
228
|
-
options[@"timeout"] = defaults[@"timeout"] ? defaults[@"timeout"] : @"0";
|
229
|
-
|
230
|
-
if (options[@"reply"] || defaults[@"timeout"] || defaults[@"actions"]) options[@"waitForResponse"] = @YES;
|
231
|
-
|
232
|
-
if (defaults[@"open"]) {
|
233
|
-
/*
|
234
|
-
* it may be better to use stringByAddingPercentEncodingWithAllowedCharacters instead of stringByAddingPercentEscapesUsingEncoding,
|
235
|
-
* but stringByAddingPercentEncodingWithAllowedCharacters is only available on OS X 10.9 or higher.
|
236
|
-
*/
|
237
|
-
NSString *encodedURL = [defaults[@"open"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
238
|
-
NSURL *url = [NSURL URLWithString:defaults[@"open"]];
|
239
|
-
NSString *fragment = [url fragment];
|
240
|
-
if (fragment) {
|
241
|
-
options[@"open"] = [self decodeFragmentInURL:encodedURL fragment:fragment];
|
242
|
-
} else {
|
243
|
-
options[@"open"] = encodedURL;
|
244
|
-
}
|
245
|
-
}
|
246
|
-
|
247
|
-
options[@"uuid"] = [NSString stringWithFormat:@"%ld", self.hash];
|
248
|
-
|
249
|
-
[self deliverNotificationWithTitle:defaults[@"title"] ?: @"Terminal"
|
250
|
-
subtitle:subtitle
|
251
|
-
message:message
|
252
|
-
options:options
|
253
|
-
sound:sound];
|
254
|
-
}
|
255
|
-
}
|
256
|
-
}
|
257
|
-
|
258
|
-
- (NSImage*)getImageFromURL:(NSString *) url;
|
259
|
-
{
|
260
|
-
NSURL *imageURL = [NSURL URLWithString:url];
|
261
|
-
if([[imageURL scheme] length] == 0){
|
262
|
-
// Prefix 'file://' if no scheme
|
263
|
-
imageURL = [NSURL fileURLWithPath:url];
|
264
|
-
}
|
265
|
-
return [[NSImage alloc] initWithContentsOfURL:imageURL];
|
266
|
-
}
|
267
|
-
|
268
|
-
/**
|
269
|
-
* Decode fragment identifier
|
270
|
-
*
|
271
|
-
* @see http://tools.ietf.org/html/rfc3986#section-2.1
|
272
|
-
* @see http://en.wikipedia.org/wiki/URI_scheme
|
273
|
-
*/
|
274
|
-
- (NSString*)decodeFragmentInURL:(NSString *) encodedURL fragment:(NSString *) framgent
|
275
|
-
{
|
276
|
-
NSString *beforeStr = [@"%23" stringByAppendingString:framgent];
|
277
|
-
NSString *afterStr = [@"#" stringByAppendingString:framgent];
|
278
|
-
NSString *decodedURL = [encodedURL stringByReplacingOccurrencesOfString:beforeStr withString:afterStr];
|
279
|
-
return decodedURL;
|
280
|
-
}
|
281
|
-
|
282
|
-
- (void)deliverNotificationWithTitle:(NSString *)title
|
283
|
-
subtitle:(NSString *)subtitle
|
284
|
-
message:(NSString *)message
|
285
|
-
options:(NSDictionary *)options
|
286
|
-
sound:(NSString *)sound;
|
287
|
-
{
|
288
|
-
// First remove earlier notification with the same group ID.
|
289
|
-
if (options[@"groupID"]) [self removeNotificationWithGroupID:options[@"groupID"]];
|
290
|
-
|
291
|
-
NSUserNotification *userNotification = [NSUserNotification new];
|
292
|
-
userNotification.title = title;
|
293
|
-
userNotification.subtitle = subtitle;
|
294
|
-
userNotification.informativeText = message;
|
295
|
-
userNotification.userInfo = options;
|
296
|
-
|
297
|
-
if(isMavericks()){
|
298
|
-
// Mavericks options
|
299
|
-
if(options[@"appIcon"]){
|
300
|
-
// replacement app icon
|
301
|
-
[userNotification setValue:[self getImageFromURL:options[@"appIcon"]] forKey:@"_identityImage"];
|
302
|
-
[userNotification setValue:@(false) forKey:@"_identityImageHasBorder"];
|
303
|
-
}
|
304
|
-
if(options[@"contentImage"]){
|
305
|
-
// content image
|
306
|
-
userNotification.contentImage = [self getImageFromURL:options[@"contentImage"]];
|
307
|
-
}
|
308
|
-
// Actions
|
309
|
-
if (options[@"actions"]){
|
310
|
-
[userNotification setValue:@YES forKey:@"_showsButtons"];
|
311
|
-
NSArray *myActions = [options[@"actions"] componentsSeparatedByString:@","];
|
312
|
-
if (myActions.count > 1) {
|
313
|
-
[userNotification setValue:@YES forKey:@"_alwaysShowAlternateActionMenu"];
|
314
|
-
[userNotification setValue:myActions forKey:@"_alternateActionButtonTitles"];
|
315
|
-
|
316
|
-
//Main Actions Title
|
317
|
-
if(options[@"dropdownLabel"]){
|
318
|
-
userNotification.actionButtonTitle = options[@"dropdownLabel"];
|
319
|
-
userNotification.hasActionButton = true;
|
320
|
-
}
|
321
|
-
}else{
|
322
|
-
userNotification.actionButtonTitle = options[@"actions"];
|
323
|
-
}
|
324
|
-
}else if (options[@"reply"]) {
|
325
|
-
[userNotification setValue:@YES forKey:@"_showsButtons"];
|
326
|
-
userNotification.hasReplyButton = 1;
|
327
|
-
userNotification.responsePlaceholder = options[@"reply"];
|
328
|
-
}
|
329
|
-
|
330
|
-
// Close button
|
331
|
-
if(options[@"closeLabel"]){
|
332
|
-
userNotification.otherButtonTitle = options[@"closeLabel"];
|
333
|
-
}
|
334
|
-
}
|
335
|
-
|
336
|
-
if (sound != nil) {
|
337
|
-
userNotification.soundName = [sound isEqualToString: @"default"] ? NSUserNotificationDefaultSoundName : sound;
|
338
|
-
}
|
339
|
-
|
340
|
-
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
341
|
-
center.delegate = self;
|
342
|
-
[center deliverNotification:userNotification];
|
343
|
-
}
|
344
|
-
|
345
|
-
- (void)removeNotificationWithGroupID:(NSString *)groupID;
|
346
|
-
{
|
347
|
-
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
348
|
-
for (NSUserNotification *userNotification in center.deliveredNotifications) {
|
349
|
-
if ([@"ALL" isEqualToString:groupID] || [userNotification.userInfo[@"groupID"] isEqualToString:groupID]) {
|
350
|
-
[center removeDeliveredNotification:userNotification];
|
351
|
-
}
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
- (void)userActivatedNotification:(NSUserNotification *)userNotification;
|
356
|
-
{
|
357
|
-
[[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:userNotification];
|
358
|
-
|
359
|
-
NSString *groupID = userNotification.userInfo[@"groupID"];
|
360
|
-
NSString *bundleID = userNotification.userInfo[@"bundleID"];
|
361
|
-
NSString *command = userNotification.userInfo[@"command"];
|
362
|
-
NSString *open = userNotification.userInfo[@"open"];
|
363
|
-
|
364
|
-
if (bundleID) [self activateAppWithBundleID:bundleID];
|
365
|
-
if (command) [self executeShellCommand:command];
|
366
|
-
if (open) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:open]];
|
367
|
-
}
|
368
|
-
|
369
|
-
- (BOOL)activateAppWithBundleID:(NSString *)bundleID;
|
370
|
-
{
|
371
|
-
id app = [SBApplication applicationWithBundleIdentifier:bundleID];
|
372
|
-
if (app) {
|
373
|
-
[app activate];
|
374
|
-
return YES;
|
375
|
-
|
376
|
-
} else {
|
377
|
-
NSLog(@"Unable to find an application with the specified bundle indentifier.");
|
378
|
-
return NO;
|
379
|
-
}
|
380
|
-
}
|
381
|
-
|
382
|
-
- (BOOL)executeShellCommand:(NSString *)command;
|
383
|
-
{
|
384
|
-
NSPipe *pipe = [NSPipe pipe];
|
385
|
-
NSFileHandle *fileHandle = [pipe fileHandleForReading];
|
386
|
-
|
387
|
-
NSTask *task = [NSTask new];
|
388
|
-
task.launchPath = @"/bin/sh";
|
389
|
-
task.arguments = @[@"-c", command];
|
390
|
-
task.standardOutput = pipe;
|
391
|
-
task.standardError = pipe;
|
392
|
-
[task launch];
|
393
|
-
|
394
|
-
NSData *data = nil;
|
395
|
-
NSMutableData *accumulatedData = [NSMutableData data];
|
396
|
-
while ((data = [fileHandle availableData]) && [data length]) {
|
397
|
-
[accumulatedData appendData:data];
|
398
|
-
}
|
399
|
-
|
400
|
-
[task waitUntilExit];
|
401
|
-
NSLog(@"command output:\n%@", [[NSString alloc] initWithData:accumulatedData encoding:NSUTF8StringEncoding]);
|
402
|
-
return [task terminationStatus] == 0;
|
403
|
-
}
|
404
|
-
|
405
|
-
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
|
406
|
-
shouldPresentNotification:(NSUserNotification *)notification;
|
407
|
-
{
|
408
|
-
return YES;
|
409
|
-
}
|
410
|
-
|
411
|
-
// Once the notification is delivered we can exit. (Only if no actions or reply)
|
412
|
-
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
413
|
-
didDeliverNotification:(NSUserNotification *)userNotification;
|
414
|
-
{
|
415
|
-
if (!userNotification.userInfo[@"waitForResponse"]) exit(0);
|
416
|
-
|
417
|
-
currentNotification = userNotification;
|
418
|
-
|
419
|
-
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
420
|
-
^{
|
421
|
-
__block BOOL notificationStillPresent;
|
422
|
-
do {
|
423
|
-
@autoreleasepool {
|
424
|
-
notificationStillPresent = NO;
|
425
|
-
for (NSUserNotification *nox in [[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications]) {
|
426
|
-
if ([nox.userInfo[@"uuid"] isEqualToString:[NSString stringWithFormat:@"%ld", self.hash] ]) notificationStillPresent = YES;
|
427
|
-
}
|
428
|
-
if (notificationStillPresent) [NSThread sleepForTimeInterval:0.20f];
|
429
|
-
}
|
430
|
-
} while (notificationStillPresent);
|
431
|
-
|
432
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
433
|
-
NSDictionary *udict = @{@"activationType" : @"closed", @"activationValue" : userNotification.otherButtonTitle};
|
434
|
-
[self Quit:udict notification:userNotification];
|
435
|
-
exit(0);
|
436
|
-
});
|
437
|
-
});
|
438
|
-
|
439
|
-
if ([userNotification.userInfo[@"timeout"] integerValue] > 0){
|
440
|
-
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
441
|
-
^{
|
442
|
-
[NSThread sleepForTimeInterval:[userNotification.userInfo[@"timeout"] integerValue]];
|
443
|
-
[center removeDeliveredNotification:currentNotification];
|
444
|
-
[center removeDeliveredNotification:userNotification];
|
445
|
-
NSDictionary *udict = @{@"activationType" : @"timeout"};
|
446
|
-
[self Quit:udict notification:userNotification];
|
447
|
-
exit(0);
|
448
|
-
});
|
449
|
-
}
|
450
|
-
}
|
451
|
-
|
452
|
-
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
453
|
-
didActivateNotification:(NSUserNotification *)notification {
|
454
|
-
|
455
|
-
if ([notification.userInfo[@"uuid"] isNotEqualTo:[NSString stringWithFormat:@"%ld", self.hash] ]) {
|
456
|
-
return;
|
457
|
-
};
|
458
|
-
|
459
|
-
unsigned long long additionalActionIndex = ULLONG_MAX;
|
460
|
-
|
461
|
-
NSString* ActionsClicked = @"";
|
462
|
-
switch (notification.activationType) {
|
463
|
-
|
464
|
-
case NSUserNotificationActivationTypeAdditionalActionClicked:
|
465
|
-
case NSUserNotificationActivationTypeActionButtonClicked:
|
466
|
-
if ([[(NSObject*)notification valueForKey:@"_alternateActionButtonTitles"] count] > 1 ){
|
467
|
-
NSNumber *alternateActionIndex = [(NSObject*)notification valueForKey:@"_alternateActionIndex"];
|
468
|
-
additionalActionIndex = [alternateActionIndex unsignedLongLongValue];
|
469
|
-
ActionsClicked = [(NSObject*)notification valueForKey:@"_alternateActionButtonTitles"][additionalActionIndex];
|
470
|
-
|
471
|
-
NSDictionary *udict = @{@"activationType" : @"actionClicked", @"activationValue" : ActionsClicked, @"activationValueIndex" :[NSString stringWithFormat:@"%llu", additionalActionIndex]};
|
472
|
-
[self Quit:udict notification:notification];
|
473
|
-
}else{
|
474
|
-
NSDictionary *udict = @{@"activationType" : @"actionClicked", @"activationValue" : notification.actionButtonTitle};
|
475
|
-
[self Quit:udict notification:notification];
|
476
|
-
}
|
477
|
-
break;
|
478
|
-
|
479
|
-
case NSUserNotificationActivationTypeContentsClicked:
|
480
|
-
[self userActivatedNotification:notification];
|
481
|
-
[self Quit:@{@"activationType" : @"contentsClicked"} notification:notification];
|
482
|
-
break;
|
483
|
-
|
484
|
-
case NSUserNotificationActivationTypeReplied:
|
485
|
-
[self Quit:@{@"activationType" : @"replied",@"activationValue":notification.response.string} notification:notification];
|
486
|
-
break;
|
487
|
-
case NSUserNotificationActivationTypeNone:
|
488
|
-
default:
|
489
|
-
[self Quit:@{@"activationType" : @"none"} notification:notification];
|
490
|
-
break;
|
491
|
-
}
|
492
|
-
|
493
|
-
[center removeDeliveredNotification:notification];
|
494
|
-
[center removeDeliveredNotification:currentNotification];
|
495
|
-
exit(0);
|
496
|
-
}
|
497
|
-
|
498
|
-
- (BOOL)Quit:(NSDictionary *)udict notification:(NSUserNotification *)notification;
|
499
|
-
{
|
500
|
-
if ([notification.userInfo[@"output"] isEqualToString:@"outputEvent"]) {
|
501
|
-
if ([udict[@"activationType"] isEqualToString:@"closed"]) {
|
502
|
-
if ([udict[@"activationValue"] isEqualToString:@""]) {
|
503
|
-
printf("%s", "@CLOSED" );
|
504
|
-
}else{
|
505
|
-
printf("%s", [udict[@"activationValue"] UTF8String] );
|
506
|
-
}
|
507
|
-
} else if ([udict[@"activationType"] isEqualToString:@"timeout"]) {
|
508
|
-
printf("%s", "@TIMEOUT" );
|
509
|
-
} else if ([udict[@"activationType"] isEqualToString:@"contentsClicked"]) {
|
510
|
-
printf("%s", "@CONTENTCLICKED" );
|
511
|
-
} else{
|
512
|
-
if ([udict[@"activationValue"] isEqualToString:@""]) {
|
513
|
-
printf("%s", "@ACTIONCLICKED" );
|
514
|
-
}else{
|
515
|
-
printf("%s", [udict[@"activationValue"] UTF8String] );
|
516
|
-
}
|
517
|
-
}
|
518
|
-
|
519
|
-
return 1;
|
520
|
-
}
|
521
|
-
|
522
|
-
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
523
|
-
dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
|
524
|
-
|
525
|
-
// Dictionary with several key/value pairs and the above array of arrays
|
526
|
-
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
527
|
-
[dict addEntriesFromDictionary:udict];
|
528
|
-
[dict setValue:[dateFormatter stringFromDate:notification.actualDeliveryDate] forKey:@"deliveredAt"];
|
529
|
-
[dict setValue:[dateFormatter stringFromDate:[NSDate new]] forKey:@"activationAt"];
|
530
|
-
|
531
|
-
NSError *error = nil;
|
532
|
-
NSData *json;
|
533
|
-
|
534
|
-
// Dictionary convertable to JSON ?
|
535
|
-
if ([NSJSONSerialization isValidJSONObject:dict])
|
536
|
-
{
|
537
|
-
// Serialize the dictionary
|
538
|
-
json = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
|
539
|
-
|
540
|
-
// If no errors, let's view the JSON
|
541
|
-
if (json != nil && error == nil)
|
542
|
-
{
|
543
|
-
NSString *jsonString = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
|
544
|
-
printf("%s", [jsonString cStringUsingEncoding:NSUTF8StringEncoding]);
|
545
|
-
}
|
546
|
-
}
|
547
|
-
|
548
|
-
return 1;
|
549
|
-
}
|
550
|
-
|
551
|
-
- (void)listNotificationWithGroupID:(NSString *)listGroupID;
|
552
|
-
{
|
553
|
-
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
554
|
-
|
555
|
-
NSMutableArray *lines = [NSMutableArray array];
|
556
|
-
for (NSUserNotification *userNotification in center.deliveredNotifications) {
|
557
|
-
NSString *deliveredgroupID = userNotification.userInfo[@"groupID"];
|
558
|
-
NSString *title = userNotification.title;
|
559
|
-
NSString *subtitle = userNotification.subtitle;
|
560
|
-
NSString *message = userNotification.informativeText;
|
561
|
-
NSString *deliveredAt = [userNotification.actualDeliveryDate description];
|
562
|
-
|
563
|
-
if ([@"ALL" isEqualToString:listGroupID] || [deliveredgroupID isEqualToString:listGroupID]) {
|
564
|
-
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
565
|
-
[dict setValue:deliveredgroupID forKey:@"GroupID"];
|
566
|
-
[dict setValue:title forKey:@"Title"];
|
567
|
-
[dict setValue:subtitle forKey:@"subtitle"];
|
568
|
-
[dict setValue:message forKey:@"message"];
|
569
|
-
[dict setValue:deliveredAt forKey:@"deliveredAt"];
|
570
|
-
[lines addObject:dict];
|
571
|
-
}
|
572
|
-
}
|
573
|
-
|
574
|
-
if (lines.count > 0) {
|
575
|
-
NSData *json;
|
576
|
-
NSError *error = nil;
|
577
|
-
// Dictionary convertable to JSON ?
|
578
|
-
if ([NSJSONSerialization isValidJSONObject:lines])
|
579
|
-
{
|
580
|
-
// Serialize the dictionary
|
581
|
-
json = [NSJSONSerialization dataWithJSONObject:lines options:NSJSONWritingPrettyPrinted error:&error];
|
582
|
-
|
583
|
-
// If no errors, let's view the JSON
|
584
|
-
if (json != nil && error == nil)
|
585
|
-
{
|
586
|
-
NSString *jsonString = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
|
587
|
-
printf("%s", [jsonString cStringUsingEncoding:NSUTF8StringEncoding]);
|
588
|
-
}
|
589
|
-
}
|
590
|
-
|
591
|
-
}
|
592
|
-
}
|
593
|
-
|
594
|
-
@end
|