diff --git a/service/modules/renderers/friends/friends.go b/service/modules/renderers/friends/friends.go index 48dc00af..c1b44d9f 100644 --- a/service/modules/renderers/friends/friends.go +++ b/service/modules/renderers/friends/friends.go @@ -97,31 +97,50 @@ var svg = ` ` +// Name 是网站的名字,取首字符的大写作为图标。 +func genSvgURL(name string) string { + // 预告填充成 SVG 首字母(因为可能加载失败)。 + var first rune + if len(name) > 0 { + first, _ = utf8.DecodeRune([]byte(strings.ToUpper(name))) + } + letter := fmt.Sprintf(svg, string(first)) + return `data:image/svg+xml;base64,` + base64.StdEncoding.EncodeToString([]byte(letter)) +} + +// TODO 更好的做法是 parse 页面,取 0 { - first, _ = utf8.DecodeRune([]byte(strings.ToUpper(fr.Name))) + u, err := resolveIconURL(fr.URL, fr.Icon) + if err != nil { + log.Println(err) + continue } - letter := fmt.Sprintf(svg, string(first)) - fr.iconDataURL = `data:image/svg+xml;base64,` + base64.StdEncoding.EncodeToString([]byte(letter)) - go func(i int, fr *Friend) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, fr.Icon, nil) + go func(i int, u string, fr *Friend) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { log.Println(`头像请求失败:`, err) return @@ -146,6 +165,6 @@ func (f *Friends) prepareIconURL(ctx context.Context, fs []*Friend) { } uri := fmt.Sprintf(`data:%s;base64,%s`, contentType, base64.StdEncoding.EncodeToString(body)) fr.iconDataURL = uri - }(i, fr) + }(i, u, fr) } } diff --git a/service/modules/renderers/friends/friends_test.go b/service/modules/renderers/friends/friends_test.go new file mode 100644 index 00000000..257f7bfc --- /dev/null +++ b/service/modules/renderers/friends/friends_test.go @@ -0,0 +1,60 @@ +package friends + +import "testing" + +func TestResolveURL(t *testing.T) { + testCases := []struct { + SiteURL string + FaviconURL string + Want string + }{ + { + SiteURL: `https://blog.twofei.com`, + FaviconURL: ``, + Want: `https://blog.twofei.com/favicon.ico`, + }, + { + SiteURL: `https://blog.twofei.com`, + FaviconURL: `https://blog.twofei.com/favicon.avif`, + Want: `https://blog.twofei.com/favicon.avif`, + }, + { + SiteURL: `https://blog.twofei.com`, + FaviconURL: `/favicon.avif`, + Want: `https://blog.twofei.com/favicon.avif`, + }, + { + SiteURL: `https://blog.twofei.com`, + FaviconURL: `favicon.avif`, + Want: `https://blog.twofei.com/favicon.avif`, + }, + { + SiteURL: `https://blog.twofei.com/sub`, + FaviconURL: `favicon.avif`, + Want: `https://blog.twofei.com/favicon.avif`, + }, + { + SiteURL: `https://blog.twofei.com/sub`, + FaviconURL: `/favicon.avif`, + Want: `https://blog.twofei.com/favicon.avif`, + }, + { + SiteURL: `https://blog.twofei.com/sub/`, + FaviconURL: `favicon.avif`, + Want: `https://blog.twofei.com/sub/favicon.avif`, + }, + { + SiteURL: `https://blog.twofei.com/sub/`, + FaviconURL: `/favicon.avif`, + Want: `https://blog.twofei.com/favicon.avif`, + }, + } + + for i, tc := range testCases { + u, err := resolveIconURL(tc.SiteURL, tc.FaviconURL) + if err != nil || u != tc.Want { + t.Errorf(`error: %d: %s %s %s`, i, tc.SiteURL, tc.FaviconURL, tc.Want) + continue + } + } +}