132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"hash/fnv"
|
|
"strings"
|
|
|
|
"github.com/tinode/chat/server/store"
|
|
"github.com/tinode/chat/server/store/types"
|
|
)
|
|
|
|
const (
|
|
imAssetsConfigGroup = "im_assets"
|
|
imDefaultMaleAvatarURLsConfig = "default_user_avatar_male_urls"
|
|
imDefaultFemaleAvatarURLsConfig = "default_user_avatar_female_urls"
|
|
)
|
|
|
|
func applyDefaultUserAvatar(data map[string]any, userID, gender string) {
|
|
if data == nil {
|
|
return
|
|
}
|
|
if strings.TrimSpace(stringFromAny(data["avatar_file_id"])) == "" {
|
|
avatarURL := defaultUserAvatarURL(userID, gender)
|
|
if avatarURL == "" {
|
|
return
|
|
}
|
|
data["avatar_file_id"] = avatarURL
|
|
}
|
|
applyAvatarURL(data)
|
|
}
|
|
|
|
func applyAvatarURL(data map[string]any) {
|
|
if data == nil || strings.TrimSpace(stringFromAny(data["avatar_url"])) != "" {
|
|
return
|
|
}
|
|
avatar := strings.TrimSpace(stringFromAny(data["avatar_file_id"]))
|
|
if isAvatarURLValue(avatar) {
|
|
data["avatar_url"] = avatar
|
|
return
|
|
}
|
|
// 裸文件 ID:统一补一个可直接下载的 avatar_url。
|
|
// 下载走 /v0/file/s/{file_id},自定义头像的 ACL 对登录用户放行(见 adapter.FileUserHasAccess)。
|
|
if avatar != "" && !types.ParseUid(avatar).IsZero() {
|
|
data["avatar_url"] = "/v0/file/s/" + avatar
|
|
}
|
|
}
|
|
|
|
func publicWithDefaultUserAvatar(public any, userID string) any {
|
|
data := publicToMap(public)
|
|
gender := stringFromAny(data["gender"])
|
|
applyDefaultUserAvatar(data, userID, gender)
|
|
return data
|
|
}
|
|
|
|
func publicToMap(public any) map[string]any {
|
|
switch value := public.(type) {
|
|
case nil:
|
|
return map[string]any{}
|
|
case map[string]any:
|
|
out := make(map[string]any, len(value)+1)
|
|
for k, v := range value {
|
|
out[k] = v
|
|
}
|
|
return out
|
|
case types.KVMap:
|
|
out := make(map[string]any, len(value)+1)
|
|
for k, v := range value {
|
|
out[k] = v
|
|
}
|
|
return out
|
|
default:
|
|
return map[string]any{}
|
|
}
|
|
}
|
|
|
|
func isAvatarURLValue(value string) bool {
|
|
return strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") ||
|
|
strings.HasPrefix(value, "data:") || strings.HasPrefix(value, "/")
|
|
}
|
|
|
|
func defaultUserAvatarURL(userID, gender string) string {
|
|
if store.ThirdPartyConfig == nil {
|
|
return ""
|
|
}
|
|
values, err := store.ThirdPartyConfig.Get(imAssetsConfigGroup, imDefaultMaleAvatarURLsConfig, imDefaultFemaleAvatarURLsConfig)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
key := imDefaultMaleAvatarURLsConfig
|
|
if strings.EqualFold(strings.TrimSpace(gender), "female") {
|
|
key = imDefaultFemaleAvatarURLsConfig
|
|
}
|
|
urls := parseDefaultAvatarURLs(values[key])
|
|
if len(urls) == 0 && key == imDefaultFemaleAvatarURLsConfig {
|
|
urls = parseDefaultAvatarURLs(values[imDefaultMaleAvatarURLsConfig])
|
|
}
|
|
if len(urls) == 0 {
|
|
return ""
|
|
}
|
|
return urls[defaultAvatarIndex(userID, len(urls))]
|
|
}
|
|
|
|
func parseDefaultAvatarURLs(raw string) []string {
|
|
var urls []string
|
|
if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &urls); err != nil {
|
|
return nil
|
|
}
|
|
out := urls[:0]
|
|
for _, url := range urls {
|
|
if url = strings.TrimSpace(url); url != "" {
|
|
out = append(out, url)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func defaultAvatarIndex(userID string, count int) int {
|
|
if count <= 1 {
|
|
return 0
|
|
}
|
|
uid := types.ParseUserId(strings.TrimSpace(userID))
|
|
if uid.IsZero() {
|
|
uid = types.ParseUid(strings.TrimSpace(userID))
|
|
}
|
|
if !uid.IsZero() {
|
|
return int(uint64(uid) % uint64(count))
|
|
}
|
|
h := fnv.New32a()
|
|
_, _ = h.Write([]byte(userID))
|
|
return int(h.Sum32() % uint32(count))
|
|
}
|