FastComments.com

实时评论 API

FastComments API

FastComments 提供了一个用于与众多资源交互的 API。您可以构建与我们平台的集成,甚至自行编写客户端!

在本文档中,您将找到 API 支持的所有资源及其请求和响应类型的文档。

对于企业客户,所有 API 访问都会记录在审计日志中。

生成的 SDK

FastComments 现在可以从我们的代码生成 API 规范(尚未完整,但已包含许多 API)。

我们还提供了流行语言的 SDK:

身份验证

API 通过将您的 api key 作为 X-API-KEY 请求头或 API_KEY 查询参数传递进行身份验证。您还需要提供 tenantId 来进行 API 调用。该信息可在与 api key 同一页面获取。

安全说明

这些路由应当从 服务器 调用。请勿 从浏览器调用。这样会泄露您的 API 密钥——任何能够查看页面源代码的人都将获得对您账户的完整访问权限!

身份验证选项一 - 请求头

  • Header: X-API-KEY
  • Header: X-TENANT-ID

身份验证选项二 - 查询参数

  • Query Param: API_KEY
  • Query Param: tenantId

身份验证选项三 - OAuth Bearer Token

  • Header: Authorization: Bearer fcat_...

Zapier 等第三方应用以及 MCP server 的客户端通过 OAuth 获取令牌,而不是使用 API 密钥。该令牌可用于此处的所有端点。租户信息由令牌隐含,因此 tenantId 为可选项,但若提供必须与令牌匹配。GET 请求需要 read 范围,其他方法需要 write 范围。完整流程(包括客户端注册、PKCE、刷新和撤销)已在 OAuth Authorization 中记录。发现过程从 https://fastcomments.com/.well-known/oauth-authorization-server 开始。

读取自己的写入

FastComments 提供主动-主动可用性。来自您数据中心的请求会被路由到离您最近的 接入点。这一步是自动完成的,通常您可以观察到读写一致性语义。如果您想确保读取到自己的写入,可以通过将请求固定到特定区域的 API 主机来实现(不过大多数集成通常不需要这样做):

  • gdc-oregon.fastcomments.com
  • gdc-virginia.fastcomments.com
  • gdc-singapore.fastcomments.com
  • gdc-falkenstein2.fastcomments.com
  • gdc-sao-paulo.fastcomments.com
  • eudc-helsinki2.fastcomments.com
  • eudc-limburg.fastcomments.com
  • eudc-france.fastcomments.com

请注意,如果您这样做,可能需要定义回退方案,因为我们过去已弃用某些入口节点,并在切换时使用了新名称。

审计日志结构 Internal Link

An AuditLog 是一个对象,表示对拥有此功能访问权限的租户的审计事件。

AuditLog 对象的结构如下:

审计日志结构
Copy Copy
1
2interface AuditLog {
3 id: string;
4 /** 执行此事件的用户。 **/
5 userId?: string;
6 username?: string;
7 resourceName: string;
8 crudType: 'c' | 'r' | 'u' | 'd' | 'login';
9 from: string;
10 url?: string;
11 ip?: string;
12 /** 执行此事件的浏览器(如果来自浏览器)。 **/
13 ua?: string;
14 /** 事件来源会话的哈希,用于关联同一人的操作。永远不包含会话本身。 **/
15 sIdHashed?: string;
16 when: string;
17 description?: string;
18 serverStartDate: string;
19 /** 事件执行对象的 ID(区别于执行者)。 **/
20 targetId?: string;
21 /** 该对象的可读标签,例如 "jsmith (jsmith@example.com)"。 **/
22 targetLabel?: string;
23 objectDetails?: object;
24}
25

targetId 和 targetLabel 描述事件作用的对象;userId 和 username 描述执行者。对于更新操作,objectDetails.changes 保存一个 {field: {from, to}} 映射,记录实际的更改内容。

审计日志是不可变的,且不能手动写入。FastComments.com 只能决定何时写入审计日志。不过,您可以通过此 API 读取审计日志。

审计日志中的事件将在两年后过期。

批处理作业结构 Internal Link

A BatchJob 是一种长时间运行的任务,例如导入、导出或页面迁移。作业会被排队,然后被取出并在后台运行。

processedCount 和 totalCount 显示进度。它们的单位取决于 type:

  • migrate-pages:已完成的迁移数,针对请求的迁移总数。
  • undo-page-migration:已恢复的记录。没有 totalCount。
  • import、export 以及其他作业:已处理的行数。

BatchJob 对象的结构如下:

BatchJob 结构
Copy Copy
1
2interface BatchJob {
3 id: string
4 /** 例如 migrate-pages、undo-page-migration、migrate-domains、import、export、users-export 或 copy-comments。 **/
5 type: string
6 status: 'requested' | 'running' | 'success' | 'failed'
7 createdAt: string
8 completedAt?: string
9 processedCount: number
10 /** 总工作量(如果事先已知)。 **/
11 totalCount?: number
12 /** migrate-pages:每个已完成的迁移对应一个条目,按请求顺序。 **/
13 pageMigrationResults?: Array<PageMigrationSuccess | PageMigrationFailure>
14 /**
15 * migrate-pages:在作业完成(成功或失败)后设置。请在 90 天内将其传递给 POST /api/v1/pages/migrations/:undoId/undo。
16 * undo-page-migration:正在撤销的迁移。
17 **/
18 undoId?: string
19 /** undo-page-migration:在作业完成后设置。 **/
20 pageMigrationUndoResult?: PageMigrationUndoResult
21}
22
23interface PageMigrationSuccess {
24 from: string
25 to: string
26 type: 'urlId' | 'domain'
27 status: 'success'
28 commentsMigrated: number
29}
30
31interface PageMigrationFailure {
32 from: string
33 to: string
34 type: 'urlId' | 'domain'
35 status: 'failed'
36 code: string
37 reason: string
38}
39
40interface PageMigrationUndoResult {
41 /** 记录已恢复到迁移前的值。 **/
42 restored: number
43 /** 记录保持不变,因为它们在迁移后已更改,或已不存在。 **/
44 skipped: number
45 /** 记录在合并页面时被删除的迁移。这些记录无法恢复。 **/
46 notRestorable: number
47}
48

