This commit is contained in:
2025-09-07 20:29:55 +02:00
parent 2815ea6ea0
commit a7716a65c7
10 changed files with 78 additions and 95 deletions

View File

@@ -12,6 +12,8 @@ import (
) )
var spotRe *regexp.Regexp = regexp.MustCompile(`DX\sde\s([\w\d]+).*:\s+(\d+.\d)\s+([\w\d\/]+)\s+(CW|SSB|FT8|FT4|RTTY|USB|LSB)?\s+(.*)\s\s\s+([\d]+\w{1})`) var spotRe *regexp.Regexp = regexp.MustCompile(`DX\sde\s([\w\d]+).*:\s+(\d+.\d)\s+([\w\d\/]+)\s+(CW|SSB|FT8|FT4|RTTY|USB|LSB)?\s+(.*)\s\s\s+([\d]+\w{1})`)
var defaultLoginRe *regexp.Regexp = regexp.MustCompile("[\\w\\d-_]+ login:")
var defaultPasswordRe *regexp.Regexp = regexp.MustCompile("Password:")
type TCPClient struct { type TCPClient struct {
Login string Login string
@@ -24,7 +26,7 @@ type TCPClient struct {
Reader *bufio.Reader Reader *bufio.Reader
Writer *bufio.Writer Writer *bufio.Writer
Scanner *bufio.Scanner Scanner *bufio.Scanner
Conn *net.TCPConn Conn net.Conn
TCPServer TCPServer TCPServer TCPServer
MsgChan chan string MsgChan chan string
CmdChan chan string CmdChan chan string
@@ -33,6 +35,8 @@ type TCPClient struct {
Log *log.Logger Log *log.Logger
Config *Config Config *Config
Countries Countries Countries Countries
LoginRe *regexp.Regexp
PasswordRe *regexp.Regexp
} }
func NewTCPClient(TCPServer *TCPServer, Countries Countries) *TCPClient { func NewTCPClient(TCPServer *TCPServer, Countries Countries) *TCPClient {
@@ -58,35 +62,32 @@ func (c *TCPClient) setDefaultParams() {
c.LogWriter = bufio.NewWriter(os.Stdout) c.LogWriter = bufio.NewWriter(os.Stdout)
} }
c.LoggedIn = false c.LoggedIn = false
if c.LoginRe == nil {
c.LoginRe = defaultLoginRe
}
if c.PasswordRe == nil {
c.PasswordRe = defaultPasswordRe
}
} }
func (c *TCPClient) StartClient() { func (c *TCPClient) StartClient() {
var err error var err error
addr, err := net.ResolveTCPAddr("tcp", c.Address+":"+c.Port)
if err != nil {
Log.Error("Cannot resolve Telnet Client address:", err)
}
c.setDefaultParams() c.setDefaultParams()
c.Conn, err = net.DialTCP("tcp", nil, addr)
c.Conn, err = net.Dial("tcp", c.Address+":"+c.Port)
if err != nil { if err != nil {
Log.Error("Cannot connect to Telnet Client:", err) Log.Error("Cannot connect to Telnet Client:", err)
} }
// err = c.Conn.SetKeepAlive(true)
// if err != nil {
// Log.Error("Error while setting keep alive:", err)
// }
c.Reader = bufio.NewReader(c.Conn) c.Reader = bufio.NewReader(c.Conn)
c.Writer = bufio.NewWriter(c.Conn) c.Writer = bufio.NewWriter(c.Conn)
go func() { go func() {
for message := range c.TCPServer.CmdChan { for message := range c.TCPServer.CmdChan {
Log.Infof("Received Command: %s", message) Log.Infof("Received Command: %s", message)
message := message + "\n" c.Write([]byte(message + "\r\n"))
c.WriteString(message)
} }
}() }()
@@ -133,64 +134,67 @@ func (c *TCPClient) SetFilters() {
func (c *TCPClient) ReadLine() { func (c *TCPClient) ReadLine() {
for { for {
// Need to check data with space first to find login and then use \n // Need to check data with space first to find login and then use \n
if !c.LoggedIn { if !c.LoggedIn {
message, err := c.Reader.ReadString(' ') message, err := c.Reader.ReadBytes(':')
message, _ = strings.CutSuffix(message, "\n")
message, _ = strings.CutSuffix(message, "\r")
if err != nil { if err != nil {
Log.Errorf("Error reading message: %s", err) Log.Errorf("Error reading message: %s", err)
c.Conn.Close() c.Conn.Close()
c.StartClient() c.StartClient()
} }
if strings.Contains(message, Cfg.Cluster.LoginPrompt) { // message, _ = strings.CutSuffix(message, "\n")
// message, _ = strings.CutSuffix(message, "\r")
if strings.Contains(string(message), Cfg.Cluster.LoginPrompt) || strings.Contains(string(message), "login:") {
time.Sleep(time.Second * 1)
Log.Debug("Found login prompt...sending callsign") Log.Debug("Found login prompt...sending callsign")
c.Write([]byte(c.Login + "\r\n")) c.Write([]byte(c.Login + "\n\r"))
c.LoggedIn = true c.LoggedIn = true
c.SetFilters()
if Cfg.Cluster.Command != "" {
c.WriteString(Cfg.Cluster.Command + "\n\r")
}
Log.Infof("Connected to DX cluster %s:%s", Cfg.Cluster.Server, Cfg.Cluster.Port) Log.Infof("Connected to DX cluster %s:%s", Cfg.Cluster.Server, Cfg.Cluster.Port)
Log.Info("Start receiving spots")
continue continue
} }
} }
if c.LoggedIn { if c.LoggedIn {
message, err := c.Reader.ReadString('\n') message, err := c.Reader.ReadBytes('\n')
message, _ = strings.CutSuffix(message, "\n") messageString := string(message)
message, _ = strings.CutSuffix(message, "\r") // Log.Println(messageString)
if strings.Contains(message, "password") { if messageString != "" {
Log.Debug("Found password prompt...sending password") if err != nil {
c.Write([]byte(c.Password + "\r\n")) Log.Errorf("Error reading message: %s", err)
} c.Conn.Close()
c.StartClient()
if err != nil {
Log.Errorf("Error reading message: %s", err)
c.Conn.Close()
c.StartClient()
}
if strings.Contains(message, "Error reading from server: read tcp") {
Log.Error("Disconnected from Telnet Server, reconnecting")
c.Close()
c.StartClient()
} else {
if c.LoggedIn && strings.Contains(message, "DX") {
ProcessTelnetSpot(spotRe, message, c.SpotChanToFlex, c.SpotChanToHTTPServer, c.Countries)
} }
if strings.Contains(messageString, "password") {
Log.Debug("Found password prompt...sending password...")
c.Write([]byte(c.Password + "\r\n"))
}
if strings.Contains(messageString, "Hello") || strings.Contains(messageString, "Welcome") {
go c.SetFilters()
if Cfg.Cluster.Command != "" {
c.WriteString(Cfg.Cluster.Command + "\n\r")
Log.Debugf("Sending Command: %s", Cfg.Cluster.Command)
}
}
if strings.Contains(messageString, "Error reading from server: read tcp") {
Log.Error("Disconnected from Telnet Server, reconnecting")
c.Close()
c.StartClient()
}
if strings.Contains(messageString, "DX") {
ProcessTelnetSpot(spotRe, messageString, c.SpotChanToFlex, c.SpotChanToHTTPServer, c.Countries)
}
// Send the spot message to TCP server
c.MsgChan <- messageString
} }
// Send the spot message to TCP server
c.MsgChan <- message
} }
} }
} }

View File

@@ -64,7 +64,7 @@ func (s *TCPServer) StartServer() {
for { for {
s.Conn, err = s.Listener.Accept() s.Conn, err = s.Listener.Accept()
Log.Info("Client connected", s.Conn.RemoteAddr().String()) Log.Info("Client connected: ", s.Conn.RemoteAddr().String())
if err != nil { if err != nil {
Log.Error("Could not accept connections to telnet server") Log.Error("Could not accept connections to telnet server")
continue continue

View File

@@ -2,12 +2,12 @@ general:
delete_log_file_at_start: true delete_log_file_at_start: true
callsign: F4BPO # Log4OM Callsign used to check if you get spotted by someone callsign: F4BPO # Log4OM Callsign used to check if you get spotted by someone
log_to_file: true log_to_file: true
log_level: DEBUG # INFO or DEBUG or WARN log_level: INFO # INFO or DEBUG or WARN
telnetserver: true # not in use for now telnetserver: true # not in use for now
flexradiospot: true # not in use for now flexradiospot: true # not in use for now
database: database:
mysql: true #only one of the two can be true mysql: false #only one of the two can be true
sqlite: false sqlite: true
mysql_db_user: rouggy mysql_db_user: rouggy
mysql_db_password: 89DGgg290379 mysql_db_password: 89DGgg290379
mysql_db_name: log_f4bpo mysql_db_name: log_f4bpo
@@ -16,17 +16,17 @@ database:
sqlite: sqlite:
sqlite_path: 'C:\Perso\Seafile\Radio\Logs\Log4OM\F4BPO.SQLite' # SQLite Db oath of Log4OM sqlite_path: 'C:\Perso\Seafile\Radio\Logs\Log4OM\F4BPO.SQLite' # SQLite Db oath of Log4OM
cluster: cluster:
server: dxc.sm7iun.se # dxc.k0xm.net dxc.sm7iun.se server: cluster.f4bpo.com # dxc.k0xm.net dxc.sm7iun.se
port: 7300 port: 7300
login: f4bpo login: f4bpo
password: 89DGgg password: 89DGgg
skimmer: true skimmer: false
ft8: false ft8: false
ft4: false ft4: false
command: "SET/NOFILTER" #"SET/FILTER DOC/PASS EA,OH,G,F,DL,I,SV,9A,SK,S5,LX,OE,HA,CT" command: "SET/FILTER DOC/PASS 1A,3A,4O,9A,9H,C3,CT,CU,DL,E7,EA,EA6,EI,ER,ES,EU,F,G,GD,GI,GJ,GM,GU,GW,HA,HB,HB0,HV,I,IS,IT9,JW,JX,LA,LX,LY,LZ,OE,OH,OH0,OJ0,OK,OM,ON,OY,OZ,PA,S5,SM,SP,SV,SV5,SV9,T7,TA1,TF,TK,UA,UR,YL,YO,YU,Z6,Z3,ZA,ZB" #"SET/FILTER DOC/PASS 1A,3A,4O,9A,9H,C3,CT,CU,DL,E7,EA,EA6,EI,ER,ES,EU,F,G,GD,GI,GJ,GM,GU,GW,HA,HB,HB0,HV,I,IS,IT9,JW,JX,LA,LX,LY,LZ,OE,OH,OH0,OJ0,OK,OM,ON,OY,OZ,PA,S5,SM,SP,SV,SV5,SV9,T7,TA1,TF,TK,UA,UR,YL,YO,YU,Z6,Z3,ZA,ZB"
login_prompt: "login:" login_prompt: "login:"
flex: flex:
discovery: false # Radio must be on same LAN than the program discovery: true # Radio must be on same LAN than the program
ip: 82.67.157.19 # if discovery is true no need to put an IP ip: 82.67.157.19 # if discovery is true no need to put an IP
spot_life: 600 #seconds spot_life: 600 #seconds
telnetserver: # Log4OM must be connected to this server ie: localhost:7301 if on same machine as this program else ip:7301 telnetserver: # Log4OM must be connected to this server ie: localhost:7301 if on same machine as this program else ip:7301

View File

@@ -9921,33 +9921,6 @@
</CountryPrefix> </CountryPrefix>
</CountryPrefixList> </CountryPrefixList>
</Country> </Country>
<Country>
<ArrlPrefix>VS2</ArrlPrefix>
<Comment />
<Continent>AS</Continent>
<CountryName>Malaya</CountryName>
<CqZone>28</CqZone>
<CqZoneList>
<int>28</int>
</CqZoneList>
<Dxcc>155</Dxcc>
<ItuZone>54</ItuZone>
<IaruRegion>3</IaruRegion>
<ItuZoneList>
<int>54</int>
</ItuZoneList>
<Latitude>1.3</Latitude>
<Longitude>103.8</Longitude>
<Active>false</Active>
<CountryTag />
<CountryPrefixList>
<CountryPrefix>
<PrefixList>^9M2.*|^VS2.*</PrefixList>
<StartDate xsi:nil="true" />
<EndDate>1963-09-15T23:59:59Z</EndDate>
</CountryPrefix>
</CountryPrefixList>
</Country>
<Country> <Country>
<ArrlPrefix>8Q</ArrlPrefix> <ArrlPrefix>8Q</ArrlPrefix>
<Comment /> <Comment />

View File

@@ -145,9 +145,9 @@ func (r *Log4OMContactsRepository) ListByCountry(countryID string, contactsChan
func (r *Log4OMContactsRepository) ListByCountryMode(countryID string, mode string, contactsModeChan chan []Contact, wg *sync.WaitGroup) { func (r *Log4OMContactsRepository) ListByCountryMode(countryID string, mode string, contactsModeChan chan []Contact, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
if mode == "USB" || mode == "LSB" { if mode == "USB" || mode == "LSB" || mode == "SSB" {
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND (mode = ? OR mode = ?)", countryID, "USB", "LSB") rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND (mode = ? OR mode = ? OR mode = ?)", countryID, "USB", "LSB", "SSB")
if err != nil { if err != nil {
log.Error("could not query database", err) log.Error("could not query database", err)
} }

View File

@@ -36,6 +36,7 @@ type FlexSpot struct {
NewMode bool NewMode bool
NewSlot bool NewSlot bool
Worked bool Worked bool
CountryName string
} }
type Discovery struct { type Discovery struct {
@@ -167,6 +168,7 @@ func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
NewMode: spot.NewMode, NewMode: spot.NewMode,
NewSlot: spot.NewSlot, NewSlot: spot.NewSlot,
Worked: spot.CallsignWorked, Worked: spot.CallsignWorked,
CountryName: spot.CountryName,
} }
flexSpot.Comment = flexSpot.Comment + " [" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "]" flexSpot.Comment = flexSpot.Comment + " [" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "]"

View File

@@ -20,7 +20,7 @@ func Gotify(spot FlexSpot) {
if Cfg.Gotify.Enable && !strings.Contains(ExceptionList, spot.DX) { if Cfg.Gotify.Enable && !strings.Contains(ExceptionList, spot.DX) {
message := fmt.Sprintf("DX: %s\nFrom: %s\nFreq: %s\nMode: %s\nTime: %v\n", spot.DX, spot.SpotterCallsign, spot.FrequencyMhz, spot.Mode, spot.TimeStamp) message := fmt.Sprintf("DX: %s\nFrom: %s\nFreq: %s\nMode: %s\nCountry: %s\nTime: %s\n", spot.DX, spot.SpotterCallsign, spot.FrequencyMhz, spot.Mode, spot.CountryName, spot.UTCTime)
gotifyMsg := GotifyMessage{ gotifyMsg := GotifyMessage{
Title: "", Title: "",

View File

@@ -54,6 +54,7 @@ func main() {
// Load country.xml to get all the DXCC number // Load country.xml to get all the DXCC number
Countries := LoadCountryFile() Countries := LoadCountryFile()
log.Debug("XML Country File has been loaded properly.")
// Database to keep track of all spots // Database to keep track of all spots
fRepo := NewFlexDXDatabase("flex.sqlite") fRepo := NewFlexDXDatabase("flex.sqlite")

View File

@@ -17,6 +17,7 @@ type TelnetSpot struct {
Band string Band string
Time string Time string
DXCC string DXCC string
CountryName string
Comment string Comment string
CommandNumber int CommandNumber int
FlexSpotNumber int FlexSpotNumber int
@@ -43,7 +44,9 @@ func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChanToFlex chan Te
Time: match[6], Time: match[6],
} }
spot.DXCC = GetDXCC(spot.DX, Countries) DXCC := GetDXCC(spot.DX, Countries)
spot.DXCC = DXCC.DXCC
spot.CountryName = DXCC.CountryName
if spot.DXCC == "" { if spot.DXCC == "" {
Log.Errorf("Could not identify the DXCC for %s", spot.DX) Log.Errorf("Could not identify the DXCC for %s", spot.DX)

6
xml.go
View File

@@ -75,7 +75,7 @@ func LoadCountryFile() Countries {
return countries return countries
} }
func GetDXCC(dxCall string, Countries Countries) string { func GetDXCC(dxCall string, Countries Countries) DXCC {
DXCCList := []DXCC{} DXCCList := []DXCC{}
d := DXCC{} d := DXCC{}
@@ -130,10 +130,10 @@ func GetDXCC(dxCall string, Countries Countries) string {
DXCCMatch = DXCCList[0] DXCCMatch = DXCCList[0]
} }
return DXCCMatch.DXCC return DXCCMatch
} else { } else {
Log.Errorf("Could not find %s in country list", dxCall) Log.Errorf("Could not find %s in country list", dxCall)
} }
return "" return DXCC{}
} }