A Slack application framework inspired by Slack's Bolt Framework and the net/http
library.
app.RegisterCommand("/hi", func(req *slap.CommandRequest) error {
// Respond with 200 and an ephemeral message immediately
req.AckWithAction(slap.CommandResponseAction{
ResponseType: slap.RespondEphemeral,
Text: "Hi, how are you?"
})
// Send another message!
channel, ts, err := req.Client.PostEphemeral(req.Payload.ChannelID, req.Payload.UserID, slack.MsgOptionText("You said: " + req.Payload.Text))
if err != nil {
return err
}
// Open a modal!
res, err := req.Client.OpenView(req.Payload.TriggerID, slack.ModalViewRequest{
Type: "modal",
CallbackID: "form-modal",
Title: &slack.TextBlockObject{
Type: "plain_text",
Text: "Form"
}
...
})
return nil
})
app.RegisterViewSubmission("form-modal", func(req *slap.ViewSubmissionRequest) error {
// Get a value from the view submission
text := req.Payload.View.State.Values["text-input"]["text-input"]
if !isTextValid(text) {
// Respond with 200 and an error visible to the user
req.AckWithAction(slap.ViewResponseAction{
ResponseAction: slap.ViewResponseErrors,
Errors: map[string]string{
"text-input": "Please input a valid sentence"
}
})
return nil
}
// Close the modal stack using the "clear" response action
req.AckWithAction(slap.ViewResponseAction{
ResponseAction: slap.ViewResponseClear
})
// Do something with the text value - save it to a store with the user's ID
if err := store.save(req.Payload.User.ID, text); err != nil {
return err
}
return nil
})
app.RegisterBlockAction("start-button", func(req *slap.BlockActionRequest) error {
// Respond to Slack with 200
req.Ack()
// Open a modal!
res, err := req.Client.OpenView(req.Payload.TriggerID, slack.ModalViewRequest{
Type: "modal",
CallbackID: "form-modal",
Title: &slack.TextBlockObject{
Type: "plain_text",
Text: "Form"
}
...
})
return nil
})
app.RegisterEventHandler("message", func(req *slap.EventRequest) error {
// Parse the inner Events API event
var message slack.MessageEvent
if err := json.Unmarshal(req.Payload.Event, &message); err != nil {
return err
}
// Respond to Slack with 200
req.Ack()
// Ignore messages that your bot has sent or you will get stuck in recursive message hell
if message.BotId != "" {
return nil
}
// Do something with the message
slog.Info("Received message", "message", message.Text)
_, _, err := req.Client.PostMessage(message.Channel, slack.MsgOptionText("You wrote: " + message.Text, false))
if err != nil {
return err
}
return nil
})
- Create a Slack App at api.slack.com/apps and install it to your workspace (Settings -> Install App)
- Set the following environment variables from your Slack App Settings
export BOT_TOKEN={Settings -> Install App -> Bot User OAuth Token}
export SIGNING_SECRET={Settings -> Basic Information -> Signing Secret}
- Add the following to your
main.go
package main import ( "net/http" "os" "github.com/jacob-ian/slap" ) func main() { router := http.NewServeMux() app := slap.New(slap.Config{ Router: router, BotToken: func(teamID string) (string, error) { return os.Getenv("BOT_TOKEN"), nil }, SigningSecret: os.Getenv("SIGNING_SECRET"), }) app.RegisterCommand("/start", func(req *slap.CommandRequest) error { req.AckWithAction(slap.CommandResponseAction{ ResponseType: slap.RespondInChannel, Text: "Hello world!", }) return nil }) server := &http.Server{ Addr: ":4000", Handler: router, } panic(server.ListenAndServe()) }
- Run
go run main.go
- Use ngrok to get a public URL for your local environment
- Update your Slack App Settings:
- Slash Commands -> Create New Command
- Command:
/start
- Request URL:
https://{YOUR NGROK URL}/commands
- Command:
- Slash Commands -> Create New Command
- Use the
/start
command in your Slack Client
To use Slap, you will need to update your Slack App's settings at api.slack.com/apps.
For all Slash Commands:
- Request URL:
https://{YOUR PUBLIC URL}/commands
- Turn on Interactivity
- Request URL:
https://{YOUR PUBLIC URL}/interactions
- Enable Events
- Request URL:
https://{YOUR PUBLIC URL}/events
- Slap will automatically complete URL verification
Slap supports app distribution to multiple workspaces with the BotTokenGetter
in slap.Config
:
app := slap.New(slap.Config{
...,
BotToken: func(teamID string) (string, error) {
token, err := db.GetBotTokenByTeamID(teamID)
if err != nil {
return "", err
}
return token, nil
}
})
This allows for the fetching of a workspace's bot token from your store by the workspace's Team ID, which is then used by the Slack API client.
- Add shortcut support
- Add
view_closed
support - Add
block_suggestion
support - Add support for Gorilla Mux
- Add support for Echo
Thank you to the contributors at github.com/go-slack/slack for creating and maintaining the Slack API client and types which are needed to make Slap work.