### 推送通知
```
+--------------------+ +-------------------+
Matrix HTTP | | | |
Notification Protocol | 应用开发者 | | 设备厂商 |
| | | |
+-------------------+ | +----------------+ | | +---------------+ |
| | | | | | | | | |
| Matrix 主服务器 +-----> 推送网关(Gateway)+------> 推送供应商 | |
| | | | | | | | | |
+-^-----------------+ | +----------------+ | | +----+----------+ |
| | | | | |
Matrix | | | | | |
客户端/服务器API + | | | | |
| | +--------------------+ +-------------------+
| +--+-+ |
| | <-------------------------------------------+
+---+ |
| | 供应商推送协议
+----+
移动设备或客户端
```
本模块增加了对推送通知的支持。主服务器会将事件的通知发送到用户配置的 HTTP 端点。用户也可以自定义多条规则,决定哪些事件会生成通知。这些规则全部存储并由用户的主服务器管理。这允许用户专属的推送设置在不同客户端应用之间复用。
上述图示显示了推送通知发送到手机时的流程,通知通过手机厂商(例如 Apple 的 APNS 或 Google 的 GCM)进行提交。具体流程如下:
1. 客户端应用登录到主服务器。
2. 客户端应用在其厂商的推送供应商处注册,并获得某种路由令牌。
3. 移动应用通过客户端/服务器 API 添加一个“推送者(pusher)”,并提供为该应用配置的特定推送网关的 URL,同时提供从推送供应商获得的路由令牌。
4. 主服务器使用提供的 URL 向推送网关发送 HTTP 请求。推送网关将该通知中继给推送供应商,并附带发送推送通知所需的路由令牌及相关私密凭证。
5. 推送供应商将通知发送至设备。
本节相关术语定义如下:
推送供应商(Push Provider)
: 推送供应商是由设备厂商管理的服务,可以直接将通知发送到设备。例如 Google Cloud Messaging(GCM)和 Apple Push Notification Service(APNS)都是推送供应商的例子。
推送网关(Push Gateway)
: 推送网关是接收主服务器 HTTP 事件通知并将其转发给其他协议(如 iOS 设备的 APNS 或 Android 设备的 GCM)的服务器。客户端在设置 Pusher 时,会告知主服务器该将通知发送至哪个推送网关。
推送者(Pusher)
: Pusher 是主服务器上负责管理并发送 HTTP 通知给用户的工作进程。每个用户可以有多个 pusher,每组装置对应一个。
推送规则(Push Rule)
: 推送规则是一条声明了在什么*条件*下事件会被发送到推送网关,以及通知应该如何呈现的规则。这些规则储存在用户的主服务器上。用户可通过客户端/服务器 API 手动配置、创建与查看这些规则。
推送规则集(Push Ruleset)
: 推送规则集*根据某些标准限定某组规则的作用范围*。比如,某些规则只针对来自特定发信人的消息、特定聊天室、或作为默认规则。推送规则集包含了全部作用域与规则集合。
#### 推送规则
推送规则是一条声明了在什么*条件*下事件会被发送到推送网关,以及通知该如何呈现的规则。推送规则有多种“类型(kind)”,每条规则都有相应的优先级。每条推送规则必须包含 `kind` 和 `rule_id`。`rule_id` 是在该类型和作用域内部唯一的字符串:同种类型但属于不同设备的规则之间,`rule_id` 并不要求全局唯一。依据 `kind` 类型,规则可以具有额外的键。
不同的 `kind` 类型按如下顺序检查:
1. **Override 规则 (`override`)。**
最高优先级的规则,由用户配置作为覆盖项。
2. **内容专属规则 (`content`)。**
针对匹配某些模式的消息配置行为。内容规则包含一个参数 —— `pattern`,提供待匹配的[glob 风格模式](/appendices#glob-style-matching)。
该匹配大小写不敏感,必须匹配消息内容的 `content.body` 属性中任何以单词边界起止的子串。单词边界指文本的起始或结尾,或者非 `[A-Z]`, `[a-z]`, `[0-9]`, `_` 集合中的任意字符。大小写不敏感的具体实现由主服务器定义。
3. **房间专属规则 (`room`)。**
更改某个房间内所有消息的通知行为。房间规则的 `rule_id` 总为其作用房间的 ID。
4. **发信人专属规则 (`sender`)。**
针对某个 Matrix 用户 ID 发出的消息配置通知行为。发信人规则的 `rule_id` 总为该用户的 Matrix ID。
5. **Underride 规则 (`underride`)。**
与 `override` 规则作用相同,但其优先级低于 `content`、`room` 和 `sender` 规则。
同一 `kind` 的规则可以指定顺序优先级,用于确定在多条规则命中时选择哪一条。例如,规则 A 匹配“tea”,规则 B 匹配“time”,则消息 “It's time for tea” 会被两条规则匹配,随后按顺序决定实际生效的规则。只有优先级最高的规则的 `actions` 会被发送给推送网关。
每条规则可以启用或禁用。被禁用的规则永不匹配。如果没有任何规则匹配某事件,主服务器不得为该事件通知推送网关。主服务器也不得为用户自己发送的事件通知推送网关。
##### 动作(Actions)
所有规则都有一个关联的 `actions` 动作列表。动作决定对于匹配的事件,通知是否被送达及如何送达。定义如下:
`notify`
: 为每个匹配事件产生推送通知。
`set_tweak`
: 设置发送通知请求给推送网关时 `tweaks` 字典键中的一项。格式为字典对象,`set_tweak` 为要设置的调整项名,如果需要还可带有 `value` 指定其值。
已定义以下调整项:
`sound`
: 字符串,表示此通知到达时播放的声音。`default` 表示播放默认提示音。设备也可根据实际选择如振动等其他告警方式。
`highlight`
: 布尔值,是否应在界面中高亮展示这条消息。通常会以不同颜色/样式突出消息,或调整 UI 以特别提示发生消息的房间。如果给定了没有显式值的 `highlight` 调整项,其值视为 `true`。若未指定则为 `false`。
调整项会透明地通过主服务器传递,客户端应用与推送网关可约定自定义调整项。例如,可指定在移动设备上如何闪烁通知灯。
无参数的动作用字符串表示,否则用以动作名为键、相关参数为其他键的字典表示,例如:`{ "set_tweak": "sound", "value": "default" }`。
###### 历史动作说明
早期 Matrix 规范包含 `dont_notify` 和 `coalesce` 动作。客户端和主服务器必须忽略这些动作,比如遇到时应从动作数组中剔除。因此,包含 `["dont_notify"]` 的规则应等效于动作数组为空的规则。
##### 条件(Conditions)
`override` 和 `underride` 规则可以有“条件”列表。事件必须满足所有条件,该规则才会匹配。若规则无任何条件,则总是匹配。
无法识别的条件不得匹配任何事件,相当于禁用该规则。
`room`、`sender` 和 `content` 规则不以条件列表的方式定义,而是有预定义条件。房间和发信人规则中,`rule_id` 的内容决定其行为。
以下条件类型已定义:
**`event_match`**
对事件某属性用 glob 模式匹配。参数:
- `key`:事件属性的[点分路径](/appendices#dot-separated-property-paths),如 `content.body`。
- `pattern`:[glob 风格模式](/appendices#glob-style-matching)。
匹配大小写不敏感,必须匹配 `key` 指定属性的整个值(但 `content.body` 见下文)。大小写不敏感的实现由主服务器定义。
如事件中 `key` 指定属性完全不存在或不是字符串,即便 `pattern` 是 `*`,条件也不匹配。
{{% boxes/note %}}
例如,若 `key` 为 `content.topic`,`pattern` 为 `lunc?*`,其事件如下将会匹配:
```json
{
"content": {
"topic": "Lunch plans",
},
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"state_key": "",
"type": "m.room.topic"
}
```
其它能匹配的 `topic` 值有:
* `"LUNCH"`(大小写不敏感,`*` 可匹配零个字符)
以下 `topic` 不匹配:
* `" lunch"`(前导空格)
* `"lunc"`(`?` 必须匹配一个字符)
* `null`(不是字符串)
{{% /boxes/note %}}
特殊情况:若 `key` 为 `content.body`,`pattern` 必须匹配属性值任一单词边界起止的子串。单词边界指值的起止或非 `[A-Z]`、`[a-z]`、`[0-9]`、`_` 集合的字符。
{{% boxes/note %}}
例如,`key` 若为 `content.body`,`pattern` 为 `ex*ple`,则如下消息会匹配:
```json
{
"content": {
"body": "An example event."
},
"event_id": "$143273976499sgjks:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"type": "m.room.message"
}
```
其它匹配 `body` 值有:
* `"exple"`(模式可匹配 body 起止)
* `"An exciting triple-whammy"`(模式可跨多单词,且 `-` 被视为分隔符)
{{% /boxes/note %}}
{{% boxes/warning %}}
注意,`state_key` 没有默认隐式条件。即,针对只匹配状态事件的推送规则,必须明确指定对 `state_key` 的条件。
例如,见下方默认规则
[`.m.rule.tombstone`](#mruletombstone)。
{{% /boxes/warning %}}
**`event_property_is`**
对事件属性值进行精确匹配。参数:
- `key`:事件属性的[点分路径](/appendices#dot-separated-property-paths),如 `content.body`。
- `value`:要匹配的值。
匹配为精确等值,仅支持非复合(canonical JSON)类型:字符串、区间 `[-(2**53)+1, (2**53)-1]` 内的整数、布尔值和 `null`。
如 `key` 指定属性不存在,或类型不是字符串、整数、布尔或 `null`,条件不匹配。
{{% boxes/note %}}
例如,若 `key` 为 `content.m\.federate`,`value` 为 `true`,事件如下匹配:
```json
{
"content": {
"creator": "@example:example.org",
"m.federate": true,
"predecessor": {
"event_id": "$something:example.org",
"room_id": "!oldroom:example.org"
},
"room_version": "1"
},
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"state_key": "",
"type": "m.room.create"
}
```
下列 `m.federate` 值不匹配:
* `"true"`(类型不同,字符串)
* `1`(不进行类型转换)
{{% /boxes/note %}}
**`event_property_contains`**
如事件某数组属性*精确包含*特定值则匹配。参数:
- `key`:事件属性的[点分路径](/appendices#dot-separated-property-paths),如 `content.body`。
- `value`:要匹配的值。
仅当数组元素为非复合 canonical JSON 类型(字符串、上述区间内整数、布尔、`null`)时有效。其他类型将被忽略。
如 `key` 指定属性不存在,或不是数组,则条件不匹配。
{{% boxes/note %}}
例如,若 `key` 为 `content.alt_aliases`,`value` 为 `"#myroom:example.com"`,如下事件会匹配:
```json
{
"content": {
"alias": "#somewhere:localhost",
"alt_aliases": [
"#somewhere:example.org",
"#myroom:example.com"
]
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "",
"type": "m.room.canonical_alias",
"unsigned": {
"age": 1234
}
}
```
下列 `alt_aliases` 值不匹配:
* `":example.com"`(部分匹配不通过)
{{% /boxes/note %}}
**`contains_display_name`**
匹配 `content.body` 含有房间内该用户当前显示昵称的消息。因显示昵称可能变化,维护对应规则较难,故单独为此设一条件。该条件无参数。
**`room_member_count`**
匹配房间当前成员数。参数:
- `is`:一个支持可选前缀的十进制整数,前缀包括 `==`、`<`、`>`、`>=` 或 `<=` 。如 `<` 匹配成员数*小于*给定数字,依此类推。缺省无前缀时为 `==`。
**`sender_notification_permission`**
结合房内当前权限设置,确保事件发送者权限足够以触发通知。
参数:
- `key`:字符串,指定需触发何种类型通知时发信人需具备的权限级别,如 `room`。详见
[m.room.power\_levels](#mroompower_levels) 事件结构。该 `key` 用于根据 power level 对象内容查找通知类型对应权限级别。
#### 预定义规则
主服务器可指定“服务器默认规则”。该类规则优先级低于“用户自定义规则”,唯一例外是 `.m.rule.master`,它总是所有规则中最高的。所有服务器默认规则的 `rule_id` 必须以点(".")开头以便区分。以下为规定的服务器默认规则:
##### 默认 Override 规则
**`.m.rule.master`**
匹配所有事件。启用后可关闭所有推送通知。不同于其它服务器默认规则,此规则始终优先级最高,连用户自定义规则都排在其后。默认禁用。
定义:
```json
{
"rule_id": ".m.rule.master",
"default": true,
"enabled": false,
"conditions": [],
"actions": []
}
```
**`.m.rule.suppress_notices`**
匹配 `msgtype` 为 `notice` 的消息。
定义:
```json
{
"rule_id": ".m.rule.suppress_notices",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "content.msgtype",
"pattern": "m.notice"
}
],
"actions": []
}
```
**`.m.rule.invite_for_me`**
匹配针对该用户的新房间邀请。
定义:
```json
{
"rule_id": ".m.rule.invite_for_me",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
},
{
"key": "content.membership",
"kind": "event_match",
"pattern": "invite"
},
{
"key": "state_key",
"kind": "event_match",
"pattern": "[the user's Matrix ID]"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
}
]
}
```
**`.m.rule.member_event`**
匹配任意 `m.room.member_event`。
定义:
```json
{
"rule_id": ".m.rule.member_event",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
}
],
"actions": []
}
```
**`.m.rule.is_user_mention`**
{{% added-in v="1.7" %}}
匹配在 `m.mentions` 属性下 `user_ids` 包含用户 Matrix ID 的任意消息。
定义:
```json
{
"rule_id": ".m.rule.is_user_mention",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_property_contains",
"key": "content.m\\.mentions.user_ids",
"value": "[the user's Matrix ID]"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight"
}
]
}
```
**`.m.rule.contains_display_name`**
{{% changed-in v="1.7" %}}
自 `v1.7` 起,该规则已废弃,**仅在事件未含 [`m.mentions` 属性](#definition-mmentions) 时启用**。
匹配内容含有用户当前房间显示昵称的消息。
定义:
```json
{
"rule_id": ".m.rule.contains_display_name",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "contains_display_name"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight"
}
]
}
```
**`.m.rule.is_room_mention`**
{{% added-in v="1.7" %}}
匹配拥有相应权限、`m.mentions` 属性中的 `room` 字段为 `true` 的发信人消息。
定义:
```json
{
"rule_id": ".m.rule.is_room_mention",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_property_is",
"key": "content.m\\.mentions.room",
"value": true
},
{
"kind": "sender_notification_permission",
"key": "room"
}
],
"actions": [
"notify",
{
"set_tweak": "highlight"
}
]
}
```
**`.m.rule.roomnotif`**
{{% changed-in v="1.7" %}}
自 v1.7 起,该规则已废弃,**仅在事件未含 [`m.mentions` 属性](#definition-mmentions) 时启用**。
匹配拥有权限且内容含有 `@room` 字符串(需全房通知)的消息。
定义:
```json
{
"rule_id": ".m.rule.roomnotif",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "content.body",
"pattern": "@room"
},
{
"kind": "sender_notification_permission",
"key": "room"
}
],
"actions": [
"notify",
{
"set_tweak": "highlight"
}
]
}
```
**`.m.rule.tombstone`**
匹配所有类型为 `m.room.tombstone` 的状态事件。用于通知房间升级,效果类似于 `@room` 通知。
定义:
```json
{
"rule_id": ".m.rule.tombstone",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.tombstone"
},
{
"kind": "event_match",
"key": "state_key",
"pattern": ""
}
],
"actions": [
"notify",
{
"set_tweak": "highlight"
}
]
}
```
**`.m.rule.reaction`**
{{% added-in v="1.7" %}}
匹配所有类型为 `m.reaction` 的事件。用于抑制 [`m.reaction`](#mreaction) 事件的通知。
定义:
```json
{
"rule_id": ".m.rule.reaction",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.reaction"
}
],
"actions": []
}
```
**`.m.rule.room.server_acl`**
{{% added-in v="1.4" %}}
抑制对 [`m.room.server_acl`](#mroomserver_acl) 事件的通知。
定义:
```json
{
"rule_id": ".m.rule.room.server_acl",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.server_acl"
},
{
"kind": "event_match",
"key": "state_key",
"pattern": ""
}
],
"actions": []
}
```
**`.m.rule.suppress_edits`**
{{% added-in v="1.9" %}}
抑制与[事件替换](#event-replacements)相关的通知。
定义:
```json
{
"rule_id": ".m.rule.suppress_edits",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_property_is",
"key": "content.m\\.relates_to.rel_type",
"value": "m.replace"
}
],
"actions": []
}
```
##### 默认 Content 规则
**`.m.rule.contains_user_name`**
{{% changed-in v="1.7" %}}
自 v1.7 起,该规则已废弃,**仅当事件无 [`m.mentions` 属性](#definition-mmentions) 时启用**。
匹配内容包含用户 Matrix ID 本地部分(以单词边界分隔)的消息。
定义(作为 `content` 规则):
```json
{
"rule_id": ".m.rule.contains_user_name",
"default": true,
"enabled": true,
"pattern": "[the local part of the user's Matrix ID]",
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight"
}
]
}
```
##### 默认 Underride 规则
**`.m.rule.call`**
匹配所有传入 VOIP 呼叫事件。
定义:
```json
{
"rule_id": ".m.rule.call",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.call.invite"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "ring"
}
]
}
```
**`.m.rule.encrypted_room_one_to_one`**
匹配发送至仅有两名成员的加密房间内的任意加密事件。不同于普通推送规则,加密后事件无法基于内容匹配,故此规则表现为“全匹配”或“不匹配” —— 若为 1:1 房间内所有加密事件都匹配,否则全不匹配。
定义:
```json
{
"rule_id": ".m.rule.encrypted_room_one_to_one",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "room_member_count",
"is": "2"
},
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.encrypted"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
}
]
}
```
**`.m.rule.room_one_to_one`**
匹配仅有两名成员的房间内任意消息。
定义:
```json
{
"rule_id": ".m.rule.room_one_to_one",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "room_member_count",
"is": "2"
},
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.message"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
}
]
}
```
**`.m.rule.message`**
匹配所有聊天消息。
定义:
```json
{
"rule_id": ".m.rule.message",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.message"
}
],
"actions": [
"notify"
]
}
```
**`.m.rule.encrypted`**
匹配所有加密事件。由于加密,事件内容不能被常规匹配,本规则表现为组房内所有加密事件全匹配或全不匹配。
定义:
```json
{
"rule_id": ".m.rule.encrypted",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.encrypted"
}
],
"actions": [
"notify"
]
}
```
#### 推送规则:API
客户端可通过以下 API 全局或针对各设备获取、添加、修改、删除推送规则。
{{% http-api spec="client-server" api="pushrules" %}}
#### 推送规则:事件
当用户更改推送规则,会向所有客户端在下次 [`/sync`](#get_matrixclientv3sync) 请求的 `account_data` 部分发送 `m.push_rules` 事件。
事件内容为用户当前的全部推送规则。
{{% event event="m.push_rules" %}}
##### 示例
为 ID 为 `!dj234r78wl45Gh4D:matrix.org` 的房间创建禁止通知的规则:
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \
'{
"actions" : []
}'
禁止为用户名为 `@spambot:matrix.org` 的用户发送通知:
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \
'{
"actions" : []
}'
为所有消息内容含 “cake” 的消息始终通知,并设置专用提示音(规则 ID 为 `SSByZWFsbHkgbGlrZSBjYWtl`):
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \
'{
"pattern": "cake",
"actions" : ["notify", {"set_tweak":"sound", "value":"cakealarm.wav"}]
}'
添加规则:禁止通知以 “cake” 开头、“lie” 结尾的消息,并优先级高于上一条规则:
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \
'{
"pattern": "cake*lie",
"actions" : ["notify"]
}'
为成员数不超过 10 的房间内含有 “beer” 的消息添加自定义通知音(优先级高于 room, sender, content 规则):
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/v3/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \
'{
"conditions": [
{"kind": "event_match", "key": "content.body", "pattern": "beer" },
{"kind": "room_member_count", "is": "<=10"}
],
"actions" : [
"notify",
{"set_tweak":"sound", "value":"beeroclock.wav"}
]
}'
#### 客户端行为
客户端必须先配置 Pusher,才能接收推送通知。具体 API 如下所述。
{{% http-api spec="client-server" api="pusher" %}}
##### 列出通知
客户端可获取已收到通知的事件列表,帮助用户查看收到的重要消息摘要。
{{% http-api spec="client-server" api="notifications" %}}
##### 接收通知
服务器必须在客户端的 [`/sync`](#get_matrixclientv3sync) 流中包含未读通知数,并随计数变化更新。通知判定依赖于事件适用的推送规则。
对于加密事件,主服务器仅有限访问事件内容,推送规则需由客户端在*解密后*处理,客户端收到每条事件都需执行推送规则。这可能导致需修正从主服务器收到的未读通知数。
##### 标记通知为已读
当用户更新已读回执(无论使用 API 还是通过发送事件),在该事件之前(含自身)的通知必须被标记为已读。具体被影响的事件取决于是否使用了[线程已读回执](#threaded-read-receipts)。用户可同时发送 `m.read` 和 `m.read.private` 回执,两者都可清除通知。
如同一房间内用户同时拥有 `m.read` 和 `m.read.private`,应取更“新”或“更靠前”的回执为判断依据。例如,若事件 A、B、C、D 按时间升序排列,`m.read` 回执在事件 C,`m.read.private` 在事件 A,则用户已读至 C。如果 `m.read.private` 更新为 B 或 C,通知状态不变(`m.read` 回执仍领先);如 `m.read.private` 更新为 D,则用户已读到 D(`m.read` 落后于 `m.read.private`)。
{{% added-in v="1.4" %}} 处理线程已读回执时,服务器需将通知数分配至各线程(主时间线看作一条线程)。判定事件属于哪个线程,服务器应按[event 关系](#forming-relationships-between-events)查找,直到遇到 `m.thread` 关系定义的根事件(详见[线程模块](#threading)),但不建议无限遍历。建议实现时至多跳 3 层,未找到线程即视为事件不属于线程。主要确保后续事件如 `m.reaction` 被正确划分为某线程。
#### 服务器行为
主服务器收到新事件时,为房间内每位本地用户(不含发信人)处理推送规则,结果可能为:
* 生成新的未读通知数;
* 向配置的推送网关发起请求。
新事件导致的未读通知数必须与事件本身一起在同一次 [`/sync`](#get_matrixclientv3sync) 响应中返回。
#### 推送网关行为
##### APNS 推荐
APNS 推送通知的具体格式灵活,由客户端应用与其推送网关约定。由于 APNS 需求发送者具有应用开发者的私钥,每个应用需有专属推送网关。推荐如下:
- APNS Token 基于 base64 编码,直接用于 pushkey。
- 正式环境和沙箱环境使用不同的 app_id 。
- APNS 推送网关不必等待 APNS 网关返回错误再响应;可记录失败,并在下次推送相同 pushkey 时返回 'rejected'。
#### 安全性注意事项
客户端需指定用于发送事件通知的推送网关 URL 。此 URL 必须为 HTTPS,*绝不能*是 HTTP。
推送通知会经过推送供应商,消息内容应尽量不随推送本体一起发送。推送网关应发送 "sync" 指令,指导客户端直接向主服务器获取新事件。