|
|
@@ -0,0 +1,311 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "flag"
|
|
|
+ "fmt"
|
|
|
+ "io/fs"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "runtime"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/nxadm/tail"
|
|
|
+)
|
|
|
+
|
|
|
+// findPath Find the path in which the log files are stored.
|
|
|
+func findPath(LogLocation string) (logPath string) {
|
|
|
+
|
|
|
+ if LogLocation != "" {
|
|
|
+ logPath = LogLocation
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var err error
|
|
|
+ var homedir string
|
|
|
+ var Path string
|
|
|
+ var Paths []string
|
|
|
+ var PathsLinux []string
|
|
|
+ var PathsMac []string
|
|
|
+ var PathsWindows []string
|
|
|
+
|
|
|
+ // Determine the homedir of the user.
|
|
|
+ homedir, err = os.UserHomeDir()
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Define Paths to check out
|
|
|
+ PathsLinux = append(PathsLinux, "/Documents/EVE/logs/")
|
|
|
+ PathsLinux = append(PathsLinux, "/.local/share/Steam/steamapps/compatdata/8500/pfx/drive_c/users/steamuser/My Documents/EVE/logs/Chatlogs/")
|
|
|
+
|
|
|
+ PathsMac = append(PathsMac, "/Documents/EVE/logs/")
|
|
|
+ PathsMac = append(PathsMac, "/Library/Application Support/EVE Online/p_drive/User/My Documents/EVE/")
|
|
|
+
|
|
|
+ PathsWindows = append(PathsWindows, "C:\\Users\\YourUserName\\Documents\\EVE\\logs")
|
|
|
+ PathsWindows = append(PathsWindows, "MyDocuments -> EVE -> Logs")
|
|
|
+
|
|
|
+ switch runtime.GOOS {
|
|
|
+ case "linux":
|
|
|
+ Paths = PathsLinux
|
|
|
+ case "darwin":
|
|
|
+ Paths = PathsMac
|
|
|
+ case "windows":
|
|
|
+ Paths = PathsWindows
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, Path = range Paths {
|
|
|
+ if _, err := os.Stat(homedir + Path); !os.IsNotExist(err) {
|
|
|
+ logPath = homedir + Path
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// OrchestrateIntelMonitoring routine to periodically check the logpath for the intel channels.
|
|
|
+func OrchestrateIntelMonitoring(logPath string, intelChannel string, LogSystemsRaw string) {
|
|
|
+
|
|
|
+ // Declare variables
|
|
|
+ var files []fs.FileInfo
|
|
|
+ var lastName string
|
|
|
+ var tempName string
|
|
|
+
|
|
|
+ // Enter a for-loop that periodically checks.
|
|
|
+ for {
|
|
|
+ files = fetchFileListing(logPath)
|
|
|
+ tempName = findLatestLog(logPath, intelChannel, files)
|
|
|
+
|
|
|
+ if !strings.EqualFold(lastName, tempName) {
|
|
|
+ lastName = tempName
|
|
|
+ fmt.Println("Tracking file:" + lastName)
|
|
|
+ go TrackLogfile(lastName, LogSystemsRaw)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Sleep a minute before we check again.
|
|
|
+ time.Sleep(time.Second * 60)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// TrackLogfile The actual work
|
|
|
+func TrackLogfile(logPath string, LogSystemsRaw string) {
|
|
|
+ // Split the CSV string into different parts
|
|
|
+ LogSystems := strings.Split(LogSystemsRaw, ",")
|
|
|
+
|
|
|
+ // Do the main loop and start parsing the logfiles
|
|
|
+ t, err := tail.TailFile(logPath, tail.Config{Follow: true})
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ for line := range t.Lines {
|
|
|
+ // Cast a line to a text, trim the trash
|
|
|
+ logLine := string(line.Text)
|
|
|
+ logLine = strings.Trim(logLine, "\n")
|
|
|
+
|
|
|
+ // Figure out if this is a message we want to deal with
|
|
|
+ // > is critical in this regard because it is the start of the message
|
|
|
+ // and everything behind it until it hits ] is the username
|
|
|
+ splitpos := strings.Index(logLine, ">")
|
|
|
+ if -1 == splitpos {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // Somehow I cannot filter out this stuff.
|
|
|
+ // EVE System > Channel MOTD
|
|
|
+ LineDate, LineTime, LineUser, Payload := ParseChat(logLine)
|
|
|
+
|
|
|
+ if CompareElements(LogSystems, Payload) {
|
|
|
+ playBeep()
|
|
|
+ fmt.Println("[", LineDate, LineTime, "]", LineUser, ">", Payload)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // We should never reach this
|
|
|
+}
|
|
|
+
|
|
|
+// fetchFileListing As it says on the tin.
|
|
|
+func fetchFileListing(logPath string) (files []fs.FileInfo) {
|
|
|
+
|
|
|
+ // Declare Variables
|
|
|
+ var err error
|
|
|
+ var folder *os.File
|
|
|
+
|
|
|
+ // Open the folder we need to open.
|
|
|
+ folder, err = os.Open(logPath)
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Read the files UwU
|
|
|
+ files, err = folder.Readdir(-1)
|
|
|
+ folder.Close()
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Off you go! \o/
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// findLatestLog Find the last log-file based on the Internet
|
|
|
+func findLatestLog(logPath string, intelChannel string, files []fs.FileInfo) (lastName string) {
|
|
|
+
|
|
|
+ // Declare Variables
|
|
|
+ var lastTime time.Time
|
|
|
+ var tempTime time.Time
|
|
|
+
|
|
|
+ for _, file := range files {
|
|
|
+ file, err := os.Stat(logPath + file.Name())
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if strings.EqualFold(intelChannel, file.Name()[:len(intelChannel)]) {
|
|
|
+ tempTime = file.ModTime()
|
|
|
+ if tempTime.After(lastTime) {
|
|
|
+ lastTime = tempTime
|
|
|
+ lastName = logPath + file.Name()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // And there you are (or not)
|
|
|
+ return
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// StartIntelMonitoring -- The Main Loop!
|
|
|
+func StartIntelMonitoring(intelChannels string, LogSystemsRaw string, LogLocation string) {
|
|
|
+
|
|
|
+ // Declare variables
|
|
|
+ var logPath string
|
|
|
+ var IntelChannelsSplit []string
|
|
|
+ var IntelChannel string
|
|
|
+
|
|
|
+ // Split the CSV string into different parts
|
|
|
+ IntelChannelsSplit = strings.Split(intelChannels, ",")
|
|
|
+
|
|
|
+ // Get the path we need to
|
|
|
+ logPath = findPath(LogLocation)
|
|
|
+
|
|
|
+ for _, IntelChannel = range IntelChannelsSplit {
|
|
|
+ IntelChannel = strings.ReplaceAll(IntelChannel, " ", "_")
|
|
|
+ go OrchestrateIntelMonitoring(logPath, IntelChannel, LogSystemsRaw)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// ParseChat Parses a string, returns the payload of the chat
|
|
|
+// *sigh*
|
|
|
+// This took effing forever to get right.
|
|
|
+// "Lets start with Regex, cannot go wrong".
|
|
|
+// You have this string:
|
|
|
+// ��[ 2022.02.09 10:05:43 ] Kaysee Guru > EX-GBT clr nd
|
|
|
+// Cool. Looks easy.
|
|
|
+// .*\[ (.*?) (.*?) \] (.*?) > (.*)
|
|
|
+// regex101.com says its good
|
|
|
+// And no matter what I do just does not work for some magical reason.
|
|
|
+//
|
|
|
+// Lets do this with splitting by space and compare it with that. Do a bunch of ifs, it works in test aaaaaand....
|
|
|
+// It doesn't work.
|
|
|
+//
|
|
|
+// By now I have a somewhat reliable version, but it fails to look for the word MOTD.
|
|
|
+// I should prolly look into runes()
|
|
|
+// This is terrible
|
|
|
+func ParseChat(chatline string) (LineDate string, LineTime string, LineUser string, Payload []string) {
|
|
|
+ logLine := chatline[3:]
|
|
|
+
|
|
|
+ var LinePayload string
|
|
|
+ var splitpos int
|
|
|
+
|
|
|
+ splitpos = strings.Index(logLine, ">")
|
|
|
+ LineDate = logLine[2:23]
|
|
|
+ LineTime = logLine[25:41]
|
|
|
+ LineUser = logLine[46 : splitpos-2]
|
|
|
+ LinePayload = logLine[splitpos+4:]
|
|
|
+ LinePayload = shorten(LinePayload)
|
|
|
+ Payload = strings.Fields(LinePayload)
|
|
|
+
|
|
|
+ //fmt.Println(logLine)
|
|
|
+ //fmt.Println(LineDate)
|
|
|
+ //fmt.Println(LineTime)
|
|
|
+ //fmt.Println(LineUser)
|
|
|
+ //fmt.Println(splitpos)
|
|
|
+ //fmt.Println(LinePayload)
|
|
|
+ //fmt.Println(Payload)
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func shorten(payload string) (logLine string) {
|
|
|
+ runes := []rune(payload)
|
|
|
+ for pos, char := range runes {
|
|
|
+ if pos%2 == 0 && char != 13 {
|
|
|
+ logLine = logLine + string(char)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// CompareElements Compares a log msg with systems we wish to track
|
|
|
+func CompareElements(alerts []string, payload []string) bool {
|
|
|
+
|
|
|
+ for _, word := range payload {
|
|
|
+ for _, alert := range alerts {
|
|
|
+
|
|
|
+ if strings.EqualFold(word, alert) {
|
|
|
+ //fmt.Println("Found (EF): ", alert, "in", word)
|
|
|
+ return true
|
|
|
+ } //else {
|
|
|
+ // fmt.Println(word, "!=", alert)
|
|
|
+ //}
|
|
|
+
|
|
|
+ // In the odd case somebody links "C-V6DQ*"
|
|
|
+ if len(word) > 6 {
|
|
|
+ //alert = alert[:len(word)]
|
|
|
+ word = word[:6]
|
|
|
+ }
|
|
|
+
|
|
|
+ // In case of partials like "C-V"
|
|
|
+ if len(word) < 6 && len(word) >= 3 {
|
|
|
+ //word = word[:5]
|
|
|
+ alert = alert[:len(word)]
|
|
|
+ }
|
|
|
+
|
|
|
+ if strings.EqualFold(word, alert) {
|
|
|
+ //fmt.Println("Found: ", alert, "in", word)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+// playBeep Play a beep
|
|
|
+// Honestly, I tried to play alert.mp3, but after two executions I got a segfault and thought 0x07 it is!
|
|
|
+func playBeep() {
|
|
|
+ fmt.Print("\a")
|
|
|
+}
|
|
|
+
|
|
|
+// main Something Importang. Dunno
|
|
|
+func main() {
|
|
|
+
|
|
|
+ // Handle Parameters
|
|
|
+ var intelChannels = flag.String("i", "Etherium Intel,Bean-Intel", "Comma-separated list of intel channels.")
|
|
|
+ LogSystemsRaw := flag.String("s", "c-v6dq,1pf-bc,ex-gbt,z-fet0", "Comma-seperated list of systems to monitor for")
|
|
|
+ LogLocation := flag.String("l", "", "Location of the log-file to track.")
|
|
|
+ flag.Parse()
|
|
|
+
|
|
|
+ // Start the main loop
|
|
|
+ go StartIntelMonitoring(*intelChannels, *LogSystemsRaw, *LogLocation)
|
|
|
+
|
|
|
+ // Go into the eternal loop and sit this one out.
|
|
|
+ // But bug every hour, reminding them that you are still there.
|
|
|
+ for {
|
|
|
+ time.Sleep(time.Second * 3600)
|
|
|
+ fmt.Println("Hi. This is your hourly reminder that this program is still runnig and hasn't crashed!")
|
|
|
+ }
|
|
|
+
|
|
|
+}
|