重试

如果运行页面迁移作业的服务器重启,作业会重新放回队列,并在上一次完成的迁移之后继续。作业最多会尝试 3 次,之后其 status 为 failed。

评论结构 Internal Link

Comment 对象表示用户留下的评论。

父评论与子评论之间的关系通过 parentId 定义。

Comment 对象的结构如下:

Comment 结构
Copy Copy
1
2interface Comment {
3 /** READONLY: 如果垃圾评论引擎判定该评论为垃圾评论,则设置为 true。 **/
4 aiDeterminedSpam?: boolean
5 /** 该评论是否被批准显示。保存评论时设置为 true,否则将被隐藏。 **/
6 approved?: boolean
7 /** 用户的头像。 **/
8 avatarSrc?: string
9 /** 子评论。并非在所有场景下都会填充。当通过 API 将 asTree 设置为 true 时使用。 **/
10 children: Comment[]
11 /** 评论者的原始评论内容。 **/
12 comment: string
13 /** READONLY: 评论者的评论解析后的 HTML。 **/
14 commentHTML?: string
15 /** 评论者的邮箱。如果匿名评论功能关闭,则为必填项。 **/
16 commenterEmail?: string
17 /** 评论者的链接(例如,其博客)。 **/
18 commenterLink?: string
19 /** 评论者的名称。始终必填。如果不可用,请设置为类似 "Anonymous" 的值。 **/
20 commenterName: string
21 /** 评论发表的日期,以 UTC epoch 表示。 **/
22 date: number
23 /** 该评论的“显示标签”——例如 "Admin"、"Moderator",或类似 "VIP User" 的内容。 **/
24 displayLabel?: string
25 /** 发表评论的域名。 **/
26 domain?: string
27 /** READONLY: 该评论被举报的次数。 **/
28 flagCount?: number
29 /** 评论中成功解析的 #hashtags 标签。你也可以手动添加 hashtags 用于查询,但它们不会自动显示在评论文本中。 **/
30 hashTags?: CommentHashTag[]
31 /** READONLY: 该评论是否包含图片? **/
32 hasImages?: boolean
33 /** READONLY: 该评论是否包含链接? **/
34 hasLinks?: boolean
35 /** READONLY: 唯一的评论 id。 **/
36 id: string
37 /** 仅在创建时有效!存储时会被哈希处理。 **/
38 ip?: string
39 /** READONLY: 当前用户是否屏蔽了撰写此评论的用户? **/
40 isBlocked?: boolean
41 /** READONLY: 该评论是否由管理员发表?根据 userId 自动设置。 **/
42 isByAdmin?: boolean
43 /** READONLY: 该评论是否由版主发表?根据 userId 自动设置。 **/
44 isByModerator?: boolean
45 /** 如果该评论被软删除(由于其他配置而必须保留占位符),则设置为 true。 **/
46 isDeleted?: boolean
47 /** 如果用户的账户被删除而评论必须保留,则设置为 true。 **/
48 isDeletedUser?: boolean
49 /** READONLY: 是否被当前登录用户(contextUserId)举报? **/
50 isFlagged?: boolean
51 /** 该评论是否被置顶? **/
52 isPinned?: boolean
53 /** 该评论是否被锁定?为 true 时,任何人(包括版主)都无法回复、编辑或删除该评论,直到其被解锁。 **/
54 isLocked?: boolean
55 /** 该评论是否为垃圾评论? **/
56 isSpam?: boolean
57 /** READONLY: 对于当前用户(contextUserId),该评论是否被投了反对票? **/
58 isVotedDown?: boolean
59 /** READONLY: 对于当前用户(contextUserId),该评论是否被投了赞成票? **/
60 isVotedUp?: boolean
61 /** 评论所在的语言区域。如果未提供,将从 HTTP 的 language accept 头部推导。 **/
62 locale?: 'de_de' | 'en_us' | 'es_es' | 'fr_fr' | 'it_it' | 'ja_jp' | 'ko_kr' | 'pl_pl' | 'pt_br' | 'ru_ru' | 'tr_tr' | 'zh_cn' | 'zh_tw'
63 /** READONLY: 评论中成功解析的 @mentions。 **/
64 mentions?: CommentUserMention[]
65 /** 与该评论关联的可选元数据。 **/
66 meta?: Record<string, string | number | boolean>
67 /** 与该评论关联的审核组 id 的可选列表。 **/
68 moderationGroupIds?: string[]|null
69 /** READONLY: 对应于当前用户(contextUserId)对该评论投票的投票对象 id。 **/
70 myVoteId?: string
71 /** 是否已为该评论向评论者发送通知。为防止导入时发送通知,请将其设置为 true。 **/
72 notificationSentForParent?: boolean
73 /** 是否已为该评论向租户用户发送通知。为防止导入时发送通知,请将其设置为 true。 **/
74 notificationSentForParentTenant?: boolean
75 /** 该评论所在页面的标题。 **/
76 pageTitle?: string
77 /** 如果我们正在回复某条评论,这就是我们正在回复的评论 ID。 **/
78 parentId?: string|null
79 /** 该评论是否被标记为已审核。 **/
80 reviewed: boolean
81 /** 该评论所属的租户 id。 **/
82 tenantId: string
83 /** 撰写该评论的用户。保存带有名称/邮箱的评论时自动创建。 **/
84 userId?: string|null
85 /** 该评论可见的位置的 URL,例如博客文章。 **/
86 url: string
87 /** 你传给我们的 urlId 的“清理”版本。保存时你指定此字段,但当你取回该评论时,它会被“清理”,而你的原始值会被移到 "urlIdRaw"。 **/
88 urlId: string
89 /** READONLY: 你传给我们的原始 urlId。 **/
90 urlIdRaw?: string
91 /** 该用户和该评论是否已验证? **/
92 verified: boolean
93 /** 赞成票数。 **/
94 votesUp?: number
95 /** 反对票数。 **/
96 votesDown?: number
97 /** 该评论的“karma”(= 赞成票数 - 反对票数)。 **/
98 votes?: number
99}
100

