diff options
| author | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2021-07-28 18:21:52 -0700 |
|---|---|---|
| committer | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2021-07-28 14:21:52 -0700 |
| commit | 22c599abc18895f73e96095f35b71cf3357d41b4 (patch) | |
| tree | 482ef57d386c955692ea43c43e4655b3c3763499 /hotline/news.go | |
| parent | 71c56068adca18f76ebee86355f000a3e51d3127 (diff) | |
Move code to hotline dir
Diffstat (limited to 'hotline/news.go')
| -rw-r--r-- | hotline/news.go | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/hotline/news.go b/hotline/news.go new file mode 100644 index 0000000..a9e5d72 --- /dev/null +++ b/hotline/news.go @@ -0,0 +1,274 @@ +package hotline + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "log" + "sort" + "time" +) + +type ThreadedNews struct { + Categories map[string]NewsCategoryListData15 `yaml:"Categories"` +} + +type NewsCategoryListData15 struct { + Type []byte `yaml:"Type"` //Size 2 ; Bundle (2) or category (3) + Name string `yaml:"Name"` // + Articles map[uint32]*NewsArtData `yaml:"Articles"` // Optional, if Type is Category + SubCats map[string]NewsCategoryListData15 `yaml:"SubCats"` + Count []byte // Article or SubCategory count Size 2 + AddSN []byte // Size 4 + DeleteSN []byte // Size 4 + GUID []byte // Size 16 +} + +func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData { + var newsArts []NewsArtList + var newsArtsPayload []byte + + for i, art := range newscat.Articles { + ID := make([]byte, 4) + binary.BigEndian.PutUint32(ID, i) + + newArt := NewsArtList{ + ID: ID, + TimeStamp: art.Date, + ParentID: art.ParentArt, + Flags: []byte{0, 0, 0, 0}, + FlavorCount: []byte{0, 0}, + Title: []byte(art.Title), + Poster: []byte(art.Poster), + ArticleSize: art.DataSize(), + } + newsArts = append(newsArts, newArt) + } + + sort.Sort(byID(newsArts)) + + for _, v := range newsArts { + newsArtsPayload = append(newsArtsPayload, v.Payload()...) + } + + nald := NewsArtListData{ + ID: []byte{0, 0, 0, 0}, + Name: []byte{}, + Description: []byte{}, + NewsArtList: newsArtsPayload, + } + + return nald +} + +// NewsArtData repsents a single news article +type NewsArtData struct { + Title string `yaml:"Title"` + Poster string `yaml:"Poster"` + Date []byte `yaml:"Date"` //size 8 + PrevArt []byte `yaml:"PrevArt"` //size 4 + NextArt []byte `yaml:"NextArt"` //size 4 + ParentArt []byte `yaml:"ParentArt"` //size 4 + FirstChildArt []byte `yaml:"FirstChildArtArt"` //size 4 + DataFlav []byte `yaml:"DataFlav"` // "text/plain" + Data string `yaml:"Data"` +} + +func (art *NewsArtData) DataSize() []byte { + dataLen := make([]byte, 2) + binary.BigEndian.PutUint16(dataLen, uint16(len(art.Data))) + + return dataLen +} + +type NewsArtListData struct { + ID []byte `yaml:"ID"` // Size 4 + Name []byte `yaml:"Name"` + Description []byte `yaml:"Description"` // not used? + NewsArtList []byte // List of articles Optional (if article count > 0) +} + +func (nald *NewsArtListData) Payload() []byte { + count := make([]byte, 4) + binary.BigEndian.PutUint32(count, uint32(len(nald.NewsArtList))) + + out := append(nald.ID, count...) + out = append(out, []byte{uint8(len(nald.Name))}...) + out = append(out, nald.Name...) + out = append(out, []byte{uint8(len(nald.Description))}...) + out = append(out, nald.Description...) + out = append(out, nald.NewsArtList...) + + return out +} + +// NewsArtList is a summarized ver sion of a NewArtData record for display in list view +type NewsArtList struct { + ID []byte // Size 4 + TimeStamp []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes) + ParentID []byte // Size 4 + Flags []byte // Size 4 + FlavorCount []byte // Size 2 + // Title size 1 + Title []byte // string + // Poster size 1 + // Poster Poster string + Poster []byte + FlavorList []NewsFlavorList + // Flavor list… Optional (if flavor count > 0) + ArticleSize []byte // Size 2 +} + +type byID []NewsArtList + +func (s byID) Len() int { + return len(s) +} +func (s byID) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s byID) Less(i, j int) bool { + return binary.BigEndian.Uint32(s[i].ID) < binary.BigEndian.Uint32(s[j].ID) +} + +func (nal *NewsArtList) Payload() []byte { + out := append(nal.ID, nal.TimeStamp...) + out = append(out, nal.ParentID...) + out = append(out, nal.Flags...) + + out = append(out, []byte{0, 1}...) + + out = append(out, []byte{uint8(len(nal.Title))}...) + out = append(out, nal.Title...) + out = append(out, []byte{uint8(len(nal.Poster))}...) + out = append(out, nal.Poster...) + out = append(out, []byte{0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e}...) // TODO: wat? + out = append(out, nal.ArticleSize...) + + return out +} + +type NewsFlavorList struct { + // Flavor size 1 + // Flavor text size MIME type string + // Article size 2 +} + +func (newscat *NewsCategoryListData15) Payload() []byte { + count := make([]byte, 2) + binary.BigEndian.PutUint16(count, uint16(len(newscat.Articles)+len(newscat.SubCats))) + + out := append(newscat.Type, count...) + + if bytes.Equal(newscat.Type, []byte{0, 3}) { + // Generate a random GUID + b := make([]byte, 16) + _, err := rand.Read(b) + if err != nil { + log.Fatal(err) + } + + out = append(out, b...) // GUID + out = append(out, []byte{0, 0, 0, 1}...) // Add SN (TODO: not sure what this is) + out = append(out, []byte{0, 0, 0, 2}...) // Delete SN (TODO: not sure what this is) + } + + out = append(out, newscat.nameLen()...) + out = append(out, []byte(newscat.Name)...) + + return out +} + +// ReadNewsCategoryListData parses a byte slice into a NewsCategoryListData15 struct +// For use on the client side +func ReadNewsCategoryListData(payload []byte) NewsCategoryListData15 { + ncld := NewsCategoryListData15{ + Type: payload[0:2], + Count: payload[2:4], + } + + if bytes.Equal(ncld.Type, []byte{0, 3}) { + ncld.GUID = payload[4:20] + ncld.AddSN = payload[20:24] + ncld.AddSN = payload[24:28] + ncld.Name = string(payload[29:]) + } else { + ncld.Name = string(payload[5:]) + } + + return ncld +} + +func (newscat *NewsCategoryListData15) nameLen() []byte { + return []byte{uint8(len(newscat.Name))} +} + +type NewsPath struct { + Paths []string +} + +func (np *NewsPath) Payload() []byte { + var out []byte + + count := make([]byte, 2) + binary.BigEndian.PutUint16(count, uint16(len(np.Paths))) + + out = append(out, count...) + for _, p := range np.Paths { + pLen := byte(len(p)) + out = append(out, []byte{0, 0}...) // what is this? + out = append(out, pLen) + out = append(out, []byte(p)...) + } + + return out +} + +func ReadNewsPath(newsPath []byte) []string { + if len(newsPath) == 0 { + return []string{} + } + pathCount := binary.BigEndian.Uint16(newsPath[0:2]) + + pathData := newsPath[2:] + var paths []string + + for i := uint16(0); i < pathCount; i++ { + pathLen := pathData[2] + paths = append(paths, string(pathData[3:3+pathLen])) + + pathData = pathData[pathLen+3:] + } + + return paths +} + +func (s *Server) GetNewsCatByPath(paths []string) map[string]NewsCategoryListData15 { + cats := s.ThreadedNews.Categories + for _, path := range paths { + cats = cats[path].SubCats + } + return cats +} + +// News article date field contains this structure: +// Year 2 +// Milliseconds 2 (seriously?) +// Seconds 4 +func NewsDate() []byte { + t := time.Now() + ms := []byte{0, 0} + seconds := []byte{0, 0, 0, 0} + + year := []byte{0, 0} + binary.BigEndian.PutUint16(year, uint16(t.Year())) + + yearStart := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, time.Local) + + binary.BigEndian.PutUint32(seconds, uint32(t.Sub(yearStart).Seconds())) + + date := append(year, ms...) + date = append(date, seconds...) + + return date +} |