package importer import ( "fmt" "net/url" "regexp" "strconv" "strings" "proxyrotator/internal/model" ) var ( // 匹配 host:port 格式 hostPortRegex = regexp.MustCompile(`^([a-zA-Z0-9.-]+):(\d+)$`) // 匹配 user:pass@host:port 格式 userPassHostPortRegex = regexp.MustCompile(`^([^:@]+):([^@]+)@([a-zA-Z0-9.-]+):(\d+)$`) // 匹配 host:port:user:pass 格式 hostPortUserPassRegex = regexp.MustCompile(`^([a-zA-Z0-9.-]+):(\d+):([^:]+):(.+)$`) ) // ParseProxyLine 解析单行代理格式 // 支持格式: // - host:port // - user:pass@host:port // - host:port:user:pass // - http://host:port // - http://user:pass@host:port // - socks5://host:port // - socks5://user:pass@host:port func ParseProxyLine(raw string, protocolHint string) (*model.Proxy, error) { raw = strings.TrimSpace(raw) if raw == "" { return nil, fmt.Errorf("empty line") } // 尝试解析为 URL if strings.Contains(raw, "://") { return parseAsURL(raw) } // 尝试解析 host:port:user:pass 格式 if matches := hostPortUserPassRegex.FindStringSubmatch(raw); matches != nil { port, err := strconv.Atoi(matches[2]) if err != nil || port <= 0 || port >= 65536 { return nil, fmt.Errorf("invalid port: %s", matches[2]) } protocol := inferProtocol(protocolHint, port) return &model.Proxy{ Protocol: protocol, Host: matches[1], Port: port, Username: matches[3], Password: matches[4], }, nil } // 尝试解析 user:pass@host:port 格式 if matches := userPassHostPortRegex.FindStringSubmatch(raw); matches != nil { port, err := strconv.Atoi(matches[4]) if err != nil || port <= 0 || port >= 65536 { return nil, fmt.Errorf("invalid port: %s", matches[4]) } protocol := inferProtocol(protocolHint, port) return &model.Proxy{ Protocol: protocol, Host: matches[3], Port: port, Username: matches[1], Password: matches[2], }, nil } // 尝试解析 host:port 格式 if matches := hostPortRegex.FindStringSubmatch(raw); matches != nil { port, err := strconv.Atoi(matches[2]) if err != nil || port <= 0 || port >= 65536 { return nil, fmt.Errorf("invalid port: %s", matches[2]) } protocol := inferProtocol(protocolHint, port) return &model.Proxy{ Protocol: protocol, Host: matches[1], Port: port, }, nil } return nil, fmt.Errorf("unrecognized format") } // parseAsURL 解析 URL 格式的代理 func parseAsURL(raw string) (*model.Proxy, error) { u, err := url.Parse(raw) if err != nil { return nil, fmt.Errorf("invalid URL: %w", err) } var protocol model.ProxyProtocol switch strings.ToLower(u.Scheme) { case "http": protocol = model.ProtoHTTP case "https": protocol = model.ProtoHTTPS case "socks5": protocol = model.ProtoSOCKS5 default: return nil, fmt.Errorf("unsupported protocol: %s", u.Scheme) } host := u.Hostname() if host == "" { return nil, fmt.Errorf("missing host") } portStr := u.Port() if portStr == "" { // 默认端口 switch protocol { case model.ProtoHTTP: portStr = "80" case model.ProtoHTTPS: portStr = "443" case model.ProtoSOCKS5: portStr = "1080" } } port, err := strconv.Atoi(portStr) if err != nil || port <= 0 || port >= 65536 { return nil, fmt.Errorf("invalid port: %s", portStr) } var username, password string if u.User != nil { username = u.User.Username() password, _ = u.User.Password() } return &model.Proxy{ Protocol: protocol, Host: host, Port: port, Username: username, Password: password, }, nil } // inferProtocol 根据提示和端口推断协议 func inferProtocol(hint string, port int) model.ProxyProtocol { switch strings.ToLower(hint) { case "http": return model.ProtoHTTP case "https": return model.ProtoHTTPS case "socks5": return model.ProtoSOCKS5 } // 根据端口推断 switch port { case 443: return model.ProtoHTTPS case 1080: return model.ProtoSOCKS5 default: return model.ProtoHTTP } }