@atproto/ozone 0.1.57 → 0.1.58
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 +9 -0
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +4 -2
- package/dist/api/moderation/emitEvent.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +22234 -9820
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +5 -1
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +2 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts.map +1 -1
- package/dist/mod-service/index.d.ts +14 -14
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +22 -5
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js +3 -2
- package/dist/mod-service/views.js.map +1 -1
- package/package.json +3 -3
- package/src/api/moderation/emitEvent.ts +11 -2
- package/src/lexicon/lexicons.ts +9 -3
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +2 -0
- package/src/lexicon/types/tools/ozone/moderation/queryEvents.ts +1 -1
- package/src/mod-service/index.ts +30 -7
- package/src/mod-service/views.ts +3 -2
- package/tests/__snapshots__/moderation-events.test.ts.snap +123 -0
- package/tests/ack-all-subjects-of-account.test.ts +76 -30
- package/tests/moderation-events.test.ts +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/ozone",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backend service for moderating the Bluesky network.",
|
|
6
6
|
"keywords": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"structured-headers": "^1.0.1",
|
|
33
33
|
"typed-emitter": "^2.1.0",
|
|
34
34
|
"uint8arrays": "3.0.0",
|
|
35
|
-
"@atproto/api": "^0.13.
|
|
35
|
+
"@atproto/api": "^0.13.19",
|
|
36
36
|
"@atproto/common": "^0.4.4",
|
|
37
37
|
"@atproto/crypto": "^0.4.2",
|
|
38
38
|
"@atproto/identity": "^0.4.3",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"ts-node": "^10.8.2",
|
|
54
54
|
"typescript": "^5.6.3",
|
|
55
55
|
"@atproto/lex-cli": "^0.5.2",
|
|
56
|
-
"@atproto/pds": "^0.4.
|
|
56
|
+
"@atproto/pds": "^0.4.75"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/* ../../lexicons/tools/ozone/*/*",
|
|
@@ -2,6 +2,7 @@ import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
|
|
|
2
2
|
import { Server } from '../../lexicon'
|
|
3
3
|
import AppContext from '../../context'
|
|
4
4
|
import {
|
|
5
|
+
isModEventAcknowledge,
|
|
5
6
|
isModEventDivert,
|
|
6
7
|
isModEventEmail,
|
|
7
8
|
isModEventLabel,
|
|
@@ -39,6 +40,7 @@ const handleModerationEvent = async ({
|
|
|
39
40
|
const moderationService = ctx.modService(db)
|
|
40
41
|
const settingService = ctx.settingService(db)
|
|
41
42
|
const { event } = input.body
|
|
43
|
+
const isAcknowledgeEvent = isModEventAcknowledge(event)
|
|
42
44
|
const isTakedownEvent = isModEventTakedown(event)
|
|
43
45
|
const isReverseTakedownEvent = isModEventReverseTakedown(event)
|
|
44
46
|
const isLabelEvent = isModEventLabel(event)
|
|
@@ -219,8 +221,15 @@ const handleModerationEvent = async ({
|
|
|
219
221
|
}
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
+
if (
|
|
225
|
+
(isTakedownEvent || isAcknowledgeEvent) &&
|
|
226
|
+
result.event.meta?.acknowledgeAccountSubjects
|
|
227
|
+
) {
|
|
228
|
+
await moderationTxn.resolveSubjectsForAccount(
|
|
229
|
+
subject.did,
|
|
230
|
+
createdBy,
|
|
231
|
+
result.event,
|
|
232
|
+
)
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
if (isLabelEvent) {
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -11310,6 +11310,11 @@ export const schemaDict = {
|
|
|
11310
11310
|
comment: {
|
|
11311
11311
|
type: 'string',
|
|
11312
11312
|
},
|
|
11313
|
+
acknowledgeAccountSubjects: {
|
|
11314
|
+
type: 'boolean',
|
|
11315
|
+
description:
|
|
11316
|
+
'If true, all other reports on content authored by this account will be resolved (acknowledged).',
|
|
11317
|
+
},
|
|
11313
11318
|
},
|
|
11314
11319
|
},
|
|
11315
11320
|
modEventEscalate: {
|
|
@@ -12218,7 +12223,7 @@ export const schemaDict = {
|
|
|
12218
12223
|
comment: {
|
|
12219
12224
|
type: 'string',
|
|
12220
12225
|
description:
|
|
12221
|
-
'If specified, only events with comments containing the keyword are returned',
|
|
12226
|
+
'If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition.',
|
|
12222
12227
|
},
|
|
12223
12228
|
addedLabels: {
|
|
12224
12229
|
type: 'array',
|
|
@@ -13489,8 +13494,9 @@ export const schemaDict = {
|
|
|
13489
13494
|
},
|
|
13490
13495
|
},
|
|
13491
13496
|
},
|
|
13492
|
-
}
|
|
13493
|
-
|
|
13497
|
+
} as const satisfies Record<string, LexiconDoc>
|
|
13498
|
+
|
|
13499
|
+
export const schemas = Object.values(schemaDict)
|
|
13494
13500
|
export const lexicons: Lexicons = new Lexicons(schemas)
|
|
13495
13501
|
export const ids = {
|
|
13496
13502
|
ComAtprotoAdminDefs: 'com.atproto.admin.defs',
|
|
@@ -300,6 +300,8 @@ export function validateModEventLabel(v: unknown): ValidationResult {
|
|
|
300
300
|
|
|
301
301
|
export interface ModEventAcknowledge {
|
|
302
302
|
comment?: string
|
|
303
|
+
/** If true, all other reports on content authored by this account will be resolved (acknowledged). */
|
|
304
|
+
acknowledgeAccountSubjects?: boolean
|
|
303
305
|
[k: string]: unknown
|
|
304
306
|
}
|
|
305
307
|
|
|
@@ -29,7 +29,7 @@ export interface QueryParams {
|
|
|
29
29
|
limit: number
|
|
30
30
|
/** If true, only events with comments are returned */
|
|
31
31
|
hasComment?: boolean
|
|
32
|
-
/** If specified, only events with comments containing the keyword are returned */
|
|
32
|
+
/** If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition. */
|
|
33
33
|
comment?: string
|
|
34
34
|
/** If specified, only events where all of these labels were added are returned */
|
|
35
35
|
addedLabels?: string[]
|
package/src/mod-service/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
isRecordEvent,
|
|
24
24
|
REVIEWESCALATED,
|
|
25
25
|
REVIEWOPEN,
|
|
26
|
+
isModEventAcknowledge,
|
|
26
27
|
} from '../lexicon/types/tools/ozone/moderation/defs'
|
|
27
28
|
import { RepoRef, RepoBlobRef } from '../lexicon/types/com/atproto/admin/defs'
|
|
28
29
|
import {
|
|
@@ -222,8 +223,20 @@ export class ModerationService {
|
|
|
222
223
|
if (createdBefore) {
|
|
223
224
|
builder = builder.where('createdAt', '<=', createdBefore)
|
|
224
225
|
}
|
|
226
|
+
|
|
225
227
|
if (comment) {
|
|
226
|
-
|
|
228
|
+
// the input may end in || in which case, there may be item in the array which is just '' and we want to ignore those
|
|
229
|
+
const keywords = comment.split('||').filter((keyword) => !!keyword.trim())
|
|
230
|
+
if (keywords.length > 1) {
|
|
231
|
+
builder = builder.where((qb) => {
|
|
232
|
+
keywords.forEach((keyword) => {
|
|
233
|
+
qb = qb.orWhere('comment', 'ilike', `%${keyword}%`)
|
|
234
|
+
})
|
|
235
|
+
return qb
|
|
236
|
+
})
|
|
237
|
+
} else if (keywords.length === 1) {
|
|
238
|
+
builder = builder.where('comment', 'ilike', `%${keywords[0]}%`)
|
|
239
|
+
}
|
|
227
240
|
}
|
|
228
241
|
if (hasComment) {
|
|
229
242
|
builder = builder.where('comment', 'is not', null)
|
|
@@ -302,7 +315,11 @@ export class ModerationService {
|
|
|
302
315
|
return await builder.execute()
|
|
303
316
|
}
|
|
304
317
|
|
|
305
|
-
async resolveSubjectsForAccount(
|
|
318
|
+
async resolveSubjectsForAccount(
|
|
319
|
+
did: string,
|
|
320
|
+
createdBy: string,
|
|
321
|
+
accountEvent: ModerationEventRow,
|
|
322
|
+
) {
|
|
306
323
|
const subjectsToBeResolved = await this.db.db
|
|
307
324
|
.selectFrom('moderation_subject_status')
|
|
308
325
|
.where('did', '=', did)
|
|
@@ -315,6 +332,10 @@ export class ModerationService {
|
|
|
315
332
|
return
|
|
316
333
|
}
|
|
317
334
|
|
|
335
|
+
let accountEventInfo = `Account Event ID: ${accountEvent.id}`
|
|
336
|
+
if (accountEvent.comment) {
|
|
337
|
+
accountEventInfo += ` | Account Event Comment: ${accountEvent.comment}`
|
|
338
|
+
}
|
|
318
339
|
// Process subjects in chunks of 100 since each of these will trigger multiple db queries
|
|
319
340
|
for (const subjects of chunkArray(subjectsToBeResolved, 100)) {
|
|
320
341
|
await Promise.all(
|
|
@@ -323,13 +344,13 @@ export class ModerationService {
|
|
|
323
344
|
createdBy,
|
|
324
345
|
subject: subjectFromStatusRow(subject),
|
|
325
346
|
}
|
|
347
|
+
|
|
326
348
|
// For consistency's sake, when acknowledging appealed subjects, we should first resolve the appeal
|
|
327
349
|
if (subject.appealed) {
|
|
328
350
|
await this.logEvent({
|
|
329
351
|
event: {
|
|
330
352
|
$type: 'tools.ozone.moderation.defs#modEventResolveAppeal',
|
|
331
|
-
comment:
|
|
332
|
-
'[AUTO_RESOLVE_FOR_TAKENDOWN_ACCOUNT]: Automatically resolving all appealed content for a takendown account',
|
|
353
|
+
comment: `[AUTO_RESOLVE_ON_ACCOUNT_ACTION]: Automatically resolving all appealed content due to account level action | ${accountEventInfo}`,
|
|
333
354
|
},
|
|
334
355
|
...eventData,
|
|
335
356
|
})
|
|
@@ -338,8 +359,7 @@ export class ModerationService {
|
|
|
338
359
|
await this.logEvent({
|
|
339
360
|
event: {
|
|
340
361
|
$type: 'tools.ozone.moderation.defs#modEventAcknowledge',
|
|
341
|
-
comment:
|
|
342
|
-
'[AUTO_RESOLVE_FOR_TAKENDOWN_ACCOUNT]: Automatically resolving all reported content for a takendown account',
|
|
362
|
+
comment: `[AUTO_RESOLVE_ON_ACCOUNT_ACTION]: Automatically resolving all reported content due to account level action | ${accountEventInfo}`,
|
|
343
363
|
},
|
|
344
364
|
...eventData,
|
|
345
365
|
})
|
|
@@ -408,7 +428,10 @@ export class ModerationService {
|
|
|
408
428
|
if (event.cid) meta.cid = event.cid
|
|
409
429
|
}
|
|
410
430
|
|
|
411
|
-
if (
|
|
431
|
+
if (
|
|
432
|
+
(isModEventTakedown(event) || isModEventAcknowledge(event)) &&
|
|
433
|
+
event.acknowledgeAccountSubjects
|
|
434
|
+
) {
|
|
412
435
|
meta.acknowledgeAccountSubjects = true
|
|
413
436
|
}
|
|
414
437
|
|
package/src/mod-service/views.ts
CHANGED
|
@@ -127,12 +127,13 @@ export class ModerationViews {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
if (
|
|
130
|
-
event.action === 'tools.ozone.moderation.defs#modEventTakedown'
|
|
130
|
+
(event.action === 'tools.ozone.moderation.defs#modEventTakedown' ||
|
|
131
|
+
event.action === 'tools.ozone.moderation.defs#modEventAcknowledge') &&
|
|
131
132
|
event.meta?.acknowledgeAccountSubjects
|
|
132
133
|
) {
|
|
133
134
|
eventView.event = {
|
|
134
135
|
...eventView.event,
|
|
135
|
-
acknowledgeAccountSubjects: event.meta
|
|
136
|
+
acknowledgeAccountSubjects: event.meta.acknowledgeAccountSubjects,
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
|
|
@@ -197,3 +197,126 @@ Array [
|
|
|
197
197
|
},
|
|
198
198
|
]
|
|
199
199
|
`;
|
|
200
|
+
|
|
201
|
+
exports[`moderation-events query events returns events matching multiple keywords in comment 1`] = `
|
|
202
|
+
Array [
|
|
203
|
+
Object {
|
|
204
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
205
|
+
"createdBy": "user(1)",
|
|
206
|
+
"creatorHandle": "bob.test",
|
|
207
|
+
"event": Object {
|
|
208
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
209
|
+
"comment": "rainy days feel lazy",
|
|
210
|
+
"isReporterMuted": false,
|
|
211
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
212
|
+
},
|
|
213
|
+
"id": 16,
|
|
214
|
+
"subject": Object {
|
|
215
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
216
|
+
"did": "user(0)",
|
|
217
|
+
},
|
|
218
|
+
"subjectBlobCids": Array [],
|
|
219
|
+
"subjectHandle": "alice.test",
|
|
220
|
+
},
|
|
221
|
+
Object {
|
|
222
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
223
|
+
"createdBy": "user(1)",
|
|
224
|
+
"creatorHandle": "bob.test",
|
|
225
|
+
"event": Object {
|
|
226
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
227
|
+
"comment": "november rain",
|
|
228
|
+
"isReporterMuted": false,
|
|
229
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
230
|
+
},
|
|
231
|
+
"id": 15,
|
|
232
|
+
"subject": Object {
|
|
233
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
234
|
+
"did": "user(0)",
|
|
235
|
+
},
|
|
236
|
+
"subjectBlobCids": Array [],
|
|
237
|
+
"subjectHandle": "alice.test",
|
|
238
|
+
},
|
|
239
|
+
]
|
|
240
|
+
`;
|
|
241
|
+
|
|
242
|
+
exports[`moderation-events query events returns events matching multiple keywords in comment 2`] = `
|
|
243
|
+
Array [
|
|
244
|
+
Object {
|
|
245
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
246
|
+
"createdBy": "user(1)",
|
|
247
|
+
"creatorHandle": "bob.test",
|
|
248
|
+
"event": Object {
|
|
249
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
250
|
+
"comment": "rainy days feel lazy",
|
|
251
|
+
"isReporterMuted": false,
|
|
252
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
253
|
+
},
|
|
254
|
+
"id": 16,
|
|
255
|
+
"subject": Object {
|
|
256
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
257
|
+
"did": "user(0)",
|
|
258
|
+
},
|
|
259
|
+
"subjectBlobCids": Array [],
|
|
260
|
+
"subjectHandle": "alice.test",
|
|
261
|
+
},
|
|
262
|
+
Object {
|
|
263
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
264
|
+
"createdBy": "user(1)",
|
|
265
|
+
"creatorHandle": "bob.test",
|
|
266
|
+
"event": Object {
|
|
267
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
268
|
+
"comment": "november rain",
|
|
269
|
+
"isReporterMuted": false,
|
|
270
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
271
|
+
},
|
|
272
|
+
"id": 15,
|
|
273
|
+
"subject": Object {
|
|
274
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
275
|
+
"did": "user(0)",
|
|
276
|
+
},
|
|
277
|
+
"subjectBlobCids": Array [],
|
|
278
|
+
"subjectHandle": "alice.test",
|
|
279
|
+
},
|
|
280
|
+
]
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
exports[`moderation-events query events returns events matching multiple keywords in comment 3`] = `
|
|
284
|
+
Array [
|
|
285
|
+
Object {
|
|
286
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
287
|
+
"createdBy": "user(1)",
|
|
288
|
+
"creatorHandle": "bob.test",
|
|
289
|
+
"event": Object {
|
|
290
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
291
|
+
"comment": "rainy days feel lazy",
|
|
292
|
+
"isReporterMuted": false,
|
|
293
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
294
|
+
},
|
|
295
|
+
"id": 16,
|
|
296
|
+
"subject": Object {
|
|
297
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
298
|
+
"did": "user(0)",
|
|
299
|
+
},
|
|
300
|
+
"subjectBlobCids": Array [],
|
|
301
|
+
"subjectHandle": "alice.test",
|
|
302
|
+
},
|
|
303
|
+
Object {
|
|
304
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
305
|
+
"createdBy": "user(1)",
|
|
306
|
+
"creatorHandle": "bob.test",
|
|
307
|
+
"event": Object {
|
|
308
|
+
"$type": "tools.ozone.moderation.defs#modEventReport",
|
|
309
|
+
"comment": "november rain",
|
|
310
|
+
"isReporterMuted": false,
|
|
311
|
+
"reportType": "com.atproto.moderation.defs#reasonSpam",
|
|
312
|
+
},
|
|
313
|
+
"id": 15,
|
|
314
|
+
"subject": Object {
|
|
315
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
316
|
+
"did": "user(0)",
|
|
317
|
+
},
|
|
318
|
+
"subjectBlobCids": Array [],
|
|
319
|
+
"subjectHandle": "alice.test",
|
|
320
|
+
},
|
|
321
|
+
]
|
|
322
|
+
`;
|
|
@@ -35,28 +35,28 @@ describe('moderation', () => {
|
|
|
35
35
|
cid: ref.cidStr,
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const getReviewStateBySubject = (subjects: SubjectStatusView[]) => {
|
|
39
|
+
const states = new Map<string, SubjectStatusView>()
|
|
40
|
+
|
|
41
|
+
subjects.forEach((item) => {
|
|
42
|
+
if (ComAtprotoRepoStrongRef.isMain(item.subject)) {
|
|
43
|
+
states.set(item.subject.uri, item)
|
|
44
|
+
} else if (isRepoRef(item.subject)) {
|
|
45
|
+
states.set(item.subject.did, item)
|
|
46
|
+
}
|
|
41
47
|
})
|
|
42
|
-
sc = network.getSeedClient()
|
|
43
|
-
modClient = network.ozone.getModClient()
|
|
44
|
-
await basicSeed(sc)
|
|
45
|
-
await network.processAll()
|
|
46
|
-
})
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
49
|
+
return states
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
const postOne = sc.posts[
|
|
54
|
-
const postTwo = sc.posts[
|
|
52
|
+
const reportUserAndPost = async (did: string) => {
|
|
53
|
+
const postOne = sc.posts[did][0].ref
|
|
54
|
+
const postTwo = sc.posts[did][1].ref
|
|
55
55
|
await Promise.all([
|
|
56
56
|
sc.createReport({
|
|
57
57
|
reasonType: REASONSPAM,
|
|
58
|
-
subject: repoSubject(
|
|
59
|
-
reportedBy: sc.dids.
|
|
58
|
+
subject: repoSubject(did),
|
|
59
|
+
reportedBy: sc.dids.carol,
|
|
60
60
|
}),
|
|
61
61
|
sc.createReport({
|
|
62
62
|
reasonType: REASONOTHER,
|
|
@@ -71,7 +71,6 @@ describe('moderation', () => {
|
|
|
71
71
|
reportedBy: sc.dids.carol,
|
|
72
72
|
}),
|
|
73
73
|
])
|
|
74
|
-
|
|
75
74
|
await modClient.emitEvent({
|
|
76
75
|
event: {
|
|
77
76
|
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
@@ -80,6 +79,26 @@ describe('moderation', () => {
|
|
|
80
79
|
subject: recordSubject(postTwo),
|
|
81
80
|
})
|
|
82
81
|
|
|
82
|
+
return { postOne, postTwo }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
beforeAll(async () => {
|
|
86
|
+
network = await TestNetwork.create({
|
|
87
|
+
dbPostgresSchema: 'ozone_ack_all_subjects_of_account',
|
|
88
|
+
})
|
|
89
|
+
sc = network.getSeedClient()
|
|
90
|
+
modClient = network.ozone.getModClient()
|
|
91
|
+
await basicSeed(sc)
|
|
92
|
+
await network.processAll()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
afterAll(async () => {
|
|
96
|
+
await network.close()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('acknowledges all open/escalated review subjects with takedown.', async () => {
|
|
100
|
+
const { postOne, postTwo } = await reportUserAndPost(sc.dids.bob)
|
|
101
|
+
|
|
83
102
|
const { subjectStatuses: statusesBefore } = await modClient.queryStatuses({
|
|
84
103
|
subject: sc.dids.bob,
|
|
85
104
|
includeAllUserRecords: true,
|
|
@@ -95,19 +114,46 @@ describe('moderation', () => {
|
|
|
95
114
|
includeAllUserRecords: true,
|
|
96
115
|
})
|
|
97
116
|
|
|
98
|
-
const
|
|
99
|
-
|
|
117
|
+
const reviewStatesBefore = getReviewStateBySubject(statusesBefore)
|
|
118
|
+
const reviewStatesAfter = getReviewStateBySubject(statusesAfter)
|
|
119
|
+
|
|
120
|
+
// Check that review states before were different for different subjects
|
|
121
|
+
expect(reviewStatesBefore.get(postOne.uriStr)?.reviewState).toBe(REVIEWOPEN)
|
|
122
|
+
expect(reviewStatesBefore.get(postTwo.uriStr)?.reviewState).toBe(
|
|
123
|
+
REVIEWESCALATED,
|
|
124
|
+
)
|
|
125
|
+
expect(reviewStatesBefore.get(sc.dids.bob)?.reviewState).toBe(REVIEWOPEN)
|
|
100
126
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
127
|
+
// Check that review states after are all closed
|
|
128
|
+
expect(reviewStatesAfter.get(postOne.uriStr)?.reviewState).toBe(
|
|
129
|
+
REVIEWCLOSED,
|
|
130
|
+
)
|
|
131
|
+
expect(reviewStatesAfter.get(postTwo.uriStr)?.reviewState).toBe(
|
|
132
|
+
REVIEWCLOSED,
|
|
133
|
+
)
|
|
134
|
+
expect(reviewStatesAfter.get(sc.dids.bob)?.reviewState).toBe(REVIEWCLOSED)
|
|
135
|
+
})
|
|
108
136
|
|
|
109
|
-
|
|
110
|
-
}
|
|
137
|
+
it('acknowledges all open/escalated review subjects with acknowledge.', async () => {
|
|
138
|
+
const { postOne, postTwo } = await reportUserAndPost(sc.dids.alice)
|
|
139
|
+
|
|
140
|
+
const { subjectStatuses: statusesBefore } = await modClient.queryStatuses({
|
|
141
|
+
subject: sc.dids.alice,
|
|
142
|
+
includeAllUserRecords: true,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
await modClient.emitEvent({
|
|
146
|
+
subject: repoSubject(sc.dids.alice),
|
|
147
|
+
event: {
|
|
148
|
+
$type: 'tools.ozone.moderation.defs#modEventAcknowledge',
|
|
149
|
+
acknowledgeAccountSubjects: true,
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const { subjectStatuses: statusesAfter } = await modClient.queryStatuses({
|
|
154
|
+
subject: sc.dids.alice,
|
|
155
|
+
includeAllUserRecords: true,
|
|
156
|
+
})
|
|
111
157
|
|
|
112
158
|
const reviewStatesBefore = getReviewStateBySubject(statusesBefore)
|
|
113
159
|
const reviewStatesAfter = getReviewStateBySubject(statusesAfter)
|
|
@@ -117,7 +163,7 @@ describe('moderation', () => {
|
|
|
117
163
|
expect(reviewStatesBefore.get(postTwo.uriStr)?.reviewState).toBe(
|
|
118
164
|
REVIEWESCALATED,
|
|
119
165
|
)
|
|
120
|
-
expect(reviewStatesBefore.get(sc.dids.
|
|
166
|
+
expect(reviewStatesBefore.get(sc.dids.alice)?.reviewState).toBe(REVIEWOPEN)
|
|
121
167
|
|
|
122
168
|
// Check that review states after are all closed
|
|
123
169
|
expect(reviewStatesAfter.get(postOne.uriStr)?.reviewState).toBe(
|
|
@@ -126,6 +172,6 @@ describe('moderation', () => {
|
|
|
126
172
|
expect(reviewStatesAfter.get(postTwo.uriStr)?.reviewState).toBe(
|
|
127
173
|
REVIEWCLOSED,
|
|
128
174
|
)
|
|
129
|
-
expect(reviewStatesAfter.get(sc.dids.
|
|
175
|
+
expect(reviewStatesAfter.get(sc.dids.alice)?.reviewState).toBe(REVIEWCLOSED)
|
|
130
176
|
})
|
|
131
177
|
})
|
|
@@ -224,6 +224,46 @@ describe('moderation-events', () => {
|
|
|
224
224
|
expect(eventsWithComment.events.length).toEqual(10)
|
|
225
225
|
})
|
|
226
226
|
|
|
227
|
+
it('returns events matching multiple keywords in comment', async () => {
|
|
228
|
+
await sc.createReport({
|
|
229
|
+
reasonType: REASONSPAM,
|
|
230
|
+
reason: 'november rain',
|
|
231
|
+
subject: {
|
|
232
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
233
|
+
did: sc.dids.alice,
|
|
234
|
+
},
|
|
235
|
+
reportedBy: sc.dids.bob,
|
|
236
|
+
})
|
|
237
|
+
await sc.createReport({
|
|
238
|
+
reasonType: REASONSPAM,
|
|
239
|
+
reason: 'rainy days feel lazy',
|
|
240
|
+
subject: {
|
|
241
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
242
|
+
did: sc.dids.alice,
|
|
243
|
+
},
|
|
244
|
+
reportedBy: sc.dids.bob,
|
|
245
|
+
})
|
|
246
|
+
const [eventsMatchingBothKeywords, unusedTrailingSeparator, extraSpaces] =
|
|
247
|
+
await Promise.all([
|
|
248
|
+
modClient.queryEvents({
|
|
249
|
+
hasComment: true,
|
|
250
|
+
comment: 'november||lazy',
|
|
251
|
+
}),
|
|
252
|
+
modClient.queryEvents({
|
|
253
|
+
hasComment: true,
|
|
254
|
+
comment: 'november||lazy||',
|
|
255
|
+
}),
|
|
256
|
+
modClient.queryEvents({
|
|
257
|
+
hasComment: true,
|
|
258
|
+
comment: '||november||lazy|| ',
|
|
259
|
+
}),
|
|
260
|
+
])
|
|
261
|
+
|
|
262
|
+
expect(forSnapshot(eventsMatchingBothKeywords.events)).toMatchSnapshot()
|
|
263
|
+
expect(forSnapshot(unusedTrailingSeparator.events)).toMatchSnapshot()
|
|
264
|
+
expect(forSnapshot(extraSpaces.events)).toMatchSnapshot()
|
|
265
|
+
})
|
|
266
|
+
|
|
227
267
|
it('returns events matching filter params for labels', async () => {
|
|
228
268
|
const [negatedLabelEvent, createdLabelEvent] = await Promise.all([
|
|
229
269
|
modClient.emitEvent({
|