matterbridge/vendor/github.com/slack-go/slack/socketmode/socketmode_handler.go
kolsys 5b86262fdd Add support new version of Slack applocation
- Added support of Event API in socketMode
- Fix files upload for new applications
2024-07-01 15:37:09 +03:00

261 lines
7.1 KiB
Go

package socketmode
import (
"context"
"github.com/slack-go/slack"
"github.com/slack-go/slack/slackevents"
)
type SocketmodeHandler struct {
Client *Client
//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
}
// 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
// Initialization constructor for SocketmodeHandler
func NewSocketmodeHandler(client *Client) *SocketmodeHandler {
eventMap := make(map[EventType][]SocketmodeHandlerFunc)
interactionEventMap := make(map[slack.InteractionType][]SocketmodeHandlerFunc)
eventApiMap := make(map[slackevents.EventsAPIType][]SocketmodeHandlerFunc)
interactionBlockActionEventMap := make(map[string]SocketmodeHandlerFunc)
slackCommandMap := make(map[string]SocketmodeHandlerFunc)
return &SocketmodeHandler{
Client: client,
EventMap: eventMap,
EventApiMap: eventApiMap,
InteractionEventMap: interactionEventMap,
InteractionBlockActionEventMap: interactionBlockActionEventMap,
SlashCommandMap: slackCommandMap,
Default: func(e *Event, c *Client) {
c.log.Printf("Unexpected event type received: %v\n", e.Type)
},
}
}
// 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 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 middleware or handler for a Block Action referenced by its ActionID
func (r *SocketmodeHandler) HandleInteractionBlockAction(actionID string, f SocketmodeHandlerFunc) {
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)
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) {
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
func (r *SocketmodeHandler) HandleDefault(f SocketmodeHandlerFunc) {
r.Default = f
}
// RunSlackEventLoop receives the event via the socket
func (r *SocketmodeHandler) RunEventLoop() error {
go r.runEventLoop(context.Background())
return r.Client.Run()
}
func (r *SocketmodeHandler) RunEventLoopContext(ctx context.Context) error {
go r.runEventLoop(ctx)
return r.Client.RunContext(ctx)
}
// Call the dispatcher for each incomming event
func (r *SocketmodeHandler) runEventLoop(ctx context.Context) {
for {
select {
case evt, ok := <-r.Client.Events:
if !ok {
return
}
r.dispatcher(evt)
case <-ctx.Done():
return
}
}
}
// Dispatch events to the specialized dispatcher
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)
case EventTypeSlashCommand:
ishandled = r.slashCommandDispatcher(&evt)
default:
ishandled = r.socketmodeDispatcher(&evt)
}
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
}
// 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 {
r.Client.log.Printf("Ignored %+v\n", evt)
return false
}
// Level 1 - socketmode EventType
ishandled = r.socketmodeDispatcher(evt)
// 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)
}
ishandled = true
}
// Level 3 - interaction with actionID
blockActions := interaction.ActionCallback.BlockActions
// outmoded approach won`t be implemented
// attachments_actions := interaction.ActionCallback.AttachmentActions
for _, action := range blockActions {
if handler, ok := r.InteractionBlockActionEventMap[action.ActionID]; ok {
go handler(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 {
r.Client.log.Printf("Ignored %+v\n", evt)
return false
}
innerEventType := slackevents.EventsAPIType(eventsAPIEvent.InnerEvent.Type)
// Level 1 - socketmode EventType
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)
}
ishandled = true
}
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 {
r.Client.log.Printf("Ignored %+v\n", evt)
return false
}
// Level 1 - socketmode EventType
ishandled = r.socketmodeDispatcher(evt)
// Level 2 - SlackCommand by name
if handler, ok := r.SlashCommandMap[slashCommandEvent.Command]; ok {
go handler(evt, r.Client)
ishandled = true
}
return ishandled
}