docs-matrix-spec/locales/zh-Hans/client-server-api/modules/instant_messaging.md
2025-04-20 16:13:37 +08:00

18 KiB
Raw Blame History

即时消息

该模块增加了向房间发送易于理解的信息的支持,同时也支持为房间本身关联可读性强的信息,如房间名称和话题。

事件

{{% event event="m.room.message" desired_example_name="m.room.message$m.text" %}}

{{% event event="m.room.name" %}}

{{% event event="m.room.topic" %}}

{{% event event="m.room.avatar" %}}

{{% event event="m.room.pinned_events" %}}

m.room.message 消息类型msgtype

每个 m.room.message 必须包含一个 msgtype 键,用于标识发送消息的类型。不同类型的消息有各自必须和可选的键,具体如下。如果客户端无法显示给定的 msgtype,那么应当显示备用的纯文本 body 字段。

某些消息类型支持事件内容中的 HTML客户端应优先显示可用的 HTML。目前m.textm.emotem.noticem.imagem.filem.audiom.videom.key.verification.request 支持额外的 format 参数 org.matrix.custom.html。当提供该字段时,必须同时提供携带 HTML 的 formatted_body。HTML 的纯文本版本则应存于 body 字段。

{{% boxes/note %}} {{% changed-in v="1.10" %}} 在以往的规范版本中,formatformatted 字段仅限于 m.textm.emotem.notice 以及 m.key.verification.request。现在该列表扩展至 m.imagem.filem.audiom.video 以支持媒体标题。 {{% /boxes/note %}}

为防止跨站脚本攻击XSS、HTML 注入及类似攻击,客户端应限制渲染的 HTML 范围。强烈建议仅允许以下 HTML 标签,其余标签应拒绝使用与渲染:delh1h2h3h4h5h6blockquotepaulolsupsublibiustrongemscodehrbrdivtabletheadtbodytrthtdcaptionprespanimgdetailssummary

{{% boxes/note %}} {{% added-in v="1.10" %}} 当 HTML 功能在 WHATWG HTML Living Standard 标准中被弃用时,可以无需提交 规范变更提案而弃用并用其现代等价替换之。 {{% /boxes/note %}}

{{% boxes/note %}} {{% changed-in v="1.10" %}} 在以往规范中,建议使用 font 标签及其 data-mx-bg-colordata-mx-colorcolor 属性。该标签现已弃用,新的消息推荐使用带有 data-mx-bg-colordata-mx-color 属性的 span 标签替代。 {{% /boxes/note %}}

