]> git.r.bdr.sh - rbdr/mobius/blame - hotline/news.go
Replace hardcoded version with ldflag usage
[rbdr/mobius] / hotline / news.go
CommitLineData
6988a057
JH
1package hotline
2
3import (
4 "bytes"
5 "crypto/rand"
6 "encoding/binary"
9cf66aea
JH
7 "io"
8 "slices"
6988a057 9 "sort"
6988a057
JH
10)
11
2d92d26e
JH
12const defaultNewsDateFormat = "Jan02 15:04" // Jun23 20:49
13
14const defaultNewsTemplate = `From %s (%s):
15
16%s
17
18__________________________________________________________`
19
6988a057
JH
20type ThreadedNews struct {
21 Categories map[string]NewsCategoryListData15 `yaml:"Categories"`
22}
23
24type NewsCategoryListData15 struct {
9cf66aea
JH
25 Type [2]byte `yaml:"Type"` // Size 2 ; Bundle (2) or category (3)
26 Count []byte // Article or SubCategory count Size 2
72dd37f1 27 NameSize byte
6988a057
JH
28 Name string `yaml:"Name"` //
29 Articles map[uint32]*NewsArtData `yaml:"Articles"` // Optional, if Type is Category
30 SubCats map[string]NewsCategoryListData15 `yaml:"SubCats"`
72dd37f1 31 GUID []byte // Size 16
6988a057
JH
32 AddSN []byte // Size 4
33 DeleteSN []byte // Size 4
6988a057
JH
34}
35
36func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
37 var newsArts []NewsArtList
38 var newsArtsPayload []byte
39
40 for i, art := range newscat.Articles {
9cf66aea
JH
41 id := make([]byte, 4)
42 binary.BigEndian.PutUint32(id, i)
6988a057
JH
43
44 newArt := NewsArtList{
9cf66aea 45 ID: id,
95159e55
JH
46 TimeStamp: art.Date[:],
47 ParentID: art.ParentArt[:],
6988a057
JH
48 Flags: []byte{0, 0, 0, 0},
49 FlavorCount: []byte{0, 0},
50 Title: []byte(art.Title),
51 Poster: []byte(art.Poster),
52 ArticleSize: art.DataSize(),
53 }
54 newsArts = append(newsArts, newArt)
55 }
56
57 sort.Sort(byID(newsArts))
58
59 for _, v := range newsArts {
95159e55
JH
60 b, err := io.ReadAll(&v)
61 if err != nil {
62 // TODO
63 }
64 newsArtsPayload = append(newsArtsPayload, b...)
6988a057
JH
65 }
66
67 nald := NewsArtListData{
9cf66aea 68 ID: [4]byte{0, 0, 0, 0},
33265393 69 Count: len(newsArts),
6988a057
JH
70 Name: []byte{},
71 Description: []byte{},
72 NewsArtList: newsArtsPayload,
73 }
74
75 return nald
76}
77
72dd37f1 78// NewsArtData represents single news article
6988a057 79type NewsArtData struct {
95159e55
JH
80 Title string `yaml:"Title"`
81 Poster string `yaml:"Poster"`
82 Date [8]byte `yaml:"Date"` // size 8
83 PrevArt [4]byte `yaml:"PrevArt"` // size 4
84 NextArt [4]byte `yaml:"NextArt"` // size 4
85 ParentArt [4]byte `yaml:"ParentArt"` // size 4
86 FirstChildArt [4]byte `yaml:"FirstChildArtArt"` // size 4
87 DataFlav []byte `yaml:"DataFlav"` // "text/plain"
88 Data string `yaml:"Data"`
6988a057
JH
89}
90
91func (art *NewsArtData) DataSize() []byte {
92 dataLen := make([]byte, 2)
93 binary.BigEndian.PutUint16(dataLen, uint16(len(art.Data)))
94
95 return dataLen
96}
97
98type NewsArtListData struct {
9cf66aea
JH
99 ID [4]byte `yaml:"ID"` // Size 4
100 Name []byte `yaml:"Name"`
101 Description []byte `yaml:"Description"` // not used?
102 NewsArtList []byte // List of articles Optional (if article count > 0)
33265393 103 Count int
6988a057
JH
104}
105
9cf66aea 106func (nald *NewsArtListData) Read(p []byte) (int, error) {
6988a057 107 count := make([]byte, 4)
33265393 108 binary.BigEndian.PutUint32(count, uint32(nald.Count))
6988a057 109
9cf66aea
JH
110 return copy(
111 p,
112 slices.Concat(
113 nald.ID[:],
114 count,
115 []byte{uint8(len(nald.Name))},
116 nald.Name,
117 []byte{uint8(len(nald.Description))},
118 nald.Description,
119 nald.NewsArtList,
120 ),
121 ),
122 io.EOF
6988a057
JH
123}
124
72dd37f1 125// NewsArtList is a summarized version of a NewArtData record for display in list view
6988a057
JH
126type NewsArtList struct {
127 ID []byte // Size 4
128 TimeStamp []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
129 ParentID []byte // Size 4
130 Flags []byte // Size 4
131 FlavorCount []byte // Size 2
132 // Title size 1
133 Title []byte // string
134 // Poster size 1
135 // Poster Poster string
136 Poster []byte
137 FlavorList []NewsFlavorList
138 // Flavor list… Optional (if flavor count > 0)
139 ArticleSize []byte // Size 2
95159e55
JH
140
141 readOffset int // Internal offset to track read progress
6988a057
JH
142}
143
144type byID []NewsArtList
145
146func (s byID) Len() int {
147 return len(s)
148}
149func (s byID) Swap(i, j int) {
150 s[i], s[j] = s[j], s[i]
151}
152func (s byID) Less(i, j int) bool {
153 return binary.BigEndian.Uint32(s[i].ID) < binary.BigEndian.Uint32(s[j].ID)
154}
155
95159e55
JH
156func (nal *NewsArtList) Read(p []byte) (int, error) {
157 out := slices.Concat(
158 nal.ID,
159 nal.TimeStamp,
160 nal.ParentID,
161 nal.Flags,
162 []byte{0, 1},
163 []byte{uint8(len(nal.Title))},
164 nal.Title,
165 []byte{uint8(len(nal.Poster))},
166 nal.Poster,
167 []byte{0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e},
168 nal.ArticleSize,
169 )
170
171 if nal.readOffset >= len(out) {
172 return 0, io.EOF // All bytes have been read
173 }
6988a057 174
95159e55
JH
175 n := copy(p, out[nal.readOffset:])
176 nal.readOffset += n
6988a057 177
95159e55 178 return n, io.EOF
6988a057
JH
179}
180
181type NewsFlavorList struct {
182 // Flavor size 1
183 // Flavor text size MIME type string
184 // Article size 2
185}
186
72dd37f1 187func (newscat *NewsCategoryListData15) MarshalBinary() (data []byte, err error) {
6988a057
JH
188 count := make([]byte, 2)
189 binary.BigEndian.PutUint16(count, uint16(len(newscat.Articles)+len(newscat.SubCats)))
190
9cf66aea 191 out := append(newscat.Type[:], count...)
6988a057 192
9cf66aea 193 if bytes.Equal(newscat.Type[:], []byte{0, 3}) {
72dd37f1 194 // Generate a random GUID // TODO: does this need to be random?
6988a057
JH
195 b := make([]byte, 16)
196 _, err := rand.Read(b)
197 if err != nil {
72dd37f1 198 return data, err
6988a057
JH
199 }
200
201 out = append(out, b...) // GUID
202 out = append(out, []byte{0, 0, 0, 1}...) // Add SN (TODO: not sure what this is)
203 out = append(out, []byte{0, 0, 0, 2}...) // Delete SN (TODO: not sure what this is)
204 }
205
206 out = append(out, newscat.nameLen()...)
207 out = append(out, []byte(newscat.Name)...)
208
72dd37f1 209 return out, err
6988a057
JH
210}
211
6988a057
JH
212func (newscat *NewsCategoryListData15) nameLen() []byte {
213 return []byte{uint8(len(newscat.Name))}
214}
215
8eb43f95 216// TODO: re-implement as bufio.Scanner interface
6988a057
JH
217func ReadNewsPath(newsPath []byte) []string {
218 if len(newsPath) == 0 {
219 return []string{}
220 }
221 pathCount := binary.BigEndian.Uint16(newsPath[0:2])
222
223 pathData := newsPath[2:]
224 var paths []string
225
226 for i := uint16(0); i < pathCount; i++ {
227 pathLen := pathData[2]
228 paths = append(paths, string(pathData[3:3+pathLen]))
229
230 pathData = pathData[pathLen+3:]
231 }
232
233 return paths
234}
235
236func (s *Server) GetNewsCatByPath(paths []string) map[string]NewsCategoryListData15 {
237 cats := s.ThreadedNews.Categories
238 for _, path := range paths {
239 cats = cats[path].SubCats
240 }
241 return cats
242}