169 lines
No EOL
6.6 KiB
Markdown
169 lines
No EOL
6.6 KiB
Markdown
### 线程
|
||
|
||
{{% added-in v="1.4" %}}
|
||
|
||
线程允许用户在一个房间中以可视方式分支他们的对话。通常在线上讨论多个主题时使用,线程相较于传统的 [富回复](#rich-replies) 能够提供更有组织的交流方式,而富回复未必能够兼顾所有场景。
|
||
|
||
客户端应当在时间线上以区别于普通消息或回复的方式渲染线程,例如为线程提供一些上下文信息,但将完整的对话历史隐藏于可展开内容之后。
|
||
|
||
线程通过 `rel_type` 设为 `m.thread` 来建立,并引用 *线程根*(即该线程事件所指向的主时间线事件)。无法从本身已是事件关系子事件(即带有 `m.relates_to` 和 `rel_type` 属性的事件,参考 [关系类型](#relationship-types))创建线程。因此,线程也无法嵌套。
|
||
|
||
与富回复链不同,线程中的所有事件都引用线程根,而不是最新的消息。
|
||
|
||
下面通过示例展示线程及其形成方式:
|
||
|
||
```json
|
||
{
|
||
// 已省略无关字段
|
||
"type": "m.room.message",
|
||
"event_id": "$alice_hello",
|
||
"sender": "@alice:example.org",
|
||
"content": {
|
||
"msgtype": "m.text",
|
||
"body": "Hello world! How are you?"
|
||
}
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
// 已省略无关字段
|
||
"type": "m.room.message",
|
||
"event_id": "$bob_hello",
|
||
"sender": "@bob:example.org",
|
||
"content": {
|
||
"m.relates_to": {
|
||
"rel_type": "m.thread",
|
||
"event_id": "$alice_hello"
|
||
},
|
||
"msgtype": "m.text",
|
||
"body": "I'm doing okay, thank you! How about yourself?"
|
||
}
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
// 已省略无关字段
|
||
"type": "m.room.message",
|
||
"event_id": "$alice_reply",
|
||
"sender": "@alice:example.org",
|
||
"content": {
|
||
"m.relates_to": {
|
||
"rel_type": "m.thread",
|
||
"event_id": "$alice_hello" // 注意:始终指向 *线程根*
|
||
},
|
||
"msgtype": "m.text",
|
||
"body": "I'm doing great! Thanks for asking."
|
||
}
|
||
}
|
||
```
|
||
|
||
如上所示,任何没有 `rel_type` 的事件都可以仅通过 `m.thread` 关系被引用来成为线程根。
|
||
|
||
#### 非线程化客户端的回退机制
|
||
|
||
能够理解线程的客户端应直接以线程方式处理,但某些客户端(出于历史原因或功能范围限制)可能无法向用户有效展现对话历史。
|
||
|
||
为此,支持线程的客户端发送事件时应包含 [富回复](#rich-replies) 元数据,以尝试形成对话的回复链。这种方式在高线程活跃房间中并不理想,但可以为用户提供与房间内其他消息相关的上下文信息。
|
||
|
||
该兼容方式通过合并两种关系并为 `is_falling_back` 标记设为 `true` 实现。
|
||
|
||
```json
|
||
// 在事件内容中……
|
||
"m.relates_to": {
|
||
// m.thread 关系结构
|
||
"rel_type": "m.thread",
|
||
"event_id": "$root",
|
||
|
||
// 富回复结构
|
||
"m.in_reply_to": {
|
||
// 线程中客户端已知的最新消息,应选取其他客户端有较大渲染概率的事件,
|
||
// 如 `m.room.message` 事件。
|
||
"event_id": "$target"
|
||
},
|
||
|
||
// 标记此事件为带回复回退的线程
|
||
"is_falling_back": true
|
||
}
|
||
```
|
||
|
||
{{% boxes/note %}}
|
||
对于对线程有一定感知(即不直接渲染线程、但知道规范中有该功能)的客户端,可以将对带有 `rel_type` 为 `m.thread` 事件的富回复视作线程内部的回复,以实现线程客户端侧的对话连续性。
|
||
|
||
实现方法为:从被回复事件中复制出 `event_id`(线程根),添加 `m.in_reply_to` 元数据,并在 `m.relates_to` 中加入 `is_falling_back: true`。
|
||
{{% /boxes/note %}}
|
||
|
||
#### 线程内的回复
|
||
|
||
在 [非线程化客户端的回退机制](#fallback-for-unthreaded-clients) 部分,为 `m.relates_to` 新增了 `is_falling_back` 字段。当未提供该字段时,默认为 `false`,这同样允许线程消息本身作为回复。
|
||
|
||
除了 `is_falling_back` 为 `false`(或未指定)以外,客户端应利用非线程化客户端的回退机制在线程内创建回复,并据此渲染事件。
|
||
|
||
#### 服务器行为
|
||
|
||
##### `m.thread` 关系的验证
|
||
|
||
服务器应拒绝客户端针对带有 `m.relates_to` 属性的事件尝试发起线程的请求。如果客户端试图对带有 `m.relates_to` 属性的事件作为目标事件,则应返回 HTTP 400 错误及相应错误信息,按照 [标准错误响应](#standard-error-response) 结构处理。
|
||
|
||
{{% boxes/note %}}
|
||
此种情况目前没有单独的错误码:服务器应与 HTTP 400 一同返回 `M_UNKNOWN`。
|
||
{{% /boxes/note %}}
|
||
|
||
##### 服务器侧对 `m.thread` 关系的聚合
|
||
|
||
由于线程总是引用线程根,一个事件将拥有多个“子事件”,共同组成该线程。服务器应对这些事件进行 [聚合](#aggregations-of-child-events)。
|
||
|
||
线程聚合的数据包括用户在该线程中的参与情况、线程(服务器已知范围内)大致的事件数量,以及线程内最新(按服务器视角的拓扑顺序)的一条消息。
|
||
|
||
与任何其他子事件聚合一样,`m.thread` 聚合结果通过 `unsigned` 下的 `m.relations` 属性返回给线程根。例如:
|
||
|
||
```json
|
||
{
|
||
"event_id": "$root_event",
|
||
// 未显示无关字段
|
||
"unsigned": {
|
||
"m.relations": {
|
||
"m.thread": {
|
||
"latest_event": {
|
||
// 线程中最新事件的序列化副本。
|
||
// 部分字段为简化未示出。
|
||
"event_id": "$message",
|
||
"sender": "@alice:example.org",
|
||
"room_id": "!room:example.org",
|
||
"type": "m.room.message",
|
||
"content": {
|
||
"msgtype": "m.text",
|
||
"body": "Woo! Threads!"
|
||
},
|
||
"unsigned": {
|
||
"m.relations": {
|
||
// ...
|
||
}
|
||
}
|
||
},
|
||
"count": 7,
|
||
"current_user_participated": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
`latest_event` 为线程中由未被[忽略的用户](#ignoring-users)发送,服务器视拓扑顺序最新的一条事件。
|
||
|
||
注意,正如上例,`latest_event` 的子事件本身也需被聚合并包含在该事件下的 `m.relations` 下。服务器需注意避免形成循环,尽管由于 `m.thread` 不允许指向带有 `m.relates_to` 属性的事件,目前不可能产生循环。
|
||
|
||
`count` 仅指向目标事件的 `rel_type` 为 `m.thread` 的事件数量,未包含[被忽略用户](#ignoring-users)发送的事件。
|
||
|
||
`current_user_participated` 为 `true` 时,表明认证用户满足以下任一条件:
|
||
1. 是线程根事件的 `sender`;
|
||
2. 是某个引用线程根且 `rel_type` 为 `m.thread` 的事件的 `sender`。
|
||
|
||
#### 查询房间内线程
|
||
|
||
客户端如需获取某线程内的所有事件,可通过
|
||
[`GET /relations/{threadRootId}/m.thread`](#get_matrixclientv1roomsroomidrelationseventidreltype);
|
||
如需获取某房间内所有线程,则需专用 API:
|
||
|
||
{{% http-api spec="client-server" api="threads_list" %}} |