其中一些字段被标记为 READONLY——这些字段由 API 返回,但无法设置。

评论文本结构

评论以 FastComments 风格的 markdown 编写,即传统 markdown 加上用于图片的传统 bbcode 风格标签,例如 [img]path[/img]。

文本存储在两个字段中。用户输入的文本未经修改地存储在 comment 字段中。它会被渲染并存储在 commentHTML 字段中。

允许的 HTML 标签为 b, u, i, strike, pre, span, code, img, a, strong, ul, ol, li, and br。

建议渲染 HTML,因为它只是 HTML 的一个很小的子集,构建一个渲染器非常简单。例如,React Native 和 Flutter 有多个库可以帮助实现这一点。

你可以选择渲染 comment 字段的未规范化值。示例解析器在此。。

该示例解析器也可以调整为处理 HTML,将 HTML 标签转换为适合你的平台渲染的元素。

标注用户

当用户在评论中被标注时,信息会存储在一个名为 mentions 的列表中。该列表中的每个对象 具有以下结构。

Comment Mentions 对象
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** 用户 id。对于 SSO 用户,这将带有你的租户 id 前缀。 **/
4 id: string
5 /** 最终的 @mention 标签文本,包括 @ 符号。 **/
6 tag: string
7 /** 原始的 @mention 标签文本,包括 @ 符号。 **/
8 rawTag: string
9 /** 被标注的用户类型。user = FastComments.com 账户。sso = SSOUser。 **/
10 type: 'user'|'sso'
11 /** 如果用户选择不接收通知,此项仍会被设置为 true。 **/
12 sent: boolean
13}
14

HashTags

当使用 hashtags 并成功解析时,信息会存储在一个名为 hashTags 的列表中。该列表中的每个对象 具有以下结构。如果设置了 retain,hashtags 也可以手动添加到评论的 hashTags 数组中用于查询。

Comment HashTag 对象
Copy CopyRun External Link
1
2interface CommentHashTag {
3 /** 该 hashtag 的 id。 **/
4 id: string
5 /** 最终的 #hashtag 标签文本,包括 # 符号。 **/
6 tag: string
7 /** 如果该 hashtag 关联了自定义 URL,则会定义此项。 **/
8 url?: string
9 /** 如果我们应在评论更新时保留该 hashtag(即使它不存在于评论文本中)。这对于在不更改评论文本的情况下标注评论很有用。 **/
10 retain?: boolean
11}
12

电子邮件模板结构 Internal Link

EmailTemplate 对象表示租户的自定义电子邮件模板的配置。

系统将通过以下方式选择要使用的电子邮件模板:

  • 其类型标识符,我们称之为 emailTemplateId。这些是常量。
  • domain。我们会首先尝试查找与相关对象(例如 Comment)关联的域的模板,如果未找到匹配项,则会尝试查找 domain 为 null 或 * 的模板。

EmailTemplate 对象的结构如下:

电子邮件模板结构
Copy Copy
1
2interface EmailTemplate {
3 id: string
4 tenantId: string
5 emailTemplateId: string
6 displayName: string
7 /** 只读 **/
8 createdAt: string
9 /** 只读 **/
10 updatedAt: string
11 /** 只读 **/
12 updatedByUserId: string
13 /** 模板应关联的域。 **/
14 domain?: string | '*' | null
15 /** 使用 EJS 语法的电子邮件模板内容。 **/
16 ejs: string
17 /** 针对每个受支持语言环境的覆盖翻译键到值的映射。 **/
18 translationOverridesByLocale: Record<string, Record<string, string>>
19 /** 表示模板渲染上下文的对象。 **/
20 testData: object
21}
22

注意

  • 您可以从 /definitions 端点获取有效的 emailTemplateId 值。
  • /definitions 端点还包含默认翻译和测试数据。
  • 如果结构或测试数据无效,模板将保存失败。

动态帖子结构 Internal Link

A FeedPost 对象表示 FastComments 动态中的一篇帖子。动态是一系列带有各自评论线程的帖子流,由 Feed 小部件渲染。每篇帖子都有作者、可选的富内容、媒体和链接,并且可以被标记,以便对动态进行过滤。

FeedPost 对象的结构如下:

FeedPost 结构
Copy Copy
1
2interface FeedPost {
3 /** READONLY **/
4 _id: string
5 /** READONLY **/
6 tenantId: string
7 title?: string
8 /** 发布帖子的 FastComments 或 SSO 用户的 ID。 **/
9 fromUserId?: string
10 /** 未设置时从用户填充。 **/
11 fromUserDisplayName?: string | null
12 /** 只读。来自用户填充。 **/
13 fromUserAvatar?: string | null
14 /** 用于过滤动态。 **/
15 tags?: string[]
16 /** 在动态中的排序权重。值越高越靠前。 **/
17 weight?: number
18 /** 供您自行使用的自由键/值对。 **/
19 meta?: Record<string, string>
20 /** 已清理的 HTML。 **/
21 contentHTML?: string
22 media?: FeedPostMediaItem[]
23 links?: FeedPostLink[]
24 /** READONLY **/
25 createdAt: string
26 /** READONLY. Reaction type to count. **/
27 reacts?: Record<string, number>
28 /** READONLY **/
29 commentCount?: number | null
30}
31
32interface FeedPostMediaItem {
33 title?: string
34 /** 点击时媒体项链接的目标地址。 **/
35 linkUrl?: string
36 /** 每个呈现对应一个条目。小部件会选择最合适的。 **/
37 sizes: FeedPostMediaItemAsset[]
38}
39
40interface FeedPostMediaItemAsset {
41 w: number
42 h: number
43 src: string
44}
45
46interface FeedPostLink {
47 /** 链接文本,例如 “立即注册”。 **/
48 text?: string
49 /** 链接显示的标题。 **/
50 title?: string
51 /** 链接显示的描述。 **/
52 description?: string
53 url?: string
54}
55

