mirror of
https://github.com/matrix-org/matrix-spec
synced 2026-03-26 13:04:10 +01:00
Spec MSC2285 (private read receipts)
This commit is contained in:
parent
06ddb862c3
commit
03399e286a
|
|
@ -107,7 +107,19 @@ determined by the push rules which apply to an event.
|
||||||
|
|
||||||
When the user updates their read receipt (either by using the API or by
|
When the user updates their read receipt (either by using the API or by
|
||||||
sending an event), notifications prior to and including that event MUST
|
sending an event), notifications prior to and including that event MUST
|
||||||
be marked as read.
|
be marked as read. Note that users can send both an `m.read` and
|
||||||
|
`m.read.private` receipt, both of which are capable of clearing notifications.
|
||||||
|
|
||||||
|
If the user has both `m.read` and `m.read.private` set in the room then
|
||||||
|
the receipt which is more recent/ahead must be used to determine where
|
||||||
|
the user has read up to. For example, given an ordered set of events A,
|
||||||
|
B, C, and D the `m.read` receipt could be at event C and `m.read.private`
|
||||||
|
at event A - the user is considered to have read up to event C. If the
|
||||||
|
`m.read.private` receipt is then updated to point to B or C, the user's
|
||||||
|
notification state doesn't change (the `m.read` receipt is still more
|
||||||
|
ahead), however if the `m.read.private` receipt were to be updated to
|
||||||
|
event D then the user has read up to D (the `m.read` receipt is now
|
||||||
|
behind the `m.read.private` receipt).
|
||||||
|
|
||||||
##### Push Rules
|
##### Push Rules
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,16 @@ The client cannot update fully read markers by directly modifying the
|
||||||
`m.fully_read` account data event. Instead, the client must make use of
|
`m.fully_read` account data event. Instead, the client must make use of
|
||||||
the read markers API to change the values.
|
the read markers API to change the values.
|
||||||
|
|
||||||
|
{{< changed-in v="1.4" >}} `m.read.private` receipts can now be sent from
|
||||||
|
`/read_markers`.
|
||||||
|
|
||||||
The read markers API can additionally update the user's read receipt
|
The read markers API can additionally update the user's read receipt
|
||||||
(`m.read`) location in the same operation as setting the fully read
|
(`m.read` or `m.read.private`) location in the same operation as setting
|
||||||
marker location. This is because read receipts and read markers are
|
the fully read marker location. This is because read receipts and read
|
||||||
commonly updated at the same time, and therefore the client might wish
|
markers are commonly updated at the same time, and therefore the client
|
||||||
to save an extra HTTP call. Providing an `m.read` location performs the
|
might wish to save an extra HTTP call. Providing `m.read` and/or
|
||||||
same task as a request to `/receipt/m.read/$event:example.org`.
|
`m.read.private` performs the same task as a request to
|
||||||
|
[`/receipt/{receiptType}/{eventId}`](#post_matrixclientv3roomsroomidreceiptreceipttypeeventid).
|
||||||
|
|
||||||
{{% http-api spec="client-server" api="read_markers" %}}
|
{{% http-api spec="client-server" api="read_markers" %}}
|
||||||
|
|
||||||
|
|
@ -44,8 +48,9 @@ same task as a request to `/receipt/m.read/$event:example.org`.
|
||||||
|
|
||||||
The server MUST prevent clients from setting `m.fully_read` directly in
|
The server MUST prevent clients from setting `m.fully_read` directly in
|
||||||
room account data. The server must additionally ensure that it treats
|
room account data. The server must additionally ensure that it treats
|
||||||
the presence of `m.read` in the `/read_markers` request the same as how
|
the presence of `m.read` and `m.read.private` in the `/read_markers`
|
||||||
it would for a request to `/receipt/m.read/$event:example.org`.
|
request the same as how it would for a request to
|
||||||
|
[`/receipt/{receiptType}/{eventId}`](#post_matrixclientv3roomsroomidreceiptreceipttypeeventid).
|
||||||
|
|
||||||
Upon updating the `m.fully_read` event due to a request to
|
Upon updating the `m.fully_read` event due to a request to
|
||||||
`/read_markers`, the server MUST send the updated account data event
|
`/read_markers`, the server MUST send the updated account data event
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,14 @@ type: module
|
||||||
|
|
||||||
### Receipts
|
### Receipts
|
||||||
|
|
||||||
|
{{< changed-in v="1.4" >}} Added private read receipts.
|
||||||
|
|
||||||
This module adds in support for receipts. These receipts are a form of
|
This module adds in support for receipts. These receipts are a form of
|
||||||
acknowledgement of an event. This module defines a single
|
acknowledgement of an event. This module defines the `m.read` receipt
|
||||||
acknowledgement: `m.read` which indicates that the user has read up to a
|
for indicating that the user has read up to a given event, and `m.read.private`
|
||||||
given event.
|
to achieve the same purpose without any other user being aware. Primarily,
|
||||||
|
`m.read.private` is meant to clear [notifications](#receiving-notifications)
|
||||||
|
without advertising read-up-to status to others.
|
||||||
|
|
||||||
Sending a receipt for each event can result in sending large amounts of
|
Sending a receipt for each event can result in sending large amounts of
|
||||||
traffic to a homeserver. To prevent this from becoming a problem,
|
traffic to a homeserver. To prevent this from becoming a problem,
|
||||||
|
|
@ -59,12 +63,36 @@ following HTTP APIs.
|
||||||
|
|
||||||
{{% http-api spec="client-server" api="receipts" %}}
|
{{% http-api spec="client-server" api="receipts" %}}
|
||||||
|
|
||||||
|
##### Private read receipts
|
||||||
|
|
||||||
|
{{% added-in v="1.4" %}}
|
||||||
|
|
||||||
|
Some users would like to clear their [notification counts](#receiving-notifications),
|
||||||
|
but not give away the fact that they've read a particular message yet. To
|
||||||
|
achieve this, clients can send `m.read.private` receipts instead of `m.read`
|
||||||
|
to do exactly that: clear notifications and not broadcast the receipt to
|
||||||
|
other users.
|
||||||
|
|
||||||
|
Servers MUST NOT send the `m.read.private` receipt to any other user than the
|
||||||
|
one which originally sent it.
|
||||||
|
|
||||||
|
Between `m.read` and `m.read.private`, the receipt which is more "ahead" or
|
||||||
|
"recent" is used when determining the highest read-up-to mark. See the
|
||||||
|
[notifications](#receiving-notifications) section for more information on
|
||||||
|
how this affects notification counts.
|
||||||
|
|
||||||
|
If a client sends an `m.read` receipt which is "behind" the `m.read.private`
|
||||||
|
receipt, other users will see that change happen but the sending user will
|
||||||
|
not have their notification counts rewound to that point in time. While
|
||||||
|
uncommon, it is considered valid to have an `m.read` (public) receipt lag
|
||||||
|
several messages behind the `m.read.private` receipt, for example.
|
||||||
|
|
||||||
#### Server behaviour
|
#### Server behaviour
|
||||||
|
|
||||||
For efficiency, receipts SHOULD be batched into one event per room
|
For efficiency, receipts SHOULD be batched into one event per room
|
||||||
before delivering them to clients.
|
before delivering them to clients.
|
||||||
|
|
||||||
Receipts are sent across federation as EDUs with type `m.receipt`. The
|
Some receipts are sent across federation as EDUs with type `m.receipt`. The
|
||||||
format of the EDUs are:
|
format of the EDUs are:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -80,7 +108,8 @@ format of the EDUs are:
|
||||||
```
|
```
|
||||||
|
|
||||||
These are always sent as deltas to previously sent receipts. Currently
|
These are always sent as deltas to previously sent receipts. Currently
|
||||||
only a single `<receipt_type>` should be used: `m.read`.
|
only a single `<receipt_type>` should be used: `m.read`. `m.read.private`
|
||||||
|
MUST NOT appear in this federated `m.receipt` EDU.
|
||||||
|
|
||||||
#### Security considerations
|
#### Security considerations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,9 @@ paths:
|
||||||
The event ID the read marker should be located at. The
|
The event ID the read marker should be located at. The
|
||||||
event MUST belong to the room.
|
event MUST belong to the room.
|
||||||
example: "$somewhere:example.org"
|
example: "$somewhere:example.org"
|
||||||
|
x-changedInMatrixVersion:
|
||||||
|
1.4: |
|
||||||
|
This property is no longer required.
|
||||||
"m.read":
|
"m.read":
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: |-
|
||||||
|
|
@ -63,11 +66,19 @@ paths:
|
||||||
equivalent to calling `/receipt/m.read/$elsewhere:example.org`
|
equivalent to calling `/receipt/m.read/$elsewhere:example.org`
|
||||||
and is provided here to save that extra call.
|
and is provided here to save that extra call.
|
||||||
example: "$elsewhere:example.org"
|
example: "$elsewhere:example.org"
|
||||||
required: ['m.fully_read']
|
"m.read.private":
|
||||||
|
x-addedInMatrixVersion: "1.4"
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
The event ID to set the *private* read receipt location at. This
|
||||||
|
equivalent to calling `/receipt/m.read.private/$elsewhere:example.org`
|
||||||
|
and is provided here to save that extra call.
|
||||||
|
example: "$elsewhere:example.org"
|
||||||
|
required: []
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: |-
|
description: |-
|
||||||
The read marker, and read receipt if provided, have been updated.
|
The read marker, and read receipt(s) if provided, have been updated.
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
properties: {}
|
properties: {}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,10 @@ paths:
|
||||||
description: The type of receipt to send.
|
description: The type of receipt to send.
|
||||||
required: true
|
required: true
|
||||||
x-example: "m.read"
|
x-example: "m.read"
|
||||||
enum: ["m.read"]
|
x-changedInMatrixVersion:
|
||||||
|
1.4: |
|
||||||
|
Allow `m.read.private` receipts.
|
||||||
|
enum: ["m.read", "m.read.private"]
|
||||||
- in: path
|
- in: path
|
||||||
type: string
|
type: string
|
||||||
name: eventId
|
name: eventId
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,11 @@
|
||||||
"@rikj:jki.re": {
|
"@rikj:jki.re": {
|
||||||
"ts": 1436451550453
|
"ts": 1436451550453
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"m.read.private": {
|
||||||
|
"@self:example.org": {
|
||||||
|
"ts": 1661384801651
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
type: object
|
type: object
|
||||||
title: Receipt Event
|
title: Receipt Event
|
||||||
description: Informs the client of new receipts.
|
description: Informs the client of new receipts.
|
||||||
|
x-changedInMatrixVersion:
|
||||||
|
1.4: |
|
||||||
|
Added `m.read.private` receipts to the event's `content`.
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "core-event-schema/event.yaml"
|
- $ref: "core-event-schema/event.yaml"
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -23,7 +26,7 @@ properties:
|
||||||
A collection of users who have sent `m.read` receipts for
|
A collection of users who have sent `m.read` receipts for
|
||||||
this event.
|
this event.
|
||||||
patternProperties:
|
patternProperties:
|
||||||
"^@":
|
"^@": &receiptUserMap
|
||||||
type: object
|
type: object
|
||||||
title: Receipt
|
title: Receipt
|
||||||
description: |-
|
description: |-
|
||||||
|
|
@ -35,8 +38,17 @@ properties:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
description: The timestamp the receipt was sent at.
|
description: The timestamp the receipt was sent at.
|
||||||
|
"m.read.private":
|
||||||
|
type: object
|
||||||
|
title: Own User
|
||||||
|
description: |-
|
||||||
|
Similar to `m.read`, the users who have sent `m.read.private`
|
||||||
|
receipts for this event. Due to the nature of private read
|
||||||
|
receipts, this should only ever have the current user's ID.
|
||||||
|
patternProperties:
|
||||||
|
"^@": *receiptUserMap
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
enum: ["m.receipt"]
|
enum: ["m.receipt", "m.receipt.private"]
|
||||||
required: ["type", "content"]
|
required: ["type", "content"]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue