package notificator import ( "fmt" "os/exec" "runtime" "strconv" "strings" ) type Options struct { DefaultIcon string AppName string } const ( UR_NORMAL = "normal" UR_CRITICAL = "critical" ) type notifier interface { push(title string, text string, iconPath string) *exec.Cmd pushCritical(title string, text string, iconPath string) *exec.Cmd } type Notificator struct { notifier notifier defaultIcon string } func (n Notificator) Push(title string, text string, iconPath string, urgency string) error { icon := n.defaultIcon if iconPath != "" { icon = iconPath } if urgency == UR_CRITICAL { return n.notifier.pushCritical(title, text, icon).Run() } return n.notifier.push(title, text, icon).Run() } type osxNotificator struct { AppName string } func (o osxNotificator) push(title string, text string, iconPath string) *exec.Cmd { // Checks if terminal-notifier exists, and is accessible. term_notif := CheckTermNotif() os_version_check := CheckMacOSVersion() // if terminal-notifier exists, use it. // else, fall back to osascript. (Mavericks and later.) if term_notif == true { return exec.Command("terminal-notifier", "-title", o.AppName, "-message", text, "-subtitle", title, "-appIcon", iconPath) } else if os_version_check == true { title = strings.Replace(title, `"`, `\"`, -1) text = strings.Replace(text, `"`, `\"`, -1) notification := fmt.Sprintf("display notification \"%s\" with title \"%s\" subtitle \"%s\"", text, o.AppName, title) return exec.Command("osascript", "-e", notification) } // finally falls back to growlnotify. return exec.Command("growlnotify", "-n", o.AppName, "--image", iconPath, "-m", title) } // Causes the notification to stick around until clicked. func (o osxNotificator) pushCritical(title string, text string, iconPath string) *exec.Cmd { // same function as above... term_notif := CheckTermNotif() os_version_check := CheckMacOSVersion() if term_notif == true { // timeout set to 30 seconds, to show the importance of the notification return exec.Command("terminal-notifier", "-title", o.AppName, "-message", text, "-subtitle", title, "-timeout", "30") } else if os_version_check == true { notification := fmt.Sprintf("display notification \"%s\" with title \"%s\" subtitle \"%s\"", text, o.AppName, title) return exec.Command("osascript", "-e", notification) } return exec.Command("growlnotify", "-n", o.AppName, "--image", iconPath, "-m", title) } type linuxNotificator struct{} func (l linuxNotificator) push(title string, text string, iconPath string) *exec.Cmd { return exec.Command("notify-send", "-i", iconPath, title, text) } // Causes the notification to stick around until clicked. func (l linuxNotificator) pushCritical(title string, text string, iconPath string) *exec.Cmd { return exec.Command("notify-send", "-i", iconPath, title, text, "-u", "critical") } type windowsNotificator struct{} func (w windowsNotificator) push(title string, text string, iconPath string) *exec.Cmd { return exec.Command("growlnotify", "/i:", iconPath, "/t:", title, text) } // Causes the notification to stick around until clicked. func (w windowsNotificator) pushCritical(title string, text string, iconPath string) *exec.Cmd { return exec.Command("notify-send", "-i", iconPath, title, text, "/s", "true", "/p", "2") } func New(o Options) *Notificator { var Notifier notifier switch runtime.GOOS { case "darwin": Notifier = osxNotificator{AppName: o.AppName} case "linux": Notifier = linuxNotificator{} case "windows": Notifier = windowsNotificator{} } return &Notificator{notifier: Notifier, defaultIcon: o.DefaultIcon} } // Helper function for macOS func CheckTermNotif() bool { // Checks if terminal-notifier exists, and is accessible. if err := exec.Command("which", "terminal-notifier").Run(); err != nil { return false } // no error, so return true. (terminal-notifier exists) return true } func CheckMacOSVersion() bool { // Checks if the version of macOS is 10.9 or Higher (osascript support for notifications.) cmd := exec.Command("sw_vers", "-productVersion") check, _ := cmd.Output() version := strings.Split(strings.TrimSpace(string(check)), ".") // semantic versioning of macOS major, _ := strconv.Atoi(version[0]) minor, _ := strconv.Atoi(version[1]) if major < 10 { return false } else if major == 10 && minor < 9 { return false } else { return true } }