feat: Implemented the download of eQSL

This commit is contained in:
2026-07-09 13:16:04 +02:00
parent 6ee31ae45d
commit 206a6bff09
3 changed files with 190 additions and 0 deletions
+92
View File
@@ -245,6 +245,7 @@ const (
keyExtLoTWWebPassword = "extsvc.lotw.web_password" // LoTW website password (download)
keyExtLoTWLastDownload = "extsvc.lotw.last_download" // YYYY-MM-DD of last confirmation pull
keyExtQRZLastDownload = "extsvc.qrz.last_download" // YYYY-MM-DD of last QRZ confirmation pull
keyExtEQSLLastDownload = "extsvc.eqsl.last_download" // YYYY-MM-DD of last eQSL inbox pull
)
// QSLDefaults is the per-user default for the QSL / eQSL / LoTW / upload
@@ -6905,6 +6906,97 @@ func (a *App) runDownloadConfirmations(svc extsvc.Service, cfg extsvc.ExternalSe
emit(fmt.Sprintf("Confirmed %d, added %d (of %d returned)", matched, added, total))
// (last-download date already stored right after the fetch above)
case extsvc.ServiceEQSL:
sinceDate := resolveSince(keyExtEQSLLastDownload)
if sinceDate != "" {
emit("Downloading eQSL Inbox (confirmations received since " + sinceDate + ")…")
} else {
emit("Downloading full eQSL Inbox (all received confirmations)…")
}
adifText, err := extsvc.DownloadEQSLConfirmations(ctx, nil, cfg.EQSL, sinceDate)
if err != nil {
emit("Download failed: " + err.Error())
done(matched, total)
return
}
keyIDs, kerr := a.qso.DedupeKeyIDs(ctx)
if kerr != nil {
emit("Error reading local log: " + kerr.Error())
done(matched, total)
return
}
// eQSL confirmations aren't ARRL-award-valid (only LoTW + paper QSL are),
// so NEW is judged only against other eQSL confirmations.
sets, _ := a.qso.ConfirmedSlots(ctx, []string{"eqsl_rcvd"})
var items []ConfirmationItem
var unmatched []string
perr := adif.Parse(strings.NewReader(adifText), func(rec adif.Record) error {
q, ok := adif.RecordToQSO(rec)
if !ok {
return nil
}
total++
// eQSL stamps the confirmation date in QSLRDATE (fall back to today).
date := rec["qslrdate"]
if date == "" {
date = time.Now().UTC().Format("20060102")
}
a.enrichContactedFromCty(&q)
key := qso.DedupeKey(q.Callsign, q.QSODate.UTC().Format("2006-01-02T15:04"), q.Band, q.Mode)
if id, found := keyIDs[key]; found {
if e := a.qso.MarkEQSLConfirmed(ctx, id, date); e == nil {
matched++
}
} else if addNotFound {
q.EQSLSent = "Y"
q.EQSLRcvd = "Y"
q.EQSLRcvdDate = date
if newID, e := a.qso.Add(ctx, q); e == nil {
keyIDs[key] = newID
added++
}
} else {
unmatched = append(unmatched, fmt.Sprintf("%s · %s · %s · %s",
q.Callsign, q.QSODate.UTC().Format("2006-01-02 15:04Z"), q.Band, q.Mode))
}
dxccNum := 0
if q.DXCC != nil {
dxccNum = *q.DXCC
}
it := ConfirmationItem{
Callsign: q.Callsign,
QSODate: q.QSODate.UTC().Format(time.RFC3339),
Band: q.Band, Mode: q.Mode, Country: q.Country,
}
if dxccNum != 0 {
it.NewDXCC = !sets.DXCC[dxccNum]
it.NewBand = !sets.Band[qso.BandKey(dxccNum, q.Band)]
it.NewSlot = !sets.Slot[qso.SlotKey(dxccNum, q.Band, q.Mode)]
sets.DXCC[dxccNum] = true
sets.Band[qso.BandKey(dxccNum, q.Band)] = true
sets.Slot[qso.SlotKey(dxccNum, q.Band, q.Mode)] = true
}
items = append(items, it)
return nil
})
if a.ctx != nil {
wruntime.EventsEmit(a.ctx, "qslmgr:confirmations", items)
}
if perr != nil {
emit("Parse error: " + perr.Error())
}
if addNotFound {
emit(fmt.Sprintf("Matched %d, added %d (of %d eQSL confirmation(s))", matched, added, total))
} else {
emit(fmt.Sprintf("Matched %d of %d eQSL confirmation(s)", matched, total))
}
for _, u := range unmatched {
emit(" ⚠ no local QSO for: " + u)
}
if a.settings != nil {
a.setSetting(a.profileScope()+keyExtEQSLLastDownload, time.Now().UTC().Format("2006-01-02"))
}
default:
emit(fmt.Sprintf("Confirmation download isn't available for %s yet.", svc))
}