package spot import ( "regexp" "strconv" "strings" ) // Regex pour les deux formats de spots cluster var ( // Format standard : DX de SPOTTER: FREQ DX MODE COMMENT TIME SpotRe = regexp.MustCompile(`(?i)DX\sde\s([\w\d\/]+?)(?:-[#\d-]+)?\s*:\s*(\d+\.\d+)\s+([\w\d\/]+)\s+(?:(CW|SSB|FT8|FT4|RTTY|USB|LSB|FM)\s+)?(.+?)\s+(\d{4}Z)`) // Format court : FREQ DX DATE TIME COMMENT SpotReShort = regexp.MustCompile(`^(\d+\.\d+)\s+([\w\d\/]+)\s+\d{2}-\w{3}-\d{4}\s+(\d{4}Z)\s+(.+?)\s*<([\w\d\/]+)>\s*$`) // Détection rapide du format court ShortSpotDetectRe = regexp.MustCompile(`^\d+\.\d+\s+[\w\d\/]+\s+\d{2}-\w{3}-\d{4}`) ) // ParseResult contient le spot parsé et une éventuelle erreur type ParseResult struct { Spot *Spot Err error Skipped bool // true si la ligne n'est pas un spot (pas une erreur) } // ParseLine tente de parser une ligne brute du cluster en Spot // Retourne nil si la ligne n'est pas un spot DX func ParseLine(line string, clusterName string) *Spot { // Détecter si c'est un spot isSpot := strings.Contains(line, "DX de ") || ShortSpotDetectRe.MatchString(line) if !isSpot { return nil } match := SpotRe.FindStringSubmatch(line) if len(match) > 0 { return parseStandardFormat(match, clusterName) } match = SpotReShort.FindStringSubmatch(line) if len(match) > 0 { return parseShortFormat(match, clusterName) } return nil } func parseStandardFormat(match []string, clusterName string) *Spot { freqKHz := parseFreq(match[2]) return &Spot{ Spotter: match[1], FrequencyKHz: freqKHz, DX: match[3], Mode: match[4], Comment: strings.TrimSpace(match[5]), Time: match[6], ClusterName: clusterName, Source: SourceCluster, } } func parseShortFormat(match []string, clusterName string) *Spot { freqKHz := parseFreq(match[1]) return &Spot{ FrequencyKHz: freqKHz, DX: match[2], Time: match[3], Comment: strings.TrimSpace(match[4]), Spotter: match[5], ClusterName: clusterName, Source: SourceCluster, } } // parseFreq parse une fréquence string en kHz float64 // Gère kHz (>1000) et MHz (<1000) automatiquement func parseFreq(s string) float64 { f, err := strconv.ParseFloat(s, 64) if err != nil { return 0 } // Si < 1000 c'est en MHz, on convertit en kHz if f < 1000 { return f * 1000.0 } return f }