@atproto/ozone 0.1.155 → 0.1.157

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/api/moderation/emitEvent.d.ts.map +1 -1
  3. package/dist/api/moderation/emitEvent.js +20 -7
  4. package/dist/api/moderation/emitEvent.js.map +1 -1
  5. package/dist/api/util.d.ts +1 -1
  6. package/dist/api/util.d.ts.map +1 -1
  7. package/dist/api/util.js +6 -1
  8. package/dist/api/util.js.map +1 -1
  9. package/dist/daemon/event-pusher.d.ts +1 -1
  10. package/dist/daemon/event-pusher.d.ts.map +1 -1
  11. package/dist/daemon/event-pusher.js +17 -5
  12. package/dist/daemon/event-pusher.js.map +1 -1
  13. package/dist/daemon/scheduled-action-processor.d.ts +5 -1
  14. package/dist/daemon/scheduled-action-processor.d.ts.map +1 -1
  15. package/dist/daemon/scheduled-action-processor.js +44 -2
  16. package/dist/daemon/scheduled-action-processor.js.map +1 -1
  17. package/dist/jetstream/service.d.ts +1 -1
  18. package/dist/jetstream/service.d.ts.map +1 -1
  19. package/dist/jetstream/service.js +2 -2
  20. package/dist/jetstream/service.js.map +1 -1
  21. package/dist/lexicon/index.d.ts +11 -0
  22. package/dist/lexicon/index.d.ts.map +1 -1
  23. package/dist/lexicon/index.js +32 -1
  24. package/dist/lexicon/index.js.map +1 -1
  25. package/dist/lexicon/lexicons.d.ts +806 -6
  26. package/dist/lexicon/lexicons.d.ts.map +1 -1
  27. package/dist/lexicon/lexicons.js +425 -3
  28. package/dist/lexicon/lexicons.js.map +1 -1
  29. package/dist/lexicon/types/app/bsky/ageassurance/begin.d.ts +31 -0
  30. package/dist/lexicon/types/app/bsky/ageassurance/begin.d.ts.map +1 -0
  31. package/dist/lexicon/types/app/bsky/ageassurance/begin.js +7 -0
  32. package/dist/lexicon/types/app/bsky/ageassurance/begin.js.map +1 -0
  33. package/dist/lexicon/types/app/bsky/ageassurance/defs.d.ts +138 -0
  34. package/dist/lexicon/types/app/bsky/ageassurance/defs.d.ts.map +1 -0
  35. package/dist/lexicon/types/app/bsky/ageassurance/defs.js +115 -0
  36. package/dist/lexicon/types/app/bsky/ageassurance/defs.js.map +1 -0
  37. package/dist/lexicon/types/app/bsky/ageassurance/getConfig.d.ts +18 -0
  38. package/dist/lexicon/types/app/bsky/ageassurance/getConfig.d.ts.map +1 -0
  39. package/dist/lexicon/types/app/bsky/ageassurance/getConfig.js +7 -0
  40. package/dist/lexicon/types/app/bsky/ageassurance/getConfig.js.map +1 -0
  41. package/dist/lexicon/types/app/bsky/ageassurance/getState.d.ts +24 -0
  42. package/dist/lexicon/types/app/bsky/ageassurance/getState.d.ts.map +1 -0
  43. package/dist/lexicon/types/app/bsky/ageassurance/getState.js +7 -0
  44. package/dist/lexicon/types/app/bsky/ageassurance/getState.js.map +1 -0
  45. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +13 -2
  46. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  47. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  48. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +1 -1
  49. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  50. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.js.map +1 -1
  51. package/dist/lexicon/types/tools/ozone/moderation/scheduleAction.d.ts +10 -0
  52. package/dist/lexicon/types/tools/ozone/moderation/scheduleAction.d.ts.map +1 -1
  53. package/dist/lexicon/types/tools/ozone/moderation/scheduleAction.js.map +1 -1
  54. package/dist/mod-service/index.d.ts +3 -3
  55. package/dist/mod-service/index.d.ts.map +1 -1
  56. package/dist/mod-service/index.js +10 -4
  57. package/dist/mod-service/index.js.map +1 -1
  58. package/dist/mod-service/status.d.ts +1 -1
  59. package/dist/mod-service/util.d.ts.map +1 -1
  60. package/dist/mod-service/util.js +1 -0
  61. package/dist/mod-service/util.js.map +1 -1
  62. package/dist/mod-service/views.d.ts.map +1 -1
  63. package/dist/mod-service/views.js +7 -0
  64. package/dist/mod-service/views.js.map +1 -1
  65. package/dist/setting/validators.d.ts.map +1 -1
  66. package/dist/setting/validators.js +10 -0
  67. package/dist/setting/validators.js.map +1 -1
  68. package/package.json +10 -9
  69. package/src/api/moderation/emitEvent.ts +34 -9
  70. package/src/api/util.ts +7 -1
  71. package/src/daemon/event-pusher.ts +22 -5
  72. package/src/daemon/scheduled-action-processor.ts +59 -1
  73. package/src/jetstream/service.ts +1 -1
  74. package/src/lexicon/index.ts +49 -0
  75. package/src/lexicon/lexicons.ts +458 -3
  76. package/src/lexicon/types/app/bsky/ageassurance/begin.ts +54 -0
  77. package/src/lexicon/types/app/bsky/ageassurance/defs.ts +301 -0
  78. package/src/lexicon/types/app/bsky/ageassurance/getConfig.ts +34 -0
  79. package/src/lexicon/types/app/bsky/ageassurance/getState.ts +42 -0
  80. package/src/lexicon/types/tools/ozone/moderation/defs.ts +13 -2
  81. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +6 -1
  82. package/src/lexicon/types/tools/ozone/moderation/scheduleAction.ts +10 -0
  83. package/src/mod-service/index.ts +21 -7
  84. package/src/mod-service/util.ts +2 -0
  85. package/src/mod-service/views.ts +10 -0
  86. package/src/setting/validators.ts +15 -0
  87. package/tests/query-labels.test.ts +4 -1
  88. package/tests/scheduled-action-processor.test.ts +30 -6
  89. package/tests/takedown.test.ts +43 -1
  90. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,301 @@
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 = 'app.bsky.ageassurance.defs'
16
+
17
+ /** The access level granted based on Age Assurance data we've processed. */
18
+ export type Access = 'unknown' | 'none' | 'safe' | 'full' | (string & {})
19
+ /** The status of the Age Assurance process. */
20
+ export type Status =
21
+ | 'unknown'
22
+ | 'pending'
23
+ | 'assured'
24
+ | 'blocked'
25
+ | (string & {})
26
+
27
+ /** The user's computed Age Assurance state. */
28
+ export interface State {
29
+ $type?: 'app.bsky.ageassurance.defs#state'
30
+ /** The timestamp when this state was last updated. */
31
+ lastInitiatedAt?: string
32
+ status: Status
33
+ access: Access
34
+ }
35
+
36
+ const hashState = 'state'
37
+
38
+ export function isState<V>(v: V) {
39
+ return is$typed(v, id, hashState)
40
+ }
41
+
42
+ export function validateState<V>(v: V) {
43
+ return validate<State & V>(v, id, hashState)
44
+ }
45
+
46
+ /** Additional metadata needed to compute Age Assurance state client-side. */
47
+ export interface StateMetadata {
48
+ $type?: 'app.bsky.ageassurance.defs#stateMetadata'
49
+ /** The account creation timestamp. */
50
+ accountCreatedAt?: string
51
+ }
52
+
53
+ const hashStateMetadata = 'stateMetadata'
54
+
55
+ export function isStateMetadata<V>(v: V) {
56
+ return is$typed(v, id, hashStateMetadata)
57
+ }
58
+
59
+ export function validateStateMetadata<V>(v: V) {
60
+ return validate<StateMetadata & V>(v, id, hashStateMetadata)
61
+ }
62
+
63
+ export interface Config {
64
+ $type?: 'app.bsky.ageassurance.defs#config'
65
+ /** The per-region Age Assurance configuration. */
66
+ regions: ConfigRegion[]
67
+ }
68
+
69
+ const hashConfig = 'config'
70
+
71
+ export function isConfig<V>(v: V) {
72
+ return is$typed(v, id, hashConfig)
73
+ }
74
+
75
+ export function validateConfig<V>(v: V) {
76
+ return validate<Config & V>(v, id, hashConfig)
77
+ }
78
+
79
+ /** The Age Assurance configuration for a specific region. */
80
+ export interface ConfigRegion {
81
+ $type?: 'app.bsky.ageassurance.defs#configRegion'
82
+ /** The ISO 3166-1 alpha-2 country code this configuration applies to. */
83
+ countryCode: string
84
+ /** The ISO 3166-2 region code this configuration applies to. If omitted, the configuration applies to the entire country. */
85
+ regionCode?: string
86
+ /** The ordered list of Age Assurance rules that apply to this region. Rules should be applied in order, and the first matching rule determines the access level granted. The rules array should always include a default rule as the last item. */
87
+ rules: (
88
+ | $Typed<ConfigRegionRuleDefault>
89
+ | $Typed<ConfigRegionRuleIfDeclaredOverAge>
90
+ | $Typed<ConfigRegionRuleIfDeclaredUnderAge>
91
+ | $Typed<ConfigRegionRuleIfAssuredOverAge>
92
+ | $Typed<ConfigRegionRuleIfAssuredUnderAge>
93
+ | $Typed<ConfigRegionRuleIfAccountNewerThan>
94
+ | $Typed<ConfigRegionRuleIfAccountOlderThan>
95
+ | { $type: string }
96
+ )[]
97
+ }
98
+
99
+ const hashConfigRegion = 'configRegion'
100
+
101
+ export function isConfigRegion<V>(v: V) {
102
+ return is$typed(v, id, hashConfigRegion)
103
+ }
104
+
105
+ export function validateConfigRegion<V>(v: V) {
106
+ return validate<ConfigRegion & V>(v, id, hashConfigRegion)
107
+ }
108
+
109
+ /** Age Assurance rule that applies by default. */
110
+ export interface ConfigRegionRuleDefault {
111
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleDefault'
112
+ access: Access
113
+ }
114
+
115
+ const hashConfigRegionRuleDefault = 'configRegionRuleDefault'
116
+
117
+ export function isConfigRegionRuleDefault<V>(v: V) {
118
+ return is$typed(v, id, hashConfigRegionRuleDefault)
119
+ }
120
+
121
+ export function validateConfigRegionRuleDefault<V>(v: V) {
122
+ return validate<ConfigRegionRuleDefault & V>(
123
+ v,
124
+ id,
125
+ hashConfigRegionRuleDefault,
126
+ )
127
+ }
128
+
129
+ /** Age Assurance rule that applies if the user has declared themselves equal-to or over a certain age. */
130
+ export interface ConfigRegionRuleIfDeclaredOverAge {
131
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleIfDeclaredOverAge'
132
+ /** The age threshold as a whole integer. */
133
+ age: number
134
+ access: Access
135
+ }
136
+
137
+ const hashConfigRegionRuleIfDeclaredOverAge =
138
+ 'configRegionRuleIfDeclaredOverAge'
139
+
140
+ export function isConfigRegionRuleIfDeclaredOverAge<V>(v: V) {
141
+ return is$typed(v, id, hashConfigRegionRuleIfDeclaredOverAge)
142
+ }
143
+
144
+ export function validateConfigRegionRuleIfDeclaredOverAge<V>(v: V) {
145
+ return validate<ConfigRegionRuleIfDeclaredOverAge & V>(
146
+ v,
147
+ id,
148
+ hashConfigRegionRuleIfDeclaredOverAge,
149
+ )
150
+ }
151
+
152
+ /** Age Assurance rule that applies if the user has declared themselves under a certain age. */
153
+ export interface ConfigRegionRuleIfDeclaredUnderAge {
154
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleIfDeclaredUnderAge'
155
+ /** The age threshold as a whole integer. */
156
+ age: number
157
+ access: Access
158
+ }
159
+
160
+ const hashConfigRegionRuleIfDeclaredUnderAge =
161
+ 'configRegionRuleIfDeclaredUnderAge'
162
+
163
+ export function isConfigRegionRuleIfDeclaredUnderAge<V>(v: V) {
164
+ return is$typed(v, id, hashConfigRegionRuleIfDeclaredUnderAge)
165
+ }
166
+
167
+ export function validateConfigRegionRuleIfDeclaredUnderAge<V>(v: V) {
168
+ return validate<ConfigRegionRuleIfDeclaredUnderAge & V>(
169
+ v,
170
+ id,
171
+ hashConfigRegionRuleIfDeclaredUnderAge,
172
+ )
173
+ }
174
+
175
+ /** Age Assurance rule that applies if the user has been assured to be equal-to or over a certain age. */
176
+ export interface ConfigRegionRuleIfAssuredOverAge {
177
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleIfAssuredOverAge'
178
+ /** The age threshold as a whole integer. */
179
+ age: number
180
+ access: Access
181
+ }
182
+
183
+ const hashConfigRegionRuleIfAssuredOverAge = 'configRegionRuleIfAssuredOverAge'
184
+
185
+ export function isConfigRegionRuleIfAssuredOverAge<V>(v: V) {
186
+ return is$typed(v, id, hashConfigRegionRuleIfAssuredOverAge)
187
+ }
188
+
189
+ export function validateConfigRegionRuleIfAssuredOverAge<V>(v: V) {
190
+ return validate<ConfigRegionRuleIfAssuredOverAge & V>(
191
+ v,
192
+ id,
193
+ hashConfigRegionRuleIfAssuredOverAge,
194
+ )
195
+ }
196
+
197
+ /** Age Assurance rule that applies if the user has been assured to be under a certain age. */
198
+ export interface ConfigRegionRuleIfAssuredUnderAge {
199
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleIfAssuredUnderAge'
200
+ /** The age threshold as a whole integer. */
201
+ age: number
202
+ access: Access
203
+ }
204
+
205
+ const hashConfigRegionRuleIfAssuredUnderAge =
206
+ 'configRegionRuleIfAssuredUnderAge'
207
+
208
+ export function isConfigRegionRuleIfAssuredUnderAge<V>(v: V) {
209
+ return is$typed(v, id, hashConfigRegionRuleIfAssuredUnderAge)
210
+ }
211
+
212
+ export function validateConfigRegionRuleIfAssuredUnderAge<V>(v: V) {
213
+ return validate<ConfigRegionRuleIfAssuredUnderAge & V>(
214
+ v,
215
+ id,
216
+ hashConfigRegionRuleIfAssuredUnderAge,
217
+ )
218
+ }
219
+
220
+ /** Age Assurance rule that applies if the account is equal-to or newer than a certain date. */
221
+ export interface ConfigRegionRuleIfAccountNewerThan {
222
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleIfAccountNewerThan'
223
+ /** The date threshold as a datetime string. */
224
+ date: string
225
+ access: Access
226
+ }
227
+
228
+ const hashConfigRegionRuleIfAccountNewerThan =
229
+ 'configRegionRuleIfAccountNewerThan'
230
+
231
+ export function isConfigRegionRuleIfAccountNewerThan<V>(v: V) {
232
+ return is$typed(v, id, hashConfigRegionRuleIfAccountNewerThan)
233
+ }
234
+
235
+ export function validateConfigRegionRuleIfAccountNewerThan<V>(v: V) {
236
+ return validate<ConfigRegionRuleIfAccountNewerThan & V>(
237
+ v,
238
+ id,
239
+ hashConfigRegionRuleIfAccountNewerThan,
240
+ )
241
+ }
242
+
243
+ /** Age Assurance rule that applies if the account is older than a certain date. */
244
+ export interface ConfigRegionRuleIfAccountOlderThan {
245
+ $type?: 'app.bsky.ageassurance.defs#configRegionRuleIfAccountOlderThan'
246
+ /** The date threshold as a datetime string. */
247
+ date: string
248
+ access: Access
249
+ }
250
+
251
+ const hashConfigRegionRuleIfAccountOlderThan =
252
+ 'configRegionRuleIfAccountOlderThan'
253
+
254
+ export function isConfigRegionRuleIfAccountOlderThan<V>(v: V) {
255
+ return is$typed(v, id, hashConfigRegionRuleIfAccountOlderThan)
256
+ }
257
+
258
+ export function validateConfigRegionRuleIfAccountOlderThan<V>(v: V) {
259
+ return validate<ConfigRegionRuleIfAccountOlderThan & V>(
260
+ v,
261
+ id,
262
+ hashConfigRegionRuleIfAccountOlderThan,
263
+ )
264
+ }
265
+
266
+ /** Object used to store Age Assurance data in stash. */
267
+ export interface Event {
268
+ $type?: 'app.bsky.ageassurance.defs#event'
269
+ /** The date and time of this write operation. */
270
+ createdAt: string
271
+ /** The unique identifier for this instance of the Age Assurance flow, in UUID format. */
272
+ attemptId: string
273
+ /** The status of the Age Assurance process. */
274
+ status: 'unknown' | 'pending' | 'assured' | 'blocked' | (string & {})
275
+ /** The access level granted based on Age Assurance data we've processed. */
276
+ access: 'unknown' | 'none' | 'safe' | 'full' | (string & {})
277
+ /** The ISO 3166-1 alpha-2 country code provided when beginning the Age Assurance flow. */
278
+ countryCode: string
279
+ /** The ISO 3166-2 region code provided when beginning the Age Assurance flow. */
280
+ regionCode?: string
281
+ /** The email used for Age Assurance. */
282
+ email?: string
283
+ /** The IP address used when initiating the Age Assurance flow. */
284
+ initIp?: string
285
+ /** The user agent used when initiating the Age Assurance flow. */
286
+ initUa?: string
287
+ /** The IP address used when completing the Age Assurance flow. */
288
+ completeIp?: string
289
+ /** The user agent used when completing the Age Assurance flow. */
290
+ completeUa?: string
291
+ }
292
+
293
+ const hashEvent = 'event'
294
+
295
+ export function isEvent<V>(v: V) {
296
+ return is$typed(v, id, hashEvent)
297
+ }
298
+
299
+ export function validateEvent<V>(v: V) {
300
+ return validate<Event & V>(v, id, hashEvent)
301
+ }
@@ -0,0 +1,34 @@
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
+ import type * as AppBskyAgeassuranceDefs from './defs.js'
13
+
14
+ const is$typed = _is$typed,
15
+ validate = _validate
16
+ const id = 'app.bsky.ageassurance.getConfig'
17
+
18
+ export type QueryParams = {}
19
+ export type InputSchema = undefined
20
+ export type OutputSchema = AppBskyAgeassuranceDefs.Config
21
+ export type HandlerInput = void
22
+
23
+ export interface HandlerSuccess {
24
+ encoding: 'application/json'
25
+ body: OutputSchema
26
+ headers?: { [key: string]: string }
27
+ }
28
+
29
+ export interface HandlerError {
30
+ status: number
31
+ message?: string
32
+ }
33
+
34
+ export type HandlerOutput = HandlerError | HandlerSuccess
@@ -0,0 +1,42 @@
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
+ import type * as AppBskyAgeassuranceDefs from './defs.js'
13
+
14
+ const is$typed = _is$typed,
15
+ validate = _validate
16
+ const id = 'app.bsky.ageassurance.getState'
17
+
18
+ export type QueryParams = {
19
+ countryCode: string
20
+ regionCode?: string
21
+ }
22
+ export type InputSchema = undefined
23
+
24
+ export interface OutputSchema {
25
+ state: AppBskyAgeassuranceDefs.State
26
+ metadata: AppBskyAgeassuranceDefs.StateMetadata
27
+ }
28
+
29
+ export type HandlerInput = void
30
+
31
+ export interface HandlerSuccess {
32
+ encoding: 'application/json'
33
+ body: OutputSchema
34
+ headers?: { [key: string]: string }
35
+ }
36
+
37
+ export interface HandlerError {
38
+ status: number
39
+ message?: string
40
+ }
41
+
42
+ export type HandlerOutput = HandlerError | HandlerSuccess
@@ -13,6 +13,7 @@ import type * as ComAtprotoAdminDefs from '../../../com/atproto/admin/defs.js'
13
13
  import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef.js'
