@atproto/ozone 0.1.62 → 0.1.64
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 +20 -0
- package/dist/api/health.d.ts.map +1 -1
- package/dist/api/health.js +0 -24
- package/dist/api/health.js.map +1 -1
- package/dist/api/moderation/queryEvents.d.ts.map +1 -1
- package/dist/api/moderation/queryEvents.js +2 -1
- package/dist/api/moderation/queryEvents.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +32 -2
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +16 -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 -0
- package/dist/lexicon/types/tools/ozone/moderation/queryEvents.d.ts.map +1 -1
- package/dist/mod-service/index.d.ts +1 -0
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +12 -1
- 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 +8 -0
- package/dist/mod-service/views.js.map +1 -1
- package/dist/setting/constants.d.ts +1 -0
- package/dist/setting/constants.d.ts.map +1 -1
- package/dist/setting/constants.js +2 -1
- package/dist/setting/constants.js.map +1 -1
- package/dist/setting/validators.d.ts.map +1 -1
- package/dist/setting/validators.js +19 -0
- package/dist/setting/validators.js.map +1 -1
- package/package.json +3 -3
- package/src/api/health.ts +0 -25
- package/src/api/moderation/queryEvents.ts +2 -0
- package/src/lexicon/lexicons.ts +18 -1
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +2 -0
- package/src/lexicon/types/tools/ozone/moderation/queryEvents.ts +1 -0
- package/src/mod-service/index.ts +14 -0
- package/src/mod-service/views.ts +11 -0
- package/src/setting/constants.ts +1 -0
- package/src/setting/validators.ts +28 -1
- package/tests/takedown.test.ts +64 -0
- package/tsconfig.tests.tsbuildinfo +1 -1
package/src/api/health.ts
CHANGED
|
@@ -5,31 +5,6 @@ import AppContext from '../context'
|
|
|
5
5
|
export const createRouter = (ctx: AppContext): express.Router => {
|
|
6
6
|
const router = express.Router()
|
|
7
7
|
|
|
8
|
-
router.get('/', function (req, res) {
|
|
9
|
-
res.type('text/plain')
|
|
10
|
-
res.send(`
|
|
11
|
-
,o888888o. 8888888888',8888' ,o888888o. b. 8 8 8888888888
|
|
12
|
-
. 8888 '88. ,8',8888'. 8888 '88. 888o. 8 8 8888
|
|
13
|
-
,8 8888 '8b ,8',8888',8 8888 '8b Y88888o. 8 8 8888
|
|
14
|
-
88 8888 '8b ,8',8888' 88 8888 '8b .'Y888888o. 8 8 8888
|
|
15
|
-
88 8888 88 ,8',8888' 88 8888 88 8o. 'Y888888o. 8 8 888888888888
|
|
16
|
-
88 8888 88 ,8',8888' 88 8888 88 8'Y8o. 'Y88888o8 8 8888
|
|
17
|
-
88 8888 ,8P ,8',8888' 88 8888 ,8P 8 'Y8o. 'Y8888 8 8888
|
|
18
|
-
'8 8888 ,8P ,8',8888' '8 8888 ,8P 8 'Y8o. 'Y8 8 8888
|
|
19
|
-
' 8888 ,88' ,8',8888' ' 8888 ,88' 8 'Y8o.' 8 8888
|
|
20
|
-
'8888888P' ,8',8888888888888 '8888888P' 8 'Yo 8 888888888888
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
This is an AT Protocol Moderation Service API Server.
|
|
24
|
-
|
|
25
|
-
Most API routes are under /xrpc/
|
|
26
|
-
|
|
27
|
-
Code: https://github.com/bluesky-social/atproto
|
|
28
|
-
Self-Host: https://github.com/bluesky-social/ozone
|
|
29
|
-
Protocol: https://atproto.com
|
|
30
|
-
`)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
8
|
router.get('/robots.txt', function (req, res) {
|
|
34
9
|
res.type('text/plain')
|
|
35
10
|
res.send(
|
|
@@ -25,6 +25,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
25
25
|
reportTypes,
|
|
26
26
|
collections = [],
|
|
27
27
|
subjectType,
|
|
28
|
+
policies,
|
|
28
29
|
} = params
|
|
29
30
|
const db = ctx.db
|
|
30
31
|
const modService = ctx.modService(db)
|
|
@@ -47,6 +48,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
47
48
|
reportTypes,
|
|
48
49
|
collections,
|
|
49
50
|
subjectType,
|
|
51
|
+
policies,
|
|
50
52
|
})
|
|
51
53
|
return {
|
|
52
54
|
encoding: 'application/json',
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -11309,6 +11309,15 @@ export const schemaDict = {
|
|
|
11309
11309
|
description:
|
|
11310
11310
|
'If true, all other reports on content authored by this account will be resolved (acknowledged).',
|
|
11311
11311
|
},
|
|
11312
|
+
policies: {
|
|
11313
|
+
type: 'array',
|
|
11314
|
+
maxLength: 5,
|
|
11315
|
+
items: {
|
|
11316
|
+
type: 'string',
|
|
11317
|
+
},
|
|
11318
|
+
description:
|
|
11319
|
+
'Names/Keywords of the policies that drove the decision.',
|
|
11320
|
+
},
|
|
11312
11321
|
},
|
|
11313
11322
|
},
|
|
11314
11323
|
modEventReverseTakedown: {
|
|
@@ -12345,6 +12354,14 @@ export const schemaDict = {
|
|
|
12345
12354
|
type: 'string',
|
|
12346
12355
|
},
|
|
12347
12356
|
},
|
|
12357
|
+
policies: {
|
|
12358
|
+
type: 'array',
|
|
12359
|
+
items: {
|
|
12360
|
+
type: 'string',
|
|
12361
|
+
description:
|
|
12362
|
+
'If specified, only events where the policy matches the given policy are returned',
|
|
12363
|
+
},
|
|
12364
|
+
},
|
|
12348
12365
|
cursor: {
|
|
12349
12366
|
type: 'string',
|
|
12350
12367
|
},
|
|
@@ -12515,9 +12532,9 @@ export const schemaDict = {
|
|
|
12515
12532
|
},
|
|
12516
12533
|
tags: {
|
|
12517
12534
|
type: 'array',
|
|
12535
|
+
maxLength: 25,
|
|
12518
12536
|
items: {
|
|
12519
12537
|
type: 'string',
|
|
12520
|
-
maxLength: 25,
|
|
12521
12538
|
description:
|
|
12522
12539
|
'Items in this array are applied with OR filters. To apply AND filter, put all tags in the same string and separate using && characters',
|
|
12523
12540
|
},
|
|
@@ -174,6 +174,8 @@ export interface ModEventTakedown {
|
|
|
174
174
|
durationInHours?: number
|
|
175
175
|
/** If true, all other reports on content authored by this account will be resolved (acknowledged). */
|
|
176
176
|
acknowledgeAccountSubjects?: boolean
|
|
177
|
+
/** Names/Keywords of the policies that drove the decision. */
|
|
178
|
+
policies?: string[]
|
|
177
179
|
[k: string]: unknown
|
|
178
180
|
}
|
|
179
181
|
|
package/src/mod-service/index.ts
CHANGED
|
@@ -152,6 +152,7 @@ export class ModerationService {
|
|
|
152
152
|
reportTypes?: string[]
|
|
153
153
|
collections: string[]
|
|
154
154
|
subjectType?: string
|
|
155
|
+
policies?: string[]
|
|
155
156
|
}): Promise<{ cursor?: string; events: ModerationEventRow[] }> {
|
|
156
157
|
const {
|
|
157
158
|
subject,
|
|
@@ -172,6 +173,7 @@ export class ModerationService {
|
|
|
172
173
|
reportTypes,
|
|
173
174
|
collections,
|
|
174
175
|
subjectType,
|
|
176
|
+
policies,
|
|
175
177
|
} = opts
|
|
176
178
|
const { ref } = this.db.db.dynamic
|
|
177
179
|
let builder = this.db.db.selectFrom('moderation_event').selectAll()
|
|
@@ -264,6 +266,14 @@ export class ModerationService {
|
|
|
264
266
|
if (reportTypes?.length) {
|
|
265
267
|
builder = builder.where(sql`meta->>'reportType'`, 'in', reportTypes)
|
|
266
268
|
}
|
|
269
|
+
if (policies?.length) {
|
|
270
|
+
builder = builder.where((qb) => {
|
|
271
|
+
policies.forEach((policy) => {
|
|
272
|
+
qb = qb.orWhere(sql`meta->>'policies'`, 'ilike', `%${policy}%`)
|
|
273
|
+
})
|
|
274
|
+
return qb
|
|
275
|
+
})
|
|
276
|
+
}
|
|
267
277
|
|
|
268
278
|
const keyset = new TimeIdKeyset(
|
|
269
279
|
ref(`moderation_event.createdAt`),
|
|
@@ -435,6 +445,10 @@ export class ModerationService {
|
|
|
435
445
|
meta.acknowledgeAccountSubjects = true
|
|
436
446
|
}
|
|
437
447
|
|
|
448
|
+
if (isModEventTakedown(event) && event.policies?.length) {
|
|
449
|
+
meta.policies = event.policies.join(',')
|
|
450
|
+
}
|
|
451
|
+
|
|
438
452
|
// Keep trace of reports that came in while the reporter was in muted stated
|
|
439
453
|
if (isModEventReport(event)) {
|
|
440
454
|
const isReportingMuted = await this.isReportingMutedForSubject(createdBy)
|
package/src/mod-service/views.ts
CHANGED
|
@@ -137,6 +137,17 @@ export class ModerationViews {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
if (
|
|
141
|
+
event.action === 'tools.ozone.moderation.defs#modEventTakedown' &&
|
|
142
|
+
typeof event.meta?.policies === 'string' &&
|
|
143
|
+
event.meta.policies.length > 0
|
|
144
|
+
) {
|
|
145
|
+
eventView.event = {
|
|
146
|
+
...eventView.event,
|
|
147
|
+
policies: event.meta.policies.split(','),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
140
151
|
if (event.action === 'tools.ozone.moderation.defs#modEventLabel') {
|
|
141
152
|
eventView.event = {
|
|
142
153
|
...eventView.event,
|
package/src/setting/constants.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Selectable } from 'kysely'
|
|
2
2
|
import { Setting } from '../db/schema/setting'
|
|
3
|
-
import { ProtectedTagSettingKey } from './constants'
|
|
3
|
+
import { PolicyListSettingKey, ProtectedTagSettingKey } from './constants'
|
|
4
4
|
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
5
5
|
|
|
6
6
|
export const settingValidators = new Map<
|
|
@@ -58,4 +58,31 @@ export const settingValidators = new Map<
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
],
|
|
61
|
+
[
|
|
62
|
+
PolicyListSettingKey,
|
|
63
|
+
async (setting: Partial<Selectable<Setting>>) => {
|
|
64
|
+
if (setting.managerRole !== 'tools.ozone.team.defs#roleAdmin') {
|
|
65
|
+
throw new InvalidRequestError(
|
|
66
|
+
'Only admins should be able to manage policy list',
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof setting.value !== 'object') {
|
|
71
|
+
throw new InvalidRequestError('Invalid value')
|
|
72
|
+
}
|
|
73
|
+
for (const [key, val] of Object.entries(setting.value)) {
|
|
74
|
+
if (!val || typeof val !== 'object') {
|
|
75
|
+
throw new InvalidRequestError(
|
|
76
|
+
`Invalid configuration for policy ${key}`,
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!val['name'] || !val['description']) {
|
|
81
|
+
throw new InvalidRequestError(
|
|
82
|
+
`Must define a name and description for policy ${key}`,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
],
|
|
61
88
|
])
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TestNetwork,
|
|
3
|
+
TestOzone,
|
|
4
|
+
SeedClient,
|
|
5
|
+
basicSeed,
|
|
6
|
+
ModeratorClient,
|
|
7
|
+
} from '@atproto/dev-env'
|
|
8
|
+
import { AtpAgent } from '@atproto/api'
|
|
9
|
+
|
|
10
|
+
describe('moderation', () => {
|
|
11
|
+
let network: TestNetwork
|
|
12
|
+
let ozone: TestOzone
|
|
13
|
+
let agent: AtpAgent
|
|
14
|
+
let bskyAgent: AtpAgent
|
|
15
|
+
let pdsAgent: AtpAgent
|
|
16
|
+
let sc: SeedClient
|
|
17
|
+
let modClient: ModeratorClient
|
|
18
|
+
|
|
19
|
+
const repoSubject = (did: string) => ({
|
|
20
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
21
|
+
did,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
beforeAll(async () => {
|
|
25
|
+
network = await TestNetwork.create({
|
|
26
|
+
dbPostgresSchema: 'ozone_takedown',
|
|
27
|
+
})
|
|
28
|
+
ozone = network.ozone
|
|
29
|
+
agent = network.ozone.getClient()
|
|
30
|
+
bskyAgent = network.bsky.getClient()
|
|
31
|
+
pdsAgent = network.pds.getClient()
|
|
32
|
+
sc = network.getSeedClient()
|
|
33
|
+
modClient = network.ozone.getModClient()
|
|
34
|
+
await basicSeed(sc)
|
|
35
|
+
await network.processAll()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
afterAll(async () => {
|
|
39
|
+
await network.close()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('allows specifying policy for takedown actions.', async () => {
|
|
43
|
+
await modClient.performTakedown({
|
|
44
|
+
subject: repoSubject(sc.dids.bob),
|
|
45
|
+
policies: ['trolling'],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Verify that that the takedown even exposes the policy specified for it
|
|
49
|
+
const { events } = await modClient.queryEvents({
|
|
50
|
+
subject: sc.dids.bob,
|
|
51
|
+
types: ['tools.ozone.moderation.defs#modEventTakedown'],
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
expect(events[0].event.policies?.[0]).toEqual('trolling')
|
|
55
|
+
|
|
56
|
+
// Verify that event stream can be filtered by policy
|
|
57
|
+
const { events: filteredEvents } = await modClient.queryEvents({
|
|
58
|
+
subject: sc.dids.bob,
|
|
59
|
+
policies: ['trolling'],
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
expect(filteredEvents[0].subject.did).toEqual(sc.dids.bob)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./tests/3p-labeler.test.ts","./tests/_util.ts","./tests/ack-all-subjects-of-account.test.ts","./tests/blob-divert.test.ts","./tests/communication-templates.test.ts","./tests/content-tagger.test.ts","./tests/db.test.ts","./tests/get-config.test.ts","./tests/get-lists.test.ts","./tests/get-profiles.test.ts","./tests/get-record.test.ts","./tests/get-records.test.ts","./tests/get-repo.test.ts","./tests/get-repos.test.ts","./tests/get-starter-pack.test.ts","./tests/moderation-appeals.test.ts","./tests/moderation-events.test.ts","./tests/moderation-status-tags.test.ts","./tests/moderation-statuses.test.ts","./tests/moderation.test.ts","./tests/protected-tags.test.ts","./tests/query-labels.test.ts","./tests/record-and-account-events.test.ts","./tests/repo-search.test.ts","./tests/report-muting.test.ts","./tests/sequencer.test.ts","./tests/server.test.ts","./tests/sets.test.ts","./tests/settings.test.ts","./tests/team.test.ts"],"version":"5.6.3"}
|
|
1
|
+
{"root":["./tests/3p-labeler.test.ts","./tests/_util.ts","./tests/ack-all-subjects-of-account.test.ts","./tests/blob-divert.test.ts","./tests/communication-templates.test.ts","./tests/content-tagger.test.ts","./tests/db.test.ts","./tests/get-config.test.ts","./tests/get-lists.test.ts","./tests/get-profiles.test.ts","./tests/get-record.test.ts","./tests/get-records.test.ts","./tests/get-repo.test.ts","./tests/get-repos.test.ts","./tests/get-starter-pack.test.ts","./tests/moderation-appeals.test.ts","./tests/moderation-events.test.ts","./tests/moderation-status-tags.test.ts","./tests/moderation-statuses.test.ts","./tests/moderation.test.ts","./tests/protected-tags.test.ts","./tests/query-labels.test.ts","./tests/record-and-account-events.test.ts","./tests/repo-search.test.ts","./tests/report-muting.test.ts","./tests/sequencer.test.ts","./tests/server.test.ts","./tests/sets.test.ts","./tests/settings.test.ts","./tests/takedown.test.ts","./tests/team.test.ts"],"version":"5.6.3"}
|