Skip to content

Commit

Permalink
feat: Handle more interaction types
Browse files Browse the repository at this point in the history
  Convert BlockID to InteractionID field into the InteractionDefinition struct,
and add the necessary dispatch mechanisms to handle the following interactions:
- shortcut
- message_actions
- view_submission
- view_closed

Add Interaction type to interaction definition
  • Loading branch information
nicolas-carlier-ls committed Oct 31, 2023
1 parent 32e6347 commit 8ac097a
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 24 deletions.
7 changes: 4 additions & 3 deletions examples/interaction-middleware/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ func main() {

bot.AddInteractionMiddleware(LoggingInteractionMiddleware())
bot.AddInteraction(&slacker.InteractionDefinition{
BlockID: "mood",
Handler: slackerInteractive,
InteractionID: "mood",
Handler: slackerInteractive,
Type: slack.InteractionTypeBlockActions,
})

ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -68,7 +69,7 @@ func LoggingInteractionMiddleware() slacker.InteractionMiddlewareHandler {
ctx.Logger().Infof(
"%s initiated \"%s\" with action \"%v\" in channel %s\n",
ctx.Callback().User.ID,
ctx.Definition().BlockID,
ctx.Definition().InteractionID,
ctx.Callback().ActionCallback.BlockActions[0].ActionID,
ctx.Callback().Channel.ID,
)
Expand Down
54 changes: 54 additions & 0 deletions examples/interaction-shortcut/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

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

"github.com/shomali11/slacker/v2"
"github.com/slack-go/slack"
)

// 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),
)

bot.AddInteraction(&slacker.InteractionDefinition{
InteractionID: "mood-survey-message-shortcut-callback-id",
Handler: moodShortcutHandler,
Type: slack.InteractionTypeMessageAction,
})

bot.AddInteraction(&slacker.InteractionDefinition{
InteractionID: "mood-survey-global-shortcut-callback-id",
Handler: moodShortcutHandler,
Type: slack.InteractionTypeShortcut,
})

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

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

func moodShortcutHandler(ctx *slacker.InteractionContext) {
switch ctx.Callback().Type {
case slack.InteractionTypeMessageAction:
{
fmt.Print("Message shortcut.\n")
}
case slack.InteractionTypeShortcut:
{
fmt.Print("Global shortcut.\n")
}
}
}
118 changes: 118 additions & 0 deletions examples/interaction-view/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package main

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

"github.com/shomali11/slacker/v2"
"github.com/slack-go/slack"
)

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: slack.MBTInput,
BlockID: "mood",
Label: &slack.TextBlockObject{
Type: "plain_text",
Text: "Mood",
},
Element: &slack.SelectBlockElement{
Type: slack.OptTypeStatic,
ActionID: "mood",
Options: []*slack.OptionBlockObject{
{
Text: &slack.TextBlockObject{
Type: "plain_text",
Text: "Happy",
},
Value: "Happy",
},
{
Text: &slack.TextBlockObject{
Type: "plain_text",
Text: "Sad",
},
Value: "Sad",
},
},
},
},
},
},
}

// 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),
)

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

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

