journal.go (4060B)
1 package journal 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "log" 8 "os" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/MTRNord/edsm_uploader/datatypes" 14 "github.com/MTRNord/edsm_uploader/edsm" 15 "github.com/pkg/errors" 16 17 _ "github.com/mattn/go-sqlite3" 18 ) 19 20 type FileHeader struct { 21 // Define the fields of the FileHeader struct here. 22 } 23 24 type Journal struct { 25 edsm *edsm.EDSM 26 LastDate *time.Time 27 fileHeader *datatypes.FileHeader 28 logger *log.Logger 29 } 30 31 func NewJournal(edsm *edsm.EDSM, logger *log.Logger) *Journal { 32 // Read lastDate from latest.txt 33 file, err := os.OpenFile("latest.txt", os.O_RDWR|os.O_CREATE, 0755) 34 if err != nil { 35 logger.Printf("Error opening latest.txt: %s", err) 36 } 37 defer file.Close() 38 scanner := bufio.NewScanner(file) 39 var lastDate *time.Time 40 if scanner.Scan() { 41 text := scanner.Text() 42 // Check if the latest.txt is empty. 43 // If it is empty then we need to set the lastDate to nil. 44 if text == "" { 45 lastDate = nil 46 } else { 47 lastDateL, err := time.Parse(time.RFC3339, text) 48 if err != nil { 49 logger.Printf("Error parsing latest.txt: %s", err) 50 } 51 lastDate = &lastDateL 52 } 53 } 54 55 return &Journal{ 56 // TODO: Parse the last date from the sqlite database. 57 // If there is no last date, then we need to start from the beginning and define it as nil. 58 edsm: edsm, 59 LastDate: lastDate, 60 fileHeader: nil, 61 logger: logger, 62 } 63 } 64 65 func (j *Journal) ParseJournal(journalPath string) error { 66 splitPath := strings.Split(journalPath, "/") 67 j.logger.Printf("Parsing journal file: %s", splitPath[len(splitPath)-1]) 68 file, err := os.Open(journalPath) 69 if err != nil { 70 return errors.WithStack(err) 71 } 72 defer file.Close() 73 74 scanner := bufio.NewScanner(file) 75 // Parse first line first 76 if scanner.Scan() { 77 err := j.parseLine(scanner.Text()) 78 if err != nil { 79 return errors.WithStack(err) 80 } 81 } 82 var wg sync.WaitGroup 83 for scanner.Scan() { 84 wg.Add(1) 85 go func(line string) error { 86 defer wg.Done() 87 err := j.parseLine(line) 88 if err != nil { 89 return errors.WithStack(err) 90 } 91 return nil 92 }(scanner.Text()) 93 time.Sleep(1 * time.Millisecond) 94 } 95 if err := scanner.Err(); err != nil { 96 return errors.WithStack(err) 97 } 98 wg.Wait() 99 100 return nil 101 } 102 103 func (j *Journal) storeLastDate(timestamp time.Time) { 104 if j.LastDate == nil || j.LastDate.Before(timestamp) { 105 j.LastDate = ×tamp 106 file, err := os.OpenFile("latest.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) 107 if err != nil { 108 j.logger.Printf("Error opening latest.txt: %s", err) 109 } 110 defer file.Close() 111 _, err = file.WriteString(timestamp.Format(time.RFC3339)) 112 if err != nil { 113 j.logger.Printf("Error writing to latest.txt: %s", err) 114 } 115 } 116 } 117 118 // This parses the journal line of the elite dangerous journal. 119 // TODO: We need to store where we are in the journal so we can pick up where we left off. 120 func (j *Journal) parseLine(line string) error { 121 // Parse json line as a JournalLine. 122 var journalLine datatypes.JournalLine 123 bytesString := []byte(line) 124 bytesString = bytes.Trim(bytesString, "\x00") 125 // Exit early if the line is empty 126 if len(bytesString) == 0 { 127 return nil 128 } 129 err := json.Unmarshal(bytesString, &journalLine) 130 if err != nil { 131 return errors.WithStack(err) 132 } 133 134 if journalLine.Event == "Fileheader" { 135 // Parse the json line as a FileHeader. 136 var fileHeader datatypes.FileHeader 137 json.Unmarshal([]byte(line), &fileHeader) 138 139 // Store the file header. 140 j.fileHeader = &fileHeader 141 } 142 143 // If we have a startdate make sure new lines are newer than the startdate. 144 parsedDate, err := time.Parse(time.RFC3339, journalLine.Timestamp) 145 if err != nil { 146 return errors.WithStack(err) 147 } 148 if j.LastDate != nil { 149 lastDate, err := time.Parse(time.RFC3339, j.LastDate.Format(time.RFC3339)) 150 if err != nil { 151 return errors.WithStack(err) 152 } 153 if parsedDate.Before(lastDate) { 154 return nil 155 } 156 } 157 158 // Send the line to edsm. 159 err = j.edsm.SendJournalLine(j.fileHeader, line) 160 if err != nil { 161 return errors.WithStack(err) 162 } 163 164 // Store the last date. 165 j.storeLastDate(parsedDate) 166 167 return nil 168 }