2023-03-14 22:24:26 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2023-03-15 21:29:26 +00:00
|
|
|
"fmt"
|
2023-03-14 22:24:26 +00:00
|
|
|
"net/http"
|
2023-03-15 21:29:26 +00:00
|
|
|
"os"
|
2023-03-14 22:24:26 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gocolly/colly/v2"
|
|
|
|
)
|
2023-03-15 16:16:40 +00:00
|
|
|
|
2023-03-14 22:24:26 +00:00
|
|
|
/* Store the calls locally */
|
|
|
|
var (
|
|
|
|
calls []Call
|
2023-03-15 16:16:40 +00:00
|
|
|
mu sync.Mutex
|
2023-03-14 22:24:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Call struct {
|
|
|
|
Num string `json:"num"`
|
|
|
|
Date string `json:"date"`
|
|
|
|
Call string `json:"call"`
|
|
|
|
Slot string `json:"slot"`
|
|
|
|
Talkgroup string `json:"talkgroup"`
|
|
|
|
}
|
|
|
|
|
2023-03-15 21:29:26 +00:00
|
|
|
type Config struct {
|
|
|
|
Page string `json:"page"`
|
|
|
|
Reload int64 `json:"reload"`
|
|
|
|
}
|
|
|
|
|
2023-03-15 16:16:40 +00:00
|
|
|
func req(w http.ResponseWriter, r *http.Request) {
|
2023-03-14 22:24:26 +00:00
|
|
|
w.Header().Add("Content-Type", "application/json")
|
2023-03-15 16:16:40 +00:00
|
|
|
|
|
|
|
mu.Lock()
|
2023-03-14 22:24:26 +00:00
|
|
|
json.NewEncoder(w).Encode(calls)
|
2023-03-15 16:16:40 +00:00
|
|
|
mu.Unlock()
|
2023-03-14 22:24:26 +00:00
|
|
|
}
|
|
|
|
|
2023-03-15 16:16:40 +00:00
|
|
|
func serv() {
|
2023-03-14 22:24:26 +00:00
|
|
|
srv := &http.Server{
|
2023-03-15 16:16:40 +00:00
|
|
|
Addr: ":8181",
|
2023-03-14 22:24:26 +00:00
|
|
|
}
|
|
|
|
http.HandleFunc("/monitor", req)
|
|
|
|
srv.ListenAndServe()
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Scrape the dashboard */
|
2023-03-15 21:29:26 +00:00
|
|
|
func scrape(config *Config, callback chan []Call) {
|
2023-03-14 22:24:26 +00:00
|
|
|
var new_calls []Call
|
|
|
|
c := colly.NewCollector()
|
|
|
|
c.OnHTML("table > tbody", func(h *colly.HTMLElement) {
|
|
|
|
h.ForEach("tr", func(_ int, el *colly.HTMLElement) {
|
2023-03-15 16:16:40 +00:00
|
|
|
if el.ChildText("td:nth-child(1)") != "" {
|
2023-03-14 22:24:26 +00:00
|
|
|
this_call := Call{Num: el.ChildText("td:nth-child(1)"), Date: el.ChildText("td:nth-child(3)"), Call: el.ChildText("td:nth-child(8)"), Slot: el.ChildText("td:nth-child(10)"), Talkgroup: el.ChildText("td:nth-child(11)")}
|
|
|
|
new_calls = append(new_calls, this_call)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-03-15 21:29:26 +00:00
|
|
|
c.Visit(config.Page)
|
2023-03-14 22:24:26 +00:00
|
|
|
callback <- new_calls
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2023-03-15 21:29:26 +00:00
|
|
|
cFile, err := os.ReadFile("./dvsmon.conf")
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Can't open config file! Expecting .dvsmon.conf: ", err)
|
|
|
|
os.Exit(-1)
|
|
|
|
}
|
|
|
|
|
|
|
|
var config Config
|
|
|
|
if err := json.Unmarshal(cFile, &config); err != nil {
|
|
|
|
fmt.Println("Trouble parsing config file: ", err)
|
|
|
|
}
|
|
|
|
|
2023-03-14 22:24:26 +00:00
|
|
|
callback := make(chan []Call)
|
|
|
|
last_update := time.Now()
|
|
|
|
|
2023-03-15 21:29:26 +00:00
|
|
|
/* Serve the API service */
|
2023-03-14 22:24:26 +00:00
|
|
|
go serv()
|
|
|
|
|
|
|
|
for {
|
2023-03-15 21:29:26 +00:00
|
|
|
if time.Since(last_update) > time.Second*time.Duration(config.Reload) {
|
2023-03-14 22:24:26 +00:00
|
|
|
last_update = time.Now()
|
2023-03-15 21:29:26 +00:00
|
|
|
go scrape(&config, callback)
|
2023-03-15 16:16:40 +00:00
|
|
|
mu.Lock()
|
2023-03-14 22:24:26 +00:00
|
|
|
calls = <-callback
|
|
|
|
mu.Unlock()
|
|
|
|
}
|
2023-03-15 16:16:40 +00:00
|
|
|
time.Sleep(time.Millisecond * 256)
|
2023-03-14 22:24:26 +00:00
|
|
|
}
|
|
|
|
}
|