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!") } }