PushDXCluster/main.go

233 lines
5.5 KiB
Go

package main
import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"regexp"
"strings"
"time"
)
var DX = readDXExpeFile("dx.txt")
type ClusterMessage struct {
From string
DX string
Freq string
Mode string
Report string
Time string
}
func ParseFlags() (string, error) {
// String that contains the configured configuration path
var configPath string
// Set up a CLI flag called "-config" to allow users
// to supply the configuration file
flag.StringVar(&configPath, "config", "./config.yml", "path to config file")
// Actually parse the flags
flag.Parse()
// Validate the path first
if err := ValidateConfigPath(configPath); err != nil {
return "", err
}
// Return the configuration path
return configPath, nil
}
// Message structure for Gotify
type GotifyMessage struct {
Title string `json:"title"`
Message string `json:"message"`
Priority int `json:"priority"`
}
func readDXExpeFile(filename string) string {
file, err := os.Open(filename)
if err != nil {
fmt.Println("Error while opening file", err)
}
defer file.Close()
// Lire tout le contenu du fichier
content, err := io.ReadAll(file)
if err != nil {
fmt.Println("Error while reading the file", err)
}
log.Printf("DX Expe file has been loaded properly with following calls: %s", content)
return string(content)
}
// Function to send message to Gotify
func sendToGotify(title string, sMess ClusterMessage, priority int, cfg Config) {
message := fmt.Sprintf("DX: %s\nFrom: %s\nFreq: %s\nMode: %s\nReport: %s\nTime: %s", sMess.DX, sMess.From, sMess.Freq, sMess.Mode, sMess.Report, sMess.Time)
gotifyMsg := GotifyMessage{
Title: title,
Message: message,
Priority: priority,
}
jsonData, err := json.Marshal(gotifyMsg)
if err != nil {
log.Println("Error marshaling JSON:", err)
return
}
req, err := http.NewRequest("POST", cfg.Gotify.URL, bytes.NewBuffer(jsonData))
if err != nil {
log.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer "+cfg.Gotify.Token)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Println("Gotify server returned non-OK status:", resp.Status)
} else {
log.Println("message successfully sent to Gotify")
}
}
func SanitizeClusterMessage(message string) ClusterMessage {
r := regexp.MustCompile(`DX\sde\s([A-Z0-9]+)[-#:]+[\s]+([0-9]+.[0-9])[\s]+([^\s]+)[\s]+([A-Z]+[0-9])\s+(.*dB).*(.{4})Z$`)
matches := r.FindStringSubmatch(message)
mes := ClusterMessage{}
if len(matches) > 0 {
timeLayout := "1504"
formatedTime, _ := time.Parse(timeLayout, matches[6])
formatedTime = formatedTime.Add(time.Hour * time.Duration(7))
newTime := formatedTime.Format("15:04")
mes := ClusterMessage{
From: matches[1],
Freq: matches[2],
DX: matches[3],
Mode: matches[4],
Report: matches[5],
Time: newTime,
}
return mes
}
return mes
}
func sendTelnetMessage(conn net.Conn, message string) {
if message != "XV9Q-5" {
time.Sleep(2 * time.Second)
}
_, err := conn.Write([]byte(message + "\n"))
if err != nil {
conn.Close()
time.Sleep(5 * time.Second) // Wait before retrying
}
}
func sendFilters(conn net.Conn) {
go sendTelnetMessage(conn, "set/ft8")
time.Sleep(1 * time.Second)
//go sendTelnetMessage(conn, "SET/FILTER DOC/PASS 3W")
//time.Sleep(1 * time.Second)
go sendTelnetMessage(conn, "set/skimmer")
time.Sleep(1 * time.Second)
go sendTelnetMessage(conn, "set/ft4")
}
func main() {
// Generate our config based on the config supplied
// by the user in the flags
cfgPath, err := ParseFlags()
if err != nil {
log.Fatal(err)
}
cfg, err := NewConfig(cfgPath)
if err != nil {
log.Fatal(err)
}
login := false
filters_sent := false
fmt.Println("PushDXCluster v0.1")
for {
// Connect to the Telnet server
conn, err := net.Dial("tcp", cfg.Cluster.Host)
if err != nil {
log.Printf("Failed to connect to Telnet server: %v", err)
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
log.Printf("Connected to %s server", cfg.Cluster.Host)
// Create a buffered reader to read from the Telnet server
reader := bufio.NewReader(conn)
// Loop to read from the Telnet server
for {
message, err := reader.ReadString('\n')
if err != nil {
log.Printf("Error reading from Telnet server: %v", err)
conn.Close()
break
}
// Trim and Sanitize spots messages
message = strings.TrimSpace(message)
sMess := SanitizeClusterMessage(message)
if sMess.DX != "" {
log.Printf("Sanitized message: Reporter: %s, DX: %s, Freq: %s, Report: %s, Time: %s", sMess.From, sMess.DX, sMess.Freq, sMess.Report, sMess.Time)
} else {
log.Printf("Received message: %s", message)
}
// Send Call to ID to the Server
if message != "" && strings.Contains("Please enter your call:", message) {
go sendTelnetMessage(conn, "XV9Q-5")
login = true
}
// Set the FT8, CW, FT4 and only spots from Vietnam filters only once
if login && !filters_sent {
go sendFilters(conn)
filters_sent = true
}
// If calls is in the DX List then send a Gotify notification
if sMess.DX != "" && sMess.From == "XV9Q" && strings.Contains(DX, sMess.DX) {
sendToGotify("Spot", sMess, 5, *cfg)
}
}
log.Println("Disconnected from Telnet server, reconnecting...")
time.Sleep(5 * time.Second) // Wait before reconnecting
}
}