Files
Warp/server/internal/storage/delayed.go
T
tajniak81andClaude Opus 5.5 76dfc98a86
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
Group voice and video calls with MatrixRTC and LiveKit
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>
2026-10-18 19:33:29 +02:00

111 lines
4.2 KiB
Go

package storage
import (
"context"
"encoding/json"
"errors"
)
// ErrNoSuchDelayedEvent is returned for a delay ID that is unknown, belongs
// to someone else, or is no longer scheduled.
var ErrNoSuchDelayedEvent = errors.New("no such delayed event")
// DelayedEvent is an event a client asked to have sent later (MSC4140).
//
// While it is scheduled FinalisedAtMS is zero. Once it has been sent or
// cancelled it is kept for a while with its outcome, so a client that comes
// back can find out what became of it.
type DelayedEvent struct {
DelayID string
UserID string
DeviceID string
RoomID string
Type string
// StateKey is nil for a timeline event.
StateKey *string
Content json.RawMessage
// DelayMS is the delay the client asked for. A restart counts it again
// from now.
DelayMS int64
// RunningSinceMS is when the delay was last (re)started, and SendAtMS
// when it runs out: RunningSinceMS + DelayMS.
RunningSinceMS int64
SendAtMS int64
// The outcome, once finalised: Outcome is "send" or "cancel", Reason
// "delay", "action" or "error". EventID is the event that was sent,
// and Error the Matrix error that stopped it.
FinalisedAtMS int64
Outcome string
Reason string
EventID string
Error json.RawMessage
}
// DelayedEventStore keeps the delayed events of every user.
type DelayedEventStore interface {
// InsertDelayedEvent schedules an event.
InsertDelayedEvent(ctx context.Context, e DelayedEvent) error
// DelayedEvent returns one of userID's events, scheduled or finalised.
DelayedEvent(ctx context.Context, userID, delayID string) (DelayedEvent, error)
// ScheduledDelayedEvent returns a scheduled event whoever owns it: the
// action endpoints are addressed by the delay ID alone.
ScheduledDelayedEvent(ctx context.Context, delayID string) (DelayedEvent, error)
// DelayedEvents lists userID's events, scheduled ones first by when
// they are due, then finalised ones newest first.
DelayedEvents(ctx context.Context, userID string) ([]DelayedEvent, error)
// CountScheduledDelayedEvents counts userID's scheduled events.
CountScheduledDelayedEvents(ctx context.Context, userID string) (int, error)
// RestartDelayedEvent starts a scheduled event's delay again from nowMS.
// It returns ErrNoSuchDelayedEvent if the event is not scheduled.
RestartDelayedEvent(ctx context.Context, delayID string, nowMS int64) error
// FinaliseDelayedEvent records what became of a scheduled event. It
// returns ErrNoSuchDelayedEvent if the event was already finalised,
// which is how two racing finalisers are told apart: only one of them
// gets to send.
FinaliseDelayedEvent(ctx context.Context, e DelayedEvent) error
// DueDelayedEvents returns up to limit scheduled events whose delay
// has run out at nowMS, the earliest first.
DueDelayedEvents(ctx context.Context, nowMS int64, limit int) ([]DelayedEvent, error)
// NextDelayedEventDue reports when the next scheduled event is due,
// and false when nothing is scheduled.
NextDelayedEventDue(ctx context.Context) (int64, bool, error)
// ScheduledDelayedStateEvents lists the scheduled state events for one
// piece of room state, of every user.
ScheduledDelayedStateEvents(ctx context.Context, roomID, eventType, stateKey string) ([]DelayedEvent, error)
// PurgeFinalisedDelayedEvents forgets the events finalised before
// beforeMS.
PurgeFinalisedDelayedEvents(ctx context.Context, beforeMS int64) error
}
// ErrNoSuchOpenIDToken is returned for an OpenID token that is unknown or
// has expired.
var ErrNoSuchOpenIDToken = errors.New("no such OpenID token")
// OpenIDTokenStore keeps the OpenID tokens users hand to third parties, so
// those can find out who the user is (and nothing more) from this server.
type OpenIDTokenStore interface {
// InsertOpenIDToken stores a token by its hash.
InsertOpenIDToken(ctx context.Context, tokenHash, userID string, expiresAtMS int64) error
// OpenIDTokenUser returns the user a token was issued to, or
// ErrNoSuchOpenIDToken once it has expired at nowMS.
OpenIDTokenUser(ctx context.Context, tokenHash string, nowMS int64) (string, error)
// PurgeOpenIDTokens forgets tokens that expired before beforeMS.
PurgeOpenIDTokens(ctx context.Context, beforeMS int64) error
}