@atproto/ozone 0.0.9 → 0.0.10
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/util.d.ts +1 -1
- package/dist/db/index.js +20 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/schema/moderation_event.d.ts +3 -1
- package/dist/db/schema/moderation_subject_status.d.ts +1 -0
- package/dist/index.js +318 -51
- package/dist/index.js.map +3 -3
- package/dist/lexicon/lexicons.d.ts +57 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +9 -0
- package/dist/lexicon/types/com/atproto/admin/emitModerationEvent.d.ts +1 -1
- package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +2 -0
- package/dist/lexicon/types/com/atproto/admin/queryModerationStatuses.d.ts +2 -0
- package/dist/logger.d.ts +1 -0
- package/dist/mod-service/index.d.ts +18 -4
- package/dist/mod-service/lang.d.ts +15 -0
- package/dist/mod-service/status.d.ts +1 -1
- package/dist/mod-service/types.d.ts +1 -1
- package/dist/mod-service/views.d.ts +2 -1
- package/package.json +6 -6
- package/src/api/admin/emitModerationEvent.ts +22 -10
- package/src/api/admin/queryModerationEvents.ts +4 -0
- package/src/api/admin/queryModerationStatuses.ts +4 -0
- package/src/api/moderation/createReport.ts +15 -4
- package/src/api/moderation/util.ts +1 -0
- package/src/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.ts +31 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/moderation_event.ts +3 -0
- package/src/db/schema/moderation_subject_status.ts +1 -0
- package/src/lexicon/lexicons.ts +62 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +24 -0
- package/src/lexicon/types/com/atproto/admin/emitModerationEvent.ts +1 -0
- package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +4 -0
- package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +2 -0
- package/src/logger.ts +2 -0
- package/src/mod-service/index.ts +60 -10
- package/src/mod-service/lang.ts +82 -0
- package/src/mod-service/status.ts +18 -4
- package/src/mod-service/types.ts +1 -0
- package/src/mod-service/views.ts +21 -2
- package/tests/__snapshots__/get-record.test.ts.snap +6 -0
- package/tests/__snapshots__/get-repo.test.ts.snap +3 -0
- package/tests/__snapshots__/moderation-events.test.ts.snap +45 -4
- package/tests/__snapshots__/moderation-statuses.test.ts.snap +59 -3
- package/tests/__snapshots__/moderation.test.ts.snap +3 -3
- package/tests/get-record.test.ts +0 -8
- package/tests/moderation-events.test.ts +57 -5
- package/tests/moderation-status-tags.test.ts +92 -0
- package/tests/moderation-statuses.test.ts +20 -3
|
@@ -148,7 +148,7 @@ describe('moderation-events', () => {
|
|
|
148
148
|
|
|
149
149
|
expect(
|
|
150
150
|
[...new Set(allEvents.data.events.map((e) => e.event.$type))].length,
|
|
151
|
-
).toEqual(
|
|
151
|
+
).toEqual(4)
|
|
152
152
|
})
|
|
153
153
|
|
|
154
154
|
it('returns events for all content by user', async () => {
|
|
@@ -203,10 +203,11 @@ describe('moderation-events', () => {
|
|
|
203
203
|
const defaultEvents = await getPaginatedEvents()
|
|
204
204
|
const reversedEvents = await getPaginatedEvents('asc')
|
|
205
205
|
|
|
206
|
-
expect(allEvents.data.events.length).toEqual(
|
|
206
|
+
expect(allEvents.data.events.length).toEqual(7)
|
|
207
207
|
expect(defaultEvents.length).toEqual(allEvents.data.events.length)
|
|
208
208
|
expect(reversedEvents.length).toEqual(allEvents.data.events.length)
|
|
209
|
-
|
|
209
|
+
// First event in the reversed list is the last item in the default list
|
|
210
|
+
expect(reversedEvents[0].id).toEqual(defaultEvents[6].id)
|
|
210
211
|
})
|
|
211
212
|
|
|
212
213
|
it('returns report events matching reportType filters', async () => {
|
|
@@ -242,7 +243,7 @@ describe('moderation-events', () => {
|
|
|
242
243
|
expect(eventsWithComment.data.events.length).toEqual(12)
|
|
243
244
|
})
|
|
244
245
|
|
|
245
|
-
it('returns events matching filter params for
|
|
246
|
+
it('returns events matching filter params for labels', async () => {
|
|
246
247
|
const [negatedLabelEvent, createdLabelEvent] = await Promise.all([
|
|
247
248
|
emitModerationEvent({
|
|
248
249
|
event: {
|
|
@@ -301,6 +302,54 @@ describe('moderation-events', () => {
|
|
|
301
302
|
withoutOneLabel.data.events[0].id,
|
|
302
303
|
)
|
|
303
304
|
})
|
|
305
|
+
it('returns events matching filter params for tags', async () => {
|
|
306
|
+
const tagEvent = async ({
|
|
307
|
+
add,
|
|
308
|
+
remove,
|
|
309
|
+
}: {
|
|
310
|
+
add: string[]
|
|
311
|
+
remove: string[]
|
|
312
|
+
}) =>
|
|
313
|
+
emitModerationEvent({
|
|
314
|
+
event: {
|
|
315
|
+
$type: 'com.atproto.admin.defs#modEventTag',
|
|
316
|
+
comment: 'X',
|
|
317
|
+
add,
|
|
318
|
+
remove,
|
|
319
|
+
},
|
|
320
|
+
subject: {
|
|
321
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
322
|
+
did: sc.dids.carol,
|
|
323
|
+
},
|
|
324
|
+
createdBy: sc.dids.bob,
|
|
325
|
+
})
|
|
326
|
+
const addEvent = await tagEvent({ add: ['L1', 'L2'], remove: [] })
|
|
327
|
+
const addAndRemoveEvent = await tagEvent({ add: ['L3'], remove: ['L2'] })
|
|
328
|
+
const [addFinder, addAndRemoveFinder, removeFinder] = await Promise.all([
|
|
329
|
+
queryModerationEvents({
|
|
330
|
+
addedTags: ['L1'],
|
|
331
|
+
}),
|
|
332
|
+
queryModerationEvents({
|
|
333
|
+
addedTags: ['L3'],
|
|
334
|
+
removedTags: ['L2'],
|
|
335
|
+
}),
|
|
336
|
+
queryModerationEvents({
|
|
337
|
+
removedTags: ['L2'],
|
|
338
|
+
}),
|
|
339
|
+
])
|
|
340
|
+
|
|
341
|
+
expect(addFinder.data.events.length).toEqual(1)
|
|
342
|
+
expect(addEvent.data.id).toEqual(addFinder.data.events[0].id)
|
|
343
|
+
|
|
344
|
+
expect(addAndRemoveEvent.data.id).toEqual(
|
|
345
|
+
addAndRemoveFinder.data.events[0].id,
|
|
346
|
+
)
|
|
347
|
+
expect(addAndRemoveEvent.data.id).toEqual(
|
|
348
|
+
addAndRemoveFinder.data.events[0].id,
|
|
349
|
+
)
|
|
350
|
+
expect(addAndRemoveEvent.data.event.add).toEqual(['L3'])
|
|
351
|
+
expect(addAndRemoveEvent.data.event.remove).toEqual(['L2'])
|
|
352
|
+
})
|
|
304
353
|
})
|
|
305
354
|
|
|
306
355
|
describe('get event', () => {
|
|
@@ -331,7 +380,10 @@ describe('moderation-events', () => {
|
|
|
331
380
|
})
|
|
332
381
|
const { data: result } =
|
|
333
382
|
await pdsAgent.api.com.atproto.admin.queryModerationEvents(
|
|
334
|
-
{
|
|
383
|
+
{
|
|
384
|
+
subject: post.ref.uriStr,
|
|
385
|
+
types: ['com.atproto.admin.defs#modEventTakedown'],
|
|
386
|
+
},
|
|
335
387
|
{ headers: network.ozone.adminAuthHeaders('moderator') },
|
|
336
388
|
)
|
|
337
389
|
expect(result.events[0]).toMatchObject({
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
|
|
2
|
+
import AtpAgent from '@atproto/api'
|
|
3
|
+
import { REASONSPAM } from '../src/lexicon/types/com/atproto/moderation/defs'
|
|
4
|
+
|
|
5
|
+
describe('moderation-status-tags', () => {
|
|
6
|
+
let network: TestNetwork
|
|
7
|
+
let agent: AtpAgent
|
|
8
|
+
let pdsAgent: AtpAgent
|
|
9
|
+
let sc: SeedClient
|
|
10
|
+
|
|
11
|
+
const emitModerationEvent = async (eventData) => {
|
|
12
|
+
return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
|
|
13
|
+
encoding: 'application/json',
|
|
14
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const queryModerationStatuses = (statusQuery) =>
|
|
19
|
+
agent.api.com.atproto.admin.queryModerationStatuses(statusQuery, {
|
|
20
|
+
headers: network.bsky.adminAuthHeaders('moderator'),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
network = await TestNetwork.create({
|
|
25
|
+
dbPostgresSchema: 'ozone_moderation_status_tags',
|
|
26
|
+
})
|
|
27
|
+
agent = network.ozone.getClient()
|
|
28
|
+
pdsAgent = network.pds.getClient()
|
|
29
|
+
sc = network.getSeedClient()
|
|
30
|
+
await basicSeed(sc)
|
|
31
|
+
await network.processAll()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
afterAll(async () => {
|
|
35
|
+
await network.close()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('manage tags on subject status', () => {
|
|
39
|
+
it('adds and removes tags on a subject', async () => {
|
|
40
|
+
const bobsAccount = {
|
|
41
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
42
|
+
did: sc.dids.bob,
|
|
43
|
+
}
|
|
44
|
+
await emitModerationEvent({
|
|
45
|
+
subject: bobsAccount,
|
|
46
|
+
event: {
|
|
47
|
+
$type: 'com.atproto.admin.defs#modEventReport',
|
|
48
|
+
comment: 'X',
|
|
49
|
+
reportType: REASONSPAM,
|
|
50
|
+
},
|
|
51
|
+
createdBy: sc.dids.alice,
|
|
52
|
+
})
|
|
53
|
+
await emitModerationEvent({
|
|
54
|
+
subject: bobsAccount,
|
|
55
|
+
event: {
|
|
56
|
+
$type: 'com.atproto.admin.defs#modEventTag',
|
|
57
|
+
add: ['interaction-churn'],
|
|
58
|
+
remove: [],
|
|
59
|
+
},
|
|
60
|
+
createdBy: sc.dids.alice,
|
|
61
|
+
})
|
|
62
|
+
const { data: statusAfterInteractionTag } = await queryModerationStatuses(
|
|
63
|
+
{
|
|
64
|
+
subject: bobsAccount.did,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
expect(statusAfterInteractionTag.subjectStatuses[0].tags).toContain(
|
|
68
|
+
'interaction-churn',
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
await emitModerationEvent({
|
|
72
|
+
subject: bobsAccount,
|
|
73
|
+
event: {
|
|
74
|
+
$type: 'com.atproto.admin.defs#modEventTag',
|
|
75
|
+
remove: ['interaction-churn'],
|
|
76
|
+
add: ['follow-churn'],
|
|
77
|
+
},
|
|
78
|
+
createdBy: sc.dids.alice,
|
|
79
|
+
})
|
|
80
|
+
const { data: statusAfterFollowTag } = await queryModerationStatuses({
|
|
81
|
+
subject: bobsAccount.did,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expect(statusAfterFollowTag.subjectStatuses[0].tags).not.toContain(
|
|
85
|
+
'interaction-churn',
|
|
86
|
+
)
|
|
87
|
+
expect(statusAfterFollowTag.subjectStatuses[0].tags).toContain(
|
|
88
|
+
'follow-churn',
|
|
89
|
+
)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
|
@@ -39,8 +39,8 @@ describe('moderation-statuses', () => {
|
|
|
39
39
|
}
|
|
40
40
|
const bobsPost = {
|
|
41
41
|
$type: 'com.atproto.repo.strongRef',
|
|
42
|
-
uri: sc.posts[sc.dids.bob][
|
|
43
|
-
cid: sc.posts[sc.dids.bob][
|
|
42
|
+
uri: sc.posts[sc.dids.bob][0].ref.uriStr,
|
|
43
|
+
cid: sc.posts[sc.dids.bob][0].ref.cidStr,
|
|
44
44
|
}
|
|
45
45
|
const alicesPost = {
|
|
46
46
|
$type: 'com.atproto.repo.strongRef',
|
|
@@ -95,6 +95,23 @@ describe('moderation-statuses', () => {
|
|
|
95
95
|
expect(forSnapshot(response.data.subjectStatuses)).toMatchSnapshot()
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
+
it('returns statuses filtered by subject language', async () => {
|
|
99
|
+
const klingonQueue = await queryModerationStatuses({
|
|
100
|
+
tags: ['lang:i'],
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expect(forSnapshot(klingonQueue.data.subjectStatuses)).toMatchSnapshot()
|
|
104
|
+
|
|
105
|
+
const nonKlingonQueue = await queryModerationStatuses({
|
|
106
|
+
excludeTags: ['lang:i'],
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Verify that the klingon tagged subject is not returned when excluding klingon
|
|
110
|
+
expect(
|
|
111
|
+
nonKlingonQueue.data.subjectStatuses.map((s) => s.id),
|
|
112
|
+
).not.toContain(klingonQueue.data.subjectStatuses[0].id)
|
|
113
|
+
})
|
|
114
|
+
|
|
98
115
|
it('returns paginated statuses', async () => {
|
|
99
116
|
// We know there will be exactly 4 statuses in db
|
|
100
117
|
const getPaginatedStatuses = async (
|
|
@@ -119,7 +136,7 @@ describe('moderation-statuses', () => {
|
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
const list = await getPaginatedStatuses({})
|
|
122
|
-
expect(list[0].id).toEqual(
|
|
139
|
+
expect(list[0].id).toEqual(7)
|
|
123
140
|
expect(list[list.length - 1].id).toEqual(1)
|
|
124
141
|
|
|
125
142
|
await emitModerationEvent({
|