Files
updata_hfb/internal/hfb/parser.go
T
2026-07-23 13:03:47 +08:00

215 lines
6.1 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 hfb
import (
"fmt"
"regexp"
"strconv"
"strings"
)
var numberPattern = regexp.MustCompile(`[-+]?\d+(?:\.\d+)?`)
func ParseUploadText(text string) (ParsedUpload, error) {
values := make(map[string]string)
for lineNo, line := range strings.Split(text, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
key, value, ok := splitKeyValue(line)
if !ok {
return ParsedUpload{}, fmt.Errorf("第 %d 行格式不正确,应为“字段:值”", lineNo+1)
}
key = normalizeKey(key)
if key == "" {
continue
}
values[key] = strings.TrimSpace(value)
}
account := ExternalAccountData{}
var err error
account.LoginMethod = firstValue(values, "上号方式", "登录方式")
account.Rank = firstValue(values, "段位")
account.Level, err = parseRequiredInt(values, "等级")
if err != nil {
return ParsedUpload{}, err
}
account.SafeSlots, err = parseRequiredInt(values, "保险格数", "保险箱格数")
if err != nil {
return ParsedUpload{}, err
}
account.DailyLossM, err = parseRequiredFloat(values, "每日消耗")
if err != nil {
return ParsedUpload{}, err
}
account.SecretKD, err = parseRequiredFloat(values, "绝密KD", "绝密kd")
if err != nil {
return ParsedUpload{}, err
}
account.Deposit, err = parseRequiredFloat(values, "押金")
if err != nil {
return ParsedUpload{}, err
}
account.BanRecord = optionalDefault(firstValue(values, "封禁记录"), "无")
account.CommonRegion = firstValue(values, "常用地区", "常用区域")
account.ContactPhone = firstValue(values, "联系电话", "手机号", "电话")
account.OwnerOnlineTime = firstValue(values, "号主在线时间", "在线时间")
account.Currency.HafuCoin, err = parseRequiredFloat(values, "哈夫币纯币", "哈夫币")
if err != nil {
return ParsedUpload{}, err
}
account.Currency.RecycleRatio, err = parseRequiredRatio(values, "回收比例")
if err != nil {
return ParsedUpload{}, err
}
account.Currency.RecycleRent, err = parseRequiredFloat(values, "回收租金")
if err != nil {
return ParsedUpload{}, err
}
account.DailyConsumption.Stamina, err = parseRequiredInt(values, "体力")
if err != nil {
return ParsedUpload{}, err
}
account.DailyConsumption.Weight, err = parseRequiredInt(values, "负重")
if err != nil {
return ParsedUpload{}, err
}
account.Inventory.AWMBullets, err = parseOptionalInt(values, "AWM子弹数量", "AWM子弹")
if err != nil {
return ParsedUpload{}, err
}
account.Inventory.Level6Helmets, err = parseOptionalInt(values, "6头数量", "六头数量", "6头")
if err != nil {
return ParsedUpload{}, err
}
account.Inventory.Level6Armor, err = parseOptionalInt(values, "6甲数量", "六甲数量", "6甲")
if err != nil {
return ParsedUpload{}, err
}
account.Inventory.Skins = parseSkins(firstValue(values, "皮肤"))
if strings.TrimSpace(account.LoginMethod) == "" {
return ParsedUpload{}, fmt.Errorf("缺少必填字段:上号方式")
}
if strings.TrimSpace(account.Rank) == "" {
return ParsedUpload{}, fmt.Errorf("缺少必填字段:段位")
}
return ParsedUpload{
UploaderName: firstValue(values, "上传人", "上传者", "上传账号", "上传管理员"),
Account: account,
}, nil
}
func splitKeyValue(line string) (string, string, bool) {
if idx := strings.Index(line, ""); idx >= 0 {
return line[:idx], line[idx+len(""):], true
}
if idx := strings.Index(line, ":"); idx >= 0 {
return line[:idx], line[idx+1:], true
}
return "", "", false
}
func normalizeKey(key string) string {
return strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(key), " ", ""))
}
func firstValue(values map[string]string, labels ...string) string {
for _, label := range labels {
if value, ok := values[normalizeKey(label)]; ok {
return strings.TrimSpace(value)
}
}
return ""
}
func optionalDefault(value, fallback string) string {
if strings.TrimSpace(value) == "" {
return fallback
}
return strings.TrimSpace(value)
}
func parseRequiredInt(values map[string]string, labels ...string) (int, error) {
value := firstValue(values, labels...)
if value == "" {
return 0, fmt.Errorf("缺少必填字段:%s", labels[0])
}
return parseIntLabel(labels[0], value)
}
func parseOptionalInt(values map[string]string, labels ...string) (int, error) {
value := firstValue(values, labels...)
if value == "" {
return 0, nil
}
return parseIntLabel(labels[0], value)
}
func parseIntLabel(label, value string) (int, error) {
number, err := parseFloatText(value, false)
if err != nil {
return 0, fmt.Errorf("%s 数字格式不正确:%s", label, value)
}
return int(number), nil
}
func parseRequiredFloat(values map[string]string, labels ...string) (float64, error) {
value := firstValue(values, labels...)
if value == "" {
return 0, fmt.Errorf("缺少必填字段:%s", labels[0])
}
number, err := parseFloatText(value, false)
if err != nil {
return 0, fmt.Errorf("%s 数字格式不正确:%s", labels[0], value)
}
return number, nil
}
func parseRequiredRatio(values map[string]string, labels ...string) (float64, error) {
value := firstValue(values, labels...)
if value == "" {
return 0, fmt.Errorf("缺少必填字段:%s", labels[0])
}
number, err := parseFloatText(value, true)
if err != nil {
return 0, fmt.Errorf("%s 数字格式不正确:%s", labels[0], value)
}
return number, nil
}
func parseFloatText(value string, preferLast bool) (float64, error) {
value = strings.TrimSpace(strings.ReplaceAll(value, ",", ""))
matches := numberPattern.FindAllString(value, -1)
if len(matches) == 0 {
return 0, strconv.ErrSyntax
}
pick := matches[0]
if preferLast {
pick = matches[len(matches)-1]
}
return strconv.ParseFloat(pick, 64)
}
func parseSkins(value string) []string {
value = strings.TrimSpace(value)
if value == "" || value == "无" || strings.EqualFold(value, "none") {
return nil
}
parts := strings.FieldsFunc(value, func(r rune) bool {
return r == ',' || r == '' || r == '、' || r == ';' || r == ''
})
skins := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part != "" && part != "无" {
skins = append(skins, part)
}
}
return skins
}