package main
import (
"crypto/rand"
"encoding/hex"
"sync"
"time"
)
type Invite struct {
From string `json:"from"`
MeetingID string `json:"meetingId"`
JitsiURL string `json:"jitsiUrl"`
CreatedAt time.Time `json:"createdAt"`
}
const presenceWindow = 90 * time.Second
const inviteTTL = 2 * time.Minute
type Store struct {
mu sync.Mutex
lastSeen map[string]time.Time
invites map[string][]Invite
}
func NewStore() *Store {
return &Store{
lastSeen: make(map[string]time.Time),
invites: make(map[string][]Invite),
}
}
func (s *Store) Touch(username string) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastSeen[username] = time.Now()
}
func (s *Store) IsOnline(username string) bool {
s.mu.Lock()
defer s.mu.Unlock()
t, ok := s.lastSeen[username]
return ok && time.Since(t) < presenceWindow
}
func (s *Store) PlaceCall(to string, inv Invite) Invite {
inv.CreatedAt = time.Now()
s.mu.Lock()
defer s.mu.Unlock()
s.invites[to] = append(s.invites[to], inv)
return inv
}
func (s *Store) DrainInvites(username string) []Invite {
s.mu.Lock()
defer s.mu.Unlock()
s.lastSeen[username] = time.Now()
pending := s.invites[username]
delete(s.invites, username)
cutoff := time.Now().Add(-inviteTTL)
fresh := make([]Invite, 0, len(pending))
for _, inv := range pending {
if inv.CreatedAt.After(cutoff) {
fresh = append(fresh, inv)
}
}
return fresh
}
func newMeetingID() string {
buf := make([]byte, 8)
_, _ = rand.Read(buf)
return "quple-" + hex.EncodeToString(buf)
}