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/README.md b/README.md index d27a41b..011606c 100644 --- a/README.md +++ b/README.md @@ -32,48 +32,59 @@ 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: - 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```. -- 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: @@ -94,6 +105,8 @@ Notes: # 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 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/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/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 diff --git a/yo/YoCommandLine.swift b/yo/YoCommandLine.swift index 8f7ea81..e87d2ba 100644 --- a/yo/YoCommandLine.swift +++ b/yo/YoCommandLine.swift @@ -33,13 +33,14 @@ 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 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 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.") 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..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. @@ -81,6 +91,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 {