注意:

  • 某些字段标记为 READONLY —— 这些字段由 API 返回,但不能被设置。
  • 帖子上的评论是普通评论,其 urlId 为 post: 加上帖子 _id。使用该值配合评论 API 可读取或创建帖子的评论。

标签结构 Internal Link

HashTag 对象表示用户可以留下的标签。HashTags 可用于链接到外部内容,或将相关评论关联在一起。

HashTag 对象的结构如下:

HashTag 结构
Copy Copy
1
2interface HashTag {
3 /** 应以 "#" 或所需字符开头。 **/
4 tag: string
5 /** 一个可选的 URL,hashtag 可以指向它。点击时,UI 将重定向到该 URL,而不是按 hashtag 筛选评论。 **/
6 url?: string
7 /** 只读 **/
8 createdAt: string
9}
10

注意事项:

  • 在某些 API 端点中,你会看到 hashtag 被用于 URL 中。请记住对值进行 URI 编码。例如,# 应表示为 %23。
  • 其中一些字段被标记为 READONLY(只读)——它们由 API 返回,但无法设置。

通知计数结构 Internal Link

NotificationCount 对象表示用户的未读通知计数和元数据。

如果没有未读通知,则不会为该用户生成 NotificationCount。

NotificationCount 对象会自动创建,无法通过 API 创建。它们也会在一年后过期。

您可以通过删除用户的 NotificationCount 来清除其未读通知计数。

NotificationCount 对象的结构如下:

NotificationCount 结构
Copy Copy
1
2interface NotificationCount {
3 id: string // 用户 id
4 count: number
5 createdAt: string // 日期字符串
6 expireAt: string // 日期字符串
7}
8

通知结构 Internal Link

一个 Notification 对象表示针对用户的通知。

Notification 对象是自动创建的,无法通过 API 创建。它们在一年后会过期。 通知不能被删除。但可以更新将 viewed 设为 false,并且可以按 viewed 查询。

用户也可以通过将通知中的 optedOut 设置为 true 来选择不接收针对特定评论的通知。将其设置为 false 可以重新选择接收。

存在不同的通知类型 —— 请检查 relatedObjectType 和 type。

通知的创建方式非常灵活,可由多种情景触发(参见 NotificationType)。

截至目前,Notification 的存在并不意味着会或应该发送电子邮件。相反,通知用于通知提要和相关集成。

Notification 对象的结构如下:

Notification 对象结构
Copy Copy
1
2enum NotificationObjectType {
3 Comment = 0,
4 Profile = 1,
5 Tenant = 2
6}
7
8enum NotificationType {
9 /** 如果有人回复你。 **/
10 RepliedToMe = 0,
11 /** 如果有人在你评论的线程中的任何位置回复(即使是嵌套的子评论)。 **/
12 RepliedTransientChild = 1,
13 /** 如果你的评论被点赞。 **/
14 VotedMyComment = 2,
15 /** 如果在你订阅的页面根评论处有新的评论。 **/
16 SubscriptionReplyRoot = 3,
17 /** 如果有人在你的个人资料上评论。 **/
18 CommentedOnProfile = 4,
19 /** 如果你有私信。 **/
20 DirectMessage = 5,
21 /** TrialLimits 仅适用于租户用户。 **/
22 TrialLimits = 6,
23 /** 如果你被 @提及。 **/
24 Mentioned = 7
25}
26
27interface Notification {
28 id: string
29 tenantId: string
30 /** 使用 SSO 时,用户 id 的格式为 `<tenant id>:<user id>`。 **/
31 userId?: string
32 /** 在使用 SSO 时,你只需关心 `userId`。 **/
33 anonUserId?: string
34 /** urlId 几乎总是已定义。它仅在租户级通知(不常见)中为可选。 **/
35 urlId?: string
36 /** URL 被缓存以便快速导航到通知来源。 **/
37 url?: string
38 /** 页面标题被缓存以便快速查看通知来源。 **/
39 pageTitle?: string
40 relatedObjectType: NotificationObjectType
41 /** 例如,评论 id。 **/
42 relatedObjectId: string
43 viewed: boolean
44 createdAt: string // 日期字符串
45 type: NotificationType
46 fromCommentId?: string
47 fromVoteId?: string
48 /** fromUserName 和 fromUserAvatarSrc 在此处被缓存以便快速显示通知。用户更新时它们也会更新。 **/
49 fromUserName: string
50 fromUserId: string
51 fromUserAvatarSrc?: string
52 /** 将此设置为 true 以停止接收该对象的通知。 **/
53 optedOut?: boolean
54}
55

页面结构 Internal Link

A Page 对象表示许多评论可能属于的页面。此关系由 urlId 定义。

A Page 存储信息,例如页面标题、评论计数和 urlId。

Page 对象的结构如下:

页面结构
Copy Copy
1
2interface Page {
3 id: string
4 urlId: string
5 url: string
6 title?: string
7 createdAt: string
8 commentCount: number
9 rootCommentCount: number
10 /** 将此设置为 null 意味着所有 SSO 用户都可以看到该页面。空列表表示对所有用户关闭。 **/
11 accessibleByGroupIds?: string[] | null
12 /** 此页面是否关闭以添加新评论? **/
13 isClosed?: boolean
14}
15

投票结构 Internal Link

Poll(投票)是附加在评论上的,而不是一个独立的对象。它在创建评论时一并创建(参见 POST /api/v1/comments),或者稍后通过 PUT /api/v1/polls/:commentId 添加到已有评论上。

投票计数保存在投票对象本身上,因此读取投票时无需额外累加即可获得结果。这些计数背后的单个投票记录是 PollVote 对象。

