55 "io"
66 "net"
77 "net/url"
8+ "regexp"
89 "strings"
910 "time"
1011
@@ -16,6 +17,8 @@ import (
1617
1718// ===================== 邮件处理 =====================
1819
20+ var markdownLinkRegex = regexp .MustCompile (`\[([^\]\n]{0,240})\]\((https?://[^)\s]+|长链接由于超长已被过滤)\)` )
21+
1922func cleanHTML (htmlStr string ) string {
2023 doc , err := goquery .NewDocumentFromReader (strings .NewReader (htmlStr ))
2124 if err != nil {
@@ -35,14 +38,14 @@ func cleanHTML(htmlStr string) string {
3538 href = sanitizeURL (href )
3639 if text == "" {
3740 text = "链接"
38- } else if len ( text ) > 80 {
39- text = text [: 77 ] + "..."
41+ } else {
42+ text = truncateRunes ( text , maxForwardLinkTextRunes )
4043 }
4144 text = escapeMarkdownText (text )
4245 if href != "" {
4346 href = escapeMarkdownLinkURL (href )
44- if len (href ) > 600 {
45- s .SetText (fmt . Sprintf ( "[%s](长链接由于超长已被过滤)" , text ) )
47+ if len (href ) > maxForwardURLLength {
48+ s .SetText (text )
4649 } else {
4750 s .SetText (fmt .Sprintf ("[%s](%s)" , text , href ))
4851 }
@@ -62,8 +65,8 @@ func cleanHTML(htmlStr string) string {
6265
6366 text := normalizeFormattedText (doc .Text ())
6467 text = urlRegex .ReplaceAllStringFunc (text , func (u string ) string {
65- if len (u ) > 600 {
66- return u [: 80 ] + "...(该段长链接由于超长已被过滤) "
68+ if len (u ) > maxForwardURLLength {
69+ return " "
6770 }
6871 return u
6972 })
@@ -146,19 +149,112 @@ func normalizeFormattedText(text string) string {
146149
147150func formatPlainTextBody (body string ) string {
148151 body = urlRegex .ReplaceAllStringFunc (body , func (u string ) string {
149- disp := u
150- if len (disp ) > 80 {
151- disp = disp [:77 ] + "..."
152+ if len (u ) > maxForwardURLLength {
153+ return ""
152154 }
155+ disp := truncateRunes (u , maxForwardLinkTextRunes )
153156 u = escapeMarkdownLinkURL (u )
154- if len (u ) > 600 {
155- return fmt .Sprintf ("[%s](长链接由于超长已被过滤)" , disp )
156- }
157157 return fmt .Sprintf ("[%s](%s)" , disp , u )
158158 })
159159 return normalizeFormattedText (body )
160160}
161161
162+ func formatForwardBody (body string , includeLinks bool ) string {
163+ body = normalizeFormattedText (body )
164+ if includeLinks {
165+ return normalizeFormattedText (formatURLsAsMarkdown (body ))
166+ }
167+ return normalizeFormattedText (removeLinksFromText (body ))
168+ }
169+
170+ func formatURLsAsMarkdown (text string ) string {
171+ var b strings.Builder
172+ last := 0
173+ matches := urlRegex .FindAllStringIndex (text , - 1 )
174+ for _ , loc := range matches {
175+ start , end := loc [0 ], loc [1 ]
176+ if isMarkdownURLDestination (text , start , end ) {
177+ continue
178+ }
179+ u := text [start :end ]
180+ b .WriteString (text [last :start ])
181+ if len (u ) <= maxForwardURLLength {
182+ disp := truncateRunes (u , maxForwardLinkTextRunes )
183+ b .WriteString (fmt .Sprintf ("[%s](%s)" , disp , escapeMarkdownLinkURL (u )))
184+ }
185+ last = end
186+ }
187+ if last == 0 {
188+ return text
189+ }
190+ b .WriteString (text [last :])
191+ return b .String ()
192+ }
193+
194+ func removeLinksFromText (text string ) string {
195+ text = markdownLinkRegex .ReplaceAllStringFunc (text , func (match string ) string {
196+ parts := markdownLinkRegex .FindStringSubmatch (match )
197+ if len (parts ) < 2 {
198+ return ""
199+ }
200+ return unescapeMarkdownText (parts [1 ])
201+ })
202+ text = urlRegex .ReplaceAllString (text , "" )
203+
204+ lines := strings .Split (text , "\n " )
205+ kept := make ([]string , 0 , len (lines ))
206+ for _ , line := range lines {
207+ if isDanglingLinkLabel (line ) {
208+ continue
209+ }
210+ kept = append (kept , line )
211+ }
212+ return strings .Join (kept , "\n " )
213+ }
214+
215+ func isMarkdownURLDestination (text string , start int , end int ) bool {
216+ if start <= 0 || text [start - 1 ] != '(' {
217+ return false
218+ }
219+ return (end < len (text ) && text [end ] == ')' ) || (end > start && text [end - 1 ] == ')' )
220+ }
221+
222+ func isDanglingLinkLabel (line string ) bool {
223+ line = strings .TrimSpace (line )
224+ line = strings .TrimRight (line , "::" )
225+ switch strings .ToLower (line ) {
226+ case "链接" , "url" , "link" :
227+ return true
228+ default :
229+ return false
230+ }
231+ }
232+
233+ func truncateRunes (text string , max int ) string {
234+ runes := []rune (text )
235+ if len (runes ) <= max {
236+ return text
237+ }
238+ if max <= 3 {
239+ return string (runes [:max ])
240+ }
241+ return string (runes [:max - 3 ]) + "..."
242+ }
243+
244+ func unescapeMarkdownText (text string ) string {
245+ replacer := strings .NewReplacer (
246+ `\\[` , `[` ,
247+ `\\]` , `]` ,
248+ `\\(` , `(` ,
249+ `\\)` , `)` ,
250+ "\\ `" , "`" ,
251+ `\\*` , `*` ,
252+ `\\_` , `_` ,
253+ `\\\\` , `\` ,
254+ )
255+ return replacer .Replace (text )
256+ }
257+
162258func normalizeFolderKey (folder string ) string {
163259 folder = strings .TrimSpace (folder )
164260 if folder == "" {
0 commit comments