Compare commits
19 Commits
bf8a95bf21
...
main
Author | SHA1 | Date | |
---|---|---|---|
115051d463 | |||
9a03f3d4c6 | |||
bd1b8da2f7 | |||
a394767832 | |||
f424633083 | |||
a39dadce1a | |||
2d392d8033 | |||
b680af776e | |||
6bb76d73a1 | |||
80a882c411 | |||
acde3434dd | |||
45a73d45b9 | |||
5bac8fd4d2 | |||
9e72764c2a | |||
6d8d69deeb | |||
0892078851 | |||
28e606c414 | |||
54e087b2e1 | |||
8d5d5ca34a |
BIN
PushDXCluster.exe
Normal file
BIN
PushDXCluster.exe
Normal file
Binary file not shown.
51
config.go
Normal file
51
config.go
Normal 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
|
||||||
|
}
|
@ -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
2
dx.txt
@ -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
2
go.mod
@ -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
4
go.sum
Normal 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=
|
146
main.go
146
main.go
@ -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)
|
||||||
|
|
||||||
|
if sMess.DX != "" {
|
||||||
|
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)
|
log.Printf("Received message: %s", message)
|
||||||
if sMess.DX != "" && sMess.From == "XV9Q" && strings.Contains(DX, sMess.DX) {
|
|
||||||
sendToGotify("Spot", sMess, 5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
BIN
rsrc_windows_386.syso
Normal file
Binary file not shown.
BIN
rsrc_windows_amd64.syso
Normal file
BIN
rsrc_windows_amd64.syso
Normal file
Binary file not shown.
Reference in New Issue
Block a user