Group voice and video calls with MatrixRTC and LiveKit
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
Every call in the web client now goes through MatrixRTC with a LiveKit
SFU, the model Element Call and Element X use; the legacy 1:1 m.call.*
calls are gone. In a room of two the other person is rung and can answer
or decline; in a bigger room a banner offers to join the call. Calls show
a grid of participant tiles with mute, camera, screen sharing and leave,
and media is end-to-end encrypted in encrypted rooms.
Server:
- MSC4140 delayed events (send/state with a delay, plus the
delayed_events list/restart/cancel/send API), stored and scheduled, so
a member whose client drops off is taken out of the call.
- POST /user/{userId}/openid/request_token, which lk-jwt-service uses to
hand out LiveKit tokens.
- org.matrix.msc4143.rtc_foci in /.well-known/matrix/client, configured
under client_api.rtc.
- Room presets let members send call membership state.
- Migration 0014 for both SQL dialects.
Deploy: LiveKit and lk-jwt-service in compose (calls.yml) and the helm
chart. Docs: docs/calls.md, configuration and deployment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
325431a5be
commit
76dfc98a86
49 files changed
+3849
-432
No files matched your search
@@ -2,16 +2,20 @@ package clientapi_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dariuszjaniak/warp/server/internal/clientapi"
|
||||
"github.com/dariuszjaniak/warp/server/internal/config"
|
||||
"github.com/dariuszjaniak/warp/server/internal/delayedevents"
|
||||
"github.com/dariuszjaniak/warp/server/internal/federation"
|
||||
"github.com/dariuszjaniak/warp/server/internal/httputil"
|
||||
"github.com/dariuszjaniak/warp/server/internal/matrix/signing"
|
||||
@@ -39,6 +43,44 @@ type testServer struct {
|
||||
settings *settings.Service
|
||||
fedGate *federation.Gate
|
||||
db storage.Database
|
||||
|
||||
// delayed is the delayed event service, on a clock of its own that
|
||||
// tests move with advance.
|
||||
delayed *delayedevents.Service
|
||||
clock *testClock
|
||||
}
|
||||
|
||||
// testClock is a clock a test moves by hand.
|
||||
type testClock struct {
|
||||
mu sync.Mutex
|
||||
now time.Time
|
||||
}
|
||||
|
||||
func (c *testClock) Now() time.Time {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.now
|
||||
}
|
||||
|
||||
func (c *testClock) advance(d time.Duration) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.now = c.now.Add(d)
|
||||
}
|
||||
|
||||
// teeNotifier passes the roomserver's emit stage on to the syncapi and,
|
||||
// once it is built, to the delayed event service, which needs the
|
||||
// roomserver to exist first.
|
||||
type teeNotifier struct {
|
||||
first roomserver.Notifier
|
||||
then roomserver.Notifier
|
||||
}
|
||||
|
||||
func (n *teeNotifier) EventPersisted(ctx context.Context, note roomserver.Notification) {
|
||||
n.first.EventPersisted(ctx, note)
|
||||
if n.then != nil {
|
||||
n.then.EventPersisted(ctx, note)
|
||||
}
|
||||
}
|
||||
|
||||
// newTestServer builds a server. tweak adjusts the options before the
|
||||
@@ -91,13 +133,14 @@ func newTestServer(t *testing.T, tweak func(*clientapi.Options)) *testServer {
|
||||
// stage and the syncapi's wake-up are the same object, exactly as in
|
||||
// a real server.
|
||||
notifier := syncapi.NewNotifier()
|
||||
emit := &teeNotifier{first: notifier}
|
||||
rooms, err := roomserver.New(roomserver.Options{
|
||||
ServerName: testServerName,
|
||||
Store: db.Rooms(),
|
||||
Profiles: users,
|
||||
SigningKey: key,
|
||||
FederationEnabled: settingsService.FederationEnabled,
|
||||
Notifier: notifier,
|
||||
Notifier: emit,
|
||||
Logger: slog.New(slog.DiscardHandler),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -121,14 +164,34 @@ func newTestServer(t *testing.T, tweak func(*clientapi.Options)) *testServer {
|
||||
}
|
||||
t.Cleanup(sync.Close)
|
||||
|
||||
// Delayed events are on, with the scheduler left stopped: a test
|
||||
// moves the clock and calls SendDue itself.
|
||||
clock := &testClock{now: time.Now()}
|
||||
delayed, err := delayedevents.New(delayedevents.Options{
|
||||
Store: db.DelayedEvents(),
|
||||
Rooms: rooms,
|
||||
MaxDelay: time.Hour,
|
||||
MaxPerUser: 5,
|
||||
DescribeError: clientapi.DescribeRoomError,
|
||||
Logger: slog.New(slog.DiscardHandler),
|
||||
Now: clock.Now,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("delayedevents.New: %v", err)
|
||||
}
|
||||
t.Cleanup(delayed.Close)
|
||||
emit.then = delayed
|
||||
|
||||
opts := clientapi.Options{
|
||||
ServerName: testServerName,
|
||||
Users: users,
|
||||
Rooms: rooms,
|
||||
Sync: sync,
|
||||
Settings: settingsService,
|
||||
Federation: gate,
|
||||
Logger: slog.New(slog.DiscardHandler),
|
||||
ServerName: testServerName,
|
||||
Users: users,
|
||||
Rooms: rooms,
|
||||
Sync: sync,
|
||||
Settings: settingsService,
|
||||
Federation: gate,
|
||||
DelayedEvents: delayed,
|
||||
OpenIDTokens: db.OpenIDTokens(),
|
||||
Logger: slog.New(slog.DiscardHandler),
|
||||
}
|
||||
if tweak != nil {
|
||||
tweak(&opts)
|
||||
@@ -150,6 +213,8 @@ func newTestServer(t *testing.T, tweak func(*clientapi.Options)) *testServer {
|
||||
settings: settingsService,
|
||||
fedGate: gate,
|
||||
db: db,
|
||||
delayed: delayed,
|
||||
clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user