@atproto/bsky 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/cache/read-through.d.ts +30 -0
- package/dist/config.d.ts +18 -0
- package/dist/context.d.ts +6 -6
- package/dist/daemon/config.d.ts +15 -0
- package/dist/daemon/context.d.ts +15 -0
- package/dist/daemon/index.d.ts +23 -0
- package/dist/daemon/logger.d.ts +3 -0
- package/dist/daemon/notifications.d.ts +18 -0
- package/dist/daemon/services.d.ts +11 -0
- package/dist/db/database-schema.d.ts +1 -2
- package/dist/db/index.js +16 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20231205T000257238Z-remove-did-cache.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/did-cache.d.ts +10 -7
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1917 -934
- package/dist/index.js.map +3 -3
- package/dist/indexer/context.d.ts +2 -0
- package/dist/indexer/index.d.ts +1 -0
- package/dist/lexicon/index.d.ts +12 -0
- package/dist/lexicon/lexicons.d.ts +134 -0
- package/dist/lexicon/types/com/atproto/admin/deleteAccount.d.ts +25 -0
- package/dist/lexicon/types/com/atproto/temp/importRepo.d.ts +32 -0
- package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
- package/dist/lexicon/types/com/atproto/temp/transferAccount.d.ts +42 -0
- package/dist/logger.d.ts +1 -0
- package/dist/redis.d.ts +10 -1
- package/dist/services/actor/index.d.ts +18 -4
- package/dist/services/actor/views.d.ts +4 -3
- package/dist/services/feed/index.d.ts +6 -4
- package/dist/services/feed/views.d.ts +5 -4
- package/dist/services/index.d.ts +3 -7
- package/dist/services/label/index.d.ts +10 -4
- package/dist/services/moderation/index.d.ts +0 -1
- package/dist/services/types.d.ts +3 -0
- package/dist/services/util/notification.d.ts +5 -0
- package/dist/services/util/post.d.ts +6 -6
- package/dist/util/retry.d.ts +1 -6
- package/package.json +5 -5
- package/src/cache/read-through.ts +151 -0
- package/src/config.ts +90 -1
- package/src/context.ts +7 -7
- package/src/daemon/config.ts +60 -0
- package/src/daemon/context.ts +27 -0
- package/src/daemon/index.ts +78 -0
- package/src/daemon/logger.ts +6 -0
- package/src/daemon/notifications.ts +54 -0
- package/src/daemon/services.ts +22 -0
- package/src/db/database-schema.ts +0 -2
- package/src/db/migrations/20231205T000257238Z-remove-did-cache.ts +14 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/did-cache.ts +33 -56
- package/src/feed-gen/index.ts +0 -4
- package/src/index.ts +55 -16
- package/src/indexer/context.ts +5 -0
- package/src/indexer/index.ts +10 -7
- package/src/lexicon/index.ts +50 -0
- package/src/lexicon/lexicons.ts +156 -0
- package/src/lexicon/types/com/atproto/admin/deleteAccount.ts +38 -0
- package/src/lexicon/types/com/atproto/temp/importRepo.ts +45 -0
- package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
- package/src/lexicon/types/com/atproto/temp/transferAccount.ts +62 -0
- package/src/logger.ts +2 -0
- package/src/redis.ts +43 -3
- package/src/services/actor/index.ts +55 -7
- package/src/services/actor/views.ts +13 -7
- package/src/services/feed/index.ts +27 -13
- package/src/services/feed/views.ts +20 -10
- package/src/services/index.ts +14 -14
- package/src/services/indexing/index.ts +7 -10
- package/src/services/indexing/plugins/post.ts +13 -0
- package/src/services/label/index.ts +66 -22
- package/src/services/moderation/index.ts +1 -1
- package/src/services/moderation/status.ts +1 -4
- package/src/services/types.ts +4 -0
- package/src/services/util/notification.ts +70 -0
- package/src/util/retry.ts +1 -44
- package/tests/admin/get-repo.test.ts +5 -3
- package/tests/admin/moderation.test.ts +2 -2
- package/tests/admin/repo-search.test.ts +1 -0
- package/tests/algos/hot-classic.test.ts +1 -2
- package/tests/auth.test.ts +1 -1
- package/tests/auto-moderator/labeler.test.ts +19 -20
- package/tests/auto-moderator/takedowns.test.ts +16 -10
- package/tests/blob-resolver.test.ts +4 -2
- package/tests/daemon.test.ts +191 -0
- package/tests/did-cache.test.ts +20 -5
- package/tests/handle-invalidation.test.ts +1 -5
- package/tests/indexing.test.ts +20 -13
- package/tests/redis-cache.test.ts +231 -0
- package/tests/seeds/basic.ts +3 -0
- package/tests/subscription/repo.test.ts +4 -7
- package/tests/views/profile.test.ts +0 -1
- package/tests/views/thread.test.ts +73 -78
- package/tests/views/threadgating.test.ts +38 -0
- package/dist/db/tables/did-cache.d.ts +0 -10
- package/dist/feed-gen/best-of-follows.d.ts +0 -29
- package/dist/feed-gen/whats-hot.d.ts +0 -29
- package/dist/feed-gen/with-friends.d.ts +0 -3
- package/dist/label-cache.d.ts +0 -19
- package/src/db/tables/did-cache.ts +0 -13
- package/src/feed-gen/best-of-follows.ts +0 -77
- package/src/feed-gen/whats-hot.ts +0 -101
- package/src/feed-gen/with-friends.ts +0 -43
- package/src/label-cache.ts +0 -90
- package/tests/algos/whats-hot.test.ts +0 -118
- package/tests/algos/with-friends.test.ts +0 -145
|
@@ -165,22 +165,21 @@ describe('pds thread views', () => {
|
|
|
165
165
|
|
|
166
166
|
describe('takedown', () => {
|
|
167
167
|
it('blocks post by actor', async () => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
did: alice,
|
|
175
|
-
},
|
|
176
|
-
createdBy: 'did:example:admin',
|
|
177
|
-
reason: 'Y',
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
encoding: 'application/json',
|
|
181
|
-
headers: network.pds.adminAuthHeaders(),
|
|
168
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
169
|
+
{
|
|
170
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
171
|
+
subject: {
|
|
172
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
173
|
+
did: alice,
|
|
182
174
|
},
|
|
183
|
-
|
|
175
|
+
createdBy: 'did:example:admin',
|
|
176
|
+
reason: 'Y',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
encoding: 'application/json',
|
|
180
|
+
headers: network.pds.adminAuthHeaders(),
|
|
181
|
+
},
|
|
182
|
+
)
|
|
184
183
|
|
|
185
184
|
// Same as shallow post thread test, minus alice
|
|
186
185
|
const promise = agent.api.app.bsky.feed.getPostThread(
|
|
@@ -211,22 +210,21 @@ describe('pds thread views', () => {
|
|
|
211
210
|
})
|
|
212
211
|
|
|
213
212
|
it('blocks replies by actor', async () => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
{
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
did: carol,
|
|
221
|
-
},
|
|
222
|
-
createdBy: 'did:example:admin',
|
|
223
|
-
reason: 'Y',
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
encoding: 'application/json',
|
|
227
|
-
headers: network.pds.adminAuthHeaders(),
|
|
213
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
214
|
+
{
|
|
215
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
216
|
+
subject: {
|
|
217
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
218
|
+
did: carol,
|
|
228
219
|
},
|
|
229
|
-
|
|
220
|
+
createdBy: 'did:example:admin',
|
|
221
|
+
reason: 'Y',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
encoding: 'application/json',
|
|
225
|
+
headers: network.pds.adminAuthHeaders(),
|
|
226
|
+
},
|
|
227
|
+
)
|
|
230
228
|
|
|
231
229
|
// Same as deep post thread test, minus carol
|
|
232
230
|
const thread = await agent.api.app.bsky.feed.getPostThread(
|
|
@@ -255,22 +253,21 @@ describe('pds thread views', () => {
|
|
|
255
253
|
})
|
|
256
254
|
|
|
257
255
|
it('blocks ancestors by actor', async () => {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
did: bob,
|
|
265
|
-
},
|
|
266
|
-
createdBy: 'did:example:admin',
|
|
267
|
-
reason: 'Y',
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
encoding: 'application/json',
|
|
271
|
-
headers: network.pds.adminAuthHeaders(),
|
|
256
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
257
|
+
{
|
|
258
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
259
|
+
subject: {
|
|
260
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
261
|
+
did: bob,
|
|
272
262
|
},
|
|
273
|
-
|
|
263
|
+
createdBy: 'did:example:admin',
|
|
264
|
+
reason: 'Y',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
encoding: 'application/json',
|
|
268
|
+
headers: network.pds.adminAuthHeaders(),
|
|
269
|
+
},
|
|
270
|
+
)
|
|
274
271
|
|
|
275
272
|
// Same as ancestor post thread test, minus bob
|
|
276
273
|
const thread = await agent.api.app.bsky.feed.getPostThread(
|
|
@@ -300,23 +297,22 @@ describe('pds thread views', () => {
|
|
|
300
297
|
|
|
301
298
|
it('blocks post by record', async () => {
|
|
302
299
|
const postRef = sc.posts[alice][1].ref
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
{
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
cid: postRef.cidStr,
|
|
311
|
-
},
|
|
312
|
-
createdBy: 'did:example:admin',
|
|
313
|
-
reason: 'Y',
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
encoding: 'application/json',
|
|
317
|
-
headers: network.pds.adminAuthHeaders(),
|
|
300
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
301
|
+
{
|
|
302
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
303
|
+
subject: {
|
|
304
|
+
$type: 'com.atproto.repo.strongRef',
|
|
305
|
+
uri: postRef.uriStr,
|
|
306
|
+
cid: postRef.cidStr,
|
|
318
307
|
},
|
|
319
|
-
|
|
308
|
+
createdBy: 'did:example:admin',
|
|
309
|
+
reason: 'Y',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
encoding: 'application/json',
|
|
313
|
+
headers: network.pds.adminAuthHeaders(),
|
|
314
|
+
},
|
|
315
|
+
)
|
|
320
316
|
|
|
321
317
|
const promise = agent.api.app.bsky.feed.getPostThread(
|
|
322
318
|
{ depth: 1, uri: postRef.uriStr },
|
|
@@ -354,23 +350,22 @@ describe('pds thread views', () => {
|
|
|
354
350
|
|
|
355
351
|
const parent = threadPreTakedown.data.thread.parent?.['post']
|
|
356
352
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
{
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
cid: parent.cid,
|
|
365
|
-
},
|
|
366
|
-
createdBy: 'did:example:admin',
|
|
367
|
-
reason: 'Y',
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
encoding: 'application/json',
|
|
371
|
-
headers: network.pds.adminAuthHeaders(),
|
|
353
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
354
|
+
{
|
|
355
|
+
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
356
|
+
subject: {
|
|
357
|
+
$type: 'com.atproto.repo.strongRef',
|
|
358
|
+
uri: parent.uri,
|
|
359
|
+
cid: parent.cid,
|
|
372
360
|
},
|
|
373
|
-
|
|
361
|
+
createdBy: 'did:example:admin',
|
|
362
|
+
reason: 'Y',
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
encoding: 'application/json',
|
|
366
|
+
headers: network.pds.adminAuthHeaders(),
|
|
367
|
+
},
|
|
368
|
+
)
|
|
374
369
|
|
|
375
370
|
// Same as ancestor post thread test, minus parent post
|
|
376
371
|
const thread = await agent.api.app.bsky.feed.getPostThread(
|
|
@@ -407,7 +402,7 @@ describe('pds thread views', () => {
|
|
|
407
402
|
const post1 = threadPreTakedown.data.thread.replies?.[0].post
|
|
408
403
|
const post2 = threadPreTakedown.data.thread.replies?.[1].replies[0].post
|
|
409
404
|
|
|
410
|
-
|
|
405
|
+
await Promise.all(
|
|
411
406
|
[post1, post2].map((post) =>
|
|
412
407
|
agent.api.com.atproto.admin.emitModerationEvent(
|
|
413
408
|
{
|
|
@@ -49,6 +49,7 @@ describe('views with thread gating', () => {
|
|
|
49
49
|
{ post: post.ref.uriStr, createdAt: iso(), allow: [] },
|
|
50
50
|
sc.getHeaders(sc.dids.carol),
|
|
51
51
|
)
|
|
52
|
+
await network.processAll()
|
|
52
53
|
await sc.reply(sc.dids.alice, post.ref, post.ref, 'empty rules reply')
|
|
53
54
|
await network.processAll()
|
|
54
55
|
const {
|
|
@@ -64,6 +65,33 @@ describe('views with thread gating', () => {
|
|
|
64
65
|
await checkReplyDisabled(post.ref.uriStr, sc.dids.alice, true)
|
|
65
66
|
})
|
|
66
67
|
|
|
68
|
+
it('does not generate notifications when post violates threadgate.', async () => {
|
|
69
|
+
const post = await sc.post(sc.dids.carol, 'notifications')
|
|
70
|
+
await pdsAgent.api.app.bsky.feed.threadgate.create(
|
|
71
|
+
{ repo: sc.dids.carol, rkey: post.ref.uri.rkey },
|
|
72
|
+
{ post: post.ref.uriStr, createdAt: iso(), allow: [] },
|
|
73
|
+
sc.getHeaders(sc.dids.carol),
|
|
74
|
+
)
|
|
75
|
+
await network.processAll()
|
|
76
|
+
const reply = await sc.reply(
|
|
77
|
+
sc.dids.alice,
|
|
78
|
+
post.ref,
|
|
79
|
+
post.ref,
|
|
80
|
+
'notifications reply',
|
|
81
|
+
)
|
|
82
|
+
await network.processAll()
|
|
83
|
+
const {
|
|
84
|
+
data: { notifications },
|
|
85
|
+
} = await agent.api.app.bsky.notification.listNotifications(
|
|
86
|
+
{},
|
|
87
|
+
{ headers: await network.serviceHeaders(sc.dids.carol) },
|
|
88
|
+
)
|
|
89
|
+
const notificationFromReply = notifications.find(
|
|
90
|
+
(notif) => notif.uri === reply.ref.uriStr,
|
|
91
|
+
)
|
|
92
|
+
expect(notificationFromReply).toBeUndefined()
|
|
93
|
+
})
|
|
94
|
+
|
|
67
95
|
it('applies gate for mention rule.', async () => {
|
|
68
96
|
const post = await sc.post(
|
|
69
97
|
sc.dids.carol,
|
|
@@ -92,6 +120,7 @@ describe('views with thread gating', () => {
|
|
|
92
120
|
},
|
|
93
121
|
sc.getHeaders(sc.dids.carol),
|
|
94
122
|
)
|
|
123
|
+
await network.processAll()
|
|
95
124
|
await sc.reply(
|
|
96
125
|
sc.dids.alice,
|
|
97
126
|
post.ref,
|
|
@@ -141,6 +170,7 @@ describe('views with thread gating', () => {
|
|
|
141
170
|
},
|
|
142
171
|
sc.getHeaders(sc.dids.carol),
|
|
143
172
|
)
|
|
173
|
+
await network.processAll()
|
|
144
174
|
// carol only follows alice
|
|
145
175
|
await sc.reply(
|
|
146
176
|
sc.dids.dan,
|
|
@@ -231,6 +261,7 @@ describe('views with thread gating', () => {
|
|
|
231
261
|
},
|
|
232
262
|
sc.getHeaders(sc.dids.carol),
|
|
233
263
|
)
|
|
264
|
+
await network.processAll()
|
|
234
265
|
//
|
|
235
266
|
await sc.reply(sc.dids.bob, post.ref, post.ref, 'list rule reply disallow')
|
|
236
267
|
const aliceReply = await sc.reply(
|
|
@@ -298,6 +329,7 @@ describe('views with thread gating', () => {
|
|
|
298
329
|
},
|
|
299
330
|
sc.getHeaders(sc.dids.carol),
|
|
300
331
|
)
|
|
332
|
+
await network.processAll()
|
|
301
333
|
await sc.reply(
|
|
302
334
|
sc.dids.alice,
|
|
303
335
|
post.ref,
|
|
@@ -339,6 +371,7 @@ describe('views with thread gating', () => {
|
|
|
339
371
|
},
|
|
340
372
|
sc.getHeaders(sc.dids.carol),
|
|
341
373
|
)
|
|
374
|
+
await network.processAll()
|
|
342
375
|
// carol only follows alice, and the post mentions dan.
|
|
343
376
|
await sc.reply(sc.dids.bob, post.ref, post.ref, 'multi rule reply disallow')
|
|
344
377
|
const aliceReply = await sc.reply(
|
|
@@ -397,6 +430,7 @@ describe('views with thread gating', () => {
|
|
|
397
430
|
{ post: post.ref.uriStr, createdAt: iso() },
|
|
398
431
|
sc.getHeaders(sc.dids.carol),
|
|
399
432
|
)
|
|
433
|
+
await network.processAll()
|
|
400
434
|
const aliceReply = await sc.reply(
|
|
401
435
|
sc.dids.alice,
|
|
402
436
|
post.ref,
|
|
@@ -432,6 +466,7 @@ describe('views with thread gating', () => {
|
|
|
432
466
|
},
|
|
433
467
|
sc.getHeaders(sc.dids.carol),
|
|
434
468
|
)
|
|
469
|
+
await network.processAll()
|
|
435
470
|
// carol only follows alice
|
|
436
471
|
const orphanedReply = await sc.reply(
|
|
437
472
|
sc.dids.alice,
|
|
@@ -493,6 +528,7 @@ describe('views with thread gating', () => {
|
|
|
493
528
|
{ post: post.ref.uriStr, createdAt: iso(), allow: [] },
|
|
494
529
|
sc.getHeaders(sc.dids.carol),
|
|
495
530
|
)
|
|
531
|
+
await network.processAll()
|
|
496
532
|
const selfReply = await sc.reply(
|
|
497
533
|
sc.dids.carol,
|
|
498
534
|
post.ref,
|
|
@@ -527,6 +563,7 @@ describe('views with thread gating', () => {
|
|
|
527
563
|
},
|
|
528
564
|
sc.getHeaders(sc.dids.carol),
|
|
529
565
|
)
|
|
566
|
+
await network.processAll()
|
|
530
567
|
// carol only follows alice
|
|
531
568
|
const badReply = await sc.reply(
|
|
532
569
|
sc.dids.dan,
|
|
@@ -571,6 +608,7 @@ describe('views with thread gating', () => {
|
|
|
571
608
|
{ post: postB.ref.uriStr, createdAt: iso(), allow: [] },
|
|
572
609
|
sc.getHeaders(sc.dids.carol),
|
|
573
610
|
)
|
|
611
|
+
await network.processAll()
|
|
574
612
|
await sc.reply(sc.dids.alice, postA.ref, postA.ref, 'ungated reply')
|
|
575
613
|
await sc.reply(sc.dids.alice, postB.ref, postB.ref, 'ungated reply')
|
|
576
614
|
await network.processAll()
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { AlgoHandler } from './types';
|
|
2
|
-
import { GenericKeyset } from '../db/pagination';
|
|
3
|
-
declare const handler: AlgoHandler;
|
|
4
|
-
export default handler;
|
|
5
|
-
declare type Result = {
|
|
6
|
-
score: number;
|
|
7
|
-
cid: string;
|
|
8
|
-
};
|
|
9
|
-
declare type LabeledResult = {
|
|
10
|
-
primary: number;
|
|
11
|
-
secondary: string;
|
|
12
|
-
};
|
|
13
|
-
export declare class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
14
|
-
labelResult(result: Result): {
|
|
15
|
-
primary: number;
|
|
16
|
-
secondary: string;
|
|
17
|
-
};
|
|
18
|
-
labeledResultToCursor(labeled: LabeledResult): {
|
|
19
|
-
primary: string;
|
|
20
|
-
secondary: string;
|
|
21
|
-
};
|
|
22
|
-
cursorToLabeledResult(cursor: {
|
|
23
|
-
primary: string;
|
|
24
|
-
secondary: string;
|
|
25
|
-
}): {
|
|
26
|
-
primary: number;
|
|
27
|
-
secondary: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { AlgoHandler } from './types';
|
|
2
|
-
import { GenericKeyset } from '../db/pagination';
|
|
3
|
-
declare const handler: AlgoHandler;
|
|
4
|
-
export default handler;
|
|
5
|
-
declare type Result = {
|
|
6
|
-
score: number;
|
|
7
|
-
cid: string;
|
|
8
|
-
};
|
|
9
|
-
declare type LabeledResult = {
|
|
10
|
-
primary: number;
|
|
11
|
-
secondary: string;
|
|
12
|
-
};
|
|
13
|
-
export declare class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
14
|
-
labelResult(result: Result): {
|
|
15
|
-
primary: number;
|
|
16
|
-
secondary: string;
|
|
17
|
-
};
|
|
18
|
-
labeledResultToCursor(labeled: LabeledResult): {
|
|
19
|
-
primary: string;
|
|
20
|
-
secondary: string;
|
|
21
|
-
};
|
|
22
|
-
cursorToLabeledResult(cursor: {
|
|
23
|
-
primary: string;
|
|
24
|
-
secondary: string;
|
|
25
|
-
}): {
|
|
26
|
-
primary: number;
|
|
27
|
-
secondary: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
package/dist/label-cache.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { PrimaryDatabase } from './db';
|
|
2
|
-
import { Label } from './db/tables/label';
|
|
3
|
-
export declare class LabelCache {
|
|
4
|
-
db: PrimaryDatabase;
|
|
5
|
-
bySubject: Record<string, Label[]>;
|
|
6
|
-
latestLabel: string;
|
|
7
|
-
refreshes: number;
|
|
8
|
-
destroyed: boolean;
|
|
9
|
-
constructor(db: PrimaryDatabase);
|
|
10
|
-
start(): void;
|
|
11
|
-
fullRefresh(): Promise<void>;
|
|
12
|
-
partialRefresh(): Promise<void>;
|
|
13
|
-
poll(): Promise<void>;
|
|
14
|
-
processLabels(labels: Label[]): void;
|
|
15
|
-
wipeCache(): void;
|
|
16
|
-
stop(): void;
|
|
17
|
-
forSubject(subject: string, includeNeg?: boolean): Label[];
|
|
18
|
-
forSubjects(subjects: string[], includeNeg?: boolean): Label[];
|
|
19
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
|
|
2
|
-
import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
3
|
-
import { AlgoHandler, AlgoResponse } from './types'
|
|
4
|
-
import { GenericKeyset, paginate } from '../db/pagination'
|
|
5
|
-
import AppContext from '../context'
|
|
6
|
-
|
|
7
|
-
const handler: AlgoHandler = async (
|
|
8
|
-
ctx: AppContext,
|
|
9
|
-
params: SkeletonParams,
|
|
10
|
-
viewer: string | null,
|
|
11
|
-
): Promise<AlgoResponse> => {
|
|
12
|
-
if (!viewer) {
|
|
13
|
-
throw new AuthRequiredError('This feed requires being logged-in')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const { limit, cursor } = params
|
|
17
|
-
const db = ctx.db.getReplica('feed')
|
|
18
|
-
const feedService = ctx.services.feed(db)
|
|
19
|
-
const { ref } = db.db.dynamic
|
|
20
|
-
|
|
21
|
-
// candidates are ranked within a materialized view by like count, depreciated over time.
|
|
22
|
-
|
|
23
|
-
let builder = feedService
|
|
24
|
-
.selectPostQb()
|
|
25
|
-
.innerJoin('algo_whats_hot_view as candidate', 'candidate.uri', 'post.uri')
|
|
26
|
-
.where((qb) =>
|
|
27
|
-
qb
|
|
28
|
-
.where('post.creator', '=', viewer)
|
|
29
|
-
.orWhereExists((inner) =>
|
|
30
|
-
inner
|
|
31
|
-
.selectFrom('follow')
|
|
32
|
-
.where('follow.creator', '=', viewer)
|
|
33
|
-
.whereRef('follow.subjectDid', '=', 'post.creator'),
|
|
34
|
-
),
|
|
35
|
-
)
|
|
36
|
-
.select('candidate.score')
|
|
37
|
-
.select('candidate.cid')
|
|
38
|
-
|
|
39
|
-
const keyset = new ScoreKeyset(ref('candidate.score'), ref('candidate.cid'))
|
|
40
|
-
builder = paginate(builder, { limit, cursor, keyset })
|
|
41
|
-
|
|
42
|
-
const feedItems = await builder.execute()
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
feedItems,
|
|
46
|
-
cursor: keyset.packFromResult(feedItems),
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export default handler
|
|
51
|
-
|
|
52
|
-
type Result = { score: number; cid: string }
|
|
53
|
-
type LabeledResult = { primary: number; secondary: string }
|
|
54
|
-
export class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
55
|
-
labelResult(result: Result) {
|
|
56
|
-
return {
|
|
57
|
-
primary: result.score,
|
|
58
|
-
secondary: result.cid,
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
labeledResultToCursor(labeled: LabeledResult) {
|
|
62
|
-
return {
|
|
63
|
-
primary: Math.round(labeled.primary).toString(),
|
|
64
|
-
secondary: labeled.secondary,
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
cursorToLabeledResult(cursor: { primary: string; secondary: string }) {
|
|
68
|
-
const score = parseInt(cursor.primary, 10)
|
|
69
|
-
if (isNaN(score)) {
|
|
70
|
-
throw new InvalidRequestError('Malformed cursor')
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
primary: score,
|
|
74
|
-
secondary: cursor.secondary,
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { NotEmptyArray } from '@atproto/common'
|
|
2
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
3
|
-
import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
4
|
-
import { AlgoHandler, AlgoResponse } from './types'
|
|
5
|
-
import { GenericKeyset, paginate } from '../db/pagination'
|
|
6
|
-
import AppContext from '../context'
|
|
7
|
-
import { valuesList } from '../db/util'
|
|
8
|
-
import { sql } from 'kysely'
|
|
9
|
-
import { FeedItemType } from '../services/feed/types'
|
|
10
|
-
|
|
11
|
-
const NO_WHATS_HOT_LABELS: NotEmptyArray<string> = [
|
|
12
|
-
'!no-promote',
|
|
13
|
-
'corpse',
|
|
14
|
-
'self-harm',
|
|
15
|
-
'porn',
|
|
16
|
-
'sexual',
|
|
17
|
-
'nudity',
|
|
18
|
-
'underwear',
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
const handler: AlgoHandler = async (
|
|
22
|
-
ctx: AppContext,
|
|
23
|
-
params: SkeletonParams,
|
|
24
|
-
_viewer: string | null,
|
|
25
|
-
): Promise<AlgoResponse> => {
|
|
26
|
-
const { limit, cursor } = params
|
|
27
|
-
const db = ctx.db.getReplica('feed')
|
|
28
|
-
|
|
29
|
-
const { ref } = db.db.dynamic
|
|
30
|
-
|
|
31
|
-
// candidates are ranked within a materialized view by like count, depreciated over time.
|
|
32
|
-
|
|
33
|
-
let builder = db.db
|
|
34
|
-
.selectFrom('algo_whats_hot_view as candidate')
|
|
35
|
-
.innerJoin('post', 'post.uri', 'candidate.uri')
|
|
36
|
-
.leftJoin('post_embed_record', 'post_embed_record.postUri', 'candidate.uri')
|
|
37
|
-
.whereNotExists((qb) =>
|
|
38
|
-
qb
|
|
39
|
-
.selectFrom('label')
|
|
40
|
-
.selectAll()
|
|
41
|
-
.whereRef('val', 'in', valuesList(NO_WHATS_HOT_LABELS))
|
|
42
|
-
.where('neg', '=', false)
|
|
43
|
-
.where((clause) =>
|
|
44
|
-
clause
|
|
45
|
-
.whereRef('label.uri', '=', ref('post.creator'))
|
|
46
|
-
.orWhereRef('label.uri', '=', ref('post.uri'))
|
|
47
|
-
.orWhereRef('label.uri', '=', ref('post_embed_record.embedUri')),
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
.select([
|
|
51
|
-
sql<FeedItemType>`${'post'}`.as('type'),
|
|
52
|
-
'post.uri as uri',
|
|
53
|
-
'post.uri as postUri',
|
|
54
|
-
'post.creator as originatorDid',
|
|
55
|
-
'post.creator as postAuthorDid',
|
|
56
|
-
'post.replyParent as replyParent',
|
|
57
|
-
'post.replyRoot as replyRoot',
|
|
58
|
-
'post.indexedAt as sortAt',
|
|
59
|
-
'candidate.score',
|
|
60
|
-
'candidate.cid',
|
|
61
|
-
])
|
|
62
|
-
|
|
63
|
-
const keyset = new ScoreKeyset(ref('candidate.score'), ref('candidate.cid'))
|
|
64
|
-
builder = paginate(builder, { limit, cursor, keyset })
|
|
65
|
-
|
|
66
|
-
const feedItems = await builder.execute()
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
feedItems,
|
|
70
|
-
cursor: keyset.packFromResult(feedItems),
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export default handler
|
|
75
|
-
|
|
76
|
-
type Result = { score: number; cid: string }
|
|
77
|
-
type LabeledResult = { primary: number; secondary: string }
|
|
78
|
-
export class ScoreKeyset extends GenericKeyset<Result, LabeledResult> {
|
|
79
|
-
labelResult(result: Result) {
|
|
80
|
-
return {
|
|
81
|
-
primary: result.score,
|
|
82
|
-
secondary: result.cid,
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
labeledResultToCursor(labeled: LabeledResult) {
|
|
86
|
-
return {
|
|
87
|
-
primary: Math.round(labeled.primary).toString(),
|
|
88
|
-
secondary: labeled.secondary,
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
cursorToLabeledResult(cursor: { primary: string; secondary: string }) {
|
|
92
|
-
const score = parseInt(cursor.primary, 10)
|
|
93
|
-
if (isNaN(score)) {
|
|
94
|
-
throw new InvalidRequestError('Malformed cursor')
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
primary: score,
|
|
98
|
-
secondary: cursor.secondary,
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import AppContext from '../context'
|
|
2
|
-
import { QueryParams as SkeletonParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
3
|
-
import { paginate } from '../db/pagination'
|
|
4
|
-
import { AlgoHandler, AlgoResponse } from './types'
|
|
5
|
-
import { FeedKeyset, getFeedDateThreshold } from '../api/app/bsky/util/feed'
|
|
6
|
-
import { AuthRequiredError } from '@atproto/xrpc-server'
|
|
7
|
-
|
|
8
|
-
const handler: AlgoHandler = async (
|
|
9
|
-
ctx: AppContext,
|
|
10
|
-
params: SkeletonParams,
|
|
11
|
-
viewer: string | null,
|
|
12
|
-
): Promise<AlgoResponse> => {
|
|
13
|
-
if (!viewer) {
|
|
14
|
-
throw new AuthRequiredError('This feed requires being logged-in')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const { cursor, limit = 50 } = params
|
|
18
|
-
const db = ctx.db.getReplica('feed')
|
|
19
|
-
const feedService = ctx.services.feed(db)
|
|
20
|
-
const { ref } = db.db.dynamic
|
|
21
|
-
|
|
22
|
-
const keyset = new FeedKeyset(ref('post.sortAt'), ref('post.cid'))
|
|
23
|
-
const sortFrom = keyset.unpack(cursor)?.primary
|
|
24
|
-
|
|
25
|
-
let postsQb = feedService
|
|
26
|
-
.selectPostQb()
|
|
27
|
-
.innerJoin('follow', 'follow.subjectDid', 'post.creator')
|
|
28
|
-
.innerJoin('post_agg', 'post_agg.uri', 'post.uri')
|
|
29
|
-
.where('post_agg.likeCount', '>=', 5)
|
|
30
|
-
.where('follow.creator', '=', viewer)
|
|
31
|
-
.where('post.sortAt', '>', getFeedDateThreshold(sortFrom))
|
|
32
|
-
|
|
33
|
-
postsQb = paginate(postsQb, { limit, cursor, keyset, tryIndex: true })
|
|
34
|
-
|
|
35
|
-
const feedItems = await postsQb.execute()
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
feedItems,
|
|
39
|
-
cursor: keyset.packFromResult(feedItems),
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default handler
|