每个选项都有一个在投票创建时生成的 id。该 id 用于投出选票、重新命名选项,以及在使用 PUT 更新投票(增删选项)时保留某个选项(及其选票)。这是引用选项的唯一安全方式——绝不要使用它在列表中的位置。

Poll 结构
Copy Copy
1
2interface CommentPollOption {
3 id: string
4 label: string
5 votes: number
6}
7
8interface CommentPoll {
9 question: string
10 options: CommentPollOption[]
11 totalVotes: number
12 /** 当已设置且时间已过,投票即关闭,不再接受选票。 **/
13 closesAt?: string | null
14 /** 0 为匿名(默认),1 为管理员和版主可见,2 为所有人可见。缺省表示匿名。 **/
15 privacy?: 0 | 1 | 2 | null
16 /** 为 true 时,尚未投票的用户无法看到计数。缺省表示 false。 **/
17 requireVoteToSeeResults?: boolean | null
18}
19

限制

  • 问题(question)为必填项,最多 200 个字符。
  • 一个投票有 2 到 10 个选项。
  • 选项标签为必填项,最多 100 个字符,且在同一投票内必须唯一(忽略大小写)。
  • closesAt 在创建投票时必须是未来的时间。要立即关闭投票,请使用过去的日期对其进行 PATCH 操作。

站点设置

投票遵循你的站点配置,你可以在 Customize Widget(自定义小部件)下进行更改:

  • 必须先启用投票功能才能创建投票,否则 API 会返回 polls-disabled。
  • 投票可以限制为仅限已登录用户,在这种情况下,仅携带 anonUserId 发送的选票将被拒绝,并返回 poll-login-required。

投票记录结构 Internal Link

A PollVote 是一个人对投票的答案。投票本身显示的计数会与这些保持同步,因此只有在想知道谁投了什么,而不是总计时才需要这些。

每个投票人在同一投票中最多只能投一次。再次投票会将其已有的投票移动到新选项,而不是添加第二个投票,updatedAt 记录了发生的时间。

voterId 是投票人登录时的 userId,否则为 anonUserId。

PollVote 结构
Copy Copy
1
2interface PollVote {
3 id: string
4 tenantId: string
5 commentId: string
6 urlId: string
7 /** 投票人登录时的 userId,否则为 anonUserId。 **/
8 voterId: string
9 optionId: string
10 createdAt: string
11 /** 投票人上次将投票移动到不同选项的时间。 **/
12 updatedAt?: string
13}
14

隐私

投票的 privacy 设置对该 API 的作用方式与在评论小部件中的作用方式相同:

  • Anonymous(默认):没有人能看到任何人的投票方式,因此投票内容不可读取。GET /api/v1/poll-votes 和 GET /api/v1/poll-votes/:id 会返回 poll-anonymous。投票的计数仍可通过 GET /api/v1/polls/:commentId 获取。
  • Admins and moderators:你的 API 密钥属于站点的管理员,因此可以读取投票。
  • Everyone:投票可以被读取。

投票一旦产生,投票的隐私设置可以收紧但不能放宽。

待处理Webhook事件结构 Internal Link

一个 PendingWebhookEvent 对象表示一个处于排队等待中的 webhook 事件。

PendingWebhookEvent 对象会自动创建,不能通过 API 手动创建。它们也会在一年后过期。 可以删除它们,从而将任务从队列中移除。

存在不同的事件类型 —— 请检查 eventType (OutboundSyncEventType) 和 type (OutboundSyncType)。

此 API 的一个常见用例是实现自定义监控。你可能希望定期调用 /count 端点,以轮询给定筛选器下的未完成数量。

PendingWebhookEvent 对象的结构如下:

PendingWebhookEvent 结构
Copy Copy
1
2enum OutboundSyncEventType {
3 Create: 0,
4 Delete: 1,
5 Update: 2
6}
7
8enum OutboundSyncType {
9 /** 特定于 WordPress 的同步任务。 **/
10 WP: 0,
11 Webhook: 1
12}
13
14interface PendingWebhookEvent {
15 id: string
16 /** 与该事件关联的评论 id。 **/
17 commentId: string
18 /** 事件发生时的 comment 对象。我们从 2023 年 11 月开始添加这些。 **/
19 comment: Comment
20 /** 可能与评论关联的外部 id。 **/
21 externalId: string | null
22 createdAt: Date
23 tenantId: string
24 attemptCount: number
25 /** 在第一次尝试前以及每次失败后设置。 **/
26 nextAttemptAt: Date
27 /** 表示这是创建、删除还是更新事件。 **/
28 eventType: OutboundSyncEventType
29 /** 要执行的同步类型(WordPress、调用 API 等)。 **/
30 type: OutboundSyncType
31 /** 与评论匹配的域。我们使用该域来选择 API 密钥。 **/
32 domain: string
33 /** 最近发生的错误。此类型未指定具体类型,是对发生情况的“转储”。通常它包含一个具有 statusCode、body 和 headers 映射的对象。 **/
34 lastError: object | null
35}
36

单点登录用户结构 Internal Link

FastComments 提供了一个易于使用的 SSO 解决方案。通过基于 HMAC 的集成更新用户信息是 像让用户使用更新后的负载加载页面一样简单。

但是,可能希望在该流程之外管理用户,以提高应用的一致性。

SSO 用户 API 提供了一种对我们称之为 SSOUsers 的对象进行 CRUD 的方式。这些对象不同于常规 Users 并且 为类型安全而分开保存。

SSOUser 对象的结构如下:

