main.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "io/fs"
  6. "log"
  7. "os"
  8. "runtime"
  9. "strings"
  10. "time"
  11. "github.com/nxadm/tail"
  12. )
  13. // findPath Find the path in which the log files are stored.
  14. func findPath(LogLocation string) (logPath string) {
  15. if LogLocation != "" {
  16. logPath = LogLocation
  17. return
  18. }
  19. var err error
  20. var homedir string
  21. var Path string
  22. var Paths []string
  23. var PathsLinux []string
  24. var PathsMac []string
  25. var PathsWindows []string
  26. // Determine the homedir of the user.
  27. homedir, err = os.UserHomeDir()
  28. if err != nil {
  29. log.Fatal(err)
  30. }
  31. // Define Paths to check out
  32. PathsLinux = append(PathsLinux, "/Documents/EVE/logs/")
  33. PathsLinux = append(PathsLinux, "/.local/share/Steam/steamapps/compatdata/8500/pfx/drive_c/users/steamuser/My Documents/EVE/logs/Chatlogs/")
  34. PathsMac = append(PathsMac, "/Documents/EVE/logs/")
  35. PathsMac = append(PathsMac, "/Library/Application Support/EVE Online/p_drive/User/My Documents/EVE/")
  36. PathsWindows = append(PathsWindows, "C:\\Users\\YourUserName\\Documents\\EVE\\logs")
  37. PathsWindows = append(PathsWindows, "MyDocuments -> EVE -> Logs")
  38. switch runtime.GOOS {
  39. case "linux":
  40. Paths = PathsLinux
  41. case "darwin":
  42. Paths = PathsMac
  43. case "windows":
  44. Paths = PathsWindows
  45. }
  46. for _, Path = range Paths {
  47. if _, err := os.Stat(homedir + Path); !os.IsNotExist(err) {
  48. logPath = homedir + Path
  49. }
  50. }
  51. return
  52. }
  53. // OrchestrateIntelMonitoring routine to periodically check the logpath for the intel channels.
  54. func OrchestrateIntelMonitoring(logPath string, intelChannel string, LogSystemsRaw string) {
  55. // Declare variables
  56. var files []fs.FileInfo
  57. var lastName string
  58. var tempName string
  59. // Enter a for-loop that periodically checks.
  60. for {
  61. files = fetchFileListing(logPath)
  62. tempName = findLatestLog(logPath, intelChannel, files)
  63. if !strings.EqualFold(lastName, tempName) {
  64. lastName = tempName
  65. fmt.Println("Tracking file:" + lastName)
  66. go TrackLogfile(lastName, LogSystemsRaw)
  67. }
  68. // Sleep a minute before we check again.
  69. time.Sleep(time.Second * 60)
  70. }
  71. }
  72. // TrackLogfile The actual work
  73. func TrackLogfile(logPath string, LogSystemsRaw string) {
  74. // Split the CSV string into different parts
  75. LogSystems := strings.Split(LogSystemsRaw, ",")
  76. // Do the main loop and start parsing the logfiles
  77. t, err := tail.TailFile(logPath, tail.Config{Follow: true})
  78. if err != nil {
  79. panic(err)
  80. }
  81. for line := range t.Lines {
  82. // Cast a line to a text, trim the trash
  83. logLine := string(line.Text)
  84. logLine = strings.Trim(logLine, "\n")
  85. // Figure out if this is a message we want to deal with
  86. // > is critical in this regard because it is the start of the message
  87. // and everything behind it until it hits ] is the username
  88. splitpos := strings.Index(logLine, ">")
  89. if -1 == splitpos {
  90. continue
  91. }
  92. // Somehow I cannot filter out this stuff.
  93. // EVE System > Channel MOTD
  94. LineDate, LineTime, LineUser, Payload := ParseChat(logLine)
  95. if CompareElements(LogSystems, Payload) {
  96. playBeep()
  97. fmt.Println("[", LineDate, LineTime, "]", LineUser, ">", Payload)
  98. }
  99. }
  100. // We should never reach this
  101. }
  102. // fetchFileListing As it says on the tin.
  103. func fetchFileListing(logPath string) (files []fs.FileInfo) {
  104. // Declare Variables
  105. var err error
  106. var folder *os.File
  107. // Open the folder we need to open.
  108. folder, err = os.Open(logPath)
  109. if err != nil {
  110. log.Fatal(err)
  111. }
  112. // Read the files UwU
  113. files, err = folder.Readdir(-1)
  114. folder.Close()
  115. if err != nil {
  116. log.Fatal(err)
  117. }
  118. // Off you go! \o/
  119. return
  120. }
  121. // findLatestLog Find the last log-file based on the Internet
  122. func findLatestLog(logPath string, intelChannel string, files []fs.FileInfo) (lastName string) {
  123. // Declare Variables
  124. var lastTime time.Time
  125. var tempTime time.Time
  126. for _, file := range files {
  127. file, err := os.Stat(logPath + file.Name())
  128. if err != nil {
  129. log.Fatal(err)
  130. }
  131. if strings.EqualFold(intelChannel, file.Name()[:len(intelChannel)]) {
  132. tempTime = file.ModTime()
  133. if tempTime.After(lastTime) {
  134. lastTime = tempTime
  135. lastName = logPath + file.Name()
  136. }
  137. }
  138. }
  139. // And there you are (or not)
  140. return
  141. }
  142. // StartIntelMonitoring -- The Main Loop!
  143. func StartIntelMonitoring(intelChannels string, LogSystemsRaw string, LogLocation string) {
  144. // Declare variables
  145. var logPath string
  146. var IntelChannelsSplit []string
  147. var IntelChannel string
  148. // Split the CSV string into different parts
  149. IntelChannelsSplit = strings.Split(intelChannels, ",")
  150. // Get the path we need to
  151. logPath = findPath(LogLocation)
  152. for _, IntelChannel = range IntelChannelsSplit {
  153. IntelChannel = strings.ReplaceAll(IntelChannel, " ", "_")
  154. go OrchestrateIntelMonitoring(logPath, IntelChannel, LogSystemsRaw)
  155. }
  156. }
  157. // ParseChat Parses a string, returns the payload of the chat
  158. // *sigh*
  159. // This took effing forever to get right.
  160. // "Lets start with Regex, cannot go wrong".
  161. // You have this string:
  162. // ��[ 2022.02.09 10:05:43 ] Kaysee Guru > EX-GBT clr nd
  163. // Cool. Looks easy.
  164. // .*\[ (.*?) (.*?) \] (.*?) > (.*)
  165. // regex101.com says its good
  166. // And no matter what I do just does not work for some magical reason.
  167. //
  168. // Lets do this with splitting by space and compare it with that. Do a bunch of ifs, it works in test aaaaaand....
  169. // It doesn't work.
  170. //
  171. // By now I have a somewhat reliable version, but it fails to look for the word MOTD.
  172. // I should prolly look into runes()
  173. // This is terrible
  174. func ParseChat(chatline string) (LineDate string, LineTime string, LineUser string, Payload []string) {
  175. logLine := chatline[3:]
  176. var LinePayload string
  177. var splitpos int
  178. splitpos = strings.Index(logLine, ">")
  179. LineDate = logLine[2:23]
  180. LineTime = logLine[25:41]
  181. LineUser = logLine[46 : splitpos-2]
  182. LinePayload = logLine[splitpos+4:]
  183. LinePayload = shorten(LinePayload)
  184. Payload = strings.Fields(LinePayload)
  185. //fmt.Println(logLine)
  186. //fmt.Println(LineDate)
  187. //fmt.Println(LineTime)
  188. //fmt.Println(LineUser)
  189. //fmt.Println(splitpos)
  190. //fmt.Println(LinePayload)
  191. //fmt.Println(Payload)
  192. return
  193. }
  194. func shorten(payload string) (logLine string) {
  195. runes := []rune(payload)
  196. for pos, char := range runes {
  197. if pos%2 == 0 && char != 13 {
  198. logLine = logLine + string(char)
  199. }
  200. }
  201. return
  202. }
  203. // CompareElements Compares a log msg with systems we wish to track
  204. func CompareElements(alerts []string, payload []string) bool {
  205. for _, word := range payload {
  206. for _, alert := range alerts {
  207. if strings.EqualFold(word, alert) {
  208. //fmt.Println("Found (EF): ", alert, "in", word)
  209. return true
  210. } //else {
  211. // fmt.Println(word, "!=", alert)
  212. //}
  213. // In the odd case somebody links "C-V6DQ*"
  214. if len(word) > 6 {
  215. //alert = alert[:len(word)]
  216. word = word[:6]
  217. }
  218. // In case of partials like "C-V"
  219. if len(word) < 6 && len(word) >= 3 {
  220. //word = word[:5]
  221. alert = alert[:len(word)]
  222. }
  223. if strings.EqualFold(word, alert) {
  224. //fmt.Println("Found: ", alert, "in", word)
  225. return true
  226. }
  227. }
  228. }
  229. return false
  230. }
  231. // playBeep Play a beep
  232. // Honestly, I tried to play alert.mp3, but after two executions I got a segfault and thought 0x07 it is!
  233. func playBeep() {
  234. fmt.Print("\a")
  235. }
  236. // main Something Importang. Dunno
  237. func main() {
  238. // Handle Parameters
  239. var intelChannels = flag.String("i", "Etherium Intel,Bean-Intel", "Comma-separated list of intel channels.")
  240. LogSystemsRaw := flag.String("s", "c-v6dq,1pf-bc,ex-gbt,z-fet0", "Comma-seperated list of systems to monitor for")
  241. LogLocation := flag.String("l", "", "Location of the log-file to track.")
  242. flag.Parse()
  243. // Start the main loop
  244. go StartIntelMonitoring(*intelChannels, *LogSystemsRaw, *LogLocation)
  245. // Go into the eternal loop and sit this one out.
  246. // But bug every hour, reminding them that you are still there.
  247. for {
  248. time.Sleep(time.Second * 3600)
  249. fmt.Println("Hi. This is your hourly reminder that this program is still runnig and hasn't crashed!")
  250. }
  251. }