@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
|
@@ -236,6 +236,12 @@ export declare const schemaDict: {
|
|
|
236
236
|
type: string;
|
|
237
237
|
format: string;
|
|
238
238
|
};
|
|
239
|
+
tags: {
|
|
240
|
+
type: string;
|
|
241
|
+
items: {
|
|
242
|
+
type: string;
|
|
243
|
+
};
|
|
244
|
+
};
|
|
239
245
|
};
|
|
240
246
|
};
|
|
241
247
|
reportViewDetail: {
|
|
@@ -779,6 +785,31 @@ export declare const schemaDict: {
|
|
|
779
785
|
};
|
|
780
786
|
};
|
|
781
787
|
};
|
|
788
|
+
modEventTag: {
|
|
789
|
+
type: string;
|
|
790
|
+
description: string;
|
|
791
|
+
required: string[];
|
|
792
|
+
properties: {
|
|
793
|
+
add: {
|
|
794
|
+
type: string;
|
|
795
|
+
items: {
|
|
796
|
+
type: string;
|
|
797
|
+
};
|
|
798
|
+
description: string;
|
|
799
|
+
};
|
|
800
|
+
remove: {
|
|
801
|
+
type: string;
|
|
802
|
+
items: {
|
|
803
|
+
type: string;
|
|
804
|
+
};
|
|
805
|
+
description: string;
|
|
806
|
+
};
|
|
807
|
+
comment: {
|
|
808
|
+
type: string;
|
|
809
|
+
description: string;
|
|
810
|
+
};
|
|
811
|
+
};
|
|
812
|
+
};
|
|
782
813
|
communicationTemplateView: {
|
|
783
814
|
type: string;
|
|
784
815
|
required: string[];
|
|
@@ -1343,6 +1374,20 @@ export declare const schemaDict: {
|
|
|
1343
1374
|
};
|
|
1344
1375
|
description: string;
|
|
1345
1376
|
};
|
|
1377
|
+
addedTags: {
|
|
1378
|
+
type: string;
|
|
1379
|
+
items: {
|
|
1380
|
+
type: string;
|
|
1381
|
+
};
|
|
1382
|
+
description: string;
|
|
1383
|
+
};
|
|
1384
|
+
removedTags: {
|
|
1385
|
+
type: string;
|
|
1386
|
+
items: {
|
|
1387
|
+
type: string;
|
|
1388
|
+
};
|
|
1389
|
+
description: string;
|
|
1390
|
+
};
|
|
1346
1391
|
reportTypes: {
|
|
1347
1392
|
type: string;
|
|
1348
1393
|
items: {
|
|
@@ -1458,6 +1503,18 @@ export declare const schemaDict: {
|
|
|
1458
1503
|
maximum: number;
|
|
1459
1504
|
default: number;
|
|
1460
1505
|
};
|
|
1506
|
+
tags: {
|
|
1507
|
+
type: string;
|
|
1508
|
+
items: {
|
|
1509
|
+
type: string;
|
|
1510
|
+
};
|
|
1511
|
+
};
|
|
1512
|
+
excludeTags: {
|
|
1513
|
+
type: string;
|
|
1514
|
+
items: {
|
|
1515
|
+
type: string;
|
|
1516
|
+
};
|
|
1517
|
+
};
|
|
1461
1518
|
cursor: {
|
|
1462
1519
|
type: string;
|
|
1463
1520
|
};
|
|
@@ -82,6 +82,7 @@ export interface SubjectStatusView {
|
|
|
82
82
|
takendown?: boolean;
|
|
83
83
|
appealed?: boolean;
|
|
84
84
|
suspendUntil?: string;
|
|
85
|
+
tags?: string[];
|
|
85
86
|
[k: string]: unknown;
|
|
86
87
|
}
|
|
87
88
|
export declare function isSubjectStatusView(v: unknown): v is SubjectStatusView;
|
|
@@ -317,6 +318,14 @@ export interface ModEventEmail {
|
|
|
317
318
|
}
|
|
318
319
|
export declare function isModEventEmail(v: unknown): v is ModEventEmail;
|
|
319
320
|
export declare function validateModEventEmail(v: unknown): ValidationResult;
|
|
321
|
+
export interface ModEventTag {
|
|
322
|
+
add: string[];
|
|
323
|
+
remove: string[];
|
|
324
|
+
comment?: string;
|
|
325
|
+
[k: string]: unknown;
|
|
326
|
+
}
|
|
327
|
+
export declare function isModEventTag(v: unknown): v is ModEventTag;
|
|
328
|
+
export declare function validateModEventTag(v: unknown): ValidationResult;
|
|
320
329
|
export interface CommunicationTemplateView {
|
|
321
330
|
id: string;
|
|
322
331
|
name: string;
|
|
@@ -5,7 +5,7 @@ import * as ComAtprotoRepoStrongRef from '../repo/strongRef';
|
|
|
5
5
|
export interface QueryParams {
|
|
6
6
|
}
|
|
7
7
|
export interface InputSchema {
|
|
8
|
-
event: ComAtprotoAdminDefs.ModEventTakedown | ComAtprotoAdminDefs.ModEventAcknowledge | ComAtprotoAdminDefs.ModEventEscalate | ComAtprotoAdminDefs.ModEventComment | ComAtprotoAdminDefs.ModEventLabel | ComAtprotoAdminDefs.ModEventReport | ComAtprotoAdminDefs.ModEventMute | ComAtprotoAdminDefs.ModEventReverseTakedown | ComAtprotoAdminDefs.ModEventUnmute | ComAtprotoAdminDefs.ModEventEmail | {
|
|
8
|
+
event: ComAtprotoAdminDefs.ModEventTakedown | ComAtprotoAdminDefs.ModEventAcknowledge | ComAtprotoAdminDefs.ModEventEscalate | ComAtprotoAdminDefs.ModEventComment | ComAtprotoAdminDefs.ModEventLabel | ComAtprotoAdminDefs.ModEventReport | ComAtprotoAdminDefs.ModEventMute | ComAtprotoAdminDefs.ModEventReverseTakedown | ComAtprotoAdminDefs.ModEventUnmute | ComAtprotoAdminDefs.ModEventEmail | ComAtprotoAdminDefs.ModEventTag | {
|
|
9
9
|
$type: string;
|
|
10
10
|
[k: string]: unknown;
|
|
11
11
|
};
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { subsystemLogger } from '@atproto/common';
|
|
2
2
|
export declare const dbLogger: ReturnType<typeof subsystemLogger>;
|
|
3
3
|
export declare const httpLogger: ReturnType<typeof subsystemLogger>;
|
|
4
|
+
export declare const langLogger: ReturnType<typeof subsystemLogger>;
|
|
4
5
|
export declare const loggerMiddleware: import("pino-http").HttpLogger;
|
|
@@ -38,6 +38,8 @@ export declare class ModerationService {
|
|
|
38
38
|
createdBefore?: string;
|
|
39
39
|
addedLabels: string[];
|
|
40
40
|
removedLabels: string[];
|
|
41
|
+
addedTags: string[];
|
|
42
|
+
removedTags: string[];
|
|
41
43
|
reportTypes?: string[];
|
|
42
44
|
}): Promise<{
|
|
43
45
|
cursor?: string;
|
|
@@ -60,6 +62,7 @@ export declare class ModerationService {
|
|
|
60
62
|
comment: string | null;
|
|
61
63
|
lastReportedAt: string | null;
|
|
62
64
|
lastReviewedAt: string | null;
|
|
65
|
+
tags: string[] | null;
|
|
63
66
|
muteUntil: string | null;
|
|
64
67
|
lastReviewedBy: string | null;
|
|
65
68
|
lastAppealedAt: string | null;
|
|
@@ -74,7 +77,10 @@ export declare class ModerationService {
|
|
|
74
77
|
subject: ModSubject;
|
|
75
78
|
createdBy: string;
|
|
76
79
|
createdAt?: Date;
|
|
77
|
-
}): Promise<
|
|
80
|
+
}): Promise<{
|
|
81
|
+
event: ModerationEventRow;
|
|
82
|
+
subjectStatus: ModerationSubjectStatusRow | null;
|
|
83
|
+
}>;
|
|
78
84
|
getLastReversibleEventForSubject(subject: ReversalSubject): Promise<{
|
|
79
85
|
id: number;
|
|
80
86
|
subjectBlobCids: string[] | null;
|
|
@@ -84,7 +90,7 @@ export declare class ModerationService {
|
|
|
84
90
|
createLabelVals: string | null;
|
|
85
91
|
negateLabelVals: string | null;
|
|
86
92
|
durationInHours: number | null;
|
|
87
|
-
action: "com.atproto.admin.defs#modEventTakedown" | "com.atproto.admin.defs#modEventAcknowledge" | "com.atproto.admin.defs#modEventEscalate" | "com.atproto.admin.defs#modEventComment" | "com.atproto.admin.defs#modEventLabel" | "com.atproto.admin.defs#modEventReport" | "com.atproto.admin.defs#modEventMute" | "com.atproto.admin.defs#modEventReverseTakedown" | "com.atproto.admin.defs#modEventEmail" | "com.atproto.admin.defs#modEventResolveAppeal";
|
|
93
|
+
action: "com.atproto.admin.defs#modEventTakedown" | "com.atproto.admin.defs#modEventAcknowledge" | "com.atproto.admin.defs#modEventEscalate" | "com.atproto.admin.defs#modEventComment" | "com.atproto.admin.defs#modEventLabel" | "com.atproto.admin.defs#modEventReport" | "com.atproto.admin.defs#modEventMute" | "com.atproto.admin.defs#modEventReverseTakedown" | "com.atproto.admin.defs#modEventEmail" | "com.atproto.admin.defs#modEventResolveAppeal" | "com.atproto.admin.defs#modEventTag";
|
|
88
94
|
subjectType: "com.atproto.admin.defs#repoRef" | "com.atproto.repo.strongRef";
|
|
89
95
|
subjectDid: string;
|
|
90
96
|
subjectUri: string | null;
|
|
@@ -92,6 +98,8 @@ export declare class ModerationService {
|
|
|
92
98
|
meta: Record<string, string | boolean> | null;
|
|
93
99
|
expiresAt: string | null;
|
|
94
100
|
legacyRefId: number | null;
|
|
101
|
+
addedTags: string[] | null;
|
|
102
|
+
removedTags: string[] | null;
|
|
95
103
|
} | null | undefined>;
|
|
96
104
|
getSubjectsDueForReversal(): Promise<ReversalSubject[]>;
|
|
97
105
|
isSubjectSuspended(did: string): Promise<boolean>;
|
|
@@ -106,8 +114,11 @@ export declare class ModerationService {
|
|
|
106
114
|
subject: ModSubject;
|
|
107
115
|
reportedBy: string;
|
|
108
116
|
createdAt?: Date;
|
|
109
|
-
}): Promise<
|
|
110
|
-
|
|
117
|
+
}): Promise<{
|
|
118
|
+
event: ModerationEventRow;
|
|
119
|
+
subjectStatus: ModerationSubjectStatusRow | null;
|
|
120
|
+
}>;
|
|
121
|
+
getSubjectStatuses({ cursor, limit, takendown, appealed, reviewState, reviewedAfter, reviewedBefore, reportedAfter, reportedBefore, includeMuted, ignoreSubjects, sortDirection, lastReviewedBy, sortField, subject, tags, excludeTags, }: {
|
|
111
122
|
cursor?: string;
|
|
112
123
|
limit?: number;
|
|
113
124
|
takendown?: boolean;
|
|
@@ -123,6 +134,8 @@ export declare class ModerationService {
|
|
|
123
134
|
sortDirection: 'asc' | 'desc';
|
|
124
135
|
lastReviewedBy?: string;
|
|
125
136
|
sortField: 'lastReviewedAt' | 'lastReportedAt';
|
|
137
|
+
tags: string[];
|
|
138
|
+
excludeTags: string[];
|
|
126
139
|
}): Promise<{
|
|
127
140
|
statuses: {
|
|
128
141
|
handle: string;
|
|
@@ -135,6 +148,7 @@ export declare class ModerationService {
|
|
|
135
148
|
comment: string | null;
|
|
136
149
|
lastReportedAt: string | null;
|
|
137
150
|
lastReviewedAt: string | null;
|
|
151
|
+
tags: string[] | null;
|
|
138
152
|
muteUntil: string | null;
|
|
139
153
|
lastReviewedBy: string | null;
|
|
140
154
|
lastAppealedAt: string | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ModerationService } from '.';
|
|
2
|
+
import { ModSubject } from './subject';
|
|
3
|
+
import { ModerationSubjectStatusRow } from './types';
|
|
4
|
+
export declare class ModerationLangService {
|
|
5
|
+
private moderationService;
|
|
6
|
+
constructor(moderationService: ModerationService);
|
|
7
|
+
tagSubjectWithLang({ subject, subjectStatus, createdBy, }: {
|
|
8
|
+
subject: ModSubject;
|
|
9
|
+
createdBy: string;
|
|
10
|
+
subjectStatus: ModerationSubjectStatusRow | null;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
getRecordLang({ subject, }: {
|
|
13
|
+
subject: ModSubject;
|
|
14
|
+
}): Promise<string[] | null>;
|
|
15
|
+
}
|
|
@@ -2,7 +2,7 @@ import { AtUri } from '@atproto/syntax';
|
|
|
2
2
|
import { Database } from '../db';
|
|
3
3
|
import { ModerationSubjectStatus } from '../db/schema/moderation_subject_status';
|
|
4
4
|
import { ModerationEventRow } from './types';
|
|
5
|
-
export declare const adjustModerationSubjectStatus: (db: Database, moderationEvent: ModerationEventRow, blobCids?: string[]) => Promise<import("kysely").
|
|
5
|
+
export declare const adjustModerationSubjectStatus: (db: Database, moderationEvent: ModerationEventRow, blobCids?: string[]) => Promise<import("kysely").Selectable<ModerationSubjectStatus> | null>;
|
|
6
6
|
type ModerationSubjectStatusFilter = Pick<ModerationSubjectStatus, 'did'> | Pick<ModerationSubjectStatus, 'did' | 'recordPath'> | Pick<ModerationSubjectStatus, 'did' | 'recordPath' | 'recordCid'>;
|
|
7
7
|
export declare const getModerationSubjectStatus: (db: Database, filters: ModerationSubjectStatusFilter) => Promise<{} | undefined>;
|
|
8
8
|
export declare const getStatusIdentifierFromSubject: (subject: string | AtUri) => {
|
|
@@ -16,6 +16,6 @@ export type ModerationSubjectStatusRow = Selectable<ModerationSubjectStatus>;
|
|
|
16
16
|
export type ModerationSubjectStatusRowWithHandle = ModerationSubjectStatusRow & {
|
|
17
17
|
handle: string | null;
|
|
18
18
|
};
|
|
19
|
-
export type ModEventType = ComAtprotoAdminDefs.ModEventTakedown | ComAtprotoAdminDefs.ModEventAcknowledge | ComAtprotoAdminDefs.ModEventEscalate | ComAtprotoAdminDefs.ModEventComment | ComAtprotoAdminDefs.ModEventLabel | ComAtprotoAdminDefs.ModEventReport | ComAtprotoAdminDefs.ModEventMute | ComAtprotoAdminDefs.ModEventReverseTakedown;
|
|
19
|
+
export type ModEventType = ComAtprotoAdminDefs.ModEventTakedown | ComAtprotoAdminDefs.ModEventAcknowledge | ComAtprotoAdminDefs.ModEventEscalate | ComAtprotoAdminDefs.ModEventComment | ComAtprotoAdminDefs.ModEventLabel | ComAtprotoAdminDefs.ModEventReport | ComAtprotoAdminDefs.ModEventMute | ComAtprotoAdminDefs.ModEventReverseTakedown | ComAtprotoAdminDefs.ModEventTag;
|
|
20
20
|
export declare const UNSPECCED_TAKEDOWN_LABEL = "!unspecced-takedown";
|
|
21
21
|
export declare const UNSPECCED_TAKEDOWN_BLOBS_LABEL = "!unspecced-takedown-blobs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import AtpAgent from '@atproto/api';
|
|
1
|
+
import AtpAgent, { AppBskyFeedDefs } from '@atproto/api';
|
|
2
2
|
import { BlobRef } from '@atproto/lexicon';
|
|
3
3
|
import { Database } from '../db';
|
|
4
4
|
import { ModEventView, RepoView, RepoViewDetail, RecordView, RecordViewDetail, ReportViewDetail, BlobView, SubjectStatusView, ModEventViewDetail, AccountView } from '../lexicon/types/com/atproto/admin/defs';
|
|
@@ -29,6 +29,7 @@ export declare class ModerationViews {
|
|
|
29
29
|
labels(subject: string, includeNeg?: boolean): Promise<Label[]>;
|
|
30
30
|
getSubjectStatus(subjects: string[]): Promise<Map<string, ModerationSubjectStatusRowWithHandle>>;
|
|
31
31
|
formatSubjectStatus(status: ModerationSubjectStatusRowWithHandle): SubjectStatusView;
|
|
32
|
+
fetchAuthorFeed(actor: string): Promise<AppBskyFeedDefs.FeedViewPost[]>;
|
|
32
33
|
}
|
|
33
34
|
type RecordSubject = {
|
|
34
35
|
uri: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/ozone",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backend service for moderating the Bluesky network.",
|
|
6
6
|
"keywords": [
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"pino-http": "^8.2.1",
|
|
31
31
|
"typed-emitter": "^2.1.0",
|
|
32
32
|
"uint8arrays": "3.0.0",
|
|
33
|
-
"@atproto/api": "^0.9.
|
|
33
|
+
"@atproto/api": "^0.9.8",
|
|
34
34
|
"@atproto/common": "^0.3.3",
|
|
35
35
|
"@atproto/crypto": "^0.3.0",
|
|
36
|
-
"@atproto/syntax": "^0.1.5",
|
|
37
36
|
"@atproto/identity": "^0.3.2",
|
|
38
37
|
"@atproto/lexicon": "^0.3.1",
|
|
38
|
+
"@atproto/syntax": "^0.1.5",
|
|
39
39
|
"@atproto/xrpc-server": "^0.4.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"@types/pg": "^8.6.6",
|
|
47
47
|
"@types/qs": "^6.9.7",
|
|
48
48
|
"axios": "^0.27.2",
|
|
49
|
-
"@atproto/api": "^0.9.
|
|
50
|
-
"@atproto/dev-env": "^0.2.
|
|
49
|
+
"@atproto/api": "^0.9.8",
|
|
50
|
+
"@atproto/dev-env": "^0.2.31",
|
|
51
51
|
"@atproto/lex-cli": "^0.3.0",
|
|
52
|
-
"@atproto/pds": "^0.3.
|
|
52
|
+
"@atproto/pds": "^0.3.19",
|
|
53
53
|
"@atproto/xrpc": "^0.4.1"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
isModEventTakedown,
|
|
8
8
|
} from '../../lexicon/types/com/atproto/admin/defs'
|
|
9
9
|
import { subjectFromInput } from '../../mod-service/subject'
|
|
10
|
+
import { ModerationLangService } from '../../mod-service/lang'
|
|
10
11
|
|
|
11
12
|
export default function (server: Server, ctx: AppContext) {
|
|
12
13
|
server.com.atproto.admin.emitModerationEvent({
|
|
@@ -77,10 +78,21 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
77
78
|
createdBy,
|
|
78
79
|
})
|
|
79
80
|
|
|
81
|
+
const moderationLangService = new ModerationLangService(moderationTxn)
|
|
82
|
+
await moderationLangService.tagSubjectWithLang({
|
|
83
|
+
subject,
|
|
84
|
+
createdBy: ctx.cfg.service.did,
|
|
85
|
+
subjectStatus: result.subjectStatus,
|
|
86
|
+
})
|
|
87
|
+
|
|
80
88
|
if (subject.isRepo()) {
|
|
81
89
|
if (isTakedownEvent) {
|
|
82
|
-
const isSuspend = !!result.durationInHours
|
|
83
|
-
await moderationTxn.takedownRepo(
|
|
90
|
+
const isSuspend = !!result.event.durationInHours
|
|
91
|
+
await moderationTxn.takedownRepo(
|
|
92
|
+
subject,
|
|
93
|
+
result.event.id,
|
|
94
|
+
isSuspend,
|
|
95
|
+
)
|
|
84
96
|
} else if (isReverseTakedownEvent) {
|
|
85
97
|
await moderationTxn.reverseTakedownRepo(subject)
|
|
86
98
|
}
|
|
@@ -88,7 +100,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
88
100
|
|
|
89
101
|
if (subject.isRecord()) {
|
|
90
102
|
if (isTakedownEvent) {
|
|
91
|
-
await moderationTxn.takedownRecord(subject, result.id)
|
|
103
|
+
await moderationTxn.takedownRecord(subject, result.event.id)
|
|
92
104
|
} else if (isReverseTakedownEvent) {
|
|
93
105
|
await moderationTxn.reverseTakedownRecord(subject)
|
|
94
106
|
}
|
|
@@ -96,20 +108,20 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
96
108
|
|
|
97
109
|
if (isLabelEvent) {
|
|
98
110
|
await moderationTxn.formatAndCreateLabels(
|
|
99
|
-
result.subjectUri ?? result.subjectDid,
|
|
100
|
-
result.subjectCid,
|
|
111
|
+
result.event.subjectUri ?? result.event.subjectDid,
|
|
112
|
+
result.event.subjectCid,
|
|
101
113
|
{
|
|
102
|
-
create: result.createLabelVals?.length
|
|
103
|
-
? result.createLabelVals.split(' ')
|
|
114
|
+
create: result.event.createLabelVals?.length
|
|
115
|
+
? result.event.createLabelVals.split(' ')
|
|
104
116
|
: undefined,
|
|
105
|
-
negate: result.negateLabelVals?.length
|
|
106
|
-
? result.negateLabelVals.split(' ')
|
|
117
|
+
negate: result.event.negateLabelVals?.length
|
|
118
|
+
? result.event.negateLabelVals.split(' ')
|
|
107
119
|
: undefined,
|
|
108
120
|
},
|
|
109
121
|
)
|
|
110
122
|
}
|
|
111
123
|
|
|
112
|
-
return result
|
|
124
|
+
return result.event
|
|
113
125
|
})
|
|
114
126
|
|
|
115
127
|
return {
|
|
@@ -20,6 +20,8 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
20
20
|
createdBefore,
|
|
21
21
|
addedLabels = [],
|
|
22
22
|
removedLabels = [],
|
|
23
|
+
addedTags = [],
|
|
24
|
+
removedTags = [],
|
|
23
25
|
reportTypes,
|
|
24
26
|
} = params
|
|
25
27
|
const db = ctx.db
|
|
@@ -37,7 +39,9 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
37
39
|
createdAfter,
|
|
38
40
|
createdBefore,
|
|
39
41
|
addedLabels,
|
|
42
|
+
addedTags,
|
|
40
43
|
removedLabels,
|
|
44
|
+
removedTags,
|
|
41
45
|
reportTypes,
|
|
42
46
|
})
|
|
43
47
|
return {
|
|
@@ -22,6 +22,8 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
22
22
|
includeMuted = false,
|
|
23
23
|
limit = 50,
|
|
24
24
|
cursor,
|
|
25
|
+
tags = [],
|
|
26
|
+
excludeTags = [],
|
|
25
27
|
} = params
|
|
26
28
|
const db = ctx.db
|
|
27
29
|
const modService = ctx.modService(db)
|
|
@@ -41,6 +43,8 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
41
43
|
sortField,
|
|
42
44
|
limit,
|
|
43
45
|
cursor,
|
|
46
|
+
tags,
|
|
47
|
+
excludeTags,
|
|
44
48
|
})
|
|
45
49
|
const subjectStatuses = results.statuses.map((status) =>
|
|
46
50
|
modService.views.formatSubjectStatus(status),
|
|
@@ -4,6 +4,7 @@ import { getReasonType } from './util'
|
|
|
4
4
|
import { subjectFromInput } from '../../mod-service/subject'
|
|
5
5
|
import { REASONAPPEAL } from '../../lexicon/types/com/atproto/moderation/defs'
|
|
6
6
|
import { ForbiddenError } from '@atproto/xrpc-server'
|
|
7
|
+
import { ModerationLangService } from '../../mod-service/lang'
|
|
7
8
|
|
|
8
9
|
export default function (server: Server, ctx: AppContext) {
|
|
9
10
|
server.com.atproto.moderation.createReport({
|
|
@@ -23,12 +24,22 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
23
24
|
const db = ctx.db
|
|
24
25
|
const report = await db.transaction(async (dbTxn) => {
|
|
25
26
|
const moderationTxn = ctx.modService(dbTxn)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const { event: reportEvent, subjectStatus } =
|
|
28
|
+
await moderationTxn.report({
|
|
29
|
+
reasonType: getReasonType(reasonType),
|
|
30
|
+
reason,
|
|
31
|
+
subject,
|
|
32
|
+
reportedBy: requester || ctx.cfg.service.did,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const moderationLangService = new ModerationLangService(moderationTxn)
|
|
36
|
+
await moderationLangService.tagSubjectWithLang({
|
|
29
37
|
subject,
|
|
30
|
-
|
|
38
|
+
subjectStatus,
|
|
39
|
+
createdBy: ctx.cfg.service.did,
|
|
31
40
|
})
|
|
41
|
+
|
|
42
|
+
return reportEvent
|
|
32
43
|
})
|
|
33
44
|
|
|
34
45
|
const body = ctx.modService(db).views.formatReport(report)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.alterTable('moderation_event')
|
|
6
|
+
.addColumn('addedTags', 'jsonb')
|
|
7
|
+
.execute()
|
|
8
|
+
await db.schema
|
|
9
|
+
.alterTable('moderation_event')
|
|
10
|
+
.addColumn('removedTags', 'jsonb')
|
|
11
|
+
.execute()
|
|
12
|
+
await db.schema
|
|
13
|
+
.alterTable('moderation_subject_status')
|
|
14
|
+
.addColumn('tags', 'jsonb')
|
|
15
|
+
.execute()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
19
|
+
await db.schema
|
|
20
|
+
.alterTable('moderation_event')
|
|
21
|
+
.dropColumn('addedTags')
|
|
22
|
+
.execute()
|
|
23
|
+
await db.schema
|
|
24
|
+
.alterTable('moderation_event')
|
|
25
|
+
.dropColumn('removedTags')
|
|
26
|
+
.execute()
|
|
27
|
+
await db.schema
|
|
28
|
+
.alterTable('moderation_subject_status')
|
|
29
|
+
.dropColumn('tags')
|
|
30
|
+
.execute()
|
|
31
|
+
}
|
|
@@ -5,3 +5,4 @@
|
|
|
5
5
|
export * as _20231219T205730722Z from './20231219T205730722Z-init'
|
|
6
6
|
export * as _20240116T085607200Z from './20240116T085607200Z-communication-template'
|
|
7
7
|
export * as _20240201T051104136Z from './20240201T051104136Z-mod-event-blobs'
|
|
8
|
+
export * as _20240208T213404429Z from './20240208T213404429Z-add-tags-column-to-moderation-subject'
|
|
@@ -15,6 +15,7 @@ export interface ModerationEvent {
|
|
|
15
15
|
| 'com.atproto.admin.defs#modEventReverseTakedown'
|
|
16
16
|
| 'com.atproto.admin.defs#modEventEmail'
|
|
17
17
|
| 'com.atproto.admin.defs#modEventResolveAppeal'
|
|
18
|
+
| 'com.atproto.admin.defs#modEventTag'
|
|
18
19
|
subjectType: 'com.atproto.admin.defs#repoRef' | 'com.atproto.repo.strongRef'
|
|
19
20
|
subjectDid: string
|
|
20
21
|
subjectUri: string | null
|
|
@@ -28,6 +29,8 @@ export interface ModerationEvent {
|
|
|
28
29
|
durationInHours: number | null
|
|
29
30
|
expiresAt: string | null
|
|
30
31
|
meta: Record<string, string | boolean> | null
|
|
32
|
+
addedTags: string[] | null
|
|
33
|
+
removedTags: string[] | null
|
|
31
34
|
legacyRefId: number | null
|
|
32
35
|
}
|
|
33
36
|
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -303,6 +303,12 @@ export const schemaDict = {
|
|
|
303
303
|
type: 'string',
|
|
304
304
|
format: 'datetime',
|
|
305
305
|
},
|
|
306
|
+
tags: {
|
|
307
|
+
type: 'array',
|
|
308
|
+
items: {
|
|
309
|
+
type: 'string',
|
|
310
|
+
},
|
|
311
|
+
},
|
|
306
312
|
},
|
|
307
313
|
},
|
|
308
314
|
reportViewDetail: {
|
|
@@ -897,6 +903,33 @@ export const schemaDict = {
|
|
|
897
903
|
},
|
|
898
904
|
},
|
|
899
905
|
},
|
|
906
|
+
modEventTag: {
|
|
907
|
+
type: 'object',
|
|
908
|
+
description: 'Add/Remove a tag on a subject',
|
|
909
|
+
required: ['add', 'remove'],
|
|
910
|
+
properties: {
|
|
911
|
+
add: {
|
|
912
|
+
type: 'array',
|
|
913
|
+
items: {
|
|
914
|
+
type: 'string',
|
|
915
|
+
},
|
|
916
|
+
description:
|
|
917
|
+
"Tags to be added to the subject. If already exists, won't be duplicated.",
|
|
918
|
+
},
|
|
919
|
+
remove: {
|
|
920
|
+
type: 'array',
|
|
921
|
+
items: {
|
|
922
|
+
type: 'string',
|
|
923
|
+
},
|
|
924
|
+
description:
|
|
925
|
+
"Tags to be removed to the subject. Ignores a tag If it doesn't exist, won't be duplicated.",
|
|
926
|
+
},
|
|
927
|
+
comment: {
|
|
928
|
+
type: 'string',
|
|
929
|
+
description: 'Additional comment about added/removed tags.',
|
|
930
|
+
},
|
|
931
|
+
},
|
|
932
|
+
},
|
|
900
933
|
communicationTemplateView: {
|
|
901
934
|
type: 'object',
|
|
902
935
|
required: [
|
|
@@ -1075,6 +1108,7 @@ export const schemaDict = {
|
|
|
1075
1108
|
'lex:com.atproto.admin.defs#modEventReverseTakedown',
|
|
1076
1109
|
'lex:com.atproto.admin.defs#modEventUnmute',
|
|
1077
1110
|
'lex:com.atproto.admin.defs#modEventEmail',
|
|
1111
|
+
'lex:com.atproto.admin.defs#modEventTag',
|
|
1078
1112
|
],
|
|
1079
1113
|
},
|
|
1080
1114
|
subject: {
|
|
@@ -1503,6 +1537,22 @@ export const schemaDict = {
|
|
|
1503
1537
|
description:
|
|
1504
1538
|
'If specified, only events where all of these labels were removed are returned',
|
|
1505
1539
|
},
|
|
1540
|
+
addedTags: {
|
|
1541
|
+
type: 'array',
|
|
1542
|
+
items: {
|
|
1543
|
+
type: 'string',
|
|
1544
|
+
},
|
|
1545
|
+
description:
|
|
1546
|
+
'If specified, only events where all of these tags were added are returned',
|
|
1547
|
+
},
|
|
1548
|
+
removedTags: {
|
|
1549
|
+
type: 'array',
|
|
1550
|
+
items: {
|
|
1551
|
+
type: 'string',
|
|
1552
|
+
},
|
|
1553
|
+
description:
|
|
1554
|
+
'If specified, only events where all of these tags were removed are returned',
|
|
1555
|
+
},
|
|
1506
1556
|
reportTypes: {
|
|
1507
1557
|
type: 'array',
|
|
1508
1558
|
items: {
|
|
@@ -1620,6 +1670,18 @@ export const schemaDict = {
|
|
|
1620
1670
|
maximum: 100,
|
|
1621
1671
|
default: 50,
|
|
1622
1672
|
},
|
|
1673
|
+
tags: {
|
|
1674
|
+
type: 'array',
|
|
1675
|
+
items: {
|
|
1676
|
+
type: 'string',
|
|
1677
|
+
},
|
|
1678
|
+
},
|
|
1679
|
+
excludeTags: {
|
|
1680
|
+
type: 'array',
|
|
1681
|
+
items: {
|
|
1682
|
+
type: 'string',
|
|
1683
|
+
},
|
|
1684
|
+
},
|
|
1623
1685
|
cursor: {
|
|
1624
1686
|
type: 'string',
|
|
1625
1687
|
},
|
|
@@ -156,6 +156,7 @@ export interface SubjectStatusView {
|
|
|
156
156
|
/** True indicates that the a previously taken moderator action was appealed against, by the author of the content. False indicates last appeal was resolved by moderators. */
|
|
157
157
|
appealed?: boolean
|
|
158
158
|
suspendUntil?: string
|
|
159
|
+
tags?: string[]
|
|
159
160
|
[k: string]: unknown
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -720,6 +721,29 @@ export function validateModEventEmail(v: unknown): ValidationResult {
|
|
|
720
721
|
return lexicons.validate('com.atproto.admin.defs#modEventEmail', v)
|
|
721
722
|
}
|
|
722
723
|
|
|
724
|
+
/** Add/Remove a tag on a subject */
|
|
725
|
+
export interface ModEventTag {
|
|
726
|
+
/** Tags to be added to the subject. If already exists, won't be duplicated. */
|
|
727
|
+
add: string[]
|
|
728
|
+
/** Tags to be removed to the subject. Ignores a tag If it doesn't exist, won't be duplicated. */
|
|
729
|
+
remove: string[]
|
|
730
|
+
/** Additional comment about added/removed tags. */
|
|
731
|
+
comment?: string
|
|
732
|
+
[k: string]: unknown
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export function isModEventTag(v: unknown): v is ModEventTag {
|
|
736
|
+
return (
|
|
737
|
+
isObj(v) &&
|
|
738
|
+
hasProp(v, '$type') &&
|
|
739
|
+
v.$type === 'com.atproto.admin.defs#modEventTag'
|
|
740
|
+
)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
export function validateModEventTag(v: unknown): ValidationResult {
|
|
744
|
+
return lexicons.validate('com.atproto.admin.defs#modEventTag', v)
|
|
745
|
+
}
|
|
746
|
+
|
|
723
747
|
export interface CommunicationTemplateView {
|
|
724
748
|
id: string
|
|
725
749
|
/** Name of the template. */
|
|
@@ -24,6 +24,7 @@ export interface InputSchema {
|
|
|
24
24
|
| ComAtprotoAdminDefs.ModEventReverseTakedown
|
|
25
25
|
| ComAtprotoAdminDefs.ModEventUnmute
|
|
26
26
|
| ComAtprotoAdminDefs.ModEventEmail
|
|
27
|
+
| ComAtprotoAdminDefs.ModEventTag
|
|
27
28
|
| { $type: string; [k: string]: unknown }
|
|
28
29
|
subject:
|
|
29
30
|
| ComAtprotoAdminDefs.RepoRef
|