SSOUser 结构
Copy Copy
1
2interface SSOUser {
3 id: string
4 username: string
5 email?: string
6 websiteUrl?: string
7 signUpDate: number
8 createdFromUrlId?: string
9 loginCount?: number
10 avatarSrc?: string
11 optedInNotifications?: boolean
12 optedInSubscriptionNotifications?: boolean
13 displayLabel?: string
14 displayName?: string
15 isAccountOwner?: boolean // 管理权限 - 带有此标志的 SSO 用户按 SSO 管理员计费(与常规 SSO 用户分开)
16 isAdminAdmin?: boolean // 管理权限 - 带有此标志的 SSO 用户按 SSO 管理员计费(与常规 SSO 用户分开)
17 isCommentModeratorAdmin?: boolean // 管理员权限 - 带有此标志的 SSO 用户按 SSO 管理员(版主)计费(与常规 SSO 用户分开)
18 /** If null, Access Control will not be applied to the user. If an empty list, this user will not be able to see any pages or @mention other users. **/
19 groupIds?: string[] | null
20 createdFromSimpleSSO?: boolean
21 /** Don't let other users see this user's activity, including comments, on their profile. Default is true to provide secure profiles by default. **/
22 isProfileActivityPrivate?: boolean
23 /** Don't let other users leave comments on the user's profile, or see existing profile comments. Default false. **/
24 isProfileCommentsPrivate?: boolean
25 /** Don't let other users send direct messages to this user. Default false. **/
26 isProfileDMDisabled?: boolean
27 karma?: number
28 /** Optional configuration for user badges. **/
29 badgeConfig?: {
30 /** Array of badge IDs to assign to the user. Limited to 30 badges. Order is respected. These are global badges visible on all pages. **/
31 badgeIds: string[]
32 /** Array of badge IDs scoped to the current page (urlId). These badges are only displayed on the page where they were assigned. **/
33 pageBadgeIds?: string[]
34 /** If true, replaces all existing displayed badges with the provided ones. Global and page-scoped badges are overridden independently. If false, adds to existing badges. **/
35 override?: boolean
36 /** If true, updates badge display properties from tenant configuration. **/
37 update?: boolean
38 }
39}
40

SSO 用户计费

SSO 用户的计费基于其权限标志有所不同:

  • 常规 SSO 用户:没有管理员或版主权限的用户按常规 SSO 用户计费
  • SSO 管理员:具有 isAccountOwner 或 isAdminAdmin 标志的用户作为 SSO 管理员单独计费(费率与常规租户管理员相同)
  • SSO 版主:具有 isCommentModeratorAdmin 标志的用户作为 SSO 版主单独计费(费率与常规版主相同)

重要:为防止重复计费,系统会根据电子邮件地址自动对 SSO 用户与常规租户用户和版主进行去重。如果 SSO 用户与常规租户用户或版主具有相同的电子邮件,则不会重复计费。

访问控制

用户可以被划分到组中。这就是 groupIds 字段的用途,且该字段为可选。

@提及

默认情况下,当输入 @ 字符时,@mentions 将使用 username 来搜索其他 sso 用户。如果使用 displayName,则当存在与 displayName 匹配的结果时,会忽略与 username 的匹配,@mention 的搜索结果将使用 displayName。

订阅

使用 FastComments,用户可以通过点击评论小部件中的铃铛图标并点击订阅来订阅页面。

对于常规用户,我们会根据他们的通知设置向他们发送通知电子邮件。

对于 SSO 用户,我们为了向后兼容将其分开处理。只有当您将 optedInSubscriptionNotifications 设置为 true 时,用户才会收到这些额外的订阅通知电子邮件。

徽章

您可以使用 badgeConfig 属性为 SSO 用户分配徽章。徽章是在评论中显示在用户名称旁边的视觉指示。

  • badgeIds - 要分配给用户的徽章 ID 数组。这些是全局徽章,在所有页面上可见。必须是您在 FastComments 帐户中创建的有效徽章 ID。限制为 30 个徽章。
  • pageBadgeIds - 可选的徽章 ID 数组,作用域为当前页面(urlId)。这些徽章仅在分配它们的页面上显示。不同页面可以为同一用户设置不同的页面作用域徽章。
  • override - 如果为 true,则会用提供的徽章替换所有现有显示的徽章。全局徽章和页面作用域徽章独立覆盖 —— 覆盖全局徽章不会影响页面作用域徽章,反之亦然。如果为 false 或省略,则会将提供的徽章添加到现有徽章中。
  • update - 如果为 true,则在用户登录时会从租户配置更新徽章显示属性。

订阅结构 Internal Link

Subscription 对象表示用户的订阅。

当用户在评论组件中点击通知铃铛并点击“订阅此页面”时,会创建 Subscription 对象。

订阅也可以通过 API 创建。

拥有 Subscription 对象后,当在 Subscription 所关联页面的根目录下有新评论时,会生成 Notification 对象并发送电子邮件。是否发送电子邮件取决于用户类型。对于普通用户,这取决于 optedInNotifications。对于 SSO 用户,这取决于 optedInSubscriptionNotifications。请注意,某些应用程序可能没有网页可访问页面的概念,在这种情况下,只需将 urlId 设置为您订阅的项目的 id(与传递给评论组件的 urlId 值相同)。

Subscription 对象的结构如下:

Subscription 结构
Copy Copy
1
2interface Subscription {
3 id: string
4 tenantId: string
5 /** 使用 SSO 时,用户 id 的格式为 `<tenant id>:<user id>`。 **/
6 userId: string
7 anonUserId?: string
8 urlId: string
9 url?: string
10 pageTitle?: string
11 createdAt: string // 日期字符串
12}
13

租户每日使用情况结构 Internal Link

TenantDailyUsage 对象表示某个租户在特定日期的使用情况。如果某个租户在某一天没有任何活动,则该天将不会有 TenantDailyUsage 对象。

TenantDailyUsage 对象不是实时的,可能比实际使用情况滞后几分钟。

TenantDailyUsage 对象的结构如下:

TenantDailyUsage 结构
Copy Copy
1
2export interface TenantDailyUsage {
3 yearNumber: number
4 monthNumber: number
5 dayNumber: number
6 commentFetchCount?: number
7 commentCreateCount?: number
8 conversationCreateCount?: number
9 voteCount?: number
10 accountCreatedCount?: number
11 userMentionSearch?: number
12 hashTagSearch?: number
13 gifSearchTrending?: number
14 gifSearch?: number
15 apiCreditsUsed?: number
16 createdAt: string
17 billed: boolean
18 /** 计费时忽略。 **/
19 ignored: boolean
20}
21

