gitee.com/larksuite/oapi-sdk-go/v3@v3.0.3/event/dispatcher/dispatcher.go (about) 1 /* 2 * MIT License 3 * 4 * Copyright (c) 2022 Lark Technologies Pte. Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 * 8 * The above copyright notice and this permission notice, shall be included in all copies or substantial portions of the Software. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 */ 12 13 package dispatcher 14 15 import ( 16 "context" 17 "encoding/json" 18 "errors" 19 "fmt" 20 "net/http" 21 22 larkcore "gitee.com/larksuite/oapi-sdk-go/v3/core" 23 "gitee.com/larksuite/oapi-sdk-go/v3/event" 24 ) 25 26 type EventDispatcher struct { 27 // 事件map,key为事件类型,value为事件处理器 28 eventType2EventHandler map[string]larkevent.EventHandler 29 // 事件回调签名token,消息解密key 30 verificationToken string 31 eventEncryptKey string 32 *larkcore.Config 33 } 34 35 func (dispatcher *EventDispatcher) Logger() larkcore.Logger { 36 return dispatcher.Config.Logger 37 } 38 39 func (d *EventDispatcher) InitConfig(options ...larkevent.OptionFunc) { 40 for _, option := range options { 41 option(d.Config) 42 } 43 larkcore.NewLogger(d.Config) 44 } 45 46 func NewEventDispatcher(verificationToken, eventEncryptKey string) *EventDispatcher { 47 reqDispatcher := &EventDispatcher{ 48 eventType2EventHandler: make(map[string]larkevent.EventHandler), 49 verificationToken: verificationToken, 50 eventEncryptKey: eventEncryptKey, 51 Config: &larkcore.Config{Logger: larkcore.NewEventLogger()}, 52 } 53 // 注册app_ticket事件 54 reqDispatcher.eventType2EventHandler["app_ticket"] = &appTicketEventHandler{} 55 return reqDispatcher 56 } 57 58 func processError(ctx context.Context, logger larkcore.Logger, path string, err error) *larkevent.EventResp { 59 header := map[string][]string{} 60 statusCode := http.StatusInternalServerError 61 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 62 eventResp := &larkevent.EventResp{ 63 Header: header, 64 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, err.Error())), 65 StatusCode: statusCode, 66 } 67 logger.Error(ctx, fmt.Sprintf("handle event,path:%s,err: %v", path, err)) 68 return eventResp 69 } 70 71 func recoveryResult() *larkevent.EventResp { 72 header := map[string][]string{} 73 statusCode := http.StatusInternalServerError 74 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 75 eventResp := &larkevent.EventResp{ 76 Header: header, 77 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, "Server Internal Error")), 78 StatusCode: statusCode, 79 } 80 return eventResp 81 } 82 83 func (d *EventDispatcher) Handle(ctx context.Context, req *larkevent.EventReq) (eventResp *larkevent.EventResp) { 84 defer func() { 85 if err := recover(); err != nil { 86 d.Config.Logger.Error(ctx, fmt.Sprintf("handle event,path:%s, error:%v", req.RequestURI, err)) 87 eventResp = recoveryResult() 88 } 89 }() 90 91 cipherEventJsonStr, err := d.ParseReq(ctx, req) 92 if err != nil { 93 return processError(ctx, d.Config.Logger, req.RequestURI, err) 94 } 95 96 plainEventJsonStr, err := d.DecryptEvent(ctx, cipherEventJsonStr) 97 if err != nil { 98 return processError(ctx, d.Config.Logger, req.RequestURI, err) 99 } 100 101 reqType, challenge, token, eventType, err := parse(plainEventJsonStr) 102 if err != nil { 103 return processError(ctx, d.Config.Logger, req.RequestURI, err) 104 } 105 if reqType != larkevent.ReqTypeChallenge { 106 err = d.VerifySign(ctx, req) 107 if err != nil { 108 return processError(ctx, d.Config.Logger, req.RequestURI, err) 109 } 110 } 111 112 result, err := d.DoHandle(ctx, reqType, eventType, challenge, token, plainEventJsonStr, req.RequestURI, req) 113 if err != nil { 114 return processError(ctx, d.Config.Logger, req.RequestURI, err) 115 } 116 return result 117 } 118 119 func (d *EventDispatcher) ParseReq(ctx context.Context, req *larkevent.EventReq) (string, error) { 120 d.Config.Logger.Debug(ctx, fmt.Sprintf("event request: header:%v,body:%s", req.Header, string(req.Body))) 121 if d.eventEncryptKey != "" { 122 var encrypt larkevent.EventEncryptMsg 123 err := json.Unmarshal(req.Body, &encrypt) 124 if err != nil { 125 err = fmt.Errorf("event message unmarshal failed:%v", err) 126 return "", err 127 } 128 if encrypt.Encrypt == "" { 129 err = fmt.Errorf("event unmarshal failed,%s", "encrypted message is blank") 130 return "", err 131 } 132 return encrypt.Encrypt, nil 133 } 134 return string(req.Body), nil 135 } 136 137 func (d *EventDispatcher) DecryptEvent(ctx context.Context, cipherEventJsonStr string) (str string, er error) { 138 if d.eventEncryptKey != "" { 139 body, err := larkevent.EventDecrypt(cipherEventJsonStr, d.eventEncryptKey) 140 if err != nil { 141 err = fmt.Errorf("event message decryption failed:%v", err) 142 return "", err 143 } 144 return string(body), nil 145 } 146 return cipherEventJsonStr, nil 147 } 148 149 func (d *EventDispatcher) VerifySign(ctx context.Context, req *larkevent.EventReq) error { 150 if d.eventEncryptKey == "" { 151 return nil 152 } 153 // 解析签名头 154 requestTimestamps := req.Header[larkevent.EventRequestTimestamp] 155 requestNonces := req.Header[larkevent.EventRequestNonce] 156 var requestTimestamp = "" 157 var requestNonce = "" 158 if len(requestTimestamps) > 0 { 159 requestTimestamp = requestTimestamps[0] 160 } 161 if len(requestNonces) > 0 { 162 requestNonce = requestNonces[0] 163 } 164 // 执行sha256签名计算 165 targetSign := larkevent.Signature(requestTimestamp, requestNonce, 166 d.eventEncryptKey, string(req.Body)) 167 168 sourceSigns := req.Header[larkevent.EventSignature] 169 var sourceSign = "" 170 if len(sourceSigns) > 0 { 171 sourceSign = sourceSigns[0] 172 } 173 174 // 验签 175 if targetSign == sourceSign { 176 return nil 177 } 178 return errors.New("the result of signature verification failed") 179 } 180 181 func parse(plainEventJsonStr string) (larkevent.ReqType, string, string, string, error) { 182 fuzzy := &larkevent.EventFuzzy{} 183 err := json.Unmarshal([]byte(plainEventJsonStr), fuzzy) 184 if err != nil { 185 err = fmt.Errorf("event json unmarshal, err: %v", err) 186 return "", "", "", "", err 187 } 188 if fuzzy.Encrypt != "" { 189 err = errors.New("event data is encrypted, Need to set up the `EncryptKey` for your APP") 190 return "", "", "", "", err 191 } 192 193 reqType := larkevent.ReqType(fuzzy.Type) 194 var eventType string 195 token := fuzzy.Token 196 challenge := fuzzy.Challenge 197 if fuzzy.Event != nil { 198 if et, ok := fuzzy.Event.Type.(string); ok { 199 eventType = et 200 } 201 } 202 if fuzzy.Header != nil { 203 token = fuzzy.Header.Token 204 eventType = fuzzy.Header.EventType 205 } 206 207 return reqType, challenge, token, eventType, nil 208 } 209 210 func (d *EventDispatcher) getErrorResp(ctx context.Context, header map[string][]string, err error) *larkevent.EventResp { 211 eventResp := &larkevent.EventResp{ 212 Header: header, 213 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, err.Error())), 214 StatusCode: http.StatusInternalServerError, 215 } 216 d.Config.Logger.Error(ctx, fmt.Sprintf("event handle err: %v", err)) 217 return eventResp 218 } 219 220 func (d *EventDispatcher) AuthByChallenge(ctx context.Context, reqType larkevent.ReqType, challenge, token string) (*larkevent.EventResp, error) { 221 if reqType == larkevent.ReqTypeChallenge { 222 if token != d.verificationToken { 223 err := errors.New("the result of auth by challenge failed") 224 return nil, err 225 } 226 227 header := map[string][]string{} 228 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 229 eventResp := larkevent.EventResp{ 230 Header: header, 231 Body: []byte(fmt.Sprintf(larkevent.ChallengeResponseFormat, challenge)), 232 StatusCode: http.StatusOK, 233 } 234 d.Config.Logger.Info(ctx, fmt.Sprintf("AuthByChallenge Success")) 235 return &eventResp, nil 236 } 237 return nil, nil 238 } 239 240 func (d *EventDispatcher) DoHandle(ctx context.Context, reqType larkevent.ReqType, eventType, challenge, token, 241 plainEventJsonStr string, path string, req *larkevent.EventReq) (*larkevent.EventResp, error) { 242 // auth by challenge 243 resp, err := d.AuthByChallenge(ctx, reqType, challenge, token) 244 if err != nil { 245 return nil, err 246 } 247 if resp != nil { 248 return resp, nil 249 } 250 251 // 查找处理器 252 handler := d.eventType2EventHandler[eventType] 253 if handler == nil { 254 err = ¬FoundEventHandlerErr{eventType: eventType} 255 header := map[string][]string{} 256 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 257 eventResp := &larkevent.EventResp{ 258 Header: header, 259 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, err.Error())), 260 StatusCode: http.StatusOK, 261 } 262 d.Config.Logger.Error(ctx, fmt.Sprintf("handle event,path:%s,error:%v", path, err.Error())) 263 return eventResp, nil 264 } 265 266 // 反序列化 267 eventMsg := handler.Event() 268 if _, ok := handler.(*defaultHandler); !ok { 269 err = json.Unmarshal([]byte(plainEventJsonStr), eventMsg) 270 if err != nil { 271 return nil, err 272 } 273 } else { 274 eventMsg = req 275 } 276 277 if msg, ok := eventMsg.(larkevent.EventHandlerModel); ok { 278 msg.RawReq(req) 279 } 280 281 // 执行处理器 282 err = handler.Handle(ctx, eventMsg) 283 if err != nil { 284 return nil, err 285 } 286 287 //返回结果 288 header := map[string][]string{} 289 header[larkevent.ContentTypeHeader] = []string{larkevent.DefaultContentType} 290 eventResp := &larkevent.EventResp{ 291 Header: header, 292 Body: []byte(fmt.Sprintf(larkevent.WebhookResponseFormat, "success")), 293 StatusCode: http.StatusOK, 294 } 295 return eventResp, nil 296 } 297 298 type notFoundEventHandlerErr struct { 299 eventType string 300 } 301 302 func (e notFoundEventHandlerErr) Error() string { 303 return fmt.Sprintf("event type: %s, not found handler", e.eventType) 304 } 305 306 type defaultHandler struct { 307 handler func(context.Context, *larkevent.EventReq) error 308 } 309 310 func (h *defaultHandler) Event() interface{} { 311 return nil 312 } 313 314 func (h *defaultHandler) Handle(ctx context.Context, event interface{}) error { 315 return h.handler(ctx, event.(*larkevent.EventReq)) 316 }