@atproto/bsky 0.0.28 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE.txt +1 -1
- package/dist/auto-moderator/index.d.ts +1 -14
- package/dist/context.d.ts +0 -3
- package/dist/db/index.js +34593 -3478
- package/dist/db/index.js.map +3 -3
- package/dist/db/pagination.d.ts +1 -0
- package/dist/index.d.ts +0 -4
- package/dist/index.js +1531 -1723
- package/dist/index.js.map +3 -3
- package/dist/indexer/config.d.ts +0 -8
- package/dist/lexicon/index.d.ts +0 -2
- package/dist/lexicon/lexicons.d.ts +38 -47
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +2 -2
- package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +7 -0
- package/package.json +7 -6
- package/src/api/app/bsky/actor/getSuggestions.ts +1 -1
- package/src/api/app/bsky/actor/searchActors.ts +1 -0
- package/src/api/app/bsky/feed/getActorFeeds.ts +6 -0
- package/src/api/app/bsky/feed/getActorLikes.ts +4 -0
- package/src/api/app/bsky/feed/getAuthorFeed.ts +4 -0
- package/src/api/app/bsky/feed/getFeed.ts +8 -5
- package/src/api/app/bsky/feed/getLikes.ts +4 -0
- package/src/api/app/bsky/feed/getListFeed.ts +4 -0
- package/src/api/app/bsky/feed/getRepostedBy.ts +4 -0
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +1 -1
- package/src/api/app/bsky/feed/getTimeline.ts +4 -0
- package/src/api/app/bsky/feed/searchPosts.ts +1 -0
- package/src/api/app/bsky/graph/getBlocks.ts +7 -0
- package/src/api/app/bsky/graph/getFollowers.ts +4 -0
- package/src/api/app/bsky/graph/getFollows.ts +4 -0
- package/src/api/app/bsky/graph/getList.ts +4 -0
- package/src/api/app/bsky/graph/getListBlocks.ts +4 -0
- package/src/api/app/bsky/graph/getListMutes.ts +7 -0
- package/src/api/app/bsky/graph/getLists.ts +7 -0
- package/src/api/app/bsky/graph/getMutes.ts +7 -0
- package/src/api/app/bsky/notification/listNotifications.ts +3 -0
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +7 -1
- package/src/api/index.ts +0 -6
- package/src/auto-moderator/index.ts +9 -176
- package/src/context.ts +0 -6
- package/src/db/pagination.ts +3 -0
- package/src/index.ts +1 -6
- package/src/indexer/config.ts +0 -29
- package/src/lexicon/index.ts +0 -12
- package/src/lexicon/lexicons.ts +43 -50
- package/src/lexicon/types/com/atproto/admin/defs.ts +2 -0
- package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +13 -0
- package/src/logger.ts +32 -0
- package/tests/auto-moderator/labeler.test.ts +2 -0
- package/tests/feed-generation.test.ts +0 -6
- package/tests/views/notifications.test.ts +9 -0
- package/tests/views/timeline.test.ts +8 -0
- package/dist/api/app/bsky/feed/describeFeedGenerator.d.ts +0 -3
- package/dist/api/app/bsky/feed/getFeedSkeleton.d.ts +0 -3
- package/dist/api/app/bsky/unspecced/getTimelineSkeleton.d.ts +0 -3
- package/dist/auto-moderator/abyss.d.ts +0 -48
- package/dist/auto-moderator/fuzzy-matcher.d.ts +0 -14
- package/dist/feed-gen/bsky-team.d.ts +0 -3
- package/dist/feed-gen/hot-classic.d.ts +0 -3
- package/dist/feed-gen/index.d.ts +0 -2
- package/dist/feed-gen/mutuals.d.ts +0 -3
- package/dist/feed-gen/types.d.ts +0 -15
- package/dist/lexicon/types/app/bsky/unspecced/getTimelineSkeleton.d.ts +0 -35
- package/src/api/app/bsky/feed/describeFeedGenerator.ts +0 -21
- package/src/api/app/bsky/feed/getFeedSkeleton.ts +0 -30
- package/src/api/app/bsky/unspecced/getTimelineSkeleton.ts +0 -26
- package/src/auto-moderator/abyss.ts +0 -114
- package/src/auto-moderator/fuzzy-matcher.ts +0 -126
- package/src/feed-gen/bsky-team.ts +0 -42
- package/src/feed-gen/hot-classic.ts +0 -55
- package/src/feed-gen/index.ts +0 -17
- package/src/feed-gen/mutuals.ts +0 -57
- package/src/feed-gen/types.ts +0 -32
- package/src/lexicon/types/app/bsky/unspecced/getTimelineSkeleton.ts +0 -49
- package/tests/algos/hot-classic.test.ts +0 -87
- package/tests/auto-moderator/fuzzy-matcher.test.ts +0 -163
- package/tests/auto-moderator/takedowns.test.ts +0 -202
|
@@ -8,21 +8,12 @@ import { BackgroundQueue } from '../background'
|
|
|
8
8
|
import { IndexerConfig } from '../indexer/config'
|
|
9
9
|
import { buildBasicAuth } from '../auth-verifier'
|
|
10
10
|
import { CID } from 'multiformats/cid'
|
|
11
|
-
import { ImageFlagger } from './abyss'
|
|
12
11
|
import { HiveLabeler, ImgLabeler } from './hive'
|
|
13
12
|
import { KeywordLabeler, TextLabeler } from './keyword'
|
|
14
13
|
import { ids } from '../lexicon/lexicons'
|
|
15
|
-
import { Abyss } from './abyss'
|
|
16
|
-
import { FuzzyMatcher, TextFlagger } from './fuzzy-matcher'
|
|
17
|
-
import {
|
|
18
|
-
REASONOTHER,
|
|
19
|
-
REASONVIOLATION,
|
|
20
|
-
} from '../lexicon/types/com/atproto/moderation/defs'
|
|
21
14
|
|
|
22
15
|
export class AutoModerator {
|
|
23
16
|
public pushAgent: AtpAgent
|
|
24
|
-
public imageFlagger?: ImageFlagger
|
|
25
|
-
public textFlagger?: TextFlagger
|
|
26
17
|
public imgLabeler?: ImgLabeler
|
|
27
18
|
public textLabeler?: TextLabeler
|
|
28
19
|
|
|
@@ -34,24 +25,9 @@ export class AutoModerator {
|
|
|
34
25
|
backgroundQueue: BackgroundQueue
|
|
35
26
|
},
|
|
36
27
|
) {
|
|
37
|
-
const { hiveApiKey
|
|
28
|
+
const { hiveApiKey } = ctx.cfg
|
|
38
29
|
this.imgLabeler = hiveApiKey ? new HiveLabeler(hiveApiKey, ctx) : undefined
|
|
39
30
|
this.textLabeler = new KeywordLabeler(ctx.cfg.labelerKeywords)
|
|
40
|
-
if (abyssEndpoint && abyssPassword) {
|
|
41
|
-
this.imageFlagger = new Abyss(abyssEndpoint, abyssPassword, ctx)
|
|
42
|
-
} else {
|
|
43
|
-
log.error(
|
|
44
|
-
{ abyssEndpoint, abyssPassword },
|
|
45
|
-
'abyss not properly configured',
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (ctx.cfg.fuzzyMatchB64) {
|
|
50
|
-
this.textFlagger = FuzzyMatcher.fromB64(
|
|
51
|
-
ctx.cfg.fuzzyMatchB64,
|
|
52
|
-
ctx.cfg.fuzzyFalsePositiveB64,
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
31
|
|
|
56
32
|
const url = new URL(ctx.cfg.moderationPushUrl)
|
|
57
33
|
this.pushAgent = new AtpAgent({ service: url.origin })
|
|
@@ -64,35 +40,17 @@ export class AutoModerator {
|
|
|
64
40
|
processRecord(uri: AtUri, cid: CID, obj: unknown) {
|
|
65
41
|
this.ctx.backgroundQueue.add(async () => {
|
|
66
42
|
const { text, imgs } = getFieldsFromRecord(obj, uri)
|
|
67
|
-
await
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}),
|
|
74
|
-
this.flagRecordText(uri, cid, text).catch((err) => {
|
|
75
|
-
log.error(
|
|
76
|
-
{ err, uri: uri.toString(), record: obj },
|
|
77
|
-
'failed to check record for text flagging',
|
|
78
|
-
)
|
|
79
|
-
}),
|
|
80
|
-
this.checkImgForTakedown(uri, cid, imgs).catch((err) => {
|
|
81
|
-
log.error(
|
|
82
|
-
{ err, uri: uri.toString(), record: obj },
|
|
83
|
-
'failed to check img for takedown',
|
|
84
|
-
)
|
|
85
|
-
}),
|
|
86
|
-
])
|
|
43
|
+
await this.labelRecord(uri, cid, text, imgs).catch((err) => {
|
|
44
|
+
log.error(
|
|
45
|
+
{ err, uri: uri.toString(), record: obj },
|
|
46
|
+
'failed to label record',
|
|
47
|
+
)
|
|
48
|
+
})
|
|
87
49
|
})
|
|
88
50
|
}
|
|
89
51
|
|
|
90
|
-
processHandle(
|
|
91
|
-
this
|
|
92
|
-
await this.flagSubjectText(handle, { did }).catch((err) => {
|
|
93
|
-
log.error({ err, handle, did }, 'failed to label handle')
|
|
94
|
-
})
|
|
95
|
-
})
|
|
52
|
+
processHandle(_handle: string, _did: string) {
|
|
53
|
+
// no-op since this functionality moved to auto-mod service
|
|
96
54
|
}
|
|
97
55
|
|
|
98
56
|
async labelRecord(uri: AtUri, recordCid: CID, text: string[], imgs: CID[]) {
|
|
@@ -108,131 +66,6 @@ export class AutoModerator {
|
|
|
108
66
|
await this.pushLabels(uri, recordCid, labels)
|
|
109
67
|
}
|
|
110
68
|
|
|
111
|
-
async flagRecordText(uri: AtUri, cid: CID, text: string[]) {
|
|
112
|
-
if (
|
|
113
|
-
![
|
|
114
|
-
ids.AppBskyActorProfile,
|
|
115
|
-
ids.AppBskyGraphList,
|
|
116
|
-
ids.AppBskyFeedGenerator,
|
|
117
|
-
].includes(uri.collection)
|
|
118
|
-
) {
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
return this.flagSubjectText(text.join(' '), { uri, cid })
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async flagSubjectText(
|
|
125
|
-
text: string,
|
|
126
|
-
subject: { did: string } | { uri: AtUri; cid: CID },
|
|
127
|
-
) {
|
|
128
|
-
if (!this.textFlagger) return
|
|
129
|
-
const matches = this.textFlagger.getMatches(text)
|
|
130
|
-
if (matches.length < 1) return
|
|
131
|
-
const formattedSubject =
|
|
132
|
-
'did' in subject
|
|
133
|
-
? {
|
|
134
|
-
$type: 'com.atproto.admin.defs#repoRef',
|
|
135
|
-
did: subject.did,
|
|
136
|
-
}
|
|
137
|
-
: {
|
|
138
|
-
$type: 'com.atproto.repo.strongRef',
|
|
139
|
-
uri: subject.uri.toString(),
|
|
140
|
-
cid: subject.cid.toString(),
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
await this.pushAgent.api.com.atproto.admin.emitModerationEvent({
|
|
144
|
-
event: {
|
|
145
|
-
$type: 'com.atproto.admin.defs#modEventReport',
|
|
146
|
-
comment: `Automatically flagged for possible slurs: ${matches.join(
|
|
147
|
-
', ',
|
|
148
|
-
)}`,
|
|
149
|
-
reportType: REASONOTHER,
|
|
150
|
-
},
|
|
151
|
-
subject: formattedSubject,
|
|
152
|
-
createdBy: this.ctx.cfg.serverDid,
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async checkImgForTakedown(uri: AtUri, recordCid: CID, imgCids: CID[]) {
|
|
157
|
-
if (imgCids.length < 0) return
|
|
158
|
-
const results = await Promise.all(
|
|
159
|
-
imgCids.map((cid) => this.imageFlagger?.scanImage(uri.host, cid, uri)),
|
|
160
|
-
)
|
|
161
|
-
const takedownCids: CID[] = []
|
|
162
|
-
for (let i = 0; i < results.length; i++) {
|
|
163
|
-
if (results.at(i)?.length) {
|
|
164
|
-
takedownCids.push(imgCids[i])
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (takedownCids.length === 0) return
|
|
168
|
-
try {
|
|
169
|
-
await this.persistTakedown(
|
|
170
|
-
uri,
|
|
171
|
-
recordCid,
|
|
172
|
-
takedownCids,
|
|
173
|
-
dedupe(results.flat()),
|
|
174
|
-
)
|
|
175
|
-
} catch (err) {
|
|
176
|
-
log.error(
|
|
177
|
-
{
|
|
178
|
-
err,
|
|
179
|
-
uri: uri.toString(),
|
|
180
|
-
imgCids: imgCids.map((c) => c.toString()),
|
|
181
|
-
results,
|
|
182
|
-
},
|
|
183
|
-
'failed to persist takedown',
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
async persistTakedown(
|
|
189
|
-
uri: AtUri,
|
|
190
|
-
recordCid: CID,
|
|
191
|
-
takedownCids: CID[],
|
|
192
|
-
labels: string[],
|
|
193
|
-
) {
|
|
194
|
-
const reportReason = `automated takedown (${labels.join(
|
|
195
|
-
', ',
|
|
196
|
-
)}). account needs review and possibly additional action`
|
|
197
|
-
const takedownReason = `automated takedown for labels: ${labels.join(', ')}`
|
|
198
|
-
log.warn(
|
|
199
|
-
{
|
|
200
|
-
uri: uri.toString(),
|
|
201
|
-
blobCids: takedownCids,
|
|
202
|
-
labels,
|
|
203
|
-
},
|
|
204
|
-
'hard takedown of record (and blobs) based on auto-matching',
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
await this.pushAgent.api.com.atproto.admin.emitModerationEvent({
|
|
208
|
-
event: {
|
|
209
|
-
$type: 'com.atproto.admin.defs#modEventReport',
|
|
210
|
-
comment: reportReason,
|
|
211
|
-
reportType: REASONVIOLATION,
|
|
212
|
-
},
|
|
213
|
-
subject: {
|
|
214
|
-
$type: 'com.atproto.repo.strongRef',
|
|
215
|
-
uri: uri.toString(),
|
|
216
|
-
cid: recordCid.toString(),
|
|
217
|
-
},
|
|
218
|
-
createdBy: this.ctx.cfg.serverDid,
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
await this.pushAgent.com.atproto.admin.emitModerationEvent({
|
|
222
|
-
event: {
|
|
223
|
-
$type: 'com.atproto.admin.defs#modEventTakedown',
|
|
224
|
-
comment: takedownReason,
|
|
225
|
-
},
|
|
226
|
-
subject: {
|
|
227
|
-
$type: 'com.atproto.repo.strongRef',
|
|
228
|
-
uri: uri.toString(),
|
|
229
|
-
cid: recordCid.toString(),
|
|
230
|
-
},
|
|
231
|
-
subjectBlobCids: takedownCids.map((c) => c.toString()),
|
|
232
|
-
createdBy: this.ctx.cfg.serverDid,
|
|
233
|
-
})
|
|
234
|
-
}
|
|
235
|
-
|
|
236
69
|
async pushLabels(uri: AtUri, cid: CID, labels: string[]): Promise<void> {
|
|
237
70
|
if (labels.length < 1) return
|
|
238
71
|
|
package/src/context.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { ImageUriBuilder } from './image/uri'
|
|
|
9
9
|
import { Services } from './services'
|
|
10
10
|
import DidRedisCache from './did-cache'
|
|
11
11
|
import { BackgroundQueue } from './background'
|
|
12
|
-
import { MountedAlgos } from './feed-gen/types'
|
|
13
12
|
import { Redis } from './redis'
|
|
14
13
|
import { AuthVerifier } from './auth-verifier'
|
|
15
14
|
import { BsyncClient } from './bsync'
|
|
@@ -30,7 +29,6 @@ export class AppContext {
|
|
|
30
29
|
searchAgent?: AtpAgent
|
|
31
30
|
bsyncClient?: BsyncClient
|
|
32
31
|
courierClient?: CourierClient
|
|
33
|
-
algos: MountedAlgos
|
|
34
32
|
authVerifier: AuthVerifier
|
|
35
33
|
},
|
|
36
34
|
) {}
|
|
@@ -99,10 +97,6 @@ export class AppContext {
|
|
|
99
97
|
get backgroundQueue(): BackgroundQueue {
|
|
100
98
|
return this.opts.backgroundQueue
|
|
101
99
|
}
|
|
102
|
-
|
|
103
|
-
get algos(): MountedAlgos {
|
|
104
|
-
return this.opts.algos
|
|
105
|
-
}
|
|
106
100
|
}
|
|
107
101
|
|
|
108
102
|
export default AppContext
|
package/src/db/pagination.ts
CHANGED
|
@@ -27,6 +27,9 @@ export abstract class GenericKeyset<R, LR extends LabeledResult> {
|
|
|
27
27
|
abstract labelResult(result: R): LR
|
|
28
28
|
abstract labeledResultToCursor(labeled: LR): Cursor
|
|
29
29
|
abstract cursorToLabeledResult(cursor: Cursor): LR
|
|
30
|
+
static clearlyBad(cursor?: string) {
|
|
31
|
+
return cursor !== undefined && !cursor.includes('::')
|
|
32
|
+
}
|
|
30
33
|
packFromResult(results: R | R[]): string | undefined {
|
|
31
34
|
const result = Array.isArray(results) ? results.at(-1) : results
|
|
32
35
|
if (!result) return
|
package/src/index.ts
CHANGED
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
ImageProcessingServerInvalidator,
|
|
29
29
|
} from './image/invalidator'
|
|
30
30
|
import { BackgroundQueue } from './background'
|
|
31
|
-
import { MountedAlgos } from './feed-gen/types'
|
|
32
31
|
import { AtpAgent } from '@atproto/api'
|
|
33
32
|
import { Keypair } from '@atproto/crypto'
|
|
34
33
|
import { Redis } from './redis'
|
|
@@ -37,14 +36,12 @@ import { authWithApiKey as bsyncAuth, createBsyncClient } from './bsync'
|
|
|
37
36
|
import { authWithApiKey as courierAuth, createCourierClient } from './courier'
|
|
38
37
|
|
|
39
38
|
export type { ServerConfigValues } from './config'
|
|
40
|
-
export type { MountedAlgos } from './feed-gen/types'
|
|
41
39
|
export { ServerConfig } from './config'
|
|
42
40
|
export { Database, PrimaryDatabase, DatabaseCoordinator } from './db'
|
|
43
41
|
export { Redis } from './redis'
|
|
44
42
|
export { ViewMaintainer } from './db/views'
|
|
45
43
|
export { AppContext } from './context'
|
|
46
44
|
export type { ImageInvalidator } from './image/invalidator'
|
|
47
|
-
export { makeAlgos } from './feed-gen'
|
|
48
45
|
export * from './daemon'
|
|
49
46
|
export * from './indexer'
|
|
50
47
|
export * from './ingester'
|
|
@@ -67,9 +64,8 @@ export class BskyAppView {
|
|
|
67
64
|
config: ServerConfig
|
|
68
65
|
signingKey: Keypair
|
|
69
66
|
imgInvalidator?: ImageInvalidator
|
|
70
|
-
algos?: MountedAlgos
|
|
71
67
|
}): BskyAppView {
|
|
72
|
-
const { db, redis, config, signingKey
|
|
68
|
+
const { db, redis, config, signingKey } = opts
|
|
73
69
|
let maybeImgInvalidator = opts.imgInvalidator
|
|
74
70
|
const app = express()
|
|
75
71
|
app.set('trust proxy', true)
|
|
@@ -170,7 +166,6 @@ export class BskyAppView {
|
|
|
170
166
|
searchAgent,
|
|
171
167
|
bsyncClient,
|
|
172
168
|
courierClient,
|
|
173
|
-
algos,
|
|
174
169
|
authVerifier,
|
|
175
170
|
})
|
|
176
171
|
|
package/src/indexer/config.ts
CHANGED
|
@@ -15,11 +15,7 @@ export interface IndexerConfigValues {
|
|
|
15
15
|
didCacheMaxTTL: number
|
|
16
16
|
handleResolveNameservers?: string[]
|
|
17
17
|
hiveApiKey?: string
|
|
18
|
-
abyssEndpoint?: string
|
|
19
|
-
abyssPassword?: string
|
|
20
18
|
imgUriEndpoint?: string
|
|
21
|
-
fuzzyMatchB64?: string
|
|
22
|
-
fuzzyFalsePositiveB64?: string
|
|
23
19
|
labelerKeywords: Record<string, string>
|
|
24
20
|
moderationPushUrl: string
|
|
25
21
|
courierUrl?: string
|
|
@@ -89,8 +85,6 @@ export class IndexerConfig {
|
|
|
89
85
|
process.env.BSKY_COURIER_IGNORE_BAD_TLS === 'true'
|
|
90
86
|
assert(courierHttpVersion === '1.1' || courierHttpVersion === '2')
|
|
91
87
|
const hiveApiKey = process.env.HIVE_API_KEY || undefined
|
|
92
|
-
const abyssEndpoint = process.env.ABYSS_ENDPOINT
|
|
93
|
-
const abyssPassword = process.env.ABYSS_PASSWORD
|
|
94
88
|
const imgUriEndpoint = process.env.IMG_URI_ENDPOINT
|
|
95
89
|
const indexerPartitionIds =
|
|
96
90
|
overrides?.indexerPartitionIds ||
|
|
@@ -109,9 +103,6 @@ export class IndexerConfig {
|
|
|
109
103
|
const ingesterPartitionCount =
|
|
110
104
|
maybeParseInt(process.env.INGESTER_PARTITION_COUNT) ?? 64
|
|
111
105
|
const labelerKeywords = {}
|
|
112
|
-
const fuzzyMatchB64 = process.env.FUZZY_MATCH_B64 || undefined
|
|
113
|
-
const fuzzyFalsePositiveB64 =
|
|
114
|
-
process.env.FUZZY_FALSE_POSITIVE_B64 || undefined
|
|
115
106
|
const pushNotificationEndpoint = process.env.PUSH_NOTIFICATION_ENDPOINT
|
|
116
107
|
assert(dbPostgresUrl)
|
|
117
108
|
assert(redisHost || (redisSentinelName && redisSentinelHosts?.length))
|
|
@@ -135,8 +126,6 @@ export class IndexerConfig {
|
|
|
135
126
|
courierHttpVersion,
|
|
136
127
|
courierIgnoreBadTls,
|
|
137
128
|
hiveApiKey,
|
|
138
|
-
abyssEndpoint,
|
|
139
|
-
abyssPassword,
|
|
140
129
|
imgUriEndpoint,
|
|
141
130
|
indexerPartitionIds,
|
|
142
131
|
indexerConcurrency,
|
|
@@ -146,8 +135,6 @@ export class IndexerConfig {
|
|
|
146
135
|
indexerPort,
|
|
147
136
|
ingesterPartitionCount,
|
|
148
137
|
labelerKeywords,
|
|
149
|
-
fuzzyMatchB64,
|
|
150
|
-
fuzzyFalsePositiveB64,
|
|
151
138
|
pushNotificationEndpoint,
|
|
152
139
|
...stripUndefineds(overrides ?? {}),
|
|
153
140
|
})
|
|
@@ -225,14 +212,6 @@ export class IndexerConfig {
|
|
|
225
212
|
return this.cfg.hiveApiKey
|
|
226
213
|
}
|
|
227
214
|
|
|
228
|
-
get abyssEndpoint() {
|
|
229
|
-
return this.cfg.abyssEndpoint
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
get abyssPassword() {
|
|
233
|
-
return this.cfg.abyssPassword
|
|
234
|
-
}
|
|
235
|
-
|
|
236
215
|
get imgUriEndpoint() {
|
|
237
216
|
return this.cfg.imgUriEndpoint
|
|
238
217
|
}
|
|
@@ -269,14 +248,6 @@ export class IndexerConfig {
|
|
|
269
248
|
return this.cfg.labelerKeywords
|
|
270
249
|
}
|
|
271
250
|
|
|
272
|
-
get fuzzyMatchB64() {
|
|
273
|
-
return this.cfg.fuzzyMatchB64
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
get fuzzyFalsePositiveB64() {
|
|
277
|
-
return this.cfg.fuzzyFalsePositiveB64
|
|
278
|
-
}
|
|
279
|
-
|
|
280
251
|
get pushNotificationEndpoint() {
|
|
281
252
|
return this.cfg.pushNotificationEndpoint
|
|
282
253
|
}
|
package/src/lexicon/index.ts
CHANGED
|
@@ -127,7 +127,6 @@ import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/
|
|
|
127
127
|
import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen'
|
|
128
128
|
import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators'
|
|
129
129
|
import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecced/getTaggedSuggestions'
|
|
130
|
-
import * as AppBskyUnspeccedGetTimelineSkeleton from './types/app/bsky/unspecced/getTimelineSkeleton'
|
|
131
130
|
import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton'
|
|
132
131
|
import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton'
|
|
133
132
|
|
|
@@ -1649,17 +1648,6 @@ export class AppBskyUnspeccedNS {
|
|
|
1649
1648
|
return this._server.xrpc.method(nsid, cfg)
|
|
1650
1649
|
}
|
|
1651
1650
|
|
|
1652
|
-
getTimelineSkeleton<AV extends AuthVerifier>(
|
|
1653
|
-
cfg: ConfigOf<
|
|
1654
|
-
AV,
|
|
1655
|
-
AppBskyUnspeccedGetTimelineSkeleton.Handler<ExtractAuth<AV>>,
|
|
1656
|
-
AppBskyUnspeccedGetTimelineSkeleton.HandlerReqCtx<ExtractAuth<AV>>
|
|
1657
|
-
>,
|
|
1658
|
-
) {
|
|
1659
|
-
const nsid = 'app.bsky.unspecced.getTimelineSkeleton' // @ts-ignore
|
|
1660
|
-
return this._server.xrpc.method(nsid, cfg)
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
1651
|
searchActorsSkeleton<AV extends AuthVerifier>(
|
|
1664
1652
|
cfg: ConfigOf<
|
|
1665
1653
|
AV,
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -91,6 +91,7 @@ export const schemaDict = {
|
|
|
91
91
|
'lex:com.atproto.admin.defs#modEventEscalate',
|
|
92
92
|
'lex:com.atproto.admin.defs#modEventMute',
|
|
93
93
|
'lex:com.atproto.admin.defs#modEventEmail',
|
|
94
|
+
'lex:com.atproto.admin.defs#modEventResolveAppeal',
|
|
94
95
|
],
|
|
95
96
|
},
|
|
96
97
|
subject: {
|
|
@@ -147,6 +148,7 @@ export const schemaDict = {
|
|
|
147
148
|
'lex:com.atproto.admin.defs#modEventAcknowledge',
|
|
148
149
|
'lex:com.atproto.admin.defs#modEventEscalate',
|
|
149
150
|
'lex:com.atproto.admin.defs#modEventMute',
|
|
151
|
+
'lex:com.atproto.admin.defs#modEventEmail',
|
|
150
152
|
'lex:com.atproto.admin.defs#modEventResolveAppeal',
|
|
151
153
|
],
|
|
152
154
|
},
|
|
@@ -1450,6 +1452,16 @@ export const schemaDict = {
|
|
|
1450
1452
|
description:
|
|
1451
1453
|
'Sort direction for the events. Defaults to descending order of created at timestamp.',
|
|
1452
1454
|
},
|
|
1455
|
+
createdAfter: {
|
|
1456
|
+
type: 'string',
|
|
1457
|
+
format: 'datetime',
|
|
1458
|
+
description: 'Retrieve events created after a given timestamp',
|
|
1459
|
+
},
|
|
1460
|
+
createdBefore: {
|
|
1461
|
+
type: 'string',
|
|
1462
|
+
format: 'datetime',
|
|
1463
|
+
description: 'Retrieve events created before a given timestamp',
|
|
1464
|
+
},
|
|
1453
1465
|
subject: {
|
|
1454
1466
|
type: 'string',
|
|
1455
1467
|
format: 'uri',
|
|
@@ -1466,6 +1478,37 @@ export const schemaDict = {
|
|
|
1466
1478
|
maximum: 100,
|
|
1467
1479
|
default: 50,
|
|
1468
1480
|
},
|
|
1481
|
+
hasComment: {
|
|
1482
|
+
type: 'boolean',
|
|
1483
|
+
description: 'If true, only events with comments are returned',
|
|
1484
|
+
},
|
|
1485
|
+
comment: {
|
|
1486
|
+
type: 'string',
|
|
1487
|
+
description:
|
|
1488
|
+
'If specified, only events with comments containing the keyword are returned',
|
|
1489
|
+
},
|
|
1490
|
+
addedLabels: {
|
|
1491
|
+
type: 'array',
|
|
1492
|
+
items: {
|
|
1493
|
+
type: 'string',
|
|
1494
|
+
},
|
|
1495
|
+
description:
|
|
1496
|
+
'If specified, only events where all of these labels were added are returned',
|
|
1497
|
+
},
|
|
1498
|
+
removedLabels: {
|
|
1499
|
+
type: 'array',
|
|
1500
|
+
items: {
|
|
1501
|
+
type: 'string',
|
|
1502
|
+
},
|
|
1503
|
+
description:
|
|
1504
|
+
'If specified, only events where all of these labels were removed are returned',
|
|
1505
|
+
},
|
|
1506
|
+
reportTypes: {
|
|
1507
|
+
type: 'array',
|
|
1508
|
+
items: {
|
|
1509
|
+
type: 'string',
|
|
1510
|
+
},
|
|
1511
|
+
},
|
|
1469
1512
|
cursor: {
|
|
1470
1513
|
type: 'string',
|
|
1471
1514
|
},
|
|
@@ -8100,55 +8143,6 @@ export const schemaDict = {
|
|
|
8100
8143
|
},
|
|
8101
8144
|
},
|
|
8102
8145
|
},
|
|
8103
|
-
AppBskyUnspeccedGetTimelineSkeleton: {
|
|
8104
|
-
lexicon: 1,
|
|
8105
|
-
id: 'app.bsky.unspecced.getTimelineSkeleton',
|
|
8106
|
-
defs: {
|
|
8107
|
-
main: {
|
|
8108
|
-
type: 'query',
|
|
8109
|
-
description:
|
|
8110
|
-
'DEPRECATED: a skeleton of a timeline. Unspecced and will be unavailable soon.',
|
|
8111
|
-
parameters: {
|
|
8112
|
-
type: 'params',
|
|
8113
|
-
properties: {
|
|
8114
|
-
limit: {
|
|
8115
|
-
type: 'integer',
|
|
8116
|
-
minimum: 1,
|
|
8117
|
-
maximum: 100,
|
|
8118
|
-
default: 50,
|
|
8119
|
-
},
|
|
8120
|
-
cursor: {
|
|
8121
|
-
type: 'string',
|
|
8122
|
-
},
|
|
8123
|
-
},
|
|
8124
|
-
},
|
|
8125
|
-
output: {
|
|
8126
|
-
encoding: 'application/json',
|
|
8127
|
-
schema: {
|
|
8128
|
-
type: 'object',
|
|
8129
|
-
required: ['feed'],
|
|
8130
|
-
properties: {
|
|
8131
|
-
cursor: {
|
|
8132
|
-
type: 'string',
|
|
8133
|
-
},
|
|
8134
|
-
feed: {
|
|
8135
|
-
type: 'array',
|
|
8136
|
-
items: {
|
|
8137
|
-
type: 'ref',
|
|
8138
|
-
ref: 'lex:app.bsky.feed.defs#skeletonFeedPost',
|
|
8139
|
-
},
|
|
8140
|
-
},
|
|
8141
|
-
},
|
|
8142
|
-
},
|
|
8143
|
-
},
|
|
8144
|
-
errors: [
|
|
8145
|
-
{
|
|
8146
|
-
name: 'UnknownFeed',
|
|
8147
|
-
},
|
|
8148
|
-
],
|
|
8149
|
-
},
|
|
8150
|
-
},
|
|
8151
|
-
},
|
|
8152
8146
|
AppBskyUnspeccedSearchActorsSkeleton: {
|
|
8153
8147
|
lexicon: 1,
|
|
8154
8148
|
id: 'app.bsky.unspecced.searchActorsSkeleton',
|
|
@@ -8438,7 +8432,6 @@ export const ids = {
|
|
|
8438
8432
|
'app.bsky.unspecced.getPopularFeedGenerators',
|
|
8439
8433
|
AppBskyUnspeccedGetTaggedSuggestions:
|
|
8440
8434
|
'app.bsky.unspecced.getTaggedSuggestions',
|
|
8441
|
-
AppBskyUnspeccedGetTimelineSkeleton: 'app.bsky.unspecced.getTimelineSkeleton',
|
|
8442
8435
|
AppBskyUnspeccedSearchActorsSkeleton:
|
|
8443
8436
|
'app.bsky.unspecced.searchActorsSkeleton',
|
|
8444
8437
|
AppBskyUnspeccedSearchPostsSkeleton: 'app.bsky.unspecced.searchPostsSkeleton',
|
|
@@ -40,6 +40,7 @@ export interface ModEventView {
|
|
|
40
40
|
| ModEventEscalate
|
|
41
41
|
| ModEventMute
|
|
42
42
|
| ModEventEmail
|
|
43
|
+
| ModEventResolveAppeal
|
|
43
44
|
| { $type: string; [k: string]: unknown }
|
|
44
45
|
subject:
|
|
45
46
|
| RepoRef
|
|
@@ -76,6 +77,7 @@ export interface ModEventViewDetail {
|
|
|
76
77
|
| ModEventAcknowledge
|
|
77
78
|
| ModEventEscalate
|
|
78
79
|
| ModEventMute
|
|
80
|
+
| ModEventEmail
|
|
79
81
|
| ModEventResolveAppeal
|
|
80
82
|
| { $type: string; [k: string]: unknown }
|
|
81
83
|
subject:
|
|
@@ -15,10 +15,23 @@ export interface QueryParams {
|
|
|
15
15
|
createdBy?: string
|
|
16
16
|
/** Sort direction for the events. Defaults to descending order of created at timestamp. */
|
|
17
17
|
sortDirection: 'asc' | 'desc'
|
|
18
|
+
/** Retrieve events created after a given timestamp */
|
|
19
|
+
createdAfter?: string
|
|
20
|
+
/** Retrieve events created before a given timestamp */
|
|
21
|
+
createdBefore?: string
|
|
18
22
|
subject?: string
|
|
19
23
|
/** If true, events on all record types (posts, lists, profile etc.) owned by the did are returned */
|
|
20
24
|
includeAllUserRecords: boolean
|
|
21
25
|
limit: number
|
|
26
|
+
/** If true, only events with comments are returned */
|
|
27
|
+
hasComment?: boolean
|
|
28
|
+
/** If specified, only events with comments containing the keyword are returned */
|
|
29
|
+
comment?: string
|
|
30
|
+
/** If specified, only events where all of these labels were added are returned */
|
|
31
|
+
addedLabels?: string[]
|
|
32
|
+
/** If specified, only events where all of these labels were removed are returned */
|
|
33
|
+
removedLabels?: string[]
|
|
34
|
+
reportTypes?: string[]
|
|
22
35
|
cursor?: string
|
|
23
36
|
}
|
|
24
37
|
|
package/src/logger.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import pino from 'pino'
|
|
1
2
|
import pinoHttp from 'pino-http'
|
|
3
|
+
import * as jose from 'jose'
|
|
2
4
|
import { subsystemLogger } from '@atproto/common'
|
|
5
|
+
import { parseBasicAuth } from './auth-verifier'
|
|
3
6
|
|
|
4
7
|
export const dbLogger: ReturnType<typeof subsystemLogger> =
|
|
5
8
|
subsystemLogger('bsky:db')
|
|
@@ -21,5 +24,34 @@ export const loggerMiddleware = pinoHttp({
|
|
|
21
24
|
message: err?.message,
|
|
22
25
|
}
|
|
23
26
|
},
|
|
27
|
+
req: (req) => {
|
|
28
|
+
const serialized = pino.stdSerializers.req(req)
|
|
29
|
+
const authHeader = serialized.headers.authorization || ''
|
|
30
|
+
let auth: string | undefined = undefined
|
|
31
|
+
if (authHeader.startsWith('Bearer ')) {
|
|
32
|
+
const token = authHeader.slice('Bearer '.length)
|
|
33
|
+
const { iss } = jose.decodeJwt(token)
|
|
34
|
+
if (iss) {
|
|
35
|
+
auth = 'Bearer ' + iss
|
|
36
|
+
} else {
|
|
37
|
+
auth = 'Bearer Invalid'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (authHeader.startsWith('Basic ')) {
|
|
41
|
+
const parsed = parseBasicAuth(authHeader)
|
|
42
|
+
if (!parsed) {
|
|
43
|
+
auth = 'Basic Invalid'
|
|
44
|
+
} else {
|
|
45
|
+
auth = 'Basic ' + parsed.username
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
...serialized,
|
|
50
|
+
headers: {
|
|
51
|
+
...serialized.headers,
|
|
52
|
+
authorization: auth,
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
},
|
|
24
56
|
},
|
|
25
57
|
})
|
|
@@ -86,12 +86,6 @@ describe('feed generation', () => {
|
|
|
86
86
|
await network.close()
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
-
// @TODO enable once getFeed is implemented
|
|
90
|
-
it('describes the feed generator', async () => {
|
|
91
|
-
const res = await agent.api.app.bsky.feed.describeFeedGenerator()
|
|
92
|
-
expect(res.data.did).toBe(network.bsky.ctx.cfg.feedGenDid)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
89
|
it('feed gen records can be created.', async () => {
|
|
96
90
|
const all = await pdsAgent.api.app.bsky.feed.generator.create(
|
|
97
91
|
{ repo: alice, rkey: 'all' },
|
|
@@ -296,4 +296,13 @@ describe('notification views', () => {
|
|
|
296
296
|
),
|
|
297
297
|
)
|
|
298
298
|
})
|
|
299
|
+
|
|
300
|
+
it('fails open on clearly bad cursor.', async () => {
|
|
301
|
+
const { data: notifs } =
|
|
302
|
+
await agent.api.app.bsky.notification.listNotifications(
|
|
303
|
+
{ cursor: 'bad' },
|
|
304
|
+
{ headers: await network.serviceHeaders(alice) },
|
|
305
|
+
)
|
|
306
|
+
expect(notifs).toEqual({ notifications: [] })
|
|
307
|
+
})
|
|
299
308
|
})
|
|
@@ -298,4 +298,12 @@ describe('timeline views', () => {
|
|
|
298
298
|
),
|
|
299
299
|
)
|
|
300
300
|
})
|
|
301
|
+
|
|
302
|
+
it('fails open on clearly bad cursor.', async () => {
|
|
303
|
+
const { data: timeline } = await agent.api.app.bsky.feed.getTimeline(
|
|
304
|
+
{ cursor: 'bad' },
|
|
305
|
+
{ headers: await network.serviceHeaders(alice) },
|
|
306
|
+
)
|
|
307
|
+
expect(timeline).toEqual({ feed: [] })
|
|
308
|
+
})
|
|
301
309
|
})
|