租户结构 Internal Link

Tenant 定义了一个 FastComments.com 客户。具有白标(white labeling)权限的租户可以通过 API 创建租户。白标租户不能创建其他白标租户(仅允许一级嵌套)。

Tenant 对象的结构如下:

Tenant 结构
Copy Copy
1
2export enum SiteType {
3 Unknown = 0,
4 WordPress = 1
5}
6
7/** 这也可以通过 DomainConfig API 处理。 **/
8export interface TenantDomainConfig {
9 domain: string
10 emailFromName?: string
11 emailFromEmail?: string
12 createdAt?: string,
13 siteType?: FastCommentsSiteType, // 你可能想要 Unknown
14 logoSrc?: string, // 原始图片路径
15 logoSrc100px?: string, // 为缩略图调整大小
16 footerUnsubscribeURL?: string,
17 emailHeaders?: Record<string, string>,
18 disableUnsubscribeLinks?: boolean,
19 dkim?: {
20 domainName: string,
21 keySelector: string,
22 privateKey: string
23 }
24}
25
26export interface TenantBillingInfo {
27 name: string
28 address: string
29 city: string
30 state: string
31 zip: string
32 country: string
33}
34
35export enum TenantPaymentFrequency {
36 Monthly = 0,
37 Annually = 1
38}
39
40export interface Tenant {
41 id: string
42 name: string
43 email?: string
44 signUpDate: number; // 由于“历史遗留”原因,类型为 number
45 packageId?: string | null
46 paymentFrequency?: TenantPaymentFrequency
47 billingInfoValid?: boolean
48 billingHandledExternally?: boolean
49 createdBy?: string
50 isSetup?: boolean
51 domainConfiguration: FastCommentsAPITenantDomainConfig[]
52 billingInfo?: FastCommentsAPITenantBillingInfo
53 stripeCustomerId?: string
54 stripeSubscriptionId?: string
55 stripePlanId?: string
56 enableProfanityFilter?: boolean
57 enableSpamFilter?: boolean
58 lastBillingIssueReminderDate?: string
59 removeUnverifiedComments?: boolean
60 unverifiedCommentsTTLms?: number
61 commentsRequireApproval?: boolean
62 autoApproveCommentOnVerification?: boolean
63 sendProfaneToSpam?: boolean
64 /** @readonly - 根据 packageId 计算。 **/
65 hasFlexPricing?: boolean
66 /** @readonly **/
67 flexLastBilledAmount?: number
68 /** @readonly - 根据 packageId 计算。 **/
69 hasAuditing?: boolean
70 /** 你可以在租户中存储一个键值对,用于查询。键不能包含 "." 或 "$",且长度不能超过 100 个字符。值的长度不能超过 2k 个字符。 **/
71 meta?: Record<string, string | null>
72}
73

用户结构 Internal Link

User 是一个对象,代表所有用户的最小公共集合。

请记住,在 FastComments 中我们有许多不同的用户使用场景:

  • Secure SSO
  • Simple SSO
  • 租户用户(例如:管理员)
  • 评论者

此 API 适用于评论者以及通过 Simple SSO 创建的用户。基本上,任何通过您的网站创建的用户都可以通过此 API 访问。租户用户也可以通过这种方式获取,但通过与 /tenant-users/ API 交互,您将获得更多信息。

对于 Secure SSO,请使用 /sso-users/ API。

您无法更新这些类型的用户。他们是通过您的网站创建的账户,因此我们提供一些基本的只读访问权限,但您无法进行更改。如果您想要这种类型的流程,您需要设置 Secure SSO。

User 对象的结构如下:

User 结构
Copy Copy
1
2export interface User {
3 /** 这也是评论对象上用作 userId 的 id。 **/
4 id: string
5 username: string
6 /** 例如,指向评论者博客的链接。 **/
7 websiteUrl?: string
8 email: string
9 signUpDate: number
10 createdFromUrlId: string
11 createdFromTenantId: string
12 avatarSrc?: string
13 locale: FastCommentsLocale
14 displayLabel?: string
15 karma?: number
16}
17

投票结构 Internal Link

A Vote 对象表示用户留下的投票。

评论与投票之间的关系通过 commentId 来定义。

Vote 对象的结构如下:

Vote 对象结构
Copy Copy
1
2interface Vote {
3 id: string
4 urlId: string
5 commentId: string
6 userId: string
7 direction: 1 | -1
8 createdAt: string
9}
10

问题配置结构 Internal Link

FastComments 提供了一种构建问题并汇总其结果的方式。一个问题的示例(以下称为 QuestionConfig)可以是星级评分、滑块,或 NPS 问题(由 type 决定)。

问题数据可以按单个、组合、随时间、总体、按页面等方式汇总。

该框架具备构建客户端小部件(在此 API 之前由您的服务器处理)、管理面板和报表工具所需的所有功能。

首先,我们必须定义一个 QuestionConfig。其结构如下:

QuestionConfig 结构
Copy Copy
1
2type QuestionConfigType = 'nps' | 'slider' | 'star' | 'thumbs';
3
4interface QuestionConfig {
5 id: string
6 tenantId: string
7 name: string
8 question: string
9 helpText?: string
10 createdAt: string
11 createdBy: string
12 /** 只读 - 对每个新响应递增。 **/
13 usedCount: number
14 /** 表示配置最后一次被使用(留下结果)的日期字符串。 **/
15 lastUsed?: string
16 type: QuestionConfigType
17 numStars?: number
18 min?: number
19 max?: number
20 defaultValue?: number
21 labelNegative?: string
22 labelPositive?: string
23 subQuestionIds?: string[]
24 alwaysShowSubQuestions?: boolean
25 reportingOrder: number
26}
27

问题结果结构 Internal Link

