/
modal.go
158 lines (133 loc) · 5.07 KB
/
modal.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
152
153
154
155
156
157
158
// Modal example - How to respond to a slash command with an interactive modal and parse the response
// The flow of this example:
// 1. User trigers your app with a slash command (e.g. /modaltest) that will send a request to http://URL/slash and respond with a request to open a modal
// 2. User fills out fields first and last name in modal and hits submit
// 3. This will send a request to http://URL/modal and send a greeting message to the user
// Note: Within your slack app you will need to enable and provide a URL for "Interactivity & Shortcuts" and "Slash Commands"
// Note: Be sure to update YOUR_SIGNING_SECRET_HERE and YOUR_TOKEN_HERE
// You can use ngrok to test this example: https://api.slack.com/tutorials/tunneling-with-ngrok
// Helpful slack documentation to learn more: https://api.slack.com/interactivity/handling
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/slack-go/slack"
)
func generateModalRequest() slack.ModalViewRequest {
// Create a ModalViewRequest with a header and two inputs
titleText := slack.NewTextBlockObject("plain_text", "My App", false, false)
closeText := slack.NewTextBlockObject("plain_text", "Close", false, false)
submitText := slack.NewTextBlockObject("plain_text", "Submit", false, false)
headerText := slack.NewTextBlockObject("mrkdwn", "Please enter your name", false, false)
headerSection := slack.NewSectionBlock(headerText, nil, nil)
firstNameText := slack.NewTextBlockObject("plain_text", "First Name", false, false)
firstNameHint := slack.NewTextBlockObject("plain_text", "First Name Hint", false, false)
firstNamePlaceholder := slack.NewTextBlockObject("plain_text", "Enter your first name", false, false)
firstNameElement := slack.NewPlainTextInputBlockElement(firstNamePlaceholder, "firstName")
// Notice that blockID is a unique identifier for a block
firstName := slack.NewInputBlock("First Name", firstNameText, firstNameHint, firstNameElement)
lastNameText := slack.NewTextBlockObject("plain_text", "Last Name", false, false)
lastNameHint := slack.NewTextBlockObject("plain_text", "Last Name Hint", false, false)
lastNamePlaceholder := slack.NewTextBlockObject("plain_text", "Enter your first name", false, false)
lastNameElement := slack.NewPlainTextInputBlockElement(lastNamePlaceholder, "lastName")
lastName := slack.NewInputBlock("Last Name", lastNameText, lastNameHint, lastNameElement)
blocks := slack.Blocks{
BlockSet: []slack.Block{
headerSection,
firstName,
lastName,
},
}
var modalRequest slack.ModalViewRequest
modalRequest.Type = slack.ViewType("modal")
modalRequest.Title = titleText
modalRequest.Close = closeText
modalRequest.Submit = submitText
modalRequest.Blocks = blocks
return modalRequest
}
// This was taken from the slash example
// https://github.com/slack-go/slack/blob/master/examples/slash/slash.go
func verifySigningSecret(r *http.Request) error {
signingSecret := "YOUR_SIGNING_SECRET_HERE"
verifier, err := slack.NewSecretsVerifier(r.Header, signingSecret)
if err != nil {
fmt.Println(err.Error())
return err
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err.Error())
return err
}
// Need to use r.Body again when unmarshalling SlashCommand and InteractionCallback
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
verifier.Write(body)
if err = verifier.Ensure(); err != nil {
fmt.Println(err.Error())
return err
}
return nil
}
func handleSlash(w http.ResponseWriter, r *http.Request) {
err := verifySigningSecret(r)
if err != nil {
fmt.Printf(err.Error())
w.WriteHeader(http.StatusUnauthorized)
return
}
s, err := slack.SlashCommandParse(r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println(err.Error())
return
}
switch s.Command {
case "/humboldttest":
api := slack.New("YOUR_TOKEN_HERE")
modalRequest := generateModalRequest()
_, err = api.OpenView(s.TriggerID, modalRequest)
if err != nil {
fmt.Printf("Error opening view: %s", err)
}
default:
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func handleModal(w http.ResponseWriter, r *http.Request) {
err := verifySigningSecret(r)
if err != nil {
fmt.Printf(err.Error())
w.WriteHeader(http.StatusUnauthorized)
return
}
var i slack.InteractionCallback
err = json.Unmarshal([]byte(r.FormValue("payload")), &i)
if err != nil {
fmt.Printf(err.Error())
w.WriteHeader(http.StatusUnauthorized)
return
}
// Note there might be a better way to get this info, but I figured this structure out from looking at the json response
firstName := i.View.State.Values["First Name"]["firstName"].Value
lastName := i.View.State.Values["Last Name"]["lastName"].Value
msg := fmt.Sprintf("Hello %s %s, nice to meet you!", firstName, lastName)
api := slack.New("YOUR_TOKEN_HERE")
_, _, err = api.PostMessage(i.User.ID,
slack.MsgOptionText(msg, false),
slack.MsgOptionAttachments())
if err != nil {
fmt.Printf(err.Error())
w.WriteHeader(http.StatusUnauthorized)
return
}
}
func main() {
http.HandleFunc("/slash", handleSlash)
http.HandleFunc("/modal", handleModal)
http.ListenAndServe(":4390", nil)
}