上述标签的所有属性均不应被允许,因为部分属性可能带来其他干扰性风险,比如添加 onclick 事件或设置过大的文本。客户端仅应允许下表中为各标签列出的属性。其中,data-mx-bg-colordata-mx-color 为列表项时,客户端应将其值(即 # 开头的 6 位十六进制颜色代码)转换为该标签相应的 CSS/属性。

标签 允许的属性
span data-mx-bg-colordata-mx-colordata-mx-spoiler(参见剧透消息)、data-mx-maths(参见数学消息
a targethref(前提是值不是相对路径,且 scheme 为 httpshttpftpmailtomagnet 中之一)
img widthheightalttitlesrc(前提是来源为 Matrix 内容 (mxc://) URI
ol start
code class(仅允许以 language- 开头的 class以便语法高亮
div data-mx-maths(参见数学消息

除此之外Web 客户端应确保所有 a 标签获得 rel="noopener" 属性,以防目标页面获取当前客户端标签页/窗口的引用。

标签嵌套不得超过 100 层。客户端仅应支持其能够渲染的子集标签,对无法渲染的标签采用其他表现方式显示。例如,若客户端无法正确渲染表格,可回退为制表符分隔文本。

除了不渲染不安全的 HTML 外,客户端也不应在事件中生成不安全的 HTML。客户端同样不应生成不必要的 HTML比如由于富文本编辑导致的多余的段落标签。事件中的 HTML 应为有效 HTML例如有适当的闭合标签、正确的属性结合本文档自定义说明且整体结构合法。

{{% boxes/note %}} {{% changed-in v="1.13" %}} 在更早的规范版本中,富回复 可以使用特殊标签 mx-reply。现在不再需要这样做。客户端应去除该标签及其内容。详情请参见“富回复”章节。 {{% /boxes/note %}}

{{% boxes/note %}} 未来的规范会支持更强大且可扩展的消息格式化选项,例如提案 MSC1767。 {{% /boxes/note %}}

{{% msgtypes %}}

客户端行为

客户端应验证收到事件的结构确保所需字段存在且类型正确。对于格式错误的事件可以选择丢弃或向用户显示占位提示消息。被修订redactedm.room.message 事件必须从客户端删除,可以用占位文本(如“[REDACTED]”)替换,或直接从消息视图移除。

带有附件的事件(如 m.imagem.file)应使用内容仓库模块上传(如可用)。所得的 mxc:// URI 可用于 url 字段。

客户端可通过 info.thumbnail_url 字段为附件带上客户端生成的缩略图。该缩略图也应为 mxc:// URI。呈现附带附件的事件时客户端可直接使用缩略图或者通过内容仓库模块请求 homeserver 基于原始附件生成缩略图。

发送消息时的推荐做法

在发送失败时,客户端应使用指数退避算法重试请求,重试时间 T 为一段时间,建议不超过 5 分钟。超时后客户端应停止重试,并将消息标记为“未发送”。用户应能够手动重新发送未发送消息。

用户可能会一次输入并快速发送多条消息。客户端应保持用户发送消息的顺序,这意味着应等待上一请求响应后再发送下一个请求。这可能导致“队头阻塞”。为减轻此影响,应按房间分别使用队列而非全局队列,因为顺序仅在单一房间内有意义,房间间无需严格顺序。

本地回显Local Echo

用户点击“发送”按钮时,消息应立即在消息视图中显示,哪怕消息正在发送中。这一过程称为“本地回显”。客户端应实现本地消息回显。客户端可采用不同展示方式显示尚未被服务器处理的消息。当服务器响应后应移除该特殊格式。

客户端需要能将其发送的消息和从事件流中收到的同一消息进行匹配。从事件流收到的同一消息的回显称为“远程回显”。本地回显和远程回显都要能被识别为相同消息以防止重复显示。理想情况下这一过程对用户透明UI 从本地回显切换为远程回显时不会闪烁。通过使用用于发送事件的事务 ID可减少切换时的闪烁。事务 ID 会作为收到事件时 unsigned 数据中的 transaction_id 字段返回。

如果客户端无法使用事务 ID那么当远程回显在消息发送请求完成之前到达事件流时,很可能会出现闪烁。在这种情况下,事件在消息发送请求完成、客户端获得事件 ID 之前就到了,导致无法将其识别为远程回显。这样客户端在一段时间内(取决于服务器响应速度)会同时显示两条消息。请求完成后,客户端可通过查找重复事件 ID 移除多余事件。

计算用户的显示名

客户端可能希望在成员列表或消息发送时展示房间成员的可读型显示名。然而,不同成员可能出现显示名冲突。显示名在展示给用户前必须唯一化处理,以防止冒充其他用户。

为确保客户端间一致处理,推荐使用如下算法为指定用户计算唯一显示名:

  1. 检查相关用户的 m.room.member 状态事件。
  2. 若该状态事件无 displayname 字段或该字段为 null,则用其原始用户 ID 作为显示名。否则:
  3. m.room.member 事件中的 displayname 在房间中所有 membership: joinmembership: invite 成员里是唯一的,则用该 displayname 作为可见显示名。否则:
  4. displayname 不唯一,应结合用户 ID 做唯一化处理,例如“显示名 (@id:homeserver.org)”。

开发者在实现该算法时需注意:

  • 一名成员的显示名有可能因其他成员状态变化而变化。例如,若 @user1:matrix.org 在房间中显示为 Alice,当 @user2:example.com 也以 Alice 加入该房间时,两名用户都必须使用唯一化后的显示名。相反,若其中一名用户更改显示名致不再冲突,两者又可拥有自己原先的显示名。客户端需注意并确保对受影响成员正确重命名。
  • 房间显示名也可能因成员名单变化而受影响。因为房间名有时基于用户显示名派生(见计算房间显示名)。
  • 若全量遍历成员列表以查重显示名,则会导致 O(N^2) 复杂度,该实现对房间成员众多时很低效。建议客户端维护一个从 displayname 到使用该名成员列表的哈希表,以高效判断是否需唯一化。
随消息同步展示成员信息

客户端可能希望显示发送消息成员的显示名与头像 URL。可通过检查该用户 ID 的 m.room.member 状态事件获取(参见计算用户显示名)。

在用户分页浏览历史记录时,客户端可能希望展示成员的历史显示名与头像 URL。由于分页时会返回旧的 m.room.member 事件,因此可以实现该功能。一般做法是同时维护两组房间状态:旧状态和当前状态。随着新事件到达和/或用户回溯浏览,这两组状态会逐渐分化:新事件更新当前状态,分页事件更新旧状态。当分页事件顺序处理时,旧状态即为消息发送时的房间状态。历史显示名和头像 URL 可由此设置。

计算房间显示名

客户端可能希望显示房间的可读型名称。命名方式有多种选择。为保持不同客户端之间房间命名一致,推荐按照如下算法选择房间名:

  1. 若房间具有 m.room.name 状态事件且其 name 字段非空,则采用该字段给出的名称。
  2. 若房间有 m.room.canonical_alias 状态事件且该 alias 字段有效,则使用之。请注意,客户端在计算房间名时应避免使用 alt_aliases
  3. 如果以上条件都不满足,应根据房间成员组合房间名。客户端应考虑除当前用户外的 m.room.member 事件(定义如下)。
    1. 若房间 m.heroes 数量大于等于 m.joined_member_count + m.invited_member_count - 1,则可利用英雄成员的事件计算用户显示名(必要时唯一化并拼接。比如客户端可选择展示“Alice, Bob以及 Charlie (@charlie:example.org)”作为房间名。客户端可根据用户体验选择限制用于生成房间名的成员数量。
    2. 若英雄成员数少于 m.joined_member_count + m.invited_member_count - 1,且总成员数大于 1则应用英雄成员计算显示名必要时唯一化拼接后加上剩余成员人数。例如“Alice、Bob 及其他 1234 位成员”。
    3. 若成员总数(加入和被邀请之和)小于等于 1表明该成员为唯一成员则依据上述规则显示房间为空。例如“空房间曾为 Alice”、“空房间曾为 Alice 及 1234 位成员)”或无成员时显示“空房间”。

客户端用 m.heroes 计算房间名时应对各国语言进行国际化处理。生成房间名时,客户端应尽量使用不少于 5 名英雄成员,但可根据实际需求调整数量以配合用户体验。

剧透消息

{{% added-in v="1.1" %}}

消息中的部分内容可通过剧透形式在视觉上对用户隐藏。这不影响服务器对事件内容的存储,仅是在视觉上提示用户相关内容可能会暴露重要信息,导致“剧透”。

发送剧透消息时,客户端必须使用 formatted_body,即上文描述的 org.matrix.custom.html 格式。因此,支持剧透的任意 msgtype 都须支持该格式。

剧透内容包裹在 span 标签中,原因(可选)放在 data-mx-spoiler 属性里。若无原因,属性值可留空或未定义,但该属性不能省略。

一个剧透消息示例:

{
  "msgtype": "m.text",
  "format": "org.matrix.custom.html",
  "body": "Alice [剧透](mxc://example.org/abc123) 在电影里。",
  "formatted_body": "Alice <span data-mx-spoiler>最终幸福地生活下去</span> 在电影里。"
}

若提供原因,则如下:

{
  "msgtype": "m.text",
  "format": "org.matrix.custom.html",
  "body": "Alice [健康剧透](mxc://example.org/abc123) 在电影里。",
  "formatted_body": "Alice <span data-mx-spoiler='健康'>最终幸福地生活下去</span> 在电影里。"
}

发送剧透时,客户端应如上示例在 body 字段提供包含原因的备用内容。备用 body 字段不应包含剧透正文,因为 body 可能被文本类客户端或通知直接显示。为防止剧透内容被泄露,强烈推荐客户端首先将剧透正文上传至媒体仓库,然后以 markdown 链接形式引用对应 mxc:// URI如上述示例。

客户端应区别渲染剧透内容,并以某种显式交互提示。例如,可将剧透文本模糊化,提示用户点击后显示。

媒体标题

{{% added-in v="1.10" %}}

媒体消息(包括 m.imagem.filem.audiom.video)可包含题注,以补充说明媒体内容。

发送标题时,客户端必须同时使用 filenamebody 字段,formatted_bodyorg.matrix.custom.html 格式为可选。

如存在 filename 字段,且其与 body 不同,则将 body 视为题注,否则 body 视为文件名。formatformatted_body 仅用于题注。

{{% boxes/note %}} 在旧规范中,body 字段通常用于上传文件名,而 filename 字段仅出现在 m.file 上且用法一致。 {{% /boxes/note %}}

媒体消息附带题注示例:

{
    "msgtype": "m.image",
    "url": "mxc://example.org/abc123",
    "filename": "dog.jpg",
    "body": "这是一张~~猫咪~~照片 :3",
    "format": "org.matrix.custom.html",
    "formatted_body": "这是一张 <s>猫咪</s> 照片 :3",
    "info": {
        "w": 479,
        "h": 640,
        "mimetype": "image/jpeg",
        "size": 27253
    },
    "m.mentions": {}
}

客户端必须与媒体一起渲染标题,并应优先渲染其格式化形式。

数学消息

{{% added-in v="1.11" %}}

用户可能希望在消息中发送数学符号或公式。

发送数学公式时,客户端必须使用 formatted_body,即采用上述 org.matrix.custom.html 格式。任何可用该格式的 msgtype 均可支持数学形式。

数学内容根据是否需要行内显示,使用 spandiv 标签。用 data-mx-maths 属性书写 LaTeX 格式的公式。

标签内容为不能渲染 LaTeX 的客户端备用显示。可用图片、HTML 近似表示或原始 LaTeX 源文本作为备用。若用图片作为备用,发送方应注意接收端可能背景色不同所带来的显示问题。body 字段应包含文本表示的公式。

数学消息示例:

{
  "msgtype": "m.text",
  "format": "org.matrix.custom.html",
  "body": "这是一个方程sin(x)=a/b。",
  "formatted_body": "这是一个方程:<span data-mx-maths=\"\\sin(x)=\\frac{a}{b}\">sin(<i>x</i>)=<sup><i>a</i></sup>/<sub><i>b</i></sub></span>"
}

LaTeX 语法定义不完整且有多种扩展,若客户端遇到无法渲染的语法,应优先显示备用内容。但客户端最低应支持LaTeX2e 的数学命令及 TeX 数学命令(部分命令因安全风险可例外)。

{{% boxes/warning %}} 总的说来LaTeX 给客户端带来了安全处理压力。部分命令(如可创建宏的命令)具潜在风险。客户端应拒绝处理此类命令,或确保安全处理(如限制递归)。客户端应以白名单方式只允许已知安全命令,而非黑名单拒绝已知不安全命令。

因此,客户端在未安全隔离环境下,不应直接调用 LaTeX 编译器渲染数学表达式,因为相关可执行文件并未设计处理不可信输入。有些 LaTeX 渲染库适合,仅允许部分 LaTeX 并限制递归深度。 {{% /boxes/warning %}}

服务器行为

HomeServer 在收到不包含 msgtype 键,或无文本型 body 键的 m.room.message 事件时,应拒绝请求并返回 400 HTTP 状态码。

安全注意事项

使用本模块发送的消息不会加密端到端加密E2E仍在开发中详见 E2E 模块)。

客户端应对所有显示的键进行不安全 HTML 的过滤以防止跨站脚本XSS攻击。这包括房间名称和话题。