bot.AddInteraction(&slacker.InteractionDefinition{
InteractionID: "mood-survey-callback-id",
Handler: moodViewHandler,
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 moodViewHandler(ctx *slacker.InteractionContext) {
switch ctx.Callback().Type {
case slack.InteractionTypeViewSubmission:
{
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")
}
}
}
5 changes: 3 additions & 2 deletions examples/interaction/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ func main() {
})

bot.AddInteraction(&slacker.InteractionDefinition{
BlockID: "mood",
Handler: slackerInteractive,
InteractionID: "mood",
Handler: slackerInteractive,
Type: slack.InteractionTypeBlockActions,
})

ctx, cancel := context.WithCancel(context.Background())
Expand Down
9 changes: 6 additions & 3 deletions interaction.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package slacker

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

// InteractionDefinition structure contains definition of the bot interaction
type InteractionDefinition struct {
BlockID string
Middlewares []InteractionMiddlewareHandler
Handler InteractionHandler
InteractionID string
Middlewares []InteractionMiddlewareHandler
Handler InteractionHandler
Type slack.InteractionType
}

// newInteraction creates a new bot interaction object
Expand Down
65 changes: 49 additions & 16 deletions slacker.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func NewClient(botToken, appToken string, clientOptions ...ClientOption) *Slacke
botInteractionMode: options.BotMode,
sanitizeEventTextHandler: defaultEventTextSanitizer,
logger: options.Logger,
interactions: make(map[slack.InteractionType][]*Interaction),
}
return slacker
}
Expand All @@ -54,7 +55,7 @@ type Slacker struct {
commandMiddlewares []CommandMiddlewareHandler
commandGroups []*CommandGroup
interactionMiddlewares []InteractionMiddlewareHandler
interactions []*Interaction
interactions map[slack.InteractionType][]*Interaction
jobMiddlewares []JobMiddlewareHandler
jobs []*Job
onHello func(socketmode.Event)
Expand All @@ -78,7 +79,7 @@ func (s *Slacker) GetCommandGroups() []*CommandGroup {
}

// GetInteractions returns Groups
func (s *Slacker) GetInteractions() []*Interaction {
func (s *Slacker) GetInteractions() map[slack.InteractionType][]*Interaction {
return s.interactions
}

Expand Down Expand Up @@ -174,11 +175,15 @@ func (s *Slacker) AddCommandGroup(prefix string) *CommandGroup {

// AddInteraction define a new interaction and append it to the list of interactions
func (s *Slacker) AddInteraction(definition *InteractionDefinition) {
if len(definition.BlockID) == 0 {
s.logger.Error("missing `BlockID`")
if len(definition.InteractionID) == 0 {
s.logger.Error("missing `ID`")
return
}
s.interactions = append(s.interactions, newInteraction(definition))
if len(definition.Type) == 0 {
s.logger.Error("missing `Type`")
return
}
s.interactions[definition.Type] = append(s.interactions[definition.Type], newInteraction(definition))
}

// AddInteractionMiddleware appends a new interaction middleware to the list of root level interaction middlewares
Expand Down Expand Up @@ -437,21 +442,49 @@ func (s *Slacker) handleInteractionEvent(ctx context.Context, callback *slack.In
middlewares := make([]InteractionMiddlewareHandler, 0)
middlewares = append(middlewares, s.interactionMiddlewares...)

for _, interaction := range s.interactions {
for _, action := range callback.ActionCallback.BlockActions {
definition := interaction.Definition()
if action.BlockID != definition.BlockID {
continue
var interaction *Interaction
var definition *InteractionDefinition

switch callback.Type {
case slack.InteractionTypeBlockActions:
for _, i := range s.interactions[callback.Type] {
for _, a := range callback.ActionCallback.BlockActions {
definition = i.Definition()
if a.BlockID == definition.InteractionID {
interaction = i
break
}
}
if interaction != nil {
break
}

interactionCtx := newInteractionContext(ctx, s.logger, s.slackClient, callback, definition)

middlewares = append(middlewares, definition.Middlewares...)
executeInteraction(interactionCtx, definition.Handler, middlewares...)
return
}
case slack.InteractionTypeViewClosed, slack.InteractionTypeViewSubmission:
for _, i := range s.interactions[callback.Type] {
definition = i.Definition()
if definition.InteractionID == callback.View.CallbackID {
interaction = i
break
}
}
case slack.InteractionTypeShortcut, slack.InteractionTypeMessageAction:
for _, i := range s.interactions[callback.Type] {
definition = i.Definition()
if definition.InteractionID == callback.CallbackID {
interaction = i
break
}
}
}

if interaction != nil {
interactionCtx := newInteractionContext(ctx, s.logger, s.slackClient, callback, definition)
middlewares = append(middlewares, definition.Middlewares...)
executeInteraction(interactionCtx, definition.Handler, middlewares...)
return
}

s.logger.Debugf("unsupported interaction type received %s\n", callback.Type)
if s.unsupportedInteractionHandler != nil {
interactionCtx := newInteractionContext(ctx, s.logger, s.slackClient, callback, nil)
executeInteraction(interactionCtx, s.unsupportedInteractionHandler, middlewares...)
Expand Down

0 comments on commit 8ac097a

Please sign in to comment.