backend for xcvr appview
1package recordmanager
2
3import (
4 "context"
5 "errors"
6 atoauth "github.com/bluesky-social/indigo/atproto/auth/oauth"
7 "github.com/bluesky-social/indigo/atproto/syntax"
8 "github.com/rachel-mp4/lrcd"
9 "os"
10 "rvcx/internal/atputils"
11 "rvcx/internal/lex"
12 "rvcx/internal/oauth"
13 "rvcx/internal/types"
14 "slices"
15 "time"
16)
17
18func (rm *RecordManager) AcceptMessage(m *types.Message, ctx context.Context) error {
19 wasNew, err := rm.storeMessage(m, ctx)
20 if err != nil {
21 return errors.New("failed to store message: " + err.Error())
22 }
23 if !wasNew {
24 return nil
25 }
26 err = rm.forwardMessage(m, ctx)
27 if err != nil {
28 return errors.New("failed to forward message: " + err.Error())
29 }
30 return nil
31}
32
33func (rm *RecordManager) AcceptMessageUpdate(m *types.Message, did string, ctx context.Context) error {
34 err := rm.updateMessage(m, ctx)
35 if err != nil {
36 return errors.New("failed to store message: " + err.Error())
37 }
38 err = rm.checkInterference(m, did, ctx)
39 if err != nil {
40 return errors.New("error while checking interference: " + err.Error())
41 }
42 return nil
43}
44
45func (rm *RecordManager) AcceptMessageDelete(uri string, ctx context.Context) error {
46 err := rm.db.DeleteMessage(uri, ctx)
47 if err != nil {
48 return errors.New("failed to delete message: " + err.Error())
49 }
50 return nil
51}
52
53func (rm *RecordManager) checkInterference(m *types.Message, did string, ctx context.Context) error {
54 handle, err := rm.db.QuerySignetHandle(m.SignetURI, ctx)
55 if err != nil {
56 return errors.New("couldn't find signet")
57 }
58 sdid, err := atputils.DidFromUri(m.SignetURI)
59 if sdid != atputils.GetMyDid() {
60 return nil
61 }
62 mhandle, err := rm.db.ResolveDid(did, ctx)
63 if err != nil {
64 return errors.New("couldn't resolve mhandle")
65 }
66 if handle != mhandle {
67 return nil
68 }
69 err = rm.DeleteSignet(m.SignetURI, ctx)
70 if err != nil {
71 return errors.New("failed to delete signet")
72 }
73 return nil
74}
75
76func (rm *RecordManager) PostMessage(cs *atoauth.ClientSession, ctx context.Context, pmr *types.PostMessageRequest) error {
77 rm.log.Deprintln("validate")
78 lmr, now, _, _, err := rm.validateMessage(pmr, ctx)
79 if err != nil {
80 return errors.New("failed to validate message: " + err.Error())
81 }
82 rm.log.Deprintln("create")
83 m, err := rm.createMessage(cs, lmr, now, ctx)
84 if err != nil {
85 return errors.New("failed to create message: " + err.Error())
86 }
87 rm.log.Deprintln("store")
88 wasNew, err := rm.storeMessage(m, ctx)
89 if err != nil {
90 return errors.New("failed to store message: " + err.Error())
91 }
92 if !wasNew {
93 return nil
94 }
95 rm.log.Deprintln("forward")
96 err = rm.forwardMessage(m, ctx)
97 if err != nil {
98 return errors.New("failed to forward message: " + err.Error())
99 }
100 return nil
101}
102
103func (rm *RecordManager) PostMyMessage(ctx context.Context, pmr *types.PostMessageRequest) error {
104 lmr, now, handle, nonce, err := rm.validateMessage(pmr, ctx)
105 if err != nil {
106 return errors.New("failed to validate message: " + err.Error())
107 }
108 err = rm.validateHandleAndNonce(handle, nonce, lmr.SignetURI, ctx)
109 if err != nil {
110 return errors.New("failed to validate my handle and nonce: " + err.Error())
111 }
112 m, err := rm.createMyMessage(lmr, now, ctx)
113 if err != nil {
114 return errors.New("failed to create message: " + err.Error())
115 }
116 wasNew, err := rm.storeMessage(m, ctx)
117 if err != nil {
118 return errors.New("failed to store message: " + err.Error())
119 }
120 if !wasNew {
121 return nil
122 }
123 err = rm.forwardMessage(m, ctx)
124 if err != nil {
125 return errors.New("failed to forward message: " + err.Error())
126 }
127 return nil
128}
129
130func (rm *RecordManager) validateHandleAndNonce(handle *string, nonce []byte, signetUri string, ctx context.Context) error {
131 if handle == nil || *handle != atputils.GetMyHandle() {
132 return errors.New("i only post my messages")
133 }
134 curi, mid, err := rm.db.QuerySignetChannelIdNum(signetUri, ctx)
135 if err != nil {
136 return errors.New("failed to find signet")
137 }
138 correctNonce := lrcd.GenerateNonce(mid, curi, os.Getenv("LRCD_SECRET"))
139 if !slices.Equal(nonce, correctNonce) {
140 return errors.New("i think user tried to post someone else's post")
141 }
142 return nil
143}
144
145func (rm *RecordManager) createMyMessage(lmr *lex.MessageRecord, now *time.Time, ctx context.Context) (*types.Message, error) {
146 cid, uri, err := rm.myClient.CreateXCVRMessage(lmr, ctx)
147 if err != nil {
148 return nil, errors.New("couldn't add to user repo: " + err.Error())
149 }
150 var coloruint32ptr *uint32
151 if lmr.Color != nil {
152 color := uint32(*lmr.Color)
153 coloruint32ptr = &color
154 }
155 message := &types.Message{
156 URI: uri,
157 DID: atputils.GetMyDid(),
158 CID: cid,
159 SignetURI: lmr.SignetURI,
160 Body: lmr.Body,
161 Nick: lmr.Nick,
162 Color: coloruint32ptr,
163 PostedAt: *now,
164 }
165 return message, nil
166}
167
168func (rm *RecordManager) createMessage(cs *atoauth.ClientSession, lmr *lex.MessageRecord, now *time.Time, ctx context.Context) (*types.Message, error) {
169 uri, cid, err := oauth.CreateXCVRMessage(cs, lmr, ctx)
170 if err != nil {
171 return nil, errors.New("couldn't add to user repo: " + err.Error())
172 }
173 var coloruint32ptr *uint32
174 if lmr.Color != nil {
175 color := uint32(*lmr.Color)
176 coloruint32ptr = &color
177 }
178 message := &types.Message{
179 URI: uri,
180 DID: cs.Data.AccountDID.String(),
181 CID: cid,
182 SignetURI: lmr.SignetURI,
183 Body: lmr.Body,
184 Nick: lmr.Nick,
185 Color: coloruint32ptr,
186 PostedAt: *now,
187 }
188 return message, nil
189}
190
191func (rm *RecordManager) updateMessage(m *types.Message, ctx context.Context) error {
192 return rm.db.UpdateMessage(m, ctx)
193}
194
195func (rm *RecordManager) storeMessage(m *types.Message, ctx context.Context) (wasNew bool, err error) {
196 return rm.db.StoreMessage(m, ctx)
197}
198
199func (rm *RecordManager) forwardMessage(m *types.Message, ctx context.Context) error {
200 curi, err := rm.db.GetMsgChannelURI(m.SignetURI, ctx)
201 if err != nil {
202 return errors.New("aaaaaaaaaaaa " + err.Error())
203 }
204 return rm.broadcaster.BroadcastMessage(curi, m)
205}
206
207func (rm *RecordManager) validateMessage(mr *types.PostMessageRequest, ctx context.Context) (lmr *lex.MessageRecord, now *time.Time, handle *string, nonce []byte, err error) {
208 lmr = &lex.MessageRecord{}
209 if mr.SignetURI == nil {
210 if mr.MessageID == nil || mr.ChannelURI == nil {
211 err = errors.New("must provide a way to determine signet")
212 return
213 }
214 signetUri, signetHandle, yorks := rm.db.QuerySignet(*mr.ChannelURI, *mr.MessageID, ctx)
215 if yorks != nil {
216 err = errors.New("i couldn't find the signet :c : " + yorks.Error())
217 return
218 }
219 mr.SignetURI = &signetUri
220 handle = &signetHandle
221 } else {
222 signetHandle, yorks := rm.db.QuerySignetHandle(*mr.SignetURI, ctx)
223 if yorks != nil {
224 err = errors.New("yorks skooby 💀" + yorks.Error())
225 return
226 }
227 handle = &signetHandle
228 }
229 lmr.SignetURI = *mr.SignetURI
230 lmr.Body = mr.Body
231 if mr.Nick != nil {
232 nick := *mr.Nick
233 if atputils.ValidateLength(nick, 16) {
234 err = errors.New("that nick is too long")
235 return
236 }
237 }
238 lmr.Nick = mr.Nick
239
240 if mr.Color != nil {
241 color := uint64(*mr.Color)
242 if color > 16777215 {
243 err = errors.New("that color is too big")
244 return
245 }
246 lmr.Color = &color
247 }
248
249 nonce = mr.Nonce
250 nowsyn := syntax.DatetimeNow()
251 lmr.PostedAt = nowsyn.String()
252 nt := nowsyn.Time()
253 now = &nt
254 return
255}