Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions examples/interaction-middleware/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"log"
"os"

"github.com/slack-io/slacker"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-io/slacker"
)

// Show cases interaction middlewares
Expand Down Expand Up @@ -48,7 +49,7 @@ func slackerCmd(blockID string) slacker.CommandHandler {
}
}

func slackerInteractive(ctx *slacker.InteractionContext) {
func slackerInteractive(ctx *slacker.InteractionContext, req *socketmode.Request) {
text := ""
action := ctx.Callback().ActionCallback.BlockActions[0]
switch action.ActionID {
Expand All @@ -65,14 +66,14 @@ func slackerInteractive(ctx *slacker.InteractionContext) {

func LoggingInteractionMiddleware() slacker.InteractionMiddlewareHandler {
return func(next slacker.InteractionHandler) slacker.InteractionHandler {
return func(ctx *slacker.InteractionContext) {
return func(ctx *slacker.InteractionContext, req *socketmode.Request) {
ctx.Logger().Info("logging interaction middleware",
"user_id", ctx.Callback().User.ID,
"interaction_id", ctx.Definition().InteractionID,
"action_id", ctx.Callback().ActionCallback.BlockActions[0].ActionID,
"channel_id", ctx.Callback().Channel.ID,
)
next(ctx)
next(ctx, req)
}
}
}
5 changes: 3 additions & 2 deletions examples/interaction-shortcut/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
"log"
"os"

"github.com/slack-io/slacker"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-io/slacker"
)

// Implements a basic interactive command with modal view.
Expand Down Expand Up @@ -40,7 +41,7 @@ func main() {
}
}

func moodShortcutHandler(ctx *slacker.InteractionContext) {
func moodShortcutHandler(ctx *slacker.InteractionContext, req *socketmode.Request) {
switch ctx.Callback().Type {
case slack.InteractionTypeMessageAction:
{
Expand Down
5 changes: 3 additions & 2 deletions examples/interaction-sink/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import (
"log"
"os"

"github.com/slack-io/slacker"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-io/slacker"
)

// Show cases having one handler for all interactions

func main() {
bot := slacker.NewClient(os.Getenv("SLACK_BOT_TOKEN"), os.Getenv("SLACK_APP_TOKEN"))

bot.UnsupportedInteractionHandler(func(ctx *slacker.InteractionContext) {
bot.UnsupportedInteractionHandler(func(ctx *slacker.InteractionContext, req *socketmode.Request) {
callback := ctx.Callback()
if callback.Type != slack.InteractionTypeBlockActions {
return
Expand Down
137 changes: 137 additions & 0 deletions examples/interaction-view-with-error/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package main

import (
"context"
"fmt"
"log"
"os"
"regexp"

"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-io/slacker"
)

var moodSurveyView = slack.ModalViewRequest{
Type: "modal",
CallbackID: "mood-survey-callback-id",
Title: &slack.TextBlockObject{
Type: "plain_text",
Text: "Which mood are you in?",
},
Submit: &slack.TextBlockObject{
Type: "plain_text",
Text: "Submit",
},
NotifyOnClose: true,
Blocks: slack.Blocks{
BlockSet: []slack.Block{
&slack.InputBlock{
Type: "input",
DispatchAction: true,
BlockID: "mood",
Label: &slack.TextBlockObject{
Type: slack.PlainTextType,
Text: "Input mood",
},
Element: &slack.PlainTextInputBlockElement{
MaxLength: 23,
Type: slack.METPlainTextInput,
ActionID: "mood",
Placeholder: &slack.TextBlockObject{
Type: slack.PlainTextType,
Text: "Enter your mood",
},
},
},
},
},
}

// Implements a basic interactive command with modal view.
func main() {
bot := slacker.NewClient(
os.Getenv("SLACK_BOT_TOKEN"),
os.Getenv("SLACK_APP_TOKEN"),
slacker.WithDebug(false),
slacker.WithSelfAck(true),
)

bot.AddCommand(&slacker.CommandDefinition{
Command: "mood",
Handler: moodCmdHandler,
})

bot.AddInteraction(&slacker.InteractionDefinition{
InteractionID: "mood-survey-callback-id",
Handler: moodViewHanlerWithBotClient(bot),
Type: slack.InteractionTypeViewSubmission,
})

bot.AddInteraction(&slacker.InteractionDefinition{
InteractionID: "mood-survey-callback-id",
Handler: moodViewHanlerWithBotClient(bot),
Type: slack.InteractionTypeViewClosed,
})

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := bot.Listen(ctx)
if err != nil {
log.Fatal(err)
}
}

func moodCmdHandler(ctx *slacker.CommandContext) {
_, err := ctx.SlackClient().OpenView(
ctx.Event().Data.(*slack.SlashCommand).TriggerID,
moodSurveyView,
)
if err != nil {
log.Printf("ERROR openEscalationModal: %v", err)
}
}

func moodViewHanlerWithBotClient(bot *slacker.Slacker) slacker.InteractionHandler {
return func(ctx *slacker.InteractionContext, req *socketmode.Request) {
switch ctx.Callback().Type {
case slack.InteractionTypeViewSubmission:
{

valid, errorMessage := validateInput(ctx.Callback().View.State.Values["mood"]["mood"].Value)
if !valid {
errorResponse := map[string]interface{}{
"response_action": "errors",
"errors": map[string]string{
"mood": errorMessage, // Block ID for the input field
},
}

bot.SocketModeClient().Ack(*req, errorResponse)

} else {
bot.SocketModeClient().Ack(*req)

}
viewState := ctx.Callback().View.State.Values
fmt.Printf(
"Mood view submitted.\nMood: %s\n",
viewState["mood"]["mood"].SelectedOption.Value,
)
}
case slack.InteractionTypeViewClosed:
{
fmt.Print("Mood view closed.\n")
}
}
}
}

func validateInput(input string) (bool, string) {
re := regexp.MustCompile(`^[A-Za-z0-9-]{1,23}$`)
if !re.MatchString(input) {
return false, "Mood can not contain special characters"
}
return true, ""
}
5 changes: 3 additions & 2 deletions examples/interaction-view/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
"log"
"os"

"github.com/slack-io/slacker"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-io/slacker"
)

var moodSurveyView = slack.ModalViewRequest{
Expand Down Expand Up @@ -100,7 +101,7 @@ func moodCmdHandler(ctx *slacker.CommandContext) {
}
}

func moodViewHandler(ctx *slacker.InteractionContext) {
func moodViewHandler(ctx *slacker.InteractionContext, req *socketmode.Request) {
switch ctx.Callback().Type {
case slack.InteractionTypeViewSubmission:
{
Expand Down
5 changes: 3 additions & 2 deletions examples/interaction/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"log"
"os"

"github.com/slack-io/slacker"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-io/slacker"
)

// Implements a basic interactive command.
Expand Down Expand Up @@ -47,7 +48,7 @@ func slackerCmd(blockID string) slacker.CommandHandler {
}
}

func slackerInteractive(ctx *slacker.InteractionContext) {
func slackerInteractive(ctx *slacker.InteractionContext, req *socketmode.Request) {
text := ""
action := ctx.Callback().ActionCallback.BlockActions[0]
switch action.ActionID {
Expand Down
6 changes: 4 additions & 2 deletions executors.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package slacker

import "github.com/slack-go/slack/socketmode"

func executeCommand(ctx *CommandContext, handler CommandHandler, middlewares ...CommandMiddlewareHandler) {
if handler == nil {
return
Expand All @@ -12,7 +14,7 @@ func executeCommand(ctx *CommandContext, handler CommandHandler, middlewares ...
handler(ctx)
}

func executeInteraction(ctx *InteractionContext, handler InteractionHandler, middlewares ...InteractionMiddlewareHandler) {
func executeInteraction(ctx *InteractionContext, handler InteractionHandler, request *socketmode.Request, middlewares ...InteractionMiddlewareHandler) {
if handler == nil {
return
}
Expand All @@ -21,7 +23,7 @@ func executeInteraction(ctx *InteractionContext, handler InteractionHandler, mid
handler = middlewares[i](handler)
}

handler(ctx)
handler(ctx, request)
}

func executeJob(ctx *JobContext, handler JobHandler, middlewares ...JobMiddlewareHandler) func() {
Expand Down
4 changes: 3 additions & 1 deletion handler.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package slacker

import "github.com/slack-go/slack/socketmode"

// CommandMiddlewareHandler represents the command middleware handler function
type CommandMiddlewareHandler func(CommandHandler) CommandHandler

Expand All @@ -10,7 +12,7 @@ type CommandHandler func(*CommandContext)
type InteractionMiddlewareHandler func(InteractionHandler) InteractionHandler

// InteractionHandler represents the interaction handler function
type InteractionHandler func(*InteractionContext)
type InteractionHandler func(*InteractionContext, *socketmode.Request)

// JobMiddlewareHandler represents the job middleware handler function
type JobMiddlewareHandler func(JobHandler) JobHandler
Expand Down
8 changes: 8 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,16 @@ func WithCronLocation(location *time.Location) ClientOption {
}
}

func WithSelfAck(selfAck bool) ClientOption {
return func(defaults *clientOptions) {
defaults.SelfAck = selfAck
}
}

type clientOptions struct {
APIURL string
Debug bool
SelfAck bool
BotMode BotMode
Logger Logger
CronLocation *time.Location
Expand All @@ -58,6 +65,7 @@ func newClientOptions(options ...ClientOption) *clientOptions {
Debug: false,
BotMode: BotModeIgnoreAll,
CronLocation: time.Local,
SelfAck: false,
}

for _, option := range options {
Expand Down
25 changes: 19 additions & 6 deletions slacker.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func NewClient(botToken, appToken string, clientOptions ...ClientOption) *Slacke
sanitizeEventTextHandler: defaultEventTextSanitizer,
logger: options.Logger,
interactions: make(map[slack.InteractionType][]*Interaction),
selfAck: options.SelfAck,
}
return slacker
}
Expand Down Expand Up @@ -73,6 +74,7 @@ type Slacker struct {
botInteractionMode BotMode
sanitizeEventTextHandler func(string) string
logger Logger
selfAck bool
}

// GetCommandGroups returns Command Groups
Expand Down Expand Up @@ -313,10 +315,12 @@ func (s *Slacker) Listen(ctx context.Context) error {
continue
}

// Acknowledge receiving the request
s.socketModeClient.Ack(*socketEvent.Request)
// Acknowledge receiving the request if self Acknowledge is disabled
if !s.selfAck {
s.socketModeClient.Ack(*socketEvent.Request)
}

go s.handleInteractionEvent(ctx, &callback)
go s.handleInteractionEvent(ctx, &callback, *socketEvent.Request)

default:
if s.unsupportedEventHandler != nil {
Expand Down Expand Up @@ -440,7 +444,7 @@ func (s *Slacker) startCronJobs(ctx context.Context) {
s.cronClient.Start()
}

func (s *Slacker) handleInteractionEvent(ctx context.Context, callback *slack.InteractionCallback) {
func (s *Slacker) handleInteractionEvent(ctx context.Context, callback *slack.InteractionCallback, request socketmode.Request) {
middlewares := make([]InteractionMiddlewareHandler, 0)
middlewares = append(middlewares, s.interactionMiddlewares...)

Expand All @@ -461,6 +465,14 @@ func (s *Slacker) handleInteractionEvent(ctx context.Context, callback *slack.In
break
}
}
case slack.InteractionTypeBlockSuggestion:
for _, i := range s.interactions[callback.Type] {
definition = i.Definition()
if definition.InteractionID == callback.ActionID {
interaction = i
break
}
}
case slack.InteractionTypeViewClosed, slack.InteractionTypeViewSubmission:
for _, i := range s.interactions[callback.Type] {
definition = i.Definition()
Expand All @@ -482,14 +494,15 @@ func (s *Slacker) handleInteractionEvent(ctx context.Context, callback *slack.In
if interaction != nil {
interactionCtx := newInteractionContext(ctx, s.logger, s.slackClient, callback, definition)
middlewares = append(middlewares, definition.Middlewares...)
executeInteraction(interactionCtx, definition.Handler, middlewares...)
executeInteraction(interactionCtx, definition.Handler, &request, middlewares...)
return
}

s.logger.Debug("unsupported interaction type", "type", callback.Type)

if s.unsupportedInteractionHandler != nil {
interactionCtx := newInteractionContext(ctx, s.logger, s.slackClient, callback, nil)
executeInteraction(interactionCtx, s.unsupportedInteractionHandler, middlewares...)
executeInteraction(interactionCtx, s.unsupportedInteractionHandler, &request, middlewares...)
}
}

Expand Down
Loading