14
14
  import type * as ChatBskyConvoDefs from '../../../chat/bsky/convo/defs.js'
15
15
  import type * as ComAtprotoModerationDefs from '../../../com/atproto/moderation/defs.js'
16
+ import type * as AppBskyAgeassuranceDefs from '../../../app/bsky/ageassurance/defs.js'
16
17
  import type * as ComAtprotoServerDefs from '../../../com/atproto/server/defs.js'
17
18
  import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.js'
18
19
 
@@ -308,6 +309,8 @@ export interface ModEventTakedown {
308
309
  policies?: string[]
309
310
  /** Severity level of the violation (e.g., 'sev-0', 'sev-1', 'sev-2', etc.). */
310
311
  severityLevel?: string
312
+ /** List of services where the takedown should be applied. If empty or not provided, takedown is applied on all configured services. */
313
+ targetServices?: ('appview' | 'pds' | (string & {}))[]
311
314
  /** Number of strikes to assign to the user for this violation. */
312
315
  strikeCount?: number
313
316
  /** When the strike should expire. If not provided, the strike never expires. */
@@ -447,10 +450,15 @@ export interface AgeAssuranceEvent {
447
450
  $type?: 'tools.ozone.moderation.defs#ageAssuranceEvent'
448
451
  /** The date and time of this write operation. */
449
452
  createdAt: string
450
- /** The status of the age assurance process. */
451
- status: 'unknown' | 'pending' | 'assured' | (string & {})
452
453
  /** The unique identifier for this instance of the age assurance flow, in UUID format. */
453
454
  attemptId: string
455
+ /** The status of the Age Assurance process. */
456
+ status: 'unknown' | 'pending' | 'assured' | (string & {})
457
+ access?: AppBskyAgeassuranceDefs.Access
458
+ /** The ISO 3166-1 alpha-2 country code provided when beginning the Age Assurance flow. */
459
+ countryCode?: string
460
+ /** The ISO 3166-2 region code provided when beginning the Age Assurance flow. */
461
+ regionCode?: string
454
462
  /** The IP address used when initiating the AA flow. */
455
463
  initIp?: string
456
464
  /** The user agent used when initiating the AA flow. */
@@ -476,6 +484,7 @@ export interface AgeAssuranceOverrideEvent {
476
484
  $type?: 'tools.ozone.moderation.defs#ageAssuranceOverrideEvent'
477
485
  /** The status to be set for the user decided by a moderator, overriding whatever value the user had previously. Use reset to default to original state. */
478
486
  status: 'assured' | 'reset' | 'blocked' | (string & {})
487
+ access?: AppBskyAgeassuranceDefs.Access
479
488
  /** Comment describing the reason for the override. */
480
489
  comment: string
481
490
  }
@@ -634,6 +643,8 @@ export interface ModEventEmail {
634
643
  strikeCount?: number
635
644
  /** When the strike should expire. If not provided, the strike never expires. */
636
645
  strikeExpiresAt?: string
646
+ /** Indicates whether the email was successfully delivered to the user's inbox. */
647
+ isDelivered?: boolean
637
648
  }
638
649
 
639
650
  const hashModEventEmail = 'modEventEmail'
@@ -51,7 +51,12 @@ export type QueryParams = {
51
51
  /** When set to true, only muted subjects and reporters will be returned. */
52
52
  onlyMuted?: boolean
53
53
  /** Specify when fetching subjects in a certain state */
54
- reviewState?: string
54
+ reviewState?:
55
+ | 'tools.ozone.moderation.defs#reviewOpen'
56
+ | 'tools.ozone.moderation.defs#reviewClosed'
57
+ | 'tools.ozone.moderation.defs#reviewEscalated'
58
+ | 'tools.ozone.moderation.defs#reviewNone'
59
+ | (string & {})
55
60
  ignoreSubjects?: string[]
56
61
  /** Get all subject statuses that were reviewed by a specific moderator */
57
62
  lastReviewedBy?: string
@@ -56,6 +56,16 @@ export interface Takedown {
56
56
  acknowledgeAccountSubjects?: boolean
57
57
  /** Names/Keywords of the policies that drove the decision. */
58
58
  policies?: string[]
59
+ /** Severity level of the violation (e.g., 'sev-0', 'sev-1', 'sev-2', etc.). */
60
+ severityLevel?: string
61
+ /** Number of strikes to assign to the user when takedown is applied. */
62
+ strikeCount?: number
63
+ /** When the strike should expire. If not provided, the strike never expires. */
64
+ strikeExpiresAt?: string
65
+ /** Email content to be sent to the user upon takedown. */
66
+ emailContent?: string
67
+ /** Subject of the email to be sent to the user upon takedown. */
68
+ emailSubject?: string
59
69
  }
60
70
 
61
71
  const hashTakedown = 'takedown'
@@ -494,6 +494,7 @@ export class ModerationService {
494
494
 
495
495
  if (isModEventEmail(event)) {
496
496
  meta.subjectLine = event.subjectLine
497
+ meta.isDelivered = !!event.isDelivered
497
498
  if (event.content) {
498
499
  meta.content = event.content
499
500
  }
@@ -572,6 +573,10 @@ export class ModerationService {
572
573
  meta.policies = event.policies.join(',')
573
574
  }
574
575
 
576
+ if (isModEventTakedown(event) && event.targetServices?.length) {
577
+ meta.targetServices = event.targetServices.join(',')
578
+ }
579
+
575
580
  // Keep trace of reports that came in while the reporter was in muted stated
576
581
  if (isModEventReport(event)) {
577
582
  const isReportingMuted = await this.isReportingMutedForSubject(createdBy)
@@ -773,17 +778,20 @@ export class ModerationService {
773
778
  async takedownRepo(
774
779
  subject: RepoSubject,
775
780
  takedownId: number,
781
+ targetServices: Set<string>,
776
782
  isSuspend = false,
777
783
  ) {
778
784
  const takedownRef = `BSKY-${
779
785
  isSuspend ? 'SUSPEND' : 'TAKEDOWN'
780
786
  }-${takedownId}`
781
787
 
782
- const values = this.eventPusher.takedowns.map((eventType) => ({
783
- eventType,
784
- subjectDid: subject.did,
785
- takedownRef,
786
- }))
788
+ const values = this.eventPusher
789
+ .getTakedownServices(targetServices)
790
+ .map((eventType) => ({
791
+ eventType,
792
+ subjectDid: subject.did,
793
+ takedownRef,
794
+ }))
787
795
 
788
796
  const repoEvts = await this.db.db
789
797
  .insertInto('repo_push_event')
@@ -849,7 +857,11 @@ export class ModerationService {
849
857
  })
850
858
  }
851
859
 
852
- async takedownRecord(subject: RecordSubject, takedownId: number) {
860
+ async takedownRecord(
861
+ subject: RecordSubject,
862
+ takedownId: number,
863
+ targetServices: Set<string>,
864
+ ) {
853
865
  this.db.assertTransaction()
854
866
  await this.formatAndCreateLabels(subject.uri, subject.cid, {
855
867
  create: [TAKEDOWN_LABEL],
@@ -859,7 +871,9 @@ export class ModerationService {
859
871
  const blobCids = subject.blobCids
860
872
  if (blobCids && blobCids.length > 0) {
861
873
  const blobValues: Insertable<BlobPushEvent>[] = []
862
- for (const eventType of this.eventPusher.takedowns) {
874
+ for (const eventType of this.eventPusher.getTakedownServices(
875
+ targetServices,
876
+ )) {
863
877
  for (const cid of blobCids) {
864
878
  blobValues.push({
865
879
  eventType,
@@ -1,3 +1,5 @@
1
+ /* eslint-disable import/no-deprecated */
2
+
1
3
  import net from 'node:net'
2
4
  import { sql } from 'kysely'
3
5
  import AtpAgent from '@atproto/api'
@@ -195,6 +195,15 @@ export class ModerationViews {
195
195
  }
196
196
  }
197
197
 
198
+ if (isModEventTakedown(event)) {
199
+ if (
200
+ typeof meta.targetServices === 'string' &&
201
+ meta.targetServices.length > 0
202
+ ) {
203
+ event.targetServices = meta.targetServices.split(',')
204
+ }
205
+ }
206
+
198
207
  if (isModEventLabel(event)) {
199
208
  event.createLabelVals = row.createLabelVals?.length
200
209
  ? row.createLabelVals.split(' ')
@@ -229,6 +238,7 @@ export class ModerationViews {
229
238
  if (isModEventEmail(event)) {
230
239
  event.content = ifString(meta.content)!
231
240
  event.subjectLine = ifString(meta.subjectLine)!
241
+ event.isDelivered = !!meta.isDelivered
232
242
  }
233
243
 
234
244
  if (isModEventComment(event) && meta.sticky) {
@@ -198,6 +198,21 @@ export const settingValidators = new Map<
198
198
  hasDefault = true
199
199
  }
200
200
  }
201
+
202
+ if (severityVal['targetServices'] !== undefined) {
203
+ if (!Array.isArray(severityVal['targetServices'])) {
204
+ throw new InvalidRequestError(
205
+ `targetServices must be an array for severity level ${severityKey} in policy ${key}`,
206
+ )
207
+ }
208
+ for (const service of severityVal['targetServices']) {
209
+ if (typeof service !== 'string') {
210
+ throw new InvalidRequestError(
211
+ `Each target service must be a string for severity level ${severityKey} in policy ${key}`,
212
+ )
213
+ }
214
+ }
215
+ }
201
216
  }
202
217
  }
203
218
  }
@@ -1,8 +1,11 @@
1
+ /* eslint-disable import/no-deprecated */
2
+
1
3
  import { AtpAgent } from '@atproto/api'
2
4
  import { cborEncode } from '@atproto/common'
3
5
  import { Secp256k1Keypair, verifySignature } from '@atproto/crypto'
4
6
  import { EXAMPLE_LABELER, TestNetwork } from '@atproto/dev-env'
5
- import { DisconnectError, Subscription } from '@atproto/xrpc-server'
7
+ import { DisconnectError } from '@atproto/ws-client'
8
+ import { Subscription } from '@atproto/xrpc-server'
6
9
  import { ids, lexicons } from '../src/lexicon/lexicons'
7
10
  import { Label } from '../src/lexicon/types/com/atproto/label/defs'
8
11
  import {
@@ -19,13 +19,20 @@ describe('scheduled action processor', () => {
19
19
  }
20
20
  }
21
21
 
22
- const scheduleTestAction = async (subject: string, scheduling: any) => {
22
+ const scheduleTestAction = async (
23
+ subject: string,
24
+ scheduling: any,
25
+ emailData?: { emailSubject?: string; emailContent?: string },
26
+ ) => {
23
27
  return await adminAgent.tools.ozone.moderation.scheduleAction(
24
28
  {
25
29
  action: {
26
30
  $type: 'tools.ozone.moderation.scheduleAction#takedown',
27
31
  comment: 'Test scheduled takedown',
28
32
  policies: ['spam'],
33
+ severityLevel: 'sev-1',
34
+ strikeCount: 1,
35
+ ...emailData,
29
36
  },
30
37
  subjects: [subject],
31
38
  createdBy: 'did:plc:moderator',
@@ -74,7 +81,14 @@ describe('scheduled action processor', () => {
74
81
  const testSubject = sc.dids.alice
75
82
 
76
83
  const pastTime = new Date(Date.now() - 1000).toISOString()
77
- await scheduleTestAction(testSubject, { executeAt: pastTime })
84
+ await scheduleTestAction(
85
+ testSubject,
86
+ { executeAt: pastTime },
87
+ {
88
+ emailSubject: 'Test Email Subject',
89
+ emailContent: 'Test Email Content',
90
+ },
91
+ )
78
92
 
79
93
  const pendingActions = await getScheduledActions(
80
94
  ['pending'],
@@ -94,12 +108,20 @@ describe('scheduled action processor', () => {
94
108
 
95
109
  const modEvents = await getModerationEvents(testSubject, [
96
110
  'tools.ozone.moderation.defs#modEventTakedown',
111
+ 'tools.ozone.moderation.defs#modEventEmail',
97
112
  ])
98
- expect(modEvents.length).toBe(1)
99
-
100
- expect(modEvents[0].event['comment']).toContain(
101
- '[SCHEDULED_ACTION] Test scheduled takedown',
113
+ expect(modEvents.length).toBe(2)
114
+ const takedownEvent = modEvents.find(
115
+ (e) => e.event.$type === 'tools.ozone.moderation.defs#modEventTakedown',
116
+ )
117
+ const emailEvent = modEvents.find(
118
+ (e) => e.event.$type === 'tools.ozone.moderation.defs#modEventEmail',
102
119
  )
120
+
121
+ expect(takedownEvent?.event['comment']).toBeDefined()
122
+
123
+ expect(emailEvent?.event['subjectLine']).toBe('Test Email Subject')
124
+ expect(emailEvent?.event['content']).toBe('Test Email Content')
103
125
  })
104
126
 
105
127
  it('skips actions scheduled for future execution', async () => {
@@ -202,7 +224,9 @@ describe('scheduled action processor', () => {
202
224
  // Verify the moderation event has all properties
203
225
  const modEvents = await getModerationEvents(testSubject, [
204
226
  'tools.ozone.moderation.defs#modEventTakedown',
227
+ 'tools.ozone.moderation.defs#modEventEmail',
205
228
  ])
229
+ // No email was sent
206
230
  expect(modEvents.length).toBe(1)
207
231
 
208
232
  const takedownEvent = modEvents[0].event as ModEventTakedown