### 推送通知 ``` +--------------------+ +-------------------+ 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" 指令,指导客户端直接向主服务器获取新事件。