在现有邮件通知之外补两条广播/点对点通道: - 站级 Webhook([notify] webhook_url):新提问 / 新回答 / 新追问 / 新私聊 / 新投票回复统一 POST JSON 到通用 Webhook(Discord / Slack / 企业微信机器人等)。 消息体同时带 content(Discord 直接可用)与结构化 event/title/body/link 字段。 - 用户级 Telegram([notify] telegram_bot_token + users.telegram_chat_id):箱主 自填 Chat ID,新提问 / 新投票回复点对点推送。 设计约束:通知是「尽力而为」旁路 —— 任一通道发送失败只记日志、不向上抛错, 绝不影响提问/回答的主流程;通道未配置时静默跳过。发送放在 shared.SafeGo 异步 执行,与现有邮件通知同一条路径。 6 处业务触发点接入 notify;私聊消息保留仅邮件(已有 5 分钟节流,避免广播刷屏)。 profile 页新增 Telegram Chat ID 输入框 + 四语言字典。app.sample.ini 加 [notify] 段。 测试覆盖 webhook 的 JSON 结构与未配置时的静默跳过。
55 lines
1.8 KiB
Go
55 lines
1.8 KiB
Go
package notify
|
||
|
||
import (
|
||
"encoding/json"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
|
||
"github.com/tamakyi/TamaBox/internal/conf"
|
||
)
|
||
|
||
// TestSendWebhookPostsJSON 验证 webhook 发送的是带 content/event/title 的 JSON,
|
||
// 且能正确 POST 到目标 URL。
|
||
func TestSendWebhookPostsJSON(t *testing.T) {
|
||
var got map[string]interface{}
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
require.Equal(t, http.MethodPost, r.Method)
|
||
require.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||
require.NoError(t, json.NewDecoder(r.Body).Decode(&got))
|
||
w.WriteHeader(http.StatusOK)
|
||
}))
|
||
defer srv.Close()
|
||
|
||
sendWebhook(t.Context(), srv.URL, event{
|
||
Event: "new_question",
|
||
Title: "新的提问",
|
||
Body: "你好",
|
||
Link: "https://example.com/_/box/1",
|
||
Time: "2026-10-29 19:00:00",
|
||
})
|
||
|
||
require.Equal(t, "new_question", got["event"])
|
||
require.Equal(t, "新的提问", got["title"])
|
||
require.Equal(t, "你好", got["body"])
|
||
require.NotEmpty(t, got["content"], "Discord 兼容的 content 字段必须有")
|
||
}
|
||
|
||
// TestSendWebhookSkipsWhenUnconfigured 未配置 webhook_url 时 Send 静默跳过(不 panic)。
|
||
func TestSendWebhookSkipsWhenUnconfigured(t *testing.T) {
|
||
conf.Notify.WebhookURL = ""
|
||
defer func() { conf.Notify.WebhookURL = "" }()
|
||
// 不应 panic,也不应发起任何请求。
|
||
Send(t.Context(), "new_question", "t", "b", "l")
|
||
}
|
||
|
||
// TestSendToTelegramSkipsWhenUnconfigured chatID 或 bot token 为空时静默跳过。
|
||
func TestSendToTelegramSkipsWhenUnconfigured(t *testing.T) {
|
||
conf.Notify.TelegramBotToken = ""
|
||
defer func() { conf.Notify.TelegramBotToken = "" }()
|
||
SendToTelegram(t.Context(), "", "hello") // 空 chatID
|
||
SendToTelegram(t.Context(), "123", "hello") // 空 token
|
||
}
|