为了保存问题的结果,您需要创建一个 QuestionResult。您随后可以对问题结果进行汇总,并且 将它们与评论关联以用于报告目的。

QuestionResult 结构
Copy Copy
1
2interface QuestionResultMeta {
3 name: string
4 values: string[]
5}
6
7interface QuestionResult {
8 id: string
9 tenantId: string
10 urlId: string
11 anonUserId?: string
12 userId?: string
13 createdAt?: string
14 value: number
15 commentId?: string
16 questionId: string
17 meta?: QuestionResultMeta[]
18}
19

用户徽章结构 Internal Link

UserBadge 是一个对象,表示分配给 FastComments 系统中用户的徽章。

徽章可以根据用户的活动(例如评论数量、回复时间、资深状态)自动分配,或由站点管理员手动分配。

UserBadge 对象的结构如下:

UserBadge 结构
Copy Copy
1
2export interface UserBadge {
3 /** 此用户徽章分配的唯一标识符 */
4 id: string
5 /** 此徽章分配到的用户的 ID */
6 userId: string
7 /** 来自租户徽章目录的徽章定义的 ID */
8 badgeId: string
9 /** 创建/分配此徽章的租户的 ID */
10 fromTenantId: string
11 /** 此徽章创建时间(自纪元以来的毫秒数) */
12 createdAt?: number
13 /** 用户获得此徽章的时间(自纪元以来的毫秒数) */
14 receivedAt?: number
15 /**
16 * 徽章类型:
17 * 0=CommentCount, 1=CommentUpVotes, 2=CommentReplies, 3=CommentsPinned,
18 * 4=Veteran, 5=NightOwl, 6=FastReplyTime, 7=ModeratorCommentsDeleted,
19 * 8=ModeratorCommentsApproved, 9=ModeratorCommentsUnapproved, 10=ModeratorCommentsReviewed,
20 * 11=ModeratorCommentsMarkedSpam, 12=ModeratorCommentsMarkedNotSpam, 13=RepliedToSpecificPage,
21 * 14=Manual
22 */
23 type: number
24 /** 对于基于阈值的徽章,阈值数值 */
25 threshold?: number
26 /** 徽章的名称/标签 */
27 name?: string
28 /** 徽章的详细描述 */
29 description?: string
30 /** 徽章上显示的文本 */
31 displayLabel?: string
32 /** 徽章所显示图像的 URL */
33 displaySrc?: string
34 /** 徽章的背景颜色(十六进制代码) */
35 backgroundColor?: string
36 /** 徽章的边框颜色(十六进制代码) */
37 borderColor?: string
38 /** 徽章的文本颜色(十六进制代码) */
39 textColor?: string
40 /** 用于样式的额外 CSS 类 */
41 cssClass?: string
42 /** 对于资深用户徽章,时间阈值(毫秒) */
43 veteranUserThresholdMillis?: number
44 /** 此徽章是否显示在用户的评论上 */
45 displayedOnComments: boolean
46 /** 徽章的显示顺序 */
47 order?: number
48 /** 如果设置,此徽章仅在具有匹配 urlId 的页面上显示。全局徽章为 null。 */
49 urlId?: string | null
50}
51

用户徽章进度结构 Internal Link

UserBadgeProgress 是一个对象,表示用户在 FastComments 系统中获取各种徽章的进度。

此跟踪有助于确定何时应根据用户在社区的活动和参与自动授予徽章。

UserBadgeProgress 对象的结构如下:

UserBadgeProgress 结构
Copy Copy
1
2export interface UserBadgeProgress {
3 /** 此进度记录的唯一标识符 */
4 id: string
5 /** 此进度记录所属租户的 ID */
6 tenantId: string
7 /** 此进度记录所跟踪的用户的 ID */
8 userId: string
9 /** 用户在系统中的第一条评论的 ID */
10 firstCommentId?: string
11 /** 用户第一条评论的日期(自纪元以来的毫秒数) */
12 firstCommentDate?: number
13 /** 基于用户活动自动计算的信任因子 */
14 autoTrustFactor?: number
15 /** 由管理员手动设置的信任因子 */
16 manualTrustFactor?: number
17 /** 包含各种指标的详细进度对象,键与 BadgeType 枚举匹配 */
18 progress: {
19 /** 0: CommentCount - 用户发布的评论数量 */
20 '0'?: number
21 /** 1: CommentUpVotes - 用户收到的赞数 */
22 '1'?: number
23 /** 2: CommentReplies - 用户发布的回复数量 */
24 '2'?: number
25 /** 3: CommentsPinned - 用户的被置顶评论数量 */
26 '3'?: number
27 /** 4: Veteran - 用户账户年龄 */
28 '4'?: number
29 /** 5: NightOwl - 用户在夜间时段发布的次数 */
30 '5'?: number
31 /** 6: FastReplyTime - 平均回复时间(以毫秒为单位) */
32 '6'?: number
33 /** 7: ModeratorCommentsDeleted - 针对版主徽章,已删除评论的数量 */
34 '7'?: number
35 /** 8: ModeratorCommentsApproved - 针对版主徽章,已通过审核的评论数量 */
36 '8'?: number
37 /** 9: ModeratorCommentsUnapproved - 针对版主徽章,未批准的评论数量 */
38 '9'?: number
39 /** 10: ModeratorCommentsReviewed - 针对版主徽章,已审核的评论数量 */
40 '10'?: number
41 /** 11: ModeratorCommentsMarkedSpam - 针对版主徽章,被标记为垃圾评论的数量 */
42 '11'?: number
43 /** 12: ModeratorCommentsMarkedNotSpam - 针对版主徽章,被标记为非垃圾评论的数量 */
44 '12'?: number
45 /** 13: RepliedToSpecificPage - 针对每个页面的回复数量 */
46 '13'?: Record<string, number>
47 }
48}
49

总结

我们希望您认为我们的 API 文档详尽且易于理解。如果您发现任何遗漏,请在下方告诉我们。