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 = &notFoundEventHandlerErr{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  }