I’m working on a Slack app in Go, using the slack-go library. I’ve found some references to what sound like similar issues, but none of the recommended solutions seem to work for me.
Here’s the flow: a user clicks on the “Add book” button at the bottom of an existing message showing a “library” of books. This opens up a modal view with inputs for specifying details of a book to search for. So far so good. However, when I submit my book search form, my app attempts to update the modal view with the results of the book search. I am doing this by way of the [UpdateView][1]
method in the slack-go library. Importantly, to avoid Slack timeouts, I am responding immediately to all requests with an ack response. Ok, now to my issue. When I call UpdateView
, the view updates, but it also shows an error message saying “we had some trouble connecting”. If I click on one of the search results to add it to the library, it works. So the only problem is that the error message is confusing to users. If anyone has any insight for how to avoid this error message, I’d appreciate it.
Edit: I’ve added a slightly simplified version of my handler code below.
package api
import (
"bytes"
"encoding/json"
"io"
"net/http"
"github.com/go-chi/httplog"
"github.com/slack-go/slack"
)
type ack struct {
ResponseAction string `json:"response_action"`
}
var (
ackIt = ack{ResponseAction: "clear", ResponseType: "in_channel"}
)
func NewSlackInteractiveHandler(slackSigningSecret string, slackBotToken string, libraryService LibraryService) http.HandlerFunc {
logger := httplog.NewLogger("slack_interactive_handler", httplog.Options{
JSON: true,
LogLevel: "debug",
})
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
sv, err := slack.NewSecretsVerifier(r.Header, slackSigningSecret)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if _, err := sv.Write(body); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
if err := sv.Ensure(); err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
// NOTE: we respond immediately even though we haven't done much work yet.
resp, _ := json.Marshal(ackIt)
w.Write(resp)
r.Body = io.NopCloser(bytes.NewBuffer(body))
slackClient := slack.New(slackBotToken)
var callback slack.InteractionCallback
err = json.Unmarshal([]byte(r.FormValue("payload")), &callback)
if err != nil {
logger.Error().Msg(err.Error())
return
}
if isBookSearchButton(callback) {
bookSearchAction, err := extractSearchBookAction(callback)
if err != nil {
logger.Error().Msg(err.Error())
return
}
logger.Info().
Str("author", bookSearchAction.AuthorName).
Str("title", bookSearchAction.Title).
Str("book_search_external_id", bookSearchAction.BookSearchExternalID()).
Msg("Request has an book_search callbackID")
// Update modal view with book search results
maxNumResults := 5
searchResults, err := libraryService.SearchBooks(ctx, bookSearchAction.AuthorName, bookSearchAction.Title, QueryString(""), uint(maxNumResults))
if err != nil {
logger.Error().Msg(err.Error())
return
}
modal := bookSearchAction.BookSearchResultsModal(searchResults)
viewResponse, err := slackClient.UpdateView(modal, bookSearchAction.BookSearchExternalID(), bookSearchAction.Hash, "")
if err != nil {
logger.Error().Msg(err.Error())
return
}
bytes, err := json.Marshal(viewResponse)
logger.Debug().Str("view_response", string(bytes)).Msg("Received a ViewResponse")
return
}
})
}
1