@@ -8,11 +8,11 @@ import (
88 "net"
99 "net/http"
1010 "net/url"
11- "regexp"
1211 "strings"
1312 "time"
1413
1514 "github.com/fastclaw-ai/fastclaw/internal/toolproviders"
15+ webfetchprovider "github.com/fastclaw-ai/fastclaw/internal/toolproviders/webfetch"
1616)
1717
1818type webFetchArgs struct {
@@ -26,8 +26,6 @@ const (
2626 fetchUserAgent = "FastClaw/1.0 (AI Agent Web Fetcher)"
2727)
2828
29- var htmlTagRe = regexp .MustCompile (`<[^>]*>` )
30-
3129// safeFetchClient is an http.Client whose dialer rejects private,
3230// loopback, link-local, multicast, and CGNAT addresses — the SSRF
3331// defense for web_fetch. The check runs at DIAL time, after DNS has
@@ -275,15 +273,17 @@ func webFetchTool(ctx context.Context, r *Registry, rawArgs json.RawMessage) (st
275273 return "" , fmt .Errorf ("HTTP %d: %s" , resp .StatusCode , resp .Status )
276274 }
277275
278- // Read body with a limit to prevent memory issues
279- limitReader := io .LimitReader (resp .Body , int64 (maxLen * 3 )) // read more than needed since HTML is verbose
276+ // Read body with a limit to prevent memory issues. WeChat articles
277+ // need a bounded larger window because the readable #js_content node
278+ // can appear after a long script/config prelude.
279+ limitReader := io .LimitReader (resp .Body , webfetchprovider .FetchReadLimit (args .URL , maxLen ))
280280 body , err := io .ReadAll (limitReader )
281281 if err != nil {
282282 return "" , fmt .Errorf ("read body: %w" , err )
283283 }
284284
285- // Strip HTML tags
286- text := stripHTML ( string (body ))
285+ // Strip HTML tags, using site-specific article extraction when needed.
286+ text := webfetchprovider . HTMLToText ( args . URL , string (body ))
287287
288288 // Truncate to max length (UTF-8 safe: back up to a valid rune boundary).
289289 if len (text ) > maxLen {
@@ -312,33 +312,3 @@ func assertHTTPScheme(rawURL string) error {
312312 }
313313 return nil
314314}
315-
316- // stripHTML removes HTML tags and cleans up whitespace.
317- func stripHTML (html string ) string {
318- // Remove script and style elements entirely
319- scriptRe := regexp .MustCompile (`(?is)<script[^>]*>.*?</script>` )
320- html = scriptRe .ReplaceAllString (html , "" )
321- styleRe := regexp .MustCompile (`(?is)<style[^>]*>.*?</style>` )
322- html = styleRe .ReplaceAllString (html , "" )
323-
324- // Remove HTML tags
325- text := htmlTagRe .ReplaceAllString (html , " " )
326-
327- // Decode common HTML entities
328- text = strings .ReplaceAll (text , "&" , "&" )
329- text = strings .ReplaceAll (text , "<" , "<" )
330- text = strings .ReplaceAll (text , ">" , ">" )
331- text = strings .ReplaceAll (text , """ , "\" " )
332- text = strings .ReplaceAll (text , "'" , "'" )
333- text = strings .ReplaceAll (text , " " , " " )
334-
335- // Collapse whitespace
336- spaceRe := regexp .MustCompile (`[ \t]+` )
337- text = spaceRe .ReplaceAllString (text , " " )
338-
339- // Collapse multiple newlines
340- nlRe := regexp .MustCompile (`\n{3,}` )
341- text = nlRe .ReplaceAllString (text , "\n \n " )
342-
343- return strings .TrimSpace (text )
344- }
0 commit comments