terminal-notifier 1.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.markdown +5 -5
- data/lib/terminal-notifier.rb +39 -8
- data/vendor/terminal-notifier/LICENSE.md +9 -0
- data/vendor/terminal-notifier/README.markdown +256 -0
- data/vendor/terminal-notifier/Ruby/Gemfile +2 -0
- data/vendor/terminal-notifier/Ruby/Gemfile.lock +23 -0
- data/vendor/terminal-notifier/Ruby/LICENSE +24 -0
- data/vendor/terminal-notifier/Ruby/README.markdown +48 -0
- data/vendor/terminal-notifier/Ruby/Rakefile +48 -0
- data/vendor/terminal-notifier/Ruby/bin/terminal-notifier +13 -0
- data/vendor/terminal-notifier/Ruby/lib/terminal-notifier.rb +124 -0
- data/vendor/terminal-notifier/Ruby/spec/terminal-notifier_spec.rb +79 -0
- data/vendor/terminal-notifier/Ruby/terminal-notifier.gemspec +26 -0
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.pbxproj +315 -0
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.xcworkspace/xcshareddata/Terminal Notifier.xccheckout +41 -0
- data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/xcshareddata/xcschemes/Terminal Notifier.xcscheme +140 -0
- data/vendor/terminal-notifier/Terminal Notifier/AppDelegate.h +4 -0
- data/vendor/terminal-notifier/Terminal Notifier/AppDelegate.m +382 -0
- data/vendor/terminal-notifier/Terminal Notifier/Terminal Notifier-Info.plist +43 -0
- data/vendor/terminal-notifier/Terminal Notifier/Terminal Notifier-Prefix.pch +7 -0
- data/vendor/terminal-notifier/Terminal Notifier/en.lproj/Credits.rtf +29 -0
- data/vendor/terminal-notifier/Terminal Notifier/en.lproj/InfoPlist.strings +2 -0
- data/vendor/terminal-notifier/Terminal Notifier/en.lproj/MainMenu.xib +3387 -0
- data/vendor/terminal-notifier/Terminal Notifier/main.m +6 -0
- 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 +63 -0
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier +0 -0
- data/vendor/terminal-notifier/terminal-notifier.app/Contents/PkgInfo +1 -0
- 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 +29 -0
- 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
- metadata +42 -4
- data/vendor/terminal-notifier +0 -0
@@ -0,0 +1,41 @@
|
|
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:julienXX/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:julienXX/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>
|
@@ -0,0 +1,140 @@
|
|
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>
|
@@ -0,0 +1,382 @@
|
|
1
|
+
#import "AppDelegate.h"
|
2
|
+
#import <ScriptingBridge/ScriptingBridge.h>
|
3
|
+
#import <objc/runtime.h>
|
4
|
+
|
5
|
+
NSString * const TerminalNotifierBundleID = @"fr.julienxx.oss.terminal-notifier";
|
6
|
+
NSString * const NotificationCenterUIBundleID = @"com.apple.notificationcenterui";
|
7
|
+
|
8
|
+
NSString *_fakeBundleIdentifier = nil;
|
9
|
+
|
10
|
+
@implementation NSBundle (FakeBundleIdentifier)
|
11
|
+
|
12
|
+
// Overriding bundleIdentifier works, but overriding NSUserNotificationAlertStyle does not work.
|
13
|
+
|
14
|
+
- (NSString *)__bundleIdentifier;
|
15
|
+
{
|
16
|
+
if (self == [NSBundle mainBundle]) {
|
17
|
+
return _fakeBundleIdentifier ? _fakeBundleIdentifier : TerminalNotifierBundleID;
|
18
|
+
} else {
|
19
|
+
return [self __bundleIdentifier];
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
@end
|
24
|
+
|
25
|
+
static BOOL
|
26
|
+
InstallFakeBundleIdentifierHook()
|
27
|
+
{
|
28
|
+
Class class = objc_getClass("NSBundle");
|
29
|
+
if (class) {
|
30
|
+
method_exchangeImplementations(class_getInstanceMethod(class, @selector(bundleIdentifier)),
|
31
|
+
class_getInstanceMethod(class, @selector(__bundleIdentifier)));
|
32
|
+
return YES;
|
33
|
+
}
|
34
|
+
return NO;
|
35
|
+
}
|
36
|
+
|
37
|
+
@implementation NSUserDefaults (SubscriptAndUnescape)
|
38
|
+
- (id)objectForKeyedSubscript:(id)key;
|
39
|
+
{
|
40
|
+
id obj = [self objectForKey:key];
|
41
|
+
if ([obj isKindOfClass:[NSString class]] && [(NSString *)obj hasPrefix:@"\\"]) {
|
42
|
+
obj = [(NSString *)obj substringFromIndex:1];
|
43
|
+
}
|
44
|
+
return obj;
|
45
|
+
}
|
46
|
+
@end
|
47
|
+
|
48
|
+
|
49
|
+
@implementation AppDelegate
|
50
|
+
|
51
|
+
+(void)initializeUserDefaults
|
52
|
+
{
|
53
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
54
|
+
|
55
|
+
// initialize the dictionary with default values depending on OS level
|
56
|
+
NSDictionary *appDefaults;
|
57
|
+
appDefaults = @{@"sender": @"com.apple.Terminal"};
|
58
|
+
|
59
|
+
// and set them appropriately
|
60
|
+
[defaults registerDefaults:appDefaults];
|
61
|
+
}
|
62
|
+
|
63
|
+
- (void)printHelpBanner;
|
64
|
+
{
|
65
|
+
const char *appName = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String];
|
66
|
+
const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];
|
67
|
+
printf("%s (%s) is a command-line tool to send macOS User Notifications.\n" \
|
68
|
+
"\n" \
|
69
|
+
"Usage: %s -[message|list|remove] [VALUE|ID|ID] [options]\n" \
|
70
|
+
"\n" \
|
71
|
+
" Either of these is required (unless message data is piped to the tool):\n" \
|
72
|
+
"\n" \
|
73
|
+
" -help Display this help banner.\n" \
|
74
|
+
" -version Display terminal-notifier version.\n" \
|
75
|
+
" -message VALUE The notification message.\n" \
|
76
|
+
" -remove ID Removes a notification with the specified ‘group’ ID.\n" \
|
77
|
+
" -list ID If the specified ‘group’ ID exists show when it was delivered,\n" \
|
78
|
+
" or use ‘ALL’ as ID to see all notifications.\n" \
|
79
|
+
" The output is a tab-separated list.\n"
|
80
|
+
"\n" \
|
81
|
+
" Optional:\n" \
|
82
|
+
"\n" \
|
83
|
+
" -title VALUE The notification title. Defaults to ‘Terminal’.\n" \
|
84
|
+
" -subtitle VALUE The notification subtitle.\n" \
|
85
|
+
" -sound NAME The name of a sound to play when the notification appears. The names are listed\n" \
|
86
|
+
" in Sound Preferences. Use 'default' for the default notification sound.\n" \
|
87
|
+
" -group ID A string which identifies the group the notifications belong to.\n" \
|
88
|
+
" Old notifications with the same ID will be removed.\n" \
|
89
|
+
" -activate ID The bundle identifier of the application to activate when the user clicks the notification.\n" \
|
90
|
+
" -sender ID The bundle identifier of the application that should be shown as the sender, including its icon.\n" \
|
91
|
+
" -appIcon URL The URL of a image to display instead of the application icon (Mavericks+ only)\n" \
|
92
|
+
" -contentImage URL The URL of a image to display attached to the notification (Mavericks+ only)\n" \
|
93
|
+
" -open URL The URL of a resource to open when the user clicks the notification.\n" \
|
94
|
+
" -execute COMMAND A shell command to perform when the user clicks the notification.\n" \
|
95
|
+
" -ignoreDnD Send notification even if Do Not Disturb is enabled.\n" \
|
96
|
+
"\n" \
|
97
|
+
"When the user activates a notification, the results are logged to the system logs.\n" \
|
98
|
+
"Use Console.app to view these logs.\n" \
|
99
|
+
"\n" \
|
100
|
+
"Note that in some circumstances the first character of a message has to be escaped in order to be recognized.\n" \
|
101
|
+
"An example of this is when using an open bracket, which has to be escaped like so: ‘\\[’.\n" \
|
102
|
+
"\n" \
|
103
|
+
"For more information see https://github.com/julienXX/terminal-notifier.\n",
|
104
|
+
appName, appVersion, appName);
|
105
|
+
}
|
106
|
+
|
107
|
+
- (void)printVersion;
|
108
|
+
{
|
109
|
+
const char *appName = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String];
|
110
|
+
const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];
|
111
|
+
printf("%s %s.\n", appName, appVersion);
|
112
|
+
}
|
113
|
+
|
114
|
+
- (void)applicationDidFinishLaunching:(NSNotification *)notification;
|
115
|
+
{
|
116
|
+
NSUserNotification *userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
|
117
|
+
if (userNotification) {
|
118
|
+
[self userActivatedNotification:userNotification];
|
119
|
+
|
120
|
+
} else {
|
121
|
+
if ([[[NSProcessInfo processInfo] arguments] indexOfObject:@"-help"] != NSNotFound) {
|
122
|
+
[self printHelpBanner];
|
123
|
+
exit(0);
|
124
|
+
}
|
125
|
+
|
126
|
+
if ([[[NSProcessInfo processInfo] arguments] indexOfObject:@"-version"] != NSNotFound) {
|
127
|
+
[self printVersion];
|
128
|
+
exit(0);
|
129
|
+
}
|
130
|
+
|
131
|
+
NSArray *runningProcesses = [[[NSWorkspace sharedWorkspace] runningApplications] valueForKey:@"bundleIdentifier"];
|
132
|
+
if ([runningProcesses indexOfObject:NotificationCenterUIBundleID] == NSNotFound) {
|
133
|
+
NSLog(@"[!] Unable to post a notification for the current user (%@), as it has no running NotificationCenter instance.", NSUserName());
|
134
|
+
exit(1);
|
135
|
+
}
|
136
|
+
|
137
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
138
|
+
|
139
|
+
NSString *subtitle = defaults[@"subtitle"];
|
140
|
+
NSString *message = defaults[@"message"];
|
141
|
+
NSString *remove = defaults[@"remove"];
|
142
|
+
NSString *list = defaults[@"list"];
|
143
|
+
NSString *sound = defaults[@"sound"];
|
144
|
+
|
145
|
+
// If there is no message and data is piped to the application, use that
|
146
|
+
// instead.
|
147
|
+
if (message == nil && !isatty(STDIN_FILENO)) {
|
148
|
+
NSData *inputData = [NSData dataWithData:[[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile]];
|
149
|
+
message = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
|
150
|
+
}
|
151
|
+
|
152
|
+
if (message == nil && remove == nil && list == nil) {
|
153
|
+
[self printHelpBanner];
|
154
|
+
exit(1);
|
155
|
+
}
|
156
|
+
|
157
|
+
if (list) {
|
158
|
+
[self listNotificationWithGroupID:list];
|
159
|
+
exit(0);
|
160
|
+
}
|
161
|
+
|
162
|
+
// Install the fake bundle ID hook so we can fake the sender. This also
|
163
|
+
// needs to be done to be able to remove a message.
|
164
|
+
if (defaults[@"sender"]) {
|
165
|
+
@autoreleasepool {
|
166
|
+
if (InstallFakeBundleIdentifierHook()) {
|
167
|
+
_fakeBundleIdentifier = defaults[@"sender"];
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
if (remove) {
|
173
|
+
[self removeNotificationWithGroupID:remove];
|
174
|
+
if (message == nil || ([message length] == 0)) {
|
175
|
+
exit(0);
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
if (message) {
|
180
|
+
NSMutableDictionary *options = [NSMutableDictionary dictionary];
|
181
|
+
if (defaults[@"activate"]) options[@"bundleID"] = defaults[@"activate"];
|
182
|
+
if (defaults[@"group"]) options[@"groupID"] = defaults[@"group"];
|
183
|
+
if (defaults[@"execute"]) options[@"command"] = defaults[@"execute"];
|
184
|
+
if (defaults[@"appIcon"]) options[@"appIcon"] = defaults[@"appIcon"];
|
185
|
+
if (defaults[@"contentImage"]) options[@"contentImage"] = defaults[@"contentImage"];
|
186
|
+
|
187
|
+
if (defaults[@"open"]) {
|
188
|
+
NSURL *url = [NSURL URLWithString:defaults[@"open"]];
|
189
|
+
if ((url && url.scheme && url.host) || [url isFileURL]) {
|
190
|
+
options[@"open"] = defaults[@"open"];
|
191
|
+
}else{
|
192
|
+
NSLog(@"'%@' is not a valid URI.", defaults[@"open"]);
|
193
|
+
exit(1);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
if([[[NSProcessInfo processInfo] arguments] containsObject:@"-ignoreDnD"] == true) {
|
198
|
+
options[@"ignoreDnD"] = @YES;
|
199
|
+
}
|
200
|
+
|
201
|
+
[self deliverNotificationWithTitle:defaults[@"title"] ?: @"Terminal"
|
202
|
+
subtitle:subtitle
|
203
|
+
message:message
|
204
|
+
options:options
|
205
|
+
sound:sound];
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
- (NSImage*)getImageFromURL:(NSString *) url;
|
211
|
+
{
|
212
|
+
NSURL *imageURL = [NSURL URLWithString:url];
|
213
|
+
if([[imageURL scheme] length] == 0){
|
214
|
+
// Prefix 'file://' if no scheme
|
215
|
+
imageURL = [NSURL fileURLWithPath:url];
|
216
|
+
}
|
217
|
+
return [[NSImage alloc] initWithContentsOfURL:imageURL];
|
218
|
+
}
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Decode fragment identifier
|
222
|
+
*
|
223
|
+
* @see http://tools.ietf.org/html/rfc3986#section-2.1
|
224
|
+
* @see http://en.wikipedia.org/wiki/URI_scheme
|
225
|
+
*/
|
226
|
+
- (NSString*)decodeFragmentInURL:(NSString *) encodedURL fragment:(NSString *) framgent
|
227
|
+
{
|
228
|
+
NSString *beforeStr = [@"%23" stringByAppendingString:framgent];
|
229
|
+
NSString *afterStr = [@"#" stringByAppendingString:framgent];
|
230
|
+
NSString *decodedURL = [encodedURL stringByReplacingOccurrencesOfString:beforeStr withString:afterStr];
|
231
|
+
return decodedURL;
|
232
|
+
}
|
233
|
+
|
234
|
+
- (void)deliverNotificationWithTitle:(NSString *)title
|
235
|
+
subtitle:(NSString *)subtitle
|
236
|
+
message:(NSString *)message
|
237
|
+
options:(NSDictionary *)options
|
238
|
+
sound:(NSString *)sound;
|
239
|
+
{
|
240
|
+
// First remove earlier notification with the same group ID.
|
241
|
+
if (options[@"groupID"]) [self removeNotificationWithGroupID:options[@"groupID"]];
|
242
|
+
|
243
|
+
NSUserNotification *userNotification = [NSUserNotification new];
|
244
|
+
userNotification.title = title;
|
245
|
+
userNotification.subtitle = subtitle;
|
246
|
+
userNotification.informativeText = message;
|
247
|
+
userNotification.userInfo = options;
|
248
|
+
|
249
|
+
if(options[@"appIcon"]){
|
250
|
+
[userNotification setValue:[self getImageFromURL:options[@"appIcon"]] forKey:@"_identityImage"];
|
251
|
+
[userNotification setValue:@(false) forKey:@"_identityImageHasBorder"];
|
252
|
+
}
|
253
|
+
if(options[@"contentImage"]){
|
254
|
+
userNotification.contentImage = [self getImageFromURL:options[@"contentImage"]];
|
255
|
+
}
|
256
|
+
|
257
|
+
if (sound != nil) {
|
258
|
+
userNotification.soundName = [sound isEqualToString: @"default"] ? NSUserNotificationDefaultSoundName : sound ;
|
259
|
+
}
|
260
|
+
|
261
|
+
if(options[@"ignoreDnD"]){
|
262
|
+
[userNotification setValue:@YES forKey:@"_ignoresDoNotDisturb"];
|
263
|
+
}
|
264
|
+
|
265
|
+
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
266
|
+
center.delegate = self;
|
267
|
+
[center scheduleNotification:userNotification];
|
268
|
+
}
|
269
|
+
|
270
|
+
- (void)removeNotificationWithGroupID:(NSString *)groupID;
|
271
|
+
{
|
272
|
+
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
273
|
+
for (NSUserNotification *userNotification in center.deliveredNotifications) {
|
274
|
+
if ([@"ALL" isEqualToString:groupID] || [userNotification.userInfo[@"groupID"] isEqualToString:groupID]) {
|
275
|
+
NSString *deliveredAt = [userNotification.actualDeliveryDate description];
|
276
|
+
printf("* Removing previously sent notification, which was sent on: %s\n", [deliveredAt UTF8String]);
|
277
|
+
[center removeDeliveredNotification:userNotification];
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
- (void)listNotificationWithGroupID:(NSString *)listGroupID;
|
283
|
+
{
|
284
|
+
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
285
|
+
|
286
|
+
NSMutableArray *lines = [NSMutableArray array];
|
287
|
+
for (NSUserNotification *userNotification in center.deliveredNotifications) {
|
288
|
+
NSString *deliveredgroupID = userNotification.userInfo[@"groupID"];
|
289
|
+
NSString *title = userNotification.title;
|
290
|
+
NSString *subtitle = userNotification.subtitle;
|
291
|
+
NSString *message = userNotification.informativeText;
|
292
|
+
NSString *deliveredAt = [userNotification.actualDeliveryDate description];
|
293
|
+
if ([@"ALL" isEqualToString:listGroupID] || [deliveredgroupID isEqualToString:listGroupID]) {
|
294
|
+
[lines addObject:[NSString stringWithFormat:@"%@\t%@\t%@\t%@\t%@", deliveredgroupID, title, subtitle, message, deliveredAt]];
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
if (lines.count > 0) {
|
299
|
+
printf("GroupID\tTitle\tSubtitle\tMessage\tDelivered At\n");
|
300
|
+
for (NSString *line in lines) {
|
301
|
+
printf("%s\n", [line UTF8String]);
|
302
|
+
}
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
|
307
|
+
- (void)userActivatedNotification:(NSUserNotification *)userNotification;
|
308
|
+
{
|
309
|
+
[[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:userNotification];
|
310
|
+
|
311
|
+
NSString *groupID = userNotification.userInfo[@"groupID"];
|
312
|
+
NSString *bundleID = userNotification.userInfo[@"bundleID"];
|
313
|
+
NSString *command = userNotification.userInfo[@"command"];
|
314
|
+
NSString *open = userNotification.userInfo[@"open"];
|
315
|
+
|
316
|
+
NSLog(@"User activated notification:");
|
317
|
+
NSLog(@" group ID: %@", groupID);
|
318
|
+
NSLog(@" title: %@", userNotification.title);
|
319
|
+
NSLog(@" subtitle: %@", userNotification.subtitle);
|
320
|
+
NSLog(@" message: %@", userNotification.informativeText);
|
321
|
+
NSLog(@"bundle ID: %@", bundleID);
|
322
|
+
NSLog(@" command: %@", command);
|
323
|
+
NSLog(@" open: %@", open);
|
324
|
+
|
325
|
+
BOOL success = YES;
|
326
|
+
if (bundleID) success &= [self activateAppWithBundleID:bundleID];
|
327
|
+
if (command) success &= [self executeShellCommand:command];
|
328
|
+
if (open) success &= [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:open]];
|
329
|
+
|
330
|
+
exit(success ? 0 : 1);
|
331
|
+
}
|
332
|
+
|
333
|
+
- (BOOL)activateAppWithBundleID:(NSString *)bundleID;
|
334
|
+
{
|
335
|
+
id app = [SBApplication applicationWithBundleIdentifier:bundleID];
|
336
|
+
if (app) {
|
337
|
+
[app activate];
|
338
|
+
return YES;
|
339
|
+
|
340
|
+
} else {
|
341
|
+
NSLog(@"Unable to find an application with the specified bundle indentifier.");
|
342
|
+
return NO;
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
- (BOOL)executeShellCommand:(NSString *)command;
|
347
|
+
{
|
348
|
+
NSPipe *pipe = [NSPipe pipe];
|
349
|
+
NSFileHandle *fileHandle = [pipe fileHandleForReading];
|
350
|
+
|
351
|
+
NSTask *task = [NSTask new];
|
352
|
+
task.launchPath = @"/bin/sh";
|
353
|
+
task.arguments = @[@"-c", command];
|
354
|
+
task.standardOutput = pipe;
|
355
|
+
task.standardError = pipe;
|
356
|
+
[task launch];
|
357
|
+
|
358
|
+
NSData *data = nil;
|
359
|
+
NSMutableData *accumulatedData = [NSMutableData data];
|
360
|
+
while ((data = [fileHandle availableData]) && [data length]) {
|
361
|
+
[accumulatedData appendData:data];
|
362
|
+
}
|
363
|
+
|
364
|
+
[task waitUntilExit];
|
365
|
+
NSLog(@"command output:\n%@", [[NSString alloc] initWithData:accumulatedData encoding:NSUTF8StringEncoding]);
|
366
|
+
return [task terminationStatus] == 0;
|
367
|
+
}
|
368
|
+
|
369
|
+
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
|
370
|
+
shouldPresentNotification:(NSUserNotification *)userNotification;
|
371
|
+
{
|
372
|
+
return YES;
|
373
|
+
}
|
374
|
+
|
375
|
+
// Once the notification is delivered we can exit.
|
376
|
+
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
377
|
+
didDeliverNotification:(NSUserNotification *)userNotification;
|
378
|
+
{
|
379
|
+
exit(0);
|
380
|
+
}
|
381
|
+
|
382
|
+
@end
|