Compare commits

..

19 Commits

Author SHA1 Message Date
115051d463 mode 2025-04-06 13:39:15 +02:00
9a03f3d4c6 up 2025-04-06 13:36:08 +02:00
bd1b8da2f7 update call 2025-04-06 12:10:23 +02:00
a394767832 update 2024-10-09 20:30:23 +03:00
f424633083 up 2024-10-01 11:33:11 +07:00
a39dadce1a Merge branch 'main' of https://git.rouggy.com/rouggy/PushDXCluster 2024-09-15 17:50:24 +07:00
2d392d8033 update 2024-09-15 17:50:08 +07:00
b680af776e Update main.go 2024-09-15 17:42:28 +07:00
6bb76d73a1 Delete database.gp 2024-09-15 17:32:29 +07:00
80a882c411 up 2024-07-03 15:46:24 +07:00
acde3434dd update 2024-07-03 15:44:35 +07:00
45a73d45b9 update 2024-07-03 12:36:38 +07:00
5bac8fd4d2 Merge branch 'main' of https://git.rouggy.com/rouggy/PushDXCluster 2024-07-03 12:30:45 +07:00
9e72764c2a update 2024-07-03 12:30:11 +07:00
6d8d69deeb Delete rsrc_windows_amd64.syso 2024-07-01 23:27:35 +07:00
0892078851 Delete rsrc_windows_386.syso 2024-07-01 23:27:18 +07:00
28e606c414 remove exe 2024-07-01 23:26:48 +07:00
54e087b2e1 up 2024-07-01 23:25:13 +07:00
8d5d5ca34a update 2024-07-01 23:24:53 +07:00
9 changed files with 158 additions and 56 deletions

BIN
PushDXCluster.exe Normal file

Binary file not shown.

51
config.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"fmt"
"os"
"gopkg.in/yaml.v2"
)
type Config struct {
Gotify struct {
URL string `yaml:"url"`
Token string `yanl:"token"`
} `yaml:"gotify"`
Cluster struct {
Host string `yaml:"host"`
Call string `yaml:"call"`
} `yaml:"cluster"`
Log struct {
SQLitePath string `yaml:"sqlitePath"`
} `yaml:"log"`
}
func NewConfig(configPath string) (*Config, error) {
config := &Config{}
file, err := os.Open(configPath)
if err != nil {
return nil, err
}
defer file.Close()
d := yaml.NewDecoder(file)
if err := d.Decode(&config); err != nil {
return nil, err
}
return config, nil
}
func ValidateConfigPath(path string) error {
s, err := os.Stat(path)
if err != nil {
return err
}
if s.IsDir() {
return fmt.Errorf("'%s' is a directory, not a normal file", path)
}
return nil
}

View File

@ -3,5 +3,8 @@ gotify:
token: ALaGS4MVMWTEMcP token: ALaGS4MVMWTEMcP
cluster: cluster:
host: dxc.nc7j.com:23 host: dxc.k0xm.net:7300
call: XV9Q call: F4BPO
log:
sqlitePath: "C:\\Perso\\Seafile\\Radio\\Logs\\Log4OM\\Vietnam.SQLite"

2
dx.txt
View File

@ -1 +1 @@
8P9CB ZC4GW TM5FI 3A/LB3LJ VP2V/W5GI VP9/AB2E 8Q7KR FT4GL 8Q7KB 5W1SA TF2MSN FW1JG CO8BLY PZ5RA OD5KU TF2MSN T32AZ DT0IP YN2RP KH8T 9J2AO XT2AW 6O3T 5H1WX 5H3MB CR3W A25AO 5W1SA

2
go.mod
View File

@ -1,3 +1,5 @@
module gitea.rouggy.com/PushDXCluster module gitea.rouggy.com/PushDXCluster
go 1.21.4 go 1.21.4
require gopkg.in/yaml.v2 v2.4.0

4
go.sum Normal file
View File

@ -0,0 +1,4 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

