Files
2026-06-02 23:14:41 +08:00

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
}