diff --git a/client.go b/client.go index ec527dd0..95cbacb5 100644 --- a/client.go +++ b/client.go @@ -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) diff --git a/event/delayed.go b/event/delayed.go new file mode 100644 index 00000000..fefb62af --- /dev/null +++ b/event/delayed.go @@ -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" +) diff --git a/go.mod b/go.mod index 70bf601e..d77428d8 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 639b30a2..dee6616c 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/id/opaque.go b/id/opaque.go index 1d9f0dcf..c1ad4988 100644 --- a/id/opaque.go +++ b/id/opaque.go @@ -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) } diff --git a/requests.go b/requests.go index 9dfe09ab..f0287b3c 100644 --- a/requests.go +++ b/requests.go @@ -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 diff --git a/responses.go b/responses.go index a79be28b..3484c134 100644 --- a/responses.go +++ b/responses.go @@ -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 {