Repository: slack-go/slack
Pull request #882 (2021-01-12 13:59:24)

Allow message unfurling with auth URL
This PR is basically a continuation of prior work by @kautsig in #839. I've just extracted his change into a separate branch for easier merging and added some test cases.

## API changes

- Adds UnfurlMessageWithAuthURL method to the client
- Adds UnfurlMessageWithAuthURLContext method to the client
- Adds MsgOptionUnfurlAuthURL used within the two methods above

The Unfurl methods were added to be consistent with the UnfurlMessage method already exposed by the client.



Patch:
diff --git a/chat.go b/chat.go
index 439f937..f5b8034 100644
--- a/chat.go
+++ b/chat.go
@@ -191,6 +191,22 @@ func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string
 	return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...))
 }
 
+// UnfurlMessageWithAuthURL sends an unfurl request containing an
+// authentication URL.
+// For more details see:
+// https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
+func (api *Client) UnfurlMessageWithAuthURL(channelID, timestamp string, userAuthURL string, options ...MsgOption) (string, string, string, error) {
+	return api.UnfurlMessageWithAuthURLContext(context.Background(), channelID, timestamp, userAuthURL, options...)
+}
+
+// UnfurlMessageWithAuthURLContext sends an unfurl request containing an
+// authentication URL.
+// For more details see:
+// https://api.slack.com/reference/messaging/link-unfurling#authenticated_unfurls
+func (api *Client) UnfurlMessageWithAuthURLContext(ctx context.Context, channelID, timestamp string, userAuthURL string, options ...MsgOption) (string, string, string, error) {
+	return api.SendMessageContext(ctx, channelID, MsgOptionUnfurlAuthURL(timestamp, userAuthURL), MsgOptionCompose(options...))
+}
+
 // SendMessage more flexible method for configuring messages.
 func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
 	return api.SendMessageContext(context.Background(), channel, options...)
@@ -413,6 +429,38 @@ func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption
 	}
 }
 
+// MsgOptionUnfurlAuthURL unfurls a message using an auth url based on the timestamp.
+func MsgOptionUnfurlAuthURL(timestamp string, userAuthURL string) MsgOption {
+	return func(config *sendConfig) error {
+		config.endpoint = config.apiurl + string(chatUnfurl)
+		config.values.Add("ts", timestamp)
+		config.values.Add("user_auth_url", userAuthURL)
+		return nil
+	}
+}
+
+// MsgOptionUnfurlAuthRequired requests that the user installs the
+// Slack app for unfurling.
+func MsgOptionUnfurlAuthRequired(timestamp string) MsgOption {
+	return func(config *sendConfig) error {
+		config.endpoint = config.apiurl + string(chatUnfurl)
+		config.values.Add("ts", timestamp)
+		config.values.Add("user_auth_required", "true")
+		return nil
+	}
+}
+
+// MsgOptionUnfurlAuthMessage attaches a message inviting the user to
+// authenticate.
+func MsgOptionUnfurlAuthMessage(timestamp string, msg string) MsgOption {
+	return func(config *sendConfig) error {
+		config.endpoint = config.apiurl + string(chatUnfurl)
+		config.values.Add("ts", timestamp)
+		config.values.Add("user_auth_message", msg)
+		return nil
+	}
+}
+
 // MsgOptionResponseURL supplies a url to use as the endpoint.
 func MsgOptionResponseURL(url string, responseType string) MsgOption {
 	return func(config *sendConfig) error {


Test patch:
diff --git a/chat_test.go b/chat_test.go
index 450cae7..40cea90 100644
--- a/chat_test.go
+++ b/chat_test.go
@@ -73,6 +73,7 @@ func TestGetPermalink(t *testing.T) {
 
 func TestPostMessage(t *testing.T) {
 	type messageTest struct {
+		endpoint string
 		opt      []MsgOption
 		expected url.Values
 	}
@@ -82,6 +83,7 @@ func TestPostMessage(t *testing.T) {
 
 	tests := map[string]messageTest{
 		"Blocks": {
+			endpoint: "/chat.postMessage",
 			opt: []MsgOption{
 				MsgOptionBlocks(blocks...),
 				MsgOptionText("text", false),
@@ -94,6 +96,7 @@ func TestPostMessage(t *testing.T) {
 			},
 		},
 		"Attachment": {
+			endpoint: "/chat.postMessage",
 			opt: []MsgOption{
 				MsgOptionAttachments(
 					Attachment{
@@ -106,6 +109,54 @@ func TestPostMessage(t *testing.T) {
 				"token":       []string{"testing-token"},
 			},
 		},
+		"Unfurl": {
+			endpoint: "/chat.unfurl",
+			opt: []MsgOption{
+				MsgOptionUnfurl("123", map[string]Attachment{"something": {Text: "attachment-test"}}),
+			},
+			expected: url.Values{
+				"channel": []string{"CXXX"},
+				"token":   []string{"testing-token"},
+				"ts":      []string{"123"},
+				"unfurls": []string{`{"something":{"text":"attachment-test","blocks":null}}`},
+			},
+		},
+		"UnfurlAuthURL": {
+			endpoint: "/chat.unfurl",
+			opt: []MsgOption{
+				MsgOptionUnfurlAuthURL("123", "https://auth-url.com"),
+			},
+			expected: url.Values{
+				"channel":       []string{"CXXX"},
+				"token":         []string{"testing-token"},
+				"ts":            []string{"123"},
+				"user_auth_url": []string{"https://auth-url.com"},
+			},
+		},
+		"UnfurlAuthRequired": {
+			endpoint: "/chat.unfurl",
+			opt: []MsgOption{
+				MsgOptionUnfurlAuthRequired("123"),
+			},
+			expected: url.Values{
+				"channel":            []string{"CXXX"},
+				"token":              []string{"testing-token"},
+				"ts":                 []string{"123"},
+				"user_auth_required": []string{"true"},
+			},
+		},
+		"UnfurlAuthMessage": {
+			endpoint: "/chat.unfurl",
+			opt: []MsgOption{
+				MsgOptionUnfurlAuthMessage("123", "Please!"),
+			},
+			expected: url.Values{
+				"channel":           []string{"CXXX"},
+				"token":             []string{"testing-token"},
+				"ts":                []string{"123"},
+				"user_auth_message": []string{"Please!"},
+			},
+		},
 	}
 
 	once.Do(startServer)
@@ -114,7 +165,7 @@ func TestPostMessage(t *testing.T) {
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
 			http.DefaultServeMux = new(http.ServeMux)
-			http.HandleFunc("/chat.postMessage", func(rw http.ResponseWriter, r *http.Request) {
+			http.HandleFunc(test.endpoint, func(rw http.ResponseWriter, r *http.Request) {
 				body, err := ioutil.ReadAll(r.Body)
 				if err != nil {
 					t.Errorf("unexpected error: %v", err)