client,event: MSC4140: Delayed events
Some checks failed
Go / Lint (latest) (push) Has been cancelled
Go / Build (old, libolm) (push) Has been cancelled
Go / Build (latest, libolm) (push) Has been cancelled
Go / Build (old, goolm) (push) Has been cancelled
Go / Build (latest, goolm) (push) Has been cancelled

Includes transparent migration from deprecated MSC fields still used
in Synapse to later revision.
This commit is contained in:
Toni Spets 2025-10-10 14:58:50 +03:00
commit 22ea75db96
7 changed files with 122 additions and 6 deletions

View file

@ -1313,6 +1313,32 @@ func (cli *Client) SendMassagedStateEvent(ctx context.Context, roomID id.RoomID,
return
}
func (cli *Client) DelayedEvents(ctx context.Context, req *ReqDelayedEvents) (resp *RespDelayedEvents, err error) {
query := map[string]string{}
if req.DelayID != "" {
query["delay_id"] = string(req.DelayID)
}
if req.Status != "" {
query["status"] = string(req.Status)
}
if req.NextBatch != "" {
query["next_batch"] = req.NextBatch
}
urlPath := cli.BuildURLWithQuery(ClientURLPath{"unstable", "org.matrix.msc4140", "delayed_events"}, query)
_, err = cli.MakeRequest(ctx, http.MethodGet, urlPath, req, &resp)
// Migration: merge old keys with new ones
if resp != nil {
resp.Scheduled = append(resp.Scheduled, resp.DelayedEvents...)
resp.DelayedEvents = nil
resp.Finalised = append(resp.Finalised, resp.FinalisedEvents...)
resp.FinalisedEvents = nil
}
return
}
func (cli *Client) UpdateDelayedEvent(ctx context.Context, req *ReqUpdateDelayedEvent) (resp *RespUpdateDelayedEvent, err error) {
urlPath := cli.BuildClientURL("unstable", "org.matrix.msc4140", "delayed_events", req.DelayID)
_, err = cli.MakeRequest(ctx, http.MethodPost, urlPath, req, &resp)

70
event/delayed.go Normal file
View file

@ -0,0 +1,70 @@
package event
import (
"encoding/json"
"go.mau.fi/util/jsontime"
"maunium.net/go/mautrix/id"
)
type ScheduledDelayedEvent struct {
DelayID id.DelayID `json:"delay_id"`
RoomID id.RoomID `json:"room_id"`
Type Type `json:"type"`
StateKey *string `json:"state_key,omitempty"`
Delay int64 `json:"delay"`
RunningSince jsontime.UnixMilli `json:"running_since"`
Content Content `json:"content"`
}
func (e ScheduledDelayedEvent) AsEvent(eventID id.EventID, ts jsontime.UnixMilli) (*Event, error) {
evt := &Event{
ID: eventID,
RoomID: e.RoomID,
Type: e.Type,
StateKey: e.StateKey,
Content: e.Content,
Timestamp: ts.UnixMilli(),
}
return evt, evt.Content.ParseRaw(evt.Type)
}
type FinalisedDelayedEvent struct {
DelayedEvent *ScheduledDelayedEvent `json:"scheduled_event"`
Outcome DelayOutcome `json:"outcome"`
Reason DelayReason `json:"reason"`
Error json.RawMessage `json:"error,omitempty"`
EventID id.EventID `json:"event_id,omitempty"`
Timestamp jsontime.UnixMilli `json:"origin_server_ts"`
}
type DelayStatus string
var (
DelayStatusScheduled DelayStatus = "scheduled"
DelayStatusFinalised DelayStatus = "finalised"
)
type DelayAction string
var (
DelayActionSend DelayAction = "send"
DelayActionCancel DelayAction = "cancel"
DelayActionRestart DelayAction = "restart"
)
type DelayOutcome string
var (
DelayOutcomeSend DelayOutcome = "send"
DelayOutcomeCancel DelayOutcome = "cancel"
)
type DelayReason string
var (
DelayReasonAction DelayReason = "action"
DelayReasonError DelayReason = "error"
DelayReasonDelay DelayReason = "delay"
)

2
go.mod
View file

@ -17,7 +17,7 @@ require (
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
github.com/yuin/goldmark v1.7.13
go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10
go.mau.fi/util v0.9.2-0.20251014102252-c9ee13b043c8
go.mau.fi/zeroconfig v0.2.0
golang.org/x/crypto v0.42.0
golang.org/x/exp v0.0.0-20250911091902-df9299821621

4
go.sum
View file

@ -51,8 +51,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10 h1:EvX/di02gOriKN0xGDJuQ5mgiNdAF4LJc8moffI7Svo=
go.mau.fi/util v0.9.2-0.20251001114608-d99877b9cc10/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ=
go.mau.fi/util v0.9.2-0.20251014102252-c9ee13b043c8 h1:36oe41yPjz7QLjJWb72qHi82IOINqgp06eHIVRdalGs=
go.mau.fi/util v0.9.2-0.20251014102252-c9ee13b043c8/go.mod h1:M0bM9SyaOWJniaHs9hxEzz91r5ql6gYq6o1q5O1SsjQ=
go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU=
go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=

View file

@ -32,6 +32,9 @@ type EventID string
// https://github.com/matrix-org/matrix-doc/pull/2716
type BatchID string
// A DelayID is a string identifying a delayed event.
type DelayID string
func (roomID RoomID) String() string {
return string(roomID)
}

View file

@ -376,9 +376,15 @@ type ReqSendEvent struct {
MeowEventID id.EventID
}
type ReqDelayedEvents struct {
DelayID id.DelayID `json:"-"`
Status event.DelayStatus `json:"-"`
NextBatch string `json:"-"`
}
type ReqUpdateDelayedEvent struct {
DelayID string `json:"-"`
Action string `json:"action"` // TODO use enum
DelayID id.DelayID `json:"-"`
Action event.DelayAction `json:"action"`
}
// ReqDeviceInfo is the JSON request for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3devicesdeviceid

View file

@ -104,11 +104,22 @@ type RespContext struct {
type RespSendEvent struct {
EventID id.EventID `json:"event_id"`
UnstableDelayID string `json:"delay_id,omitempty"`
UnstableDelayID id.DelayID `json:"delay_id,omitempty"`
}
type RespUpdateDelayedEvent struct{}
type RespDelayedEvents struct {
Scheduled []*event.ScheduledDelayedEvent `json:"scheduled,omitempty"`
Finalised []*event.FinalisedDelayedEvent `json:"finalised,omitempty"`
NextBatch string `json:"next_batch,omitempty"`
// Deprecated: Synapse implementation still returns this
DelayedEvents []*event.ScheduledDelayedEvent `json:"delayed_events,omitempty"`
// Deprecated: Synapse implementation still returns this
FinalisedEvents []*event.FinalisedDelayedEvent `json:"finalised_events,omitempty"`
}
type RespRedactUserEvents struct {
IsMoreEvents bool `json:"is_more_events"`
RedactedEvents struct {