@atproto/ozone 0.0.17-next.0 → 0.0.17-next.1
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/dist/api/util.d.ts +10 -0
- package/dist/auth-verifier.d.ts +2 -2
- package/dist/communication-service/template.d.ts +2 -2
- package/dist/config/config.d.ts +5 -0
- package/dist/config/env.d.ts +2 -0
- package/dist/context.d.ts +6 -0
- package/dist/daemon/blob-diverter.d.ts +26 -0
- package/dist/daemon/event-pusher.d.ts +4 -0
- package/dist/daemon/index.d.ts +1 -0
- package/dist/db/index.js +21 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20240228T003647759Z-add-label-sigs.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/schema/index.d.ts +2 -1
- package/dist/db/schema/label.d.ts +4 -0
- package/dist/db/schema/moderation_event.d.ts +1 -1
- package/dist/db/schema/moderation_subject_status.d.ts +1 -1
- package/dist/db/schema/signing_key.d.ts +9 -0
- package/dist/index.js +10949 -10628
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +48 -28
- package/dist/lexicon/lexicons.d.ts +4641 -4650
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +7 -7
- 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/com/atproto/admin/defs.d.ts +0 -305
- package/dist/lexicon/types/com/atproto/label/defs.d.ts +5 -0
- package/dist/lexicon/types/{com/atproto/admin/createCommunicationTemplate.d.ts → tools/ozone/communication/createTemplate.d.ts} +2 -2
- package/dist/lexicon/types/tools/ozone/communication/defs.d.ts +14 -0
- package/dist/lexicon/types/{com/atproto/admin/listCommunicationTemplates.d.ts → tools/ozone/communication/listTemplates.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin/updateCommunicationTemplate.d.ts → tools/ozone/communication/updateTemplate.d.ts} +2 -2
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +269 -0
- package/dist/lexicon/types/{com/atproto/admin/emitModerationEvent.d.ts → tools/ozone/moderation/emitEvent.d.ts} +5 -4
- package/dist/lexicon/types/{com/atproto/admin/getModerationEvent.d.ts → tools/ozone/moderation/getEvent.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRecord.d.ts +2 -2
- package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRepo.d.ts +2 -2
- package/dist/lexicon/types/{com/atproto/admin/queryModerationEvents.d.ts → tools/ozone/moderation/queryEvents.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin/queryModerationStatuses.d.ts → tools/ozone/moderation/queryStatuses.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/searchRepos.d.ts +2 -2
- package/dist/mod-service/index.d.ts +16 -15
- package/dist/mod-service/subject.d.ts +1 -1
- package/dist/mod-service/types.d.ts +2 -2
- package/dist/mod-service/util.d.ts +6 -0
- package/dist/mod-service/views.d.ts +9 -3
- package/dist/sequencer/sequencer.d.ts +6 -4
- package/dist/util.d.ts +2 -0
- package/package.json +7 -7
- package/src/api/{admin/createCommunicationTemplate.ts → communication/createTemplate.ts} +1 -1
- package/src/api/{admin/deleteCommunicationTemplate.ts → communication/deleteTemplate.ts} +1 -1
- package/src/api/{admin/listCommunicationTemplates.ts → communication/listTemplates.ts} +1 -1
- package/src/api/{admin/updateCommunicationTemplate.ts → communication/updateTemplate.ts} +1 -1
- package/src/api/index.ts +21 -21
- package/src/api/{temp → label}/fetchLabels.ts +4 -2
- package/src/api/label/queryLabels.ts +4 -2
- package/src/api/moderation/emitEvent.ts +218 -0
- package/src/api/{admin/getModerationEvent.ts → moderation/getEvent.ts} +1 -1
- package/src/api/{admin → moderation}/getRecord.ts +2 -2
- package/src/api/{admin → moderation}/getRepo.ts +2 -2
- package/src/api/{admin/queryModerationEvents.ts → moderation/queryEvents.ts} +2 -2
- package/src/api/{admin/queryModerationStatuses.ts → moderation/queryStatuses.ts} +2 -2
- package/src/api/{admin → moderation}/searchRepos.ts +1 -1
- package/src/api/{moderation → report}/createReport.ts +1 -1
- package/src/api/util.ts +119 -0
- package/src/auth-verifier.ts +2 -2
- package/src/communication-service/template.ts +2 -2
- package/src/config/config.ts +14 -0
- package/src/config/env.ts +4 -0
- package/src/context.ts +35 -9
- package/src/daemon/blob-diverter.ts +150 -0
- package/src/daemon/context.ts +9 -5
- package/src/daemon/event-pusher.ts +49 -14
- package/src/daemon/index.ts +1 -0
- package/src/db/migrations/20240228T003647759Z-add-label-sigs.ts +25 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/index.ts +2 -0
- package/src/db/schema/label.ts +3 -0
- package/src/db/schema/moderation_event.ts +11 -11
- package/src/db/schema/moderation_subject_status.ts +1 -1
- package/src/db/schema/signing_key.ts +10 -0
- package/src/lexicon/index.ts +178 -138
- package/src/lexicon/lexicons.ts +6078 -6106
- package/src/lexicon/types/app/bsky/actor/defs.ts +11 -11
- 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/com/atproto/admin/defs.ts +0 -697
- package/src/lexicon/types/com/atproto/label/defs.ts +10 -0
- package/src/lexicon/types/{com/atproto/admin/createCommunicationTemplate.ts → tools/ozone/communication/createTemplate.ts} +2 -2
- package/src/lexicon/types/tools/ozone/communication/defs.ts +35 -0
- package/src/lexicon/types/{com/atproto/admin/listCommunicationTemplates.ts → tools/ozone/communication/listTemplates.ts} +2 -2
- package/src/lexicon/types/{com/atproto/admin/updateCommunicationTemplate.ts → tools/ozone/communication/updateTemplate.ts} +2 -2
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +641 -0
- package/src/lexicon/types/{com/atproto/admin/emitModerationEvent.ts → tools/ozone/moderation/emitEvent.ts} +15 -14
- package/src/lexicon/types/{com/atproto/admin/getModerationEvent.ts → tools/ozone/moderation/getEvent.ts} +2 -2
- package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRecord.ts +2 -2
- package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRepo.ts +2 -2
- package/src/lexicon/types/{com/atproto/admin/queryModerationEvents.ts → tools/ozone/moderation/queryEvents.ts} +3 -3
- package/src/lexicon/types/{com/atproto/admin/queryModerationStatuses.ts → tools/ozone/moderation/queryStatuses.ts} +2 -2
- package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/searchRepos.ts +2 -2
- package/src/mod-service/index.ts +42 -47
- package/src/mod-service/lang.ts +1 -1
- package/src/mod-service/status.ts +19 -16
- package/src/mod-service/subject.ts +1 -1
- package/src/mod-service/types.ts +10 -10
- package/src/mod-service/util.ts +49 -5
- package/src/mod-service/views.ts +45 -18
- package/src/sequencer/sequencer.ts +12 -11
- package/src/util.ts +21 -0
- package/tests/__snapshots__/blob-divert.test.ts.snap +22 -0
- package/tests/__snapshots__/get-record.test.ts.snap +10 -2
- package/tests/__snapshots__/get-repo.test.ts.snap +5 -1
- package/tests/__snapshots__/moderation-events.test.ts.snap +8 -8
- package/tests/__snapshots__/moderation-statuses.test.ts.snap +6 -6
- package/tests/_util.ts +5 -0
- package/tests/blob-divert.test.ts +87 -0
- package/tests/communication-templates.test.ts +30 -34
- package/tests/db.test.ts +6 -6
- package/tests/get-record.test.ts +6 -6
- package/tests/get-repo.test.ts +11 -11
- package/tests/moderation-appeals.test.ts +28 -28
- package/tests/moderation-events.test.ts +44 -44
- package/tests/moderation-status-tags.test.ts +8 -10
- package/tests/moderation-statuses.test.ts +27 -27
- package/tests/moderation.test.ts +50 -57
- package/tests/query-labels.test.ts +86 -10
- package/tests/repo-search.test.ts +8 -8
- package/tests/sequencer.test.ts +6 -3
- package/dist/api/admin/util.d.ts +0 -5
- package/dist/api/moderation/util.d.ts +0 -4
- package/src/api/admin/emitModerationEvent.ts +0 -174
- package/src/api/admin/util.ts +0 -54
- package/src/api/moderation/util.ts +0 -67
- /package/dist/api/{admin/createCommunicationTemplate.d.ts → communication/createTemplate.d.ts} +0 -0
- /package/dist/api/{admin/deleteCommunicationTemplate.d.ts → communication/deleteTemplate.d.ts} +0 -0
- /package/dist/api/{admin/emitModerationEvent.d.ts → communication/listTemplates.d.ts} +0 -0
- /package/dist/api/{admin/getModerationEvent.d.ts → communication/updateTemplate.d.ts} +0 -0
- /package/dist/api/{temp → label}/fetchLabels.d.ts +0 -0
- /package/dist/api/{admin/getRecord.d.ts → moderation/emitEvent.d.ts} +0 -0
- /package/dist/api/{admin/getRepo.d.ts → moderation/getEvent.d.ts} +0 -0
- /package/dist/api/{admin/listCommunicationTemplates.d.ts → moderation/getRecord.d.ts} +0 -0
- /package/dist/api/{admin/queryModerationEvents.d.ts → moderation/getRepo.d.ts} +0 -0
- /package/dist/api/{admin/queryModerationStatuses.d.ts → moderation/queryEvents.d.ts} +0 -0
- /package/dist/api/{admin/searchRepos.d.ts → moderation/queryStatuses.d.ts} +0 -0
- /package/dist/api/{admin/updateCommunicationTemplate.d.ts → moderation/searchRepos.d.ts} +0 -0
- /package/dist/api/{moderation → report}/createReport.d.ts +0 -0
- /package/dist/lexicon/types/{com/atproto/admin/deleteCommunicationTemplate.d.ts → tools/ozone/communication/deleteTemplate.d.ts} +0 -0
- /package/src/lexicon/types/{com/atproto/admin/deleteCommunicationTemplate.ts → tools/ozone/communication/deleteTemplate.ts} +0 -0
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
ModeratorClient,
|
|
7
7
|
} from '@atproto/dev-env'
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
ToolsOzoneModerationDefs,
|
|
10
|
+
ToolsOzoneModerationQueryStatuses,
|
|
11
11
|
} from '@atproto/api'
|
|
12
12
|
import { forSnapshot } from './_util'
|
|
13
13
|
import {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
REVIEWOPEN,
|
|
19
19
|
REVIEWNONE,
|
|
20
|
-
} from '../src/lexicon/types/
|
|
20
|
+
} from '../src/lexicon/types/tools/ozone/moderation/defs'
|
|
21
21
|
|
|
22
22
|
describe('moderation-statuses', () => {
|
|
23
23
|
let network: TestNetwork
|
|
@@ -79,19 +79,19 @@ describe('moderation-statuses', () => {
|
|
|
79
79
|
|
|
80
80
|
describe('query statuses', () => {
|
|
81
81
|
it('returns statuses for subjects that received moderation events', async () => {
|
|
82
|
-
const response = await modClient.
|
|
82
|
+
const response = await modClient.queryStatuses({})
|
|
83
83
|
|
|
84
84
|
expect(forSnapshot(response.subjectStatuses)).toMatchSnapshot()
|
|
85
85
|
})
|
|
86
86
|
|
|
87
87
|
it('returns statuses filtered by subject language', async () => {
|
|
88
|
-
const klingonQueue = await modClient.
|
|
88
|
+
const klingonQueue = await modClient.queryStatuses({
|
|
89
89
|
tags: ['lang:i'],
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
expect(forSnapshot(klingonQueue.subjectStatuses)).toMatchSnapshot()
|
|
93
93
|
|
|
94
|
-
const nonKlingonQueue = await modClient.
|
|
94
|
+
const nonKlingonQueue = await modClient.queryStatuses({
|
|
95
95
|
excludeTags: ['lang:i'],
|
|
96
96
|
})
|
|
97
97
|
|
|
@@ -104,13 +104,13 @@ describe('moderation-statuses', () => {
|
|
|
104
104
|
it('returns paginated statuses', async () => {
|
|
105
105
|
// We know there will be exactly 4 statuses in db
|
|
106
106
|
const getPaginatedStatuses = async (
|
|
107
|
-
params:
|
|
107
|
+
params: ToolsOzoneModerationQueryStatuses.QueryParams,
|
|
108
108
|
) => {
|
|
109
109
|
let cursor: string | undefined = ''
|
|
110
|
-
const statuses:
|
|
110
|
+
const statuses: ToolsOzoneModerationDefs.SubjectStatusView[] = []
|
|
111
111
|
let count = 0
|
|
112
112
|
do {
|
|
113
|
-
const results = await modClient.
|
|
113
|
+
const results = await modClient.queryStatuses({
|
|
114
114
|
limit: 1,
|
|
115
115
|
cursor,
|
|
116
116
|
...params,
|
|
@@ -128,10 +128,10 @@ describe('moderation-statuses', () => {
|
|
|
128
128
|
expect(list[0].id).toEqual(7)
|
|
129
129
|
expect(list[list.length - 1].id).toEqual(1)
|
|
130
130
|
|
|
131
|
-
await modClient.
|
|
131
|
+
await modClient.emitEvent({
|
|
132
132
|
subject: list[1].subject,
|
|
133
133
|
event: {
|
|
134
|
-
$type: '
|
|
134
|
+
$type: 'tools.ozone.moderation.defs#modEventAcknowledge',
|
|
135
135
|
comment: 'X',
|
|
136
136
|
},
|
|
137
137
|
})
|
|
@@ -160,7 +160,7 @@ describe('moderation-statuses', () => {
|
|
|
160
160
|
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
161
161
|
}
|
|
162
162
|
const getBobsAccountStatus = async () => {
|
|
163
|
-
const data = await modClient.
|
|
163
|
+
const data = await modClient.queryStatuses({
|
|
164
164
|
subject: bobsAccount.did,
|
|
165
165
|
})
|
|
166
166
|
|
|
@@ -170,20 +170,20 @@ describe('moderation-statuses', () => {
|
|
|
170
170
|
const bobsAccountStatusBeforeTag = await getBobsAccountStatus()
|
|
171
171
|
|
|
172
172
|
await Promise.all([
|
|
173
|
-
modClient.
|
|
173
|
+
modClient.emitEvent({
|
|
174
174
|
subject: bobsAccount,
|
|
175
175
|
event: {
|
|
176
|
-
$type: '
|
|
176
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
177
177
|
add: ['newTag'],
|
|
178
178
|
remove: [],
|
|
179
179
|
comment: 'X',
|
|
180
180
|
},
|
|
181
181
|
createdBy: sc.dids.alice,
|
|
182
182
|
}),
|
|
183
|
-
modClient.
|
|
183
|
+
modClient.emitEvent({
|
|
184
184
|
subject: bobsAccount,
|
|
185
185
|
event: {
|
|
186
|
-
$type: '
|
|
186
|
+
$type: 'tools.ozone.moderation.defs#modEventComment',
|
|
187
187
|
comment: 'X',
|
|
188
188
|
},
|
|
189
189
|
createdBy: sc.dids.alice,
|
|
@@ -197,7 +197,7 @@ describe('moderation-statuses', () => {
|
|
|
197
197
|
|
|
198
198
|
// Since alice's post didn't have a reviewState it is set to reviewNone on first non-impactful event
|
|
199
199
|
const getAlicesPostStatus = async () => {
|
|
200
|
-
const data = await modClient.
|
|
200
|
+
const data = await modClient.queryStatuses({
|
|
201
201
|
subject: alicesPost.uri,
|
|
202
202
|
})
|
|
203
203
|
|
|
@@ -207,10 +207,10 @@ describe('moderation-statuses', () => {
|
|
|
207
207
|
const alicesPostStatusBeforeTag = await getAlicesPostStatus()
|
|
208
208
|
expect(alicesPostStatusBeforeTag).toBeUndefined()
|
|
209
209
|
|
|
210
|
-
await modClient.
|
|
210
|
+
await modClient.emitEvent({
|
|
211
211
|
subject: alicesPost,
|
|
212
212
|
event: {
|
|
213
|
-
$type: '
|
|
213
|
+
$type: 'tools.ozone.moderation.defs#modEventComment',
|
|
214
214
|
comment: 'X',
|
|
215
215
|
},
|
|
216
216
|
createdBy: sc.dids.alice,
|
|
@@ -218,10 +218,10 @@ describe('moderation-statuses', () => {
|
|
|
218
218
|
const alicesPostStatusAfterTag = await getAlicesPostStatus()
|
|
219
219
|
expect(alicesPostStatusAfterTag.reviewState).toEqual(REVIEWNONE)
|
|
220
220
|
|
|
221
|
-
await modClient.
|
|
221
|
+
await modClient.emitEvent({
|
|
222
222
|
subject: alicesPost,
|
|
223
223
|
event: {
|
|
224
|
-
$type: '
|
|
224
|
+
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
225
225
|
reportType: REASONMISLEADING,
|
|
226
226
|
comment: 'X',
|
|
227
227
|
},
|
|
@@ -236,9 +236,9 @@ describe('moderation-statuses', () => {
|
|
|
236
236
|
it('are tracked on takendown subject', async () => {
|
|
237
237
|
const post = sc.posts[sc.dids.carol][0]
|
|
238
238
|
assert(post.images.length > 1)
|
|
239
|
-
await modClient.
|
|
239
|
+
await modClient.emitEvent({
|
|
240
240
|
event: {
|
|
241
|
-
$type: '
|
|
241
|
+
$type: 'tools.ozone.moderation.defs#modEventTakedown',
|
|
242
242
|
},
|
|
243
243
|
subject: {
|
|
244
244
|
$type: 'com.atproto.repo.strongRef',
|
|
@@ -248,7 +248,7 @@ describe('moderation-statuses', () => {
|
|
|
248
248
|
subjectBlobCids: [post.images[0].image.ref.toString()],
|
|
249
249
|
createdBy: sc.dids.alice,
|
|
250
250
|
})
|
|
251
|
-
const result = await modClient.
|
|
251
|
+
const result = await modClient.queryStatuses({
|
|
252
252
|
subject: post.ref.uriStr,
|
|
253
253
|
})
|
|
254
254
|
expect(result.subjectStatuses.length).toBe(1)
|
|
@@ -260,9 +260,9 @@ describe('moderation-statuses', () => {
|
|
|
260
260
|
|
|
261
261
|
it('are tracked on reverse-takendown subject based on previous status', async () => {
|
|
262
262
|
const post = sc.posts[sc.dids.carol][0]
|
|
263
|
-
await modClient.
|
|
263
|
+
await modClient.emitEvent({
|
|
264
264
|
event: {
|
|
265
|
-
$type: '
|
|
265
|
+
$type: 'tools.ozone.moderation.defs#modEventReverseTakedown',
|
|
266
266
|
},
|
|
267
267
|
subject: {
|
|
268
268
|
$type: 'com.atproto.repo.strongRef',
|
|
@@ -270,7 +270,7 @@ describe('moderation-statuses', () => {
|
|
|
270
270
|
cid: post.ref.cidStr,
|
|
271
271
|
},
|
|
272
272
|
})
|
|
273
|
-
const result = await modClient.
|
|
273
|
+
const result = await modClient.queryStatuses({
|
|
274
274
|
subject: post.ref.uriStr,
|
|
275
275
|
})
|
|
276
276
|
expect(result.subjectStatuses.length).toBe(1)
|
package/tests/moderation.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
basicSeed,
|
|
7
7
|
ModeratorClient,
|
|
8
8
|
} from '@atproto/dev-env'
|
|
9
|
-
import AtpAgent, {
|
|
9
|
+
import AtpAgent, { ToolsOzoneModerationEmitEvent } from '@atproto/api'
|
|
10
10
|
import { AtUri } from '@atproto/syntax'
|
|
11
11
|
import { forSnapshot } from './_util'
|
|
12
12
|
import {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
ModEventLabel,
|
|
19
19
|
REVIEWCLOSED,
|
|
20
20
|
REVIEWESCALATED,
|
|
21
|
-
} from '../src/lexicon/types/
|
|
21
|
+
} from '../src/lexicon/types/tools/ozone/moderation/defs'
|
|
22
22
|
import { EventReverser } from '../src'
|
|
23
23
|
import { TestOzone } from '@atproto/dev-env/src/ozone'
|
|
24
24
|
import { ImageInvalidator } from '../src/image-invalidator'
|
|
@@ -176,10 +176,9 @@ describe('moderation', () => {
|
|
|
176
176
|
subject: repoSubject(sc.dids.bob),
|
|
177
177
|
})
|
|
178
178
|
|
|
179
|
-
const moderationStatusOnBobsAccount =
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
})
|
|
179
|
+
const moderationStatusOnBobsAccount = await modClient.queryStatuses({
|
|
180
|
+
subject: sc.dids.bob,
|
|
181
|
+
})
|
|
183
182
|
|
|
184
183
|
// Validate that subject status is set to review closed and takendown flag is on
|
|
185
184
|
expect(moderationStatusOnBobsAccount.subjectStatuses[0]).toMatchObject({
|
|
@@ -204,10 +203,10 @@ describe('moderation', () => {
|
|
|
204
203
|
uri: alicesPostRef.uri.toString(),
|
|
205
204
|
cid: alicesPostRef.cid.toString(),
|
|
206
205
|
}
|
|
207
|
-
await modClient.
|
|
206
|
+
await modClient.emitEvent(
|
|
208
207
|
{
|
|
209
208
|
event: {
|
|
210
|
-
$type: '
|
|
209
|
+
$type: 'tools.ozone.moderation.defs#modEventEscalate',
|
|
211
210
|
comment: 'Y',
|
|
212
211
|
},
|
|
213
212
|
subject: alicesPostSubject,
|
|
@@ -216,7 +215,7 @@ describe('moderation', () => {
|
|
|
216
215
|
'triage',
|
|
217
216
|
)
|
|
218
217
|
|
|
219
|
-
const alicesPostStatus = await modClient.
|
|
218
|
+
const alicesPostStatus = await modClient.queryStatuses({
|
|
220
219
|
subject: alicesPostRef.uri.toString(),
|
|
221
220
|
})
|
|
222
221
|
|
|
@@ -234,10 +233,10 @@ describe('moderation', () => {
|
|
|
234
233
|
uri: alicesPostRef.uri.toString(),
|
|
235
234
|
cid: alicesPostRef.cid.toString(),
|
|
236
235
|
}
|
|
237
|
-
await modClient.
|
|
236
|
+
await modClient.emitEvent(
|
|
238
237
|
{
|
|
239
238
|
event: {
|
|
240
|
-
$type: '
|
|
239
|
+
$type: 'tools.ozone.moderation.defs#modEventComment',
|
|
241
240
|
sticky: true,
|
|
242
241
|
comment: 'This is a persistent note',
|
|
243
242
|
},
|
|
@@ -247,7 +246,7 @@ describe('moderation', () => {
|
|
|
247
246
|
'triage',
|
|
248
247
|
)
|
|
249
248
|
|
|
250
|
-
const alicesPostStatus = await modClient.
|
|
249
|
+
const alicesPostStatus = await modClient.queryStatuses({
|
|
251
250
|
subject: alicesPostRef.uri.toString(),
|
|
252
251
|
})
|
|
253
252
|
|
|
@@ -259,8 +258,8 @@ describe('moderation', () => {
|
|
|
259
258
|
it('reverses status when revert event is triggered.', async () => {
|
|
260
259
|
const alicesPostRef = sc.posts[sc.dids.alice][0].ref
|
|
261
260
|
const emitModEvent = async (
|
|
262
|
-
event:
|
|
263
|
-
overwrites: Partial<
|
|
261
|
+
event: ToolsOzoneModerationEmitEvent.InputSchema['event'],
|
|
262
|
+
overwrites: Partial<ToolsOzoneModerationEmitEvent.InputSchema> = {},
|
|
264
263
|
) => {
|
|
265
264
|
const baseAction = {
|
|
266
265
|
subject: {
|
|
@@ -270,7 +269,7 @@ describe('moderation', () => {
|
|
|
270
269
|
},
|
|
271
270
|
createdBy: 'did:example:admin',
|
|
272
271
|
}
|
|
273
|
-
return modClient.
|
|
272
|
+
return modClient.emitEvent({
|
|
274
273
|
event,
|
|
275
274
|
...baseAction,
|
|
276
275
|
...overwrites,
|
|
@@ -278,20 +277,19 @@ describe('moderation', () => {
|
|
|
278
277
|
}
|
|
279
278
|
// Validate that subject status is marked as escalated
|
|
280
279
|
await emitModEvent({
|
|
281
|
-
$type: '
|
|
280
|
+
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
282
281
|
reportType: REASONSPAM,
|
|
283
282
|
})
|
|
284
283
|
await emitModEvent({
|
|
285
|
-
$type: '
|
|
284
|
+
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
286
285
|
reportType: REASONMISLEADING,
|
|
287
286
|
})
|
|
288
287
|
await emitModEvent({
|
|
289
|
-
$type: '
|
|
288
|
+
$type: 'tools.ozone.moderation.defs#modEventEscalate',
|
|
289
|
+
})
|
|
290
|
+
const alicesPostStatusAfterEscalation = await modClient.queryStatuses({
|
|
291
|
+
subject: alicesPostRef.uriStr,
|
|
290
292
|
})
|
|
291
|
-
const alicesPostStatusAfterEscalation =
|
|
292
|
-
await modClient.queryModerationStatuses({
|
|
293
|
-
subject: alicesPostRef.uriStr,
|
|
294
|
-
})
|
|
295
293
|
expect(
|
|
296
294
|
alicesPostStatusAfterEscalation.subjectStatuses[0].reviewState,
|
|
297
295
|
).toEqual(REVIEWESCALATED)
|
|
@@ -299,30 +297,28 @@ describe('moderation', () => {
|
|
|
299
297
|
// Validate that subject status is marked as takendown
|
|
300
298
|
|
|
301
299
|
await emitModEvent({
|
|
302
|
-
$type: '
|
|
300
|
+
$type: 'tools.ozone.moderation.defs#modEventLabel',
|
|
303
301
|
createLabelVals: ['nsfw'],
|
|
304
302
|
negateLabelVals: [],
|
|
305
303
|
})
|
|
306
304
|
await emitModEvent({
|
|
307
|
-
$type: '
|
|
305
|
+
$type: 'tools.ozone.moderation.defs#modEventTakedown',
|
|
308
306
|
})
|
|
309
307
|
|
|
310
|
-
const alicesPostStatusAfterTakedown =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
})
|
|
308
|
+
const alicesPostStatusAfterTakedown = await modClient.queryStatuses({
|
|
309
|
+
subject: alicesPostRef.uriStr,
|
|
310
|
+
})
|
|
314
311
|
expect(alicesPostStatusAfterTakedown.subjectStatuses[0]).toMatchObject({
|
|
315
312
|
reviewState: REVIEWCLOSED,
|
|
316
313
|
takendown: true,
|
|
317
314
|
})
|
|
318
315
|
|
|
319
316
|
await emitModEvent({
|
|
320
|
-
$type: '
|
|
317
|
+
$type: 'tools.ozone.moderation.defs#modEventReverseTakedown',
|
|
318
|
+
})
|
|
319
|
+
const alicesPostStatusAfterRevert = await modClient.queryStatuses({
|
|
320
|
+
subject: alicesPostRef.uriStr,
|
|
321
321
|
})
|
|
322
|
-
const alicesPostStatusAfterRevert =
|
|
323
|
-
await modClient.queryModerationStatuses({
|
|
324
|
-
subject: alicesPostRef.uriStr,
|
|
325
|
-
})
|
|
326
322
|
// Validate that after reverting, the status of the subject is reverted to the last status changing event
|
|
327
323
|
expect(alicesPostStatusAfterRevert.subjectStatuses[0]).toMatchObject({
|
|
328
324
|
reviewState: REVIEWCLOSED,
|
|
@@ -482,10 +478,10 @@ describe('moderation', () => {
|
|
|
482
478
|
})
|
|
483
479
|
|
|
484
480
|
it('does not allow triage moderators to label.', async () => {
|
|
485
|
-
const attemptLabel = modClient.
|
|
481
|
+
const attemptLabel = modClient.emitEvent(
|
|
486
482
|
{
|
|
487
483
|
event: {
|
|
488
|
-
$type: '
|
|
484
|
+
$type: 'tools.ozone.moderation.defs#modEventLabel',
|
|
489
485
|
negateLabelVals: ['a'],
|
|
490
486
|
createLabelVals: ['b', 'c'],
|
|
491
487
|
},
|
|
@@ -624,10 +620,10 @@ describe('moderation', () => {
|
|
|
624
620
|
})
|
|
625
621
|
|
|
626
622
|
it('allows full moderators to takedown.', async () => {
|
|
627
|
-
await modClient.
|
|
623
|
+
await modClient.emitEvent(
|
|
628
624
|
{
|
|
629
625
|
event: {
|
|
630
|
-
$type: '
|
|
626
|
+
$type: 'tools.ozone.moderation.defs#modEventTakedown',
|
|
631
627
|
},
|
|
632
628
|
createdBy: 'did:example:moderator',
|
|
633
629
|
subject: {
|
|
@@ -647,10 +643,10 @@ describe('moderation', () => {
|
|
|
647
643
|
})
|
|
648
644
|
|
|
649
645
|
it('does not allow non-full moderators to takedown.', async () => {
|
|
650
|
-
const attemptTakedownTriage = modClient.
|
|
646
|
+
const attemptTakedownTriage = modClient.emitEvent(
|
|
651
647
|
{
|
|
652
648
|
event: {
|
|
653
|
-
$type: '
|
|
649
|
+
$type: 'tools.ozone.moderation.defs#modEventTakedown',
|
|
654
650
|
},
|
|
655
651
|
createdBy: 'did:example:moderator',
|
|
656
652
|
subject: {
|
|
@@ -679,7 +675,7 @@ describe('moderation', () => {
|
|
|
679
675
|
})
|
|
680
676
|
await ozone.processAll()
|
|
681
677
|
|
|
682
|
-
const statusesAfterTakedown = await modClient.
|
|
678
|
+
const statusesAfterTakedown = await modClient.queryStatuses(
|
|
683
679
|
{ subject: sc.dids.bob },
|
|
684
680
|
'moderator',
|
|
685
681
|
)
|
|
@@ -697,11 +693,8 @@ describe('moderation', () => {
|
|
|
697
693
|
await ozone.processAll()
|
|
698
694
|
|
|
699
695
|
const [eventList, statuses] = await Promise.all([
|
|
700
|
-
modClient.
|
|
701
|
-
modClient.
|
|
702
|
-
{ subject: sc.dids.bob },
|
|
703
|
-
'moderator',
|
|
704
|
-
),
|
|
696
|
+
modClient.queryEvents({ subject: sc.dids.bob }, 'moderator'),
|
|
697
|
+
modClient.queryStatuses({ subject: sc.dids.bob }, 'moderator'),
|
|
705
698
|
])
|
|
706
699
|
|
|
707
700
|
expect(statuses.subjectStatuses[0]).toMatchObject({
|
|
@@ -713,7 +706,7 @@ describe('moderation', () => {
|
|
|
713
706
|
expect(eventList.events[0]).toMatchObject({
|
|
714
707
|
createdBy: action.createdBy,
|
|
715
708
|
event: {
|
|
716
|
-
$type: '
|
|
709
|
+
$type: 'tools.ozone.moderation.defs#modEventReverseTakedown',
|
|
717
710
|
comment:
|
|
718
711
|
'[SCHEDULED_REVERSAL] Reverting action as originally scheduled',
|
|
719
712
|
},
|
|
@@ -737,16 +730,16 @@ describe('moderation', () => {
|
|
|
737
730
|
})
|
|
738
731
|
|
|
739
732
|
async function emitLabelEvent(
|
|
740
|
-
opts: Partial<
|
|
741
|
-
subject:
|
|
733
|
+
opts: Partial<ToolsOzoneModerationEmitEvent.InputSchema> & {
|
|
734
|
+
subject: ToolsOzoneModerationEmitEvent.InputSchema['subject']
|
|
742
735
|
createLabelVals: ModEventLabel['createLabelVals']
|
|
743
736
|
negateLabelVals: ModEventLabel['negateLabelVals']
|
|
744
737
|
},
|
|
745
738
|
) {
|
|
746
739
|
const { createLabelVals, negateLabelVals } = opts
|
|
747
|
-
const result = await modClient.
|
|
740
|
+
const result = await modClient.emitEvent({
|
|
748
741
|
event: {
|
|
749
|
-
$type: '
|
|
742
|
+
$type: 'tools.ozone.moderation.defs#modEventLabel',
|
|
750
743
|
createLabelVals,
|
|
751
744
|
negateLabelVals,
|
|
752
745
|
},
|
|
@@ -758,13 +751,13 @@ describe('moderation', () => {
|
|
|
758
751
|
}
|
|
759
752
|
|
|
760
753
|
async function reverse(
|
|
761
|
-
opts: Partial<
|
|
762
|
-
subject:
|
|
754
|
+
opts: Partial<ToolsOzoneModerationEmitEvent.InputSchema> & {
|
|
755
|
+
subject: ToolsOzoneModerationEmitEvent.InputSchema['subject']
|
|
763
756
|
},
|
|
764
757
|
) {
|
|
765
|
-
await modClient.
|
|
758
|
+
await modClient.emitEvent({
|
|
766
759
|
event: {
|
|
767
|
-
$type: '
|
|
760
|
+
$type: 'tools.ozone.moderation.defs#modEventReverseTakedown',
|
|
768
761
|
},
|
|
769
762
|
createdBy: 'did:example:admin',
|
|
770
763
|
reason: 'Y',
|
|
@@ -773,7 +766,7 @@ describe('moderation', () => {
|
|
|
773
766
|
}
|
|
774
767
|
|
|
775
768
|
async function getRecordLabels(uri: string) {
|
|
776
|
-
const result = await agent.api.
|
|
769
|
+
const result = await agent.api.tools.ozone.moderation.getRecord(
|
|
777
770
|
{ uri },
|
|
778
771
|
{ headers: await network.ozone.modHeaders() },
|
|
779
772
|
)
|
|
@@ -782,7 +775,7 @@ describe('moderation', () => {
|
|
|
782
775
|
}
|
|
783
776
|
|
|
784
777
|
async function getRepoLabels(did: string) {
|
|
785
|
-
const result = await agent.api.
|
|
778
|
+
const result = await agent.api.tools.ozone.moderation.getRepo(
|
|
786
779
|
{ did },
|
|
787
780
|
{ headers: await network.ozone.modHeaders() },
|
|
788
781
|
)
|
|
@@ -818,7 +811,7 @@ describe('moderation', () => {
|
|
|
818
811
|
})
|
|
819
812
|
|
|
820
813
|
it('sets blobCids in moderation status', async () => {
|
|
821
|
-
const { subjectStatuses } = await modClient.
|
|
814
|
+
const { subjectStatuses } = await modClient.queryStatuses({
|
|
822
815
|
subject: post.ref.uriStr,
|
|
823
816
|
})
|
|
824
817
|
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import AtpAgent from '@atproto/api'
|
|
2
|
-
import { TestNetwork } from '@atproto/dev-env'
|
|
2
|
+
import { EXAMPLE_LABELER, TestNetwork } from '@atproto/dev-env'
|
|
3
3
|
import { DisconnectError, Subscription } from '@atproto/xrpc-server'
|
|
4
4
|
import { ids, lexicons } from '../src/lexicon/lexicons'
|
|
5
5
|
import { Label } from '../src/lexicon/types/com/atproto/label/defs'
|
|
6
|
+
import { Secp256k1Keypair, verifySignature } from '@atproto/crypto'
|
|
7
|
+
import { cborEncode } from '@atproto/common'
|
|
8
|
+
import { ModerationService } from '../src/mod-service'
|
|
6
9
|
import {
|
|
7
10
|
OutputSchema as LabelMessage,
|
|
8
11
|
isLabels,
|
|
9
12
|
} from '../src/lexicon/types/com/atproto/label/subscribeLabels'
|
|
13
|
+
import { getSigningKeyId } from '../src/util'
|
|
10
14
|
|
|
11
15
|
describe('ozone query labels', () => {
|
|
12
16
|
let network: TestNetwork
|
|
@@ -21,44 +25,44 @@ describe('ozone query labels', () => {
|
|
|
21
25
|
|
|
22
26
|
agent = network.ozone.getClient()
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
const toCreate = [
|
|
25
29
|
{
|
|
26
|
-
src:
|
|
30
|
+
src: EXAMPLE_LABELER,
|
|
27
31
|
uri: 'did:example:blah',
|
|
28
32
|
val: 'spam',
|
|
29
33
|
neg: false,
|
|
30
34
|
cts: new Date().toISOString(),
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
|
-
src:
|
|
37
|
+
src: EXAMPLE_LABELER,
|
|
34
38
|
uri: 'did:example:blah',
|
|
35
39
|
val: 'impersonation',
|
|
36
40
|
neg: false,
|
|
37
41
|
cts: new Date().toISOString(),
|
|
38
42
|
},
|
|
39
43
|
{
|
|
40
|
-
src:
|
|
44
|
+
src: EXAMPLE_LABELER,
|
|
41
45
|
uri: 'at://did:example:blah/app.bsky.feed.post/1234abcde',
|
|
42
46
|
val: 'spam',
|
|
43
47
|
neg: false,
|
|
44
48
|
cts: new Date().toISOString(),
|
|
45
49
|
},
|
|
46
50
|
{
|
|
47
|
-
src:
|
|
51
|
+
src: EXAMPLE_LABELER,
|
|
48
52
|
uri: 'at://did:example:blah/app.bsky.feed.post/1234abcfg',
|
|
49
53
|
val: 'spam',
|
|
50
54
|
neg: false,
|
|
51
55
|
cts: new Date().toISOString(),
|
|
52
56
|
},
|
|
53
57
|
{
|
|
54
|
-
src:
|
|
58
|
+
src: EXAMPLE_LABELER,
|
|
55
59
|
uri: 'at://did:example:blah/app.bsky.actor.profile/self',
|
|
56
60
|
val: 'spam',
|
|
57
61
|
neg: false,
|
|
58
62
|
cts: new Date().toISOString(),
|
|
59
63
|
},
|
|
60
64
|
{
|
|
61
|
-
src:
|
|
65
|
+
src: EXAMPLE_LABELER,
|
|
62
66
|
uri: 'did:example:thing',
|
|
63
67
|
val: 'spam',
|
|
64
68
|
neg: false,
|
|
@@ -67,7 +71,7 @@ describe('ozone query labels', () => {
|
|
|
67
71
|
]
|
|
68
72
|
|
|
69
73
|
const modService = network.ozone.ctx.modService(network.ozone.ctx.db)
|
|
70
|
-
await modService.createLabels(
|
|
74
|
+
labels = await modService.createLabels(toCreate)
|
|
71
75
|
})
|
|
72
76
|
|
|
73
77
|
afterAll(async () => {
|
|
@@ -128,6 +132,72 @@ describe('ozone query labels', () => {
|
|
|
128
132
|
)
|
|
129
133
|
})
|
|
130
134
|
|
|
135
|
+
it('returns validly signed labels', async () => {
|
|
136
|
+
const res = await agent.api.com.atproto.label.queryLabels({
|
|
137
|
+
uriPatterns: ['*'],
|
|
138
|
+
})
|
|
139
|
+
const signingKey = network.ozone.ctx.signingKey.did()
|
|
140
|
+
for (const label of res.data.labels) {
|
|
141
|
+
const { sig, ...rest } = label
|
|
142
|
+
if (!sig) {
|
|
143
|
+
throw new Error('Missing signature')
|
|
144
|
+
}
|
|
145
|
+
const encodedLabel = cborEncode(rest)
|
|
146
|
+
const isValid = await verifySignature(signingKey, encodedLabel, sig)
|
|
147
|
+
expect(isValid).toBe(true)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('resigns labels if the signingKey changes', async () => {
|
|
152
|
+
// mock changing the signing key for the service
|
|
153
|
+
const ctx = network.ozone.ctx
|
|
154
|
+
const origModServiceFn = ctx.modService
|
|
155
|
+
|
|
156
|
+
const modSrvc = ctx.modService(ctx.db)
|
|
157
|
+
const newSigningKey = await Secp256k1Keypair.create()
|
|
158
|
+
const newSigningKeyId = await getSigningKeyId(ctx.db, newSigningKey.did())
|
|
159
|
+
ctx.devOverride({
|
|
160
|
+
modService: ModerationService.creator(
|
|
161
|
+
newSigningKey,
|
|
162
|
+
newSigningKeyId,
|
|
163
|
+
ctx.cfg,
|
|
164
|
+
modSrvc.backgroundQueue,
|
|
165
|
+
ctx.idResolver,
|
|
166
|
+
modSrvc.eventPusher,
|
|
167
|
+
modSrvc.appviewAgent,
|
|
168
|
+
ctx.serviceAuthHeaders,
|
|
169
|
+
),
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const res = await agent.api.com.atproto.label.queryLabels({
|
|
173
|
+
uriPatterns: ['*'],
|
|
174
|
+
})
|
|
175
|
+
for (const label of res.data.labels) {
|
|
176
|
+
const { sig, ...rest } = label
|
|
177
|
+
if (!sig) {
|
|
178
|
+
throw new Error('Missing signature')
|
|
179
|
+
}
|
|
180
|
+
const encodedLabel = cborEncode(rest)
|
|
181
|
+
const isValid = await verifySignature(
|
|
182
|
+
newSigningKey.did(),
|
|
183
|
+
encodedLabel,
|
|
184
|
+
sig,
|
|
185
|
+
)
|
|
186
|
+
expect(isValid).toBe(true)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await network.ozone.processAll()
|
|
190
|
+
|
|
191
|
+
const fromDb = await ctx.db.db.selectFrom('label').selectAll().execute()
|
|
192
|
+
expect(fromDb.every((row) => row.signingKeyId === newSigningKeyId)).toBe(
|
|
193
|
+
true,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
ctx.devOverride({
|
|
197
|
+
modService: origModServiceFn,
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
131
201
|
describe('subscribeLabels', () => {
|
|
132
202
|
it('streams all labels from initial cursor.', async () => {
|
|
133
203
|
const ac = new AbortController()
|
|
@@ -154,7 +224,13 @@ describe('ozone query labels', () => {
|
|
|
154
224
|
for await (const message of sub) {
|
|
155
225
|
resetDoneTimer()
|
|
156
226
|
if (isLabels(message)) {
|
|
157
|
-
|
|
227
|
+
for (const label of message.labels) {
|
|
228
|
+
// sigs are currently parsed as a Buffer which is a Uint8Array under the hood, but fails our equality test so we cast to Uint8Array
|
|
229
|
+
streamedLabels.push({
|
|
230
|
+
...label,
|
|
231
|
+
sig: label.sig ? new Uint8Array(label.sig) : undefined,
|
|
232
|
+
})
|
|
233
|
+
}
|
|
158
234
|
}
|
|
159
235
|
}
|
|
160
236
|
expect(streamedLabels).toEqual(labels)
|