]> git.r.bdr.sh - rbdr/mobius/blame - hotline/news.go
Add keepalive to Client
[rbdr/mobius] / hotline / news.go
CommitLineData
6988a057
JH
1package hotline
2
3import (
4 "bytes"
5 "crypto/rand"
6 "encoding/binary"
7 "log"
8 "sort"
9 "time"
10)
11
12type ThreadedNews struct {
13 Categories map[string]NewsCategoryListData15 `yaml:"Categories"`
14}
15
16type NewsCategoryListData15 struct {
17 Type []byte `yaml:"Type"` //Size 2 ; Bundle (2) or category (3)
18 Name string `yaml:"Name"` //
19 Articles map[uint32]*NewsArtData `yaml:"Articles"` // Optional, if Type is Category
20 SubCats map[string]NewsCategoryListData15 `yaml:"SubCats"`
21 Count []byte // Article or SubCategory count Size 2
22 AddSN []byte // Size 4
23 DeleteSN []byte // Size 4
24 GUID []byte // Size 16
25}
26
27func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
28 var newsArts []NewsArtList
29 var newsArtsPayload []byte
30
31 for i, art := range newscat.Articles {
32 ID := make([]byte, 4)
33 binary.BigEndian.PutUint32(ID, i)
34
35 newArt := NewsArtList{
36 ID: ID,
37 TimeStamp: art.Date,
38 ParentID: art.ParentArt,
39 Flags: []byte{0, 0, 0, 0},
40 FlavorCount: []byte{0, 0},
41 Title: []byte(art.Title),
42 Poster: []byte(art.Poster),
43 ArticleSize: art.DataSize(),
44 }
45 newsArts = append(newsArts, newArt)
46 }
47
48 sort.Sort(byID(newsArts))
49
50 for _, v := range newsArts {
51 newsArtsPayload = append(newsArtsPayload, v.Payload()...)
52 }
53
54 nald := NewsArtListData{
55 ID: []byte{0, 0, 0, 0},
56 Name: []byte{},
57 Description: []byte{},
58 NewsArtList: newsArtsPayload,
59 }
60
61 return nald
62}
63
64// NewsArtData repsents a single news article
65type NewsArtData struct {
66 Title string `yaml:"Title"`
67 Poster string `yaml:"Poster"`
68 Date []byte `yaml:"Date"` //size 8
69 PrevArt []byte `yaml:"PrevArt"` //size 4
70 NextArt []byte `yaml:"NextArt"` //size 4
71 ParentArt []byte `yaml:"ParentArt"` //size 4
72 FirstChildArt []byte `yaml:"FirstChildArtArt"` //size 4
73 DataFlav []byte `yaml:"DataFlav"` // "text/plain"
74 Data string `yaml:"Data"`
75}
76
77func (art *NewsArtData) DataSize() []byte {
78 dataLen := make([]byte, 2)
79 binary.BigEndian.PutUint16(dataLen, uint16(len(art.Data)))
80
81 return dataLen
82}
83
84type NewsArtListData struct {
85 ID []byte `yaml:"ID"` // Size 4
86 Name []byte `yaml:"Name"`
87 Description []byte `yaml:"Description"` // not used?
88 NewsArtList []byte // List of articles Optional (if article count > 0)
89}
90
91func (nald *NewsArtListData) Payload() []byte {
92 count := make([]byte, 4)
93 binary.BigEndian.PutUint32(count, uint32(len(nald.NewsArtList)))
94
95 out := append(nald.ID, count...)
96 out = append(out, []byte{uint8(len(nald.Name))}...)
97 out = append(out, nald.Name...)
98 out = append(out, []byte{uint8(len(nald.Description))}...)
99 out = append(out, nald.Description...)
100 out = append(out, nald.NewsArtList...)
101
102 return out
103}
104
105// NewsArtList is a summarized ver sion of a NewArtData record for display in list view
106type NewsArtList struct {
107 ID []byte // Size 4
108 TimeStamp []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
109 ParentID []byte // Size 4
110 Flags []byte // Size 4
111 FlavorCount []byte // Size 2
112 // Title size 1
113 Title []byte // string
114 // Poster size 1
115 // Poster Poster string
116 Poster []byte
117 FlavorList []NewsFlavorList
118 // Flavor list… Optional (if flavor count > 0)
119 ArticleSize []byte // Size 2
120}
121
122type byID []NewsArtList
123
124func (s byID) Len() int {
125 return len(s)
126}
127func (s byID) Swap(i, j int) {
128 s[i], s[j] = s[j], s[i]
129}
130func (s byID) Less(i, j int) bool {
131 return binary.BigEndian.Uint32(s[i].ID) < binary.BigEndian.Uint32(s[j].ID)
132}
133
134func (nal *NewsArtList) Payload() []byte {
135 out := append(nal.ID, nal.TimeStamp...)
136 out = append(out, nal.ParentID...)
137 out = append(out, nal.Flags...)
138
139 out = append(out, []byte{0, 1}...)
140
141 out = append(out, []byte{uint8(len(nal.Title))}...)
142 out = append(out, nal.Title...)
143 out = append(out, []byte{uint8(len(nal.Poster))}...)
144 out = append(out, nal.Poster...)
145 out = append(out, []byte{0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e}...) // TODO: wat?
146 out = append(out, nal.ArticleSize...)
147
148 return out
149}
150
151type NewsFlavorList struct {
152 // Flavor size 1
153 // Flavor text size MIME type string
154 // Article size 2
155}
156
157func (newscat *NewsCategoryListData15) Payload() []byte {
158 count := make([]byte, 2)
159 binary.BigEndian.PutUint16(count, uint16(len(newscat.Articles)+len(newscat.SubCats)))
160
161 out := append(newscat.Type, count...)
162
163 if bytes.Equal(newscat.Type, []byte{0, 3}) {
164 // Generate a random GUID
165 b := make([]byte, 16)
166 _, err := rand.Read(b)
167 if err != nil {
168 log.Fatal(err)
169 }
170
171 out = append(out, b...) // GUID
172 out = append(out, []byte{0, 0, 0, 1}...) // Add SN (TODO: not sure what this is)
173 out = append(out, []byte{0, 0, 0, 2}...) // Delete SN (TODO: not sure what this is)
174 }
175
176 out = append(out, newscat.nameLen()...)
177 out = append(out, []byte(newscat.Name)...)
178
179 return out
180}
181
182// ReadNewsCategoryListData parses a byte slice into a NewsCategoryListData15 struct
183// For use on the client side
184func ReadNewsCategoryListData(payload []byte) NewsCategoryListData15 {
185 ncld := NewsCategoryListData15{
186 Type: payload[0:2],
187 Count: payload[2:4],
188 }
189
190 if bytes.Equal(ncld.Type, []byte{0, 3}) {
191 ncld.GUID = payload[4:20]
192 ncld.AddSN = payload[20:24]
193 ncld.AddSN = payload[24:28]
194 ncld.Name = string(payload[29:])
195 } else {
196 ncld.Name = string(payload[5:])
197 }
198
199 return ncld
200}
201
202func (newscat *NewsCategoryListData15) nameLen() []byte {
203 return []byte{uint8(len(newscat.Name))}
204}
205
206type NewsPath struct {
207 Paths []string
208}
209
210func (np *NewsPath) Payload() []byte {
211 var out []byte
212
213 count := make([]byte, 2)
214 binary.BigEndian.PutUint16(count, uint16(len(np.Paths)))
215
216 out = append(out, count...)
217 for _, p := range np.Paths {
218 pLen := byte(len(p))
219 out = append(out, []byte{0, 0}...) // what is this?
220 out = append(out, pLen)
221 out = append(out, []byte(p)...)
222 }
223
224 return out
225}
226
227func ReadNewsPath(newsPath []byte) []string {
228 if len(newsPath) == 0 {
229 return []string{}
230 }
231 pathCount := binary.BigEndian.Uint16(newsPath[0:2])
232
233 pathData := newsPath[2:]
234 var paths []string
235
236 for i := uint16(0); i < pathCount; i++ {
237 pathLen := pathData[2]
238 paths = append(paths, string(pathData[3:3+pathLen]))
239
240 pathData = pathData[pathLen+3:]
241 }
242
243 return paths
244}
245
246func (s *Server) GetNewsCatByPath(paths []string) map[string]NewsCategoryListData15 {
247 cats := s.ThreadedNews.Categories
248 for _, path := range paths {
249 cats = cats[path].SubCats
250 }
251 return cats
252}
253
254// News article date field contains this structure:
255// Year 2
256// Milliseconds 2 (seriously?)
257// Seconds 4
258func NewsDate() []byte {
259 t := time.Now()
260 ms := []byte{0, 0}
261 seconds := []byte{0, 0, 0, 0}
262
263 year := []byte{0, 0}
264 binary.BigEndian.PutUint16(year, uint16(t.Year()))
265
266 yearStart := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, time.Local)
267
268 binary.BigEndian.PutUint32(seconds, uint32(t.Sub(yearStart).Seconds()))
269
270 date := append(year, ms...)
271 date = append(date, seconds...)
272
273 return date
274}