Files
FlexDXCluster/TCPClient.go
2025-09-07 20:29:55 +02:00

219 lines
5.2 KiB
Go

package main
import (
"bufio"
"net"
"os"
"regexp"
"strings"
"time"
log "github.com/sirupsen/logrus"
)
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 {
Login string
Password string
Address string
Port string
LoggedIn bool
Timeout time.Duration
LogWriter *bufio.Writer
Reader *bufio.Reader
Writer *bufio.Writer
Scanner *bufio.Scanner
Conn net.Conn
TCPServer TCPServer
MsgChan chan string
CmdChan chan string
SpotChanToFlex chan TelnetSpot
SpotChanToHTTPServer chan TelnetSpot
Log *log.Logger
Config *Config
Countries Countries
LoginRe *regexp.Regexp
PasswordRe *regexp.Regexp
}
func NewTCPClient(TCPServer *TCPServer, Countries Countries) *TCPClient {
return &TCPClient{
Address: Cfg.Cluster.Server,
Port: Cfg.Cluster.Port,
Login: Cfg.Cluster.Login,
Password: Cfg.Cluster.Password,
MsgChan: TCPServer.MsgChan,
CmdChan: TCPServer.CmdChan,
SpotChanToFlex: make(chan TelnetSpot, 100),
TCPServer: *TCPServer,
SpotChanToHTTPServer: make(chan TelnetSpot, 100),
Countries: Countries,
}
}
func (c *TCPClient) setDefaultParams() {
if c.Timeout == 0 {
c.Timeout = 600 * time.Second
}
if c.LogWriter == nil {
c.LogWriter = bufio.NewWriter(os.Stdout)
}
c.LoggedIn = false
if c.LoginRe == nil {
c.LoginRe = defaultLoginRe
}
if c.PasswordRe == nil {
c.PasswordRe = defaultPasswordRe
}
}
func (c *TCPClient) StartClient() {
var err error
c.setDefaultParams()
c.Conn, err = net.Dial("tcp", c.Address+":"+c.Port)
if err != nil {
Log.Error("Cannot connect to Telnet Client:", err)
}
c.Reader = bufio.NewReader(c.Conn)
c.Writer = bufio.NewWriter(c.Conn)
go func() {
for message := range c.TCPServer.CmdChan {
Log.Infof("Received Command: %s", message)
c.Write([]byte(message + "\r\n"))
}
}()
go c.ReadLine()
}
func (c *TCPClient) Close() {
c.Writer.WriteString("bye")
time.Sleep(time.Second * 2)
}
func (c *TCPClient) SetFilters() {
if Cfg.Cluster.FT8 {
c.Write([]byte("set/ft8\r\n"))
Log.Info("FT8: On")
}
if Cfg.Cluster.Skimmer {
c.Write([]byte("set/skimmer\r\n"))
Log.Info("Skimmer: On")
}
if Cfg.Cluster.FT4 {
c.Write([]byte("set/ft4\r\n"))
Log.Info("FT4: On")
}
if !Cfg.Cluster.FT8 {
c.Write([]byte("set/noft8\r\n"))
Log.Info("FT8: Off")
}
if !Cfg.Cluster.FT4 {
c.Write([]byte("set/noft4\r\n"))
Log.Info("FT4: Off")
}
if !Cfg.Cluster.Skimmer {
c.Write([]byte("set/noskimmer\r\n"))
Log.Info("Skimmer: Off")
}
}
func (c *TCPClient) ReadLine() {
for {
// Need to check data with space first to find login and then use \n
if !c.LoggedIn {
message, err := c.Reader.ReadBytes(':')
if err != nil {
Log.Errorf("Error reading message: %s", err)
c.Conn.Close()
c.StartClient()
}
// 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")
c.Write([]byte(c.Login + "\n\r"))
c.LoggedIn = true
Log.Infof("Connected to DX cluster %s:%s", Cfg.Cluster.Server, Cfg.Cluster.Port)
continue
}
}
if c.LoggedIn {
message, err := c.Reader.ReadBytes('\n')
messageString := string(message)
// Log.Println(messageString)
if messageString != "" {
if err != nil {
Log.Errorf("Error reading message: %s", err)
c.Conn.Close()
c.StartClient()
}
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
}
}
}
}
// Write sends raw data to remove telnet server
func (tc *TCPClient) Write(data []byte) (n int, err error) {
n, err = tc.Writer.Write(data)
if err == nil {
err = tc.Writer.Flush()
}
return
}
func (tc *TCPClient) WriteString(data string) (n int, err error) {
n, err = tc.Writer.Write([]byte(data))
if err == nil {
err = tc.Writer.Flush()
}
return
}