2026-06-11 01:02:16 +08:00

514 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gamemanagement
import (
"strings"
"unicode"
)
const (
robotNicknameLanguageArabic = "arabic"
robotNicknameLanguageEnglish = "english"
)
var defaultRobotAvatarSources = []string{
"https://randomuser.me/api/portraits/men/0.jpg",
"https://randomuser.me/api/portraits/men/1.jpg",
"https://randomuser.me/api/portraits/men/2.jpg",
"https://randomuser.me/api/portraits/men/3.jpg",
"https://randomuser.me/api/portraits/men/4.jpg",
"https://randomuser.me/api/portraits/men/5.jpg",
"https://randomuser.me/api/portraits/men/6.jpg",
"https://randomuser.me/api/portraits/men/7.jpg",
"https://randomuser.me/api/portraits/men/8.jpg",
"https://randomuser.me/api/portraits/men/9.jpg",
"https://randomuser.me/api/portraits/men/10.jpg",
"https://randomuser.me/api/portraits/men/11.jpg",
"https://randomuser.me/api/portraits/men/12.jpg",
"https://randomuser.me/api/portraits/men/13.jpg",
"https://randomuser.me/api/portraits/men/14.jpg",
"https://randomuser.me/api/portraits/men/15.jpg",
"https://randomuser.me/api/portraits/men/16.jpg",
"https://randomuser.me/api/portraits/men/17.jpg",
"https://randomuser.me/api/portraits/men/18.jpg",
"https://randomuser.me/api/portraits/men/19.jpg",
"https://randomuser.me/api/portraits/men/20.jpg",
"https://randomuser.me/api/portraits/men/21.jpg",
"https://randomuser.me/api/portraits/men/22.jpg",
"https://randomuser.me/api/portraits/men/23.jpg",
"https://randomuser.me/api/portraits/men/24.jpg",
"https://randomuser.me/api/portraits/men/25.jpg",
"https://randomuser.me/api/portraits/men/26.jpg",
"https://randomuser.me/api/portraits/men/27.jpg",
"https://randomuser.me/api/portraits/men/28.jpg",
"https://randomuser.me/api/portraits/men/29.jpg",
"https://randomuser.me/api/portraits/men/30.jpg",
"https://randomuser.me/api/portraits/men/31.jpg",
"https://randomuser.me/api/portraits/men/32.jpg",
"https://randomuser.me/api/portraits/men/33.jpg",
"https://randomuser.me/api/portraits/men/34.jpg",
"https://randomuser.me/api/portraits/men/35.jpg",
"https://randomuser.me/api/portraits/men/36.jpg",
"https://randomuser.me/api/portraits/men/37.jpg",
"https://randomuser.me/api/portraits/men/38.jpg",
"https://randomuser.me/api/portraits/men/39.jpg",
"https://randomuser.me/api/portraits/men/40.jpg",
"https://randomuser.me/api/portraits/men/41.jpg",
"https://randomuser.me/api/portraits/men/42.jpg",
"https://randomuser.me/api/portraits/men/43.jpg",
"https://randomuser.me/api/portraits/men/44.jpg",
"https://randomuser.me/api/portraits/men/45.jpg",
"https://randomuser.me/api/portraits/men/46.jpg",
"https://randomuser.me/api/portraits/men/47.jpg",
"https://randomuser.me/api/portraits/men/48.jpg",
"https://randomuser.me/api/portraits/men/49.jpg",
"https://randomuser.me/api/portraits/men/50.jpg",
"https://randomuser.me/api/portraits/men/51.jpg",
"https://randomuser.me/api/portraits/men/52.jpg",
"https://randomuser.me/api/portraits/men/53.jpg",
"https://randomuser.me/api/portraits/men/54.jpg",
"https://randomuser.me/api/portraits/men/55.jpg",
"https://randomuser.me/api/portraits/men/56.jpg",
"https://randomuser.me/api/portraits/men/57.jpg",
"https://randomuser.me/api/portraits/men/58.jpg",
"https://randomuser.me/api/portraits/men/59.jpg",
"https://randomuser.me/api/portraits/men/60.jpg",
"https://randomuser.me/api/portraits/men/61.jpg",
"https://randomuser.me/api/portraits/men/62.jpg",
"https://randomuser.me/api/portraits/men/63.jpg",
"https://randomuser.me/api/portraits/men/64.jpg",
"https://randomuser.me/api/portraits/men/65.jpg",
"https://randomuser.me/api/portraits/men/66.jpg",
"https://randomuser.me/api/portraits/men/67.jpg",
"https://randomuser.me/api/portraits/men/68.jpg",
"https://randomuser.me/api/portraits/men/69.jpg",
"https://randomuser.me/api/portraits/men/70.jpg",
"https://randomuser.me/api/portraits/men/71.jpg",
"https://randomuser.me/api/portraits/men/72.jpg",
"https://randomuser.me/api/portraits/men/73.jpg",
"https://randomuser.me/api/portraits/men/74.jpg",
"https://randomuser.me/api/portraits/men/75.jpg",
"https://randomuser.me/api/portraits/men/76.jpg",
"https://randomuser.me/api/portraits/men/77.jpg",
"https://randomuser.me/api/portraits/men/78.jpg",
"https://randomuser.me/api/portraits/men/79.jpg",
"https://randomuser.me/api/portraits/men/80.jpg",
"https://randomuser.me/api/portraits/men/81.jpg",
"https://randomuser.me/api/portraits/men/82.jpg",
"https://randomuser.me/api/portraits/men/83.jpg",
"https://randomuser.me/api/portraits/men/84.jpg",
"https://randomuser.me/api/portraits/men/85.jpg",
"https://randomuser.me/api/portraits/men/86.jpg",
"https://randomuser.me/api/portraits/men/87.jpg",
"https://randomuser.me/api/portraits/men/88.jpg",
"https://randomuser.me/api/portraits/men/89.jpg",
"https://randomuser.me/api/portraits/men/90.jpg",
"https://randomuser.me/api/portraits/men/91.jpg",
"https://randomuser.me/api/portraits/men/92.jpg",
"https://randomuser.me/api/portraits/men/93.jpg",
"https://randomuser.me/api/portraits/men/94.jpg",
"https://randomuser.me/api/portraits/men/95.jpg",
"https://randomuser.me/api/portraits/men/96.jpg",
"https://randomuser.me/api/portraits/men/97.jpg",
"https://randomuser.me/api/portraits/men/98.jpg",
"https://randomuser.me/api/portraits/men/99.jpg",
"https://randomuser.me/api/portraits/women/0.jpg",
"https://randomuser.me/api/portraits/women/1.jpg",
"https://randomuser.me/api/portraits/women/2.jpg",
"https://randomuser.me/api/portraits/women/3.jpg",
"https://randomuser.me/api/portraits/women/4.jpg",
"https://randomuser.me/api/portraits/women/5.jpg",
"https://randomuser.me/api/portraits/women/6.jpg",
"https://randomuser.me/api/portraits/women/7.jpg",
"https://randomuser.me/api/portraits/women/8.jpg",
"https://randomuser.me/api/portraits/women/9.jpg",
"https://randomuser.me/api/portraits/women/10.jpg",
"https://randomuser.me/api/portraits/women/11.jpg",
"https://randomuser.me/api/portraits/women/12.jpg",
"https://randomuser.me/api/portraits/women/13.jpg",
"https://randomuser.me/api/portraits/women/14.jpg",
"https://randomuser.me/api/portraits/women/15.jpg",
"https://randomuser.me/api/portraits/women/16.jpg",
"https://randomuser.me/api/portraits/women/17.jpg",
"https://randomuser.me/api/portraits/women/18.jpg",
"https://randomuser.me/api/portraits/women/19.jpg",
"https://randomuser.me/api/portraits/women/20.jpg",
"https://randomuser.me/api/portraits/women/21.jpg",
"https://randomuser.me/api/portraits/women/22.jpg",
"https://randomuser.me/api/portraits/women/23.jpg",
"https://randomuser.me/api/portraits/women/24.jpg",
"https://randomuser.me/api/portraits/women/25.jpg",
"https://randomuser.me/api/portraits/women/26.jpg",
"https://randomuser.me/api/portraits/women/27.jpg",
"https://randomuser.me/api/portraits/women/28.jpg",
"https://randomuser.me/api/portraits/women/29.jpg",
"https://randomuser.me/api/portraits/women/30.jpg",
"https://randomuser.me/api/portraits/women/31.jpg",
"https://randomuser.me/api/portraits/women/32.jpg",
"https://randomuser.me/api/portraits/women/33.jpg",
"https://randomuser.me/api/portraits/women/34.jpg",
"https://randomuser.me/api/portraits/women/35.jpg",
"https://randomuser.me/api/portraits/women/36.jpg",
"https://randomuser.me/api/portraits/women/37.jpg",
"https://randomuser.me/api/portraits/women/38.jpg",
"https://randomuser.me/api/portraits/women/39.jpg",
"https://randomuser.me/api/portraits/women/40.jpg",
"https://randomuser.me/api/portraits/women/41.jpg",
"https://randomuser.me/api/portraits/women/42.jpg",
"https://randomuser.me/api/portraits/women/43.jpg",
"https://randomuser.me/api/portraits/women/44.jpg",
"https://randomuser.me/api/portraits/women/45.jpg",
"https://randomuser.me/api/portraits/women/46.jpg",
"https://randomuser.me/api/portraits/women/47.jpg",
"https://randomuser.me/api/portraits/women/48.jpg",
"https://randomuser.me/api/portraits/women/49.jpg",
"https://randomuser.me/api/portraits/women/50.jpg",
"https://randomuser.me/api/portraits/women/51.jpg",
"https://randomuser.me/api/portraits/women/52.jpg",
"https://randomuser.me/api/portraits/women/53.jpg",
"https://randomuser.me/api/portraits/women/54.jpg",
"https://randomuser.me/api/portraits/women/55.jpg",
"https://randomuser.me/api/portraits/women/56.jpg",
"https://randomuser.me/api/portraits/women/57.jpg",
"https://randomuser.me/api/portraits/women/58.jpg",
"https://randomuser.me/api/portraits/women/59.jpg",
"https://randomuser.me/api/portraits/women/60.jpg",
"https://randomuser.me/api/portraits/women/61.jpg",
"https://randomuser.me/api/portraits/women/62.jpg",
"https://randomuser.me/api/portraits/women/63.jpg",
"https://randomuser.me/api/portraits/women/64.jpg",
"https://randomuser.me/api/portraits/women/65.jpg",
"https://randomuser.me/api/portraits/women/66.jpg",
"https://randomuser.me/api/portraits/women/67.jpg",
"https://randomuser.me/api/portraits/women/68.jpg",
"https://randomuser.me/api/portraits/women/69.jpg",
"https://randomuser.me/api/portraits/women/70.jpg",
"https://randomuser.me/api/portraits/women/71.jpg",
"https://randomuser.me/api/portraits/women/72.jpg",
"https://randomuser.me/api/portraits/women/73.jpg",
"https://randomuser.me/api/portraits/women/74.jpg",
"https://randomuser.me/api/portraits/women/75.jpg",
"https://randomuser.me/api/portraits/women/76.jpg",
"https://randomuser.me/api/portraits/women/77.jpg",
"https://randomuser.me/api/portraits/women/78.jpg",
"https://randomuser.me/api/portraits/women/79.jpg",
"https://randomuser.me/api/portraits/women/80.jpg",
"https://randomuser.me/api/portraits/women/81.jpg",
"https://randomuser.me/api/portraits/women/82.jpg",
"https://randomuser.me/api/portraits/women/83.jpg",
"https://randomuser.me/api/portraits/women/84.jpg",
"https://randomuser.me/api/portraits/women/85.jpg",
"https://randomuser.me/api/portraits/women/86.jpg",
"https://randomuser.me/api/portraits/women/87.jpg",
"https://randomuser.me/api/portraits/women/88.jpg",
"https://randomuser.me/api/portraits/women/89.jpg",
"https://randomuser.me/api/portraits/women/90.jpg",
"https://randomuser.me/api/portraits/women/91.jpg",
"https://randomuser.me/api/portraits/women/92.jpg",
"https://randomuser.me/api/portraits/women/93.jpg",
"https://randomuser.me/api/portraits/women/94.jpg",
"https://randomuser.me/api/portraits/women/95.jpg",
"https://randomuser.me/api/portraits/women/96.jpg",
"https://randomuser.me/api/portraits/women/97.jpg",
"https://randomuser.me/api/portraits/women/98.jpg",
"https://randomuser.me/api/portraits/women/99.jpg",
}
var defaultArabicRobotNicknames = []string{
"أمير",
"سيف",
"مالك",
"فارس",
"ليث",
"بدر",
"ماهر",
"رامي",
"كريم",
"سامي",
"عمر",
"يوسف",
"آدم",
"ريان",
"زيد",
"نور",
"ياسين",
"أنس",
"خالد",
"طارق",
"حازم",
"نادر",
"وسيم",
"جواد",
"مروان",
"إياد",
"نبيل",
"هلال",
"تميم",
"سلمان",
"راشد",
"جاسم",
"ماجد",
"سلطان",
"عادل",
"هاني",
"بلال",
"يزن",
"غيث",
"قصي",
"سليم",
"ريان_الليل",
"نجم",
"ملك_اللعبة",
"صقر",
"ذيب",
"أسد",
"الزعيم",
"القناص",
"الأسطورة",
"ظل",
"برق",
"رعد",
"موج",
"نمر",
"شهاب",
"فهد",
"نايف",
"نوف",
"ليان",
"ريم",
"سارة",
"مريم",
"لينا",
"هنا",
"جود",
"تالا",
"دانا",
"لارا",
"ياسمين",
"نوران",
"رؤى",
"سما",
"ملك",
"جنى",
"آية",
"ندى",
"فرح",
"أمل",
"هدى",
"سلمى",
"ليلى",
"رنا",
"نورا",
"ديما",
"مايا",
"ميس",
"رهف",
"شهد",
"غلا",
"لولو",
"وردة",
"قمر",
"نجمة",
"لؤلؤة",
"الملكة",
"أميرة",
"روح",
"حلم",
"بسمة",
}
var defaultEnglishRobotNicknames = []string{
"Alex",
"Jordan",
"Taylor",
"Morgan",
"Riley",
"Nova",
"Phoenix",
"Hunter",
"Luna",
"Mia",
"Noah",
"Liam",
"Ava",
"Emma",
"Oliver",
"Elijah",
"James",
"William",
"Benjamin",
"Lucas",
"Mason",
"Ethan",
"Logan",
"Jackson",
"Levi",
"Sebastian",
"Mateo",
"Jack",
"Owen",
"Theodore",
"Aiden",
"Samuel",
"Joseph",
"John",
"David",
"Wyatt",
"Matthew",
"Luke",
"Asher",
"Carter",
"Julian",
"Grayson",
"Leo",
"Jayden",
"Gabriel",
"Isaac",
"Lincoln",
"Anthony",
"Hudson",
"Dylan",
"Ezra",
"Thomas",
"Charles",
"Christopher",
"Jaxon",
"Maverick",
"Josiah",
"Isaiah",
"Andrew",
"Elias",
"Joshua",
"Nathan",
"Caleb",
"Ryan",
"Adrian",
"Miles",
"Eli",
"Nolan",
"Christian",
"Aaron",
"Cameron",
"Ezekiel",
"Colton",
"Luca",
"Landon",
"Hannah",
"Sofia",
"Isabella",
"Amelia",
"Olivia",
"Charlotte",
"Harper",
"Evelyn",
"Abigail",
"Ella",
"Scarlett",
"Grace",
"Chloe",
"Victoria",
"Aria",
"Penelope",
"Layla",
"Zoey",
"Nora",
"Lily",
"Ellie",
"Violet",
"Aurora",
"Stella",
"Hazel",
}
func normalizeRobotNicknameLanguage(value string) string {
// 后台只开放英语和阿拉伯语两类昵称池;未知值回退英语,避免历史请求继续落到英阿混合池。
switch strings.ToLower(strings.TrimSpace(value)) {
case "ar", "arabic", "arab":
return robotNicknameLanguageArabic
case "en", "english":
return robotNicknameLanguageEnglish
default:
return robotNicknameLanguageEnglish
}
}
func robotAccountLanguage(nicknameLanguage string) string {
// 用户资料里的 language 跟昵称池保持一致,后续客户端或运营筛选机器人时不用再反推昵称字符集。
if normalizeRobotNicknameLanguage(nicknameLanguage) == robotNicknameLanguageArabic {
return "ar"
}
return "en"
}
func robotNickname(index int, nicknameLanguage string) string {
// 机器人昵称只从单一语言池取值,不能再拼随机 hex否则会出现数字、下划线和英阿混排。
language := normalizeRobotNicknameLanguage(nicknameLanguage)
if language == robotNicknameLanguageArabic {
return robotNicknameFromPool(index, defaultArabicRobotNicknames, "لاعب", language)
}
return robotNicknameFromPool(index, defaultEnglishRobotNicknames, "Player", language)
}
func robotNicknameFromPool(index int, pool []string, fallback string, language string) string {
// 前 100 个直接使用预设名;超过预设数量时用同语言两个预设名拼接,保证批量 200 个仍然不带数字后缀。
if index < 0 {
index = 0
}
if len(pool) == 0 {
return fallback
}
base := sanitizeRobotNickname(pool[index%len(pool)], language)
if base == "" {
base = fallback
}
if index < len(pool) {
return base
}
next := sanitizeRobotNickname(pool[(index%len(pool)+1)%len(pool)], language)
if next == "" || next == base {
next = sanitizeRobotNickname(pool[(index+index/len(pool)+2)%len(pool)], language)
}
if next == "" || next == base {
return base
}
return base + next
}
func sanitizeRobotNickname(value string, language string) string {
// 预设里可能有旧的分隔符或非目标语言字符;写库前统一剔除,保证“英语纯英语、阿拉伯语纯阿拉伯语”。
var builder strings.Builder
for _, item := range strings.TrimSpace(value) {
switch language {
case robotNicknameLanguageArabic:
if unicode.Is(unicode.Arabic, item) {
builder.WriteRune(item)
}
default:
if (item >= 'A' && item <= 'Z') || (item >= 'a' && item <= 'z') {
builder.WriteRune(item)
}
}
}
return builder.String()
}
func robotGender(index int, requested string) string {
requested = strings.TrimSpace(requested)
if requested != "" {
return requested
}
if index%2 == 0 {
return "male"
}
return "female"
}
func robotAvatarSource(index int, requested []string) string {
if source := pickString(requested, index); source != "" {
return source
}
if len(defaultRobotAvatarSources) == 0 {
return ""
}
return defaultRobotAvatarSources[index%len(defaultRobotAvatarSources)]
}