How we use yo.app for actionable native notifications

iJake
Valued Contributor

At Cisco we had a need for a notification system that was more native looking than jamf helper but persistent unlike default Notification Center alerts. This led us to yo.app which is an open source notification alert utility.

By default the binary accepts arguments detailed on the project page linked above, which also means this can be scripted. The owner of the project provides a Python script that is meant for use with Casper but we found it limited in its customization ability. To that end, we wrote our bash script that allows us to create an entire alert as a policy using only script parameters. Those parameters are detailed in the screenshot below.

7ba91862538e443487fe73ece1fd9bab

Here is the script that we use:

#!/bin/bash

yoBinary="/PATH/TO/yo.app/Contents/MacOS/yo"
title=$(printf %q "$4")
subtitle=$(printf %q "$5")
info=$(printf %q "$6")
otherButton=$(printf %q "$7")
actionButton=$(printf %q "$8")
actionPath=$(printf %q "$9")
scriptURL=$(printf %q "${10}")
iconURL=$(printf %q "${11}")


grabConsoleUserAndHome(){
  currentUser=$(stat -f %Su "/dev/console")
  homeFolder=$(dscl . read "/Users/$currentUser" NFSHomeDirectory | cut -d: -f 2 | sed 's/^ *//'| tr -d '
')
  case "$homeFolder" in  
     * * )
           homeFolder=$(printf %q "$homeFolder")
          ;;
       *)
           ;;
esac
}

downloadIcon()
{
iconURLFile="${iconURL##*/}"
extension="${iconURLFile##*.}"
iconLocal="/tmp/icon."$extension""
rm -f "$iconLocal"
filerConnection=$(/usr/bin/curl -L -s -o /dev/null --silent --head --write-out '%{http_code}' "http://pkg.cloudapps.cisco.com/" --location-trusted -X GET)
if [[ "$filerConnection" = 200 ]]; then
    echo "Downloading Icon..."
    /usr/bin/curl -L "$iconURL" -o "$iconLocal" --location-trusted
else
    echo "Unable to download icon. Skipping..."
fi
}

downloadScript()
{
scriptLocal="/tmp/script.sh"
rm -f "$scriptLocal"
filerConnection=$(/usr/bin/curl -L -s -o /dev/null --silent --head --write-out '%{http_code}' "http://pkg.cloudapps.cisco.com/" --location-trusted -X GET)
if [[ "$filerConnection" = 200 ]]; then
    echo "Downloading Script..."
    /usr/bin/curl -L "$scriptURL" -o "$scriptLocal" --location-trusted
    chmod +x "$scriptLocal"
else
    echo "Unable to download script. Exiting..."
    exit 1
fi
}

grabConsoleUserAndHome

if [[ "$currentUser" == "root" ]]; then
    exit 0
fi

if [[ "$iconURL" != "''" ]]
    then
        downloadIcon
fi

if [[ "$scriptURL" != "''" ]]
    then
        downloadScript
fi

IFS=$'	
'
if [[ -z $otherButton ]]
    then
        if [[ -e "$iconLocal" ]] && [[ -e "$scriptLocal" ]]
            then
                su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --action-btn "$actionButton" --icon "$iconLocal" --bash-action "$scriptLocal" -p"      
            elif [[ -e "$iconLocal" ]]
                then
                    su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --action-btn "$actionButton" --action-path "$actionPath" --icon "$iconLocal" -p"   
            elif [[ -e "$scriptLocal" ]]
                then
                    su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --action-btn "$actionButton" --bash-action "$scriptLocal" -p"
            else
                su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --action-btn "$actionButton" --action-path "$actionPath" -p"
        fi
    else
        if [[ -e "$iconLocal" ]] && [[ -e "$scriptLocal" ]]
            then
                su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --other-btn "$otherButton" --action-btn "$actionButton" --icon "$iconLocal" --bash-action "$scriptLocal" -p"     
            elif [[ -e "$iconLocal" ]]
                then
                    su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --other-btn "$otherButton" --action-btn "$actionButton" --action-path "$actionPath" --icon "$iconLocal" -p"  
            elif [[ -e "$scriptLocal" ]]
                then
                    su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --other-btn "$otherButton" --action-btn "$actionButton" --bash-action "$scriptLocal" -p"
            else
                su - "$currentUser" -c "$yoBinary --title "$title" --subtitle "$subtitle" --info "$info" --other-btn "$otherButton" --action-btn "$actionButton" --action-path "$actionPath" -p"
        fi
fi

unset IFS

exit 0

Let me know if this proves useful for you or if you have any comments, suggestions.

6 REPLIES 6

mbezzo
Contributor III

Thanks for this - definitely bookmarking and will take a look when I'm able!

Matt

chase_tb
New Contributor II

Very cool. Thanks for posting!

davidhiggs
Contributor III

Thanks mate, will check it out!

iJake
Valued Contributor

As an update for those that want a preview this is what the alerts look like.

Standard icon from when you built the project with Xcode:
a305bef6938b463c9b95232f05d20191

Custom icon set with the policy:
40ab3b56ef3f4e7da08290f2b2fc9355

We occasionally have a need to track if people click the action button so for that we assign a script to the action button even if just opening a URL. (Note, though, any script called by the action button will run as the user.) This allows us to write a marker file on the system to grab with an EA.

#!/bin/bash

grabConsoleUserAndHome(){
  currentUser=$(stat -f %Su "/dev/console")
  homeFolder=$(dscl . read "/Users/$currentUser" NFSHomeDirectory | cut -d: -f 2 | sed 's/^ *//'| tr -d '
')
  case "$homeFolder" in  
     * * )
           homeFolder=$(printf %q "$homeFolder")
          ;;
       *)
           ;;
esac
}

grabConsoleUserAndHome

mkdir -p "$homeFolder"/PATH/TO/Attributes

touch "$homeFolder"/PATH/TO/Attributes/.attributegoeshere
echo "true" > "$homeFolder"/PATH/TO/Attributes/.attributegoeshere
open URLGOESHERE

davidhiggs
Contributor III

just wanted to say thanks @iJake, i've been using your script in testing and found it to be a reliable way to invoke yo.app using JAMF policies. much appreciated!

iJake
Valued Contributor

@davidhiggs Happy to hear you're using it and glad to help!