> ## Content Index
> Fetch the complete content index at: https://nigzu.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Matrix 评论系统：我为 Cactus Comments 开发了些新功能
- URL: https://nigzu.com/matrix-cactus-comments-new-features-installation-tutorial/
- Published: 2025-06-08T15:01:21.000Z
- Updated: 2025-06-08T17:41:26.000Z
- Author: 宁嘉
- Tags: Matrix, 砖工技术

[Cactus Comments](https://cactus.chat/?ref=nigzu.com "null") 是一款独特的开源评论系统，它利用去中心化通讯协议 [Matrix](https://zh.wikipedia.org/wiki/Matrix%5F%28%E5%8D%8F%E8%AE%AE%29?ref=nigzu.com "null") 作为后端，这赋予了它超越传统评论组件的巨大潜力——无论是实现网站实时聊天、集成 AI 智能客服，还是作为微博客（Microblog）的内容发布渠道，其扩展性在同类开源项目中都难得一见。

然而，原项目已停止更新，我为其注入了新的活力：**不仅修复了原有缺陷，更独立开发了一系列实用新功能**（我的分支目前尚未合并到主干）。

经过持续打磨，这个新版本已经相当完善。本文将为你详细介绍我带来的新特性，并提供一份结合新功能的中文安装教程。你可以在我的[个人博客](https://nigzu.com/ "null")上体验。

> *请注意：为了服务器的稳定，体验前需要注册。因为访客模式可能会对自托管的* Matrix *服务端造成难以预估的压力。*

## 新版 Cactus Comments：都有哪些亮点？

### 🚀 核心升级

- **架构更新**：重构了构建配置和依赖，现已支持在 **Node.js v22+** 环境下进行开发和部署。

### 🖼️ 媒体功能大升级

1. **安全媒体支持**：新增 `isAuthenticated` 选项，当你的 Matrix 服务端启用媒体认证后，评论系统将通过认证端点请求媒体资源，并利用缓存优化加载速度。未认证状态下则沿用原始链接。
2. **三级媒体缓存系统**：为了更好的加载体验，我设计了一套缓存策略。

  - **IndexedDB 持久化缓存**：将媒体链接、文件数据、时间戳和类型存储在浏览器数据库中，并会自动清理超过一天的过期缓存。
  - **智能读取策略**：

    1. **内存缓存优先**：最快响应。
    2. **IndexedDB 次之**：秒开已缓存的媒体。
    3. **网络获取保底**：最后从服务器拉取。

### 💬 完整的消息回复功能

1. **实现引用回复**：现在支持消息间的引用关系，无论是普通回复还是在线程中回复，都能清晰地展示上下文。
2. **动态加载与显示**：对于已加载的引用，会显示其内容摘要；对于未加载的，会先提示“尚未加载”，一旦加载完成便自动更新，体验流畅。
3. **悬停预览与跳转**：

  - **交互优化**：鼠标悬停在引用上时，会立即弹出预览框（支持文本、图片、视频），让你一目了然。
  - **精准跳转**：点击引用，页面会自动滚动到原始消息位置并高亮显示。
4. **编辑器集成**：在编辑器中即可发起引用回复，引用的内容会清晰地显示在编辑框上方。

### 🔒 只读模式

- 新增 `commentEnabled` 配置项（默认为 `true`）。当设置为 `false` 时，评论区将变为只读模式，隐藏所有编辑和发送按钮，适用于内容展示或归档场景。
- **注意**：即便在只读模式下，访客自动注册功能依然是必需的（只在用户客户端首次访问网站的时候），这能确保用户可以正常查看历史消息和访问需要认证的媒体。如果你的网站访问量较大，最好为其增加权限，例如仅会员可见。

### 🌐 全面的多语言支持

- **语言切换**：新增 `language` 配置项，允许你自由设定显示语言。
- **灵活的翻译方案**：使用独立的 JS 或 JSON 文件管理翻译，并支持三种智能回退机制：

  1. 优先使用 `translationsData` 配置项引用的 JS 翻译文件。
  2. 其次尝试加载站点下的 `/language/{lang}.json` 语言文件。
  3. 最后默认显示英文，保证可用性。

### ✨ 前端渲染与体验修复

1. **核心样式修复**：

  - 解决了嵌套 `flex` 布局下元素如代码块溢出的问题。
  - 限制了音频播放器的最大宽度，防止破坏页面布局。
  - 改进了文本换行逻辑，能正确处理长单词和 `<em>` 等标签内的换行。
2. **评论列表性能优化**：

  - **防止重复加载**：在加载过程中，“查看更多”按钮会被禁用，有效避免了并发请求和重复渲染消息。
  - **增量加载**：现在点击“查看更多”只会追加新评论，而不是刷新整个评论区，避免闪烁和媒体播放状态重置。

## 横向对比与功能演示

### 与其他 Matrix 应用对比

我只选取了同样基于 Matrix 协议的嵌入式应用进行比较，以凸显新版 Cactus 的优势。

| 项目                                                                   | 嵌入方式 | 功能支持情况                                                                     | 特点                          | 最后发布日期                   |
| -------------------------------------------------------------------- | ---- | -------------------------------------------------------------------------- | --------------------------- | ------------------------ |
| [Cactus](https://cactus.chat/?ref=nigzu.com)                         | 盒子元素 | 反应 ❌私聊 ❌访客 ✔️登录 ✔️附件上传 ❌媒体显示 ✔️媒体认证(此分支) ✔️回复(此分支) ✔️只读(此分支) ✔️国际化(此分支) ✔️ | 简洁易用功能丰富没有明显的BUG            | 原项目：2022 年 7 月 8此分支：持续更新 |
| [Chatterbox](https://github.com/element-hq/chatterbox?ref=nigzu.com) | 气泡按钮 | 反应 ✔️私聊 ✔️访客(服务号) ✔️登录 ❌附件上传 ✔️媒体显示 ✔️媒体认证 ❌回复❌只读❌国际化❌                     | 支持反应和附件上传使用应用程序服务注册账号而非访客模式 | 2022 年 10 月 17           |
| [Livematrix](https://github.com/osousa/livematrix?ref=nigzu.com)     | 气泡按钮 | 反应 ❌私聊 ✔️访客 ✔️登录 ❌附件上传 ❌媒体显示 ✔️媒体认证 ❌回复❌只读❌国际化❌                            |                             | 2022 年 6 月 6             |

如果用作在网页独立区域展示的评论系统、短内容发布频道，推荐 Cactus Comments ，如果用作客服系统、消息气泡，Chatterbox 也许是更好的选择。

类似的项目还有：

- [hydrogen-web](https://github.com/element-hq/hydrogen-web?ref=nigzu.com)，这是个轻量级的 Matrix web 客户端，有 SDK 可以用来开发嵌入式前端，受限于 hydrogen 自身的实现， 访客、投票、消息列等尚未支持，优点是这是由element.io 组织托管和项目仍在持续更新。
- [Chatrix](https://github.com/Automattic/chatrix?ref=nigzu.com)，功能丰富，只适用于 WordPress。
- [cerulean](https://github.com/matrix-org/cerulean?ref=nigzu.com) 和 [Matrix Live](https://live.hello-matrix.net/?ref=nigzu.com)，将 Matrix 房间发布为微博，功能较为简陋，可被Cactus完美替代。

以上大概就是全部了。

### 功能展示（截图）

1. 图片和AI演示：

![](https://nigzu.com/file/Pasted%20image%2020250521081237.png)

1. 引用和引用预览演示  
![](https://nigzu.com/file/PixPin_2025-06-08_20-48-50.gif)
2. 加载、夜间模式演示  
![](https://nigzu.com/file/PixPin_2025-06-08_21-00-54.gif)

## 安装教程

### 基础准备：引入脚本

无论采用何种部署方式，您都需要先获取项目文件。

1. 前往 [Cactus Comments 的 GitHub 分支项目](https://github.com/code-gal/cactus-client?ref=nigzu.com) 下载最新 release 版本。
2. 将 `cactus.js`、`style.css`、`cactus-lang.js` 三个文件上传到您的服务器或 CDN。
3. 在您网站的 HTML 页面中引入这些文件：

```html
<link rel="stylesheet" href="https://.../.../cactus-style.css" type="text/css">
<script type="text/javascript" src="https://.../.../cactus.js"></script>
<script type="text/javascript" src="https://.../.../cactus-lang.js"></script>

```

### 快速入门：使用官方服务 (无需自建服务端)

#### 步骤 1: 注册站点

与官方服务机器人 `@cactusbot:cactus.chat` 发起私聊，发送以下命令来注册您的站点：

```text
register <YOUR-SITE-NAME>

```

请将 `<YOUR-SITE-NAME>` 替换为您网站的唯一英文名称。

#### 步骤 2: 嵌入评论区

将以下代码嵌入到您网站需要显示评论区的页面。

```html
<!-- 假设这是您网站页面上用来展示评论的描点元素 -->
<div id="comment-section"></div>

<script>
  // 初始化评论区
  initComments({
    // 必填参数
    node: document.getElementById("comment-section"),
    defaultHomeserverUrl: "https://matrix.cactus.chat:8448",
    serverName: "cactus.chat",
    siteName: "<YOUR-SITE-NAME>", // 替换为您注册的站点名称
    commentSectionId: "unique-page-id", // 每个页面的唯一ID，决定了评论所属的Matrix房间

    // 其他可选参数...
  });
</script>

```

- **`siteName`**: 您在上一步注册的站点名称。
- **`commentSectionId`**: 每个页面的唯一标识符。它将用于生成 Matrix 房间的别名。如果希望整个站点的所有页面共享同一个评论区，可以所有页面都使用相同的 ID。
- 房间的消息预览将加载在`id="comment-section"`的评论区。

#### 工作原理

- **访客用户**: 当用户首次访问页面时，系统会自动在 `matrix.cactus.chat` 服务器上为他们注册一个访客账户，并将凭证缓存在浏览器本地。
- **房间创建**: 当第一个访客访问时，服务机器人会自动创建一个公开的 Matrix 房间，别名格式为 `#comments_siteName_commentSectionId:cactus.chat`。
- **房间管理**: 您用于注册站点的 Matrix 账号将成为该房间的管理员，可以修改房间名称、描述等，但不能更改加入和阅读权限。

> **服务提示**: `matrix.cactus.chat` 是一个用于测试和快速体验的站点，不保证长期稳定。若服务中断，旧的访客账号将失效，且无法注册新访客或创建新评论房间。为了长期稳定使用，**强烈建议自托管 Matrix 服务端**。
> 
> 因为房间是联邦的，如果使用联邦管理账号启用联邦别名，在网页上修改`serverName`等参数对应房间别名，可以无缝切换到新的matrix homeserver 来承载这个评论房间，前提是新的服务端支持访客模式，如若需要自动创建新的评论房间，还应该在服务端部署Cactus Comments的应用程序服务，也就是需要有类似`@cactusbot:cactus.chat`的服务机器人。

### 前端自定义

#### 可选的initComments参数：

| 名称                   | 类型          | 必填 | 默认值                | 描述                                                                        |
| -------------------- | ----------- | -- | ------------------ | ------------------------------------------------------------------------- |
| node                 | HTML 元素或字符串 | 是  | \-                 | 页面上将被评论区替换的 HTML 元素。字符串作为选择器传递给 document.querySelector。                   |
| defaultHomeserverUrl | 字符串         | 是  | \-                 | 含有 Cactus Comments 应用服务的 Matrix 主服务器 URL，不包含斜杠结尾。                         |
| serverName           | 字符串         | 是  | \-                 | 含有 Cactus Comments 应用服务的 Matrix 主服务器名称，通常是 defaultHomeserverUrl 的域名部分。    |
| siteName             | 字符串         | 是  | \-                 | 您在 Cactusbot 注册的站点唯一标识符。                                                  |
| commentSectionId     | 字符串         | 是  | \-                 | 评论区的唯一标识符，不能包含斜杠和下划线。                                                     |
| pageSize             | 整数          | 否  | 10                 | 初始加载及每次点击“查看更多”时加载的评论数量。                                                  |
| loginEnabled         | 布尔值         | 否  | true               | 若为 true，用户可使用 Matrix 凭据在页面登录；若为 false，登录按钮变为 Matrix.to 链接，跳转至 Matrix 客户端。 |
| guestPostingEnabled  | 布尔值         | 否  | true               | 若为 true，用户可无需登录以访客身份发表评论。                                                 |
| updateInterval       | 浮点数         | 否  | 0                  | 获取新评论的时间间隔（秒）。若为零或负值，则不会自动获取更新。                                           |
| isAuthenticated      | 布尔值         | 否  | false              | 媒体认证功能                                                                    |
| commentEnabled       | 布尔值         |    | true               | false为评论区只读模式                                                             |
| translationsData     | 字符串         | 否  | cactusTranslations | cactusTranslations值在cactus-lang.js中定义，默认提供多语言版本，也可以自定义（单语言）翻译文件，在这个参数中引用。 |
| language             | 字符串         | 否  | en                 | 符合ISO 639标准的语言代码，如cn、fr、en                                                |

#### 样式自定义

Cactus 前端项目有一个TODO:[样式表](https://github.com/code-gal/cactus-client/blob/mdev/src/style.css?ref=nigzu.com)，元素都可以自定义。

##### 颜色模式

Cactus 前端预设了浅色和深色模式。默认情况下，样式表会根据浏览器选择的配色方案进行调整，因此你不需要做任何事情。如果想明确控制亮/暗模式，可以通过在 `<html>` 元素上设置 `light` 和 `dark` 类来覆盖它。

##### 评论元素顺序调整

1. 颠倒评论列表和编辑器的顺序，将评论列表置于顶部，编辑器置于底部，可以使用以下规则：

```css
.cactus-container {
  flex-direction: column-reverse;
}

```

1. 颠倒评论本身的顺序，将较旧的评论放在顶部，较新的评论放在底部，可以使用以下规则：

```css
.cactus-comments-container {
  flex-direction: column-reverse;
}

.cactus-comments-list {
  flex-direction: column-reverse;
}

```

### 自托管部署（以 Synapse 为例）

#### 步骤1\. 启用访客模式

服务端使用 Synapse，其他Matrix服务端实现参考各自的官方说明。在`homeserver.yaml`配置中启用访客模式：`allow_guest_access: true`，由于当有大量访客时可能会给服务器带来额外负担，配置中有另外几个和guest账号相关的选项建议：

- `turn_allow_guests: false`
- `auto_join_rooms_for_guests: true`
- `send_server_notice_to_guests: false`

此外，如果synapse启用了隐私政策：`user_consent`和其中的`block_events_error`，那么访客首次发送消息时会响应一条 错误信息，其中有包含用户id的隐私政策链接，需要用户手动复制链接打开，同意之后才能发送消息，为避免影响使用体验建议关闭，或者在独立的JS中为用户处理。

如果想对访客作出更多限制（例如自动过期、自动id名称前缀后缀），可以使用这个应用程序模块：[restricted-guests](https://github.com/element-hq/element-modules/tree/main/modules/restricted-guests/synapse?ref=nigzu.com)。

#### 步骤2\. 创建 Cactus Comments 应用程序服务

##### 1\. 创建Appservice 注册文件

```yaml
# 应用服务的唯一自定义ID，永远不会改变。
id: "Cactus Comments"

# cactus-appservice 的托管地址
# 端口是后续安装的cactus-appservice容器映射到外部的端口，需要synapse服务可以访问到
# 例"http://localhost:5000"
url: "http://cactus:5000"

# 用于在我们的服务与主服务器之间（以及反向）认证请求的唯一令牌。请使用某些随机内容的 sha256 哈希值。
# 请更改这些值。
# 使用 `pwgen -syc 64 1` 或 `openssl rand -base64 32` (或更长) 直接生成 token
as_token: "a2d7789eedb3c5076af0864f4af7bef77b1f250ac4e454c373c806876e939cca"
hs_token: "b3b05236568ab46f0d98a978936c514eac93d8f90e6d5cd3895b3db5bb8d788b"

# 与我们的服务关联的用户。在本例中为 "@cactusbot:example.com"
sender_localpart: "cactusbot"

namespaces:
  aliases:
    - exclusive: true
      regex: "#comments_.*"

```

##### 2\. 在 Synapse 中注册 Appservice

将 `cactus.yaml` 文件复制到 Synapse 可以访问的目录，并在 `homeserver.yaml` 中引用它：

```yaml
app_service_config_files:
  # /path/to/ synapse的内部目录
  - "/path/to/cactus.yaml"

```

##### 3\. 启用旧版 Appservice 授权 (重要)

Cactus Comments 的 Appservice 依赖一个较旧的授权机制。请在 `homeserver.yaml` 中添加以下配置，否则服务机器人将无法被创建，也无法注册站点。

```
use_appservice_legacy_authorization: true

```

旧版本的Matrix规范通过access\_token查询参数来进行应用服务授权，此选项被认为是不安全的，从 Synapse v1.71.0 版本开始，应用服务令牌已通过 Authorization 标头发送，使得不安全的查询参数授权变得多余，此选项默认为 false，cactus-appservice依赖比较旧，需要要这个选项才能进行一些管理操作，包括：

- 通过应用服务创建管理机器人账号。
- 网站管理员通过这个机器人注册站点。
- 机器人创建审核室，使用内置工具帮助用户进行审核，两个功能：  
  1. 功率级复制，如果您邀请并提升某人到审核室，他们在您的所有评论房间中都将具有相同的权限部分。 作为一种简单的机制，可以让多人成为评论部分的版主。
  2. 禁止传播，如果你在你的评论区封禁了一个用户，他们将自动在你的所有评论区被封禁。

**错误提示**：当首次创建服务未启用该功能时，会在日志中出现`INFO in app: 401 CHAT.CACTUS.APPSERVICE_UNAUTHORIZED` ，机器人账号无法创建，提示不存在，自然也就无法注册站点和创建评论房间了，即便你手动创建账号也不行，必须通过应用服务。

如果你只需要一个评论房间应用到自己的网站上，后续可以关闭这个配置选项，机器人将无法再注册新的网站，无法为新的`commentSectionId:`创建房间，也无法使用内置的审核工具，不过已经启用的评论房间和访客模式不受影响。

#### 步骤 3: 部署 Cactus Appservice 容器

##### docker-compose.yaml

```
services:
 # ... 您的 synapse 服务 ...
  cactus:
    image: cactuscomments/cactus-appservice:latest
    container_name: cactus
    restart: unless-stopped
    network_mode: host
    # 或者bridge模式，前面一个端口要和`cactus.yaml` 中的对应上
    # network_mode: bridge
    # ports:
    #   - 8500:5000
    environment:
      - "CACTUS_HS_TOKEN=xxx"
      - "CACTUS_AS_TOKEN=xxx"
      - "CACTUS_HOMESERVER_URL=https://matrix.domain.com:8448"
      - "CACTUS_USER_ID=@cactusbot:domain.com"
      - "CACTUS_NAMESPACE_REGEX=#comments_.*"
      - "CACTUS_NAMESPACE_PREFIX=comments_"
      - "CACTUS_REGISTRATION_REGEX=@userid:domain.com"
   # 如果和synapse一起部署，并且synapse 启用了healthcheck，建议加上这个
   # depends_on:
    #   synapse:
    #     condition: service_healthy

```

##### 其中的环境变量

| 名称                          | 是否必需 | 描述                                                                           |                     |
| --------------------------- | ---- | ---------------------------------------------------------------------------- | ------------------- |
| CACTUS\_HS\_TOKEN           | 是    | 用于认证主服务器的令牌。此值应与注册 YAML 文件中的 hs\_token 保持一致。                                 |                     |
| CACTUS\_AS\_TOKEN           | 是    | 用于将应用服务认证到主服务器的令牌。此值应与注册 YAML 文件中的 as\_token 保持一致。                           |                     |
| CACTUS\_HOMESERVER\_URL     | 是    | 应用服务注册到的主服务器的完整 URL。                                                         |                     |
| CACTUS\_USER\_ID            | 是    | 聊天机器人接口的用户 ID。此值应基于注册 YAML 文件中的 sender\_localpart，例如：@cactusbot:example.com。 |                     |
| CACTUS\_NAMESPACE\_REGEX    | 否    | 应用服务控制的房间别名的正则表达式。此值应与注册 YAML 文件中的正则表达式保持一致。例如：#comments\_.                  |                     |
| CACTUS\_NAMESPACE\_PREFIX   | 否    | 应用服务控制的房间别名前缀。仅当设置了 CACTUS\_NAMESPACE\_REGEX 时才应设置，并且应与其保持一致。例如：comments\_   |                     |
| CACTUS\_REGISTRATION\_REGEX | 否    | 仅允许符合此正则表达式的用户 ID 注册新站点。例如：@user1:example.org                                | @user2:example.org。 |

运行 `docker-compose up -d` 启动服务。现在，您可以像使用官方服务一样，与您自己的 `@cactusbot:your-matrix-domain.com` 机器人交互来注册站点和管理评论了。

## 附录：什么是Matrix 媒体认证

**媒体认证 (Authenticated Media)** 是 Matrix 的一项安全特性 ([MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3916-authentication-for-media.md?ref=nigzu.com "null"))，旨在解决非加密媒体文件可被任何人通过直链公开访问的问题。

在 Synapse 中，此功能通过 `homeserver.yaml` 的 `enable_authenticated_media` 选项控制，**默认已启用**。

- **启用后**：新上传的媒体文件将需要提供有效的 `access_token` (即使用户是访客) 才能访问，增强了隐私保护。
- **启用前**：已经存在的媒体文件不受影响，仍然可以公开访问。

当您在 `initComments` 中设置 **`isAuthenticated: true`** 时，Cactus Comments 会在请求媒体文件时附带用户的 `access_token`，从而能够正常显示受此特性保护的图片和文件。如果您的 Synapse 服务端启用了媒体认证，请务必开启此选项。