Skip to content

Commit

Permalink
feat: support card callback
Browse files Browse the repository at this point in the history
  • Loading branch information
crispgm committed Jan 2, 2024
1 parent 41ac45d commit 1c35b0f
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 18 deletions.
60 changes: 45 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
[![build](https://github.com/go-lark/lark-gin/actions/workflows/ci.yml/badge.svg)](https://github.com/go-lark/lark-gin/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/go-lark/lark-gin/branch/main/graph/badge.svg?token=MQL8MFPF2Q)](https://codecov.io/gh/go-lark/lark-gin)

Gin Middleware for go-lark.
Gin Middlewares for go-lark.

NOTICE: Only URL challenge and incoming message event (schema 1.0) are supported.
Other events will be supported with future v2 version with event schema 2.0.
## Supported events

- URL challenge for general events and card callback
- Event v2 (Schema 2.0)
- Card Callback
- (Legacy) Incoming message event (Schema 1.0)

## Installation

Expand All @@ -29,19 +33,30 @@ import (

func main() {
r := gin.Default()

middleware := larkgin.NewLarkMiddleware()

// lark server challenge
r.Use(middleware.LarkChallengeHandler())
// Event Schema 1.0, for older bots
r.Use(middleware.LarkMessageHandler())
// Event Scheme 2.0, for newer bots
r.Use(middleware.LarkEventHandler())

r.POST("/", func(c *gin.Context) {
if msg, ok := middleware.GetMessage(c); ok { // => returns `*lark.EventMessage`
fmt.Println(msg.Event.Text)
}
})

// all supported events
eventGroup := r.Group("/event")
{
eventGroup.Use(middleware.LarkEventHandler())
eventGroup.POST("/", func(c *gin.Context) {
if event, ok := middleware.GetEvent(e); ok { // => returns `*lark.EventV2`
}
})
}

// card callback only
cardGroup := r.Group("/card")
{
cardGroup.Use(middleware.LarkCardHandler())
cardGroup.POST("/callback", func(c *gin.Context) {
if card, ok := middleware.GetCardCallback(c); ok { // => returns `*lark.EventCardCallback`
}
})
}
}
```

Expand Down Expand Up @@ -70,6 +85,19 @@ r.POST("/", func(c *gin.Context) {
})
```

### Card Callback

We may also setup callback for card actions (e.g. button). The URL challenge part is the same.

We may use `LarkCardHandler` to handle the actions:
```go
r.Use(middleware.LarkCardHandler())
r.POST("/callback", func(c *gin.Context) {
if card, ok := middleware.GetCardCallback(c); ok {
}
})
```

### URL Binding

Only bind specific URL for events:
Expand All @@ -85,10 +113,12 @@ middleware.WithTokenVerfication("asodjiaoijoi121iuhiaud")

### Encryption

> Notice: encryption is not available for card callback, due to restriction from Lark Open Platform.
```go
middleware.WithEncryption("1231asda")
```

## About

Copyright (c) go-lark Developers, 2018-2022.
Copyright (c) go-lark Developers, 2018-2024.
67 changes: 67 additions & 0 deletions card.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package larkgin

import (
"crypto/sha1"
"encoding/json"
"fmt"
"log"
"strings"

"github.com/gin-gonic/gin"
"github.com/go-lark/lark"
)

// GetCardCallback from gin context
func (opt LarkMiddleware) GetCardCallback(c *gin.Context) (*lark.EventCardCallback, bool) {
if card, ok := c.Get(opt.cardKey); ok {
msg, ok := card.(lark.EventCardCallback)
return &msg, ok
}

return nil, false
}

// LarkCardHandler card callback handler
// Encryption is automatically ignored, because it's not supported officially
func (opt LarkMiddleware) LarkCardHandler() gin.HandlerFunc {
return func(c *gin.Context) {
defer c.Next()
body, err := fetchBody(c)
if err != nil {
return
}
var inputBody = body

var event lark.EventCardCallback
err = json.Unmarshal(inputBody, &event)
if err != nil {
log.Println(err)
return
}
if opt.enableTokenVerification {
nonce := c.Request.Header.Get("X-Lark-Request-Nonce")
timestamp := c.Request.Header.Get("X-Lark-Request-Timestamp")
signature := c.Request.Header.Get("X-Lark-Signature")
token := opt.cardSignature(nonce, timestamp, string(body), opt.verificationToken)
log.Println(token, signature)
if signature != token {
log.Println("Token verification failed")
return
}
}
c.Set(opt.cardKey, event)
}
}

func (opt LarkMiddleware) cardSignature(nonce string, timestamp string, body string, token string) string {
var b strings.Builder
b.WriteString(timestamp)
b.WriteString(nonce)
b.WriteString(token)
b.WriteString(body)
bs := []byte(b.String())
h := sha1.New()
h.Write(bs)
bs = h.Sum(nil)
return fmt.Sprintf("%x", bs)
}
43 changes: 43 additions & 0 deletions card_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package larkgin

import (
"testing"

"github.com/gin-gonic/gin"
"github.com/go-lark/lark"
"github.com/stretchr/testify/assert"
)

func TestCardCallback(t *testing.T) {
var (
r = gin.Default()
middleware = NewLarkMiddleware()

ok bool
event *lark.EventCardCallback
)
r.Use(middleware.LarkCardHandler())

card := map[string]interface{}{
"app_id": "fake_app_id",
"open_id": "fake_open_id",
"user_id": "f123f456",
"open_message_id": "om_8169c75fbae56c6bebb7e914b92253b4",
"open_chat_id": "fake_oc_id",
"tenant_key": "1068767a888dd740",
"token": "c-2fa8cd831bc83e6350b5be32eb24d2863be4bc5b",
"action": map[string]interface{}{
"tag": "button",
"value": map[string]interface{}{"action": "1"},
},
}
r.POST("/", func(c *gin.Context) {
event, ok = middleware.GetCardCallback(c)
t.Log(event, ok)
})
performRequest(r, "POST", "/", card)
assert.True(t, ok)
if assert.NotNil(t, event) {
assert.Equal(t, "button", event.Action.Tag)
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ go 1.13

require (
github.com/gin-gonic/gin v1.7.1
github.com/go-lark/lark v1.7.0-beta.4
github.com/go-lark/lark v1.13.1
github.com/stretchr/testify v1.7.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8=
github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-lark/lark v1.7.0-beta.4 h1:3Sx5swdA69TAH6uoT0yuED9cCaBLeJ7TjMhWyVVIWXk=
github.com/go-lark/lark v1.7.0-beta.4/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
github.com/go-lark/lark v1.13.1 h1:Vm2SEZdkYnyeuxHopzkTbNsr+nnjs9mCTWenML6JP8Q=
github.com/go-lark/lark v1.13.1/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
Expand Down

0 comments on commit 1c35b0f

Please sign in to comment.