Skip to content

Commit b544fc2

Browse files
authored
Added support for Lark persistent websocket connection (#185)
1 parent 12911d1 commit b544fc2

File tree

4 files changed

+73
-36
lines changed

4 files changed

+73
-36
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/gorilla/schema v1.4.0
2020
github.com/invopop/jsonschema v0.8.0
2121
github.com/jmoiron/sqlx v1.4.0
22-
github.com/larksuite/oapi-sdk-go/v3 v3.2.7
22+
github.com/larksuite/oapi-sdk-go/v3 v3.4.9
2323
github.com/lib/pq v1.10.9
2424
github.com/miekg/dns v1.1.61
2525
github.com/mitchellh/mapstructure v1.5.0
@@ -45,6 +45,7 @@ require (
4545
github.com/go-faster/city v1.0.1 // indirect
4646
github.com/go-faster/errors v0.6.1 // indirect
4747
github.com/gobwas/glob v0.2.3 // indirect
48+
github.com/gogo/protobuf v1.3.2 // indirect
4849
github.com/google/go-querystring v1.1.0 // indirect
4950
github.com/google/uuid v1.6.0 // indirect
5051
github.com/gorilla/websocket v1.5.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HK
310310
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
311311
github.com/larksuite/oapi-sdk-go/v3 v3.2.7 h1:1dAf8dkJJsHA0Qvan3d8LPMxFSppU4I9IAi5BabeIDE=
312312
github.com/larksuite/oapi-sdk-go/v3 v3.2.7/go.mod h1:ZEplY+kwuIrj/nqw5uSCINNATcH3KdxSN7y+UxYY5fI=
313+
github.com/larksuite/oapi-sdk-go/v3 v3.4.9 h1:ZzsPtdF2tsLbocQdKLNFwFCzWfwmKtD4kbyx7to++M0=
314+
github.com/larksuite/oapi-sdk-go/v3 v3.4.9/go.mod h1:ZEplY+kwuIrj/nqw5uSCINNATcH3KdxSN7y+UxYY5fI=
313315
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
314316
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
315317
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=

internal/modules/lark/config.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,25 @@ type Config struct {
99
AppID string `mapstructure:"app_id"`
1010
AppSecret string `mapstructure:"app_secret"`
1111
VerificationToken string `mapstructure:"verification_token"`
12+
Mode string `mapstructure:"mode"`
1213
EncryptKey string `mapstructure:"encrypt_key"`
1314
TLSEnabled bool `mapstructure:"tls_enabled"`
1415
ProxyURL string `mapstructure:"proxy_url"`
1516
ProxyInsecure bool `mapstructure:"proxy_insecure"`
1617
}
1718

19+
const (
20+
ModeWebhook = "webhook"
21+
ModeWebsocket = "websocket"
22+
)
23+
1824
func (c Config) Validate() error {
1925
return validation.ValidateStruct(&c,
2026
validation.Field(&c.Admin, validation.Required),
2127
validation.Field(&c.AppID, validation.Required),
2228
validation.Field(&c.AppSecret, validation.Required),
23-
validation.Field(&c.VerificationToken, validation.Required),
24-
validation.Field(&c.EncryptKey),
29+
validation.Field(&c.Mode, validation.In(ModeWebhook, ModeWebsocket)),
30+
validation.Field(&c.VerificationToken, validation.When(c.Mode == ModeWebhook, validation.Required)),
31+
validation.Field(&c.EncryptKey, validation.When(c.Mode == ModeWebhook, validation.Required)),
2532
)
2633
}

internal/modules/lark/lark.go

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/larksuite/oapi-sdk-go/v3/core/httpserverext"
2020
larkevent "github.com/larksuite/oapi-sdk-go/v3/event"
2121
larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
22+
larkws "github.com/larksuite/oapi-sdk-go/v3/ws"
2223

2324
"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
2425

@@ -66,8 +67,7 @@ func New(cfg *Config, db *database.DB, tlsConfig *tls.Config, acts actions.Actio
6667
var client = lark.NewClient(
6768
cfg.AppID,
6869
cfg.AppSecret,
69-
lark.WithLogReqAtDebug(true),
70-
lark.WithLogLevel(larkcore.LogLevelDebug),
70+
lark.WithLogLevel(larkcore.LogLevelInfo),
7171
lark.WithHttpClient(httpClient))
7272

7373
// Check that AppID and AppSecret are valid
@@ -113,38 +113,52 @@ func New(cfg *Config, db *database.DB, tlsConfig *tls.Config, acts actions.Actio
113113
}
114114

115115
func (lrk *Lark) Start() error {
116+
var dispatcher *dispatcher.EventDispatcher
116117

118+
// Webhooks by default
119+
if lrk.cfg.Mode == ModeWebhook || lrk.cfg.Mode == "" {
120+
dispatcher = lrk.makeDispatcher(lrk.cfg.VerificationToken, lrk.cfg.EncryptKey, true)
121+
return lrk.startWebhook(dispatcher)
122+
} else {
123+
dispatcher = lrk.makeDispatcher("", "", false)
124+
return lrk.startWebsocket(dispatcher)
125+
}
126+
}
127+
128+
func (lrk *Lark) makeDispatcher(verificationToken, eventEncryptKey string, dedupEvents bool) *dispatcher.EventDispatcher {
117129
// Sometimes the same event is sent several times, so keep recent event ids
118130
// to prevent handling the same event more than once.
119131
recentEvents := map[string]time.Time{}
120132
recentEventsMutex := sync.Mutex{}
121133

122-
ticker := time.NewTicker(10 * time.Second)
123-
go func() {
124-
for {
125-
select {
126-
case <-ticker.C:
127-
toRemove := make([]string, 0)
128-
129-
for eventID, handledAt := range recentEvents {
130-
131-
// Cleanup events after 10m
132-
// TODO: config
133-
if time.Since(handledAt) > time.Minute*5 {
134-
toRemove = append(toRemove, eventID)
134+
if dedupEvents {
135+
go func() {
136+
ticker := time.NewTicker(10 * time.Second)
137+
for {
138+
select {
139+
case <-ticker.C:
140+
toRemove := make([]string, 0)
141+
142+
for eventID, handledAt := range recentEvents {
143+
144+
// Cleanup events after 10m
145+
// TODO: config
146+
if time.Since(handledAt) > time.Minute*5 {
147+
toRemove = append(toRemove, eventID)
148+
}
135149
}
136-
}
137150

138-
recentEventsMutex.Lock()
139-
for _, eventID := range toRemove {
140-
delete(recentEvents, eventID)
151+
recentEventsMutex.Lock()
152+
for _, eventID := range toRemove {
153+
delete(recentEvents, eventID)
154+
}
155+
recentEventsMutex.Unlock()
141156
}
142-
recentEventsMutex.Unlock()
143157
}
144-
}
145-
}()
158+
}()
159+
}
146160

147-
handler := dispatcher.NewEventDispatcher(lrk.cfg.VerificationToken, lrk.cfg.EncryptKey).
161+
return dispatcher.NewEventDispatcher(verificationToken, eventEncryptKey).
148162
OnP2MessageReceiveV1(
149163
func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
150164

@@ -159,17 +173,19 @@ func (lrk *Lark) Start() error {
159173
return nil
160174
}
161175

162-
eventID := event.EventV2Base.Header.EventID
176+
if dedupEvents {
177+
eventID := event.EventV2Base.Header.EventID
163178

164-
recentEventsMutex.Lock()
165-
if _, ok := recentEvents[eventID]; ok {
166-
recentEventsMutex.Unlock()
179+
recentEventsMutex.Lock()
180+
if _, ok := recentEvents[eventID]; ok {
181+
recentEventsMutex.Unlock()
167182

168-
// Event was already handled
169-
return nil
183+
// Event was already handled
184+
return nil
185+
}
186+
recentEvents[eventID] = time.Now()
187+
recentEventsMutex.Unlock()
170188
}
171-
recentEvents[eventID] = time.Now()
172-
recentEventsMutex.Unlock()
173189

174190
userID := event.Event.Sender.SenderId.UserId
175191
msgID := event.Event.Message.MessageId
@@ -234,12 +250,23 @@ func (lrk *Lark) Start() error {
234250
return nil
235251
},
236252
)
253+
}
254+
255+
func (lrk *Lark) startWebsocket(eventHandler *dispatcher.EventDispatcher) error {
256+
cli := larkws.NewClient(lrk.cfg.AppID, lrk.cfg.AppSecret,
257+
larkws.WithEventHandler(eventHandler),
258+
larkws.WithLogLevel(larkcore.LogLevelInfo),
259+
)
260+
261+
return cli.Start(context.TODO())
262+
}
237263

264+
func (lrk *Lark) startWebhook(eventHandler *dispatcher.EventDispatcher) error {
238265
mux := http.NewServeMux()
239266

240267
// TODO: take path from config
241-
mux.HandleFunc("/webhook/event", httpserverext.NewEventHandlerFunc(handler,
242-
larkevent.WithLogLevel(larkcore.LogLevelDebug)))
268+
mux.HandleFunc("/webhook/event", httpserverext.NewEventHandlerFunc(eventHandler,
269+
larkevent.WithLogLevel(larkcore.LogLevelInfo)))
243270

244271
// TODO: take port from config
245272
srv := http.Server{

0 commit comments

Comments
 (0)