通知
针对需要关注的讨论的邮件汇总和 Webhook,包括签名验证。
Hearl 可以在讨论需要关注时第一时间通知你的团队。在 通知 页面里设置。只有所有者和管理员可以修改这些设置。
何时发送通知
Hearl 每 15 分钟检查一次。满足以下条件的讨论会被包含进来:
- 平台状态为 还没人回复 或 用户有新回复
- 进度仍为 未处理
- 自动判断认为它与你的产品有关,且没有不确定的答案
- 优先级不低于你设置的最低优先级:全部、中及以上,或只要高
每条讨论在每种状态下只通知一次。如果它以 还没人回复 通知过,之后用户又跟进了,就会再以 用户有新回复 通知一次。
邮件
打开 发送邮件 并添加收件人。列表留空则发给工作空间的所有成员。任何邮箱地址都可以,比如共用的客服邮箱,最多 20 个收件人。
每次检查会发送一封汇总邮件,最多列出 10 条讨论,其余的附有收件箱链接。在 通知语言 下选择英文或中文。
Webhook
填写 推送地址。它必须是公开的 https 网址。Hearl 会根据地址识别常见的聊天机器人,并发送它们需要的消息格式:
| 地址 | 发送格式 |
|---|---|
hooks.slack.com |
Slack 消息 |
feishu.cn, larksuite.com |
飞书文本消息 |
qyapi.weixin.qq.com |
企业微信 Markdown 消息 |
oapi.dingtalk.com |
钉钉 Markdown 消息 |
discord.com, discordapp.com |
Discord 消息 |
| 其他任何网址 | 通用 JSON(见下) |
页面会显示它识别出的类型。发送测试 会发送一条示例,页面还会显示最近一次推送的结果。请求超过 8 秒算作超时。
通用 JSON
{
"event": "conversations.needs_attention",
"workspace": { "id": "org_123", "name": "Acme" },
"text": "*2 conversations need attention* · Acme\n• [reddit] Sync stopped working after 2.3 — No reply yet <https://…>",
"conversations": [
{
"id": "c_8f2a",
"platform": "reddit",
"product": "Acme",
"title": "Sync stopped working after 2.3",
"author": "some_user",
"url": "https://www.reddit.com/r/…",
"status": "待回复",
"priority": "高",
"category": "故障",
"inbox_url": "https://hearl.org/app?c=c_8f2a"
}
],
"sent_at": "2026-11-08T09:15:00.000Z"
}
| 字段 | 说明 |
|---|---|
event |
conversations.needs_attention,测试时为 test(来自 发送测试)。 |
workspace |
工作空间的 id 和 name。 |
text |
可直接发布的摘要。Slack 只读取这个字段。 |
conversations |
本次推送包含的每条讨论。 |
sent_at |
推送发送的时间,ISO 8601 格式。 |
platform 的值是 x、reddit 或 hn。status、priority 和 category 是固定的中文代码,所以无论工作空间使用什么语言,你的代码都可以按它们匹配:
| 字段 | 代码 | 含义 |
|---|---|---|
status |
待回复 |
还没人回复 |
status |
用户有新回复 |
用户有新回复 |
priority |
高 / 中 / 低 |
高 / 中 / 低 |
category |
故障 |
故障 |
category |
求助 |
求助 |
category |
功能建议 |
功能建议 |
category |
价格 |
价格 |
category |
负面反馈 |
负面反馈 |
category |
正面反馈 |
正面反馈 |
category |
购买咨询 |
购买咨询 |
category |
其他 |
其他 |
验证签名
保存 Webhook 推送地址时,Hearl 会为它创建一个 签名密钥。你可以在通知页面显示或更换它。请求会带上这个请求头:
X-Hearloop-Signature: sha256=<hex>
<hex> 是用你的签名密钥对原始请求体计算的 HMAC-SHA256,以小写十六进制表示。请对收到的原始字节计算,在解析 JSON 之前进行,并用常量时间比较。
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
const secret = process.env.HEARL_WEBHOOK_SECRET;
app.post("/hearl", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + createHmac("sha256", secret).update(req.body).digest("hex");
const received = req.get("x-hearloop-signature") ?? "";
const ok =
received.length === expected.length &&
timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!ok) return res.status(401).end();
const payload = JSON.parse(req.body.toString("utf8"));
console.log(payload.event, payload.conversations.length);
res.status(200).end();
});
app.listen(3000);import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["HEARL_WEBHOOK_SECRET"].encode()
@app.post("/hearl")
def hearl():
body = request.get_data()
expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
received = request.headers.get("X-Hearloop-Signature", "")
if not hmac.compare_digest(received, expected):
abort(401)
payload = json.loads(body)
print(payload["event"], len(payload["conversations"]))
return "", 200返回任意 2xx 状态码即表示已确认收到推送。