From 8ac50524c114325b3859f3b648c7eb6046308b30 Mon Sep 17 00:00:00 2001 From: Shea Craig Date: Tue, 28 Apr 2015 11:41:36 -0400 Subject: [PATCH 1/5] Implement bash commands as an action. --- yo/AppDelegate.swift | 7 +++++++ yo/YoCommandLine.swift | 5 +++-- yo/YoNotification.swift | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/yo/AppDelegate.swift b/yo/AppDelegate.swift index ab2ded9..ba40a89 100644 --- a/yo/AppDelegate.swift +++ b/yo/AppDelegate.swift @@ -32,12 +32,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele // Test for that, and if so, execute the option tucked away in the userInfo dict. if let notification = aNotification.userInfo![NSApplicationLaunchUserNotificationKey] as? NSUserNotification { let task = NSTask() + // It's safe to just open nothing, so this is the default. task.launchPath = "/usr/bin/open" if let action = notification.userInfo!["action"] as? String { NSLog("User activated notification with action: \(action)") task.arguments = [action] } + if let bashAction = notification.userInfo!["bashAction"] as? String { + task.launchPath = "/bin/bash" + NSLog("User activated notification with action: \(bashAction)") + task.arguments = ["-c"] + [bashAction] + } + task.launch() // We're done. exit(0) diff --git a/yo/YoCommandLine.swift b/yo/YoCommandLine.swift index 8f7ea81..698a991 100644 --- a/yo/YoCommandLine.swift +++ b/yo/YoCommandLine.swift @@ -34,12 +34,13 @@ class YoCommandLine { let icon = StringOption(shortFlag: "i", longFlag: "icon", required: false, helpMessage: "Complete path to an alternate icon to use for the notification.") let contentImage = StringOption(shortFlag: "c", longFlag: "content-image", required: false, helpMessage: "Path to an image to use for the notification's 'contentImage' property.") let deliverySound = StringOption(shortFlag: "z", longFlag: "delivery-sound", required: false, helpMessage: "The name of the sound to play when delivering. Usually this is the filename of a system sound minus the extension. See the README for more info.") - let action = StringOption(shortFlag: "a", longFlag: "action-path", required: false, helpMessage: "Application to open if user selects the action button. Provide the full path as the argument. This option only does something if -b/--action-btn is also specified. Defaults to opening nothing.") + let action = StringOption(shortFlag: "a", longFlag: "action-path", required: false, helpMessage: "Application to open if user selects the action button. Provide the full path as the argument. This option only does something if -b/--action-btn is also specified.") + let bashAction = StringOption(shortFlag: "B", longFlag: "bash-action", required: false, helpMessage: "Bash script to run. Be sure to properly escape all reserved characters. This option only does something if -b/--action-btn is also specified. Defaults to opening nothing.") let help = BoolOption(shortFlag: "h", longFlag: "help", helpMessage: "Show help.") init () { // Add arguments to commandline object and handle errors or help requests. - cli.addOptions(banner, title, ignoresDoNotDisturb, lockscreenOnly, subtitle, informativeText, actionBtnText, otherBtnText, poofsOnCancel, icon, contentImage, action, deliverySound, help) + cli.addOptions(title, subtitle, informativeText, actionBtnText, action, bashAction, otherBtnText, icon, contentImage, deliverySound, ignoresDoNotDisturb, lockscreenOnly, poofsOnCancel,banner, help) let (success, error) = cli.parse() if help.value { cli.printUsage() diff --git a/yo/YoNotification.swift b/yo/YoNotification.swift index 0a4d686..e540f6b 100644 --- a/yo/YoNotification.swift +++ b/yo/YoNotification.swift @@ -81,6 +81,10 @@ class YoNotification: NSObject { notification.userInfo = ["sender": "org.da.yo"] notification.userInfo = ["action": action] } + if let bashAction = arguments.bashAction.value { + notification.userInfo = ["sender": "org.da.yo"] + notification.userInfo = ["bashAction": bashAction] + } // Optional Other button (defaults to "Cancel") if let otherBtnTitle = arguments.otherBtnText.value { From b13e1ebddecb90f8a13252093e9696a4d2bc1092 Mon Sep 17 00:00:00 2001 From: Shea Craig Date: Tue, 28 Apr 2015 14:23:32 -0400 Subject: [PATCH 2/5] Revise sound documentation. Default to NSUserNotificationDefaultSoundName for sound. --- yo/YoCommandLine.swift | 2 +- yo/YoNotification.swift | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/yo/YoCommandLine.swift b/yo/YoCommandLine.swift index 698a991..e87d2ba 100644 --- a/yo/YoCommandLine.swift +++ b/yo/YoCommandLine.swift @@ -33,7 +33,7 @@ class YoCommandLine { let poofsOnCancel = BoolOption(shortFlag: "p", longFlag: "poofs-on-cancel", helpMessage: "Set to make your notification 'poof' when the cancel button is hit.") let icon = StringOption(shortFlag: "i", longFlag: "icon", required: false, helpMessage: "Complete path to an alternate icon to use for the notification.") let contentImage = StringOption(shortFlag: "c", longFlag: "content-image", required: false, helpMessage: "Path to an image to use for the notification's 'contentImage' property.") - let deliverySound = StringOption(shortFlag: "z", longFlag: "delivery-sound", required: false, helpMessage: "The name of the sound to play when delivering. Usually this is the filename of a system sound minus the extension. See the README for more info.") + let deliverySound = StringOption(shortFlag: "z", longFlag: "delivery-sound", required: false, helpMessage: "The name of the sound to play when delivering or 'None'. The name must not include the extension, nor any path components, and should be located in '/Library/Sounds' or '~/Library/Sounds'. (Defaults to the system's default notification sound). See the README for more info.") let action = StringOption(shortFlag: "a", longFlag: "action-path", required: false, helpMessage: "Application to open if user selects the action button. Provide the full path as the argument. This option only does something if -b/--action-btn is also specified.") let bashAction = StringOption(shortFlag: "B", longFlag: "bash-action", required: false, helpMessage: "Bash script to run. Be sure to properly escape all reserved characters. This option only does something if -b/--action-btn is also specified. Defaults to opening nothing.") let help = BoolOption(shortFlag: "h", longFlag: "help", helpMessage: "Show help.") diff --git a/yo/YoNotification.swift b/yo/YoNotification.swift index e540f6b..09008ef 100644 --- a/yo/YoNotification.swift +++ b/yo/YoNotification.swift @@ -45,7 +45,17 @@ class YoNotification: NSObject { // Delivery sound. if let deliverySound = arguments.deliverySound.value { - notification.soundName = deliverySound + // If you pass a name that doesn't exist it will be nil anyway, but this allows us + // to set None explicitly. + if deliverySound == "None" { + notification.soundName = nil + } + else { + notification.soundName = deliverySound + } + } + else { + notification.soundName = NSUserNotificationDefaultSoundName } // Image elements. From 01547f08ba8d23c99dcb3533efda2c178cf7324c Mon Sep 17 00:00:00 2001 From: Shea Craig Date: Tue, 28 Apr 2015 14:31:48 -0400 Subject: [PATCH 3/5] Update documentation. --- README.md | 77 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index d27a41b..d588ba2 100644 --- a/README.md +++ b/README.md @@ -32,34 +32,36 @@ A shell script is provided (installed to ```/usr/local/bin/yo``` if you use the ``` Usage: /Applications/Utilities/yo.app/Contents/MacOS/yo [options] - -m, --banner-mode: - Does not work! Set if you would like to send a non-persistent notification. No buttons will be available if set. - -t, --title: - Title for notification. REQUIRED. - -d, --ignores-do-not-disturb: - Set to make your notification appear even if computer is in do-not-disturb mode. - -l, --lockscreen-only: - Set to make your notification appear only if computer is locked. If set, no buttons will be available. - -s, --subtitle: - Subtitle for notification. - -n, --info: - Informative text. - -b, --action-btn: - Include an action button, with the button label text supplied to this argument. - -o, --other-btn: - Alternate label for cancel button text. - -p, --poofs-on-cancel: - Set to make your notification 'poof' when the cancel button is hit. - -i, --icon: - Complete path to an alternate icon to use for the notification. - -c, --content-image: - Path to an image to use for the notification's 'contentImage' property. - -a, --action-path: - Application to open if user selects the action button. Provide the full path as the argument. This option only does something if -b/--action-btn is also specified. Defaults to opening nothing. - -z, --delivery-sound: - The name of the sound to play when delivering. Usually this is the filename of a system sound minus the extension. See the README for more info. - -h, --help: - Show help. +-t, --title: +Title for notification. REQUIRED. +-s, --subtitle: +Subtitle for notification. +-n, --info: +Informative text. +-b, --action-btn: +Include an action button, with the button label text supplied to this argument. +-a, --action-path: +Application to open if user selects the action button. Provide the full path as the argument. This option only does something if -b/--action-btn is also specified. +-B, --bash-action: +Bash script to run. Be sure to properly escape all reserved characters. This option only does something if -b/--action-btn is also specified. Defaults to opening nothing. +-o, --other-btn: +Alternate label for cancel button text. +-i, --icon: +Complete path to an alternate icon to use for the notification. +-c, --content-image: +Path to an image to use for the notification's 'contentImage' property. +-z, --delivery-sound: +Optional (Defaults to the system's default notification sound). The name of the sound to play when delivering or 'None'. The name must not include the extension, nor any path components, and should be located in '/Library/Sounds' or '~/Library/Sounds'. See the README for more info. +-d, --ignores-do-not-disturb: +Set to make your notification appear even if computer is in do-not-disturb mode. +-l, --lockscreen-only: +Set to make your notification appear only if computer is locked. If set, no buttons will be available. +-p, --poofs-on-cancel: +Set to make your notification 'poof' when the cancel button is hit. +-m, --banner-mode: +Does not work! Set if you would like to send a non-persistent notification. No buttons will be available if set. +-h, --help: +Show help. ``` Notes: @@ -67,13 +69,22 @@ Notes: - -m/--banner-mode does not seem to work at this time. - The action argument needs a path or URL. yo just calls ```open```, so anything that would work there, should work here. - If a "cancel" button doesn't make sense for your needs, but you don't want two buttons on your notification, just use ```-o/--other-btn``` with a label that seems appropriate, like "Accept", or perhaps "Confirm", but no ```-b/--btext```. -- Sounds! If you want to use a different sound, you must provide the name of the sound to the -z argument, not the filename. I.e. "Sosumi". The search path is: - - ~/Library/Sounds - - /Library/Sounds - - /Network/Library/Sounds - - /System/Library/Sounds - Remember, this is (probably) a Bash shell. If you don't escape reserved characters like ```!``` you may get unexpected results. (```!``` is the Bash history expansion operator!) +### Sound +If you want to use a different sound, you must provide the *name* of the sound to the -z argument, not the filename, and not the path. I.e "Sosumi", not "Sosumi.aiff" or "/System/Library/Sosumi.aiff". + +The search path is: +- ~/Library/Sounds +- /Library/Sounds +- /Network/Library/Sounds +- /System/Library/Sounds (This is where all of the builtin sounds live) + +If you want to include a custom sound, it needs to be available in one of those paths. So for example, if you wanted to use the sound file "TotalEclipseOfTheHeart.aiff", copy it to ```/Library/Sounds``` (which may not exist by default), and use the delivery sound option like this: +```yo.ap -t "Some title" -z "TotalEclipseOfTheHeart" + +Sounds must be a aiff; extension .aif is not valid. + ### Examples ``` # Example of basic notification: From 9ca72e091c2a53a068f735f9154dfc38e923d6f7 Mon Sep 17 00:00:00 2001 From: Shea Craig Date: Tue, 28 Apr 2015 14:38:27 -0400 Subject: [PATCH 4/5] Bump version, update CHANGELOG, include docs in Resources. --- CHANGELOG.md | 12 +++++++++++- yo.xcodeproj/project.pbxproj | 13 ++++++++++--- yo/Info.plist | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb2b2ae..231bccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. This projec ## [Unreleased][unreleased] +## [1.0.2] - 2015-04-28 +### Added +- Ability to execute bash scripts using the ```-B/--bash-action``` argument. + +### Changed +- Clarified instructions for specifying sounds. +- Defaults to using the default notification sound now. +- If you set a sound to None, i.e. ```-z None``` there will be no sound. + ## [1.0.1] - 2015-04-27 ### Added - License added to all source. @@ -20,5 +29,6 @@ All notable changes to this project will be documented in this file. This projec ### Added - Initial commit. -[unreleased]: https://github.com/sheagcraig/yo/compare/1.0.1...HEAD +[unreleased]: https://github.com/sheagcraig/yo/compare/1.0.2...HEAD +[1.0.2]: https://github.com/sheagcraig/yo/compare/1.0.1...1.0.2 [1.0.1]: https://github.com/sheagcraig/yo/compare/1.0...1.0.1 diff --git a/yo.xcodeproj/project.pbxproj b/yo.xcodeproj/project.pbxproj index 72ca423..f381c98 100644 --- a/yo.xcodeproj/project.pbxproj +++ b/yo.xcodeproj/project.pbxproj @@ -15,12 +15,15 @@ 841717B81ABCA11300150A32 /* YoCommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841717B41ABCA11300150A32 /* YoCommandLine.swift */; }; 841717B91ABCA11300150A32 /* YoNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841717B51ABCA11300150A32 /* YoNotification.swift */; }; 841717C21ABCA40200150A32 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 841717C11ABCA40200150A32 /* README.md */; }; + 9A024C6B1AF009B800C34EE4 /* CHANGELOG.md in Sources */ = {isa = PBXBuildFile; fileRef = 9A024C6A1AF009B800C34EE4 /* CHANGELOG.md */; }; + 9A024C6C1AF00ADE00C34EE4 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 841717C11ABCA40200150A32 /* README.md */; }; + 9A024C6D1AF00AE200C34EE4 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 9A024C6A1AF009B800C34EE4 /* CHANGELOG.md */; }; 9A16F3161AEAA2AC001336EA /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A16F3131AEAA2AC001336EA /* CommandLine.swift */; }; 9A16F3171AEAA2AC001336EA /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A16F3141AEAA2AC001336EA /* Option.swift */; }; 9A16F3181AEAA2AC001336EA /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A16F3151AEAA2AC001336EA /* StringExtensions.swift */; }; 9A16F31A1AEAA335001336EA /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 9A16F3191AEAA335001336EA /* LICENSE */; }; 9A16F32F1AEACCFC001336EA /* yo.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9A16F32E1AEACCFC001336EA /* yo.sh */; }; - 9A16F3311AEE9076001336EA /* CHANGELOG.md in Sources */ = {isa = PBXBuildFile; fileRef = 9A16F3301AEE9076001336EA /* CHANGELOG.md */; }; + 9A16F3311AEE9076001336EA /* (null) in Sources */ = {isa = PBXBuildFile; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -59,6 +62,7 @@ 841717B41ABCA11300150A32 /* YoCommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YoCommandLine.swift; sourceTree = ""; }; 841717B51ABCA11300150A32 /* YoNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YoNotification.swift; sourceTree = ""; }; 841717C11ABCA40200150A32 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 9A024C6A1AF009B800C34EE4 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 9A16F3131AEAA2AC001336EA /* CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandLine.swift; sourceTree = ""; }; 9A16F3141AEAA2AC001336EA /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Option.swift; sourceTree = ""; }; 9A16F3151AEAA2AC001336EA /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; @@ -87,10 +91,10 @@ 841717891ABCA0CE00150A32 = { isa = PBXGroup; children = ( - 9A16F3301AEE9076001336EA /* CHANGELOG.md */, 9A16F3191AEAA335001336EA /* LICENSE */, 9A16F32E1AEACCFC001336EA /* yo.sh */, 841717C11ABCA40200150A32 /* README.md */, + 9A024C6A1AF009B800C34EE4 /* CHANGELOG.md */, 841717941ABCA0CE00150A32 /* yo */, 841717A51ABCA0CE00150A32 /* yoTests */, 841717931ABCA0CE00150A32 /* Products */, @@ -229,6 +233,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A024C6D1AF00AE200C34EE4 /* CHANGELOG.md in Resources */, + 9A024C6C1AF00ADE00C34EE4 /* README.md in Resources */, 9A16F31A1AEAA335001336EA /* LICENSE in Resources */, 8417179A1ABCA0CE00150A32 /* Images.xcassets in Resources */, 9A16F32F1AEACCFC001336EA /* yo.sh in Resources */, @@ -251,13 +257,14 @@ buildActionMask = 2147483647; files = ( 9A16F3171AEAA2AC001336EA /* Option.swift in Sources */, + 9A024C6B1AF009B800C34EE4 /* CHANGELOG.md in Sources */, 841717B71ABCA11300150A32 /* ViewController.swift in Sources */, 9A16F3181AEAA2AC001336EA /* StringExtensions.swift in Sources */, 841717B61ABCA11300150A32 /* AppDelegate.swift in Sources */, 841717C21ABCA40200150A32 /* README.md in Sources */, 841717B91ABCA11300150A32 /* YoNotification.swift in Sources */, 9A16F3161AEAA2AC001336EA /* CommandLine.swift in Sources */, - 9A16F3311AEE9076001336EA /* CHANGELOG.md in Sources */, + 9A16F3311AEE9076001336EA /* (null) in Sources */, 841717B81ABCA11300150A32 /* YoCommandLine.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/yo/Info.plist b/yo/Info.plist index 1f8fbe3..767610b 100644 --- a/yo/Info.plist +++ b/yo/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.1 + 1.0.2 CFBundleSignature ???? CFBundleVersion - 1.0.1 + 1.0.2 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From f21b5c829af4b0ad14af8c8878aed50673e41564 Mon Sep 17 00:00:00 2001 From: Shea Craig Date: Tue, 28 Apr 2015 14:42:41 -0400 Subject: [PATCH 5/5] Add examples. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d588ba2..011606c 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,8 @@ Show help. Notes: - Title is mandatory. All other arguments are optional. -- -m/--banner-mode does not seem to work at this time. -- The action argument needs a path or URL. yo just calls ```open```, so anything that would work there, should work here. +- ```-m/--banner-mode``` does not seem to work at this time. +- The ```-a/--action``` argument needs a path or URL. yo just calls ```open```, so anything that would work there, should work here. - If a "cancel" button doesn't make sense for your needs, but you don't want two buttons on your notification, just use ```-o/--other-btn``` with a label that seems appropriate, like "Accept", or perhaps "Confirm", but no ```-b/--btext```. - Remember, this is (probably) a Bash shell. If you don't escape reserved characters like ```!``` you may get unexpected results. (```!``` is the Bash history expansion operator!) @@ -105,6 +105,8 @@ Sounds must be a aiff; extension .aif is not valid. # Example-alternate icon using the -i argument /Applications/Utilities/yo.app/Contents/MacOS/yo -t "Taco Time" -i "/Users/blanconino/Pictures/taco.png" +# Example-custom sound and bash script with escaped characters. +/Applications/Utilities/yo.app/Contents/MacOS/yo -t "Taco Time" -z "Taco" -b "Eat" -B "say 'I hope you enjoyed your tacos\!'" ``` ### Application Icon, Caveats, and Nerdery