@atproto/bsky 0.0.37 → 0.0.39
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 +21 -0
- package/dist/api/app/bsky/feed/getAuthorFeed.d.ts +2 -3
- package/dist/api/app/bsky/feed/getListFeed.d.ts +2 -2
- package/dist/api/app/bsky/feed/getTimeline.d.ts +4 -2
- package/dist/api/app/bsky/labeler/getServices.d.ts +3 -0
- package/dist/api/util.d.ts +9 -2
- package/dist/auth-verifier.d.ts +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/data-plane/server/db/database-schema.d.ts +2 -2
- package/dist/data-plane/server/db/migrations/20240226T225725627Z-labelers.d.ts +3 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/tables/labeler.d.ts +13 -0
- package/dist/data-plane/server/indexing/index.d.ts +2 -0
- package/dist/data-plane/server/indexing/plugins/labeler.d.ts +10 -0
- package/dist/data-plane/server/util.d.ts +6 -6
- package/dist/hydration/actor.d.ts +3 -0
- package/dist/hydration/hydrator.d.ts +27 -22
- package/dist/hydration/label.d.ts +23 -9
- package/dist/index.js +4100 -4645
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +7 -27
- package/dist/lexicon/lexicons.d.ts +516 -1463
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +23 -1
- package/dist/lexicon/types/app/bsky/embed/record.d.ts +2 -1
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts +3 -0
- package/dist/lexicon/types/app/bsky/labeler/defs.d.ts +41 -0
- package/dist/lexicon/types/{com/atproto/admin/searchRepos.d.ts → app/bsky/labeler/getServices.d.ts} +7 -7
- package/dist/lexicon/types/app/bsky/labeler/service.d.ts +14 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +0 -304
- package/dist/lexicon/types/com/atproto/label/defs.d.ts +23 -0
- package/dist/lexicon/types/com/atproto/server/describeServer.d.ts +7 -0
- package/dist/proto/bsky_connect.d.ts +7 -1
- package/dist/proto/bsky_pb.d.ts +25 -0
- package/dist/util.d.ts +7 -0
- package/dist/views/index.d.ts +3 -0
- package/dist/views/types.d.ts +2 -1
- package/package.json +14 -13
- package/proto/bsky.proto +12 -0
- package/src/api/app/bsky/actor/getProfile.ts +21 -17
- package/src/api/app/bsky/actor/getProfiles.ts +16 -7
- package/src/api/app/bsky/actor/getSuggestions.ts +18 -13
- package/src/api/app/bsky/actor/searchActors.ts +9 -5
- package/src/api/app/bsky/actor/searchActorsTypeahead.ts +12 -5
- package/src/api/app/bsky/feed/getActorFeeds.ts +16 -6
- package/src/api/app/bsky/feed/getActorLikes.ts +18 -8
- package/src/api/app/bsky/feed/getAuthorFeed.ts +18 -19
- package/src/api/app/bsky/feed/getFeed.ts +14 -7
- package/src/api/app/bsky/feed/getFeedGenerator.ts +8 -2
- package/src/api/app/bsky/feed/getFeedGenerators.ts +16 -5
- package/src/api/app/bsky/feed/getLikes.ts +13 -6
- package/src/api/app/bsky/feed/getListFeed.ts +13 -7
- package/src/api/app/bsky/feed/getPostThread.ts +15 -8
- package/src/api/app/bsky/feed/getPosts.ts +14 -5
- package/src/api/app/bsky/feed/getRepostedBy.ts +13 -6
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +8 -2
- package/src/api/app/bsky/feed/getTimeline.ts +14 -8
- package/src/api/app/bsky/feed/searchPosts.ts +9 -5
- package/src/api/app/bsky/graph/getBlocks.ts +10 -9
- package/src/api/app/bsky/graph/getFollowers.ts +23 -15
- package/src/api/app/bsky/graph/getFollows.ts +23 -15
- package/src/api/app/bsky/graph/getList.ts +14 -8
- package/src/api/app/bsky/graph/getListBlocks.ts +10 -7
- package/src/api/app/bsky/graph/getListMutes.ts +10 -7
- package/src/api/app/bsky/graph/getLists.ts +9 -7
- package/src/api/app/bsky/graph/getMutes.ts +10 -8
- package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +10 -7
- package/src/api/app/bsky/graph/muteActor.ts +1 -1
- package/src/api/app/bsky/labeler/getServices.ts +46 -0
- package/src/api/app/bsky/notification/listNotifications.ts +12 -8
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +6 -3
- package/src/api/com/atproto/admin/getAccountInfos.ts +10 -3
- package/src/api/index.ts +2 -0
- package/src/api/util.ts +19 -4
- package/src/auth-verifier.ts +2 -2
- package/src/context.ts +20 -0
- package/src/data-plane/server/db/database-schema.ts +4 -4
- package/src/data-plane/server/db/migrations/20240226T225725627Z-labelers.ts +27 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/labeler.ts +16 -0
- package/src/data-plane/server/indexing/index.ts +4 -0
- package/src/data-plane/server/indexing/plugins/labeler.ts +77 -0
- package/src/data-plane/server/routes/interactions.ts +17 -1
- package/src/data-plane/server/routes/labels.ts +4 -2
- package/src/data-plane/server/routes/profile.ts +15 -1
- package/src/data-plane/server/routes/records.ts +1 -0
- package/src/hydration/actor.ts +6 -0
- package/src/hydration/hydrator.ts +171 -97
- package/src/hydration/label.ts +106 -20
- package/src/index.ts +1 -3
- package/src/lexicon/index.ts +22 -137
- package/src/lexicon/lexicons.ts +552 -1635
- package/src/lexicon/types/app/bsky/actor/defs.ts +57 -1
- package/src/lexicon/types/app/bsky/embed/record.ts +2 -0
- package/src/lexicon/types/app/bsky/feed/defs.ts +1 -0
- package/src/lexicon/types/app/bsky/graph/defs.ts +3 -0
- package/src/lexicon/types/app/bsky/labeler/defs.ts +93 -0
- package/src/lexicon/types/{com/atproto/admin/searchRepos.ts → app/bsky/labeler/getServices.ts} +8 -8
- package/src/lexicon/types/app/bsky/labeler/service.ts +31 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +0 -694
- package/src/lexicon/types/com/atproto/label/defs.ts +78 -0
- package/src/lexicon/types/com/atproto/server/describeServer.ts +18 -0
- package/src/proto/bsky_connect.ts +11 -0
- package/src/proto/bsky_pb.ts +146 -0
- package/src/util.ts +44 -0
- package/src/views/index.ts +77 -8
- package/src/views/types.ts +6 -3
- package/tests/__snapshots__/feed-generation.test.ts.snap +12 -45
- package/tests/_util.ts +21 -0
- package/tests/data-plane/__snapshots__/indexing.test.ts.snap +20 -8
- package/tests/label-hydration.test.ts +162 -0
- package/tests/views/__snapshots__/author-feed.test.ts.snap +0 -46
- package/tests/views/__snapshots__/block-lists.test.ts.snap +7 -17
- package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
- package/tests/views/__snapshots__/labeler-service.test.ts.snap +156 -0
- package/tests/views/__snapshots__/list-feed.test.ts.snap +0 -20
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +10 -18
- package/tests/views/__snapshots__/mutes.test.ts.snap +0 -4
- package/tests/views/__snapshots__/notifications.test.ts.snap +0 -9
- package/tests/views/__snapshots__/posts.test.ts.snap +0 -7
- package/tests/views/__snapshots__/profile.test.ts.snap +40 -6
- package/tests/views/__snapshots__/thread.test.ts.snap +0 -38
- package/tests/views/__snapshots__/threadgating.test.ts.snap +2 -0
- package/tests/views/__snapshots__/timeline.test.ts.snap +0 -145
- package/tests/views/labeler-service.test.ts +156 -0
- package/tests/views/takedown-labels.test.ts +133 -0
- package/tests/views/timeline.test.ts +7 -2
- package/dist/data-plane/server/db/tables/moderation.d.ts +0 -42
- package/dist/lexicon/types/com/atproto/admin/createCommunicationTemplate.d.ts +0 -37
- package/dist/lexicon/types/com/atproto/admin/deleteCommunicationTemplate.d.ts +0 -25
- package/dist/lexicon/types/com/atproto/admin/emitModerationEvent.d.ts +0 -45
- package/dist/lexicon/types/com/atproto/admin/getModerationEvent.d.ts +0 -29
- package/dist/lexicon/types/com/atproto/admin/getRecord.d.ts +0 -31
- package/dist/lexicon/types/com/atproto/admin/getRepo.d.ts +0 -30
- package/dist/lexicon/types/com/atproto/admin/listCommunicationTemplates.d.ts +0 -31
- package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +0 -48
- package/dist/lexicon/types/com/atproto/admin/queryModerationStatuses.d.ts +0 -50
- package/dist/lexicon/types/com/atproto/admin/updateCommunicationTemplate.d.ts +0 -39
- package/src/data-plane/server/db/tables/moderation.ts +0 -59
- package/src/lexicon/types/com/atproto/admin/createCommunicationTemplate.ts +0 -54
- package/src/lexicon/types/com/atproto/admin/deleteCommunicationTemplate.ts +0 -38
- package/src/lexicon/types/com/atproto/admin/emitModerationEvent.ts +0 -67
- package/src/lexicon/types/com/atproto/admin/getModerationEvent.ts +0 -41
- package/src/lexicon/types/com/atproto/admin/getRecord.ts +0 -43
- package/src/lexicon/types/com/atproto/admin/getRepo.ts +0 -42
- package/src/lexicon/types/com/atproto/admin/listCommunicationTemplates.ts +0 -44
- package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +0 -73
- package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +0 -74
- package/src/lexicon/types/com/atproto/admin/updateCommunicationTemplate.ts +0 -57
|
@@ -22,7 +22,13 @@ import {
|
|
|
22
22
|
Lists,
|
|
23
23
|
RelationshipPair,
|
|
24
24
|
} from './graph'
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
LabelHydrator,
|
|
27
|
+
LabelerAggs,
|
|
28
|
+
LabelerViewerStates,
|
|
29
|
+
Labelers,
|
|
30
|
+
Labels,
|
|
31
|
+
} from './label'
|
|
26
32
|
import { HydrationMap, RecordInfo, didFromUri, urisByCollection } from './util'
|
|
27
33
|
import {
|
|
28
34
|
FeedGenAggs,
|
|
@@ -39,9 +45,16 @@ import {
|
|
|
39
45
|
FeedItem,
|
|
40
46
|
ItemRef,
|
|
41
47
|
} from './feed'
|
|
48
|
+
import { ParsedLabelers } from '../util'
|
|
49
|
+
|
|
50
|
+
export type HydrateCtx = {
|
|
51
|
+
labelers: ParsedLabelers
|
|
52
|
+
viewer: string | null
|
|
53
|
+
includeTakedowns?: boolean
|
|
54
|
+
}
|
|
42
55
|
|
|
43
56
|
export type HydrationState = {
|
|
44
|
-
|
|
57
|
+
ctx?: HydrateCtx
|
|
45
58
|
actors?: Actors
|
|
46
59
|
profileViewers?: ProfileViewerStates
|
|
47
60
|
profileAggs?: ProfileAggs
|
|
@@ -61,6 +74,9 @@ export type HydrationState = {
|
|
|
61
74
|
feedgens?: FeedGens
|
|
62
75
|
feedgenViewers?: FeedGenViewerStates
|
|
63
76
|
feedgenAggs?: FeedGenAggs
|
|
77
|
+
labelers?: Labelers
|
|
78
|
+
labelerViewers?: LabelerViewerStates
|
|
79
|
+
labelerAggs?: LabelerAggs
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
export type PostBlock = { embed: boolean; reply: boolean }
|
|
@@ -76,14 +92,11 @@ export class Hydrator {
|
|
|
76
92
|
graph: GraphHydrator
|
|
77
93
|
label: LabelHydrator
|
|
78
94
|
|
|
79
|
-
constructor(
|
|
80
|
-
public dataplane: DataPlaneClient,
|
|
81
|
-
public opts?: { labelsFromIssuerDids?: string[] },
|
|
82
|
-
) {
|
|
95
|
+
constructor(public dataplane: DataPlaneClient) {
|
|
83
96
|
this.actor = new ActorHydrator(dataplane)
|
|
84
97
|
this.feed = new FeedHydrator(dataplane)
|
|
85
98
|
this.graph = new GraphHydrator(dataplane)
|
|
86
|
-
this.label = new LabelHydrator(dataplane
|
|
99
|
+
this.label = new LabelHydrator(dataplane)
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
// app.bsky.actor.defs#profileView
|
|
@@ -92,8 +105,10 @@ export class Hydrator {
|
|
|
92
105
|
// Note: builds on the naive profile viewer hydrator and removes references to lists that have been deleted
|
|
93
106
|
async hydrateProfileViewers(
|
|
94
107
|
dids: string[],
|
|
95
|
-
|
|
108
|
+
ctx: HydrateCtx,
|
|
96
109
|
): Promise<HydrationState> {
|
|
110
|
+
const viewer = ctx.viewer
|
|
111
|
+
if (!viewer) return {}
|
|
97
112
|
const profileViewers = await this.actor.getProfileViewerStatesNaive(
|
|
98
113
|
dids,
|
|
99
114
|
viewer,
|
|
@@ -102,14 +117,14 @@ export class Hydrator {
|
|
|
102
117
|
profileViewers?.forEach((item) => {
|
|
103
118
|
listUris.push(...listUrisFromProfileViewer(item))
|
|
104
119
|
})
|
|
105
|
-
const listState = await this.hydrateListsBasic(listUris,
|
|
120
|
+
const listState = await this.hydrateListsBasic(listUris, ctx)
|
|
106
121
|
// if a list no longer exists or is not a mod list, then remove from viewer state
|
|
107
122
|
profileViewers?.forEach((item) => {
|
|
108
123
|
removeNonModListsFromProfileViewer(item, listState)
|
|
109
124
|
})
|
|
110
125
|
return mergeStates(listState, {
|
|
111
126
|
profileViewers,
|
|
112
|
-
|
|
127
|
+
ctx,
|
|
113
128
|
})
|
|
114
129
|
}
|
|
115
130
|
|
|
@@ -118,18 +133,20 @@ export class Hydrator {
|
|
|
118
133
|
// - list basic
|
|
119
134
|
async hydrateProfiles(
|
|
120
135
|
dids: string[],
|
|
121
|
-
|
|
122
|
-
includeTakedowns = false,
|
|
136
|
+
ctx: HydrateCtx,
|
|
123
137
|
): Promise<HydrationState> {
|
|
124
138
|
const [actors, labels, profileViewersState] = await Promise.all([
|
|
125
|
-
this.actor.getActors(dids, includeTakedowns),
|
|
126
|
-
this.label.getLabelsForSubjects(labelSubjectsForDid(dids)),
|
|
127
|
-
|
|
139
|
+
this.actor.getActors(dids, ctx.includeTakedowns),
|
|
140
|
+
this.label.getLabelsForSubjects(labelSubjectsForDid(dids), ctx.labelers),
|
|
141
|
+
this.hydrateProfileViewers(dids, ctx),
|
|
128
142
|
])
|
|
143
|
+
if (!ctx.includeTakedowns) {
|
|
144
|
+
actionTakedownLabels(dids, actors, labels)
|
|
145
|
+
}
|
|
129
146
|
return mergeStates(profileViewersState ?? {}, {
|
|
130
147
|
actors,
|
|
131
148
|
labels,
|
|
132
|
-
|
|
149
|
+
ctx,
|
|
133
150
|
})
|
|
134
151
|
}
|
|
135
152
|
|
|
@@ -139,10 +156,9 @@ export class Hydrator {
|
|
|
139
156
|
// - list basic
|
|
140
157
|
async hydrateProfilesBasic(
|
|
141
158
|
dids: string[],
|
|
142
|
-
|
|
143
|
-
includeTakedowns = false,
|
|
159
|
+
ctx: HydrateCtx,
|
|
144
160
|
): Promise<HydrationState> {
|
|
145
|
-
return this.hydrateProfiles(dids,
|
|
161
|
+
return this.hydrateProfiles(dids, ctx)
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
// app.bsky.actor.defs#profileViewDetailed
|
|
@@ -151,11 +167,10 @@ export class Hydrator {
|
|
|
151
167
|
// - list basic
|
|
152
168
|
async hydrateProfilesDetailed(
|
|
153
169
|
dids: string[],
|
|
154
|
-
|
|
155
|
-
includeTakedowns = false,
|
|
170
|
+
ctx: HydrateCtx,
|
|
156
171
|
): Promise<HydrationState> {
|
|
157
172
|
const [state, profileAggs] = await Promise.all([
|
|
158
|
-
this.hydrateProfiles(dids,
|
|
173
|
+
this.hydrateProfiles(dids, ctx),
|
|
159
174
|
this.actor.getProfileAggregates(dids),
|
|
160
175
|
])
|
|
161
176
|
return {
|
|
@@ -167,14 +182,12 @@ export class Hydrator {
|
|
|
167
182
|
// app.bsky.graph.defs#listView
|
|
168
183
|
// - list
|
|
169
184
|
// - profile basic
|
|
170
|
-
async hydrateLists(
|
|
171
|
-
uris: string[],
|
|
172
|
-
viewer: string | null,
|
|
173
|
-
): Promise<HydrationState> {
|
|
185
|
+
async hydrateLists(uris: string[], ctx: HydrateCtx): Promise<HydrationState> {
|
|
174
186
|
const [listsState, profilesState] = await Promise.all([
|
|
175
|
-
await this.hydrateListsBasic(uris,
|
|
176
|
-
await this.hydrateProfilesBasic(uris.map(didFromUri),
|
|
187
|
+
await this.hydrateListsBasic(uris, ctx),
|
|
188
|
+
await this.hydrateProfilesBasic(uris.map(didFromUri), ctx),
|
|
177
189
|
])
|
|
190
|
+
|
|
178
191
|
return mergeStates(listsState, profilesState)
|
|
179
192
|
}
|
|
180
193
|
|
|
@@ -182,13 +195,19 @@ export class Hydrator {
|
|
|
182
195
|
// - list basic
|
|
183
196
|
async hydrateListsBasic(
|
|
184
197
|
uris: string[],
|
|
185
|
-
|
|
198
|
+
ctx: HydrateCtx,
|
|
186
199
|
): Promise<HydrationState> {
|
|
187
|
-
const [lists, listViewers] = await Promise.all([
|
|
200
|
+
const [lists, listViewers, labels] = await Promise.all([
|
|
188
201
|
this.graph.getLists(uris),
|
|
189
|
-
viewer ? this.graph.getListViewerStates(uris, viewer) : undefined,
|
|
202
|
+
ctx.viewer ? this.graph.getListViewerStates(uris, ctx.viewer) : undefined,
|
|
203
|
+
this.label.getLabelsForSubjects(uris, ctx.labelers),
|
|
190
204
|
])
|
|
191
|
-
|
|
205
|
+
|
|
206
|
+
if (!ctx.includeTakedowns) {
|
|
207
|
+
actionTakedownLabels(uris, lists, labels)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { lists, listViewers, labels, ctx }
|
|
192
211
|
}
|
|
193
212
|
|
|
194
213
|
// app.bsky.graph.defs#listItemView
|
|
@@ -197,7 +216,7 @@ export class Hydrator {
|
|
|
197
216
|
// - list basic
|
|
198
217
|
async hydrateListItems(
|
|
199
218
|
uris: string[],
|
|
200
|
-
|
|
219
|
+
ctx: HydrateCtx,
|
|
201
220
|
): Promise<HydrationState> {
|
|
202
221
|
const listItems = await this.graph.getListItems(uris)
|
|
203
222
|
const dids: string[] = []
|
|
@@ -206,8 +225,8 @@ export class Hydrator {
|
|
|
206
225
|
dids.push(item.record.subject)
|
|
207
226
|
}
|
|
208
227
|
})
|
|
209
|
-
const profileState = await this.hydrateProfiles(dids,
|
|
210
|
-
return mergeStates(profileState, { listItems,
|
|
228
|
+
const profileState = await this.hydrateProfiles(dids, ctx)
|
|
229
|
+
return mergeStates(profileState, { listItems, ctx })
|
|
211
230
|
}
|
|
212
231
|
|
|
213
232
|
// app.bsky.feed.defs#postView
|
|
@@ -220,16 +239,18 @@ export class Hydrator {
|
|
|
220
239
|
// - feedgen
|
|
221
240
|
// - profile
|
|
222
241
|
// - list basic
|
|
242
|
+
// - mod service
|
|
243
|
+
// - profile
|
|
244
|
+
// - list basic
|
|
223
245
|
async hydratePosts(
|
|
224
246
|
refs: ItemRef[],
|
|
225
|
-
|
|
226
|
-
includeTakedowns = false,
|
|
247
|
+
ctx: HydrateCtx,
|
|
227
248
|
state: HydrationState = {},
|
|
228
249
|
): Promise<HydrationState> {
|
|
229
250
|
const uris = refs.map((ref) => ref.uri)
|
|
230
251
|
const postsLayer0 = await this.feed.getPosts(
|
|
231
252
|
uris,
|
|
232
|
-
includeTakedowns,
|
|
253
|
+
ctx.includeTakedowns,
|
|
233
254
|
state.posts,
|
|
234
255
|
)
|
|
235
256
|
// first level embeds plus thread roots we haven't fetched yet
|
|
@@ -239,7 +260,7 @@ export class Hydrator {
|
|
|
239
260
|
const postUrisLayer1 = urisLayer1ByCollection.get(ids.AppBskyFeedPost) ?? []
|
|
240
261
|
const postsLayer1 = await this.feed.getPosts(
|
|
241
262
|
[...postUrisLayer1, ...additionalRootUris],
|
|
242
|
-
includeTakedowns,
|
|
263
|
+
ctx.includeTakedowns,
|
|
243
264
|
)
|
|
244
265
|
// second level embeds, ignoring any additional root uris we mixed-in to the previous layer
|
|
245
266
|
const urisLayer2 = nestedRecordUrisFromPosts(postsLayer1, postUrisLayer1)
|
|
@@ -252,7 +273,7 @@ export class Hydrator {
|
|
|
252
273
|
}
|
|
253
274
|
}
|
|
254
275
|
const [postsLayer2, threadgates] = await Promise.all([
|
|
255
|
-
this.feed.getPosts(postUrisLayer2, includeTakedowns),
|
|
276
|
+
this.feed.getPosts(postUrisLayer2, ctx.includeTakedowns),
|
|
256
277
|
this.feed.getThreadgatesForPosts([...threadRootUris.values()]),
|
|
257
278
|
])
|
|
258
279
|
// collect list/feedgen embeds, lists in threadgates, post record hydration
|
|
@@ -265,6 +286,10 @@ export class Hydrator {
|
|
|
265
286
|
...(urisLayer1ByCollection.get(ids.AppBskyFeedGenerator) ?? []),
|
|
266
287
|
...(urisLayer2ByCollection.get(ids.AppBskyFeedGenerator) ?? []),
|
|
267
288
|
]
|
|
289
|
+
const nestedLabelerDids = [
|
|
290
|
+
...(urisLayer1ByCollection.get(ids.AppBskyLabelerService) ?? []),
|
|
291
|
+
...(urisLayer2ByCollection.get(ids.AppBskyLabelerService) ?? []),
|
|
292
|
+
].map((uri) => new AtUri(uri).hostname)
|
|
268
293
|
const posts =
|
|
269
294
|
mergeManyMaps(postsLayer0, postsLayer1, postsLayer2) ?? postsLayer0
|
|
270
295
|
const allPostUris = [...posts.keys()]
|
|
@@ -276,29 +301,36 @@ export class Hydrator {
|
|
|
276
301
|
profileState,
|
|
277
302
|
listState,
|
|
278
303
|
feedGenState,
|
|
304
|
+
labelerState,
|
|
279
305
|
] = await Promise.all([
|
|
280
306
|
this.feed.getPostAggregates(refs),
|
|
281
|
-
viewer ? this.feed.getPostViewerStates(refs, viewer) : undefined,
|
|
282
|
-
this.label.getLabelsForSubjects(allPostUris),
|
|
307
|
+
ctx.viewer ? this.feed.getPostViewerStates(refs, ctx.viewer) : undefined,
|
|
308
|
+
this.label.getLabelsForSubjects(allPostUris, ctx.labelers),
|
|
283
309
|
this.hydratePostBlocks(posts),
|
|
284
|
-
this.hydrateProfiles(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
),
|
|
289
|
-
this.hydrateLists([...nestedListUris, ...gateListUris], viewer),
|
|
290
|
-
this.hydrateFeedGens(nestedFeedGenUris, viewer),
|
|
310
|
+
this.hydrateProfiles(allPostUris.map(didFromUri), ctx),
|
|
311
|
+
this.hydrateLists([...nestedListUris, ...gateListUris], ctx),
|
|
312
|
+
this.hydrateFeedGens(nestedFeedGenUris, ctx),
|
|
313
|
+
this.hydrateLabelers(nestedLabelerDids, ctx),
|
|
291
314
|
])
|
|
315
|
+
if (!ctx.includeTakedowns) {
|
|
316
|
+
actionTakedownLabels(allPostUris, posts, labels)
|
|
317
|
+
}
|
|
292
318
|
// combine all hydration state
|
|
293
|
-
return mergeManyStates(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
319
|
+
return mergeManyStates(
|
|
320
|
+
profileState,
|
|
321
|
+
listState,
|
|
322
|
+
feedGenState,
|
|
323
|
+
labelerState,
|
|
324
|
+
{
|
|
325
|
+
posts,
|
|
326
|
+
postAggs,
|
|
327
|
+
postViewers,
|
|
328
|
+
postBlocks,
|
|
329
|
+
labels,
|
|
330
|
+
threadgates,
|
|
331
|
+
ctx,
|
|
332
|
+
},
|
|
333
|
+
)
|
|
302
334
|
}
|
|
303
335
|
|
|
304
336
|
private async hydratePostBlocks(posts: Posts): Promise<PostBlocks> {
|
|
@@ -354,19 +386,14 @@ export class Hydrator {
|
|
|
354
386
|
// - ...
|
|
355
387
|
async hydrateFeedItems(
|
|
356
388
|
items: FeedItem[],
|
|
357
|
-
|
|
358
|
-
includeTakedowns = false,
|
|
389
|
+
ctx: HydrateCtx,
|
|
359
390
|
): Promise<HydrationState> {
|
|
360
391
|
const postUris = items.map((item) => item.post.uri)
|
|
361
392
|
const repostUris = mapDefined(items, (item) => item.repost?.uri)
|
|
362
393
|
const [posts, reposts, repostProfileState] = await Promise.all([
|
|
363
|
-
this.feed.getPosts(postUris, includeTakedowns),
|
|
364
|
-
this.feed.getReposts(repostUris, includeTakedowns),
|
|
365
|
-
this.hydrateProfiles(
|
|
366
|
-
repostUris.map(didFromUri),
|
|
367
|
-
viewer,
|
|
368
|
-
includeTakedowns,
|
|
369
|
-
),
|
|
394
|
+
this.feed.getPosts(postUris, ctx.includeTakedowns),
|
|
395
|
+
this.feed.getReposts(repostUris, ctx.includeTakedowns),
|
|
396
|
+
this.hydrateProfiles(repostUris.map(didFromUri), ctx),
|
|
370
397
|
])
|
|
371
398
|
const postAndReplyRefs: ItemRef[] = []
|
|
372
399
|
posts.forEach((post, uri) => {
|
|
@@ -376,15 +403,10 @@ export class Hydrator {
|
|
|
376
403
|
postAndReplyRefs.push(post.record.reply.root, post.record.reply.parent)
|
|
377
404
|
}
|
|
378
405
|
})
|
|
379
|
-
const postState = await this.hydratePosts(
|
|
380
|
-
postAndReplyRefs,
|
|
381
|
-
viewer,
|
|
382
|
-
includeTakedowns,
|
|
383
|
-
{ posts },
|
|
384
|
-
)
|
|
406
|
+
const postState = await this.hydratePosts(postAndReplyRefs, ctx, { posts })
|
|
385
407
|
return mergeManyStates(postState, repostProfileState, {
|
|
386
408
|
reposts,
|
|
387
|
-
|
|
409
|
+
ctx,
|
|
388
410
|
})
|
|
389
411
|
}
|
|
390
412
|
|
|
@@ -400,9 +422,9 @@ export class Hydrator {
|
|
|
400
422
|
// - list basic
|
|
401
423
|
async hydrateThreadPosts(
|
|
402
424
|
refs: ItemRef[],
|
|
403
|
-
|
|
425
|
+
ctx: HydrateCtx,
|
|
404
426
|
): Promise<HydrationState> {
|
|
405
|
-
return this.hydratePosts(refs,
|
|
427
|
+
return this.hydratePosts(refs, ctx)
|
|
406
428
|
}
|
|
407
429
|
|
|
408
430
|
// app.bsky.feed.defs#generatorView
|
|
@@ -411,20 +433,27 @@ export class Hydrator {
|
|
|
411
433
|
// - list basic
|
|
412
434
|
async hydrateFeedGens(
|
|
413
435
|
uris: string[], // @TODO any way to get refs here?
|
|
414
|
-
|
|
436
|
+
ctx: HydrateCtx,
|
|
415
437
|
): Promise<HydrationState> {
|
|
416
|
-
const [feedgens, feedgenAggs, feedgenViewers, profileState] =
|
|
438
|
+
const [feedgens, feedgenAggs, feedgenViewers, profileState, labels] =
|
|
417
439
|
await Promise.all([
|
|
418
|
-
this.feed.getFeedGens(uris),
|
|
440
|
+
this.feed.getFeedGens(uris, ctx.includeTakedowns),
|
|
419
441
|
this.feed.getFeedGenAggregates(uris.map((uri) => ({ uri }))),
|
|
420
|
-
|
|
421
|
-
|
|
442
|
+
ctx.viewer
|
|
443
|
+
? this.feed.getFeedGenViewerStates(uris, ctx.viewer)
|
|
444
|
+
: undefined,
|
|
445
|
+
this.hydrateProfiles(uris.map(didFromUri), ctx),
|
|
446
|
+
this.label.getLabelsForSubjects(uris, ctx.labelers),
|
|
422
447
|
])
|
|
448
|
+
if (!ctx.includeTakedowns) {
|
|
449
|
+
actionTakedownLabels(uris, feedgens, labels)
|
|
450
|
+
}
|
|
423
451
|
return mergeStates(profileState, {
|
|
424
452
|
feedgens,
|
|
425
453
|
feedgenAggs,
|
|
426
454
|
feedgenViewers,
|
|
427
|
-
|
|
455
|
+
labels,
|
|
456
|
+
ctx,
|
|
428
457
|
})
|
|
429
458
|
}
|
|
430
459
|
|
|
@@ -432,27 +461,24 @@ export class Hydrator {
|
|
|
432
461
|
// - like
|
|
433
462
|
// - profile
|
|
434
463
|
// - list basic
|
|
435
|
-
async hydrateLikes(
|
|
436
|
-
uris: string[],
|
|
437
|
-
viewer: string | null,
|
|
438
|
-
): Promise<HydrationState> {
|
|
464
|
+
async hydrateLikes(uris: string[], ctx: HydrateCtx): Promise<HydrationState> {
|
|
439
465
|
const [likes, profileState] = await Promise.all([
|
|
440
466
|
this.feed.getLikes(uris),
|
|
441
|
-
this.hydrateProfiles(uris.map(didFromUri),
|
|
467
|
+
this.hydrateProfiles(uris.map(didFromUri), ctx),
|
|
442
468
|
])
|
|
443
|
-
return mergeStates(profileState, { likes,
|
|
469
|
+
return mergeStates(profileState, { likes, ctx })
|
|
444
470
|
}
|
|
445
471
|
|
|
446
472
|
// app.bsky.feed.getRepostedBy#repostedBy
|
|
447
473
|
// - repost
|
|
448
474
|
// - profile
|
|
449
475
|
// - list basic
|
|
450
|
-
async hydrateReposts(uris: string[],
|
|
476
|
+
async hydrateReposts(uris: string[], ctx: HydrateCtx) {
|
|
451
477
|
const [reposts, profileState] = await Promise.all([
|
|
452
478
|
this.feed.getReposts(uris),
|
|
453
|
-
this.hydrateProfiles(uris.map(didFromUri),
|
|
479
|
+
this.hydrateProfiles(uris.map(didFromUri), ctx),
|
|
454
480
|
])
|
|
455
|
-
return mergeStates(profileState, { reposts,
|
|
481
|
+
return mergeStates(profileState, { reposts, ctx })
|
|
456
482
|
}
|
|
457
483
|
|
|
458
484
|
// app.bsky.notification.listNotifications#notification
|
|
@@ -461,7 +487,7 @@ export class Hydrator {
|
|
|
461
487
|
// - list basic
|
|
462
488
|
async hydrateNotifications(
|
|
463
489
|
notifs: Notification[],
|
|
464
|
-
|
|
490
|
+
ctx: HydrateCtx,
|
|
465
491
|
): Promise<HydrationState> {
|
|
466
492
|
const uris = notifs.map((notif) => notif.uri)
|
|
467
493
|
const collections = urisByCollection(uris)
|
|
@@ -475,16 +501,17 @@ export class Hydrator {
|
|
|
475
501
|
this.feed.getLikes(likeUris), // reason: like
|
|
476
502
|
this.feed.getReposts(repostUris), // reason: repost
|
|
477
503
|
this.graph.getFollows(followUris), // reason: follow
|
|
478
|
-
this.label.getLabelsForSubjects(uris),
|
|
479
|
-
this.hydrateProfiles(uris.map(didFromUri),
|
|
504
|
+
this.label.getLabelsForSubjects(uris, ctx.labelers),
|
|
505
|
+
this.hydrateProfiles(uris.map(didFromUri), ctx),
|
|
480
506
|
])
|
|
507
|
+
actionTakedownLabels(postUris, posts, labels)
|
|
481
508
|
return mergeStates(profileState, {
|
|
482
509
|
posts,
|
|
483
510
|
likes,
|
|
484
511
|
reposts,
|
|
485
512
|
follows,
|
|
486
513
|
labels,
|
|
487
|
-
|
|
514
|
+
ctx,
|
|
488
515
|
})
|
|
489
516
|
}
|
|
490
517
|
|
|
@@ -512,6 +539,31 @@ export class Hydrator {
|
|
|
512
539
|
return { follows, followBlocks }
|
|
513
540
|
}
|
|
514
541
|
|
|
542
|
+
// app.bsky.labeler.def#labelerViewDetailed
|
|
543
|
+
// - labeler
|
|
544
|
+
// - profile
|
|
545
|
+
// - list basic
|
|
546
|
+
async hydrateLabelers(
|
|
547
|
+
dids: string[],
|
|
548
|
+
ctx: HydrateCtx,
|
|
549
|
+
): Promise<HydrationState> {
|
|
550
|
+
const [labelers, labelerAggs, labelerViewers, profileState] =
|
|
551
|
+
await Promise.all([
|
|
552
|
+
this.label.getLabelers(dids),
|
|
553
|
+
this.label.getLabelerAggregates(dids),
|
|
554
|
+
ctx.viewer
|
|
555
|
+
? this.label.getLabelerViewerStates(dids, ctx.viewer)
|
|
556
|
+
: undefined,
|
|
557
|
+
this.hydrateProfiles(dids.map(didFromUri), ctx),
|
|
558
|
+
])
|
|
559
|
+
return mergeStates(profileState, {
|
|
560
|
+
labelers,
|
|
561
|
+
labelerAggs,
|
|
562
|
+
labelerViewers,
|
|
563
|
+
ctx,
|
|
564
|
+
})
|
|
565
|
+
}
|
|
566
|
+
|
|
515
567
|
// ad-hoc record hydration
|
|
516
568
|
// in com.atproto.repo.getRecord
|
|
517
569
|
async getRecord(
|
|
@@ -560,6 +612,11 @@ export class Hydrator {
|
|
|
560
612
|
(await this.feed.getFeedGens([uri], includeTakedowns)).get(uri) ??
|
|
561
613
|
undefined
|
|
562
614
|
)
|
|
615
|
+
} else if (collection === ids.AppBskyLabelerService) {
|
|
616
|
+
return (
|
|
617
|
+
(await this.label.getLabelers([uri], includeTakedowns)).get(uri) ??
|
|
618
|
+
undefined
|
|
619
|
+
)
|
|
563
620
|
} else if (collection === ids.AppBskyActorProfile) {
|
|
564
621
|
const did = parsed.hostname
|
|
565
622
|
const actor = (await this.actor.getActors([did], includeTakedowns)).get(
|
|
@@ -681,11 +738,13 @@ export const mergeStates = (
|
|
|
681
738
|
stateB: HydrationState,
|
|
682
739
|
): HydrationState => {
|
|
683
740
|
assert(
|
|
684
|
-
!stateA.viewer ||
|
|
741
|
+
!stateA.ctx?.viewer ||
|
|
742
|
+
!stateB.ctx?.viewer ||
|
|
743
|
+
stateA.ctx?.viewer === stateB.ctx?.viewer,
|
|
685
744
|
'incompatible viewers',
|
|
686
745
|
)
|
|
687
746
|
return {
|
|
688
|
-
|
|
747
|
+
ctx: stateA.ctx ?? stateB.ctx,
|
|
689
748
|
actors: mergeMaps(stateA.actors, stateB.actors),
|
|
690
749
|
profileAggs: mergeMaps(stateA.profileAggs, stateB.profileAggs),
|
|
691
750
|
profileViewers: mergeMaps(stateA.profileViewers, stateB.profileViewers),
|
|
@@ -705,6 +764,9 @@ export const mergeStates = (
|
|
|
705
764
|
feedgens: mergeMaps(stateA.feedgens, stateB.feedgens),
|
|
706
765
|
feedgenAggs: mergeMaps(stateA.feedgenAggs, stateB.feedgenAggs),
|
|
707
766
|
feedgenViewers: mergeMaps(stateA.feedgenViewers, stateB.feedgenViewers),
|
|
767
|
+
labelers: mergeMaps(stateA.labelers, stateB.labelers),
|
|
768
|
+
labelerAggs: mergeMaps(stateA.labelerAggs, stateB.labelerAggs),
|
|
769
|
+
labelerViewers: mergeMaps(stateA.labelerViewers, stateB.labelerViewers),
|
|
708
770
|
}
|
|
709
771
|
}
|
|
710
772
|
|
|
@@ -724,3 +786,15 @@ const mergeManyStates = (...states: HydrationState[]) => {
|
|
|
724
786
|
const mergeManyMaps = <T>(...maps: HydrationMap<T>[]) => {
|
|
725
787
|
return maps.reduce(mergeMaps, undefined as HydrationMap<T> | undefined)
|
|
726
788
|
}
|
|
789
|
+
|
|
790
|
+
const actionTakedownLabels = <T>(
|
|
791
|
+
keys: string[],
|
|
792
|
+
hydrationMap: HydrationMap<T>,
|
|
793
|
+
labels: Labels,
|
|
794
|
+
) => {
|
|
795
|
+
for (const key of keys) {
|
|
796
|
+
if (labels.get(key)?.isTakendown) {
|
|
797
|
+
hydrationMap.set(key, null)
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
package/src/hydration/label.ts
CHANGED
|
@@ -1,36 +1,122 @@
|
|
|
1
1
|
import { DataPlaneClient } from '../data-plane/client'
|
|
2
2
|
import { Label } from '../lexicon/types/com/atproto/label/defs'
|
|
3
|
-
import {
|
|
3
|
+
import { Record as LabelerRecord } from '../lexicon/types/app/bsky/labeler/service'
|
|
4
|
+
import {
|
|
5
|
+
HydrationMap,
|
|
6
|
+
RecordInfo,
|
|
7
|
+
parseJsonBytes,
|
|
8
|
+
parseRecord,
|
|
9
|
+
parseString,
|
|
10
|
+
} from './util'
|
|
11
|
+
import { AtUri } from '@atproto/syntax'
|
|
12
|
+
import { ids } from '../lexicon/lexicons'
|
|
13
|
+
import { ParsedLabelers } from '../util'
|
|
4
14
|
|
|
5
15
|
export type { Label } from '../lexicon/types/com/atproto/label/defs'
|
|
6
16
|
|
|
7
|
-
export type
|
|
17
|
+
export type SubjectLabels = {
|
|
18
|
+
isTakendown: boolean
|
|
19
|
+
labels: Label[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Labels = HydrationMap<SubjectLabels>
|
|
23
|
+
|
|
24
|
+
export type LabelerAgg = {
|
|
25
|
+
likes: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type LabelerAggs = HydrationMap<LabelerAgg>
|
|
29
|
+
|
|
30
|
+
export type Labeler = RecordInfo<LabelerRecord>
|
|
31
|
+
export type Labelers = HydrationMap<Labeler>
|
|
32
|
+
|
|
33
|
+
export type LabelerViewerState = {
|
|
34
|
+
like?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type LabelerViewerStates = HydrationMap<LabelerViewerState>
|
|
8
38
|
|
|
9
39
|
export class LabelHydrator {
|
|
10
|
-
constructor(
|
|
11
|
-
public dataplane: DataPlaneClient,
|
|
12
|
-
public opts?: { labelsFromIssuerDids?: string[] },
|
|
13
|
-
) {}
|
|
40
|
+
constructor(public dataplane: DataPlaneClient) {}
|
|
14
41
|
|
|
15
42
|
async getLabelsForSubjects(
|
|
16
43
|
subjects: string[],
|
|
17
|
-
|
|
44
|
+
labelers: ParsedLabelers,
|
|
18
45
|
): Promise<Labels> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
46
|
+
if (!subjects.length || !labelers.dids.length)
|
|
47
|
+
return new HydrationMap<SubjectLabels>()
|
|
48
|
+
const res = await this.dataplane.getLabels({
|
|
49
|
+
subjects,
|
|
50
|
+
issuers: labelers.dids,
|
|
51
|
+
})
|
|
24
52
|
return res.labels.reduce((acc, cur) => {
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
const parsed = parseJsonBytes(cur) as Label | undefined
|
|
54
|
+
if (!parsed || parsed.neg) return acc
|
|
55
|
+
const { sig: _, ...label } = parsed
|
|
56
|
+
let entry = acc.get(label.uri)
|
|
57
|
+
if (!entry) {
|
|
58
|
+
entry = {
|
|
59
|
+
isTakendown: false,
|
|
60
|
+
labels: [],
|
|
61
|
+
}
|
|
62
|
+
acc.set(label.uri, entry)
|
|
63
|
+
}
|
|
64
|
+
entry.labels.push(label)
|
|
65
|
+
if (
|
|
66
|
+
TAKEDOWN_LABELS.includes(label.val) &&
|
|
67
|
+
!label.neg &&
|
|
68
|
+
labelers.redact.has(label.src)
|
|
69
|
+
) {
|
|
70
|
+
entry.isTakendown = true
|
|
32
71
|
}
|
|
33
72
|
return acc
|
|
34
|
-
}, new HydrationMap<
|
|
73
|
+
}, new HydrationMap<SubjectLabels>())
|
|
35
74
|
}
|
|
75
|
+
|
|
76
|
+
async getLabelers(
|
|
77
|
+
dids: string[],
|
|
78
|
+
includeTakedowns = false,
|
|
79
|
+
): Promise<Labelers> {
|
|
80
|
+
const res = await this.dataplane.getLabelerRecords({
|
|
81
|
+
uris: dids.map(labelerDidToUri),
|
|
82
|
+
})
|
|
83
|
+
return dids.reduce((acc, did, i) => {
|
|
84
|
+
const record = parseRecord<LabelerRecord>(
|
|
85
|
+
res.records[i],
|
|
86
|
+
includeTakedowns,
|
|
87
|
+
)
|
|
88
|
+
return acc.set(did, record ?? null)
|
|
89
|
+
}, new HydrationMap<Labeler>())
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getLabelerViewerStates(
|
|
93
|
+
dids: string[],
|
|
94
|
+
viewer: string,
|
|
95
|
+
): Promise<LabelerViewerStates> {
|
|
96
|
+
const likes = await this.dataplane.getLikesByActorAndSubjects({
|
|
97
|
+
actorDid: viewer,
|
|
98
|
+
refs: dids.map((did) => ({ uri: labelerDidToUri(did) })),
|
|
99
|
+
})
|
|
100
|
+
return dids.reduce((acc, did, i) => {
|
|
101
|
+
return acc.set(did, {
|
|
102
|
+
like: parseString(likes.uris[i]),
|
|
103
|
+
})
|
|
104
|
+
}, new HydrationMap<LabelerViewerState>())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async getLabelerAggregates(dids: string[]): Promise<LabelerAggs> {
|
|
108
|
+
const refs = dids.map((did) => ({ uri: labelerDidToUri(did) }))
|
|
109
|
+
const counts = await this.dataplane.getInteractionCounts({ refs })
|
|
110
|
+
return dids.reduce((acc, did, i) => {
|
|
111
|
+
return acc.set(did, {
|
|
112
|
+
likes: counts.likes[i] ?? 0,
|
|
113
|
+
})
|
|
114
|
+
}, new HydrationMap<LabelerAgg>())
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const labelerDidToUri = (did: string): string => {
|
|
119
|
+
return AtUri.make(did, ids.AppBskyLabelerService, 'self').toString()
|
|
36
120
|
}
|
|
121
|
+
|
|
122
|
+
const TAKEDOWN_LABELS = ['!takedown', '!suspend']
|
package/src/index.ts
CHANGED
|
@@ -77,9 +77,7 @@ export class BskyAppView {
|
|
|
77
77
|
httpVersion: config.dataplaneHttpVersion,
|
|
78
78
|
rejectUnauthorized: !config.dataplaneIgnoreBadTls,
|
|
79
79
|
})
|
|
80
|
-
const hydrator = new Hydrator(dataplane
|
|
81
|
-
labelsFromIssuerDids: config.labelsFromIssuerDids,
|
|
82
|
-
})
|
|
80
|
+
const hydrator = new Hydrator(dataplane)
|
|
83
81
|
const views = new Views(imgUriBuilder)
|
|
84
82
|
|
|
85
83
|
const bsyncClient = createBsyncClient({
|