@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
|
@@ -7,7 +7,7 @@ import { lexicons } from '../../../../lexicons'
|
|
|
7
7
|
import { isObj, hasProp } from '../../../../util'
|
|
8
8
|
import { CID } from 'multiformats/cid'
|
|
9
9
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
-
import * as
|
|
10
|
+
import * as ToolsOzoneModerationDefs from './defs'
|
|
11
11
|
|
|
12
12
|
export interface QueryParams {
|
|
13
13
|
uri: string
|
|
@@ -15,7 +15,7 @@ export interface QueryParams {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export type InputSchema = undefined
|
|
18
|
-
export type OutputSchema =
|
|
18
|
+
export type OutputSchema = ToolsOzoneModerationDefs.RecordViewDetail
|
|
19
19
|
export type HandlerInput = undefined
|
|
20
20
|
|
|
21
21
|
export interface HandlerSuccess {
|
|
@@ -7,14 +7,14 @@ import { lexicons } from '../../../../lexicons'
|
|
|
7
7
|
import { isObj, hasProp } from '../../../../util'
|
|
8
8
|
import { CID } from 'multiformats/cid'
|
|
9
9
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
-
import * as
|
|
10
|
+
import * as ToolsOzoneModerationDefs from './defs'
|
|
11
11
|
|
|
12
12
|
export interface QueryParams {
|
|
13
13
|
did: string
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export type InputSchema = undefined
|
|
17
|
-
export type OutputSchema =
|
|
17
|
+
export type OutputSchema = ToolsOzoneModerationDefs.RepoViewDetail
|
|
18
18
|
export type HandlerInput = undefined
|
|
19
19
|
|
|
20
20
|
export interface HandlerSuccess {
|
|
@@ -7,10 +7,10 @@ import { lexicons } from '../../../../lexicons'
|
|
|
7
7
|
import { isObj, hasProp } from '../../../../util'
|
|
8
8
|
import { CID } from 'multiformats/cid'
|
|
9
9
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
-
import * as
|
|
10
|
+
import * as ToolsOzoneModerationDefs from './defs'
|
|
11
11
|
|
|
12
12
|
export interface QueryParams {
|
|
13
|
-
/** The types of events (fully qualified string in the format of
|
|
13
|
+
/** The types of events (fully qualified string in the format of tools.ozone.moderation.defs#modEvent<name>) to filter by. If not specified, all events are returned. */
|
|
14
14
|
types?: string[]
|
|
15
15
|
createdBy?: string
|
|
16
16
|
/** Sort direction for the events. Defaults to descending order of created at timestamp. */
|
|
@@ -43,7 +43,7 @@ export type InputSchema = undefined
|
|
|
43
43
|
|
|
44
44
|
export interface OutputSchema {
|
|
45
45
|
cursor?: string
|
|
46
|
-
events:
|
|
46
|
+
events: ToolsOzoneModerationDefs.ModEventView[]
|
|
47
47
|
[k: string]: unknown
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -7,7 +7,7 @@ import { lexicons } from '../../../../lexicons'
|
|
|
7
7
|
import { isObj, hasProp } from '../../../../util'
|
|
8
8
|
import { CID } from 'multiformats/cid'
|
|
9
9
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
-
import * as
|
|
10
|
+
import * as ToolsOzoneModerationDefs from './defs'
|
|
11
11
|
|
|
12
12
|
export interface QueryParams {
|
|
13
13
|
subject?: string
|
|
@@ -44,7 +44,7 @@ export type InputSchema = undefined
|
|
|
44
44
|
|
|
45
45
|
export interface OutputSchema {
|
|
46
46
|
cursor?: string
|
|
47
|
-
subjectStatuses:
|
|
47
|
+
subjectStatuses: ToolsOzoneModerationDefs.SubjectStatusView[]
|
|
48
48
|
[k: string]: unknown
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -7,7 +7,7 @@ import { lexicons } from '../../../../lexicons'
|
|
|
7
7
|
import { isObj, hasProp } from '../../../../util'
|
|
8
8
|
import { CID } from 'multiformats/cid'
|
|
9
9
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
-
import * as
|
|
10
|
+
import * as ToolsOzoneModerationDefs from './defs'
|
|
11
11
|
|
|
12
12
|
export interface QueryParams {
|
|
13
13
|
/** DEPRECATED: use 'q' instead */
|
|
@@ -21,7 +21,7 @@ export type InputSchema = undefined
|
|
|
21
21
|
|
|
22
22
|
export interface OutputSchema {
|
|
23
23
|
cursor?: string
|
|
24
|
-
repos:
|
|
24
|
+
repos: ToolsOzoneModerationDefs.RepoView[]
|
|
25
25
|
[k: string]: unknown
|
|
26
26
|
}
|
|
27
27
|
|
package/src/mod-service/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { CID } from 'multiformats/cid'
|
|
|
4
4
|
import { AtUri, INVALID_HANDLE } from '@atproto/syntax'
|
|
5
5
|
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
6
6
|
import { addHoursToDate } from '@atproto/common'
|
|
7
|
+
import { Keypair } from '@atproto/crypto'
|
|
7
8
|
import { IdResolver } from '@atproto/identity'
|
|
8
9
|
import AtpAgent from '@atproto/api'
|
|
9
10
|
import { Database } from '../db'
|
|
@@ -17,9 +18,8 @@ import {
|
|
|
17
18
|
isModEventTakedown,
|
|
18
19
|
isModEventEmail,
|
|
19
20
|
isModEventTag,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from '../lexicon/types/com/atproto/admin/defs'
|
|
21
|
+
} from '../lexicon/types/tools/ozone/moderation/defs'
|
|
22
|
+
import { RepoRef, RepoBlobRef } from '../lexicon/types/com/atproto/admin/defs'
|
|
23
23
|
import {
|
|
24
24
|
adjustModerationSubjectStatus,
|
|
25
25
|
getStatusIdentifierFromSubject,
|
|
@@ -46,6 +46,7 @@ import { LabelChannel } from '../db/schema/label'
|
|
|
46
46
|
import { BlobPushEvent } from '../db/schema/blob_push_event'
|
|
47
47
|
import { BackgroundQueue } from '../background'
|
|
48
48
|
import { EventPusher } from '../daemon'
|
|
49
|
+
import { formatLabel, formatLabelRow, signLabel } from './util'
|
|
49
50
|
import { ImageInvalidator } from '../image-invalidator'
|
|
50
51
|
import { httpLogger as log } from '../logger'
|
|
51
52
|
import { OzoneConfig } from '../config'
|
|
@@ -55,45 +56,49 @@ export type ModerationServiceCreator = (db: Database) => ModerationService
|
|
|
55
56
|
export class ModerationService {
|
|
56
57
|
constructor(
|
|
57
58
|
public db: Database,
|
|
59
|
+
public signingKey: Keypair,
|
|
60
|
+
public signingKeyId: number,
|
|
58
61
|
public cfg: OzoneConfig,
|
|
59
62
|
public backgroundQueue: BackgroundQueue,
|
|
60
63
|
public idResolver: IdResolver,
|
|
61
64
|
public eventPusher: EventPusher,
|
|
62
65
|
public appviewAgent: AtpAgent,
|
|
63
66
|
private createAuthHeaders: (aud: string) => Promise<AuthHeaders>,
|
|
64
|
-
public serverDid: string,
|
|
65
67
|
public imgInvalidator?: ImageInvalidator,
|
|
66
|
-
public cdnPaths?: string[],
|
|
67
68
|
) {}
|
|
68
69
|
|
|
69
70
|
static creator(
|
|
71
|
+
signingKey: Keypair,
|
|
72
|
+
signingKeyId: number,
|
|
70
73
|
cfg: OzoneConfig,
|
|
71
74
|
backgroundQueue: BackgroundQueue,
|
|
72
75
|
idResolver: IdResolver,
|
|
73
76
|
eventPusher: EventPusher,
|
|
74
77
|
appviewAgent: AtpAgent,
|
|
75
78
|
createAuthHeaders: (aud: string) => Promise<AuthHeaders>,
|
|
76
|
-
serverDid: string,
|
|
77
79
|
imgInvalidator?: ImageInvalidator,
|
|
78
|
-
cdnPaths?: string[],
|
|
79
80
|
) {
|
|
80
81
|
return (db: Database) =>
|
|
81
82
|
new ModerationService(
|
|
82
83
|
db,
|
|
84
|
+
signingKey,
|
|
85
|
+
signingKeyId,
|
|
83
86
|
cfg,
|
|
84
87
|
backgroundQueue,
|
|
85
88
|
idResolver,
|
|
86
89
|
eventPusher,
|
|
87
90
|
appviewAgent,
|
|
88
91
|
createAuthHeaders,
|
|
89
|
-
serverDid,
|
|
90
92
|
imgInvalidator,
|
|
91
|
-
cdnPaths,
|
|
92
93
|
)
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
views = new ModerationViews(
|
|
96
|
-
this.
|
|
96
|
+
views = new ModerationViews(
|
|
97
|
+
this.db,
|
|
98
|
+
this.signingKey,
|
|
99
|
+
this.signingKeyId,
|
|
100
|
+
this.appviewAgent,
|
|
101
|
+
() => this.createAuthHeaders(this.cfg.appview.did),
|
|
97
102
|
)
|
|
98
103
|
|
|
99
104
|
async getEvent(id: number): Promise<ModerationEventRow | undefined> {
|
|
@@ -247,7 +252,7 @@ export class ModerationService {
|
|
|
247
252
|
async getReport(id: number): Promise<ModerationEventRow | undefined> {
|
|
248
253
|
return await this.db.db
|
|
249
254
|
.selectFrom('moderation_event')
|
|
250
|
-
.where('action', '=', '
|
|
255
|
+
.where('action', '=', 'tools.ozone.moderation.defs#modEventReport')
|
|
251
256
|
.selectAll()
|
|
252
257
|
.where('id', '=', id)
|
|
253
258
|
.executeTakeFirst()
|
|
@@ -370,12 +375,12 @@ export class ModerationService {
|
|
|
370
375
|
// Means the subject was suspended and needs to be unsuspended
|
|
371
376
|
if (subject.reverseSuspend) {
|
|
372
377
|
builder = builder
|
|
373
|
-
.where('action', '=', '
|
|
378
|
+
.where('action', '=', 'tools.ozone.moderation.defs#modEventTakedown')
|
|
374
379
|
.where('durationInHours', 'is not', null)
|
|
375
380
|
}
|
|
376
381
|
if (subject.reverseMute) {
|
|
377
382
|
builder = builder
|
|
378
|
-
.where('action', '=', '
|
|
383
|
+
.where('action', '=', 'tools.ozone.moderation.defs#modEventMute')
|
|
379
384
|
.where('durationInHours', 'is not', null)
|
|
380
385
|
}
|
|
381
386
|
|
|
@@ -422,13 +427,13 @@ export class ModerationService {
|
|
|
422
427
|
subject,
|
|
423
428
|
}: ReversibleModerationEvent): Promise<ModerationEventRow> {
|
|
424
429
|
const isRevertingTakedown =
|
|
425
|
-
action === '
|
|
430
|
+
action === 'tools.ozone.moderation.defs#modEventTakedown'
|
|
426
431
|
this.db.assertTransaction()
|
|
427
432
|
const { event } = await this.logEvent({
|
|
428
433
|
event: {
|
|
429
434
|
$type: isRevertingTakedown
|
|
430
|
-
? '
|
|
431
|
-
: '
|
|
435
|
+
? 'tools.ozone.moderation.defs#modEventReverseTakedown'
|
|
436
|
+
: 'tools.ozone.moderation.defs#modEventUnmute',
|
|
432
437
|
comment: comment ?? undefined,
|
|
433
438
|
},
|
|
434
439
|
createdAt,
|
|
@@ -560,27 +565,17 @@ export class ModerationService {
|
|
|
560
565
|
for (const cid of blobCids) {
|
|
561
566
|
blobValues.push({
|
|
562
567
|
eventType,
|
|
568
|
+
takedownRef,
|
|
563
569
|
subjectDid: subject.did,
|
|
570
|
+
subjectUri: subject.uri || null,
|
|
564
571
|
subjectBlobCid: cid.toString(),
|
|
565
|
-
takedownRef,
|
|
566
572
|
})
|
|
567
573
|
}
|
|
568
574
|
}
|
|
569
|
-
const blobEvts = await this.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
oc
|
|
574
|
-
.columns(['subjectDid', 'subjectBlobCid', 'eventType'])
|
|
575
|
-
.doUpdateSet({
|
|
576
|
-
takedownRef,
|
|
577
|
-
confirmedAt: null,
|
|
578
|
-
attempts: 0,
|
|
579
|
-
lastAttempted: null,
|
|
580
|
-
}),
|
|
581
|
-
)
|
|
582
|
-
.returning(['id', 'subjectDid', 'subjectBlobCid', 'eventType'])
|
|
583
|
-
.execute()
|
|
575
|
+
const blobEvts = await this.eventPusher.logBlobPushEvent(
|
|
576
|
+
blobValues,
|
|
577
|
+
takedownRef,
|
|
578
|
+
)
|
|
584
579
|
|
|
585
580
|
this.db.onCommit(() => {
|
|
586
581
|
this.backgroundQueue.add(async () => {
|
|
@@ -597,7 +592,7 @@ export class ModerationService {
|
|
|
597
592
|
if (this.imgInvalidator) {
|
|
598
593
|
await Promise.allSettled(
|
|
599
594
|
(subject.blobCids ?? []).map((cid) => {
|
|
600
|
-
const paths = (this.
|
|
595
|
+
const paths = (this.cfg.cdn.paths ?? []).map((path) =>
|
|
601
596
|
path.replace('%s', subject.did).replace('%s', cid),
|
|
602
597
|
)
|
|
603
598
|
return this.imgInvalidator
|
|
@@ -698,7 +693,7 @@ export class ModerationService {
|
|
|
698
693
|
|
|
699
694
|
const result = await this.logEvent({
|
|
700
695
|
event: {
|
|
701
|
-
$type: '
|
|
696
|
+
$type: 'tools.ozone.moderation.defs#modEventReport',
|
|
702
697
|
reportType: reasonType,
|
|
703
698
|
comment: reason,
|
|
704
699
|
},
|
|
@@ -876,7 +871,7 @@ export class ModerationService {
|
|
|
876
871
|
): Promise<Label[]> {
|
|
877
872
|
const { create = [], negate = [] } = labels
|
|
878
873
|
const toCreate = create.map((val) => ({
|
|
879
|
-
src: this.
|
|
874
|
+
src: this.cfg.service.did,
|
|
880
875
|
uri,
|
|
881
876
|
cid: cid ?? undefined,
|
|
882
877
|
val,
|
|
@@ -884,7 +879,7 @@ export class ModerationService {
|
|
|
884
879
|
cts: new Date().toISOString(),
|
|
885
880
|
}))
|
|
886
881
|
const toNegate = negate.map((val) => ({
|
|
887
|
-
src: this.
|
|
882
|
+
src: this.cfg.service.did,
|
|
888
883
|
uri,
|
|
889
884
|
cid: cid ?? undefined,
|
|
890
885
|
val,
|
|
@@ -892,21 +887,19 @@ export class ModerationService {
|
|
|
892
887
|
cts: new Date().toISOString(),
|
|
893
888
|
}))
|
|
894
889
|
const formatted = [...toCreate, ...toNegate]
|
|
895
|
-
|
|
896
|
-
return formatted
|
|
890
|
+
return this.createLabels(formatted)
|
|
897
891
|
}
|
|
898
892
|
|
|
899
|
-
async createLabels(labels: Label[]) {
|
|
900
|
-
if (labels.length < 1) return
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}))
|
|
893
|
+
async createLabels(labels: Label[]): Promise<Label[]> {
|
|
894
|
+
if (labels.length < 1) return []
|
|
895
|
+
const signedLabels = await Promise.all(
|
|
896
|
+
labels.map((l) => signLabel(l, this.signingKey)),
|
|
897
|
+
)
|
|
898
|
+
const dbVals = signedLabels.map((l) => formatLabelRow(l, this.signingKeyId))
|
|
906
899
|
const { ref } = this.db.db.dynamic
|
|
907
900
|
await sql`notify ${ref(LabelChannel)}`.execute(this.db.db)
|
|
908
901
|
const excluded = (col: string) => ref(`excluded.${col}`)
|
|
909
|
-
await this.db.db
|
|
902
|
+
const res = await this.db.db
|
|
910
903
|
.insertInto('label')
|
|
911
904
|
.values(dbVals)
|
|
912
905
|
.onConflict((oc) =>
|
|
@@ -916,7 +909,9 @@ export class ModerationService {
|
|
|
916
909
|
cts: sql`${excluded('cts')}`,
|
|
917
910
|
}),
|
|
918
911
|
)
|
|
912
|
+
.returningAll()
|
|
919
913
|
.execute()
|
|
914
|
+
return res.map((row) => formatLabel(row))
|
|
920
915
|
}
|
|
921
916
|
|
|
922
917
|
async sendEmail(opts: {
|
package/src/mod-service/lang.ts
CHANGED
|
@@ -25,7 +25,7 @@ export class ModerationLangService {
|
|
|
25
25
|
})
|
|
26
26
|
await this.moderationService.logEvent({
|
|
27
27
|
event: {
|
|
28
|
-
$type: '
|
|
28
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
29
29
|
add: recordLangs
|
|
30
30
|
? recordLangs.map((lang) => `lang:${lang}`)
|
|
31
31
|
: ['lang:und'],
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
REVIEWCLOSED,
|
|
9
9
|
REVIEWESCALATED,
|
|
10
10
|
REVIEWNONE,
|
|
11
|
-
} from '../lexicon/types/
|
|
11
|
+
} from '../lexicon/types/tools/ozone/moderation/defs'
|
|
12
12
|
import { ModerationEventRow, ModerationSubjectStatusRow } from './types'
|
|
13
13
|
import { HOUR } from '@atproto/common'
|
|
14
14
|
import { REASONAPPEAL } from '../lexicon/types/com/atproto/moderation/defs'
|
|
@@ -32,24 +32,24 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
32
32
|
: REVIEWNONE
|
|
33
33
|
|
|
34
34
|
switch (action) {
|
|
35
|
-
case '
|
|
35
|
+
case 'tools.ozone.moderation.defs#modEventAcknowledge':
|
|
36
36
|
return {
|
|
37
37
|
lastReviewedBy: createdBy,
|
|
38
38
|
reviewState: REVIEWCLOSED,
|
|
39
39
|
lastReviewedAt: createdAt,
|
|
40
40
|
}
|
|
41
|
-
case '
|
|
41
|
+
case 'tools.ozone.moderation.defs#modEventReport':
|
|
42
42
|
return {
|
|
43
43
|
reviewState: REVIEWOPEN,
|
|
44
44
|
lastReportedAt: createdAt,
|
|
45
45
|
}
|
|
46
|
-
case '
|
|
46
|
+
case 'tools.ozone.moderation.defs#modEventEscalate':
|
|
47
47
|
return {
|
|
48
48
|
lastReviewedBy: createdBy,
|
|
49
49
|
reviewState: REVIEWESCALATED,
|
|
50
50
|
lastReviewedAt: createdAt,
|
|
51
51
|
}
|
|
52
|
-
case '
|
|
52
|
+
case 'tools.ozone.moderation.defs#modEventReverseTakedown':
|
|
53
53
|
return {
|
|
54
54
|
lastReviewedBy: createdBy,
|
|
55
55
|
reviewState: REVIEWCLOSED,
|
|
@@ -57,7 +57,7 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
57
57
|
suspendUntil: null,
|
|
58
58
|
lastReviewedAt: createdAt,
|
|
59
59
|
}
|
|
60
|
-
case '
|
|
60
|
+
case 'tools.ozone.moderation.defs#modEventUnmute':
|
|
61
61
|
return {
|
|
62
62
|
lastReviewedBy: createdBy,
|
|
63
63
|
muteUntil: null,
|
|
@@ -66,7 +66,7 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
66
66
|
reviewState: defaultReviewState,
|
|
67
67
|
lastReviewedAt: createdAt,
|
|
68
68
|
}
|
|
69
|
-
case '
|
|
69
|
+
case 'tools.ozone.moderation.defs#modEventTakedown':
|
|
70
70
|
return {
|
|
71
71
|
takendown: true,
|
|
72
72
|
lastReviewedBy: createdBy,
|
|
@@ -76,7 +76,7 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
76
76
|
? new Date(Date.now() + durationInHours * HOUR).toISOString()
|
|
77
77
|
: null,
|
|
78
78
|
}
|
|
79
|
-
case '
|
|
79
|
+
case 'tools.ozone.moderation.defs#modEventMute':
|
|
80
80
|
return {
|
|
81
81
|
lastReviewedBy: createdBy,
|
|
82
82
|
lastReviewedAt: createdAt,
|
|
@@ -88,15 +88,15 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
88
88
|
// but if it does happen, default to unnecessary
|
|
89
89
|
reviewState: defaultReviewState,
|
|
90
90
|
}
|
|
91
|
-
case '
|
|
91
|
+
case 'tools.ozone.moderation.defs#modEventComment':
|
|
92
92
|
return {
|
|
93
93
|
lastReviewedBy: createdBy,
|
|
94
94
|
lastReviewedAt: createdAt,
|
|
95
95
|
reviewState: defaultReviewState,
|
|
96
96
|
}
|
|
97
|
-
case '
|
|
97
|
+
case 'tools.ozone.moderation.defs#modEventTag':
|
|
98
98
|
return { tags: [], reviewState: defaultReviewState }
|
|
99
|
-
case '
|
|
99
|
+
case 'tools.ozone.moderation.defs#modEventResolveAppeal':
|
|
100
100
|
return {
|
|
101
101
|
appealed: false,
|
|
102
102
|
}
|
|
@@ -141,7 +141,7 @@ export const adjustModerationSubjectStatus = async (
|
|
|
141
141
|
.executeTakeFirst()
|
|
142
142
|
|
|
143
143
|
const isAppealEvent =
|
|
144
|
-
action === '
|
|
144
|
+
action === 'tools.ozone.moderation.defs#modEventReport' &&
|
|
145
145
|
meta?.reportType === REASONAPPEAL
|
|
146
146
|
|
|
147
147
|
const subjectStatus = getSubjectStatusForModerationEvent({
|
|
@@ -183,7 +183,7 @@ export const adjustModerationSubjectStatus = async (
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
if (
|
|
186
|
-
action === '
|
|
186
|
+
action === 'tools.ozone.moderation.defs#modEventReverseTakedown' &&
|
|
187
187
|
!subjectStatus.takendown
|
|
188
188
|
) {
|
|
189
189
|
newStatus.takendown = false
|
|
@@ -201,19 +201,22 @@ export const adjustModerationSubjectStatus = async (
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
if (
|
|
204
|
-
action === '
|
|
204
|
+
action === 'tools.ozone.moderation.defs#modEventResolveAppeal' &&
|
|
205
205
|
subjectStatus.appealed
|
|
206
206
|
) {
|
|
207
207
|
newStatus.appealed = false
|
|
208
208
|
subjectStatus.appealed = false
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
if (
|
|
211
|
+
if (
|
|
212
|
+
action === 'tools.ozone.moderation.defs#modEventComment' &&
|
|
213
|
+
meta?.sticky
|
|
214
|
+
) {
|
|
212
215
|
newStatus.comment = comment
|
|
213
216
|
subjectStatus.comment = comment
|
|
214
217
|
}
|
|
215
218
|
|
|
216
|
-
if (action === '
|
|
219
|
+
if (action === 'tools.ozone.moderation.defs#modEventTag') {
|
|
217
220
|
let tags = currentStatus?.tags || []
|
|
218
221
|
if (addedTags?.length) {
|
|
219
222
|
tags = tags.concat(addedTags)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AtUri } from '@atproto/syntax'
|
|
2
2
|
import { InputSchema as ReportInput } from '../lexicon/types/com/atproto/moderation/createReport'
|
|
3
|
-
import { InputSchema as ActionInput } from '../lexicon/types/
|
|
3
|
+
import { InputSchema as ActionInput } from '../lexicon/types/tools/ozone/moderation/emitEvent'
|
|
4
4
|
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
5
5
|
import { ModerationEventRow, ModerationSubjectStatusRow } from './types'
|
|
6
6
|
import { RepoRef } from '../lexicon/types/com/atproto/admin/defs'
|
package/src/mod-service/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Selectable } from 'kysely'
|
|
2
2
|
import { ModerationEvent } from '../db/schema/moderation_event'
|
|
3
3
|
import { ModerationSubjectStatus } from '../db/schema/moderation_subject_status'
|
|
4
|
-
import {
|
|
4
|
+
import { ToolsOzoneModerationDefs } from '@atproto/api'
|
|
5
5
|
import { ModSubject } from './subject'
|
|
6
6
|
|
|
7
7
|
export type ModerationEventRow = Selectable<ModerationEvent>
|
|
@@ -22,15 +22,15 @@ export type ModerationSubjectStatusRowWithHandle =
|
|
|
22
22
|
ModerationSubjectStatusRow & { handle: string | null }
|
|
23
23
|
|
|
24
24
|
export type ModEventType =
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
25
|
+
| ToolsOzoneModerationDefs.ModEventTakedown
|
|
26
|
+
| ToolsOzoneModerationDefs.ModEventAcknowledge
|
|
27
|
+
| ToolsOzoneModerationDefs.ModEventEscalate
|
|
28
|
+
| ToolsOzoneModerationDefs.ModEventComment
|
|
29
|
+
| ToolsOzoneModerationDefs.ModEventLabel
|
|
30
|
+
| ToolsOzoneModerationDefs.ModEventReport
|
|
31
|
+
| ToolsOzoneModerationDefs.ModEventMute
|
|
32
|
+
| ToolsOzoneModerationDefs.ModEventReverseTakedown
|
|
33
|
+
| ToolsOzoneModerationDefs.ModEventTag
|
|
34
34
|
|
|
35
35
|
export const UNSPECCED_TAKEDOWN_LABEL = '!unspecced-takedown'
|
|
36
36
|
|
package/src/mod-service/util.ts
CHANGED
|
@@ -1,17 +1,61 @@
|
|
|
1
|
+
import { cborEncode, noUndefinedVals } from '@atproto/common'
|
|
2
|
+
import { Keypair } from '@atproto/crypto'
|
|
1
3
|
import { LabelRow } from '../db/schema/label'
|
|
2
4
|
import { Label } from '../lexicon/types/com/atproto/label/defs'
|
|
3
5
|
|
|
6
|
+
export type SignedLabel = Label & { sig: Uint8Array }
|
|
7
|
+
|
|
4
8
|
export const formatLabel = (row: LabelRow): Label => {
|
|
5
|
-
|
|
9
|
+
return noUndefinedVals({
|
|
10
|
+
ver: 1,
|
|
6
11
|
src: row.src,
|
|
7
12
|
uri: row.uri,
|
|
13
|
+
cid: row.cid === '' ? undefined : row.cid,
|
|
8
14
|
val: row.val,
|
|
9
15
|
neg: row.neg,
|
|
10
16
|
cts: row.cts,
|
|
17
|
+
exp: row.exp ?? undefined,
|
|
18
|
+
sig: row.sig ? new Uint8Array(row.sig) : undefined,
|
|
19
|
+
}) as Label
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const formatLabelRow = (
|
|
23
|
+
label: Label,
|
|
24
|
+
signingKeyId?: number,
|
|
25
|
+
): Omit<LabelRow, 'id'> => {
|
|
26
|
+
return {
|
|
27
|
+
src: label.src,
|
|
28
|
+
uri: label.uri,
|
|
29
|
+
cid: label.cid ?? '',
|
|
30
|
+
val: label.val,
|
|
31
|
+
neg: !!label.neg,
|
|
32
|
+
cts: label.cts,
|
|
33
|
+
exp: label.exp ?? null,
|
|
34
|
+
sig: label.sig ? Buffer.from(label.sig) : null,
|
|
35
|
+
signingKeyId: signingKeyId ?? null,
|
|
11
36
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const signLabel = async (
|
|
40
|
+
label: Label,
|
|
41
|
+
signingKey: Keypair,
|
|
42
|
+
): Promise<SignedLabel> => {
|
|
43
|
+
const { ver, src, uri, cid, val, neg, cts, exp } = label
|
|
44
|
+
const reformatted = noUndefinedVals({
|
|
45
|
+
ver: ver ?? 1,
|
|
46
|
+
src,
|
|
47
|
+
uri,
|
|
48
|
+
cid,
|
|
49
|
+
val,
|
|
50
|
+
neg,
|
|
51
|
+
cts,
|
|
52
|
+
exp,
|
|
53
|
+
}) as Label
|
|
54
|
+
|
|
55
|
+
const bytes = cborEncode(reformatted)
|
|
56
|
+
const sig = await signingKey.sign(bytes)
|
|
57
|
+
return {
|
|
58
|
+
...reformatted,
|
|
59
|
+
sig,
|
|
15
60
|
}
|
|
16
|
-
return label
|
|
17
61
|
}
|