From 54bd12bc33f979b483eebeededbebeb567fc712c Mon Sep 17 00:00:00 2001 From: xnok Date: Sat, 23 Jan 2021 00:03:22 -0500 Subject: [PATCH 01/34] feat(slacktest): add handler for reaction.Add --- slacktest/data.go | 6 ++++++ slacktest/handlers.go | 5 +++++ slacktest/server.go | 1 + 3 files changed, 12 insertions(+) diff --git a/slacktest/data.go b/slacktest/data.go index 37d413a0d..e33b901dc 100644 --- a/slacktest/data.go +++ b/slacktest/data.go @@ -35,6 +35,12 @@ var okWebResponse = slack.SlackResponse{ Ok: true, } +var defaultOkJSON = fmt.Sprintf(` + { + "ok": true + } + `) + var defaultChannelsListJSON = fmt.Sprintf(` { "ok": true, diff --git a/slacktest/handlers.go b/slacktest/handlers.go index 85eec5804..6e7f1dd1d 100644 --- a/slacktest/handlers.go +++ b/slacktest/handlers.go @@ -117,6 +117,11 @@ func listGroupsHandler(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte(defaultGroupsListJSON)) } +// handle reaction.Add +func reactionAddHandler(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(defaultOkJSON)) +} + // handle chat.postMessage func (sts *Server) postMessageHandler(w http.ResponseWriter, r *http.Request) { serverAddr := r.Context().Value(ServerBotHubNameContextKey).(string) diff --git a/slacktest/server.go b/slacktest/server.go index aae24995d..6e7395f5a 100644 --- a/slacktest/server.go +++ b/slacktest/server.go @@ -62,6 +62,7 @@ func NewTestServer(custom ...binder) *Server { s.Handle("/users.lookupByEmail", usersInfoHandler) s.Handle("/bots.info", botsInfoHandler) s.Handle("/auth.test", authTestHandler) + s.Handle("/reactions.add", reactionAddHandler) httpserver := httptest.NewUnstartedServer(s.mux) addr := httpserver.Listener.Addr().String() From 3178aab754525464ca70dc253c3e13f108139096 Mon Sep 17 00:00:00 2001 From: xnok Date: Sun, 21 Feb 2021 00:22:03 -0500 Subject: [PATCH 02/34] feat: basic stucture --- .../sockermode_handler/socketmode_handler.go | 138 ++++++++++++++++++ socketmode/socketmode_eventhandler.go | 115 +++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 examples/sockermode_handler/socketmode_handler.go create mode 100644 socketmode/socketmode_eventhandler.go diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go new file mode 100644 index 000000000..d1857a77d --- /dev/null +++ b/examples/sockermode_handler/socketmode_handler.go @@ -0,0 +1,138 @@ +package main + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/slack-go/slack/socketmode" + + "github.com/slack-go/slack" +) + +func main() { + appToken := os.Getenv("SLACK_APP_TOKEN") + if appToken == "" { + + } + + if !strings.HasPrefix(appToken, "xapp-") { + fmt.Fprintf(os.Stderr, "SLACK_APP_TOKEN must have the prefix \"xapp-\".") + } + + botToken := os.Getenv("SLACK_BOT_TOKEN") + if botToken == "" { + fmt.Fprintf(os.Stderr, "SLACK_BOT_TOKEN must be set.\n") + os.Exit(1) + } + + if !strings.HasPrefix(botToken, "xoxb-") { + fmt.Fprintf(os.Stderr, "SLACK_BOT_TOKEN must have the prefix \"xoxb-\".") + } + + api := slack.New( + botToken, + slack.OptionDebug(true), + slack.OptionLog(log.New(os.Stdout, "api: ", log.Lshortfile|log.LstdFlags)), + slack.OptionAppLevelToken(appToken), + ) + + client := socketmode.New( + api, + socketmode.OptionDebug(true), + socketmode.OptionLog(log.New(os.Stdout, "socketmode: ", log.Lshortfile|log.LstdFlags)), + ) + + socketmodeHandler := socketmode.NewsSocketmodeHandler(client) + + socketmodeHandler.Handle(socketmode.EventTypeConnecting, middlewareConnecting) + socketmodeHandler.Handle(socketmode.EventTypeConnectionError, middlewareConnectionError) + socketmodeHandler.Handle(socketmode.EventTypeConnected, middlewareConnected) + + // socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) + // socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) + + // socketmodeHandler.HandleDefault(middlewareDefault) + + socketmodeHandler.RunEventLoop() +} + +func middlewareConnecting(evt socketmode.Event) { + fmt.Println("Connecting to Slack with Socket Mode...") +} + +func middlewareConnectionError(evt socketmode.Event) { + fmt.Println("Connection failed. Retrying later...") +} + +func middlewareConnected(evt socketmode.Event) { + fmt.Println("Connected to Slack with Socket Mode.") +} + +func middlewareEventsAPI(evt socketmode.Event) { + // eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) + // if !ok { + // fmt.Printf("Ignored %+v\n", evt) + + // continue + // } + + // fmt.Printf("Event received: %+v\n", eventsAPIEvent) + + // client.Ack(*evt.Request) + + // switch eventsAPIEvent.Type { + // case slackevents.CallbackEvent: + // innerEvent := eventsAPIEvent.InnerEvent + // switch ev := innerEvent.Data.(type) { + // case *slackevents.AppMentionEvent: + // _, _, err := api.PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) + // if err != nil { + // fmt.Printf("failed posting message: %v", err) + // } + // case *slackevents.MemberJoinedChannelEvent: + // fmt.Printf("user %q joined to channel %q", ev.User, ev.Channel) + // } + // default: + // client.Debugf("unsupported Events API event received") + // } +} + +func middlewareSlashCommand(evt socketmode.Event) { + // cmd, ok := evt.Data.(slack.SlashCommand) + // if !ok { + // fmt.Printf("Ignored %+v\n", evt) + + // continue + // } + + // client.Debugf("Slash command received: %+v", cmd) + + // payload := map[string]interface{}{ + // "blocks": []slack.Block{ + // slack.NewSectionBlock( + // &slack.TextBlockObject{ + // Type: slack.MarkdownType, + // Text: "foo", + // }, + // nil, + // slack.NewAccessory( + // slack.NewButtonBlockElement( + // "", + // "somevalue", + // &slack.TextBlockObject{ + // Type: slack.PlainTextType, + // Text: "bar", + // }, + // ), + // ), + // ), + // }} + + // client.Ack(*evt.Request, payload) +} + +func middlewareDefault(evt socketmode.Event) { + // fmt.Fprintf(os.Stderr, "Unexpected event type received: %s\n", evt.Type) +} diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go new file mode 100644 index 000000000..aca9e52ab --- /dev/null +++ b/socketmode/socketmode_eventhandler.go @@ -0,0 +1,115 @@ +package socketmode + +import ( + "log" + + "github.com/slack-go/slack" + "github.com/slack-go/slack/slackevents" +) + +type SocketmodeHandler struct { + Socket *Client + + EventMap map[EventType][]SocketmodeHandlerFunc +} + +type SocketmodeHandlerFunc func(Event) + +func NewsSocketmodeHandler(socket *Client) *SocketmodeHandler { + eventMap := make(map[EventType][]SocketmodeHandlerFunc) + + return &SocketmodeHandler{ + Socket: socket, + EventMap: eventMap, + } +} + +func (r *SocketmodeHandler) Handle(et EventType, f func(Event)) { + r.EventMap[et] = append(r.EventMap[et], f) +} + +// RunSlackEventLoop receives the event via the socket +// It receives events from Slack and each is handled as needed +func (r *SocketmodeHandler) RunEventLoop() { + + go r.runEventLoop() + + r.Socket.Run() +} + +func (r *SocketmodeHandler) runEventLoop() { + for evt := range r.Socket.Events { + if handlers, ok := r.EventMap[evt.Type]; ok { + // If we registered an event + for _, f := range handlers { + go f(evt) + } + } else { + // We need to explicitely subscribe to event in the Application Dashboard + // So every event sould be handle otherwise this is an error + log.Printf("Unexpected event type received: %v\n", evt.Type) + } + + } +} + +func (r *SocketmodeHandler) EventTypeEventsAPIHandler(evt *Event) { + eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) + if !ok { + log.Println("Unable to handle recieved EventsAPIEvent") + return + } + + // We need to confirm that we recived that event + r.Socket.Ack(*evt.Request) + + switch eventsAPIEvent.Type { + case slackevents.CallbackEvent: + innerEvent := eventsAPIEvent.InnerEvent + switch ev := innerEvent.Data.(type) { + case *slackevents.AppMentionEvent: + + case *slackevents.MessageEvent: + + case *slackevents.MemberJoinedChannelEvent: + + default: + log.Printf("unsupported CallbackEvent event received: %T", ev) + } + default: + log.Printf("unsupported eventsAPIEvent event received: %s", eventsAPIEvent.Type) + } +} + +func (r *SocketmodeHandler) EventTypeInteractiveHandler(evt *Event) { + callback, ok := evt.Data.(slack.InteractionCallback) + if !ok { + return + } + + var payload interface{} + + switch callback.Type { + case slack.InteractionTypeBlockActions: + // See https://api.slack.com/apis/connections/socket-implement#button + case slack.InteractionTypeShortcut: + // See https://api.slack.com/interactivity/shortcuts + case slack.InteractionTypeViewSubmission: + // See https://api.slack.com/apis/connections/socket-implement#modal + case slack.InteractionTypeDialogSubmission: + + default: + log.Printf("unsupported callbackEvent event received: %s", callback.Type) + } + + r.Socket.Ack(*evt.Request, payload) +} + +func (r *SocketmodeHandler) EventTypeSlashCommandHandler(evt *Event) { + cmd, ok := evt.Data.(slack.SlashCommand) + if !ok { + return + } + + log.Printf("%s", cmd) +} From 65335cfa6b73e83bb98f7b351ab1a743f7e5b31d Mon Sep 17 00:00:00 2001 From: xnok Date: Sun, 21 Feb 2021 17:24:38 -0500 Subject: [PATCH 03/34] feat: socketmode Event Handling --- .../sockermode_handler/socketmode_handler.go | 166 +++++++++++------- socketmode/client.go | 5 + socketmode/socketmode_eventhandler.go | 80 +-------- 3 files changed, 114 insertions(+), 137 deletions(-) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index d1857a77d..46b295608 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/slack-go/slack/slackevents" "github.com/slack-go/slack/socketmode" "github.com/slack-go/slack" @@ -50,89 +51,124 @@ func main() { socketmodeHandler.Handle(socketmode.EventTypeConnectionError, middlewareConnectionError) socketmodeHandler.Handle(socketmode.EventTypeConnected, middlewareConnected) - // socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) - // socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) + // Handle the EventsAPI + socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) + + // Handle a specific event from EventsAPI + // socketmodeHandler.HandleEventsAPI(socketmode.EventTypeAppMention, middlewareAppMentionEvent) + + socketmodeHandler.Handle(socketmode.EventTypeInteractive, middlewareInteractive) + + // Handle the EventsAPI + socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) // socketmodeHandler.HandleDefault(middlewareDefault) socketmodeHandler.RunEventLoop() } -func middlewareConnecting(evt socketmode.Event) { +func middlewareConnecting(evt *socketmode.Event, client *socketmode.Client) { fmt.Println("Connecting to Slack with Socket Mode...") } -func middlewareConnectionError(evt socketmode.Event) { +func middlewareConnectionError(evt *socketmode.Event, client *socketmode.Client) { fmt.Println("Connection failed. Retrying later...") } -func middlewareConnected(evt socketmode.Event) { +func middlewareConnected(evt *socketmode.Event, client *socketmode.Client) { fmt.Println("Connected to Slack with Socket Mode.") } -func middlewareEventsAPI(evt socketmode.Event) { - // eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) - // if !ok { - // fmt.Printf("Ignored %+v\n", evt) - - // continue - // } - - // fmt.Printf("Event received: %+v\n", eventsAPIEvent) - - // client.Ack(*evt.Request) - - // switch eventsAPIEvent.Type { - // case slackevents.CallbackEvent: - // innerEvent := eventsAPIEvent.InnerEvent - // switch ev := innerEvent.Data.(type) { - // case *slackevents.AppMentionEvent: - // _, _, err := api.PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) - // if err != nil { - // fmt.Printf("failed posting message: %v", err) - // } - // case *slackevents.MemberJoinedChannelEvent: - // fmt.Printf("user %q joined to channel %q", ev.User, ev.Channel) - // } - // default: - // client.Debugf("unsupported Events API event received") - // } +func middlewareEventsAPI(evt *socketmode.Event, client *socketmode.Client) { + eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + return + } + + fmt.Printf("Event received: %+v\n", eventsAPIEvent) + + client.Ack(*evt.Request) + + switch eventsAPIEvent.Type { + case slackevents.CallbackEvent: + innerEvent := eventsAPIEvent.InnerEvent + switch ev := innerEvent.Data.(type) { + case *slackevents.AppMentionEvent: + fmt.Printf("We have been mentionned in %v", ev.Channel) + _, _, err := client.GetApiClient().PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) + if err != nil { + fmt.Printf("failed posting message: %v", err) + } + case *slackevents.MemberJoinedChannelEvent: + fmt.Printf("user %q joined to channel %q", ev.User, ev.Channel) + } + default: + client.Debugf("unsupported Events API event received") + } } -func middlewareSlashCommand(evt socketmode.Event) { - // cmd, ok := evt.Data.(slack.SlashCommand) - // if !ok { - // fmt.Printf("Ignored %+v\n", evt) - - // continue - // } - - // client.Debugf("Slash command received: %+v", cmd) - - // payload := map[string]interface{}{ - // "blocks": []slack.Block{ - // slack.NewSectionBlock( - // &slack.TextBlockObject{ - // Type: slack.MarkdownType, - // Text: "foo", - // }, - // nil, - // slack.NewAccessory( - // slack.NewButtonBlockElement( - // "", - // "somevalue", - // &slack.TextBlockObject{ - // Type: slack.PlainTextType, - // Text: "bar", - // }, - // ), - // ), - // ), - // }} - - // client.Ack(*evt.Request, payload) +func middlewareInteractive(evt *socketmode.Event, client *socketmode.Client) { + callback, ok := evt.Data.(slack.InteractionCallback) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + + return + } + + fmt.Printf("Interaction received: %+v\n", callback) + + var payload interface{} + + switch callback.Type { + case slack.InteractionTypeBlockActions: + // See https://api.slack.com/apis/connections/socket-implement#button + + client.Debugf("button clicked!") + case slack.InteractionTypeShortcut: + case slack.InteractionTypeViewSubmission: + // See https://api.slack.com/apis/connections/socket-implement#modal + case slack.InteractionTypeDialogSubmission: + default: + + } + + client.Ack(*evt.Request, payload) +} + +func middlewareSlashCommand(evt *socketmode.Event, client *socketmode.Client) { + cmd, ok := evt.Data.(slack.SlashCommand) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + return + } + + client.Debugf("Slash command received: %+v", cmd) + + payload := map[string]interface{}{ + "blocks": []slack.Block{ + slack.NewSectionBlock( + &slack.TextBlockObject{ + Type: slack.MarkdownType, + Text: "foo", + }, + nil, + slack.NewAccessory( + slack.NewButtonBlockElement( + "", + "somevalue", + &slack.TextBlockObject{ + Type: slack.PlainTextType, + Text: "bar", + }, + ), + ), + ), + }} + + client.Ack(*evt.Request, payload) } -func middlewareDefault(evt socketmode.Event) { +func middlewareDefault(evt *socketmode.Event, client *socketmode.Client) { // fmt.Fprintf(os.Stderr, "Unexpected event type received: %s\n", evt.Type) } diff --git a/socketmode/client.go b/socketmode/client.go index cce8a703b..fc15d9df2 100644 --- a/socketmode/client.go +++ b/socketmode/client.go @@ -61,3 +61,8 @@ type Client struct { debug bool log ilogger } + +// return a pointer to the slack API client +func (c Client) GetApiClient() *slack.Client { + return &c.apiClient +} diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index aca9e52ab..12821f936 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -2,29 +2,26 @@ package socketmode import ( "log" - - "github.com/slack-go/slack" - "github.com/slack-go/slack/slackevents" ) type SocketmodeHandler struct { - Socket *Client + Client *Client EventMap map[EventType][]SocketmodeHandlerFunc } -type SocketmodeHandlerFunc func(Event) +type SocketmodeHandlerFunc func(*Event, *Client) -func NewsSocketmodeHandler(socket *Client) *SocketmodeHandler { +func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) return &SocketmodeHandler{ - Socket: socket, + Client: client, EventMap: eventMap, } } -func (r *SocketmodeHandler) Handle(et EventType, f func(Event)) { +func (r *SocketmodeHandler) Handle(et EventType, f SocketmodeHandlerFunc) { r.EventMap[et] = append(r.EventMap[et], f) } @@ -34,15 +31,15 @@ func (r *SocketmodeHandler) RunEventLoop() { go r.runEventLoop() - r.Socket.Run() + r.Client.Run() } func (r *SocketmodeHandler) runEventLoop() { - for evt := range r.Socket.Events { + for evt := range r.Client.Events { if handlers, ok := r.EventMap[evt.Type]; ok { // If we registered an event for _, f := range handlers { - go f(evt) + go f(&evt, r.Client) } } else { // We need to explicitely subscribe to event in the Application Dashboard @@ -52,64 +49,3 @@ func (r *SocketmodeHandler) runEventLoop() { } } - -func (r *SocketmodeHandler) EventTypeEventsAPIHandler(evt *Event) { - eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) - if !ok { - log.Println("Unable to handle recieved EventsAPIEvent") - return - } - - // We need to confirm that we recived that event - r.Socket.Ack(*evt.Request) - - switch eventsAPIEvent.Type { - case slackevents.CallbackEvent: - innerEvent := eventsAPIEvent.InnerEvent - switch ev := innerEvent.Data.(type) { - case *slackevents.AppMentionEvent: - - case *slackevents.MessageEvent: - - case *slackevents.MemberJoinedChannelEvent: - - default: - log.Printf("unsupported CallbackEvent event received: %T", ev) - } - default: - log.Printf("unsupported eventsAPIEvent event received: %s", eventsAPIEvent.Type) - } -} - -func (r *SocketmodeHandler) EventTypeInteractiveHandler(evt *Event) { - callback, ok := evt.Data.(slack.InteractionCallback) - if !ok { - return - } - - var payload interface{} - - switch callback.Type { - case slack.InteractionTypeBlockActions: - // See https://api.slack.com/apis/connections/socket-implement#button - case slack.InteractionTypeShortcut: - // See https://api.slack.com/interactivity/shortcuts - case slack.InteractionTypeViewSubmission: - // See https://api.slack.com/apis/connections/socket-implement#modal - case slack.InteractionTypeDialogSubmission: - - default: - log.Printf("unsupported callbackEvent event received: %s", callback.Type) - } - - r.Socket.Ack(*evt.Request, payload) -} - -func (r *SocketmodeHandler) EventTypeSlashCommandHandler(evt *Event) { - cmd, ok := evt.Data.(slack.SlashCommand) - if !ok { - return - } - - log.Printf("%s", cmd) -} From 1dbd284b566b731a64f4f897f5a7c65023c0bb9f Mon Sep 17 00:00:00 2001 From: xnok Date: Sun, 21 Feb 2021 20:25:46 -0500 Subject: [PATCH 04/34] feat: handle all type of EventTypeInteractive --- .../sockermode_handler/socketmode_handler.go | 12 +++-- socketmode/socketmode_eventhandler.go | 44 +++++++++++++++++-- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index 46b295608..95c3dba87 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -57,9 +57,13 @@ func main() { // Handle a specific event from EventsAPI // socketmodeHandler.HandleEventsAPI(socketmode.EventTypeAppMention, middlewareAppMentionEvent) + // Handle the Interactive Events socketmodeHandler.Handle(socketmode.EventTypeInteractive, middlewareInteractive) - // Handle the EventsAPI + // Handle a specific Interaction + socketmodeHandler.HandleInteraction(slack.InteractionTypeBlockActions, middlewareInteractionTypeBlockActions) + + // Handle the SlashCommand socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) // socketmodeHandler.HandleDefault(middlewareDefault) @@ -123,7 +127,6 @@ func middlewareInteractive(evt *socketmode.Event, client *socketmode.Client) { switch callback.Type { case slack.InteractionTypeBlockActions: // See https://api.slack.com/apis/connections/socket-implement#button - client.Debugf("button clicked!") case slack.InteractionTypeShortcut: case slack.InteractionTypeViewSubmission: @@ -136,6 +139,10 @@ func middlewareInteractive(evt *socketmode.Event, client *socketmode.Client) { client.Ack(*evt.Request, payload) } +func middlewareInteractionTypeBlockActions(evt *socketmode.Event, client *socketmode.Client) { + client.Debugf("button clicked!") +} + func middlewareSlashCommand(evt *socketmode.Event, client *socketmode.Client) { cmd, ok := evt.Data.(slack.SlashCommand) if !ok { @@ -165,7 +172,6 @@ func middlewareSlashCommand(evt *socketmode.Event, client *socketmode.Client) { ), ), }} - client.Ack(*evt.Request, payload) } diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 12821f936..45b75445d 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -1,23 +1,29 @@ package socketmode import ( + "fmt" "log" + + "github.com/slack-go/slack" ) type SocketmodeHandler struct { Client *Client - EventMap map[EventType][]SocketmodeHandlerFunc + EventMap map[EventType][]SocketmodeHandlerFunc + InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc } type SocketmodeHandlerFunc func(*Event, *Client) func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) + interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) return &SocketmodeHandler{ - Client: client, - EventMap: eventMap, + Client: client, + EventMap: eventMap, + InteractionEventMap: interactioneventMap, } } @@ -25,6 +31,10 @@ func (r *SocketmodeHandler) Handle(et EventType, f SocketmodeHandlerFunc) { r.EventMap[et] = append(r.EventMap[et], f) } +func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f SocketmodeHandlerFunc) { + r.InteractionEventMap[et] = append(r.InteractionEventMap[et], f) +} + // RunSlackEventLoop receives the event via the socket // It receives events from Slack and each is handled as needed func (r *SocketmodeHandler) RunEventLoop() { @@ -36,6 +46,14 @@ func (r *SocketmodeHandler) RunEventLoop() { func (r *SocketmodeHandler) runEventLoop() { for evt := range r.Client.Events { + + // Some eventType can be further decomposed + switch evt.Type { + case EventTypeInteractive: + go r.Interaction(&evt) + // case EventTypeEventsAPI: + } + if handlers, ok := r.EventMap[evt.Type]; ok { // If we registered an event for _, f := range handlers { @@ -49,3 +67,23 @@ func (r *SocketmodeHandler) runEventLoop() { } } + +func (r *SocketmodeHandler) Interaction(evt *Event) { + interaction, ok := evt.Data.(slack.InteractionCallback) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + return + } + + if handlers, ok := r.InteractionEventMap[interaction.Type]; ok { + // If we registered an event + for _, f := range handlers { + go f(evt, r.Client) + } + } else { + // We need to explicitely subscribe to event in the Application Dashboard + // So every event sould be handle otherwise this is an error + log.Printf("Unexpected event type received: %v\n", evt.Type) + } + +} From 6d8be03f54f4cd408735d1f39c935d938815f87c Mon Sep 17 00:00:00 2001 From: xnok Date: Tue, 23 Feb 2021 00:10:05 -0500 Subject: [PATCH 05/34] feat: handler EventAPI inner events --- .../sockermode_handler/socketmode_handler.go | 39 ++++++++++++++++--- slackevents/inner_events.go | 34 ++++++++-------- slackevents/parsers.go | 2 +- socketmode/socketmode_eventhandler.go | 33 +++++++++++++++- socketmode/socketmode_test.go | 4 +- 5 files changed, 86 insertions(+), 26 deletions(-) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index 95c3dba87..de085a4aa 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -51,19 +51,21 @@ func main() { socketmodeHandler.Handle(socketmode.EventTypeConnectionError, middlewareConnectionError) socketmodeHandler.Handle(socketmode.EventTypeConnected, middlewareConnected) - // Handle the EventsAPI - socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) + //\\ EventTypeEventsAPI //\\ + // Handle all EventsAPI + // socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) // Handle a specific event from EventsAPI - // socketmodeHandler.HandleEventsAPI(socketmode.EventTypeAppMention, middlewareAppMentionEvent) + socketmodeHandler.HandleEventsAPI(slackevents.AppMention, middlewareAppMentionEvent) - // Handle the Interactive Events + //\\ EventTypeInteractive //\\ + // Handle all Interactive Events socketmodeHandler.Handle(socketmode.EventTypeInteractive, middlewareInteractive) // Handle a specific Interaction socketmodeHandler.HandleInteraction(slack.InteractionTypeBlockActions, middlewareInteractionTypeBlockActions) - // Handle the SlashCommand + // Handle all SlashCommand socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) // socketmodeHandler.HandleDefault(middlewareDefault) @@ -84,6 +86,7 @@ func middlewareConnected(evt *socketmode.Event, client *socketmode.Client) { } func middlewareEventsAPI(evt *socketmode.Event, client *socketmode.Client) { + fmt.Println("middlewareEventsAPI") eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { fmt.Printf("Ignored %+v\n", evt) @@ -112,11 +115,35 @@ func middlewareEventsAPI(evt *socketmode.Event, client *socketmode.Client) { } } +func middlewareAppMentionEvent(evt *socketmode.Event, client *socketmode.Client) { + fmt.Printf("middlewareAppMentionEvent: %+v\n", evt) + eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + return + } + + fmt.Printf("middlewareAppMentionEvent: %+v\n", eventsAPIEvent.InnerEvent.Data) + + client.Ack(*evt.Request) + + ev, ok := eventsAPIEvent.InnerEvent.Data.(*slackevents.AppMentionEvent) + if !ok { + fmt.Printf("Ignored %+v\n", ev) + return + } + + fmt.Printf("We have been mentionned in %v\n", ev.Channel) + _, _, err := client.GetApiClient().PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) + if err != nil { + fmt.Printf("failed posting message: %v", err) + } +} + func middlewareInteractive(evt *socketmode.Event, client *socketmode.Client) { callback, ok := evt.Data.(slack.InteractionCallback) if !ok { fmt.Printf("Ignored %+v\n", evt) - return } diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 641d1691e..675bbfba7 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -303,43 +303,45 @@ func (e MessageEvent) IsEdited() bool { e.Message.Edited != nil } +type EventAPIType string + const ( // AppMention is an Events API subscribable event - AppMention = "app_mention" + AppMention = EventAPIType("app_mention") // AppHomeOpened Your Slack app home was opened - AppHomeOpened = "app_home_opened" + AppHomeOpened = EventAPIType("app_home_opened") // AppUninstalled Your Slack app was uninstalled. - AppUninstalled = "app_uninstalled" + AppUninstalled = EventAPIType("app_uninstalled") // GridMigrationFinished An enterprise grid migration has finished on this workspace. - GridMigrationFinished = "grid_migration_finished" + GridMigrationFinished = EventAPIType("grid_migration_finished") // GridMigrationStarted An enterprise grid migration has started on this workspace. - GridMigrationStarted = "grid_migration_started" + GridMigrationStarted = EventAPIType("grid_migration_started") // LinkShared A message was posted containing one or more links relevant to your application - LinkShared = "link_shared" + LinkShared = EventAPIType("link_shared") // Message A message was posted to a channel, private channel (group), im, or mim - Message = "message" + Message = EventAPIType("message") // Member Joined Channel - MemberJoinedChannel = "member_joined_channel" + MemberJoinedChannel = EventAPIType("member_joined_channel") // Member Left Channel - MemberLeftChannel = "member_left_channel" + MemberLeftChannel = EventAPIType("member_left_channel") // PinAdded An item was pinned to a channel - PinAdded = "pin_added" + PinAdded = EventAPIType("pin_added") // PinRemoved An item was unpinned from a channel - PinRemoved = "pin_removed" + PinRemoved = EventAPIType("pin_removed") // ReactionAdded An reaction was added to a message - ReactionAdded = "reaction_added" + ReactionAdded = EventAPIType("reaction_added") // ReactionRemoved An reaction was removed from a message - ReactionRemoved = "reaction_removed" + ReactionRemoved = EventAPIType("reaction_removed") // TokensRevoked APP's API tokes are revoked - TokensRevoked = "tokens_revoked" + TokensRevoked = EventAPIType("tokens_revoked") // EmojiChanged A custom emoji has been added or changed - EmojiChanged = "emoji_changed" + EmojiChanged = EventAPIType("emoji_changed") ) // EventsAPIInnerEventMapping maps INNER Event API events to their corresponding struct // implementations. The structs should be instances of the unmarshalling // target for the matching event type. -var EventsAPIInnerEventMapping = map[string]interface{}{ +var EventsAPIInnerEventMapping = map[EventAPIType]interface{}{ AppMention: AppMentionEvent{}, AppHomeOpened: AppHomeOpenedEvent{}, AppUninstalled: AppUninstalledEvent{}, diff --git a/slackevents/parsers.go b/slackevents/parsers.go index 07f296aeb..fab7185a8 100644 --- a/slackevents/parsers.go +++ b/slackevents/parsers.go @@ -19,7 +19,7 @@ func eventsMap(t string) (interface{}, bool) { // Must parse EventsAPI FIRST as both RTM and EventsAPI // have a type: "Message" event. // TODO: Handle these cases more explicitly. - v, exists := EventsAPIInnerEventMapping[t] + v, exists := EventsAPIInnerEventMapping[EventAPIType(t)] if exists { return v, exists } diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 45b75445d..a34fbe0f9 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -5,6 +5,7 @@ import ( "log" "github.com/slack-go/slack" + "github.com/slack-go/slack/slackevents" ) type SocketmodeHandler struct { @@ -12,6 +13,7 @@ type SocketmodeHandler struct { EventMap map[EventType][]SocketmodeHandlerFunc InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc + EventApiMap map[slackevents.EventAPIType][]SocketmodeHandlerFunc } type SocketmodeHandlerFunc func(*Event, *Client) @@ -19,10 +21,12 @@ type SocketmodeHandlerFunc func(*Event, *Client) func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) + eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) return &SocketmodeHandler{ Client: client, EventMap: eventMap, + EventApiMap: eventApiMap, InteractionEventMap: interactioneventMap, } } @@ -35,6 +39,10 @@ func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f Socket r.InteractionEventMap[et] = append(r.InteractionEventMap[et], f) } +func (r *SocketmodeHandler) HandleEventsAPI(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { + r.EventApiMap[et] = append(r.EventApiMap[et], f) +} + // RunSlackEventLoop receives the event via the socket // It receives events from Slack and each is handled as needed func (r *SocketmodeHandler) RunEventLoop() { @@ -51,7 +59,8 @@ func (r *SocketmodeHandler) runEventLoop() { switch evt.Type { case EventTypeInteractive: go r.Interaction(&evt) - // case EventTypeEventsAPI: + case EventTypeEventsAPI: + go r.EventAPI(&evt) } if handlers, ok := r.EventMap[evt.Type]; ok { @@ -87,3 +96,25 @@ func (r *SocketmodeHandler) Interaction(evt *Event) { } } + +func (r *SocketmodeHandler) EventAPI(evt *Event) { + eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + return + } + + innerEventType := slackevents.EventAPIType(eventsAPIEvent.InnerEvent.Type) + + if handlers, ok := r.EventApiMap[innerEventType]; ok { + // If we registered an event + for _, f := range handlers { + go f(evt, r.Client) + } + } else { + // We need to explicitely subscribe to event in the Application Dashboard + // So every event sould be handle otherwise this is an error + log.Printf("Unexpected event type received: %v\n", evt.Type) + } + +} diff --git a/socketmode/socketmode_test.go b/socketmode/socketmode_test.go index b9ce77690..065db1eb5 100644 --- a/socketmode/socketmode_test.go +++ b/socketmode/socketmode_test.go @@ -229,9 +229,9 @@ func TestEventParsing(t *testing.T) { EventContext: "1-app_mention-redacted-redacted", }, InnerEvent: slackevents.EventsAPIInnerEvent{ - Type: slackevents.AppMention, + Type: string(slackevents.AppMention), Data: &slackevents.AppMentionEvent{ - Type: slackevents.AppMention, + Type: string(slackevents.AppMention), User: "redacted", Text: "<@U01JKSB8T7Y> test", TimeStamp: "1610927831.000200", From 03e47e9d37153318b3b9c65e22392b302cc81aa4 Mon Sep 17 00:00:00 2001 From: xnok Date: Tue, 23 Feb 2021 23:40:40 -0500 Subject: [PATCH 06/34] test: write unit tests --- .../sockermode_handler/socketmode_handler.go | 2 +- socketmode/socketmode_eventhandler.go | 98 ++++-- socketmode/socketmode_eventhandler_test.go | 300 ++++++++++++++++++ 3 files changed, 370 insertions(+), 30 deletions(-) create mode 100644 socketmode/socketmode_eventhandler_test.go diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index de085a4aa..f4cc4fd4a 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -53,7 +53,7 @@ func main() { //\\ EventTypeEventsAPI //\\ // Handle all EventsAPI - // socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) + socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) // Handle a specific event from EventsAPI socketmodeHandler.HandleEventsAPI(slackevents.AppMention, middlewareAppMentionEvent) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index a34fbe0f9..97bc5a154 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -14,6 +14,8 @@ type SocketmodeHandler struct { EventMap map[EventType][]SocketmodeHandlerFunc InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc EventApiMap map[slackevents.EventAPIType][]SocketmodeHandlerFunc + + Default SocketmodeHandlerFunc } type SocketmodeHandlerFunc func(*Event, *Client) @@ -28,23 +30,33 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { EventMap: eventMap, EventApiMap: eventApiMap, InteractionEventMap: interactioneventMap, + Default: func(e *Event, c *Client) { + log.Printf("Unexpected event type received: %v\n", e.Type) + }, } } +// Register the middleare funtion to use to handle an Event (from socketmode) func (r *SocketmodeHandler) Handle(et EventType, f SocketmodeHandlerFunc) { r.EventMap[et] = append(r.EventMap[et], f) } +// Register the middleare funtion to use to handle an Interaction func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f SocketmodeHandlerFunc) { r.InteractionEventMap[et] = append(r.InteractionEventMap[et], f) } +// Register the middleare funtion to use to handle an Event (from slackevents) func (r *SocketmodeHandler) HandleEventsAPI(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { r.EventApiMap[et] = append(r.EventApiMap[et], f) } +// Register the middleare funtion to use as a last resort +func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) { + r.Default = f +} + // RunSlackEventLoop receives the event via the socket -// It receives events from Slack and each is handled as needed func (r *SocketmodeHandler) RunEventLoop() { go r.runEventLoop() @@ -54,34 +66,48 @@ func (r *SocketmodeHandler) RunEventLoop() { func (r *SocketmodeHandler) runEventLoop() { for evt := range r.Client.Events { + r.dispatcher(evt) + } +} - // Some eventType can be further decomposed - switch evt.Type { - case EventTypeInteractive: - go r.Interaction(&evt) - case EventTypeEventsAPI: - go r.EventAPI(&evt) - } +func (r *SocketmodeHandler) dispatcher(evt Event) { + var ishandled bool + + // Some eventType can be further decomposed + switch evt.Type { + case EventTypeInteractive: + ishandled = r.interactionDispatcher(&evt) + case EventTypeEventsAPI: + ishandled = r.eventAPIDispatcher(&evt) + default: + ishandled = r.socketmodeDispatcher(&evt) + } - if handlers, ok := r.EventMap[evt.Type]; ok { - // If we registered an event - for _, f := range handlers { - go f(&evt, r.Client) - } - } else { - // We need to explicitely subscribe to event in the Application Dashboard - // So every event sould be handle otherwise this is an error - log.Printf("Unexpected event type received: %v\n", evt.Type) + if !ishandled { + go r.Default(&evt, r.Client) + } +} + +// Dispatch socketmode events to the registered middleware +func (r *SocketmodeHandler) socketmodeDispatcher(evt *Event) bool { + if handlers, ok := r.EventMap[evt.Type]; ok { + // If we registered an event + for _, f := range handlers { + go f(evt, r.Client) } + return true } + + return false } -func (r *SocketmodeHandler) Interaction(evt *Event) { +// Dispatch interactions to the registered middleware +func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { interaction, ok := evt.Data.(slack.InteractionCallback) if !ok { fmt.Printf("Ignored %+v\n", evt) - return + return false } if handlers, ok := r.InteractionEventMap[interaction.Type]; ok { @@ -89,19 +115,27 @@ func (r *SocketmodeHandler) Interaction(evt *Event) { for _, f := range handlers { go f(evt, r.Client) } - } else { - // We need to explicitely subscribe to event in the Application Dashboard - // So every event sould be handle otherwise this is an error - log.Printf("Unexpected event type received: %v\n", evt.Type) + + return true + } else if handlers, ok := r.EventMap[evt.Type]; ok { + // fallback it this event is not handle by a more specific handler + + for _, f := range handlers { + go f(evt, r.Client) + } + + return true } + return false } -func (r *SocketmodeHandler) EventAPI(evt *Event) { +// Dispatch eventAPI events to the registered middleware +func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { fmt.Printf("Ignored %+v\n", evt) - return + return false } innerEventType := slackevents.EventAPIType(eventsAPIEvent.InnerEvent.Type) @@ -111,10 +145,16 @@ func (r *SocketmodeHandler) EventAPI(evt *Event) { for _, f := range handlers { go f(evt, r.Client) } - } else { - // We need to explicitely subscribe to event in the Application Dashboard - // So every event sould be handle otherwise this is an error - log.Printf("Unexpected event type received: %v\n", evt.Type) + + return true + } else if handlers, ok := r.EventMap[evt.Type]; ok { + // fallback it this event is not handle by a more specific handler + for _, f := range handlers { + go f(evt, r.Client) + } + + return true } + return false } diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go new file mode 100644 index 000000000..2530806df --- /dev/null +++ b/socketmode/socketmode_eventhandler_test.go @@ -0,0 +1,300 @@ +package socketmode + +import ( + "reflect" + "runtime" + "testing" + + "github.com/slack-go/slack" + "github.com/slack-go/slack/slackevents" +) + +func init_SocketmodeHandler() *SocketmodeHandler { + eventMap := make(map[EventType][]SocketmodeHandlerFunc) + interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) + eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) + + return &SocketmodeHandler{ + Client: &Client{}, + EventMap: eventMap, + EventApiMap: eventApiMap, + InteractionEventMap: interactioneventMap, + } +} + +type testProbe struct { + called bool +} + +func testing_wrapper(ch chan<- string, f SocketmodeHandlerFunc) SocketmodeHandlerFunc { + return SocketmodeHandlerFunc(func(e *Event, c *Client) { + f(e, c) + + var name_f string + + // test with the name of the function we called + v := reflect.ValueOf(f) + if v.Kind() == reflect.Func { + if rf := runtime.FuncForPC(v.Pointer()); rf != nil { + name_f = rf.Name() + } + } else { + name_f = v.String() + } + + ch <- name_f + }) +} + +func middleware_interaction(evt *Event, client *Client) { + //do nothing +} + +func middleware_eventapi(evt *Event, client *Client) { + //do nothing +} + +func middleware(evt *Event, client *Client) { + //do nothing +} + +func defaultmiddleware(evt *Event, client *Client) { + //do nothing +} + +func TestSocketmodeHandler_Handle(t *testing.T) { + type args struct { + evt Event + evt_type EventType + } + tests := []struct { + name string + args args + want string //what is the name of the function we want to be called + }{ + { + name: "Event Match registed function", + args: args{ + evt: Event{ + Type: EventTypeConnecting, + }, + evt_type: EventTypeConnecting, + }, + want: "github.com/slack-go/slack/socketmode.middleware", + }, { + name: "Event do not registed function", + args: args{ + evt: Event{ + Type: EventTypeConnected, + }, + evt_type: EventTypeConnecting, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := init_SocketmodeHandler() + + c := make(chan string) + + r.Handle(tt.args.evt_type, testing_wrapper(c, middleware)) + r.HandleDefault(testing_wrapper(c, defaultmiddleware)) + + r.dispatcher(tt.args.evt) + + got := <-c + + if got != tt.want { + t.Fatalf("middleware was not called for EventTy(\"%v\"), got %v", tt.args.evt_type, got) + } + }) + } +} + +func TestSocketmodeHandler_HandleInteraction(t *testing.T) { + type args struct { + evt Event + register func(*SocketmodeHandler, chan<- string) + } + tests := []struct { + name string + args args + want string //what is the name of the function we want to be called + }{ + { + name: "Event Match registed function", + args: args{ + evt: Event{ + Type: EventTypeInteractive, + Data: slack.InteractionCallback{ + Type: slack.InteractionTypeBlockActions, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.Handle(EventTypeInteractive, testing_wrapper(c, middleware)) + r.HandleInteraction(slack.InteractionTypeBlockActions, testing_wrapper(c, middleware_interaction)) + }, + }, + want: "github.com/slack-go/slack/socketmode.middleware_interaction", + }, { + name: "Event do not Match any registed function", + args: args{ + evt: Event{ + Type: EventTypeInteractive, + Data: slack.InteractionCallback{ + Type: slack.InteractionTypeBlockActions, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteraction(slack.InteractionTypeBlockSuggestion, testing_wrapper(c, middleware_interaction)) + }, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, { + name: "Event with invalid data is handled by default middleware", + args: args{ + evt: Event{ + Type: EventTypeInteractive, + Data: map[string]string{ + "brokendata": "test", + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteraction(slack.InteractionTypeBlockActions, testing_wrapper(c, middleware_interaction)) + }, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, { + name: "Event is handled as EventTypeInteractive", + args: args{ + evt: Event{ + Type: EventTypeInteractive, + Data: slack.InteractionCallback{ + Type: slack.InteractionTypeBlockActions, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.Handle(EventTypeInteractive, testing_wrapper(c, middleware)) + }, + }, + want: "github.com/slack-go/slack/socketmode.middleware", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := init_SocketmodeHandler() + + c := make(chan string) + + tt.args.register(r, c) + r.HandleDefault(testing_wrapper(c, defaultmiddleware)) + + r.dispatcher(tt.args.evt) + + got := <-c + + if got != tt.want { + t.Fatalf("%s was not called for EventTy(\"%v\"), got %v", tt.want, tt.args.evt.Type, got) + } + }) + } +} + +func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { + type args struct { + evt Event + register func(*SocketmodeHandler, chan<- string) + } + tests := []struct { + name string + args args + want string //what is the name of the function we want to be called + }{ + { + name: "Event Match registed function", + args: args{ + evt: Event{ + Type: EventTypeEventsAPI, + Data: slackevents.EventsAPIEvent{ + Type: "event_callback", + InnerEvent: slackevents.EventsAPIInnerEvent{ + Type: string(slackevents.AppMention), + }, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.Handle(EventTypeEventsAPI, testing_wrapper(c, middleware)) + r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) + }, + }, + want: "github.com/slack-go/slack/socketmode.middleware_eventapi", + }, { + name: "Event do not Match any registed function", + args: args{ + evt: Event{ + Type: EventTypeEventsAPI, + Data: slackevents.EventsAPIEvent{ + Type: "event_callback", + InnerEvent: slackevents.EventsAPIInnerEvent{ + Type: string(slackevents.MemberJoinedChannel), + }, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) + }, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, { + name: "Event with invalid data is handled by default middleware", + args: args{ + evt: Event{ + Type: EventTypeEventsAPI, + Data: map[string]string{ + "brokendata": "test", + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) + }, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, { + name: "Event is handled as EventTypeInteractive", + args: args{ + evt: Event{ + Type: EventTypeEventsAPI, + Data: slackevents.EventsAPIEvent{ + Type: "event_callback", + InnerEvent: slackevents.EventsAPIInnerEvent{ + Type: string(slackevents.MemberJoinedChannel), + }, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.Handle(EventTypeEventsAPI, testing_wrapper(c, middleware)) + }, + }, + want: "github.com/slack-go/slack/socketmode.middleware", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := init_SocketmodeHandler() + + c := make(chan string) + + tt.args.register(r, c) + r.HandleDefault(testing_wrapper(c, defaultmiddleware)) + + r.dispatcher(tt.args.evt) + + got := <-c + + if got != tt.want { + t.Fatalf("%s was not called for EventTy(\"%v\"), got %v", tt.want, tt.args.evt.Type, got) + } + }) + } +} From 2c8000f2780a0d8aac8d3b19afaa3750a4a385dd Mon Sep 17 00:00:00 2001 From: xNok Date: Fri, 26 Feb 2021 13:19:39 -0500 Subject: [PATCH 07/34] feat: added type for SocketmodeMiddlewareFunc --- socketmode/socketmode_eventhandler.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 97bc5a154..a1f2b69bf 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -18,8 +18,12 @@ type SocketmodeHandler struct { Default SocketmodeHandlerFunc } +// Handler have access to the event and socketmode client type SocketmodeHandlerFunc func(*Event, *Client) +// Middleware accept SocketmodeHandlerFunc, and return SocketmodeHandlerFunc +type SocketmodeMiddlewareFunc func(SocketmodeHandlerFunc) SocketmodeHandlerFunc + func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) From 357e3741f9596231e11d9d592e9fb7528f5440ce Mon Sep 17 00:00:00 2001 From: xNok Date: Sat, 27 Feb 2021 18:38:16 -0500 Subject: [PATCH 08/34] fix: typos --- socketmode/socketmode_eventhandler_test.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go index 2530806df..858c4a35d 100644 --- a/socketmode/socketmode_eventhandler_test.go +++ b/socketmode/socketmode_eventhandler_test.go @@ -22,10 +22,6 @@ func init_SocketmodeHandler() *SocketmodeHandler { } } -type testProbe struct { - called bool -} - func testing_wrapper(ch chan<- string, f SocketmodeHandlerFunc) SocketmodeHandlerFunc { return SocketmodeHandlerFunc(func(e *Event, c *Client) { f(e, c) @@ -73,7 +69,7 @@ func TestSocketmodeHandler_Handle(t *testing.T) { want string //what is the name of the function we want to be called }{ { - name: "Event Match registed function", + name: "Event Match registered function", args: args{ evt: Event{ Type: EventTypeConnecting, @@ -82,7 +78,7 @@ func TestSocketmodeHandler_Handle(t *testing.T) { }, want: "github.com/slack-go/slack/socketmode.middleware", }, { - name: "Event do not registed function", + name: "Event do not registered function", args: args{ evt: Event{ Type: EventTypeConnected, @@ -123,7 +119,7 @@ func TestSocketmodeHandler_HandleInteraction(t *testing.T) { want string //what is the name of the function we want to be called }{ { - name: "Event Match registed function", + name: "Event Match registered function", args: args{ evt: Event{ Type: EventTypeInteractive, @@ -138,7 +134,7 @@ func TestSocketmodeHandler_HandleInteraction(t *testing.T) { }, want: "github.com/slack-go/slack/socketmode.middleware_interaction", }, { - name: "Event do not Match any registed function", + name: "Event do not Match any registered function", args: args{ evt: Event{ Type: EventTypeInteractive, @@ -212,7 +208,7 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { want string //what is the name of the function we want to be called }{ { - name: "Event Match registed function", + name: "Event Match registered function", args: args{ evt: Event{ Type: EventTypeEventsAPI, @@ -230,7 +226,7 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { }, want: "github.com/slack-go/slack/socketmode.middleware_eventapi", }, { - name: "Event do not Match any registed function", + name: "Event do not Match any registered function", args: args{ evt: Event{ Type: EventTypeEventsAPI, From e976c8a19225296f3f445ab5acd41629fd0ea2d7 Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 28 Feb 2021 01:18:38 -0500 Subject: [PATCH 09/34] fix: typos --- socketmode/socketmode_eventhandler.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index a1f2b69bf..c024149fc 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -40,22 +40,22 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { } } -// Register the middleare funtion to use to handle an Event (from socketmode) +// Register the middleare function to use to handle an Event (from socketmode) func (r *SocketmodeHandler) Handle(et EventType, f SocketmodeHandlerFunc) { r.EventMap[et] = append(r.EventMap[et], f) } -// Register the middleare funtion to use to handle an Interaction +// Register the middleare function to use to handle an Interaction func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f SocketmodeHandlerFunc) { r.InteractionEventMap[et] = append(r.InteractionEventMap[et], f) } -// Register the middleare funtion to use to handle an Event (from slackevents) +// Register the middleare function to use to handle an Event (from slackevents) func (r *SocketmodeHandler) HandleEventsAPI(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { r.EventApiMap[et] = append(r.EventApiMap[et], f) } -// Register the middleare funtion to use as a last resort +// Register the middleare function to use as a last resort func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) { r.Default = f } From b1318d993d1847c43dfbb9d233b7242305837d07 Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 7 Mar 2021 22:39:16 -0500 Subject: [PATCH 10/34] feat: Handle interaction block action by action ID --- .../sockermode_handler/socketmode_handler.go | 4 +- socketmode/socketmode_eventhandler.go | 85 ++++++++++++------ socketmode/socketmode_eventhandler_test.go | 87 ++++++++++++++++++- 3 files changed, 143 insertions(+), 33 deletions(-) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index f4cc4fd4a..abb2a434d 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -116,15 +116,13 @@ func middlewareEventsAPI(evt *socketmode.Event, client *socketmode.Client) { } func middlewareAppMentionEvent(evt *socketmode.Event, client *socketmode.Client) { - fmt.Printf("middlewareAppMentionEvent: %+v\n", evt) + eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { fmt.Printf("Ignored %+v\n", evt) return } - fmt.Printf("middlewareAppMentionEvent: %+v\n", eventsAPIEvent.InnerEvent.Data) - client.Ack(*evt.Request) ev, ok := eventsAPIEvent.InnerEvent.Data.(*slackevents.AppMentionEvent) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index c024149fc..ea9e7d1e7 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -11,9 +11,10 @@ import ( type SocketmodeHandler struct { Client *Client - EventMap map[EventType][]SocketmodeHandlerFunc - InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc - EventApiMap map[slackevents.EventAPIType][]SocketmodeHandlerFunc + EventMap map[EventType][]SocketmodeHandlerFunc + InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc + InteractionBlockActionEventMap map[string][]SocketmodeHandlerFunc + EventApiMap map[slackevents.EventAPIType][]SocketmodeHandlerFunc Default SocketmodeHandlerFunc } @@ -26,36 +27,43 @@ type SocketmodeMiddlewareFunc func(SocketmodeHandlerFunc) SocketmodeHandlerFunc func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) - interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) + interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) + interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) return &SocketmodeHandler{ - Client: client, - EventMap: eventMap, - EventApiMap: eventApiMap, - InteractionEventMap: interactioneventMap, + Client: client, + EventMap: eventMap, + EventApiMap: eventApiMap, + InteractionEventMap: interactionEventMap, + InteractionBlockActionEventMap: interactionBlockActionEventMap, Default: func(e *Event, c *Client) { log.Printf("Unexpected event type received: %v\n", e.Type) }, } } -// Register the middleare function to use to handle an Event (from socketmode) +// Register a middleare function to use to handle an Event (from socketmode) func (r *SocketmodeHandler) Handle(et EventType, f SocketmodeHandlerFunc) { r.EventMap[et] = append(r.EventMap[et], f) } -// Register the middleare function to use to handle an Interaction +// Register a middleare function to use to handle an Interaction func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f SocketmodeHandlerFunc) { r.InteractionEventMap[et] = append(r.InteractionEventMap[et], f) } -// Register the middleare function to use to handle an Event (from slackevents) +// Register a middleare function to use to handle an Interaction Block Action referenced by its ActionID +func (r *SocketmodeHandler) HandleInteractionBlockAction(action_id string, f SocketmodeHandlerFunc) { + r.InteractionBlockActionEventMap[action_id] = append(r.InteractionBlockActionEventMap[action_id], f) +} + +// Register a middleare function to use to handle an Event (from slackevents) func (r *SocketmodeHandler) HandleEventsAPI(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { r.EventApiMap[et] = append(r.EventApiMap[et], f) } -// Register the middleare function to use as a last resort +// Register a middleare function to use as a last resort func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) { r.Default = f } @@ -108,34 +116,55 @@ func (r *SocketmodeHandler) socketmodeDispatcher(evt *Event) bool { // Dispatch interactions to the registered middleware func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { + var ishandled bool = false + interaction, ok := evt.Data.(slack.InteractionCallback) if !ok { fmt.Printf("Ignored %+v\n", evt) return false } - if handlers, ok := r.InteractionEventMap[interaction.Type]; ok { - // If we registered an event + // Level 1 - socketmode EventType + if handlers, ok := r.EventMap[evt.Type]; ok { + for _, f := range handlers { go f(evt, r.Client) } - return true - } else if handlers, ok := r.EventMap[evt.Type]; ok { - // fallback it this event is not handle by a more specific handler + ishandled = true + } + // Level 2 - interaction EventType + if handlers, ok := r.InteractionEventMap[interaction.Type]; ok { + // If we registered an event for _, f := range handlers { go f(evt, r.Client) } - return true + ishandled = true } - return false + // Level 3 - interaction with actionID + block_actions := interaction.ActionCallback.BlockActions + // outmoded approach won`t be implemented + // attachments_actions := interaction.ActionCallback.AttachmentActions + + for _, action := range block_actions { + if handlers, ok := r.InteractionBlockActionEventMap[action.ActionID]; ok { + + for _, f := range handlers { + go f(evt, r.Client) + } + + ishandled = true + } + } + return ishandled } // Dispatch eventAPI events to the registered middleware func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { + var ishandled bool = false eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { fmt.Printf("Ignored %+v\n", evt) @@ -144,21 +173,25 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { innerEventType := slackevents.EventAPIType(eventsAPIEvent.InnerEvent.Type) - if handlers, ok := r.EventApiMap[innerEventType]; ok { - // If we registered an event + // Level 1 - socketmode EventType + if handlers, ok := r.EventMap[evt.Type]; ok { + // fallback it this event is not handle by a more specific handler for _, f := range handlers { go f(evt, r.Client) } - return true - } else if handlers, ok := r.EventMap[evt.Type]; ok { - // fallback it this event is not handle by a more specific handler + ishandled = true + } + + // Level 2 - EventAPI EventType + if handlers, ok := r.EventApiMap[innerEventType]; ok { + // If we registered an event for _, f := range handlers { go f(evt, r.Client) } - return true + ishandled = true } - return false + return ishandled } diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go index 858c4a35d..1448a1e76 100644 --- a/socketmode/socketmode_eventhandler_test.go +++ b/socketmode/socketmode_eventhandler_test.go @@ -13,15 +13,19 @@ func init_SocketmodeHandler() *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) + interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) return &SocketmodeHandler{ - Client: &Client{}, - EventMap: eventMap, - EventApiMap: eventApiMap, - InteractionEventMap: interactioneventMap, + Client: &Client{}, + EventMap: eventMap, + EventApiMap: eventApiMap, + InteractionEventMap: interactioneventMap, + InteractionBlockActionEventMap: interactionBlockActionEventMap, } } +// The goal of this function is to catch the name of the function that is behing called +// This let us validate that the dispatcher did its job correctly func testing_wrapper(ch chan<- string, f SocketmodeHandlerFunc) SocketmodeHandlerFunc { return SocketmodeHandlerFunc(func(e *Event, c *Client) { f(e, c) @@ -46,6 +50,10 @@ func middleware_interaction(evt *Event, client *Client) { //do nothing } +func middleware_interaction_block_action(evt *Event, client *Client) { + //do nothing +} + func middleware_eventapi(evt *Event, client *Client) { //do nothing } @@ -294,3 +302,74 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { }) } } + +func TestSocketmodeHandler_HandleInteractionBlockAction(t *testing.T) { + type args struct { + evt Event + register func(*SocketmodeHandler, chan<- string) + } + tests := []struct { + name string + args args + want string //what is the name of the function we want to be called + }{ + { + name: "Event Match registered function", + args: args{ + evt: Event{ + Type: EventTypeInteractive, + Data: slack.InteractionCallback{ + Type: slack.InteractionTypeBlockActions, + ActionCallback: slack.ActionCallbacks{ + BlockActions: []*slack.BlockAction{ + { + ActionID: "add_note", + Text: slack.TextBlockObject{ + Type: "plain_text", + Text: "Add a Stickie", + }, + }, + }, + }, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteractionBlockAction("add_note", testing_wrapper(c, middleware_interaction_block_action)) + }, + }, + want: "github.com/slack-go/slack/socketmode.middleware_interaction_block_action", + }, { + name: "Event do not Match any registered function", + args: args{ + evt: Event{ + Type: EventTypeInteractive, + Data: slack.InteractionCallback{ + Type: slack.InteractionTypeBlockActions, + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteractionBlockAction("add_note", testing_wrapper(c, middleware_interaction_block_action)) + }, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := init_SocketmodeHandler() + + c := make(chan string) + + tt.args.register(r, c) + r.HandleDefault(testing_wrapper(c, defaultmiddleware)) + + r.dispatcher(tt.args.evt) + + got := <-c + + if got != tt.want { + t.Fatalf("%s was not called for EventTy(\"%v\"), got %v", tt.want, tt.args.evt.Type, got) + } + }) + } +} From 28ed51b50058b9d684c99fbabab7828223a175fa Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 8 Mar 2021 23:45:40 -0500 Subject: [PATCH 11/34] fix: tests for socketmode --- socketmode/socketmode_eventhandler_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go index 1448a1e76..9d127c67d 100644 --- a/socketmode/socketmode_eventhandler_test.go +++ b/socketmode/socketmode_eventhandler_test.go @@ -136,7 +136,6 @@ func TestSocketmodeHandler_HandleInteraction(t *testing.T) { }, }, register: func(r *SocketmodeHandler, c chan<- string) { - r.Handle(EventTypeInteractive, testing_wrapper(c, middleware)) r.HandleInteraction(slack.InteractionTypeBlockActions, testing_wrapper(c, middleware_interaction)) }, }, @@ -228,7 +227,6 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { }, }, register: func(r *SocketmodeHandler, c chan<- string) { - r.Handle(EventTypeEventsAPI, testing_wrapper(c, middleware)) r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) }, }, From 16e755773ca034c2b79d57f83cb784725fa4b026 Mon Sep 17 00:00:00 2001 From: xNok Date: Wed, 21 Apr 2021 18:31:26 -0400 Subject: [PATCH 12/34] fix: linting issue --- slackevents/inner_events.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 20e84fe48..d52ce95c5 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -327,9 +327,9 @@ const ( // AppHomeOpened Your Slack app home was opened AppHomeOpened = EventAPIType("app_home_opened") // AppUninstalled Your Slack app was uninstalled. - AppUninstalled = EventAPIType("app_uninstalled") + AppUninstalled = EventAPIType("app_uninstalled") // ChannelCreated is sent when a new channel is created. - ChannelCreated = EventAPIType("channel_created") + ChannelCreated = EventAPIType("channel_created") // GridMigrationFinished An enterprise grid migration has finished on this workspace. GridMigrationFinished = EventAPIType("grid_migration_finished") // GridMigrationStarted An enterprise grid migration has started on this workspace. From 6410dfe75b33743cc478e6ea2e814221451c85c4 Mon Sep 17 00:00:00 2001 From: xNok Date: Sat, 8 May 2021 14:58:32 -0400 Subject: [PATCH 13/34] feat: dedicated handler for Slash commands --- .../sockermode_handler/socketmode_handler.go | 1 + socketmode/socketmode_eventhandler.go | 43 ++++++++---- socketmode/socketmode_eventhandler_test.go | 68 +++++++++++++++++++ 3 files changed, 100 insertions(+), 12 deletions(-) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index abb2a434d..0f63fb6fd 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -67,6 +67,7 @@ func main() { // Handle all SlashCommand socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) + socketmodeHandler.HandleSlashCommand(socketmode.EventTypeSlashCommand, middlewareSlashCommand) // socketmodeHandler.HandleDefault(middlewareDefault) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index ea9e7d1e7..05503ddd8 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -15,6 +15,7 @@ type SocketmodeHandler struct { InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc InteractionBlockActionEventMap map[string][]SocketmodeHandlerFunc EventApiMap map[slackevents.EventAPIType][]SocketmodeHandlerFunc + SlashCommandMap map[string][]SocketmodeHandlerFunc Default SocketmodeHandlerFunc } @@ -63,6 +64,10 @@ func (r *SocketmodeHandler) HandleEventsAPI(et slackevents.EventAPIType, f Socke r.EventApiMap[et] = append(r.EventApiMap[et], f) } +func (r *SocketmodeHandler) HandleSlashCommand(command string, f SocketmodeHandlerFunc) { + r.SlashCommandMap[command] = append(r.SlashCommandMap[command], f) +} + // Register a middleare function to use as a last resort func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) { r.Default = f @@ -91,6 +96,8 @@ func (r *SocketmodeHandler) dispatcher(evt Event) { ishandled = r.interactionDispatcher(&evt) case EventTypeEventsAPI: ishandled = r.eventAPIDispatcher(&evt) + case EventTypeSlashCommand: + ishandled = r.slashCommandDispatcher(&evt) default: ishandled = r.socketmodeDispatcher(&evt) } @@ -125,14 +132,7 @@ func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { } // Level 1 - socketmode EventType - if handlers, ok := r.EventMap[evt.Type]; ok { - - for _, f := range handlers { - go f(evt, r.Client) - } - - ishandled = true - } + ishandled = r.socketmodeDispatcher(evt) // Level 2 - interaction EventType if handlers, ok := r.InteractionEventMap[interaction.Type]; ok { @@ -174,8 +174,11 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { innerEventType := slackevents.EventAPIType(eventsAPIEvent.InnerEvent.Type) // Level 1 - socketmode EventType - if handlers, ok := r.EventMap[evt.Type]; ok { - // fallback it this event is not handle by a more specific handler + ishandled = r.socketmodeDispatcher(evt) + + // Level 2 - EventAPI EventType + if handlers, ok := r.EventApiMap[innerEventType]; ok { + // If we registered an event for _, f := range handlers { go f(evt, r.Client) } @@ -183,8 +186,23 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { ishandled = true } - // Level 2 - EventAPI EventType - if handlers, ok := r.EventApiMap[innerEventType]; ok { + return ishandled +} + +// Dispatch SlashCommands events to the registered middleware +func (r *SocketmodeHandler) slashCommandDispatcher(evt *Event) bool { + var ishandled bool = false + slashCommandEvent, ok := evt.Data.(slack.SlashCommand) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + return false + } + + // Level 1 - socketmode EventType + ishandled = r.socketmodeDispatcher(evt) + + // Level 2 - SlackCommand by name + if handlers, ok := r.SlashCommandMap[slashCommandEvent.Command]; ok { // If we registered an event for _, f := range handlers { go f(evt, r.Client) @@ -194,4 +212,5 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { } return ishandled + } diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go index 9d127c67d..ba3664666 100644 --- a/socketmode/socketmode_eventhandler_test.go +++ b/socketmode/socketmode_eventhandler_test.go @@ -14,6 +14,7 @@ func init_SocketmodeHandler() *SocketmodeHandler { interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) + slashCommandMap := make(map[string][]SocketmodeHandlerFunc) return &SocketmodeHandler{ Client: &Client{}, @@ -21,6 +22,7 @@ func init_SocketmodeHandler() *SocketmodeHandler { EventApiMap: eventApiMap, InteractionEventMap: interactioneventMap, InteractionBlockActionEventMap: interactionBlockActionEventMap, + SlashCommandMap: slashCommandMap, } } @@ -66,6 +68,10 @@ func defaultmiddleware(evt *Event, client *Client) { //do nothing } +func middleware_slach_command(evt *Event, client *Client) { + //do nothing +} + func TestSocketmodeHandler_Handle(t *testing.T) { type args struct { evt Event @@ -371,3 +377,65 @@ func TestSocketmodeHandler_HandleInteractionBlockAction(t *testing.T) { }) } } + +func TestSocketmodeHandler_HandleSlashCommand(t *testing.T) { + type args struct { + evt Event + register func(*SocketmodeHandler, chan<- string) + } + tests := []struct { + name string + args args + want string //what is the name of the function we want to be called + }{ + { + name: "Event Match registered function", + args: args{ + evt: Event{ + Type: EventTypeSlashCommand, + Data: slack.SlashCommand{ + Command: "/rocket", + Text: "key=value", + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleSlashCommand("/rocket", testing_wrapper(c, middleware_slach_command)) + }, + }, + want: "github.com/slack-go/slack/socketmode.middleware_slach_command", + }, { + name: "Event do not Match any registered function", + args: args{ + evt: Event{ + Type: EventTypeSlashCommand, + Data: slack.SlashCommand{ + Command: "/broken_rocket", + Text: "key=value", + }, + }, + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleSlashCommand("/rocket", testing_wrapper(c, middleware_slach_command)) + }, + }, + want: "github.com/slack-go/slack/socketmode.defaultmiddleware", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := init_SocketmodeHandler() + + c := make(chan string) + + tt.args.register(r, c) + r.HandleDefault(testing_wrapper(c, defaultmiddleware)) + + r.dispatcher(tt.args.evt) + + got := <-c + + if got != tt.want { + t.Fatalf("%s was not called for EventTy(\"%v\"), got %v", tt.want, tt.args.evt.Type, got) + } + }) + } +} From e8ecf5e0681955d509a1ed73e0d35753099b6891 Mon Sep 17 00:00:00 2001 From: xNok Date: Sat, 8 May 2021 15:05:32 -0400 Subject: [PATCH 14/34] fix: exemple --- examples/sockermode_handler/socketmode_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/sockermode_handler/socketmode_handler.go index 0f63fb6fd..867b53af1 100644 --- a/examples/sockermode_handler/socketmode_handler.go +++ b/examples/sockermode_handler/socketmode_handler.go @@ -67,7 +67,7 @@ func main() { // Handle all SlashCommand socketmodeHandler.Handle(socketmode.EventTypeSlashCommand, middlewareSlashCommand) - socketmodeHandler.HandleSlashCommand(socketmode.EventTypeSlashCommand, middlewareSlashCommand) + socketmodeHandler.HandleSlashCommand("/rocket", middlewareSlashCommand) // socketmodeHandler.HandleDefault(middlewareDefault) From 9bc8016012b3bc9d893dc708aa48077579df7915 Mon Sep 17 00:00:00 2001 From: xNok Date: Sat, 8 May 2021 15:28:37 -0400 Subject: [PATCH 15/34] fix: initialization controller --- socketmode/socketmode_eventhandler.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 05503ddd8..a78f2cc13 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -31,6 +31,7 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) + slackCommandMap := make(map[string][]SocketmodeHandlerFunc) return &SocketmodeHandler{ Client: client, @@ -38,6 +39,7 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { EventApiMap: eventApiMap, InteractionEventMap: interactionEventMap, InteractionBlockActionEventMap: interactionBlockActionEventMap, + SlashCommandMap: slackCommandMap, Default: func(e *Event, c *Client) { log.Printf("Unexpected event type received: %v\n", e.Type) }, From 9163026b47e51e57dabfee3ce0709e18c947acdf Mon Sep 17 00:00:00 2001 From: xNok Date: Wed, 2 Jun 2021 10:45:55 -0400 Subject: [PATCH 16/34] fix: typo in example file --- .../socketmode_handler.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{sockermode_handler => socketmode_handler}/socketmode_handler.go (100%) diff --git a/examples/sockermode_handler/socketmode_handler.go b/examples/socketmode_handler/socketmode_handler.go similarity index 100% rename from examples/sockermode_handler/socketmode_handler.go rename to examples/socketmode_handler/socketmode_handler.go From dd086ec5156cfdfde852cde6c98da9dbe5721a07 Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 6 Jun 2021 17:36:55 -0400 Subject: [PATCH 17/34] refact: use client logger --- socketmode/socketmode_eventhandler.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index a78f2cc13..1b06b6361 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -2,7 +2,6 @@ package socketmode import ( "fmt" - "log" "github.com/slack-go/slack" "github.com/slack-go/slack/slackevents" @@ -41,7 +40,7 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { InteractionBlockActionEventMap: interactionBlockActionEventMap, SlashCommandMap: slackCommandMap, Default: func(e *Event, c *Client) { - log.Printf("Unexpected event type received: %v\n", e.Type) + c.log.Printf("Unexpected event type received: %v\n", e.Type) }, } } From 02281d1b3540e1468cdd4880fd49b457adbe2c0a Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 6 Jun 2021 17:56:04 -0400 Subject: [PATCH 18/34] refact: panic instead of os.Exit(1) --- examples/socketmode_handler/socketmode_handler.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/socketmode_handler/socketmode_handler.go b/examples/socketmode_handler/socketmode_handler.go index 867b53af1..28ad0d798 100644 --- a/examples/socketmode_handler/socketmode_handler.go +++ b/examples/socketmode_handler/socketmode_handler.go @@ -19,17 +19,16 @@ func main() { } if !strings.HasPrefix(appToken, "xapp-") { - fmt.Fprintf(os.Stderr, "SLACK_APP_TOKEN must have the prefix \"xapp-\".") + panic("SLACK_APP_TOKEN must have the prefix \"xapp-\".") } botToken := os.Getenv("SLACK_BOT_TOKEN") if botToken == "" { - fmt.Fprintf(os.Stderr, "SLACK_BOT_TOKEN must be set.\n") - os.Exit(1) + panic("SLACK_BOT_TOKEN must be set.\n") } if !strings.HasPrefix(botToken, "xoxb-") { - fmt.Fprintf(os.Stderr, "SLACK_BOT_TOKEN must have the prefix \"xoxb-\".") + panic("SLACK_BOT_TOKEN must have the prefix \"xoxb-\".") } api := slack.New( From cf9dd14c977ab6fc849328540c3a5acdd5106874 Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 6 Jun 2021 17:59:50 -0400 Subject: [PATCH 19/34] refact: rename HandleEventsAPI to HandleEvents --- examples/socketmode_handler/socketmode_handler.go | 2 +- socketmode/socketmode_eventhandler.go | 3 ++- socketmode/socketmode_eventhandler_test.go | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/socketmode_handler/socketmode_handler.go b/examples/socketmode_handler/socketmode_handler.go index 28ad0d798..3e83f7c0d 100644 --- a/examples/socketmode_handler/socketmode_handler.go +++ b/examples/socketmode_handler/socketmode_handler.go @@ -55,7 +55,7 @@ func main() { socketmodeHandler.Handle(socketmode.EventTypeEventsAPI, middlewareEventsAPI) // Handle a specific event from EventsAPI - socketmodeHandler.HandleEventsAPI(slackevents.AppMention, middlewareAppMentionEvent) + socketmodeHandler.HandleEvents(slackevents.AppMention, middlewareAppMentionEvent) //\\ EventTypeInteractive //\\ // Handle all Interactive Events diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 1b06b6361..cd5bc9112 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -25,6 +25,7 @@ type SocketmodeHandlerFunc func(*Event, *Client) // Middleware accept SocketmodeHandlerFunc, and return SocketmodeHandlerFunc type SocketmodeMiddlewareFunc func(SocketmodeHandlerFunc) SocketmodeHandlerFunc +// Initialization constructor for SocketmodeHandler func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) @@ -61,7 +62,7 @@ func (r *SocketmodeHandler) HandleInteractionBlockAction(action_id string, f Soc } // Register a middleare function to use to handle an Event (from slackevents) -func (r *SocketmodeHandler) HandleEventsAPI(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { +func (r *SocketmodeHandler) HandleEvents(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { r.EventApiMap[et] = append(r.EventApiMap[et], f) } diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go index ba3664666..b66b4872a 100644 --- a/socketmode/socketmode_eventhandler_test.go +++ b/socketmode/socketmode_eventhandler_test.go @@ -210,7 +210,7 @@ func TestSocketmodeHandler_HandleInteraction(t *testing.T) { } } -func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { +func TestSocketmodeHandler_HandleEvents(t *testing.T) { type args struct { evt Event register func(*SocketmodeHandler, chan<- string) @@ -233,7 +233,7 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { }, }, register: func(r *SocketmodeHandler, c chan<- string) { - r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) + r.HandleEvents(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) }, }, want: "github.com/slack-go/slack/socketmode.middleware_eventapi", @@ -250,7 +250,7 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { }, }, register: func(r *SocketmodeHandler, c chan<- string) { - r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) + r.HandleEvents(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) }, }, want: "github.com/slack-go/slack/socketmode.defaultmiddleware", @@ -264,7 +264,7 @@ func TestSocketmodeHandler_HandleEventsAPI(t *testing.T) { }, }, register: func(r *SocketmodeHandler, c chan<- string) { - r.HandleEventsAPI(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) + r.HandleEvents(slackevents.AppMention, testing_wrapper(c, middleware_eventapi)) }, }, want: "github.com/slack-go/slack/socketmode.defaultmiddleware", From fb69306b2233571f2166267762c622bdeb89de3c Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 6 Jun 2021 18:01:43 -0400 Subject: [PATCH 20/34] fix: rename action_id to actionID --- socketmode/socketmode_eventhandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index cd5bc9112..2af996e4e 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -57,8 +57,8 @@ func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f Socket } // Register a middleare function to use to handle an Interaction Block Action referenced by its ActionID -func (r *SocketmodeHandler) HandleInteractionBlockAction(action_id string, f SocketmodeHandlerFunc) { - r.InteractionBlockActionEventMap[action_id] = append(r.InteractionBlockActionEventMap[action_id], f) +func (r *SocketmodeHandler) HandleInteractionBlockAction(actionID string, f SocketmodeHandlerFunc) { + r.InteractionBlockActionEventMap[actionID] = append(r.InteractionBlockActionEventMap[actionID], f) } // Register a middleare function to use to handle an Event (from slackevents) From 430c14abd542bde10436e93efa38ccee9e9d7704 Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 6 Jun 2021 18:05:03 -0400 Subject: [PATCH 21/34] refact: rename EventAPIType to EventsAPIType --- slackevents/inner_events.go | 36 +++++++++++----------- slackevents/parsers.go | 2 +- socketmode/socketmode_eventhandler.go | 8 ++--- socketmode/socketmode_eventhandler_test.go | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index d52ce95c5..5e39309c8 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -319,47 +319,47 @@ func (e MessageEvent) IsEdited() bool { e.Message.Edited != nil } -type EventAPIType string +type EventsAPIType string const ( // AppMention is an Events API subscribable event - AppMention = EventAPIType("app_mention") + AppMention = EventsAPIType("app_mention") // AppHomeOpened Your Slack app home was opened - AppHomeOpened = EventAPIType("app_home_opened") + AppHomeOpened = EventsAPIType("app_home_opened") // AppUninstalled Your Slack app was uninstalled. - AppUninstalled = EventAPIType("app_uninstalled") + AppUninstalled = EventsAPIType("app_uninstalled") // ChannelCreated is sent when a new channel is created. - ChannelCreated = EventAPIType("channel_created") + ChannelCreated = EventsAPIType("channel_created") // GridMigrationFinished An enterprise grid migration has finished on this workspace. - GridMigrationFinished = EventAPIType("grid_migration_finished") + GridMigrationFinished = EventsAPIType("grid_migration_finished") // GridMigrationStarted An enterprise grid migration has started on this workspace. - GridMigrationStarted = EventAPIType("grid_migration_started") + GridMigrationStarted = EventsAPIType("grid_migration_started") // LinkShared A message was posted containing one or more links relevant to your application - LinkShared = EventAPIType("link_shared") + LinkShared = EventsAPIType("link_shared") // Message A message was posted to a channel, private channel (group), im, or mim - Message = EventAPIType("message") + Message = EventsAPIType("message") // Member Joined Channel - MemberJoinedChannel = EventAPIType("member_joined_channel") + MemberJoinedChannel = EventsAPIType("member_joined_channel") // Member Left Channel - MemberLeftChannel = EventAPIType("member_left_channel") + MemberLeftChannel = EventsAPIType("member_left_channel") // PinAdded An item was pinned to a channel - PinAdded = EventAPIType("pin_added") + PinAdded = EventsAPIType("pin_added") // PinRemoved An item was unpinned from a channel - PinRemoved = EventAPIType("pin_removed") + PinRemoved = EventsAPIType("pin_removed") // ReactionAdded An reaction was added to a message - ReactionAdded = EventAPIType("reaction_added") + ReactionAdded = EventsAPIType("reaction_added") // ReactionRemoved An reaction was removed from a message - ReactionRemoved = EventAPIType("reaction_removed") + ReactionRemoved = EventsAPIType("reaction_removed") // TokensRevoked APP's API tokes are revoked - TokensRevoked = EventAPIType("tokens_revoked") + TokensRevoked = EventsAPIType("tokens_revoked") // EmojiChanged A custom emoji has been added or changed - EmojiChanged = EventAPIType("emoji_changed") + EmojiChanged = EventsAPIType("emoji_changed") ) // EventsAPIInnerEventMapping maps INNER Event API events to their corresponding struct // implementations. The structs should be instances of the unmarshalling // target for the matching event type. -var EventsAPIInnerEventMapping = map[EventAPIType]interface{}{ +var EventsAPIInnerEventMapping = map[EventsAPIType]interface{}{ AppMention: AppMentionEvent{}, AppHomeOpened: AppHomeOpenedEvent{}, AppUninstalled: AppUninstalledEvent{}, diff --git a/slackevents/parsers.go b/slackevents/parsers.go index fab7185a8..1acd7ba77 100644 --- a/slackevents/parsers.go +++ b/slackevents/parsers.go @@ -19,7 +19,7 @@ func eventsMap(t string) (interface{}, bool) { // Must parse EventsAPI FIRST as both RTM and EventsAPI // have a type: "Message" event. // TODO: Handle these cases more explicitly. - v, exists := EventsAPIInnerEventMapping[EventAPIType(t)] + v, exists := EventsAPIInnerEventMapping[EventsAPIType(t)] if exists { return v, exists } diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 2af996e4e..b5698a191 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -13,7 +13,7 @@ type SocketmodeHandler struct { EventMap map[EventType][]SocketmodeHandlerFunc InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc InteractionBlockActionEventMap map[string][]SocketmodeHandlerFunc - EventApiMap map[slackevents.EventAPIType][]SocketmodeHandlerFunc + EventApiMap map[slackevents.EventsAPIType][]SocketmodeHandlerFunc SlashCommandMap map[string][]SocketmodeHandlerFunc Default SocketmodeHandlerFunc @@ -30,7 +30,7 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) - eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) + eventApiMap := make(map[slackevents.EventsAPIType][]SocketmodeHandlerFunc) slackCommandMap := make(map[string][]SocketmodeHandlerFunc) return &SocketmodeHandler{ @@ -62,7 +62,7 @@ func (r *SocketmodeHandler) HandleInteractionBlockAction(actionID string, f Sock } // Register a middleare function to use to handle an Event (from slackevents) -func (r *SocketmodeHandler) HandleEvents(et slackevents.EventAPIType, f SocketmodeHandlerFunc) { +func (r *SocketmodeHandler) HandleEvents(et slackevents.EventsAPIType, f SocketmodeHandlerFunc) { r.EventApiMap[et] = append(r.EventApiMap[et], f) } @@ -173,7 +173,7 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { return false } - innerEventType := slackevents.EventAPIType(eventsAPIEvent.InnerEvent.Type) + innerEventType := slackevents.EventsAPIType(eventsAPIEvent.InnerEvent.Type) // Level 1 - socketmode EventType ishandled = r.socketmodeDispatcher(evt) diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_eventhandler_test.go index b66b4872a..eaba03acf 100644 --- a/socketmode/socketmode_eventhandler_test.go +++ b/socketmode/socketmode_eventhandler_test.go @@ -12,7 +12,7 @@ import ( func init_SocketmodeHandler() *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) - eventApiMap := make(map[slackevents.EventAPIType][]SocketmodeHandlerFunc) + eventApiMap := make(map[slackevents.EventsAPIType][]SocketmodeHandlerFunc) interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) slashCommandMap := make(map[string][]SocketmodeHandlerFunc) From 670527ffcdf26bc974b1d585b75a23fb82e816f3 Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 21:49:10 -0400 Subject: [PATCH 22/34] refact: update godoc --- socketmode/socketmode_eventhandler.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index b5698a191..2e3511897 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -46,31 +46,38 @@ func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { } } -// Register a middleare function to use to handle an Event (from socketmode) +// Register a middleware or handler for an Event from socketmode +// This most general entrypoint func (r *SocketmodeHandler) Handle(et EventType, f SocketmodeHandlerFunc) { r.EventMap[et] = append(r.EventMap[et], f) } -// Register a middleare function to use to handle an Interaction +// Register a middleware or handler for an Interaction +// There is several types of interactions, decated functions lets you better handle them +// See +// * HandleInteractionBlockAction +// * (Not Implemented) HandleShortcut +// * (Not Implemented) HandleView func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f SocketmodeHandlerFunc) { r.InteractionEventMap[et] = append(r.InteractionEventMap[et], f) } -// Register a middleare function to use to handle an Interaction Block Action referenced by its ActionID +// Register a middleware or handler for a Block Action referenced by its ActionID func (r *SocketmodeHandler) HandleInteractionBlockAction(actionID string, f SocketmodeHandlerFunc) { r.InteractionBlockActionEventMap[actionID] = append(r.InteractionBlockActionEventMap[actionID], f) } -// Register a middleare function to use to handle an Event (from slackevents) +// Register a middleware or handler for an Event (from slackevents) func (r *SocketmodeHandler) HandleEvents(et slackevents.EventsAPIType, f SocketmodeHandlerFunc) { r.EventApiMap[et] = append(r.EventApiMap[et], f) } +// Register a middleware or handler for a Slash Command func (r *SocketmodeHandler) HandleSlashCommand(command string, f SocketmodeHandlerFunc) { r.SlashCommandMap[command] = append(r.SlashCommandMap[command], f) } -// Register a middleare function to use as a last resort +// Register a middleware or handler to use as a last resort func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) { r.Default = f } @@ -83,12 +90,14 @@ func (r *SocketmodeHandler) RunEventLoop() { r.Client.Run() } +// Call the dispatcher for each incomming event func (r *SocketmodeHandler) runEventLoop() { for evt := range r.Client.Events { r.dispatcher(evt) } } +// Dispatch events to the specialized dispatcher func (r *SocketmodeHandler) dispatcher(evt Event) { var ishandled bool From 296bd7ecfcc181991f7d3c7e1903d5fb5d973c99 Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 21:51:02 -0400 Subject: [PATCH 23/34] refact: change the type of socketmode.apiClient from a non-pointer to a pointer --- socketmode/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socketmode/client.go b/socketmode/client.go index bf611de6a..aaee1e3a9 100644 --- a/socketmode/client.go +++ b/socketmode/client.go @@ -44,7 +44,7 @@ type SocketModeMessagePayload struct { // Client's New() and call Run() to start it. Please see examples/socketmode for the usage. type Client struct { // Client is the main API, embedded - apiClient slack.Client + apiClient *slack.Client // maxPingInterval is the maximum duration elapsed after the last WebSocket PING sent from Slack // until Client considers the WebSocket connection is dead and needs to be reopened. @@ -64,5 +64,5 @@ type Client struct { // return a pointer to the slack API client func (c Client) GetApiClient() *slack.Client { - return &c.apiClient + return c.apiClient } From 9bd9782050094ba46b65bccf976b2f6d34a8f443 Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 21:52:08 -0400 Subject: [PATCH 24/34] refact: RunEventLoop return errors that occured --- socketmode/socketmode_eventhandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_eventhandler.go index 2e3511897..c2886301f 100644 --- a/socketmode/socketmode_eventhandler.go +++ b/socketmode/socketmode_eventhandler.go @@ -83,11 +83,11 @@ func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) { } // RunSlackEventLoop receives the event via the socket -func (r *SocketmodeHandler) RunEventLoop() { +func (r *SocketmodeHandler) RunEventLoop() error { go r.runEventLoop() - r.Client.Run() + return r.Client.Run() } // Call the dispatcher for each incomming event From 20e09d5fc4be9dbe25f5e1ba0de412ecc52855dd Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 21:53:56 -0400 Subject: [PATCH 25/34] refact: rename file to socketmode_handler --- socketmode/{socketmode_eventhandler.go => socketmode_handler.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename socketmode/{socketmode_eventhandler.go => socketmode_handler.go} (100%) diff --git a/socketmode/socketmode_eventhandler.go b/socketmode/socketmode_handler.go similarity index 100% rename from socketmode/socketmode_eventhandler.go rename to socketmode/socketmode_handler.go From 01e7385c2cb39bbb93ad3290be5ad1d8da95a6bc Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 21:59:18 -0400 Subject: [PATCH 26/34] refact: use socketmode client's logger. --- socketmode/socketmode_handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socketmode/socketmode_handler.go b/socketmode/socketmode_handler.go index c2886301f..588f41e77 100644 --- a/socketmode/socketmode_handler.go +++ b/socketmode/socketmode_handler.go @@ -138,7 +138,7 @@ func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { interaction, ok := evt.Data.(slack.InteractionCallback) if !ok { - fmt.Printf("Ignored %+v\n", evt) + r.Client.log.Printf("Ignored %+v\n", evt) return false } @@ -178,7 +178,7 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { var ishandled bool = false eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { - fmt.Printf("Ignored %+v\n", evt) + r.Client.log.Printf("Ignored %+v\n", evt) return false } From c965498de17851bdb992138e60cde4509a21d0ae Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 21:59:39 -0400 Subject: [PATCH 27/34] refact: rename block_actions to blockActions --- socketmode/socketmode_handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socketmode/socketmode_handler.go b/socketmode/socketmode_handler.go index 588f41e77..3df6c50ed 100644 --- a/socketmode/socketmode_handler.go +++ b/socketmode/socketmode_handler.go @@ -156,11 +156,11 @@ func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { } // Level 3 - interaction with actionID - block_actions := interaction.ActionCallback.BlockActions + blockActions := interaction.ActionCallback.BlockActions // outmoded approach won`t be implemented // attachments_actions := interaction.ActionCallback.AttachmentActions - for _, action := range block_actions { + for _, action := range blockActions { if handlers, ok := r.InteractionBlockActionEventMap[action.ActionID]; ok { for _, f := range handlers { From 0273c6db3b04b26884a2670f02c9841df5285a9f Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 22:13:19 -0400 Subject: [PATCH 28/34] fix: side effect --- socketmode/socketmode.go | 2 +- socketmode/socketmode_handler.go | 5 +++-- ...tmode_eventhandler_test.go => socketmode_handler_test.go} | 0 3 files changed, 4 insertions(+), 3 deletions(-) rename socketmode/{socketmode_eventhandler_test.go => socketmode_handler_test.go} (100%) diff --git a/socketmode/socketmode.go b/socketmode/socketmode.go index 752be8cdf..54f4d69d8 100644 --- a/socketmode/socketmode.go +++ b/socketmode/socketmode.go @@ -106,7 +106,7 @@ func OptionLog(l logger) func(*Client) { // Slack's Websocket-based Socket Mode. func New(api *slack.Client, options ...Option) *Client { result := &Client{ - apiClient: *api, + apiClient: api, Events: make(chan Event, 50), socketModeResponses: make(chan *Response, 20), maxPingInterval: defaultMaxPingInterval, diff --git a/socketmode/socketmode_handler.go b/socketmode/socketmode_handler.go index 3df6c50ed..ab5170a0a 100644 --- a/socketmode/socketmode_handler.go +++ b/socketmode/socketmode_handler.go @@ -2,6 +2,7 @@ package socketmode import ( "fmt" + "log" "github.com/slack-go/slack" "github.com/slack-go/slack/slackevents" @@ -138,7 +139,7 @@ func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { interaction, ok := evt.Data.(slack.InteractionCallback) if !ok { - r.Client.log.Printf("Ignored %+v\n", evt) + log.Printf("Ignored %+v\n", evt) return false } @@ -178,7 +179,7 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { var ishandled bool = false eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { - r.Client.log.Printf("Ignored %+v\n", evt) + log.Printf("Ignored %+v\n", evt) return false } diff --git a/socketmode/socketmode_eventhandler_test.go b/socketmode/socketmode_handler_test.go similarity index 100% rename from socketmode/socketmode_eventhandler_test.go rename to socketmode/socketmode_handler_test.go From 7a1949e21e48c4016c8b229431a0a8d601650909 Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 22:21:52 -0400 Subject: [PATCH 29/34] refact: use client logger --- socketmode/socketmode_handler.go | 9 +++------ socketmode/socketmode_handler_test.go | 6 +++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/socketmode/socketmode_handler.go b/socketmode/socketmode_handler.go index ab5170a0a..0e72f92bb 100644 --- a/socketmode/socketmode_handler.go +++ b/socketmode/socketmode_handler.go @@ -1,9 +1,6 @@ package socketmode import ( - "fmt" - "log" - "github.com/slack-go/slack" "github.com/slack-go/slack/slackevents" ) @@ -139,7 +136,7 @@ func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { interaction, ok := evt.Data.(slack.InteractionCallback) if !ok { - log.Printf("Ignored %+v\n", evt) + r.Client.log.Printf("Ignored %+v\n", evt) return false } @@ -179,7 +176,7 @@ func (r *SocketmodeHandler) eventAPIDispatcher(evt *Event) bool { var ishandled bool = false eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) if !ok { - log.Printf("Ignored %+v\n", evt) + r.Client.log.Printf("Ignored %+v\n", evt) return false } @@ -206,7 +203,7 @@ func (r *SocketmodeHandler) slashCommandDispatcher(evt *Event) bool { var ishandled bool = false slashCommandEvent, ok := evt.Data.(slack.SlashCommand) if !ok { - fmt.Printf("Ignored %+v\n", evt) + r.Client.log.Printf("Ignored %+v\n", evt) return false } diff --git a/socketmode/socketmode_handler_test.go b/socketmode/socketmode_handler_test.go index eaba03acf..5b354745b 100644 --- a/socketmode/socketmode_handler_test.go +++ b/socketmode/socketmode_handler_test.go @@ -1,6 +1,8 @@ package socketmode import ( + "log" + "os" "reflect" "runtime" "testing" @@ -17,7 +19,9 @@ func init_SocketmodeHandler() *SocketmodeHandler { slashCommandMap := make(map[string][]SocketmodeHandlerFunc) return &SocketmodeHandler{ - Client: &Client{}, + Client: &Client{ + log: log.New(os.Stderr, "slack-go/slack/socketmode", log.LstdFlags|log.Lshortfile), + }, EventMap: eventMap, EventApiMap: eventApiMap, InteractionEventMap: interactioneventMap, From 38427ff91e1772c36ac7412db0728bd97e8a766c Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 7 Jun 2021 22:59:44 -0400 Subject: [PATCH 30/34] refact: must register unique action_id or slash commands --- socketmode/socketmode_handler.go | 54 ++++++++++++------- socketmode/socketmode_handler_test.go | 74 ++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 20 deletions(-) diff --git a/socketmode/socketmode_handler.go b/socketmode/socketmode_handler.go index 0e72f92bb..99a94a17e 100644 --- a/socketmode/socketmode_handler.go +++ b/socketmode/socketmode_handler.go @@ -8,11 +8,14 @@ import ( type SocketmodeHandler struct { Client *Client - EventMap map[EventType][]SocketmodeHandlerFunc - InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc - InteractionBlockActionEventMap map[string][]SocketmodeHandlerFunc - EventApiMap map[slackevents.EventsAPIType][]SocketmodeHandlerFunc - SlashCommandMap map[string][]SocketmodeHandlerFunc + //lvl 1 - the most generic type of event + EventMap map[EventType][]SocketmodeHandlerFunc + //lvl 2 - Manage event by inner type + InteractionEventMap map[slack.InteractionType][]SocketmodeHandlerFunc + EventApiMap map[slackevents.EventsAPIType][]SocketmodeHandlerFunc + //lvl 3 - the most userfriendly way of managing event + InteractionBlockActionEventMap map[string]SocketmodeHandlerFunc + SlashCommandMap map[string]SocketmodeHandlerFunc Default SocketmodeHandlerFunc } @@ -27,9 +30,10 @@ type SocketmodeMiddlewareFunc func(SocketmodeHandlerFunc) SocketmodeHandlerFunc func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) - interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventsAPIType][]SocketmodeHandlerFunc) - slackCommandMap := make(map[string][]SocketmodeHandlerFunc) + + interactionBlockActionEventMap := make(map[string]SocketmodeHandlerFunc) + slackCommandMap := make(map[string]SocketmodeHandlerFunc) return &SocketmodeHandler{ Client: client, @@ -62,7 +66,16 @@ func (r *SocketmodeHandler) HandleInteraction(et slack.InteractionType, f Socket // Register a middleware or handler for a Block Action referenced by its ActionID func (r *SocketmodeHandler) HandleInteractionBlockAction(actionID string, f SocketmodeHandlerFunc) { - r.InteractionBlockActionEventMap[actionID] = append(r.InteractionBlockActionEventMap[actionID], f) + if actionID == "" { + panic("invalid command cannot be empty") + } + if f == nil { + panic("invalid handler cannot be nil") + } + if _, exist := r.InteractionBlockActionEventMap[actionID]; exist { + panic("multiple registrations for actionID" + actionID) + } + r.InteractionBlockActionEventMap[actionID] = f } // Register a middleware or handler for an Event (from slackevents) @@ -72,7 +85,16 @@ func (r *SocketmodeHandler) HandleEvents(et slackevents.EventsAPIType, f Socketm // Register a middleware or handler for a Slash Command func (r *SocketmodeHandler) HandleSlashCommand(command string, f SocketmodeHandlerFunc) { - r.SlashCommandMap[command] = append(r.SlashCommandMap[command], f) + if command == "" { + panic("invalid command cannot be empty") + } + if f == nil { + panic("invalid handler cannot be nil") + } + if _, exist := r.SlashCommandMap[command]; exist { + panic("multiple registrations for command" + command) + } + r.SlashCommandMap[command] = f } // Register a middleware or handler to use as a last resort @@ -159,11 +181,9 @@ func (r *SocketmodeHandler) interactionDispatcher(evt *Event) bool { // attachments_actions := interaction.ActionCallback.AttachmentActions for _, action := range blockActions { - if handlers, ok := r.InteractionBlockActionEventMap[action.ActionID]; ok { + if handler, ok := r.InteractionBlockActionEventMap[action.ActionID]; ok { - for _, f := range handlers { - go f(evt, r.Client) - } + go handler(evt, r.Client) ishandled = true } @@ -211,11 +231,9 @@ func (r *SocketmodeHandler) slashCommandDispatcher(evt *Event) bool { ishandled = r.socketmodeDispatcher(evt) // Level 2 - SlackCommand by name - if handlers, ok := r.SlashCommandMap[slashCommandEvent.Command]; ok { - // If we registered an event - for _, f := range handlers { - go f(evt, r.Client) - } + if handler, ok := r.SlashCommandMap[slashCommandEvent.Command]; ok { + + go handler(evt, r.Client) ishandled = true } diff --git a/socketmode/socketmode_handler_test.go b/socketmode/socketmode_handler_test.go index 5b354745b..623e15ef9 100644 --- a/socketmode/socketmode_handler_test.go +++ b/socketmode/socketmode_handler_test.go @@ -15,8 +15,8 @@ func init_SocketmodeHandler() *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactioneventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventsAPIType][]SocketmodeHandlerFunc) - interactionBlockActionEventMap := make(map[string][]SocketmodeHandlerFunc) - slashCommandMap := make(map[string][]SocketmodeHandlerFunc) + interactionBlockActionEventMap := make(map[string]SocketmodeHandlerFunc) + slashCommandMap := make(map[string]SocketmodeHandlerFunc) return &SocketmodeHandler{ Client: &Client{ @@ -443,3 +443,73 @@ func TestSocketmodeHandler_HandleSlashCommand(t *testing.T) { }) } } + +func TestSocketmodeHandler_Handle_errors(t *testing.T) { + type args struct { + register func(*SocketmodeHandler, chan<- string) + } + tests := []struct { + name string + args args + }{ + { + name: "Attempt to register empty command", + args: args{ + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleSlashCommand("", testing_wrapper(c, middleware_slach_command)) + }, + }, + }, { + name: "Attempt to register nil handler", + args: args{ + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleSlashCommand("/command", nil) + }, + }, + }, { + name: "Attempt to register duplicate command", + args: args{ + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleSlashCommand("/command", testing_wrapper(c, middleware_slach_command)) + r.HandleSlashCommand("/command", testing_wrapper(c, middleware_slach_command)) + }, + }, + }, { + name: "Attempt to register empty Block ActionID", + args: args{ + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteractionBlockAction("", testing_wrapper(c, middleware_interaction_block_action)) + }, + }, + }, { + name: "Attempt to register nil handler", + args: args{ + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteractionBlockAction("action_id", nil) + }, + }, + }, { + name: "Attempt to register duplicate Block ActionID", + args: args{ + register: func(r *SocketmodeHandler, c chan<- string) { + r.HandleInteractionBlockAction("action_id", testing_wrapper(c, middleware_interaction_block_action)) + r.HandleInteractionBlockAction("action_id", testing_wrapper(c, middleware_interaction_block_action)) + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := init_SocketmodeHandler() + + c := make(chan string) + + defer func() { recover() }() + + tt.args.register(r, c) + + t.Errorf("should have panicked") + + }) + } +} From c062aee4939376788d484c62d7f5483d9f129c08 Mon Sep 17 00:00:00 2001 From: xNok Date: Sun, 11 Jul 2021 16:24:48 -0400 Subject: [PATCH 31/34] fix: typo --- examples/socketmode_handler/socketmode_handler.go | 2 +- socketmode/socketmode_handler.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/socketmode_handler/socketmode_handler.go b/examples/socketmode_handler/socketmode_handler.go index 3e83f7c0d..9ec8a3006 100644 --- a/examples/socketmode_handler/socketmode_handler.go +++ b/examples/socketmode_handler/socketmode_handler.go @@ -44,7 +44,7 @@ func main() { socketmode.OptionLog(log.New(os.Stdout, "socketmode: ", log.Lshortfile|log.LstdFlags)), ) - socketmodeHandler := socketmode.NewsSocketmodeHandler(client) + socketmodeHandler := socketmode.NewSocketmodeHandler(client) socketmodeHandler.Handle(socketmode.EventTypeConnecting, middlewareConnecting) socketmodeHandler.Handle(socketmode.EventTypeConnectionError, middlewareConnectionError) diff --git a/socketmode/socketmode_handler.go b/socketmode/socketmode_handler.go index 99a94a17e..636feefa5 100644 --- a/socketmode/socketmode_handler.go +++ b/socketmode/socketmode_handler.go @@ -27,7 +27,7 @@ type SocketmodeHandlerFunc func(*Event, *Client) type SocketmodeMiddlewareFunc func(SocketmodeHandlerFunc) SocketmodeHandlerFunc // Initialization constructor for SocketmodeHandler -func NewsSocketmodeHandler(client *Client) *SocketmodeHandler { +func NewSocketmodeHandler(client *Client) *SocketmodeHandler { eventMap := make(map[EventType][]SocketmodeHandlerFunc) interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc) eventApiMap := make(map[slackevents.EventsAPIType][]SocketmodeHandlerFunc) From 2135a67cf8b3d2762233aedbfdd3d74e0f8fe1b1 Mon Sep 17 00:00:00 2001 From: xNok Date: Mon, 1 Nov 2021 23:48:40 +0100 Subject: [PATCH 32/34] fix: compatibility with access to Slack client in socketmode --- examples/socketmode_handler/socketmode_handler.go | 4 ++-- socketmode/client.go | 5 ----- socketmode/socketmode.go | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/examples/socketmode_handler/socketmode_handler.go b/examples/socketmode_handler/socketmode_handler.go index 9ec8a3006..9991e8c3c 100644 --- a/examples/socketmode_handler/socketmode_handler.go +++ b/examples/socketmode_handler/socketmode_handler.go @@ -103,7 +103,7 @@ func middlewareEventsAPI(evt *socketmode.Event, client *socketmode.Client) { switch ev := innerEvent.Data.(type) { case *slackevents.AppMentionEvent: fmt.Printf("We have been mentionned in %v", ev.Channel) - _, _, err := client.GetApiClient().PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) + _, _, err := client.Client.PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) if err != nil { fmt.Printf("failed posting message: %v", err) } @@ -132,7 +132,7 @@ func middlewareAppMentionEvent(evt *socketmode.Event, client *socketmode.Client) } fmt.Printf("We have been mentionned in %v\n", ev.Channel) - _, _, err := client.GetApiClient().PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) + _, _, err := client.Client.PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false)) if err != nil { fmt.Printf("failed posting message: %v", err) } diff --git a/socketmode/client.go b/socketmode/client.go index 8c4f4821a..8fd0b9871 100644 --- a/socketmode/client.go +++ b/socketmode/client.go @@ -61,8 +61,3 @@ type Client struct { debug bool log ilogger } - -// return a pointer to the slack API client -func (c Client) GetApiClient() *slack.Client { - return c.apiClient -} diff --git a/socketmode/socketmode.go b/socketmode/socketmode.go index d1acdbd57..d91d422b1 100644 --- a/socketmode/socketmode.go +++ b/socketmode/socketmode.go @@ -106,7 +106,6 @@ func OptionLog(l logger) func(*Client) { // Slack's Websocket-based Socket Mode. func New(api *slack.Client, options ...Option) *Client { result := &Client{ - apiClient: api, Client: *api, Events: make(chan Event, 50), socketModeResponses: make(chan *Response, 20), From d06a99bd3ca6598a11c0dfa43596f997c339b0de Mon Sep 17 00:00:00 2001 From: xNok Date: Tue, 9 Nov 2021 00:40:40 +0100 Subject: [PATCH 33/34] doc: socketmode handler --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index dbf73d4ed..5203cf4ad 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,13 @@ See https://github.com/slack-go/slack/blob/master/examples/websocket/websocket.g See https://github.com/slack-go/slack/blob/master/examples/eventsapi/events.go +## Socketmode Event Handler (Experimental) +When using socket mode, dealing with an event can be pretty lengthy as it requires you to route the event to the right place. + +Instead, you can use `SocketmodeHandler` much like you use an HTTP handler to register which event you would like to listen to and what callback function will process that event when it occurs. + +See [./examples/socketmode_handler/socketmode_handler.go](./examples/socketmode_handler/socketmode_handler.go) ## Contributing You are more than welcome to contribute to this project. Fork and From b11c9d15de1bc1745ab64705c9f36f60767951c9 Mon Sep 17 00:00:00 2001 From: xNok Date: Tue, 9 Nov 2021 21:05:07 +0100 Subject: [PATCH 34/34] fix: trailing whitespace --- slackevents/inner_events.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 9b8d0e142..d005c84a4 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -419,29 +419,29 @@ const ( // AppUninstalled Your Slack app was uninstalled. AppUninstalled = EventsAPIType("app_uninstalled") // ChannelCreated is sent when a new channel is created. - ChannelCreated = EventsAPIType("channel_created") + ChannelCreated = EventsAPIType("channel_created") // ChannelDeleted is sent when a channel is deleted. - ChannelDeleted = EventsAPIType("channel_deleted") + ChannelDeleted = EventsAPIType("channel_deleted") // ChannelArchive is sent when a channel is archived. - ChannelArchive = EventsAPIType("channel_archive") + ChannelArchive = EventsAPIType("channel_archive") // ChannelUnarchive is sent when a channel is unarchived. - ChannelUnarchive = EventsAPIType("channel_unarchive") + ChannelUnarchive = EventsAPIType("channel_unarchive") // ChannelLeft is sent when a channel is left. - ChannelLeft = EventsAPIType("channel_left") + ChannelLeft = EventsAPIType("channel_left") // ChannelRename is sent when a channel is rename. - ChannelRename = EventsAPIType("channel_rename") + ChannelRename = EventsAPIType("channel_rename") // ChannelIDChanged is sent when a channel identifier is changed. ChannelIDChanged = "channel_id_changed" // GroupDeleted is sent when a group is deleted. - GroupDeleted = EventsAPIType("group_deleted") + GroupDeleted = EventsAPIType("group_deleted") // GroupArchive is sent when a group is archived. - GroupArchive = EventsAPIType("group_archive") + GroupArchive = EventsAPIType("group_archive") // GroupUnarchive is sent when a group is unarchived. - GroupUnarchive = EventsAPIType("group_unarchive") + GroupUnarchive = EventsAPIType("group_unarchive") // GroupLeft is sent when a group is left. - GroupLeft = EventsAPIType("group_left") + GroupLeft = EventsAPIType("group_left") // GroupRename is sent when a group is renamed. - GroupRename = EventsAPIType("group_rename") + GroupRename = EventsAPIType("group_rename") // GridMigrationFinished An enterprise grid migration has finished on this workspace. GridMigrationFinished = EventsAPIType("grid_migration_finished") // GridMigrationStarted An enterprise grid migration has started on this workspace. @@ -463,7 +463,7 @@ const ( // ReactionRemoved An reaction was removed from a message ReactionRemoved = EventsAPIType("reaction_removed") // TeamJoin A new user joined the workspace - TeamJoin = EventsAPIType("team_join") + TeamJoin = EventsAPIType("team_join") // TokensRevoked APP's API tokes are revoked TokensRevoked = EventsAPIType("tokens_revoked") // EmojiChanged A custom emoji has been added or changed