@atproto/ozone 0.1.140 → 0.1.141
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 +15 -0
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +3 -0
- package/dist/api/moderation/emitEvent.js.map +1 -1
- package/dist/api/report/createReport.d.ts.map +1 -1
- package/dist/api/report/createReport.js +7 -5
- package/dist/api/report/createReport.js.map +1 -1
- package/dist/api/util.d.ts +2 -3
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/util.js +7 -17
- package/dist/api/util.js.map +1 -1
- package/dist/config/config.d.ts +1 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +1 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +1 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +1 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +6 -0
- package/dist/context.js.map +1 -1
- package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.d.ts +5 -0
- package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.d.ts.map +1 -0
- package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.js +228 -0
- package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +2 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +49 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +52 -1
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +470 -16
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +329 -7
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/com/atproto/moderation/defs.d.ts +8 -8
- package/dist/lexicon/types/com/atproto/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/moderation/defs.js +7 -7
- package/dist/lexicon/types/com/atproto/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/com/atproto/temp/dereferenceScope.d.ts +24 -0
- package/dist/lexicon/types/com/atproto/temp/dereferenceScope.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/temp/dereferenceScope.js +7 -0
- package/dist/lexicon/types/com/atproto/temp/dereferenceScope.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts +92 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/defs.js +98 -0
- package/dist/lexicon/types/tools/ozone/report/defs.js.map +1 -0
- package/dist/mod-service/profile.d.ts +15 -0
- package/dist/mod-service/profile.d.ts.map +1 -0
- package/dist/mod-service/profile.js +135 -0
- package/dist/mod-service/profile.js.map +1 -0
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/mod-service/status.js +18 -17
- package/dist/mod-service/status.js.map +1 -1
- package/dist/tag-service/util.d.ts.map +1 -1
- package/dist/tag-service/util.js +7 -1
- package/dist/tag-service/util.js.map +1 -1
- package/package.json +9 -9
- package/src/api/moderation/emitEvent.ts +4 -0
- package/src/api/report/createReport.ts +9 -9
- package/src/api/util.ts +7 -28
- package/src/config/config.ts +3 -1
- package/src/config/env.ts +2 -0
- package/src/context.ts +14 -0
- package/src/db/migrations/20250718T150931000Z-update-appeal-reason-stats.ts +311 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/lexicon/index.ts +82 -0
- package/src/lexicon/lexicons.ts +341 -7
- package/src/lexicon/types/com/atproto/moderation/defs.ts +52 -7
- package/src/lexicon/types/com/atproto/temp/dereferenceScope.ts +42 -0
- package/src/lexicon/types/tools/ozone/report/defs.ts +154 -0
- package/src/mod-service/profile.ts +143 -0
- package/src/mod-service/status.ts +3 -2
- package/src/tag-service/util.ts +9 -1
- package/tests/__snapshots__/report-reason.test.ts.snap +14 -0
- package/tests/report-reason.test.ts +154 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import { type ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
5
|
+
import { CID } from 'multiformats/cid'
|
|
6
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
7
|
+
import {
|
|
8
|
+
type $Typed,
|
|
9
|
+
is$typed as _is$typed,
|
|
10
|
+
type OmitKey,
|
|
11
|
+
} from '../../../../util'
|
|
12
|
+
|
|
13
|
+
const is$typed = _is$typed,
|
|
14
|
+
validate = _validate
|
|
15
|
+
const id = 'tools.ozone.report.defs'
|
|
16
|
+
|
|
17
|
+
export type ReasonType =
|
|
18
|
+
| 'tools.ozone.report.defs#reasonAppeal'
|
|
19
|
+
| 'tools.ozone.report.defs#reasonViolenceAnimalWelfare'
|
|
20
|
+
| 'tools.ozone.report.defs#reasonViolenceThreats'
|
|
21
|
+
| 'tools.ozone.report.defs#reasonViolenceGraphicContent'
|
|
22
|
+
| 'tools.ozone.report.defs#reasonViolenceSelfHarm'
|
|
23
|
+
| 'tools.ozone.report.defs#reasonViolenceGlorification'
|
|
24
|
+
| 'tools.ozone.report.defs#reasonViolenceExtremistContent'
|
|
25
|
+
| 'tools.ozone.report.defs#reasonViolenceTrafficking'
|
|
26
|
+
| 'tools.ozone.report.defs#reasonViolenceOther'
|
|
27
|
+
| 'tools.ozone.report.defs#reasonSexualAbuseContent'
|
|
28
|
+
| 'tools.ozone.report.defs#reasonSexualNCII'
|
|
29
|
+
| 'tools.ozone.report.defs#reasonSexualSextortion'
|
|
30
|
+
| 'tools.ozone.report.defs#reasonSexualDeepfake'
|
|
31
|
+
| 'tools.ozone.report.defs#reasonSexualAnimal'
|
|
32
|
+
| 'tools.ozone.report.defs#reasonSexualUnlabeled'
|
|
33
|
+
| 'tools.ozone.report.defs#reasonSexualOther'
|
|
34
|
+
| 'tools.ozone.report.defs#reasonChildSafetyCSAM'
|
|
35
|
+
| 'tools.ozone.report.defs#reasonChildSafetyGroom'
|
|
36
|
+
| 'tools.ozone.report.defs#reasonChildSafetyMinorPrivacy'
|
|
37
|
+
| 'tools.ozone.report.defs#reasonChildSafetyEndangerment'
|
|
38
|
+
| 'tools.ozone.report.defs#reasonChildSafetyHarassment'
|
|
39
|
+
| 'tools.ozone.report.defs#reasonChildSafetyPromotion'
|
|
40
|
+
| 'tools.ozone.report.defs#reasonChildSafetyOther'
|
|
41
|
+
| 'tools.ozone.report.defs#reasonHarassmentTroll'
|
|
42
|
+
| 'tools.ozone.report.defs#reasonHarassmentTargeted'
|
|
43
|
+
| 'tools.ozone.report.defs#reasonHarassmentHateSpeech'
|
|
44
|
+
| 'tools.ozone.report.defs#reasonHarassmentDoxxing'
|
|
45
|
+
| 'tools.ozone.report.defs#reasonHarassmentOther'
|
|
46
|
+
| 'tools.ozone.report.defs#reasonMisleadingBot'
|
|
47
|
+
| 'tools.ozone.report.defs#reasonMisleadingImpersonation'
|
|
48
|
+
| 'tools.ozone.report.defs#reasonMisleadingSpam'
|
|
49
|
+
| 'tools.ozone.report.defs#reasonMisleadingScam'
|
|
50
|
+
| 'tools.ozone.report.defs#reasonMisleadingSyntheticContent'
|
|
51
|
+
| 'tools.ozone.report.defs#reasonMisleadingMisinformation'
|
|
52
|
+
| 'tools.ozone.report.defs#reasonMisleadingOther'
|
|
53
|
+
| 'tools.ozone.report.defs#reasonRuleSiteSecurity'
|
|
54
|
+
| 'tools.ozone.report.defs#reasonRuleStolenContent'
|
|
55
|
+
| 'tools.ozone.report.defs#reasonRuleProhibitedSales'
|
|
56
|
+
| 'tools.ozone.report.defs#reasonRuleBanEvasion'
|
|
57
|
+
| 'tools.ozone.report.defs#reasonRuleOther'
|
|
58
|
+
| 'tools.ozone.report.defs#reasonCivicElectoralProcess'
|
|
59
|
+
| 'tools.ozone.report.defs#reasonCivicDisclosure'
|
|
60
|
+
| 'tools.ozone.report.defs#reasonCivicInterference'
|
|
61
|
+
| 'tools.ozone.report.defs#reasonCivicMisinformation'
|
|
62
|
+
| 'tools.ozone.report.defs#reasonCivicImpersonation'
|
|
63
|
+
| (string & {})
|
|
64
|
+
|
|
65
|
+
/** Appeal a previously taken moderation action */
|
|
66
|
+
export const REASONAPPEAL = `${id}#reasonAppeal`
|
|
67
|
+
/** Animal welfare violations */
|
|
68
|
+
export const REASONVIOLENCEANIMALWELFARE = `${id}#reasonViolenceAnimalWelfare`
|
|
69
|
+
/** Threats or incitement */
|
|
70
|
+
export const REASONVIOLENCETHREATS = `${id}#reasonViolenceThreats`
|
|
71
|
+
/** Graphic violent content */
|
|
72
|
+
export const REASONVIOLENCEGRAPHICCONTENT = `${id}#reasonViolenceGraphicContent`
|
|
73
|
+
/** Self harm */
|
|
74
|
+
export const REASONVIOLENCESELFHARM = `${id}#reasonViolenceSelfHarm`
|
|
75
|
+
/** Glorification of violence */
|
|
76
|
+
export const REASONVIOLENCEGLORIFICATION = `${id}#reasonViolenceGlorification`
|
|
77
|
+
/** Extremist content. These reports will be sent only be sent to the application's Moderation Authority. */
|
|
78
|
+
export const REASONVIOLENCEEXTREMISTCONTENT = `${id}#reasonViolenceExtremistContent`
|
|
79
|
+
/** Human trafficking */
|
|
80
|
+
export const REASONVIOLENCETRAFFICKING = `${id}#reasonViolenceTrafficking`
|
|
81
|
+
/** Other violent content */
|
|
82
|
+
export const REASONVIOLENCEOTHER = `${id}#reasonViolenceOther`
|
|
83
|
+
/** Adult sexual abuse content */
|
|
84
|
+
export const REASONSEXUALABUSECONTENT = `${id}#reasonSexualAbuseContent`
|
|
85
|
+
/** Non-consensual intimate imagery */
|
|
86
|
+
export const REASONSEXUALNCII = `${id}#reasonSexualNCII`
|
|
87
|
+
/** Sextortion */
|
|
88
|
+
export const REASONSEXUALSEXTORTION = `${id}#reasonSexualSextortion`
|
|
89
|
+
/** Deepfake adult content */
|
|
90
|
+
export const REASONSEXUALDEEPFAKE = `${id}#reasonSexualDeepfake`
|
|
91
|
+
/** Animal sexual abuse */
|
|
92
|
+
export const REASONSEXUALANIMAL = `${id}#reasonSexualAnimal`
|
|
93
|
+
/** Unlabelled adult content */
|
|
94
|
+
export const REASONSEXUALUNLABELED = `${id}#reasonSexualUnlabeled`
|
|
95
|
+
/** Other sexual violence content */
|
|
96
|
+
export const REASONSEXUALOTHER = `${id}#reasonSexualOther`
|
|
97
|
+
/** Child sexual abuse material (CSAM). These reports will be sent only be sent to the application's Moderation Authority. */
|
|
98
|
+
export const REASONCHILDSAFETYCSAM = `${id}#reasonChildSafetyCSAM`
|
|
99
|
+
/** Grooming or predatory behavior. These reports will be sent only be sent to the application's Moderation Authority. */
|
|
100
|
+
export const REASONCHILDSAFETYGROOM = `${id}#reasonChildSafetyGroom`
|
|
101
|
+
/** Privacy violation involving a minor */
|
|
102
|
+
export const REASONCHILDSAFETYMINORPRIVACY = `${id}#reasonChildSafetyMinorPrivacy`
|
|
103
|
+
/** Child endangerment. These reports will be sent only be sent to the application's Moderation Authority. */
|
|
104
|
+
export const REASONCHILDSAFETYENDANGERMENT = `${id}#reasonChildSafetyEndangerment`
|
|
105
|
+
/** Harassment or bullying of minors */
|
|
106
|
+
export const REASONCHILDSAFETYHARASSMENT = `${id}#reasonChildSafetyHarassment`
|
|
107
|
+
/** Promotion of child exploitation. These reports will be sent only be sent to the application's Moderation Authority. */
|
|
108
|
+
export const REASONCHILDSAFETYPROMOTION = `${id}#reasonChildSafetyPromotion`
|
|
109
|
+
/** Other child safety. These reports will be sent only be sent to the application's Moderation Authority. */
|
|
110
|
+
export const REASONCHILDSAFETYOTHER = `${id}#reasonChildSafetyOther`
|
|
111
|
+
/** Trolling */
|
|
112
|
+
export const REASONHARASSMENTTROLL = `${id}#reasonHarassmentTroll`
|
|
113
|
+
/** Targeted harassment */
|
|
114
|
+
export const REASONHARASSMENTTARGETED = `${id}#reasonHarassmentTargeted`
|
|
115
|
+
/** Hate speech */
|
|
116
|
+
export const REASONHARASSMENTHATESPEECH = `${id}#reasonHarassmentHateSpeech`
|
|
117
|
+
/** Doxxing */
|
|
118
|
+
export const REASONHARASSMENTDOXXING = `${id}#reasonHarassmentDoxxing`
|
|
119
|
+
/** Other harassing or hateful content */
|
|
120
|
+
export const REASONHARASSMENTOTHER = `${id}#reasonHarassmentOther`
|
|
121
|
+
/** Fake account or bot */
|
|
122
|
+
export const REASONMISLEADINGBOT = `${id}#reasonMisleadingBot`
|
|
123
|
+
/** Impersonation */
|
|
124
|
+
export const REASONMISLEADINGIMPERSONATION = `${id}#reasonMisleadingImpersonation`
|
|
125
|
+
/** Spam */
|
|
126
|
+
export const REASONMISLEADINGSPAM = `${id}#reasonMisleadingSpam`
|
|
127
|
+
/** Scam */
|
|
128
|
+
export const REASONMISLEADINGSCAM = `${id}#reasonMisleadingScam`
|
|
129
|
+
/** Unlabelled gen-AI or synthetic content */
|
|
130
|
+
export const REASONMISLEADINGSYNTHETICCONTENT = `${id}#reasonMisleadingSyntheticContent`
|
|
131
|
+
/** Harmful false claims */
|
|
132
|
+
export const REASONMISLEADINGMISINFORMATION = `${id}#reasonMisleadingMisinformation`
|
|
133
|
+
/** Other misleading content */
|
|
134
|
+
export const REASONMISLEADINGOTHER = `${id}#reasonMisleadingOther`
|
|
135
|
+
/** Hacking or system attacks */
|
|
136
|
+
export const REASONRULESITESECURITY = `${id}#reasonRuleSiteSecurity`
|
|
137
|
+
/** Stolen content */
|
|
138
|
+
export const REASONRULESTOLENCONTENT = `${id}#reasonRuleStolenContent`
|
|
139
|
+
/** Promoting or selling prohibited items or services */
|
|
140
|
+
export const REASONRULEPROHIBITEDSALES = `${id}#reasonRuleProhibitedSales`
|
|
141
|
+
/** Banned user returning */
|
|
142
|
+
export const REASONRULEBANEVASION = `${id}#reasonRuleBanEvasion`
|
|
143
|
+
/** Other */
|
|
144
|
+
export const REASONRULEOTHER = `${id}#reasonRuleOther`
|
|
145
|
+
/** Electoral process violations */
|
|
146
|
+
export const REASONCIVICELECTORALPROCESS = `${id}#reasonCivicElectoralProcess`
|
|
147
|
+
/** Disclosure & transparency violations */
|
|
148
|
+
export const REASONCIVICDISCLOSURE = `${id}#reasonCivicDisclosure`
|
|
149
|
+
/** Voter intimidation or interference */
|
|
150
|
+
export const REASONCIVICINTERFERENCE = `${id}#reasonCivicInterference`
|
|
151
|
+
/** Election misinformation */
|
|
152
|
+
export const REASONCIVICMISINFORMATION = `${id}#reasonCivicMisinformation`
|
|
153
|
+
/** Impersonation of electoral officials/entities */
|
|
154
|
+
export const REASONCIVICIMPERSONATION = `${id}#reasonCivicImpersonation`
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import AtpAgent, { AppBskyLabelerDefs } from '@atproto/api'
|
|
2
|
+
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
3
|
+
import { OzoneConfig } from '../config'
|
|
4
|
+
import {
|
|
5
|
+
REASONAPPEAL,
|
|
6
|
+
REASONMISLEADING,
|
|
7
|
+
REASONRUDE,
|
|
8
|
+
REASONSEXUAL,
|
|
9
|
+
REASONSPAM,
|
|
10
|
+
REASONVIOLATION,
|
|
11
|
+
} from '../lexicon/types/com/atproto/moderation/defs'
|
|
12
|
+
import { httpLogger } from '../logger'
|
|
13
|
+
|
|
14
|
+
// Reverse mapping from new ozone namespaced reason types to old com.atproto namespaced reason types
|
|
15
|
+
export const NEW_TO_OLD_REASON_MAPPING: Record<string, string> = {
|
|
16
|
+
'tools.ozone.report.defs#reasonMisleadingSpam': REASONSPAM,
|
|
17
|
+
'tools.ozone.report.defs#reasonRuleOther': REASONVIOLATION,
|
|
18
|
+
'tools.ozone.report.defs#reasonMisleadingOther': REASONMISLEADING,
|
|
19
|
+
'tools.ozone.report.defs#reasonSexualUnlabeled': REASONSEXUAL,
|
|
20
|
+
'tools.ozone.report.defs#reasonHarassmentOther': REASONRUDE,
|
|
21
|
+
'tools.ozone.report.defs#reasonAppeal': REASONAPPEAL,
|
|
22
|
+
// Map all violence-related reasons to REASONVIOLATION
|
|
23
|
+
'tools.ozone.report.defs#reasonViolenceAnimalWelfare': REASONVIOLATION,
|
|
24
|
+
'tools.ozone.report.defs#reasonViolenceThreats': REASONVIOLATION,
|
|
25
|
+
'tools.ozone.report.defs#reasonViolenceGraphicContent': REASONVIOLATION,
|
|
26
|
+
'tools.ozone.report.defs#reasonViolenceSelfHarm': REASONVIOLATION,
|
|
27
|
+
'tools.ozone.report.defs#reasonViolenceGlorification': REASONVIOLATION,
|
|
28
|
+
'tools.ozone.report.defs#reasonViolenceExtremistContent': REASONVIOLATION,
|
|
29
|
+
'tools.ozone.report.defs#reasonViolenceTrafficking': REASONVIOLATION,
|
|
30
|
+
'tools.ozone.report.defs#reasonViolenceOther': REASONVIOLATION,
|
|
31
|
+
// Map all sexual-related reasons to REASONSEXUAL
|
|
32
|
+
'tools.ozone.report.defs#reasonSexualAbuseContent': REASONSEXUAL,
|
|
33
|
+
'tools.ozone.report.defs#reasonSexualNCII': REASONSEXUAL,
|
|
34
|
+
'tools.ozone.report.defs#reasonSexualSextortion': REASONSEXUAL,
|
|
35
|
+
'tools.ozone.report.defs#reasonSexualDeepfake': REASONSEXUAL,
|
|
36
|
+
'tools.ozone.report.defs#reasonSexualAnimal': REASONSEXUAL,
|
|
37
|
+
'tools.ozone.report.defs#reasonSexualOther': REASONSEXUAL,
|
|
38
|
+
// Map all child safety reasons to REASONVIOLATION
|
|
39
|
+
'tools.ozone.report.defs#reasonChildSafetyCSAM': REASONVIOLATION,
|
|
40
|
+
'tools.ozone.report.defs#reasonChildSafetyGroom': REASONVIOLATION,
|
|
41
|
+
'tools.ozone.report.defs#reasonChildSafetyMinorPrivacy': REASONVIOLATION,
|
|
42
|
+
'tools.ozone.report.defs#reasonChildSafetyEndangerment': REASONVIOLATION,
|
|
43
|
+
'tools.ozone.report.defs#reasonChildSafetyHarassment': REASONVIOLATION,
|
|
44
|
+
'tools.ozone.report.defs#reasonChildSafetyPromotion': REASONVIOLATION,
|
|
45
|
+
'tools.ozone.report.defs#reasonChildSafetyOther': REASONVIOLATION,
|
|
46
|
+
// Map all harassment reasons to REASONRUDE
|
|
47
|
+
'tools.ozone.report.defs#reasonHarassmentTroll': REASONRUDE,
|
|
48
|
+
'tools.ozone.report.defs#reasonHarassmentTargeted': REASONRUDE,
|
|
49
|
+
'tools.ozone.report.defs#reasonHarassmentHateSpeech': REASONRUDE,
|
|
50
|
+
'tools.ozone.report.defs#reasonHarassmentDoxxing': REASONRUDE,
|
|
51
|
+
// Map all misleading reasons to REASONMISLEADING
|
|
52
|
+
'tools.ozone.report.defs#reasonMisleadingBot': REASONMISLEADING,
|
|
53
|
+
'tools.ozone.report.defs#reasonMisleadingImpersonation': REASONMISLEADING,
|
|
54
|
+
'tools.ozone.report.defs#reasonMisleadingScam': REASONMISLEADING,
|
|
55
|
+
'tools.ozone.report.defs#reasonMisleadingSyntheticContent': REASONMISLEADING,
|
|
56
|
+
'tools.ozone.report.defs#reasonMisleadingMisinformation': REASONMISLEADING,
|
|
57
|
+
// Map all rule-related reasons to REASONVIOLATION
|
|
58
|
+
'tools.ozone.report.defs#reasonRuleSiteSecurity': REASONVIOLATION,
|
|
59
|
+
'tools.ozone.report.defs#reasonRuleStolenContent': REASONVIOLATION,
|
|
60
|
+
'tools.ozone.report.defs#reasonRuleProhibitedSales': REASONVIOLATION,
|
|
61
|
+
'tools.ozone.report.defs#reasonRuleBanEvasion': REASONVIOLATION,
|
|
62
|
+
// Map all civic reasons to REASONMISLEADING
|
|
63
|
+
'tools.ozone.report.defs#reasonCivicElectoralProcess': REASONMISLEADING,
|
|
64
|
+
'tools.ozone.report.defs#reasonCivicDisclosure': REASONMISLEADING,
|
|
65
|
+
'tools.ozone.report.defs#reasonCivicInterference': REASONMISLEADING,
|
|
66
|
+
'tools.ozone.report.defs#reasonCivicMisinformation': REASONMISLEADING,
|
|
67
|
+
'tools.ozone.report.defs#reasonCivicImpersonation': REASONMISLEADING,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface CacheEntry {
|
|
71
|
+
profile: AppBskyLabelerDefs.LabelerViewDetailed | null
|
|
72
|
+
timestamp: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type ModerationServiceProfileCreator = () => ModerationServiceProfile
|
|
76
|
+
|
|
77
|
+
export class ModerationServiceProfile {
|
|
78
|
+
private cache: CacheEntry | null = null
|
|
79
|
+
private CACHE_TTL: number
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
private cfg: OzoneConfig,
|
|
83
|
+
private appviewAgent: AtpAgent,
|
|
84
|
+
cacheTTL?: number,
|
|
85
|
+
) {
|
|
86
|
+
this.CACHE_TTL = cacheTTL || cfg.service.serviceRecordCacheTTL
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static creator(
|
|
90
|
+
cfg: OzoneConfig,
|
|
91
|
+
appviewAgent: AtpAgent,
|
|
92
|
+
): ModerationServiceProfileCreator {
|
|
93
|
+
return () => new ModerationServiceProfile(cfg, appviewAgent)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getProfile() {
|
|
97
|
+
const now = Date.now()
|
|
98
|
+
|
|
99
|
+
if (!this.cache || now - this.cache.timestamp > this.CACHE_TTL) {
|
|
100
|
+
try {
|
|
101
|
+
const { data } = await this.appviewAgent.app.bsky.labeler.getServices({
|
|
102
|
+
dids: [this.cfg.service.did],
|
|
103
|
+
detailed: true,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if (AppBskyLabelerDefs.isLabelerViewDetailed(data.views?.[0])) {
|
|
107
|
+
this.cache = {
|
|
108
|
+
profile: data.views[0],
|
|
109
|
+
timestamp: now,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// On error, fail open
|
|
114
|
+
httpLogger.error(`Failed to fetch labeler profile: ${e?.['message']}`)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return this.cache?.profile || null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async validateReasonType(reasonType: string): Promise<string> {
|
|
122
|
+
const profile = await this.getProfile()
|
|
123
|
+
|
|
124
|
+
if (!Array.isArray(profile?.reasonTypes)) {
|
|
125
|
+
return reasonType
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const supportedReasonTypes = profile.reasonTypes
|
|
129
|
+
|
|
130
|
+
// Check if the reason type is directly supported
|
|
131
|
+
if (supportedReasonTypes.includes(reasonType)) {
|
|
132
|
+
return reasonType
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Allow new reason types only if they map to a supported old reason type
|
|
136
|
+
const mappedOldReason = NEW_TO_OLD_REASON_MAPPING[reasonType]
|
|
137
|
+
if (mappedOldReason && supportedReasonTypes.includes(mappedOldReason)) {
|
|
138
|
+
return reasonType
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
throw new InvalidRequestError(`Invalid reason type: ${reasonType}`)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { HOUR } from '@atproto/common'
|
|
4
4
|
import { AtUri } from '@atproto/syntax'
|
|
5
|
+
import { isAppealReport } from '../api/util'
|
|
5
6
|
import { Database } from '../db'
|
|
6
7
|
import { DatabaseSchema } from '../db/schema'
|
|
7
8
|
import { jsonb } from '../db/types'
|
|
8
|
-
import { REASONAPPEAL } from '../lexicon/types/com/atproto/moderation/defs'
|
|
9
9
|
import {
|
|
10
10
|
REVIEWCLOSED,
|
|
11
11
|
REVIEWESCALATED,
|
|
@@ -341,7 +341,8 @@ export const adjustModerationSubjectStatus = async (
|
|
|
341
341
|
|
|
342
342
|
const isAppealEvent =
|
|
343
343
|
action === 'tools.ozone.moderation.defs#modEventReport' &&
|
|
344
|
-
meta?.reportType
|
|
344
|
+
meta?.reportType &&
|
|
345
|
+
isAppealReport(`${meta.reportType}`)
|
|
345
346
|
|
|
346
347
|
const subjectStatus = getSubjectStatusForModerationEvent({
|
|
347
348
|
currentStatus,
|
package/src/tag-service/util.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { ReasonType } from '../lexicon/types/com/atproto/moderation/defs'
|
|
2
2
|
|
|
3
3
|
export const getTagForReport = (reasonType: ReasonType) => {
|
|
4
|
-
|
|
4
|
+
const reasonWithoutPrefix = reasonType
|
|
5
|
+
.replace('com.atproto.moderation.defs#reason', '')
|
|
6
|
+
.replace('tools.ozone.report.defs#reason', '')
|
|
7
|
+
|
|
8
|
+
const kebabCase = reasonWithoutPrefix
|
|
9
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
|
|
12
|
+
return `report:${kebabCase}`
|
|
5
13
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`report reason createReport only passes for allowed reason types 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
6
|
+
"id": 1,
|
|
7
|
+
"reasonType": "tools.ozone.report.defs#reasonHarassmentTroll",
|
|
8
|
+
"reportedBy": "user(0)",
|
|
9
|
+
"subject": Object {
|
|
10
|
+
"$type": "com.atproto.admin.defs#repoRef",
|
|
11
|
+
"did": "user(1)",
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import AtpAgent from '@atproto/api'
|
|
2
|
+
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
3
|
+
import {
|
|
4
|
+
REASONRUDE,
|
|
5
|
+
REASONSPAM,
|
|
6
|
+
} from '../src/lexicon/types/com/atproto/moderation/defs'
|
|
7
|
+
import { ModerationServiceProfile } from '../src/mod-service/profile'
|
|
8
|
+
import { forSnapshot } from './_util'
|
|
9
|
+
|
|
10
|
+
describe('report reason', () => {
|
|
11
|
+
let network: TestNetwork
|
|
12
|
+
let sc: SeedClient
|
|
13
|
+
let pdsAgent: AtpAgent
|
|
14
|
+
|
|
15
|
+
const repoSubject = (did: string) => ({
|
|
16
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
17
|
+
did,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
network = await TestNetwork.create({
|
|
22
|
+
dbPostgresSchema: 'ozone_report',
|
|
23
|
+
})
|
|
24
|
+
sc = network.getSeedClient()
|
|
25
|
+
await basicSeed(sc)
|
|
26
|
+
|
|
27
|
+
// Login with ozone's service account owner and update the service profile definition
|
|
28
|
+
pdsAgent = network.pds.getClient()
|
|
29
|
+
await pdsAgent.login({
|
|
30
|
+
identifier: 'mod-authority.test',
|
|
31
|
+
password: 'hunter2',
|
|
32
|
+
})
|
|
33
|
+
await pdsAgent.com.atproto.repo.putRecord({
|
|
34
|
+
repo: network.ozone.ctx.cfg.service.did,
|
|
35
|
+
collection: 'app.bsky.labeler.service',
|
|
36
|
+
rkey: 'self',
|
|
37
|
+
record: {
|
|
38
|
+
policies: { labelValues: [] },
|
|
39
|
+
reasonTypes: ['tools.ozone.report.defs#reasonHarassmentTroll'],
|
|
40
|
+
createdAt: new Date().toISOString(),
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
await network.processAll()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
afterAll(async () => {
|
|
48
|
+
await network.close()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('createReport', () => {
|
|
52
|
+
it('only passes for allowed reason types', async () => {
|
|
53
|
+
await expect(
|
|
54
|
+
sc.createReport({
|
|
55
|
+
reasonType: 'tools.ozone.report.defs#reasonHarassmentFake',
|
|
56
|
+
subject: repoSubject(sc.dids.bob),
|
|
57
|
+
reportedBy: sc.dids.alice,
|
|
58
|
+
}),
|
|
59
|
+
).rejects.toThrow('Invalid reason type')
|
|
60
|
+
|
|
61
|
+
const validReport = await sc.createReport({
|
|
62
|
+
reasonType: 'tools.ozone.report.defs#reasonHarassmentTroll',
|
|
63
|
+
subject: repoSubject(sc.dids.bob),
|
|
64
|
+
reportedBy: sc.dids.alice,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
expect(forSnapshot(validReport)).toMatchSnapshot()
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
describe('ModerationServiceProfile', () => {
|
|
71
|
+
it('should validate against updated labeler profile when cache expires', async () => {
|
|
72
|
+
const moderationServiceProfile = new ModerationServiceProfile(
|
|
73
|
+
network.ozone.ctx.cfg,
|
|
74
|
+
network.ozone.ctx.appviewAgent,
|
|
75
|
+
500,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
await expect(
|
|
79
|
+
moderationServiceProfile.validateReasonType(
|
|
80
|
+
'tools.ozone.report.defs#reasonHarassmentFake',
|
|
81
|
+
),
|
|
82
|
+
).rejects.toThrow('Invalid reason type')
|
|
83
|
+
|
|
84
|
+
// Update labeler profile to add the new reason type
|
|
85
|
+
await pdsAgent.com.atproto.repo.putRecord({
|
|
86
|
+
repo: network.ozone.ctx.cfg.service.did,
|
|
87
|
+
collection: 'app.bsky.labeler.service',
|
|
88
|
+
rkey: 'self',
|
|
89
|
+
record: {
|
|
90
|
+
policies: { labelValues: [] },
|
|
91
|
+
reasonTypes: ['tools.ozone.report.defs#reasonHarassmentFake'],
|
|
92
|
+
createdAt: new Date().toISOString(),
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
await network.processAll()
|
|
96
|
+
|
|
97
|
+
// immediately after the update, the reason type still fails due to cache
|
|
98
|
+
await expect(
|
|
99
|
+
moderationServiceProfile.validateReasonType(
|
|
100
|
+
'tools.ozone.report.defs#reasonHarassmentFake',
|
|
101
|
+
),
|
|
102
|
+
).rejects.toThrow('Invalid reason type')
|
|
103
|
+
|
|
104
|
+
// add some manual delay to ensure cache is expired and try again
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
106
|
+
await expect(
|
|
107
|
+
moderationServiceProfile.validateReasonType(
|
|
108
|
+
'tools.ozone.report.defs#reasonHarassmentFake',
|
|
109
|
+
),
|
|
110
|
+
).resolves.toEqual('tools.ozone.report.defs#reasonHarassmentFake')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should validate mapped reason types', async () => {
|
|
114
|
+
const moderationServiceProfile = new ModerationServiceProfile(
|
|
115
|
+
network.ozone.ctx.cfg,
|
|
116
|
+
network.ozone.ctx.appviewAgent,
|
|
117
|
+
500,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
// Set up labeler profile with old reason types only
|
|
121
|
+
await pdsAgent.com.atproto.repo.putRecord({
|
|
122
|
+
repo: network.ozone.ctx.cfg.service.did,
|
|
123
|
+
collection: 'app.bsky.labeler.service',
|
|
124
|
+
rkey: 'self',
|
|
125
|
+
record: {
|
|
126
|
+
policies: { labelValues: [] },
|
|
127
|
+
reasonTypes: [REASONSPAM, REASONRUDE],
|
|
128
|
+
createdAt: new Date().toISOString(),
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
await network.processAll()
|
|
132
|
+
|
|
133
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
134
|
+
|
|
135
|
+
await expect(
|
|
136
|
+
moderationServiceProfile.validateReasonType(
|
|
137
|
+
'tools.ozone.report.defs#reasonMisleadingSpam',
|
|
138
|
+
),
|
|
139
|
+
).resolves.toEqual('tools.ozone.report.defs#reasonMisleadingSpam')
|
|
140
|
+
|
|
141
|
+
// directly supported old reason types work
|
|
142
|
+
await expect(
|
|
143
|
+
moderationServiceProfile.validateReasonType(REASONSPAM),
|
|
144
|
+
).resolves.toEqual(REASONSPAM)
|
|
145
|
+
|
|
146
|
+
// new reason types that don't map to supported old reason types are rejected
|
|
147
|
+
await expect(
|
|
148
|
+
moderationServiceProfile.validateReasonType(
|
|
149
|
+
'tools.ozone.report.defs#reasonViolenceThreats',
|
|
150
|
+
),
|
|
151
|
+
).rejects.toThrow('Invalid reason type')
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
})
|