29 KiB
推送通知
+--------------------+ +-------------------+
Matrix HTTP | | | |
Notification Protocol | 应用开发者 | | 设备厂商 |
| | | |
+-------------------+ | +----------------+ | | +---------------+ |
| | | | | | | | | |
| Matrix 主服务器 +-----> 推送网关(Gateway)+------> 推送供应商 | |
| | | | | | | | | |
+-^-----------------+ | +----------------+ | | +----+----------+ |
| | | | | |
Matrix | | | | | |
客户端/服务器API + | | | | |
| | +--------------------+ +-------------------+
| +--+-+ |
| | <-------------------------------------------+
+---+ |
| | 供应商推送协议
+----+
移动设备或客户端
本模块增加了对推送通知的支持。主服务器会将事件的通知发送到用户配置的 HTTP 端点。用户也可以自定义多条规则,决定哪些事件会生成通知。这些规则全部存储并由用户的主服务器管理。这允许用户专属的推送设置在不同客户端应用之间复用。
上述图示显示了推送通知发送到手机时的流程,通知通过手机厂商(例如 Apple 的 APNS 或 Google 的 GCM)进行提交。具体流程如下:
- 客户端应用登录到主服务器。
- 客户端应用在其厂商的推送供应商处注册,并获得某种路由令牌。
- 移动应用通过客户端/服务器 API 添加一个“推送者(pusher)”,并提供为该应用配置的特定推送网关的 URL,同时提供从推送供应商获得的路由令牌。
- 主服务器使用提供的 URL 向推送网关发送 HTTP 请求。推送网关将该通知中继给推送供应商,并附带发送推送通知所需的路由令牌及相关私密凭证。
- 推送供应商将通知发送至设备。
本节相关术语定义如下:
- 推送供应商(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
类型按如下顺序检查:
-
Override 规则 (
override
)。 最高优先级的规则,由用户配置作为覆盖项。 -
内容专属规则 (
content
)。 针对匹配某些模式的消息配置行为。内容规则包含一个参数 ——pattern
,提供待匹配的glob 风格模式。 该匹配大小写不敏感,必须匹配消息内容的content.body
属性中任何以单词边界起止的子串。单词边界指文本的起始或结尾,或者非[A-Z]
,[a-z]
,[0-9]
,_
集合中的任意字符。大小写不敏感的具体实现由主服务器定义。 -
房间专属规则 (
room
)。 更改某个房间内所有消息的通知行为。房间规则的rule_id
总为其作用房间的 ID。 -
发信人专属规则 (
sender
)。 针对某个 Matrix 用户 ID 发出的消息配置通知行为。发信人规则的rule_id
总为该用户的 Matrix ID。 -
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
指定属性的整个值(但 content.body
见下文)。大小写不敏感的实现由主服务器定义。
如事件中 key
指定属性完全不存在或不是字符串,即便 pattern
是 *
,条件也不匹配。
{{% boxes/note %}}
例如,若 key
为 content.topic
,pattern
为 lunc?*
,其事件如下将会匹配:
{
"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
,则如下消息会匹配:
{
"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
。
{{% /boxes/warning %}}
event_property_is
对事件属性值进行精确匹配。参数:
key
:事件属性的点分路径,如content.body
。value
:要匹配的值。
匹配为精确等值,仅支持非复合(canonical JSON)类型:字符串、区间 [-(2**53)+1, (2**53)-1]
内的整数、布尔值和 null
。
如 key
指定属性不存在,或类型不是字符串、整数、布尔或 null
,条件不匹配。
{{% boxes/note %}}
例如,若 key
为 content.m\.federate
,value
为 true
,事件如下匹配:
{
"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
:事件属性的点分路径,如content.body
。 -
value
:要匹配的值。
仅当数组元素为非复合 canonical JSON 类型(字符串、上述区间内整数、布尔、null
)时有效。其他类型将被忽略。
如 key
指定属性不存在,或不是数组,则条件不匹配。
{{% boxes/note %}}
例如,若 key
为 content.alt_aliases
,value
为 "#myroom:example.com"
,如下事件会匹配:
{
"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 事件结构。该key
用于根据 power level 对象内容查找通知类型对应权限级别。
预定义规则
主服务器可指定“服务器默认规则”。该类规则优先级低于“用户自定义规则”,唯一例外是 .m.rule.master
,它总是所有规则中最高的。所有服务器默认规则的 rule_id
必须以点(".")开头以便区分。以下为规定的服务器默认规则:
默认 Override 规则
.m.rule.master
匹配所有事件。启用后可关闭所有推送通知。不同于其它服务器默认规则,此规则始终优先级最高,连用户自定义规则都排在其后。默认禁用。
定义:
{
"rule_id": ".m.rule.master",
"default": true,
"enabled": false,
"conditions": [],
"actions": []
}
.m.rule.suppress_notices
匹配 msgtype
为 notice
的消息。
定义:
{
"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
匹配针对该用户的新房间邀请。
定义:
{
"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
。
定义:
{
"rule_id": ".m.rule.member_event",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
}
],
"actions": []
}
{{% added-in v="1.7" %}}
匹配在 m.mentions
属性下 user_ids
包含用户 Matrix ID 的任意消息。
定义:
{
"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"
}
]
}
{{% changed-in v="1.7" %}}
自 v1.7
起,该规则已废弃,仅在事件未含 m.mentions
属性 时启用。
匹配内容含有用户当前房间显示昵称的消息。
定义:
{
"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"
}
]
}
{{% added-in v="1.7" %}}
匹配拥有相应权限、m.mentions
属性中的 room
字段为 true
的发信人消息。
定义:
{
"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"
}
]
}
{{% changed-in v="1.7" %}}
自 v1.7 起,该规则已废弃,仅在事件未含 m.mentions
属性 时启用。
匹配拥有权限且内容含有 @room
字符串(需全房通知)的消息。
定义:
{
"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.room.tombstone
的状态事件。用于通知房间升级,效果类似于 @room
通知。
定义:
{
"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"
}
]
}
{{% added-in v="1.7" %}}
匹配所有类型为 m.reaction
的事件。用于抑制 m.reaction
事件的通知。
定义:
{
"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
事件的通知。
定义:
{
"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" %}}
抑制与事件替换相关的通知。
定义:
{
"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 规则
{{% changed-in v="1.7" %}}
自 v1.7 起,该规则已废弃,仅当事件无 m.mentions
属性 时启用。
匹配内容包含用户 Matrix ID 本地部分(以单词边界分隔)的消息。
定义(作为 content
规则):
{
"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 呼叫事件。
定义:
{
"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 房间内所有加密事件都匹配,否则全不匹配。
定义:
{
"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
匹配仅有两名成员的房间内任意消息。
定义:
{
"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
匹配所有聊天消息。
定义:
{
"rule_id": ".m.rule.message",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.message"
}
],
"actions": [
"notify"
]
}
.m.rule.encrypted
匹配所有加密事件。由于加密,事件内容不能被常规匹配,本规则表现为组房内所有加密事件全匹配或全不匹配。
定义:
{
"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
请求的 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
流中包含未读通知数,并随计数变化更新。通知判定依赖于事件适用的推送规则。
对于加密事件,主服务器仅有限访问事件内容,推送规则需由客户端在解密后处理,客户端收到每条事件都需执行推送规则。这可能导致需修正从主服务器收到的未读通知数。
标记通知为已读
当用户更新已读回执(无论使用 API 还是通过发送事件),在该事件之前(含自身)的通知必须被标记为已读。具体被影响的事件取决于是否使用了线程已读回执。用户可同时发送 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 关系查找,直到遇到 m.thread
关系定义的根事件(详见线程模块),但不建议无限遍历。建议实现时至多跳 3 层,未找到线程即视为事件不属于线程。主要确保后续事件如 m.reaction
被正确划分为某线程。
服务器行为
主服务器收到新事件时,为房间内每位本地用户(不含发信人)处理推送规则,结果可能为:
- 生成新的未读通知数;
- 向配置的推送网关发起请求。
新事件导致的未读通知数必须与事件本身一起在同一次 /sync
响应中返回。
推送网关行为
APNS 推荐
APNS 推送通知的具体格式灵活,由客户端应用与其推送网关约定。由于 APNS 需求发送者具有应用开发者的私钥,每个应用需有专属推送网关。推荐如下:
- APNS Token 基于 base64 编码,直接用于 pushkey。
- 正式环境和沙箱环境使用不同的 app_id 。
- APNS 推送网关不必等待 APNS 网关返回错误再响应;可记录失败,并在下次推送相同 pushkey 时返回 'rejected'。
安全性注意事项
客户端需指定用于发送事件通知的推送网关 URL 。此 URL 必须为 HTTPS,绝不能是 HTTP。
推送通知会经过推送供应商,消息内容应尽量不随推送本体一起发送。推送网关应发送 "sync" 指令,指导客户端直接向主服务器获取新事件。