85 lines
2.2 KiB
Go
85 lines
2.2 KiB
Go
package feishu
|
|
|
|
import (
|
|
"log/slog"
|
|
"sync"
|
|
)
|
|
|
|
// sharedWSGroup tracks all Platform instances sharing the same Feishu app
|
|
// WebSocket connection. When multiple projects use the same app_id, Feishu's
|
|
// server load-balances messages across WebSocket connections. By sharing a
|
|
// single connection and fanning out events to all platforms, every project
|
|
// receives every message and can apply its own allow_chat / allow_from filters.
|
|
type sharedWSGroup struct {
|
|
mu sync.RWMutex
|
|
platforms []*Platform
|
|
}
|
|
|
|
var (
|
|
sharedWSMu sync.Mutex
|
|
sharedWSGroups = map[string]*sharedWSGroup{} // key: app_id "|" domain
|
|
)
|
|
|
|
func sharedWSKey(appID, domain string) string {
|
|
return appID + "|" + domain
|
|
}
|
|
|
|
// registerSharedWS registers a platform in the shared WebSocket group for its
|
|
// app_id+domain. Returns the group and whether this platform is the primary
|
|
// (first to register and responsible for owning the WebSocket connection).
|
|
func registerSharedWS(p *Platform) (group *sharedWSGroup, isPrimary bool) {
|
|
key := sharedWSKey(p.appID, p.domain)
|
|
sharedWSMu.Lock()
|
|
defer sharedWSMu.Unlock()
|
|
|
|
g, exists := sharedWSGroups[key]
|
|
if !exists {
|
|
g = &sharedWSGroup{}
|
|
sharedWSGroups[key] = g
|
|
}
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
g.platforms = append(g.platforms, p)
|
|
isPrimary = len(g.platforms) == 1
|
|
if !isPrimary {
|
|
slog.Info("feishu: sharing WebSocket connection",
|
|
"app_id", p.appID, "platforms", len(g.platforms))
|
|
}
|
|
return g, isPrimary
|
|
}
|
|
|
|
// unregisterSharedWS removes a platform from its shared group.
|
|
// Returns the number of platforms remaining in the group.
|
|
func unregisterSharedWS(p *Platform) int {
|
|
key := sharedWSKey(p.appID, p.domain)
|
|
sharedWSMu.Lock()
|
|
defer sharedWSMu.Unlock()
|
|
|
|
g, exists := sharedWSGroups[key]
|
|
if !exists {
|
|
return 0
|
|
}
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
for i, plat := range g.platforms {
|
|
if plat == p {
|
|
g.platforms = append(g.platforms[:i], g.platforms[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
remaining := len(g.platforms)
|
|
if remaining == 0 {
|
|
delete(sharedWSGroups, key)
|
|
}
|
|
return remaining
|
|
}
|
|
|
|
// allPlatforms returns a snapshot of all platforms in the group.
|
|
func (g *sharedWSGroup) allPlatforms() []*Platform {
|
|
g.mu.RLock()
|
|
defer g.mu.RUnlock()
|
|
result := make([]*Platform, len(g.platforms))
|
|
copy(result, g.platforms)
|
|
return result
|
|
}
|