268 lines
8.8 KiB
Go
268 lines
8.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type SpotProcessor struct {
|
|
FlexRepo *FlexDXClusterRepository
|
|
FlexClient *FlexClient
|
|
HTTPServer *HTTPServer
|
|
SpotChan chan TelnetSpot
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
func NewSpotProcessor(flexRepo *FlexDXClusterRepository, flexClient *FlexClient, httpServer *HTTPServer, spotChan chan TelnetSpot) *SpotProcessor {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
return &SpotProcessor{
|
|
FlexRepo: flexRepo,
|
|
FlexClient: flexClient,
|
|
HTTPServer: httpServer,
|
|
SpotChan: spotChan,
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
}
|
|
}
|
|
|
|
func (sp *SpotProcessor) Start() {
|
|
Log.Info("Starting Spot Processor...")
|
|
|
|
for {
|
|
select {
|
|
case <-sp.ctx.Done():
|
|
Log.Info("Spot Processor shutting down...")
|
|
return
|
|
case spot := <-sp.SpotChan:
|
|
sp.processSpot(spot)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (sp *SpotProcessor) Stop() {
|
|
Log.Info("Stopping Spot Processor...")
|
|
sp.cancel()
|
|
}
|
|
|
|
func (sp *SpotProcessor) processSpot(spot TelnetSpot) {
|
|
freq := FreqMhztoHz(spot.Frequency)
|
|
|
|
flexSpot := FlexSpot{
|
|
CommandNumber: CommandNumber,
|
|
DX: spot.DX,
|
|
FrequencyMhz: freq,
|
|
FrequencyHz: spot.Frequency,
|
|
Band: spot.Band,
|
|
Mode: spot.Mode,
|
|
Source: "FlexDXCluster",
|
|
SpotterCallsign: spot.Spotter,
|
|
TimeStamp: time.Now().Unix(),
|
|
UTCTime: spot.Time,
|
|
LifeTime: Cfg.Flex.SpotLife,
|
|
OriginalComment: spot.Comment,
|
|
Comment: spot.Comment,
|
|
Color: "#ffeaeaea",
|
|
BackgroundColor: "#ff000000",
|
|
Priority: "5",
|
|
NewDXCC: spot.NewDXCC,
|
|
NewBand: spot.NewBand,
|
|
NewMode: spot.NewMode,
|
|
NewSlot: spot.NewSlot,
|
|
Worked: spot.CallsignWorked,
|
|
InWatchlist: false,
|
|
CountryName: spot.CountryName,
|
|
DXCC: spot.DXCC,
|
|
}
|
|
|
|
flexSpot.OriginalComment = spot.Comment
|
|
flexSpot.Comment = flexSpot.Comment + " [" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "] [" + flexSpot.UTCTime + "]"
|
|
|
|
if sp.HTTPServer != nil && sp.HTTPServer.Watchlist != nil {
|
|
if sp.HTTPServer.Watchlist.Matches(flexSpot.DX) {
|
|
flexSpot.InWatchlist = true
|
|
|
|
// Mark as seen and update last seen time
|
|
sp.HTTPServer.Watchlist.MarkSeen(flexSpot.DX)
|
|
|
|
// Get entry to check if sound should be played
|
|
entry := sp.HTTPServer.Watchlist.GetEntry(flexSpot.DX)
|
|
if entry != nil {
|
|
Log.Infof("🎯 Watchlist match: %s (LastSeen: %s)",
|
|
flexSpot.DX, entry.LastSeenStr)
|
|
|
|
// Send notification to websocket clients for sound alert
|
|
if entry.PlaySound && sp.HTTPServer != nil {
|
|
sp.HTTPServer.broadcast <- WSMessage{
|
|
Type: "watchlistAlert",
|
|
Data: map[string]interface{}{
|
|
"callsign": flexSpot.DX,
|
|
"frequency": flexSpot.FrequencyMhz,
|
|
"band": flexSpot.Band,
|
|
"mode": flexSpot.Mode,
|
|
"countryName": flexSpot.CountryName,
|
|
"playSound": entry.PlaySound,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sp.applySpotColors(&flexSpot, spot)
|
|
sp.sendGotifyNotification(flexSpot)
|
|
|
|
flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
|
|
|
|
srcFlexSpot, err := sp.FlexRepo.FindDXSameBand(flexSpot)
|
|
if err != nil {
|
|
Log.Debugf("Could not find the DX in the database: %v", err)
|
|
}
|
|
|
|
// Vérifier si le spot trouvé est valide (a un ID)
|
|
if srcFlexSpot != nil && srcFlexSpot.ID == 0 {
|
|
srcFlexSpot = nil
|
|
}
|
|
|
|
sp.handleSpotStorage(flexSpot, srcFlexSpot)
|
|
|
|
if sp.FlexClient != nil && sp.FlexClient.Enabled && sp.FlexClient.IsConnected {
|
|
sp.sendToFlexRadio(flexSpot, srcFlexSpot)
|
|
}
|
|
}
|
|
|
|
func (sp *SpotProcessor) applySpotColors(flexSpot *FlexSpot, spot TelnetSpot) {
|
|
if spot.NewDXCC {
|
|
flexSpot.Priority = "1"
|
|
flexSpot.Comment = flexSpot.Comment + " [New DXCC]"
|
|
if Cfg.General.SpotColorNewEntity != "" {
|
|
flexSpot.Color = Cfg.General.SpotColorNewEntity
|
|
flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewEntity
|
|
} else {
|
|
flexSpot.Color = "#ff3bf908"
|
|
flexSpot.BackgroundColor = "#ff000000"
|
|
}
|
|
} else if spot.DX == Cfg.General.Callsign {
|
|
flexSpot.Priority = "1"
|
|
if Cfg.General.SpotColorMyCallsign != "" {
|
|
flexSpot.Color = Cfg.General.SpotColorMyCallsign
|
|
flexSpot.BackgroundColor = Cfg.General.BackgroundColorMyCallsign
|
|
} else {
|
|
flexSpot.Color = "#ffff0000"
|
|
flexSpot.BackgroundColor = "#ff000000"
|
|
}
|
|
} else if spot.CallsignWorked {
|
|
flexSpot.Priority = "5"
|
|
flexSpot.Comment = flexSpot.Comment + " [Worked]"
|
|
if Cfg.General.SpotColorWorked != "" {
|
|
flexSpot.Color = Cfg.General.SpotColorWorked
|
|
flexSpot.BackgroundColor = Cfg.General.BackgroundColorWorked
|
|
} else {
|
|
flexSpot.Color = "#ff000000"
|
|
flexSpot.BackgroundColor = "#ff00c0c0"
|
|
}
|
|
} else if spot.NewMode && spot.NewBand {
|
|
flexSpot.Priority = "1"
|
|
flexSpot.Comment = flexSpot.Comment + " [New Band & Mode]"
|
|
if Cfg.General.SpotColorNewBandMode != "" {
|
|
flexSpot.Color = Cfg.General.SpotColorNewBandMode
|
|
flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewBandMode
|
|
} else {
|
|
flexSpot.Color = "#ffc603fc"
|
|
flexSpot.BackgroundColor = "#ff000000"
|
|
}
|
|
} else if spot.NewMode && !spot.NewBand {
|
|
flexSpot.Priority = "2"
|
|
flexSpot.Comment = flexSpot.Comment + " [New Mode]"
|
|
if Cfg.General.SpotColorNewMode != "" {
|
|
flexSpot.Color = Cfg.General.SpotColorNewMode
|
|
flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewMode
|
|
} else {
|
|
flexSpot.Color = "#fff9a908"
|
|
flexSpot.BackgroundColor = "#ff000000"
|
|
}
|
|
} else if spot.NewBand && !spot.NewMode {
|
|
flexSpot.Color = "#fff9f508"
|
|
flexSpot.Priority = "3"
|
|
flexSpot.BackgroundColor = "#ff000000"
|
|
flexSpot.Comment = flexSpot.Comment + " [New Band]"
|
|
} else if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked && spot.NewSlot {
|
|
flexSpot.Color = "#ff91d2ff"
|
|
flexSpot.Priority = "5"
|
|
flexSpot.BackgroundColor = "#ff000000"
|
|
flexSpot.Comment = flexSpot.Comment + " [New Slot]"
|
|
}
|
|
}
|
|
|
|
func (sp *SpotProcessor) handleSpotStorage(flexSpot FlexSpot, srcFlexSpot *FlexSpot) {
|
|
if srcFlexSpot == nil {
|
|
sp.FlexRepo.CreateSpot(flexSpot)
|
|
CommandNumber++
|
|
if sp.HTTPServer != nil {
|
|
sp.HTTPServer.broadcast <- WSMessage{Type: "spots", Data: sp.FlexRepo.GetAllSpots("0")}
|
|
}
|
|
} else if srcFlexSpot.Band == flexSpot.Band {
|
|
sp.FlexRepo.DeleteSpotByFlexSpotNumber(fmt.Sprintf("%d", srcFlexSpot.FlexSpotNumber))
|
|
sp.FlexRepo.CreateSpot(flexSpot)
|
|
CommandNumber++
|
|
} else {
|
|
sp.FlexRepo.CreateSpot(flexSpot)
|
|
CommandNumber++
|
|
}
|
|
}
|
|
|
|
func (sp *SpotProcessor) sendToFlexRadio(flexSpot FlexSpot, srcFlexSpot *FlexSpot) {
|
|
var stringSpot string
|
|
|
|
if srcFlexSpot == nil {
|
|
stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
|
|
flexSpot.CommandNumber, flexSpot.FrequencyMhz, flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
|
|
flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
|
|
CommandNumber++
|
|
sp.FlexClient.SendSpot(stringSpot)
|
|
} else if srcFlexSpot.Band == flexSpot.Band {
|
|
stringSpot = fmt.Sprintf("C%v|spot remove %v", flexSpot.CommandNumber, srcFlexSpot.FlexSpotNumber)
|
|
sp.FlexClient.SendSpot(stringSpot)
|
|
CommandNumber++
|
|
|
|
stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
|
|
flexSpot.CommandNumber, flexSpot.FrequencyMhz, flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
|
|
flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
|
|
CommandNumber++
|
|
sp.FlexClient.SendSpot(stringSpot)
|
|
} else {
|
|
stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
|
|
flexSpot.CommandNumber, flexSpot.FrequencyMhz, flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
|
|
flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
|
|
CommandNumber++
|
|
sp.FlexClient.SendSpot(stringSpot)
|
|
}
|
|
}
|
|
|
|
func (sp *SpotProcessor) sendGotifyNotification(flexSpot FlexSpot) {
|
|
if !Cfg.Gotify.Enable {
|
|
return
|
|
}
|
|
|
|
// Cas 1 : Nouveau DXCC - toujours notifier si activé dans la config
|
|
if flexSpot.NewDXCC && Cfg.Gotify.NewDXCC {
|
|
Gotify(flexSpot)
|
|
Log.Debugf("📢 Gotify notification sent: New DXCC - %s", flexSpot.DX)
|
|
return
|
|
}
|
|
|
|
// Cas 2 : Callsign dans la watchlist ET non contacté
|
|
if flexSpot.InWatchlist && !flexSpot.Worked {
|
|
Gotify(flexSpot)
|
|
Log.Debugf("📢 Gotify notification sent: Watchlist match (not worked) - %s", flexSpot.DX)
|
|
return
|
|
}
|
|
|
|
// Tous les autres cas : pas de notification
|
|
Log.Debugf("🔇 Gotify notification skipped for %s (InWatchlist=%v, Worked=%v, NewDXCC=%v)",
|
|
flexSpot.DX, flexSpot.InWatchlist, flexSpot.Worked, flexSpot.NewDXCC)
|
|
}
|