@atproto/bsky 0.0.14 → 0.0.15
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 +11 -0
- package/dist/api/app/bsky/feed/searchPosts.d.ts +3 -0
- package/dist/api/com/atproto/temp/fetchLabels.d.ts +3 -0
- package/dist/config.d.ts +2 -2
- package/dist/context.d.ts +1 -1
- package/dist/index.js +716 -443
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +7 -0
- package/dist/lexicon/lexicons.d.ts +60 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +2 -0
- package/dist/lexicon/types/com/atproto/temp/fetchLabels.d.ts +33 -0
- package/package.json +8 -9
- package/src/api/app/bsky/actor/getSuggestions.ts +45 -21
- package/src/api/app/bsky/feed/getPostThread.ts +16 -4
- package/src/api/app/bsky/feed/searchPosts.ts +127 -0
- package/src/api/com/atproto/admin/reverseModerationAction.ts +3 -3
- package/src/api/com/atproto/admin/takeModerationAction.ts +2 -2
- package/src/api/com/atproto/admin/util.ts +3 -1
- package/src/api/com/atproto/temp/fetchLabels.ts +30 -0
- package/src/api/index.ts +4 -0
- package/src/config.ts +6 -6
- package/src/context.ts +11 -9
- package/src/db/periodic-moderation-action-reversal.ts +1 -9
- package/src/lexicon/index.ts +22 -0
- package/src/lexicon/lexicons.ts +189 -129
- package/src/lexicon/types/app/bsky/actor/defs.ts +2 -2
- package/src/lexicon/types/app/bsky/actor/searchActors.ts +2 -2
- package/src/lexicon/types/app/bsky/actor/searchActorsTypeahead.ts +2 -2
- package/src/lexicon/types/app/bsky/feed/searchPosts.ts +3 -3
- package/src/lexicon/types/app/bsky/graph/defs.ts +2 -2
- package/src/lexicon/types/app/bsky/unspecced/searchActorsSkeleton.ts +4 -4
- package/src/lexicon/types/app/bsky/unspecced/searchPostsSkeleton.ts +3 -3
- package/src/lexicon/types/com/atproto/admin/defs.ts +5 -3
- package/src/lexicon/types/com/atproto/admin/disableAccountInvites.ts +1 -1
- package/src/lexicon/types/com/atproto/admin/enableAccountInvites.ts +1 -1
- package/src/lexicon/types/com/atproto/admin/getModerationReports.ts +3 -3
- package/src/lexicon/types/com/atproto/admin/takeModerationAction.ts +1 -1
- package/src/lexicon/types/com/atproto/label/defs.ts +9 -9
- package/src/lexicon/types/com/atproto/label/queryLabels.ts +2 -2
- package/src/lexicon/types/com/atproto/repo/applyWrites.ts +1 -1
- package/src/lexicon/types/com/atproto/repo/createRecord.ts +2 -2
- package/src/lexicon/types/com/atproto/repo/deleteRecord.ts +2 -2
- package/src/lexicon/types/com/atproto/repo/listRecords.ts +1 -1
- package/src/lexicon/types/com/atproto/repo/putRecord.ts +3 -3
- package/src/lexicon/types/com/atproto/sync/listBlobs.ts +1 -1
- package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +4 -4
- package/src/lexicon/types/com/atproto/temp/fetchLabels.ts +47 -0
- package/tests/admin/get-repo.test.ts +33 -0
- package/tests/views/suggestions.test.ts +15 -7
package/dist/lexicon/index.d.ts
CHANGED
|
@@ -65,6 +65,7 @@ import * as ComAtprotoSyncListRepos from './types/com/atproto/sync/listRepos';
|
|
|
65
65
|
import * as ComAtprotoSyncNotifyOfUpdate from './types/com/atproto/sync/notifyOfUpdate';
|
|
66
66
|
import * as ComAtprotoSyncRequestCrawl from './types/com/atproto/sync/requestCrawl';
|
|
67
67
|
import * as ComAtprotoSyncSubscribeRepos from './types/com/atproto/sync/subscribeRepos';
|
|
68
|
+
import * as ComAtprotoTempFetchLabels from './types/com/atproto/temp/fetchLabels';
|
|
68
69
|
import * as AppBskyActorGetPreferences from './types/app/bsky/actor/getPreferences';
|
|
69
70
|
import * as AppBskyActorGetProfile from './types/app/bsky/actor/getProfile';
|
|
70
71
|
import * as AppBskyActorGetProfiles from './types/app/bsky/actor/getProfiles';
|
|
@@ -149,6 +150,7 @@ export declare class AtprotoNS {
|
|
|
149
150
|
repo: RepoNS;
|
|
150
151
|
server: ServerNS;
|
|
151
152
|
sync: SyncNS;
|
|
153
|
+
temp: TempNS;
|
|
152
154
|
constructor(server: Server);
|
|
153
155
|
}
|
|
154
156
|
export declare class AdminNS {
|
|
@@ -245,6 +247,11 @@ export declare class SyncNS {
|
|
|
245
247
|
requestCrawl<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoSyncRequestCrawl.Handler<ExtractAuth<AV>>, ComAtprotoSyncRequestCrawl.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
246
248
|
subscribeRepos<AV extends StreamAuthVerifier>(cfg: ConfigOf<AV, ComAtprotoSyncSubscribeRepos.Handler<ExtractAuth<AV>>, ComAtprotoSyncSubscribeRepos.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
247
249
|
}
|
|
250
|
+
export declare class TempNS {
|
|
251
|
+
_server: Server;
|
|
252
|
+
constructor(server: Server);
|
|
253
|
+
fetchLabels<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoTempFetchLabels.Handler<ExtractAuth<AV>>, ComAtprotoTempFetchLabels.HandlerReqCtx<ExtractAuth<AV>>>): void;
|
|
254
|
+
}
|
|
248
255
|
export declare class AppNS {
|
|
249
256
|
_server: Server;
|
|
250
257
|
bsky: BskyNS;
|
|
@@ -358,6 +358,10 @@ export declare const schemaDict: {
|
|
|
358
358
|
inviteNote: {
|
|
359
359
|
type: string;
|
|
360
360
|
};
|
|
361
|
+
emailConfirmedAt: {
|
|
362
|
+
type: string;
|
|
363
|
+
format: string;
|
|
364
|
+
};
|
|
361
365
|
};
|
|
362
366
|
};
|
|
363
367
|
accountView: {
|
|
@@ -393,6 +397,10 @@ export declare const schemaDict: {
|
|
|
393
397
|
invitesDisabled: {
|
|
394
398
|
type: string;
|
|
395
399
|
};
|
|
400
|
+
emailConfirmedAt: {
|
|
401
|
+
type: string;
|
|
402
|
+
format: string;
|
|
403
|
+
};
|
|
396
404
|
inviteNote: {
|
|
397
405
|
type: string;
|
|
398
406
|
};
|
|
@@ -3585,6 +3593,46 @@ export declare const schemaDict: {
|
|
|
3585
3593
|
};
|
|
3586
3594
|
};
|
|
3587
3595
|
};
|
|
3596
|
+
ComAtprotoTempFetchLabels: {
|
|
3597
|
+
lexicon: number;
|
|
3598
|
+
id: string;
|
|
3599
|
+
defs: {
|
|
3600
|
+
main: {
|
|
3601
|
+
type: string;
|
|
3602
|
+
description: string;
|
|
3603
|
+
parameters: {
|
|
3604
|
+
type: string;
|
|
3605
|
+
properties: {
|
|
3606
|
+
since: {
|
|
3607
|
+
type: string;
|
|
3608
|
+
};
|
|
3609
|
+
limit: {
|
|
3610
|
+
type: string;
|
|
3611
|
+
minimum: number;
|
|
3612
|
+
maximum: number;
|
|
3613
|
+
default: number;
|
|
3614
|
+
};
|
|
3615
|
+
};
|
|
3616
|
+
};
|
|
3617
|
+
output: {
|
|
3618
|
+
encoding: string;
|
|
3619
|
+
schema: {
|
|
3620
|
+
type: string;
|
|
3621
|
+
required: string[];
|
|
3622
|
+
properties: {
|
|
3623
|
+
labels: {
|
|
3624
|
+
type: string;
|
|
3625
|
+
items: {
|
|
3626
|
+
type: string;
|
|
3627
|
+
ref: string;
|
|
3628
|
+
};
|
|
3629
|
+
};
|
|
3630
|
+
};
|
|
3631
|
+
};
|
|
3632
|
+
};
|
|
3633
|
+
};
|
|
3634
|
+
};
|
|
3635
|
+
};
|
|
3588
3636
|
AppBskyActorDefs: {
|
|
3589
3637
|
lexicon: number;
|
|
3590
3638
|
id: string;
|
|
@@ -3889,6 +3937,7 @@ export declare const schemaDict: {
|
|
|
3889
3937
|
defs: {
|
|
3890
3938
|
main: {
|
|
3891
3939
|
type: string;
|
|
3940
|
+
description: string;
|
|
3892
3941
|
parameters: {
|
|
3893
3942
|
type: string;
|
|
3894
3943
|
required: string[];
|
|
@@ -3915,6 +3964,7 @@ export declare const schemaDict: {
|
|
|
3915
3964
|
defs: {
|
|
3916
3965
|
main: {
|
|
3917
3966
|
type: string;
|
|
3967
|
+
description: string;
|
|
3918
3968
|
parameters: {
|
|
3919
3969
|
type: string;
|
|
3920
3970
|
required: string[];
|
|
@@ -3997,6 +4047,7 @@ export declare const schemaDict: {
|
|
|
3997
4047
|
defs: {
|
|
3998
4048
|
main: {
|
|
3999
4049
|
type: string;
|
|
4050
|
+
description: string;
|
|
4000
4051
|
key: string;
|
|
4001
4052
|
record: {
|
|
4002
4053
|
type: string;
|
|
@@ -5188,6 +5239,7 @@ export declare const schemaDict: {
|
|
|
5188
5239
|
defs: {
|
|
5189
5240
|
main: {
|
|
5190
5241
|
type: string;
|
|
5242
|
+
description: string;
|
|
5191
5243
|
parameters: {
|
|
5192
5244
|
type: string;
|
|
5193
5245
|
required: string[];
|
|
@@ -5316,6 +5368,7 @@ export declare const schemaDict: {
|
|
|
5316
5368
|
defs: {
|
|
5317
5369
|
main: {
|
|
5318
5370
|
type: string;
|
|
5371
|
+
description: string;
|
|
5319
5372
|
parameters: {
|
|
5320
5373
|
type: string;
|
|
5321
5374
|
required: string[];
|
|
@@ -5403,6 +5456,7 @@ export declare const schemaDict: {
|
|
|
5403
5456
|
defs: {
|
|
5404
5457
|
main: {
|
|
5405
5458
|
type: string;
|
|
5459
|
+
description: string;
|
|
5406
5460
|
parameters: {
|
|
5407
5461
|
type: string;
|
|
5408
5462
|
required: string[];
|
|
@@ -5551,6 +5605,7 @@ export declare const schemaDict: {
|
|
|
5551
5605
|
defs: {
|
|
5552
5606
|
main: {
|
|
5553
5607
|
type: string;
|
|
5608
|
+
description: string;
|
|
5554
5609
|
key: string;
|
|
5555
5610
|
record: {
|
|
5556
5611
|
type: string;
|
|
@@ -5575,6 +5630,7 @@ export declare const schemaDict: {
|
|
|
5575
5630
|
defs: {
|
|
5576
5631
|
main: {
|
|
5577
5632
|
type: string;
|
|
5633
|
+
description: string;
|
|
5578
5634
|
key: string;
|
|
5579
5635
|
record: {
|
|
5580
5636
|
type: string;
|
|
@@ -5691,6 +5747,7 @@ export declare const schemaDict: {
|
|
|
5691
5747
|
id: string;
|
|
5692
5748
|
defs: {
|
|
5693
5749
|
main: {
|
|
5750
|
+
description: string;
|
|
5694
5751
|
type: string;
|
|
5695
5752
|
key: string;
|
|
5696
5753
|
record: {
|
|
@@ -6609,6 +6666,7 @@ export declare const schemaDict: {
|
|
|
6609
6666
|
defs: {
|
|
6610
6667
|
main: {
|
|
6611
6668
|
type: string;
|
|
6669
|
+
description: string;
|
|
6612
6670
|
parameters: {
|
|
6613
6671
|
type: string;
|
|
6614
6672
|
properties: {
|
|
@@ -6639,6 +6697,7 @@ export declare const schemaDict: {
|
|
|
6639
6697
|
defs: {
|
|
6640
6698
|
main: {
|
|
6641
6699
|
type: string;
|
|
6700
|
+
description: string;
|
|
6642
6701
|
parameters: {
|
|
6643
6702
|
type: string;
|
|
6644
6703
|
properties: {
|
|
@@ -7207,6 +7266,7 @@ export declare const ids: {
|
|
|
7207
7266
|
ComAtprotoSyncNotifyOfUpdate: string;
|
|
7208
7267
|
ComAtprotoSyncRequestCrawl: string;
|
|
7209
7268
|
ComAtprotoSyncSubscribeRepos: string;
|
|
7269
|
+
ComAtprotoTempFetchLabels: string;
|
|
7210
7270
|
AppBskyActorDefs: string;
|
|
7211
7271
|
AppBskyActorGetPreferences: string;
|
|
7212
7272
|
AppBskyActorGetProfile: string;
|
|
@@ -128,6 +128,7 @@ export interface RepoViewDetail {
|
|
|
128
128
|
invites?: ComAtprotoServerDefs.InviteCode[];
|
|
129
129
|
invitesDisabled?: boolean;
|
|
130
130
|
inviteNote?: string;
|
|
131
|
+
emailConfirmedAt?: string;
|
|
131
132
|
[k: string]: unknown;
|
|
132
133
|
}
|
|
133
134
|
export declare function isRepoViewDetail(v: unknown): v is RepoViewDetail;
|
|
@@ -140,6 +141,7 @@ export interface AccountView {
|
|
|
140
141
|
invitedBy?: ComAtprotoServerDefs.InviteCode;
|
|
141
142
|
invites?: ComAtprotoServerDefs.InviteCode[];
|
|
142
143
|
invitesDisabled?: boolean;
|
|
144
|
+
emailConfirmedAt?: string;
|
|
143
145
|
inviteNote?: string;
|
|
144
146
|
[k: string]: unknown;
|
|
145
147
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { HandlerAuth } from '@atproto/xrpc-server';
|
|
3
|
+
import * as ComAtprotoLabelDefs from '../label/defs';
|
|
4
|
+
export interface QueryParams {
|
|
5
|
+
since?: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
}
|
|
8
|
+
export declare type InputSchema = undefined;
|
|
9
|
+
export interface OutputSchema {
|
|
10
|
+
labels: ComAtprotoLabelDefs.Label[];
|
|
11
|
+
[k: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
export declare type HandlerInput = undefined;
|
|
14
|
+
export interface HandlerSuccess {
|
|
15
|
+
encoding: 'application/json';
|
|
16
|
+
body: OutputSchema;
|
|
17
|
+
headers?: {
|
|
18
|
+
[key: string]: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface HandlerError {
|
|
22
|
+
status: number;
|
|
23
|
+
message?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare type HandlerOutput = HandlerError | HandlerSuccess;
|
|
26
|
+
export declare type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
|
27
|
+
auth: HA;
|
|
28
|
+
params: QueryParams;
|
|
29
|
+
input: HandlerInput;
|
|
30
|
+
req: express.Request;
|
|
31
|
+
res: express.Response;
|
|
32
|
+
};
|
|
33
|
+
export declare type Handler<HA extends HandlerAuth = never> = (ctx: HandlerReqCtx<HA>) => Promise<HandlerOutput> | HandlerOutput;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
"pg": "^8.10.0",
|
|
33
33
|
"pino": "^8.15.0",
|
|
34
34
|
"pino-http": "^8.2.1",
|
|
35
|
-
"sharp": "^0.
|
|
35
|
+
"sharp": "^0.32.6",
|
|
36
36
|
"typed-emitter": "^2.1.0",
|
|
37
37
|
"uint8arrays": "3.0.0",
|
|
38
38
|
"@atproto/api": "^0.6.23",
|
|
39
39
|
"@atproto/common": "^0.3.3",
|
|
40
|
-
"@atproto/crypto": "^0.
|
|
40
|
+
"@atproto/crypto": "^0.3.0",
|
|
41
41
|
"@atproto/syntax": "^0.1.4",
|
|
42
|
-
"@atproto/identity": "^0.3.
|
|
42
|
+
"@atproto/identity": "^0.3.2",
|
|
43
43
|
"@atproto/lexicon": "^0.3.0",
|
|
44
|
-
"@atproto/repo": "^0.3.
|
|
45
|
-
"@atproto/xrpc-server": "^0.4.
|
|
44
|
+
"@atproto/repo": "^0.3.5",
|
|
45
|
+
"@atproto/xrpc-server": "^0.4.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@did-plc/server": "^0.0.1",
|
|
@@ -51,12 +51,11 @@
|
|
|
51
51
|
"@types/express-serve-static-core": "^4.17.36",
|
|
52
52
|
"@types/pg": "^8.6.6",
|
|
53
53
|
"@types/qs": "^6.9.7",
|
|
54
|
-
"@types/sharp": "^0.31.0",
|
|
55
54
|
"axios": "^0.27.2",
|
|
56
55
|
"@atproto/api": "^0.6.23",
|
|
57
|
-
"@atproto/dev-env": "^0.2.
|
|
56
|
+
"@atproto/dev-env": "^0.2.15",
|
|
58
57
|
"@atproto/lex-cli": "^0.2.4",
|
|
59
|
-
"@atproto/pds": "^0.3.
|
|
58
|
+
"@atproto/pds": "^0.3.3",
|
|
60
59
|
"@atproto/xrpc": "^0.4.0"
|
|
61
60
|
},
|
|
62
61
|
"scripts": {
|
|
@@ -42,12 +42,12 @@ const skeleton = async (
|
|
|
42
42
|
ctx: Context,
|
|
43
43
|
): Promise<SkeletonState> => {
|
|
44
44
|
const { db } = ctx
|
|
45
|
-
const {
|
|
45
|
+
const { viewer } = params
|
|
46
|
+
const alreadyIncluded = parseCursor(params.cursor)
|
|
46
47
|
const { ref } = db.db.dynamic
|
|
47
|
-
|
|
48
|
+
const suggestions = await db.db
|
|
48
49
|
.selectFrom('suggested_follow')
|
|
49
50
|
.innerJoin('actor', 'actor.did', 'suggested_follow.did')
|
|
50
|
-
.innerJoin('profile_agg', 'profile_agg.did', 'actor.did')
|
|
51
51
|
.where(notSoftDeletedClause(ref('actor')))
|
|
52
52
|
.where('suggested_follow.did', '!=', viewer ?? '')
|
|
53
53
|
.whereNotExists((qb) =>
|
|
@@ -57,27 +57,30 @@ const skeleton = async (
|
|
|
57
57
|
.where('creator', '=', viewer ?? '')
|
|
58
58
|
.whereRef('subjectDid', '=', ref('actor.did')),
|
|
59
59
|
)
|
|
60
|
+
.if(alreadyIncluded.length > 0, (qb) =>
|
|
61
|
+
qb.where('suggested_follow.order', 'not in', alreadyIncluded),
|
|
62
|
+
)
|
|
60
63
|
.selectAll()
|
|
61
|
-
.select('profile_agg.postsCount as postsCount')
|
|
62
|
-
.limit(limit)
|
|
63
64
|
.orderBy('suggested_follow.order', 'asc')
|
|
65
|
+
.execute()
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
// always include first two
|
|
68
|
+
const firstTwo = suggestions.filter(
|
|
69
|
+
(row) => row.order === 1 || row.order === 2,
|
|
70
|
+
)
|
|
71
|
+
const rest = suggestions.filter((row) => row.order !== 1 && row.order !== 2)
|
|
72
|
+
const limited = firstTwo.concat(shuffle(rest)).slice(0, params.limit)
|
|
73
|
+
|
|
74
|
+
// if the result set ends up getting larger, consider using a seed included in the cursor for for the randomized shuffle
|
|
75
|
+
const cursor =
|
|
76
|
+
limited.length > 0
|
|
77
|
+
? limited
|
|
78
|
+
.map((row) => row.order.toString())
|
|
79
|
+
.concat(alreadyIncluded.map((id) => id.toString()))
|
|
80
|
+
.join(':')
|
|
81
|
+
: undefined
|
|
82
|
+
|
|
83
|
+
return { params, suggestions: limited, cursor }
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
const hydration = async (state: SkeletonState, ctx: Context) => {
|
|
@@ -110,6 +113,27 @@ const presentation = (state: HydrationState) => {
|
|
|
110
113
|
return { actors: suggestedActors, cursor }
|
|
111
114
|
}
|
|
112
115
|
|
|
116
|
+
const parseCursor = (cursor?: string): number[] => {
|
|
117
|
+
if (!cursor) {
|
|
118
|
+
return []
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
return cursor
|
|
122
|
+
.split(':')
|
|
123
|
+
.map((id) => parseInt(id, 10))
|
|
124
|
+
.filter((id) => !isNaN(id))
|
|
125
|
+
} catch {
|
|
126
|
+
return []
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const shuffle = <T>(arr: T[]): T[] => {
|
|
131
|
+
return arr
|
|
132
|
+
.map((value) => ({ value, sort: Math.random() }))
|
|
133
|
+
.sort((a, b) => a.sort - b.sort)
|
|
134
|
+
.map(({ value }) => value)
|
|
135
|
+
}
|
|
136
|
+
|
|
113
137
|
type Context = {
|
|
114
138
|
db: Database
|
|
115
139
|
actorService: ActorService
|
|
@@ -255,11 +255,15 @@ const getThreadData = async (
|
|
|
255
255
|
.orderBy('sortAt', 'desc')
|
|
256
256
|
.execute(),
|
|
257
257
|
])
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
// prevent self-referential loops
|
|
259
|
+
const includedPosts = new Set<string>([uri])
|
|
260
|
+
const parentsByUri = parents.reduce((acc, post) => {
|
|
261
|
+
return Object.assign(acc, { [post.uri]: post })
|
|
260
262
|
}, {} as Record<string, FeedRow>)
|
|
261
263
|
const childrenByParentUri = children.reduce((acc, child) => {
|
|
262
264
|
if (!child.replyParent) return acc
|
|
265
|
+
if (includedPosts.has(child.uri)) return acc
|
|
266
|
+
includedPosts.add(child.uri)
|
|
263
267
|
acc[child.replyParent] ??= []
|
|
264
268
|
acc[child.replyParent].push(child)
|
|
265
269
|
return acc
|
|
@@ -269,7 +273,12 @@ const getThreadData = async (
|
|
|
269
273
|
return {
|
|
270
274
|
post,
|
|
271
275
|
parent: post.replyParent
|
|
272
|
-
? getParentData(
|
|
276
|
+
? getParentData(
|
|
277
|
+
parentsByUri,
|
|
278
|
+
includedPosts,
|
|
279
|
+
post.replyParent,
|
|
280
|
+
parentHeight,
|
|
281
|
+
)
|
|
273
282
|
: undefined,
|
|
274
283
|
replies: getChildrenData(childrenByParentUri, uri, depth),
|
|
275
284
|
}
|
|
@@ -277,16 +286,19 @@ const getThreadData = async (
|
|
|
277
286
|
|
|
278
287
|
const getParentData = (
|
|
279
288
|
postsByUri: Record<string, FeedRow>,
|
|
289
|
+
includedPosts: Set<string>,
|
|
280
290
|
uri: string,
|
|
281
291
|
depth: number,
|
|
282
292
|
): PostThread | ParentNotFoundError | undefined => {
|
|
283
293
|
if (depth < 1) return undefined
|
|
294
|
+
if (includedPosts.has(uri)) return undefined
|
|
295
|
+
includedPosts.add(uri)
|
|
284
296
|
const post = postsByUri[uri]
|
|
285
297
|
if (!post) return new ParentNotFoundError(uri)
|
|
286
298
|
return {
|
|
287
299
|
post,
|
|
288
300
|
parent: post.replyParent
|
|
289
|
-
? getParentData(postsByUri, post.replyParent, depth - 1)
|
|
301
|
+
? getParentData(postsByUri, includedPosts, post.replyParent, depth - 1)
|
|
290
302
|
: undefined,
|
|
291
303
|
replies: [],
|
|
292
304
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import AppContext from '../../../../context'
|
|
2
|
+
import { Server } from '../../../../lexicon'
|
|
3
|
+
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
4
|
+
import AtpAgent from '@atproto/api'
|
|
5
|
+
import { AtUri } from '@atproto/syntax'
|
|
6
|
+
import { mapDefined } from '@atproto/common'
|
|
7
|
+
import { QueryParams } from '../../../../lexicon/types/app/bsky/feed/searchPosts'
|
|
8
|
+
import { Database } from '../../../../db'
|
|
9
|
+
import { FeedHydrationState, FeedService } from '../../../../services/feed'
|
|
10
|
+
import { ActorService } from '../../../../services/actor'
|
|
11
|
+
import { createPipeline } from '../../../../pipeline'
|
|
12
|
+
|
|
13
|
+
export default function (server: Server, ctx: AppContext) {
|
|
14
|
+
const searchPosts = createPipeline(
|
|
15
|
+
skeleton,
|
|
16
|
+
hydration,
|
|
17
|
+
noBlocks,
|
|
18
|
+
presentation,
|
|
19
|
+
)
|
|
20
|
+
server.app.bsky.feed.searchPosts({
|
|
21
|
+
auth: ctx.authOptionalVerifier,
|
|
22
|
+
handler: async ({ auth, params }) => {
|
|
23
|
+
const viewer = auth.credentials.did
|
|
24
|
+
const db = ctx.db.getReplica('search')
|
|
25
|
+
const feedService = ctx.services.feed(db)
|
|
26
|
+
const actorService = ctx.services.actor(db)
|
|
27
|
+
const searchAgent = ctx.searchAgent
|
|
28
|
+
if (!searchAgent) {
|
|
29
|
+
throw new InvalidRequestError('Search not available')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const results = await searchPosts(
|
|
33
|
+
{ ...params, viewer },
|
|
34
|
+
{ db, feedService, actorService, searchAgent },
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
encoding: 'application/json',
|
|
39
|
+
body: results,
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const skeleton = async (
|
|
46
|
+
params: Params,
|
|
47
|
+
ctx: Context,
|
|
48
|
+
): Promise<SkeletonState> => {
|
|
49
|
+
const res = await ctx.searchAgent.api.app.bsky.unspecced.searchPostsSkeleton({
|
|
50
|
+
q: params.q,
|
|
51
|
+
cursor: params.cursor,
|
|
52
|
+
limit: params.limit,
|
|
53
|
+
})
|
|
54
|
+
return {
|
|
55
|
+
params,
|
|
56
|
+
postUris: res.data.posts.map((a) => a.uri),
|
|
57
|
+
cursor: res.data.cursor,
|
|
58
|
+
hitsTotal: res.data.hitsTotal,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const hydration = async (
|
|
63
|
+
state: SkeletonState,
|
|
64
|
+
ctx: Context,
|
|
65
|
+
): Promise<HydrationState> => {
|
|
66
|
+
const { feedService } = ctx
|
|
67
|
+
const { params, postUris } = state
|
|
68
|
+
const uris = new Set<string>(postUris)
|
|
69
|
+
const dids = new Set<string>(postUris.map((uri) => new AtUri(uri).hostname))
|
|
70
|
+
const hydrated = await feedService.feedHydration({
|
|
71
|
+
uris,
|
|
72
|
+
dids,
|
|
73
|
+
viewer: params.viewer,
|
|
74
|
+
})
|
|
75
|
+
return { ...state, ...hydrated }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const noBlocks = (state: HydrationState): HydrationState => {
|
|
79
|
+
const { viewer } = state.params
|
|
80
|
+
state.postUris = state.postUris.filter((uri) => {
|
|
81
|
+
const post = state.posts[uri]
|
|
82
|
+
if (!viewer || !post) return true
|
|
83
|
+
return !state.bam.block([viewer, post.creator])
|
|
84
|
+
})
|
|
85
|
+
return state
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const presentation = (state: HydrationState, ctx: Context) => {
|
|
89
|
+
const { feedService, actorService } = ctx
|
|
90
|
+
const { postUris, profiles, params } = state
|
|
91
|
+
const actors = actorService.views.profileBasicPresentation(
|
|
92
|
+
Object.keys(profiles),
|
|
93
|
+
state,
|
|
94
|
+
{ viewer: params.viewer },
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const postViews = mapDefined(postUris, (uri) =>
|
|
98
|
+
feedService.views.formatPostView(
|
|
99
|
+
uri,
|
|
100
|
+
actors,
|
|
101
|
+
state.posts,
|
|
102
|
+
state.threadgates,
|
|
103
|
+
state.embeds,
|
|
104
|
+
state.labels,
|
|
105
|
+
state.lists,
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
return { posts: postViews, cursor: state.cursor, hitsTotal: state.hitsTotal }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type Context = {
|
|
112
|
+
db: Database
|
|
113
|
+
feedService: FeedService
|
|
114
|
+
actorService: ActorService
|
|
115
|
+
searchAgent: AtpAgent
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type Params = QueryParams & { viewer: string | null }
|
|
119
|
+
|
|
120
|
+
type SkeletonState = {
|
|
121
|
+
params: Params
|
|
122
|
+
postUris: string[]
|
|
123
|
+
hitsTotal?: number
|
|
124
|
+
cursor?: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
type HydrationState = SkeletonState & FeedHydrationState
|
|
@@ -85,9 +85,9 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
85
85
|
return { result, restored }
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
if (restored) {
|
|
89
|
-
const
|
|
90
|
-
const
|
|
88
|
+
if (restored && ctx.moderationPushAgent) {
|
|
89
|
+
const agent = ctx.moderationPushAgent
|
|
90
|
+
const { subjects } = restored
|
|
91
91
|
const results = await Promise.allSettled(
|
|
92
92
|
subjects.map((subject) =>
|
|
93
93
|
retryHttp(() =>
|
|
@@ -111,10 +111,10 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
111
111
|
return { result, takenDown }
|
|
112
112
|
})
|
|
113
113
|
|
|
114
|
-
if (takenDown) {
|
|
114
|
+
if (takenDown && ctx.moderationPushAgent) {
|
|
115
|
+
const agent = ctx.moderationPushAgent
|
|
115
116
|
const { did, subjects } = takenDown
|
|
116
117
|
if (did && subjects.length > 0) {
|
|
117
|
-
const agent = await ctx.pdsAdminAgent(did)
|
|
118
118
|
const results = await Promise.allSettled(
|
|
119
119
|
subjects.map((subject) =>
|
|
120
120
|
retryHttp(() =>
|
|
@@ -9,8 +9,9 @@ export const getPdsAccountInfo = async (
|
|
|
9
9
|
ctx: AppContext,
|
|
10
10
|
did: string,
|
|
11
11
|
): Promise<AccountView | null> => {
|
|
12
|
+
const agent = ctx.moderationPushAgent
|
|
13
|
+
if (!agent) return null
|
|
12
14
|
try {
|
|
13
|
-
const agent = await ctx.pdsAdminAgent(did)
|
|
14
15
|
const res = await agent.api.com.atproto.admin.getAccountInfo({ did })
|
|
15
16
|
return res.data
|
|
16
17
|
} catch (err) {
|
|
@@ -31,6 +32,7 @@ export const addAccountInfoToRepoViewDetail = (
|
|
|
31
32
|
invitesDisabled: accountInfo.invitesDisabled,
|
|
32
33
|
inviteNote: accountInfo.inviteNote,
|
|
33
34
|
invites: accountInfo.invites,
|
|
35
|
+
emailConfirmedAt: accountInfo.emailConfirmedAt,
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Server } from '../../../../lexicon'
|
|
2
|
+
import AppContext from '../../../../context'
|
|
3
|
+
|
|
4
|
+
export default function (server: Server, ctx: AppContext) {
|
|
5
|
+
server.com.atproto.temp.fetchLabels(async ({ params }) => {
|
|
6
|
+
const { limit } = params
|
|
7
|
+
const db = ctx.db.getReplica()
|
|
8
|
+
const since =
|
|
9
|
+
params.since !== undefined ? new Date(params.since).toISOString() : ''
|
|
10
|
+
const labelRes = await db.db
|
|
11
|
+
.selectFrom('label')
|
|
12
|
+
.selectAll()
|
|
13
|
+
.orderBy('label.cts', 'asc')
|
|
14
|
+
.where('cts', '>', since)
|
|
15
|
+
.limit(limit)
|
|
16
|
+
.execute()
|
|
17
|
+
|
|
18
|
+
const labels = labelRes.map((l) => ({
|
|
19
|
+
...l,
|
|
20
|
+
cid: l.cid === '' ? undefined : l.cid,
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
encoding: 'application/json',
|
|
25
|
+
body: {
|
|
26
|
+
labels,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
}
|
package/src/api/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import getLikes from './app/bsky/feed/getLikes'
|
|
|
13
13
|
import getListFeed from './app/bsky/feed/getListFeed'
|
|
14
14
|
import getPostThread from './app/bsky/feed/getPostThread'
|
|
15
15
|
import getPosts from './app/bsky/feed/getPosts'
|
|
16
|
+
import searchPosts from './app/bsky/feed/searchPosts'
|
|
16
17
|
import getActorLikes from './app/bsky/feed/getActorLikes'
|
|
17
18
|
import getProfile from './app/bsky/actor/getProfile'
|
|
18
19
|
import getProfiles from './app/bsky/actor/getProfiles'
|
|
@@ -52,6 +53,7 @@ import getModerationReport from './com/atproto/admin/getModerationReport'
|
|
|
52
53
|
import getModerationReports from './com/atproto/admin/getModerationReports'
|
|
53
54
|
import resolveHandle from './com/atproto/identity/resolveHandle'
|
|
54
55
|
import getRecord from './com/atproto/repo/getRecord'
|
|
56
|
+
import fetchLabels from './com/atproto/temp/fetchLabels'
|
|
55
57
|
|
|
56
58
|
export * as health from './health'
|
|
57
59
|
|
|
@@ -74,6 +76,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
74
76
|
getListFeed(server, ctx)
|
|
75
77
|
getPostThread(server, ctx)
|
|
76
78
|
getPosts(server, ctx)
|
|
79
|
+
searchPosts(server, ctx)
|
|
77
80
|
getActorLikes(server, ctx)
|
|
78
81
|
getProfile(server, ctx)
|
|
79
82
|
getProfiles(server, ctx)
|
|
@@ -114,5 +117,6 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
114
117
|
getModerationReports(server, ctx)
|
|
115
118
|
resolveHandle(server, ctx)
|
|
116
119
|
getRecord(server, ctx)
|
|
120
|
+
fetchLabels(server, ctx)
|
|
117
121
|
return server
|
|
118
122
|
}
|