1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
|
package socketmode
import (
"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()
return 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
// 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
}
|