148
main.go
View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
"flag"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -15,13 +16,6 @@ import (
"time" "time"
) )
const (
telnetAddress = "dxc.nc7j.com:23" // Remplace par l'adresse et le port de ton serveur Telnet
gotifyURL = "https://gotify.rouggy.com/message" // Remplace par l'URL de ton serveur Gotify
gotifyToken = "ALaGS4MVMWTEMcP" // Remplace par le token de ton application Gotify
identificationMessage = "XV9Q" // Message d'identification à envoyer au serveur Telnet
)
var DX = readDXExpeFile("dx.txt") var DX = readDXExpeFile("dx.txt")
type ClusterMessage struct { type ClusterMessage struct {
@ -33,6 +27,26 @@ type ClusterMessage struct {
Time 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 // Message structure for Gotify
type GotifyMessage struct { type GotifyMessage struct {
Title string `json:"title"` Title string `json:"title"`
@ -52,12 +66,12 @@ func readDXExpeFile(filename string) string {
if err != nil { if err != nil {
fmt.Println("Error while reading the file", err) fmt.Println("Error while reading the file", err)
} }
log.Println("DX Expe file has been loaded properly") log.Printf("DX Expe file has been loaded properly with following calls: %s", content)
return string(content) return string(content)
} }
// Function to send message to Gotify // Function to send message to Gotify
func sendToGotify(title string, sMess ClusterMessage, priority int) { 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) 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)
@ -73,13 +87,13 @@ func sendToGotify(title string, sMess ClusterMessage, priority int) {
return return
} }
req, err := http.NewRequest("POST", gotifyURL, bytes.NewBuffer(jsonData)) req, err := http.NewRequest("POST", cfg.Gotify.URL, bytes.NewBuffer(jsonData))
if err != nil { if err != nil {
log.Println("Error creating request:", err) log.Println("Error creating request:", err)
return return
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer ALaGS4MVMWTEMcP") req.Header.Add("Authorization", "Bearer "+cfg.Gotify.Token)
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
@ -98,7 +112,7 @@ func sendToGotify(title string, sMess ClusterMessage, priority int) {
} }
func SanitizeClusterMessage(message string) ClusterMessage { 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$`) r := regexp.MustCompile(`DX\sde\s([A-Z0-9]+)[-#:]+[\s]+([0-9]+.[0-9])[\s]+([^\s]+)[\s]+(\bFT8\b|\bFT4\b|\bCW\b)+\s+(.*dB).*(.{4})Z$`)
matches := r.FindStringSubmatch(message) matches := r.FindStringSubmatch(message)
mes := ClusterMessage{} mes := ClusterMessage{}
@ -120,57 +134,58 @@ func SanitizeClusterMessage(message string) ClusterMessage {
return mes return mes
} }
return mes return mes
} }
func sendTelnetMessage(conn net.Conn, message string) {
if !strings.HasPrefix(message, "F4BPO") {
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 F")
time.Sleep(1 * time.Second)
go sendTelnetMessage(conn, "set/skimmer")
time.Sleep(1 * time.Second)
go sendTelnetMessage(conn, "set/ft4")
}
func main() { 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") fmt.Println("PushDXCluster v0.1")
for { for {
// Connect to the Telnet server // Connect to the Telnet server
conn, err := net.Dial("tcp", "ve7cc.net:23") addr, err := net.ResolveTCPAddr("tcp", cfg.Cluster.Host)
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil { if err != nil {
log.Printf("Failed to connect to Telnet server: %v", err) log.Printf("Failed to connect to Telnet server: %v", err)
time.Sleep(5 * time.Second) // Wait before retrying time.Sleep(5 * time.Second) // Wait before retrying
continue continue
} }
log.Println("Connected to Telnet server") log.Printf("Connected to %s server", cfg.Cluster.Host)
time.Sleep(3 * time.Second)
// Send identification message
_, err = conn.Write([]byte(identificationMessage + "\n"))
if err != nil {
log.Printf("Failed to send identification message: %v", err)
conn.Close()
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
log.Println("Identification message sent")
time.Sleep(3 * time.Second)
_, err = conn.Write([]byte("set/ft8" + "\n"))
if err != nil {
log.Printf("Failed to send FT8 message: %v", err)
conn.Close()
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
log.Println("Set FT8 message sent")
time.Sleep(3 * time.Second)
_, err = conn.Write([]byte("SET/FILTER DOC/PASS 3W" + "\n"))
if err != nil {
log.Printf("Failed to send Filter message: %v", err)
conn.Close()
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
log.Println("Set filter Vietnam message sent")
// Create a buffered reader to read from the Telnet server // Create a buffered reader to read from the Telnet server
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
@ -178,18 +193,45 @@ func main() {
// Loop to read from the Telnet server // Loop to read from the Telnet server
for { for {
message, err := reader.ReadString('\n') message, err := reader.ReadString('\n')
message, _ = strings.CutSuffix(message, "\n")
message, _ = strings.CutSuffix(message, "\r")
if err != nil { if err != nil {
log.Printf("Error reading from Telnet server: %v", err) log.Printf("Error reading from Telnet server: %v", err)
conn.Close() conn.Close()
break break
} }
// Trim and Sanitize spots messages
message = strings.TrimSpace(message) message = strings.TrimSpace(message)
sMess := SanitizeClusterMessage(message) sMess := SanitizeClusterMessage(message)
log.Printf("Received message: %s", message)
if sMess.DX != "" && sMess.From == "XV9Q" && strings.Contains(DX, sMess.DX) { if sMess.DX != "" {
sendToGotify("Spot", sMess, 5) log.Printf("Sanitized message: Reporter: %s, DX: %s, Freq: %s, Mode: %s, Report: %s, Time: %s", sMess.From, sMess.DX, sMess.Freq, sMess.Mode, 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, "F4BPO-2")
login = true
}
if message != "" && strings.Contains("login:", message) {
go sendTelnetMessage(conn, "F4BPO-2")
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 == "F4BPO" && strings.Contains(DX, sMess.DX) {
sendToGotify("Spot", sMess, 5, *cfg)
}
} }
log.Println("Disconnected from Telnet server, reconnecting...") log.Println("Disconnected from Telnet server, reconnecting...")

BIN
rsrc_windows_386.syso Normal file

Binary file not shown.

BIN
rsrc_windows_amd64.syso Normal file

Binary file not shown.