@@ -6,6 +6,11 @@ import (
66 "time"
77)
88
9+ type plannedForwardTarget struct {
10+ ID string
11+ IncludeLinks bool
12+ }
13+
914func processPendingMessages () {
1015 // 防止消息发送队列重叠并发引发重复发送通知
1116 if ! processingMutex .TryLock () {
@@ -52,93 +57,73 @@ func processPendingMessages() {
5257 continue
5358 }
5459
55- // 匹配规则,找到第一条匹配的规则进行发送
56- ruleMatched := false
57- filterBlocked := false
58- for _ , rule := range rules {
59- rule = normalizeForwardRule (rule )
60- // 检查源账号匹配
61- if ! ruleMatchesSource (rule , msg .AccountID ) {
62- continue
63- }
64-
65- filterCtx := buildFilterContext (& msg , accounts )
66- filterResult := applyFilterRules (rule .FilterRuleIDs , filterRules , & msg , filterCtx )
67- if ! filterResult .Allowed {
68- filterBlocked = true
69- addLog (fmt .Sprintf ("消息被过滤规则拦截 [%s]: %s" , displaySubject (msg .Subject ), filterResult .Reason ), "info" )
70- continue
71- }
72-
73- targetIDs := rule .TargetWebhooks
74- if len (targetIDs ) == 0 {
75- continue
76- }
77-
78- ruleMatched = true
60+ // 汇总所有匹配规则的目标。同一目标被多条规则命中时只发送一次。
61+ filterCtx := buildFilterContext (& msg , accounts )
62+ targets , sourceMatched , filterBlocked := planForwardTargets (rules , filterRules , & msg , filterCtx )
63+ ruleMatched := len (targets ) > 0
64+ for _ , reason := range filterBlocked {
65+ addLog (fmt .Sprintf ("消息被过滤规则拦截 [%s]: %s" , displaySubject (msg .Subject ), reason ), "info" )
66+ }
7967
80- // 发送
81- var sendErr error
68+ if ruleMatched {
8269 dateStr := displayMessageDate (msg .Date )
8370 subjectForSend := displaySubject (msg .Subject )
8471 senderForSend := filterCtx .DisplaySender
85-
86- // 智能提取验证码并高亮前置
87- displayBody := formatForwardBody (msg .Body , rule .IncludeLinks )
88- var verificationCode string
89- if matches := codeRegex .FindStringSubmatch (msg .Subject ); len (matches ) > 1 {
90- verificationCode = matches [1 ]
91- } else if matches := codeRegex .FindStringSubmatch (msg .Body ); len (matches ) > 1 {
92- verificationCode = matches [1 ]
93- }
94-
95- if verificationCode != "" {
96- displayBody = fmt .Sprintf ("**[智能提取验证码] %s**\n \n %s" , verificationCode , displayBody )
97- }
98-
99- sentTargets := make ([]string , 0 , len (targetIDs ))
72+ verificationCode := extractVerificationCode (& msg )
73+ sentTargets := make ([]string , 0 , len (targets ))
10074 failedTargets := make ([]string , 0 )
101- for _ , targetID := range targetIDs {
102- webhook , ok := webhooks [targetID ]
103- if ! ok || ! webhook .Enabled {
75+ historyTargets := make ([]string , 0 , len (targets ))
76+
77+ for _ , planned := range targets {
78+ webhook , ok := webhooks [planned .ID ]
79+ if ! ok {
80+ failedTargets = append (failedTargets , fmt .Sprintf ("%s: 目标渠道不存在" , planned .ID ))
81+ historyTargets = append (historyTargets , planned .ID + "(失败)" )
82+ continue
83+ }
84+ if ! webhook .Enabled {
85+ failedTargets = append (failedTargets , fmt .Sprintf ("%s: 目标渠道已禁用" , webhook .Name ))
86+ historyTargets = append (historyTargets , webhook .Name + "(失败)" )
10487 continue
10588 }
10689
107- sendErr = sendToWebhookTarget (webhook , accounts , subjectForSend , senderForSend , dateStr , displayBody )
90+ displayBody := formatForwardBody (msg .Body , planned .IncludeLinks )
91+ if verificationCode != "" {
92+ displayBody = fmt .Sprintf ("**[智能提取验证码] %s**\n \n %s" , verificationCode , displayBody )
93+ }
10894
109- if sendErr != nil {
110- failedTargets = append (failedTargets , fmt .Sprintf ("%s: %v" , webhook .Name , sendErr ))
111- addLog (fmt .Sprintf ("发送失败 [%s -> %s]: %v" , subjectForSend , webhook .Name , sendErr ), "error" )
95+ if err := sendToWebhookTarget (webhook , accounts , subjectForSend , senderForSend , dateStr , displayBody ); err != nil {
96+ failedTargets = append (failedTargets , fmt .Sprintf ("%s: %v" , webhook .Name , err ))
97+ historyTargets = append (historyTargets , webhook .Name + "(失败)" )
98+ addLog (fmt .Sprintf ("发送失败 [%s -> %s]: %v" , subjectForSend , webhook .Name , err ), "error" )
11299 continue
113100 }
114101 sentTargets = append (sentTargets , webhook .Name )
102+ historyTargets = append (historyTargets , webhook .Name )
115103 addLog (fmt .Sprintf ("转发成功 [%s -> %s]" , subjectForSend , webhook .Name ), "success" )
116104 }
117105
106+ msg .TargetType = "multi"
107+ msg .TargetName = strings .Join (historyTargets , ", " )
118108 if len (sentTargets ) == 0 {
119109 msg .RetryCount ++
120110 msg .ErrorMessage = strings .Join (failedTargets , "; " )
121- if msg .ErrorMessage == "" {
122- msg .ErrorMessage = "没有可用的目标 Webhook"
123- }
124111 saveMessage (& msg )
125112 } else {
126113 msg .Status = "sent"
127- msg .TargetType = "multi"
128- msg .TargetName = strings .Join (sentTargets , ", " )
114+ msg .ErrorMessage = ""
129115 if len (failedTargets ) > 0 {
130116 msg .ErrorMessage = "部分目标失败: " + strings .Join (failedTargets , "; " )
131117 }
132118 now := time .Now ()
133119 msg .SentAt = & now
134120 saveMessage (& msg )
135121 }
136- break // 匹配到第一条规则并尝试发送后,不再继续匹配后续规则
137122 }
138123
139- // 没有匹配到任何转发规则的消息,标记为 no_rule 防止永远卡在 pending 堵塞队列
124+ // 没有匹配到任何转发规则的消息,标记状态防止永远卡在 pending 堵塞队列。
140125 if ! ruleMatched {
141- if filterBlocked {
126+ if sourceMatched && len ( filterBlocked ) > 0 {
142127 msg .Status = "filtered"
143128 msg .ErrorMessage = "已被过滤规则拦截"
144129 saveMessage (& msg )
@@ -153,6 +138,49 @@ func processPendingMessages() {
153138 }
154139}
155140
141+ func planForwardTargets (rules []ForwardRule , filterRules map [string ]FilterRule , msg * Message , filterCtx filterContext ) ([]plannedForwardTarget , bool , []string ) {
142+ targets := make ([]plannedForwardTarget , 0 )
143+ targetIndexes := make (map [string ]int )
144+ sourceMatched := false
145+ blockedReasons := make ([]string , 0 )
146+
147+ for _ , rule := range rules {
148+ rule = normalizeForwardRule (rule )
149+ if ! ruleMatchesSource (rule , msg .AccountID ) {
150+ continue
151+ }
152+ sourceMatched = true
153+
154+ filterResult := applyFilterRules (rule .FilterRuleIDs , filterRules , msg , filterCtx )
155+ if ! filterResult .Allowed {
156+ blockedReasons = append (blockedReasons , filterResult .Reason )
157+ continue
158+ }
159+
160+ for _ , targetID := range rule .TargetWebhooks {
161+ if index , exists := targetIndexes [targetID ]; exists {
162+ // 任一匹配规则要求保留链接时,为该目标保留链接。
163+ targets [index ].IncludeLinks = targets [index ].IncludeLinks || rule .IncludeLinks
164+ continue
165+ }
166+ targetIndexes [targetID ] = len (targets )
167+ targets = append (targets , plannedForwardTarget {ID : targetID , IncludeLinks : rule .IncludeLinks })
168+ }
169+ }
170+
171+ return targets , sourceMatched , blockedReasons
172+ }
173+
174+ func extractVerificationCode (msg * Message ) string {
175+ if matches := codeRegex .FindStringSubmatch (msg .Subject ); len (matches ) > 1 {
176+ return matches [1 ]
177+ }
178+ if matches := codeRegex .FindStringSubmatch (msg .Body ); len (matches ) > 1 {
179+ return matches [1 ]
180+ }
181+ return ""
182+ }
183+
156184func ruleMatchesSource (rule ForwardRule , accountID string ) bool {
157185 for _ , sourceID := range rule .SourceAccounts {
158186 if sourceID == "all" || sourceID == accountID {
0 commit comments