feat(通知): SafeW 只发正文并注入推送时间
去掉渠道标题前缀,模板可用 pushedAt 显示本地推送时间。
This commit is contained in:
@@ -307,9 +307,11 @@ Body 同创建。成功:`{"ok": true}`
|
||||
|
||||
```
|
||||
### {{.symbol}} {{case .action "OPEN" "开仓" "CLOSE" "平仓" "GAIN" "止盈" "SELL" "卖出" "ADD" "加仓" "REDUCE" "减仓"}}
|
||||
{{line "币种" .symbol}}{{line "周期" .period}}{{line "方向" .side}}{{line "价格" .price}}{{line "平均价" .totalAvgPx}}{{line "止盈价" .takeProfitPrice}}{{line "止损价" .stopLossPrice}}
|
||||
{{line "币种" .symbol}}{{line "周期" .period}}{{line "方向" .side}}{{line "价格" .price}}{{line "平均价" .totalAvgPx}}{{line "止盈价" .takeProfitPrice}}{{line "止损价" .stopLossPrice}}{{line "推送时间" .pushedAt}}
|
||||
```
|
||||
|
||||
渲染时会自动注入 `event`、`pushedAt`(本地时间 `2006.01.02 15:04:05`)。
|
||||
|
||||
也可用事件名:`{{case .event ".open" "开仓" ".close" "平仓"}}`
|
||||
|
||||
等价写法:`{{with .totalAvgPx}}平均价:{{.}}{{end}}`
|
||||
@@ -426,7 +428,7 @@ SMTP 使用全局 `config.yaml` 的 `smtp` 段;支持 587 STARTTLS / 465 TLS
|
||||
}
|
||||
```
|
||||
|
||||
`chat_id` 可为数字或字符串(含 `@username`)。消息以纯文本发送,不做 Markdown 转义。
|
||||
`chat_id` 可为数字或字符串(含 `@username`)。消息以纯文本发送正文,不附带 `{source}: {event}` 标题,也不做 Markdown 转义。
|
||||
|
||||
#### `POST /api/v1/channels/safew/chats` — 列出已监控的 SafeW 群
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *SafeWSender) Send(title, content string, config json.RawMessage) error
|
||||
|
||||
payload := safewMessage{
|
||||
ChatID: chatID,
|
||||
Text: title + "\n" + content,
|
||||
Text: content,
|
||||
}
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestSafeWSenderSendSuccess(t *testing.T) {
|
||||
if gotBody["parse_mode"] != nil && gotBody["parse_mode"] != "" {
|
||||
t.Fatalf("parse_mode = %#v, want omitted/plain", gotBody["parse_mode"])
|
||||
}
|
||||
wantText := "hello_world.\nprice=1.5"
|
||||
wantText := "price=1.5"
|
||||
if gotBody["text"] != wantText {
|
||||
t.Fatalf("text = %#v, want %#v", gotBody["text"], wantText)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"aiaa-notification-service/internal/condition"
|
||||
"aiaa-notification-service/internal/engine"
|
||||
@@ -85,6 +86,9 @@ func (s *Service) Process(ctx context.Context, req Request) (Result, error) {
|
||||
if _, ok := req.Data["event"]; !ok && req.Event != "" {
|
||||
req.Data["event"] = req.Event
|
||||
}
|
||||
if _, ok := req.Data["pushedAt"]; !ok {
|
||||
req.Data["pushedAt"] = time.Now().In(time.Local).Format("2006.01.02 15:04:05")
|
||||
}
|
||||
|
||||
content, err := s.renderer.Render(tmpl.Content, req.Data)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"aiaa-notification-service/internal/engine"
|
||||
@@ -28,9 +29,15 @@ func (f *fakeTemplates) GetTemplate(context.Context, int) (*model.Template, erro
|
||||
return f.tmpl, f.err
|
||||
}
|
||||
|
||||
type fakeRouter struct{ channels []string }
|
||||
type fakeRouter struct {
|
||||
channels []string
|
||||
title string
|
||||
content string
|
||||
}
|
||||
|
||||
func (f *fakeRouter) Route(context.Context, *model.Rule, string, string) []string {
|
||||
func (f *fakeRouter) Route(_ context.Context, _ *model.Rule, title, content string) []string {
|
||||
f.title = title
|
||||
f.content = content
|
||||
return f.channels
|
||||
}
|
||||
|
||||
@@ -111,6 +118,29 @@ func TestProcessTemplateCanSwitchOnEvent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessInjectsPushedAt(t *testing.T) {
|
||||
rt := &fakeRouter{channels: []string{"safew:1"}}
|
||||
svc := newSvc(
|
||||
&fakeMatcher{rule: &model.Rule{ID: 9, TemplateID: 1}},
|
||||
&fakeTemplates{tmpl: &model.Template{ID: 1, Content: `{{line "推送时间" .pushedAt}}`}},
|
||||
rt,
|
||||
)
|
||||
res, err := svc.Process(context.Background(), Request{
|
||||
Source: &model.Source{ID: 1, Name: "crypto-strategy"},
|
||||
Event: "trade.open",
|
||||
Data: map[string]interface{}{"symbol": "QNT"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !res.Matched {
|
||||
t.Fatalf("%+v", res)
|
||||
}
|
||||
if !strings.Contains(rt.content, "推送时间:") {
|
||||
t.Fatalf("content=%q", rt.content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessInvalidConditions(t *testing.T) {
|
||||
raw := json.RawMessage(`not-json`)
|
||||
svc := newSvc(&fakeMatcher{rule: &model.Rule{ID: 1, TemplateID: 1, Conditions: &raw}}, &fakeTemplates{}, &fakeRouter{})
|
||||
|
||||
Reference in New Issue
Block a user