Matrix 评论系统:我为 Cactus Comments 开发了些新功能
Cactus Comments 是一款独特的开源评论系统,它利用去中心化通讯协议 Matrix 作为后端,这赋予了它超越传统评论组件的巨大潜力——无论是实现网站实时聊天、集成 AI 智能客服,还是作为微博客(Microblog)的内容发布渠道,其扩展性在同类开源项目中都难得一见。
然而,原项目已停止更新,我为其注入了新的活力:不仅修复了原有缺陷,更独立开发了一系列实用新功能(我的分支目前尚未合并到主干)。
经过持续打磨,这个新版本已经相当完善。本文将为你详细介绍我带来的新特性,并提供一份结合新功能的中文安装教程。你可以在我的个人博客上体验。
请注意:为了服务器的稳定,体验前需要注册。因为访客模式可能会对自托管的 Matrix 服务端造成难以预估的压力。
新版 Cactus Comments:都有哪些亮点?
🚀 核心升级
- 架构更新:重构了构建配置和依赖,现已支持在 Node.js v22+ 环境下进行开发和部署。
🖼️ 媒体功能大升级
-
安全媒体支持:新增
isAuthenticated
选项,当你的 Matrix 服务端启用媒体认证后,评论系统将通过认证端点请求媒体资源,并利用缓存优化加载速度。未认证状态下则沿用原始链接。 -
三级媒体缓存系统:为了更好的加载体验,我设计了一套缓存策略。
-
IndexedDB 持久化缓存:将媒体链接、文件数据、时间戳和类型存储在浏览器数据库中,并会自动清理超过一天的过期缓存。
-
智能读取策略:
-
内存缓存优先:最快响应。
-
IndexedDB 次之:秒开已缓存的媒体。
-
网络获取保底:最后从服务器拉取。
-
-
💬 完整的消息回复功能
-
实现引用回复:现在支持消息间的引用关系,无论是普通回复还是在线程中回复,都能清晰地展示上下文。
-
动态加载与显示:对于已加载的引用,会显示其内容摘要;对于未加载的,会先提示“尚未加载”,一旦加载完成便自动更新,体验流畅。
-
悬停预览与跳转:
-
交互优化:鼠标悬停在引用上时,会立即弹出预览框(支持文本、图片、视频),让你一目了然。
-
精准跳转:点击引用,页面会自动滚动到原始消息位置并高亮显示。
-
-
编辑器集成:在编辑器中即可发起引用回复,引用的内容会清晰地显示在编辑框上方。
🔒 只读模式
-
新增
commentEnabled
配置项(默认为true
)。当设置为false
时,评论区将变为只读模式,隐藏所有编辑和发送按钮,适用于内容展示或归档场景。 -
注意:即便在只读模式下,访客自动注册功能依然是必需的(只在用户客户端首次访问网站的时候),这能确保用户可以正常查看历史消息和访问需要认证的媒体。如果你的网站访问量较大,最好为其增加权限,例如仅会员可见。
🌐 全面的多语言支持
-
语言切换:新增
language
配置项,允许你自由设定显示语言。 -
灵活的翻译方案:使用独立的 JS 或 JSON 文件管理翻译,并支持三种智能回退机制:
-
优先使用
translationsData
配置项引用的 JS 翻译文件。 -
其次尝试加载站点下的
/language/{lang}.json
语言文件。 -
最后默认显示英文,保证可用性。
-
✨ 前端渲染与体验修复
-
核心样式修复:
-
解决了嵌套
flex
布局下元素如代码块溢出的问题。 -
限制了音频播放器的最大宽度,防止破坏页面布局。
-
改进了文本换行逻辑,能正确处理长单词和
<em>
等标签内的换行。
-
-
评论列表性能优化:
-
防止重复加载:在加载过程中,“查看更多”按钮会被禁用,有效避免了并发请求和重复渲染消息。
-
增量加载:现在点击“查看更多”只会追加新评论,而不是刷新整个评论区,避免闪烁和媒体播放状态重置。
-
横向对比与功能演示
与其他 Matrix 应用对比
我只选取了同样基于 Matrix 协议的嵌入式应用进行比较,以凸显新版 Cactus 的优势。
项目 | 嵌入方式 | 功能支持情况 | 特点 | 最后发布日期 |
---|---|---|---|---|
Cactus | 盒子元素 | 反应 ❌ 私聊 ❌ 访客 ✔️ 登录 ✔️ 附件上传 ❌ 媒体显示 ✔️ 媒体认证(此分支) ✔️ 回复(此分支) ✔️ 只读(此分支) ✔️ 国际化(此分支) ✔️ |
简洁易用 功能丰富 没有明显的BUG |
原项目:2022 年 7 月 8 此分支:持续更新 |
Chatterbox | 气泡按钮 | 反应 ✔️ 私聊 ✔️ 访客(服务号) ✔️ 登录 ❌ 附件上传 ✔️ 媒体显示 ✔️ 媒体认证 ❌ 回复❌ 只读❌ 国际化❌ |
支持反应和附件上传 使用应用程序服务注册账号而非访客模式 |
2022 年 10 月 17 |
Livematrix | 气泡按钮 | 反应 ❌ 私聊 ✔️ 访客 ✔️ 登录 ❌ 附件上传 ❌ 媒体显示 ✔️ 媒体认证 ❌ 回复❌ 只读❌ 国际化❌ |
2022 年 6 月 6 |
如果用作在网页独立区域展示的评论系统、短内容发布频道,推荐 Cactus Comments ,如果用作客服系统、消息气泡,Chatterbox 也许是更好的选择。
类似的项目还有:
- hydrogen-web,这是个轻量级的 Matrix web 客户端,有 SDK 可以用来开发嵌入式前端,受限于 hydrogen 自身的实现, 访客、投票、消息列等尚未支持,优点是这是由element.io 组织托管和项目仍在持续更新。
- Chatrix,功能丰富,只适用于 WordPress。
- cerulean 和 Matrix Live,将 Matrix 房间发布为微博,功能较为简陋,可被Cactus完美替代。
以上大概就是全部了。
功能展示(截图)
- 图片和AI演示:
- 引用和引用预览演示
- 加载、夜间模式演示
安装教程
基础准备:引入脚本
无论采用何种部署方式,您都需要先获取项目文件。
-
前往 Cactus Comments 的 GitHub 分支项目 下载最新 release 版本。
-
将
cactus.js
、style.css
、cactus-lang.js
三个文件上传到您的服务器或 CDN。 -
在您网站的 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
发起私聊,发送以下命令来注册您的站点:
register <YOUR-SITE-NAME>
请将 <YOUR-SITE-NAME>
替换为您网站的唯一英文名称。
步骤 2: 嵌入评论区
将以下代码嵌入到您网站需要显示评论区的页面。
<!-- 假设这是您网站页面上用来展示评论的描点元素 -->
<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:样式表,元素都可以自定义。
颜色模式
Cactus 前端预设了浅色和深色模式。默认情况下,样式表会根据浏览器选择的配色方案进行调整,因此你不需要做任何事情。如果想明确控制亮/暗模式,可以通过在 <html>
元素上设置 light
和 dark
类来覆盖它。
评论元素顺序调整
- 颠倒评论列表和编辑器的顺序,将评论列表置于顶部,编辑器置于底部,可以使用以下规则:
.cactus-container {
flex-direction: column-reverse;
}
- 颠倒评论本身的顺序,将较旧的评论放在顶部,较新的评论放在底部,可以使用以下规则:
.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。
步骤2. 创建 Cactus Comments 应用程序服务
1. 创建Appservice 注册文件
# 应用服务的唯一自定义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
中引用它:
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依赖比较旧,需要要这个选项才能进行一些管理操作,包括:
- 通过应用服务创建管理机器人账号。
- 网站管理员通过这个机器人注册站点。
- 机器人创建审核室,使用内置工具帮助用户进行审核,两个功能:
- 功率级复制,如果您邀请并提升某人到审核室,他们在您的所有评论房间中都将具有相同的权限部分。 作为一种简单的机制,可以让多人成为评论部分的版主。
- 禁止传播,如果你在你的评论区封禁了一个用户,他们将自动在你的所有评论区被封禁。
错误提示:当首次创建服务未启用该功能时,会在日志中出现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),旨在解决非加密媒体文件可被任何人通过直链公开访问的问题。
在 Synapse 中,此功能通过 homeserver.yaml
的 enable_authenticated_media
选项控制,默认已启用。
-
启用后:新上传的媒体文件将需要提供有效的
access_token
(即使用户是访客) 才能访问,增强了隐私保护。 -
启用前:已经存在的媒体文件不受影响,仍然可以公开访问。
当您在 initComments
中设置 isAuthenticated: true
时,Cactus Comments 会在请求媒体文件时附带用户的 access_token
,从而能够正常显示受此特性保护的图片和文件。如果您的 Synapse 服务端启用了媒体认证,请务必开启此选项。
评论