feat(notify): 新增站级 Webhook 与用户级 Telegram 通知通道

在现有邮件通知之外补两条广播/点对点通道:

- 站级 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 结构与未配置时的静默跳过。
This commit is contained in:
tamakyi
2026-10-29 19:27:23 +08:00
parent 1e9ffc0718
commit b9060c982b
17 changed files with 519 additions and 218 deletions
+7 -2
View File
@@ -109,8 +109,11 @@ type User struct {
Backgroundimagealpha string `json:"Backgroundimagealpha"`
// Theme 箱主选择的主题 slug(空串=跟随站点默认,default=基础外观)。
// 取值合法性由 route/user 在写入前校验,读取侧由 internal/theme 回退。
Theme string `json:"theme" gorm:"column:theme;type:varchar(32);default:''"`
Notify NotifyType `json:"notify"`
Theme string `json:"theme" gorm:"column:theme;type:varchar(32);default:''"`
Notify NotifyType `json:"notify"`
// TelegramChatID 箱主自填的 Telegram chat_id,配合站级 [notify] telegram_bot_token
// 实现「新提问」点对点通知。空串 = 未开启 Telegram 通知。
TelegramChatID string `json:"-" gorm:"column:telegram_chat_id;type:varchar(64);default:''"`
HarassmentSetting HarassmentSettingType `json:"harassment_setting" gorm:"default:'none'"`
BlockWords string `json:"-"`
// 防骚扰设置取值 none / register_only(见下方常量)。历史账号此列为空串(建表时无默认值),
@@ -343,6 +346,7 @@ type UpdateUserOptions struct {
Bloglink string
UseCustomQRColor bool
Notify NotifyType
TelegramChatID string
Language string
}
@@ -378,6 +382,7 @@ func (db *users) Update(ctx context.Context, id uint, opts UpdateUserOptions) er
"Bloglink": opts.Bloglink,
"UseCustomQRColor": opts.UseCustomQRColor,
"Notify": opts.Notify,
"TelegramChatID": opts.TelegramChatID,
"Language": opts.Language,
}