-
Notifications
You must be signed in to change notification settings - Fork 28
/
mack.go
151 lines (125 loc) · 4.03 KB
/
mack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
** Mack
** A wrapper for AppleScript
*/
// Mack is a Golang wrapper for AppleScript. With Mack, you can easily trigger
// OS X desktop notifications and system sounds from within your Go application.
// Mack is ideal for local workflow optimization or OS X binary applications.
//
// Repository: http://github.com/everdev/mack
package mack
import (
"errors"
"os/exec"
"regexp"
"strings"
)
// Build the AppleScript command from a set of optional parameters, return the output
func run(command string) (string, error) {
cmd := exec.Command("osascript", "-e", command)
output, err := cmd.CombinedOutput()
prettyOutput := strings.Replace(string(output), "\n", "", -1)
// Ignore errors from the user hitting the cancel button
if err != nil && strings.Index(string(output), "User canceled.") < 0 {
return "", errors.New(err.Error() + ": " + prettyOutput + " (" + command + ")")
}
return prettyOutput, nil
}
// Build the AppleScript command from a set of optional parameters, return a Response
func runWithButtons(command string) (Response, error) {
output, err := run(command)
// Return if the user hit the default cancel button
if strings.Index(output, "execution error: User canceled. (-128)") > 0 {
response := Response{
Clicked: "Cancel",
}
return response, err
}
// Parse the buttons
re := regexp.MustCompile("buttons {(.*)}")
buttonMatches := re.FindStringSubmatch(command)
var buttons []string
if len(buttonMatches) > 1 {
buttons = strings.Split(buttonMatches[1], ",")
} else {
buttons = []string{"OK", "Cancel"}
}
return parseResponse(output, buttons), err
}
// Wrap text in quotes for proper command line formatting
func wrapInQuotes(text string) string {
return "\"" + text + "\""
}
// Build the AppleScript command, ignoring any blank optional parameters
func build(params ...string) string {
var validParams []string
for _, param := range params {
if param != "" {
validParams = append(validParams, param)
}
}
return strings.Join(validParams, " ")
}
// Construct an applescript string list
func mkList(items ...string) string {
var result []string
for _, item := range items {
result = append(result, wrapInQuotes(item))
}
return "{" + strings.Join(result, ",") + "}"
}
// Parse and format the button values
func makeButtonList(buttons string) string {
buttonList := strings.Split(buttons, ",")
if len(buttonList) > 3 {
buttonList = buttonList[:3]
}
var wrappedButtons []string
for _, button := range buttonList {
wrappedButtons = append(wrappedButtons, wrapInQuotes(strings.TrimSpace(button)))
}
return "buttons {" + strings.Join(wrappedButtons, ",") + "}"
}
// Parse a button response
func parseResponse(output string, buttons []string) Response {
var clicked, text string
var gaveUp bool
// Find out if the notification gave up
gaveUpRe := regexp.MustCompile("gave up:(true|false)")
gaveUpMatches := gaveUpRe.FindStringSubmatch(output)
if len(gaveUpMatches) > 1 && gaveUpMatches[1] == "true" {
gaveUp = true
}
if !gaveUp {
for _, button := range buttons {
// Find which button was clicked
button = strings.Trim(button, `"`)
buttonStr := "button returned:" + button
clickedRe := regexp.MustCompile(buttonStr + ",")
if clickedRe.MatchString(output) || output == buttonStr {
clicked = button
break
}
}
// Don't mess around with regex, just get the text returned
if strings.Index(output, ", text returned:") > 0 {
output = strings.Replace(output, "button returned:"+clicked+", ", "", 1)
output = strings.Replace(output, ", gave up:false", "", 1)
output = strings.Replace(output, "text returned:", "", 1)
text = output
}
}
// Find out if the user entered text
response := Response{
Clicked: clicked,
GaveUp: gaveUp,
Text: text,
}
return response
}
// The response format after a button click on an alert or dialog box
type Response struct {
Clicked string // The name of the button clicked
GaveUp bool // True if the user failed to respond in the duration specified
Text string // Only on Dialog boxes - The return value of the input field
}