feat: added support for eQSL

This commit is contained in:
2026-06-18 14:56:13 +02:00
parent cdd71b17c8
commit dd2deee939
10 changed files with 351 additions and 9 deletions
+55 -1
View File
@@ -195,6 +195,12 @@ const (
keyExtHRDLogAutoUpload = "extsvc.hrdlog.auto_upload"
keyExtHRDLogUploadMode = "extsvc.hrdlog.upload_mode"
keyExtEQSLUsername = "extsvc.eqsl.username"
keyExtEQSLPassword = "extsvc.eqsl.password"
keyExtEQSLQTHNick = "extsvc.eqsl.qth_nickname"
keyExtEQSLAutoUpload = "extsvc.eqsl.auto_upload"
keyExtEQSLUploadMode = "extsvc.eqsl.upload_mode"
keyExtPotaToken = "extsvc.pota.token" // pota.app session token for hunter-log sync
keyExtLoTWTQSLPath = "extsvc.lotw.tqsl_path"
@@ -4873,7 +4879,8 @@ func (a *App) loadExternalServices() extsvc.ExternalServices {
keyExtLoTWUploadFlag, keyExtLoTWUploadFlags, keyExtLoTWWriteLog,
keyExtLoTWAutoUpload, keyExtLoTWUploadMode,
keyExtLoTWUsername, keyExtLoTWWebPassword,
keyExtHRDLogCallsign, keyExtHRDLogCode, keyExtHRDLogAutoUpload, keyExtHRDLogUploadMode)
keyExtHRDLogCallsign, keyExtHRDLogCode, keyExtHRDLogAutoUpload, keyExtHRDLogUploadMode,
keyExtEQSLUsername, keyExtEQSLPassword, keyExtEQSLQTHNick, keyExtEQSLAutoUpload, keyExtEQSLUploadMode)
if err != nil {
return out
}
@@ -4930,6 +4937,19 @@ func (a *App) loadExternalServices() extsvc.ExternalServices {
out.HRDLog.Callsign = p.Callsign
}
}
out.EQSL = extsvc.ServiceConfig{
Username: m[keyExtEQSLUsername],
Password: m[keyExtEQSLPassword],
QTHNickname: m[keyExtEQSLQTHNick],
AutoUpload: m[keyExtEQSLAutoUpload] == "1",
UploadMode: extsvc.UploadMode(m[keyExtEQSLUploadMode]),
}
// Default the eQSL username to the active profile's call when unset.
if out.EQSL.Username == "" && a.profiles != nil {
if p, perr := a.profiles.Active(a.ctx); perr == nil {
out.EQSL.Username = p.Callsign
}
}
return out
}
@@ -4982,6 +5002,11 @@ func (a *App) SaveExternalServices(cfg extsvc.ExternalServices) error {
if cfg.HRDLog.AutoUpload {
hlAuto = "1"
}
eqMode := modeOf(cfg.EQSL.UploadMode)
eqAuto := "0"
if cfg.EQSL.AutoUpload {
eqAuto = "1"
}
scope := a.profileScope() // write under the active profile's prefix
for k, v := range map[string]string{
keyExtQRZAPIKey: strings.TrimSpace(cfg.QRZ.APIKey),
@@ -5011,6 +5036,12 @@ func (a *App) SaveExternalServices(cfg extsvc.ExternalServices) error {
keyExtHRDLogCode: strings.TrimSpace(cfg.HRDLog.Code),
keyExtHRDLogAutoUpload: hlAuto,
keyExtHRDLogUploadMode: hlMode,
keyExtEQSLUsername: strings.ToUpper(strings.TrimSpace(cfg.EQSL.Username)),
keyExtEQSLPassword: cfg.EQSL.Password,
keyExtEQSLQTHNick: strings.TrimSpace(cfg.EQSL.QTHNickname),
keyExtEQSLAutoUpload: eqAuto,
keyExtEQSLUploadMode: eqMode,
} {
if err := a.settings.Set(a.ctx, scope+k, v); err != nil {
return err
@@ -5044,6 +5075,11 @@ func (a *App) TestHRDLogUpload() (string, error) {
return extsvc.TestHRDLog(a.ctx, nil, a.loadExternalServices().HRDLog)
}
// TestEQSLUpload validates the eQSL credentials with a real (no-op) request.
func (a *App) TestEQSLUpload() (string, error) {
return extsvc.TestEQSL(a.ctx, nil, a.loadExternalServices().EQSL)
}
// ── QSL Manager (manual upload) ────────────────────────────────────────
// uploadColumnFor maps a service id to its QSO sent-status column.
@@ -5057,6 +5093,8 @@ func uploadColumnFor(service string) string {
return "lotw_sent"
case extsvc.ServiceHRDLog:
return "hrdlog_qso_upload_status"
case extsvc.ServiceEQSL:
return "eqsl_sent"
}
return ""
}
@@ -5197,6 +5235,8 @@ func (a *App) runManualUpload(svc extsvc.Service, ids []int64, cfg extsvc.Extern
res, err = extsvc.UploadQRZ(ctx, nil, cfg.QRZ.APIKey, rec)
case extsvc.ServiceHRDLog:
res, err = extsvc.UploadHRDLog(ctx, nil, cfg.HRDLog.Callsign, cfg.HRDLog.Code, rec)
case extsvc.ServiceEQSL:
res, err = extsvc.UploadEQSL(ctx, nil, cfg.EQSL.Username, cfg.EQSL.Password, cfg.EQSL.QTHNickname, rec)
default:
res, err = extsvc.UploadClublog(ctx, nil, cfg.Clublog, rec)
}
@@ -5770,6 +5810,8 @@ func (a *App) uploadOwnerCall(svc extsvc.Service) string {
owner = cfg.Clublog.Callsign
case extsvc.ServiceHRDLog:
owner = cfg.HRDLog.Callsign
case extsvc.ServiceEQSL:
owner = cfg.EQSL.Username
}
owner = strings.ToUpper(strings.TrimSpace(owner))
if owner == "" && a.profiles != nil {
@@ -5866,6 +5908,12 @@ func (a *App) extShouldUpload(svc extsvc.Service, id int64) bool {
return false
}
return true
case extsvc.ServiceEQSL:
if strings.EqualFold(q.EQSLSent, "Y") {
applog.Printf("extsvc: QSO %d not eligible for eqsl — EQSLSent already %q (set Confirmations default to N to upload)", id, q.EQSLSent)
return false
}
return true
case extsvc.ServiceLoTW:
for _, f := range a.loadExternalServices().LoTW.UploadFlags {
if strings.EqualFold(q.LOTWSent, f) {
@@ -5906,6 +5954,12 @@ func (a *App) markExtUploaded(svc extsvc.Service, id int64, logID string) {
applog.Printf("extsvc: mark hrdlog uploaded %d: %v", id, err)
}
}
case extsvc.ServiceEQSL:
if a.qso != nil {
if err := a.qso.MarkEQSLSent(a.ctx, id, date); err != nil {
applog.Printf("extsvc: mark eqsl sent %d: %v", id, err)
}
}
}
if a.ctx != nil {
wruntime.EventsEmit(a.ctx, "extsvc:uploaded", map[string]any{