@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
|
@@ -2,7 +2,7 @@ import { mapDefined } from '@atproto/common'
|
|
|
2
2
|
import { Server } from '../../../../lexicon'
|
|
3
3
|
import AppContext from '../../../../context'
|
|
4
4
|
import { parseString } from '../../../../hydration/util'
|
|
5
|
-
import { clearlyBadCursor } from '../../../util'
|
|
5
|
+
import { clearlyBadCursor, resHeaders } from '../../../util'
|
|
6
6
|
|
|
7
7
|
// THIS IS A TEMPORARY UNSPECCED ROUTE
|
|
8
8
|
// @TODO currently mirrors getSuggestedFeeds and ignores the "query" param.
|
|
@@ -10,8 +10,10 @@ import { clearlyBadCursor } from '../../../util'
|
|
|
10
10
|
export default function (server: Server, ctx: AppContext) {
|
|
11
11
|
server.app.bsky.unspecced.getPopularFeedGenerators({
|
|
12
12
|
auth: ctx.authVerifier.standardOptional,
|
|
13
|
-
handler: async ({ auth, params }) => {
|
|
13
|
+
handler: async ({ auth, params, req }) => {
|
|
14
14
|
const viewer = auth.credentials.iss
|
|
15
|
+
const labelers = ctx.reqLabelers(req)
|
|
16
|
+
const hydrateCtx = { viewer, labelers }
|
|
15
17
|
|
|
16
18
|
if (clearlyBadCursor(params.cursor)) {
|
|
17
19
|
return {
|
|
@@ -40,7 +42,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
40
42
|
cursor = parseString(res.cursor)
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
const hydration = await ctx.hydrator.hydrateFeedGens(uris,
|
|
45
|
+
const hydration = await ctx.hydrator.hydrateFeedGens(uris, hydrateCtx)
|
|
44
46
|
const feedViews = mapDefined(uris, (uri) =>
|
|
45
47
|
ctx.views.feedGenerator(uri, hydration),
|
|
46
48
|
)
|
|
@@ -51,6 +53,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
51
53
|
feeds: feedViews,
|
|
52
54
|
cursor,
|
|
53
55
|
},
|
|
56
|
+
headers: resHeaders({ labelers }),
|
|
54
57
|
}
|
|
55
58
|
},
|
|
56
59
|
})
|
|
@@ -5,18 +5,25 @@ import { INVALID_HANDLE } from '@atproto/syntax'
|
|
|
5
5
|
|
|
6
6
|
export default function (server: Server, ctx: AppContext) {
|
|
7
7
|
server.com.atproto.admin.getAccountInfos({
|
|
8
|
-
auth: ctx.authVerifier.
|
|
9
|
-
handler: async ({ params }) => {
|
|
8
|
+
auth: ctx.authVerifier.optionalStandardOrRole,
|
|
9
|
+
handler: async ({ params, auth }) => {
|
|
10
10
|
const { dids } = params
|
|
11
|
+
const { includeTakedowns } = ctx.authVerifier.parseCreds(auth)
|
|
12
|
+
|
|
11
13
|
const actors = await ctx.hydrator.actor.getActors(dids, true)
|
|
12
14
|
|
|
13
15
|
const infos = mapDefined(dids, (did) => {
|
|
14
16
|
const info = actors.get(did)
|
|
15
17
|
if (!info) return
|
|
18
|
+
if (info.takedownRef && !includeTakedowns) return
|
|
19
|
+
const profileRecord =
|
|
20
|
+
!info.profileTakedownRef || includeTakedowns
|
|
21
|
+
? info.profile
|
|
22
|
+
: undefined
|
|
16
23
|
return {
|
|
17
24
|
did,
|
|
18
25
|
handle: info.handle ?? INVALID_HANDLE,
|
|
19
|
-
relatedRecords:
|
|
26
|
+
relatedRecords: profileRecord ? [profileRecord] : undefined,
|
|
20
27
|
indexedAt: (info.sortedAt ?? new Date(0)).toISOString(),
|
|
21
28
|
}
|
|
22
29
|
})
|
package/src/api/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import unmuteActor from './app/bsky/graph/unmuteActor'
|
|
|
30
30
|
import muteActorList from './app/bsky/graph/muteActorList'
|
|
31
31
|
import unmuteActorList from './app/bsky/graph/unmuteActorList'
|
|
32
32
|
import getSuggestedFollowsByActor from './app/bsky/graph/getSuggestedFollowsByActor'
|
|
33
|
+
import getLabelerServices from './app/bsky/labeler/getServices'
|
|
33
34
|
import searchActors from './app/bsky/actor/searchActors'
|
|
34
35
|
import searchActorsTypeahead from './app/bsky/actor/searchActorsTypeahead'
|
|
35
36
|
import getSuggestions from './app/bsky/actor/getSuggestions'
|
|
@@ -84,6 +85,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
84
85
|
muteActorList(server, ctx)
|
|
85
86
|
unmuteActorList(server, ctx)
|
|
86
87
|
getSuggestedFollowsByActor(server, ctx)
|
|
88
|
+
getLabelerServices(server, ctx)
|
|
87
89
|
searchActors(server, ctx)
|
|
88
90
|
searchActorsTypeahead(server, ctx)
|
|
89
91
|
getSuggestions(server, ctx)
|
package/src/api/util.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ParsedLabelers, formatLabelerHeader } from '../util'
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export const ATPROTO_CONTENT_LABELERS = 'Atproto-Content-Labelers'
|
|
4
|
+
export const ATPROTO_REPO_REV = 'Atproto-Repo-Rev'
|
|
5
|
+
|
|
6
|
+
type ResHeaderOpts = {
|
|
7
|
+
labelers: ParsedLabelers
|
|
8
|
+
repoRev: string | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const resHeaders = (
|
|
12
|
+
opts: Partial<ResHeaderOpts>,
|
|
13
|
+
): Record<string, string> => {
|
|
14
|
+
const headers = {}
|
|
15
|
+
if (opts.labelers) {
|
|
16
|
+
headers[ATPROTO_CONTENT_LABELERS] = formatLabelerHeader(opts.labelers)
|
|
17
|
+
}
|
|
18
|
+
if (opts.repoRev) {
|
|
19
|
+
headers[ATPROTO_REPO_REV] = opts.repoRev
|
|
6
20
|
}
|
|
21
|
+
return headers
|
|
7
22
|
}
|
|
8
23
|
|
|
9
24
|
export const clearlyBadCursor = (cursor?: string) => {
|
package/src/auth-verifier.ts
CHANGED
|
@@ -258,7 +258,7 @@ export class AuthVerifier {
|
|
|
258
258
|
) {
|
|
259
259
|
const viewer =
|
|
260
260
|
creds.credentials.type === 'standard' ? creds.credentials.iss : null
|
|
261
|
-
const
|
|
261
|
+
const includeTakedowns =
|
|
262
262
|
(creds.credentials.type === 'role' && creds.credentials.admin) ||
|
|
263
263
|
creds.credentials.type === 'mod_service' ||
|
|
264
264
|
(creds.credentials.type === 'standard' &&
|
|
@@ -269,7 +269,7 @@ export class AuthVerifier {
|
|
|
269
269
|
|
|
270
270
|
return {
|
|
271
271
|
viewer,
|
|
272
|
-
|
|
272
|
+
includeTakedowns,
|
|
273
273
|
canPerformTakedown,
|
|
274
274
|
}
|
|
275
275
|
}
|
package/src/context.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import express from 'express'
|
|
1
2
|
import * as plc from '@did-plc/lib'
|
|
2
3
|
import { IdResolver } from '@atproto/identity'
|
|
3
4
|
import AtpAgent from '@atproto/api'
|
|
@@ -10,6 +11,12 @@ import { Views } from './views'
|
|
|
10
11
|
import { AuthVerifier } from './auth-verifier'
|
|
11
12
|
import { BsyncClient } from './bsync'
|
|
12
13
|
import { CourierClient } from './courier'
|
|
14
|
+
import {
|
|
15
|
+
ParsedLabelers,
|
|
16
|
+
defaultLabelerHeader,
|
|
17
|
+
parseLabelerHeader,
|
|
18
|
+
} from './util'
|
|
19
|
+
import { httpLogger as log } from './logger'
|
|
13
20
|
|
|
14
21
|
export class AppContext {
|
|
15
22
|
constructor(
|
|
@@ -79,6 +86,19 @@ export class AppContext {
|
|
|
79
86
|
keypair: this.signingKey,
|
|
80
87
|
})
|
|
81
88
|
}
|
|
89
|
+
|
|
90
|
+
reqLabelers(req: express.Request): ParsedLabelers {
|
|
91
|
+
const val = req.header('atproto-accept-labelers')
|
|
92
|
+
let parsed: ParsedLabelers | null
|
|
93
|
+
try {
|
|
94
|
+
parsed = parseLabelerHeader(val)
|
|
95
|
+
} catch (err) {
|
|
96
|
+
parsed = null
|
|
97
|
+
log.info({ err, val }, 'failed to parse labeler header')
|
|
98
|
+
}
|
|
99
|
+
if (!parsed) return defaultLabelerHeader(this.cfg.labelsFromIssuerDids)
|
|
100
|
+
return parsed
|
|
101
|
+
}
|
|
82
102
|
}
|
|
83
103
|
|
|
84
104
|
export default AppContext
|
|
@@ -25,7 +25,6 @@ import * as record from './tables/record'
|
|
|
25
25
|
import * as notification from './tables/notification'
|
|
26
26
|
import * as notificationPushToken from './tables/notification-push-token'
|
|
27
27
|
import * as didCache from './tables/did-cache'
|
|
28
|
-
import * as moderation from './tables/moderation'
|
|
29
28
|
import * as label from './tables/label'
|
|
30
29
|
import * as algo from './tables/algo'
|
|
31
30
|
import * as viewParam from './tables/view-param'
|
|
@@ -33,6 +32,7 @@ import * as suggestedFollow from './tables/suggested-follow'
|
|
|
33
32
|
import * as suggestedFeed from './tables/suggested-feed'
|
|
34
33
|
import * as taggedSuggestion from './tables/tagged-suggestion'
|
|
35
34
|
import * as blobTakedown from './tables/blob-takedown'
|
|
35
|
+
import * as labeler from './tables/labeler'
|
|
36
36
|
|
|
37
37
|
export type DatabaseSchemaType = duplicateRecord.PartialDB &
|
|
38
38
|
profile.PartialDB &
|
|
@@ -60,14 +60,14 @@ export type DatabaseSchemaType = duplicateRecord.PartialDB &
|
|
|
60
60
|
notification.PartialDB &
|
|
61
61
|
notificationPushToken.PartialDB &
|
|
62
62
|
didCache.PartialDB &
|
|
63
|
-
moderation.PartialDB &
|
|
64
63
|
label.PartialDB &
|
|
65
64
|
algo.PartialDB &
|
|
66
65
|
viewParam.PartialDB &
|
|
67
66
|
suggestedFollow.PartialDB &
|
|
68
67
|
suggestedFeed.PartialDB &
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
blobTakedown.PartialDB &
|
|
69
|
+
labeler.PartialDB &
|
|
70
|
+
taggedSuggestion.PartialDB
|
|
71
71
|
|
|
72
72
|
export type DatabaseSchema = Kysely<DatabaseSchemaType>
|
|
73
73
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Kysely, sql } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable('labeler')
|
|
6
|
+
.addColumn('uri', 'varchar', (col) => col.primaryKey())
|
|
7
|
+
.addColumn('cid', 'varchar', (col) => col.notNull())
|
|
8
|
+
.addColumn('creator', 'varchar', (col) => col.notNull())
|
|
9
|
+
.addColumn('createdAt', 'varchar', (col) => col.notNull())
|
|
10
|
+
.addColumn('indexedAt', 'varchar', (col) => col.notNull())
|
|
11
|
+
.addColumn('sortAt', 'varchar', (col) =>
|
|
12
|
+
col
|
|
13
|
+
.generatedAlwaysAs(sql`least("createdAt", "indexedAt")`)
|
|
14
|
+
.stored()
|
|
15
|
+
.notNull(),
|
|
16
|
+
)
|
|
17
|
+
.execute()
|
|
18
|
+
await db.schema
|
|
19
|
+
.createIndex('labeler_order_by_idx')
|
|
20
|
+
.on('labeler')
|
|
21
|
+
.columns(['sortAt', 'cid'])
|
|
22
|
+
.execute()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
26
|
+
await db.schema.dropTable('labeler').execute()
|
|
27
|
+
}
|
|
@@ -33,3 +33,4 @@ export * as _20230929T192920807Z from './20230929T192920807Z-record-cursor-index
|
|
|
33
33
|
export * as _20231003T202833377Z from './20231003T202833377Z-create-moderation-subject-status'
|
|
34
34
|
export * as _20231220T225126090Z from './20231220T225126090Z-blob-takedowns'
|
|
35
35
|
export * as _20240124T023719200Z from './20240124T023719200Z-tagged-suggestions'
|
|
36
|
+
export * as _20240226T225725627Z from './20240226T225725627Z-labelers'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GeneratedAlways } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export const tableName = 'labeler'
|
|
4
|
+
|
|
5
|
+
export interface Labeler {
|
|
6
|
+
uri: string
|
|
7
|
+
cid: string
|
|
8
|
+
creator: string
|
|
9
|
+
createdAt: string
|
|
10
|
+
indexedAt: string
|
|
11
|
+
sortAt: GeneratedAlways<string>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type PartialDB = {
|
|
15
|
+
[tableName]: Labeler
|
|
16
|
+
}
|
|
@@ -26,6 +26,7 @@ import * as ListItem from './plugins/list-item'
|
|
|
26
26
|
import * as ListBlock from './plugins/list-block'
|
|
27
27
|
import * as Block from './plugins/block'
|
|
28
28
|
import * as FeedGenerator from './plugins/feed-generator'
|
|
29
|
+
import * as Labeler from './plugins/labeler'
|
|
29
30
|
import RecordProcessor from './processor'
|
|
30
31
|
import { subLogger } from '../../../logger'
|
|
31
32
|
import { retryHttp } from '../../../util/retry'
|
|
@@ -44,6 +45,7 @@ export class IndexingService {
|
|
|
44
45
|
listBlock: ListBlock.PluginType
|
|
45
46
|
block: Block.PluginType
|
|
46
47
|
feedGenerator: FeedGenerator.PluginType
|
|
48
|
+
labeler: Labeler.PluginType
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
constructor(
|
|
@@ -63,6 +65,7 @@ export class IndexingService {
|
|
|
63
65
|
listBlock: ListBlock.makePlugin(this.db, this.background),
|
|
64
66
|
block: Block.makePlugin(this.db, this.background),
|
|
65
67
|
feedGenerator: FeedGenerator.makePlugin(this.db, this.background),
|
|
68
|
+
labeler: Labeler.makePlugin(this.db, this.background),
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -298,6 +301,7 @@ export class IndexingService {
|
|
|
298
301
|
.deleteFrom('feed_generator')
|
|
299
302
|
.where('creator', '=', did)
|
|
300
303
|
.execute()
|
|
304
|
+
await this.db.db.deleteFrom('labeler').where('creator', '=', did).execute()
|
|
301
305
|
// lists
|
|
302
306
|
await this.db.db
|
|
303
307
|
.deleteFrom('list_item')
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Selectable } from 'kysely'
|
|
2
|
+
import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
|
|
3
|
+
import { CID } from 'multiformats/cid'
|
|
4
|
+
import * as Labeler from '../../../../lexicon/types/app/bsky/labeler/service'
|
|
5
|
+
import * as lex from '../../../../lexicon/lexicons'
|
|
6
|
+
import { Database } from '../../db'
|
|
7
|
+
import { DatabaseSchema, DatabaseSchemaType } from '../../db/database-schema'
|
|
8
|
+
import RecordProcessor from '../processor'
|
|
9
|
+
import { BackgroundQueue } from '../../background'
|
|
10
|
+
|
|
11
|
+
const lexId = lex.ids.AppBskyLabelerService
|
|
12
|
+
type IndexedLabeler = Selectable<DatabaseSchemaType['labeler']>
|
|
13
|
+
|
|
14
|
+
const insertFn = async (
|
|
15
|
+
db: DatabaseSchema,
|
|
16
|
+
uri: AtUri,
|
|
17
|
+
cid: CID,
|
|
18
|
+
obj: Labeler.Record,
|
|
19
|
+
timestamp: string,
|
|
20
|
+
): Promise<IndexedLabeler | null> => {
|
|
21
|
+
if (uri.rkey !== 'self') return null
|
|
22
|
+
const inserted = await db
|
|
23
|
+
.insertInto('labeler')
|
|
24
|
+
.values({
|
|
25
|
+
uri: uri.toString(),
|
|
26
|
+
cid: cid.toString(),
|
|
27
|
+
creator: uri.host,
|
|
28
|
+
createdAt: normalizeDatetimeAlways(obj.createdAt),
|
|
29
|
+
indexedAt: timestamp,
|
|
30
|
+
})
|
|
31
|
+
.onConflict((oc) => oc.doNothing())
|
|
32
|
+
.returningAll()
|
|
33
|
+
.executeTakeFirst()
|
|
34
|
+
return inserted || null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const findDuplicate = async (): Promise<AtUri | null> => {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const notifsForInsert = () => {
|
|
42
|
+
return []
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const deleteFn = async (
|
|
46
|
+
db: DatabaseSchema,
|
|
47
|
+
uri: AtUri,
|
|
48
|
+
): Promise<IndexedLabeler | null> => {
|
|
49
|
+
const deleted = await db
|
|
50
|
+
.deleteFrom('labeler')
|
|
51
|
+
.where('uri', '=', uri.toString())
|
|
52
|
+
.returningAll()
|
|
53
|
+
.executeTakeFirst()
|
|
54
|
+
return deleted || null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const notifsForDelete = () => {
|
|
58
|
+
return { notifs: [], toDelete: [] }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type PluginType = RecordProcessor<Labeler.Record, IndexedLabeler>
|
|
62
|
+
|
|
63
|
+
export const makePlugin = (
|
|
64
|
+
db: Database,
|
|
65
|
+
background: BackgroundQueue,
|
|
66
|
+
): PluginType => {
|
|
67
|
+
return new RecordProcessor(db, background, {
|
|
68
|
+
lexId,
|
|
69
|
+
insertFn,
|
|
70
|
+
findDuplicate,
|
|
71
|
+
deleteFn,
|
|
72
|
+
notifsForInsert,
|
|
73
|
+
notifsForDelete,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default makePlugin
|
|
@@ -2,6 +2,7 @@ import { keyBy } from '@atproto/common'
|
|
|
2
2
|
import { ServiceImpl } from '@connectrpc/connect'
|
|
3
3
|
import { Service } from '../../../proto/bsky_connect'
|
|
4
4
|
import { Database } from '../db'
|
|
5
|
+
import { countAll } from '../db/util'
|
|
5
6
|
|
|
6
7
|
export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
7
8
|
async getInteractionCounts(req) {
|
|
@@ -25,16 +26,31 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
25
26
|
if (req.dids.length === 0) {
|
|
26
27
|
return { followers: [], following: [], posts: [] }
|
|
27
28
|
}
|
|
29
|
+
const { ref } = db.db.dynamic
|
|
28
30
|
const res = await db.db
|
|
29
31
|
.selectFrom('profile_agg')
|
|
30
|
-
.selectAll()
|
|
31
32
|
.where('did', 'in', req.dids)
|
|
33
|
+
.selectAll('profile_agg')
|
|
34
|
+
.select([
|
|
35
|
+
db.db
|
|
36
|
+
.selectFrom('feed_generator')
|
|
37
|
+
.whereRef('creator', '=', ref('profile_agg.did'))
|
|
38
|
+
.select(countAll.as('val'))
|
|
39
|
+
.as('feedGensCount'),
|
|
40
|
+
db.db
|
|
41
|
+
.selectFrom('list')
|
|
42
|
+
.whereRef('creator', '=', ref('profile_agg.did'))
|
|
43
|
+
.select(countAll.as('val'))
|
|
44
|
+
.as('listsCount'),
|
|
45
|
+
])
|
|
32
46
|
.execute()
|
|
33
47
|
const byDid = keyBy(res, 'did')
|
|
34
48
|
return {
|
|
35
49
|
followers: req.dids.map((uri) => byDid[uri]?.followersCount ?? 0),
|
|
36
50
|
following: req.dids.map((uri) => byDid[uri]?.followsCount ?? 0),
|
|
37
51
|
posts: req.dids.map((uri) => byDid[uri]?.postsCount ?? 0),
|
|
52
|
+
lists: req.dids.map((uri) => byDid[uri]?.listsCount ?? 0),
|
|
53
|
+
feeds: req.dids.map((uri) => byDid[uri]?.feedGensCount ?? 0),
|
|
38
54
|
}
|
|
39
55
|
},
|
|
40
56
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ui8 from 'uint8arrays'
|
|
2
|
+
import { noUndefinedVals } from '@atproto/common'
|
|
2
3
|
import { ServiceImpl } from '@connectrpc/connect'
|
|
3
4
|
import { Service } from '../../../proto/bsky_connect'
|
|
4
5
|
import { Database } from '../db'
|
|
@@ -17,10 +18,11 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
17
18
|
.execute()
|
|
18
19
|
|
|
19
20
|
const labels = res.map((l) => {
|
|
20
|
-
const formatted = {
|
|
21
|
+
const formatted = noUndefinedVals({
|
|
21
22
|
...l,
|
|
22
23
|
cid: l.cid === '' ? undefined : l.cid,
|
|
23
|
-
|
|
24
|
+
neg: l.neg === true ? true : undefined,
|
|
25
|
+
})
|
|
24
26
|
return ui8.fromString(JSON.stringify(formatted), 'utf8')
|
|
25
27
|
})
|
|
26
28
|
return { labels }
|
|
@@ -3,6 +3,7 @@ import { Service } from '../../../proto/bsky_connect'
|
|
|
3
3
|
import { keyBy } from '@atproto/common'
|
|
4
4
|
import { getRecords } from './records'
|
|
5
5
|
import { Database } from '../db'
|
|
6
|
+
import { sql } from 'kysely'
|
|
6
7
|
|
|
7
8
|
export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
8
9
|
async getActors(req) {
|
|
@@ -13,8 +14,20 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
13
14
|
const profileUris = dids.map(
|
|
14
15
|
(did) => `at://${did}/app.bsky.actor.profile/self`,
|
|
15
16
|
)
|
|
17
|
+
const { ref } = db.db.dynamic
|
|
16
18
|
const [handlesRes, profiles] = await Promise.all([
|
|
17
|
-
db.db
|
|
19
|
+
db.db
|
|
20
|
+
.selectFrom('actor')
|
|
21
|
+
.where('did', 'in', dids)
|
|
22
|
+
.selectAll('actor')
|
|
23
|
+
.select([
|
|
24
|
+
db.db
|
|
25
|
+
.selectFrom('labeler')
|
|
26
|
+
.whereRef('creator', '=', ref('actor.did'))
|
|
27
|
+
.select(sql<true>`${true}`.as('val'))
|
|
28
|
+
.as('isLabeler'),
|
|
29
|
+
])
|
|
30
|
+
.execute(),
|
|
18
31
|
getRecords(db)({ uris: profileUris }),
|
|
19
32
|
])
|
|
20
33
|
const byDid = keyBy(handlesRes, 'did')
|
|
@@ -27,6 +40,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
27
40
|
takenDown: !!row?.takedownRef,
|
|
28
41
|
takedownRef: row?.takedownRef || undefined,
|
|
29
42
|
tombstonedAt: undefined, // in current implementation, tombstoned actors are deleted
|
|
43
|
+
labeler: row?.isLabeler ?? false,
|
|
30
44
|
}
|
|
31
45
|
})
|
|
32
46
|
return { actors }
|
|
@@ -20,6 +20,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
20
20
|
getProfileRecords: getRecords(db, ids.AppBskyActorProfile),
|
|
21
21
|
getRepostRecords: getRecords(db, ids.AppBskyFeedRepost),
|
|
22
22
|
getThreadGateRecords: getRecords(db, ids.AppBskyFeedThreadgate),
|
|
23
|
+
getLabelerRecords: getRecords(db, ids.AppBskyLabelerService),
|
|
23
24
|
})
|
|
24
25
|
|
|
25
26
|
export const getRecords =
|
package/src/hydration/actor.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type Actor = {
|
|
|
15
15
|
profileTakedownRef?: string
|
|
16
16
|
sortedAt?: Date
|
|
17
17
|
takedownRef?: string
|
|
18
|
+
isLabeler: boolean
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export type Actors = HydrationMap<Actor>
|
|
@@ -36,6 +37,8 @@ export type ProfileAgg = {
|
|
|
36
37
|
followers: number
|
|
37
38
|
follows: number
|
|
38
39
|
posts: number
|
|
40
|
+
lists: number
|
|
41
|
+
feeds: number
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
export type ProfileAggs = HydrationMap<ProfileAgg>
|
|
@@ -100,6 +103,7 @@ export class ActorHydrator {
|
|
|
100
103
|
profileTakedownRef: safeTakedownRef(profile),
|
|
101
104
|
sortedAt: profile?.sortedAt?.toDate(),
|
|
102
105
|
takedownRef: safeTakedownRef(actor),
|
|
106
|
+
isLabeler: actor.labeler ?? false,
|
|
103
107
|
})
|
|
104
108
|
}, new HydrationMap<Actor>())
|
|
105
109
|
}
|
|
@@ -143,6 +147,8 @@ export class ActorHydrator {
|
|
|
143
147
|
followers: counts.followers[i] ?? 0,
|
|
144
148
|
follows: counts.following[i] ?? 0,
|
|
145
149
|
posts: counts.posts[i] ?? 0,
|
|
150
|
+
lists: counts.lists[i] ?? 0,
|
|
151
|
+
feeds: counts.feeds[i] ?? 0,
|
|
146
152
|
})
|
|
147
153
|
}, new HydrationMap<ProfileAgg>())
|
|
148
154
|
}
|