Bluesky app fork with some witchin' additions 馃挮
witchsky.app
bluesky
fork
client
1import {createServer as createHTTPServer} from 'node:http'
2import {parse} from 'node:url'
3
4import {createServer, type TestPDS} from './test-pds'
5
6let server: TestPDS
7// eslint-disable-next-line @typescript-eslint/no-misused-promises
8createHTTPServer(async (req, res) => {
9 const url = parse(req.url || '/', true)
10 if (req.method !== 'POST') {
11 return res.writeHead(200).end()
12 }
13 try {
14 console.log('Closing old server')
15 await server?.close()
16 console.log('Starting new server')
17 const inviteRequired = url?.query && 'invite' in url.query
18 server = await createServer({inviteRequired})
19 console.log('Listening at', server.pdsUrl)
20 if (url?.query) {
21 if ('users' in url.query) {
22 console.log('Generating mock users')
23 await server.mocker.createUser('alice')
24 await server.mocker.createUser('bob')
25 await server.mocker.createUser('carla')
26 await server.mocker.users.alice.agent.upsertProfile(() => ({
27 displayName: 'Alice',
28 description: 'Test user 1',
29 }))
30 await server.mocker.users.bob.agent.upsertProfile(() => ({
31 displayName: 'Bob',
32 description: 'Test user 2',
33 }))
34 await server.mocker.users.carla.agent.upsertProfile(() => ({
35 displayName: 'Carla',
36 description: 'Test user 3',
37 }))
38 if (inviteRequired) {
39 await server.mocker.createInvite(server.mocker.users.alice.did)
40 }
41 }
42 if ('follows' in url.query) {
43 console.log('Generating mock follows')
44 await server.mocker.follow('alice', 'bob')
45 await server.mocker.follow('alice', 'carla')
46 await server.mocker.follow('bob', 'alice')
47 await server.mocker.follow('bob', 'carla')
48 await server.mocker.follow('carla', 'alice')
49 await server.mocker.follow('carla', 'bob')
50 }
51 if ('posts' in url.query) {
52 console.log('Generating mock posts')
53 for (let user in server.mocker.users) {
54 await server.mocker.users[user].agent.post({text: 'Post'})
55 }
56 }
57 if ('feeds' in url.query) {
58 console.log('Generating mock feed')
59 await server.mocker.createFeed('alice', 'alice-favs', [])
60 }
61 if ('thread' in url.query) {
62 console.log('Generating mock posts')
63 const res = await server.mocker.users.bob.agent.post({
64 text: 'Thread root',
65 })
66 await server.mocker.users.carla.agent.post({
67 text: 'Thread reply',
68 reply: {
69 parent: {cid: res.cid, uri: res.uri},
70 root: {cid: res.cid, uri: res.uri},
71 },
72 })
73 }
74 if ('mergefeed' in url.query) {
75 console.log('Generating mock users')
76 await server.mocker.createUser('alice')
77 await server.mocker.createUser('bob')
78 await server.mocker.createUser('carla')
79 await server.mocker.createUser('dan')
80 await server.mocker.users.alice.agent.upsertProfile(() => ({
81 displayName: 'Alice',
82 description: 'Test user 1',
83 }))
84 await server.mocker.users.bob.agent.upsertProfile(() => ({
85 displayName: 'Bob',
86 description: 'Test user 2',
87 }))
88 await server.mocker.users.carla.agent.upsertProfile(() => ({
89 displayName: 'Carla',
90 description: 'Test user 3',
91 }))
92 await server.mocker.users.dan.agent.upsertProfile(() => ({
93 displayName: 'Dan',
94 description: 'Test user 4',
95 }))
96 console.log('Generating mock follows')
97 await server.mocker.follow('alice', 'bob')
98 await server.mocker.follow('alice', 'carla')
99 console.log('Generating mock posts')
100 let posts: Record<string, any[]> = {
101 alice: [],
102 bob: [],
103 carla: [],
104 dan: [],
105 }
106 for (let i = 0; i < 10; i++) {
107 for (let user in server.mocker.users) {
108 if (user === 'alice') continue
109 posts[user].push(await server.mocker.createPost(user, `Post ${i}`))
110 }
111 }
112 for (let i = 0; i < 10; i++) {
113 for (let user in server.mocker.users) {
114 if (user === 'alice') continue
115 if (i % 5 === 0) {
116 await server.mocker.createReply(user, 'Self reply', {
117 cid: posts[user][i].cid,
118 uri: posts[user][i].uri,
119 })
120 }
121 if (i % 5 === 1) {
122 await server.mocker.createReply(user, 'Reply to bob', {
123 cid: posts.bob[i].cid,
124 uri: posts.bob[i].uri,
125 })
126 }
127 if (i % 5 === 2) {
128 await server.mocker.createReply(user, 'Reply to dan', {
129 cid: posts.dan[i].cid,
130 uri: posts.dan[i].uri,
131 })
132 }
133 await server.mocker.users[user].agent.post({text: `Post ${i}`})
134 }
135 }
136 console.log('Generating mock feeds')
137 await server.mocker.createFeed(
138 'alice',
139 'alice-favs',
140 posts.dan.map(p => p.uri),
141 )
142 await server.mocker.createFeed(
143 'alice',
144 'alice-favs2',
145 posts.dan.map(p => p.uri),
146 )
147 }
148 if ('labels' in url.query) {
149 console.log('Generating naughty users with labels')
150
151 const anchorPost = await server.mocker.createPost(
152 'alice',
153 'Anchor post',
154 )
155
156 for (const user of [
157 'dmca-account',
158 'dmca-profile',
159 'dmca-posts',
160 'porn-account',
161 'porn-profile',
162 'porn-posts',
163 'nudity-account',
164 'nudity-profile',
165 'nudity-posts',
166 'scam-account',
167 'scam-profile',
168 'scam-posts',
169 'unknown-account',
170 'unknown-profile',
171 'unknown-posts',
172 'hide-account',
173 'hide-profile',
174 'hide-posts',
175 'no-promote-account',
176 'no-promote-profile',
177 'no-promote-posts',
178 'warn-account',
179 'warn-profile',
180 'warn-posts',
181 'muted-account',
182 'muted-by-list-acc',
183 'blocking-account',
184 'blockedby-account',
185 'mutual-block-acc',
186 ]) {
187 await server.mocker.createUser(user)
188 await server.mocker.follow('alice', user)
189 await server.mocker.follow(user, 'alice')
190 await server.mocker.createPost(user, `Unlabeled post from ${user}`)
191 await server.mocker.createReply(
192 user,
193 `Unlabeled reply from ${user}`,
194 anchorPost,
195 )
196 await server.mocker.like(user, anchorPost)
197 }
198
199 await server.mocker.labelAccount('dmca-violation', 'dmca-account')
200 await server.mocker.labelProfile('dmca-violation', 'dmca-profile')
201 await server.mocker.labelPost(
202 'dmca-violation',
203 await server.mocker.createPost('dmca-posts', 'dmca post'),
204 )
205 await server.mocker.labelPost(
206 'dmca-violation',
207 await server.mocker.createQuotePost(
208 'dmca-posts',
209 'dmca quote post',
210 anchorPost,
211 ),
212 )
213 await server.mocker.labelPost(
214 'dmca-violation',
215 await server.mocker.createReply(
216 'dmca-posts',
217 'dmca reply',
218 anchorPost,
219 ),
220 )
221
222 await server.mocker.labelAccount('porn', 'porn-account')
223 await server.mocker.labelProfile('porn', 'porn-profile')
224 await server.mocker.labelPost(
225 'porn',
226 await server.mocker.createImagePost('porn-posts', 'porn post'),
227 )
228 await server.mocker.labelPost(
229 'porn',
230 await server.mocker.createQuotePost(
231 'porn-posts',
232 'porn quote post',
233 anchorPost,
234 ),
235 )
236 await server.mocker.labelPost(
237 'porn',
238 await server.mocker.createReply(
239 'porn-posts',
240 'porn reply',
241 anchorPost,
242 ),
243 )
244
245 await server.mocker.labelAccount('nudity', 'nudity-account')
246 await server.mocker.labelProfile('nudity', 'nudity-profile')
247 await server.mocker.labelPost(
248 'nudity',
249 await server.mocker.createImagePost('nudity-posts', 'nudity post'),
250 )
251 await server.mocker.labelPost(
252 'nudity',
253 await server.mocker.createQuotePost(
254 'nudity-posts',
255 'nudity quote post',
256 anchorPost,
257 ),
258 )
259 await server.mocker.labelPost(
260 'nudity',
261 await server.mocker.createReply(
262 'nudity-posts',
263 'nudity reply',
264 anchorPost,
265 ),
266 )
267
268 await server.mocker.labelAccount('scam', 'scam-account')
269 await server.mocker.labelProfile('scam', 'scam-profile')
270 await server.mocker.labelPost(
271 'scam',
272 await server.mocker.createPost('scam-posts', 'scam post'),
273 )
274 await server.mocker.labelPost(
275 'scam',
276 await server.mocker.createQuotePost(
277 'scam-posts',
278 'scam quote post',
279 anchorPost,
280 ),
281 )
282 await server.mocker.labelPost(
283 'scam',
284 await server.mocker.createReply(
285 'scam-posts',
286 'scam reply',
287 anchorPost,
288 ),
289 )
290
291 await server.mocker.labelAccount('not-a-real-label', 'unknown-account')
292 await server.mocker.labelProfile('not-a-real-label', 'unknown-profile')
293 await server.mocker.labelPost(
294 'not-a-real-label',
295 await server.mocker.createPost('unknown-posts', 'unknown post'),
296 )
297 await server.mocker.labelPost(
298 'not-a-real-label',
299 await server.mocker.createQuotePost(
300 'unknown-posts',
301 'unknown quote post',
302 anchorPost,
303 ),
304 )
305 await server.mocker.labelPost(
306 'not-a-real-label',
307 await server.mocker.createReply(
308 'unknown-posts',
309 'unknown reply',
310 anchorPost,
311 ),
312 )
313
314 await server.mocker.labelAccount('!hide', 'hide-account')
315 await server.mocker.labelProfile('!hide', 'hide-profile')
316 await server.mocker.labelPost(
317 '!hide',
318 await server.mocker.createPost('hide-posts', 'hide post'),
319 )
320 await server.mocker.labelPost(
321 '!hide',
322 await server.mocker.createQuotePost(
323 'hide-posts',
324 'hide quote post',
325 anchorPost,
326 ),
327 )
328 await server.mocker.labelPost(
329 '!hide',
330 await server.mocker.createReply(
331 'hide-posts',
332 'hide reply',
333 anchorPost,
334 ),
335 )
336
337 await server.mocker.labelAccount('!no-promote', 'no-promote-account')
338 await server.mocker.labelProfile('!no-promote', 'no-promote-profile')
339 await server.mocker.labelPost(
340 '!no-promote',
341 await server.mocker.createPost('no-promote-posts', 'no-promote post'),
342 )
343 await server.mocker.labelPost(
344 '!no-promote',
345 await server.mocker.createQuotePost(
346 'no-promote-posts',
347 'no-promote quote post',
348 anchorPost,
349 ),
350 )
351 await server.mocker.labelPost(
352 '!no-promote',
353 await server.mocker.createReply(
354 'no-promote-posts',
355 'no-promote reply',
356 anchorPost,
357 ),
358 )
359
360 await server.mocker.labelAccount('!warn', 'warn-account')
361 await server.mocker.labelProfile('!warn', 'warn-profile')
362 await server.mocker.labelPost(
363 '!warn',
364 await server.mocker.createPost('warn-posts', 'warn post'),
365 )
366 await server.mocker.labelPost(
367 '!warn',
368 await server.mocker.createQuotePost(
369 'warn-posts',
370 'warn quote post',
371 anchorPost,
372 ),
373 )
374 await server.mocker.labelPost(
375 '!warn',
376 await server.mocker.createReply(
377 'warn-posts',
378 'warn reply',
379 anchorPost,
380 ),
381 )
382
383 await server.mocker.users.alice.agent.mute('muted-account.test')
384 await server.mocker.createPost('muted-account', 'muted post')
385 await server.mocker.createQuotePost(
386 'muted-account',
387 'muted quote post',
388 anchorPost,
389 )
390 await server.mocker.createReply(
391 'muted-account',
392 'muted reply',
393 anchorPost,
394 )
395
396 const list = await server.mocker.createMuteList('alice', 'Muted Users')
397 await server.mocker.addToMuteList(
398 'alice',
399 list,
400 server.mocker.users['muted-by-list-acc'].did,
401 )
402 await server.mocker.createPost('muted-by-list-acc', 'muted post')
403 await server.mocker.createQuotePost(
404 'muted-by-list-acc',
405 'account quote post',
406 anchorPost,
407 )
408 await server.mocker.createReply(
409 'muted-by-list-acc',
410 'account reply',
411 anchorPost,
412 )
413
414 await server.mocker.createPost('blocking-account', 'blocking post')
415 await server.mocker.createQuotePost(
416 'blocking-account',
417 'blocking quote post',
418 anchorPost,
419 )
420 await server.mocker.createReply(
421 'blocking-account',
422 'blocking reply',
423 anchorPost,
424 )
425 await server.mocker.users.alice.agent.app.bsky.graph.block.create(
426 {
427 repo: server.mocker.users.alice.did,
428 },
429 {
430 subject: server.mocker.users['blocking-account'].did,
431 createdAt: new Date().toISOString(),
432 },
433 )
434
435 await server.mocker.createPost('blockedby-account', 'blockedby post')
436 await server.mocker.createQuotePost(
437 'blockedby-account',
438 'blockedby quote post',
439 anchorPost,
440 )
441 await server.mocker.createReply(
442 'blockedby-account',
443 'blockedby reply',
444 anchorPost,
445 )
446 await server.mocker.users[
447 'blockedby-account'
448 ].agent.app.bsky.graph.block.create(
449 {
450 repo: server.mocker.users['blockedby-account'].did,
451 },
452 {
453 subject: server.mocker.users.alice.did,
454 createdAt: new Date().toISOString(),
455 },
456 )
457
458 await server.mocker.createPost('mutual-block-acc', 'mutual-block post')
459 await server.mocker.createQuotePost(
460 'mutual-block-acc',
461 'mutual-block quote post',
462 anchorPost,
463 )
464 await server.mocker.createReply(
465 'mutual-block-acc',
466 'mutual-block reply',
467 anchorPost,
468 )
469 await server.mocker.users.alice.agent.app.bsky.graph.block.create(
470 {
471 repo: server.mocker.users.alice.did,
472 },
473 {
474 subject: server.mocker.users['mutual-block-acc'].did,
475 createdAt: new Date().toISOString(),
476 },
477 )
478 await server.mocker.users[
479 'mutual-block-acc'
480 ].agent.app.bsky.graph.block.create(
481 {
482 repo: server.mocker.users['mutual-block-acc'].did,
483 },
484 {
485 subject: server.mocker.users.alice.did,
486 createdAt: new Date().toISOString(),
487 },
488 )
489
490 // flush caches
491 await server.mocker.testNet.processAll()
492 }
493 }
494 console.log('Ready')
495 return res
496 .writeHead(200, {
497 'content-type': 'application/json',
498 })
499 .end(
500 JSON.stringify({
501 pdsUrl: server.pdsUrl,
502 appviewDid: server.appviewDid,
503 }),
504 )
505 } catch (e) {
506 console.error('Error!', e)
507 return res.writeHead(500).end()
508 }
509}).listen(1986)
510console.log('Mock server manager listening on 1986')