@helyx/module-automod 1.0.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +725 -0
  3. package/README.md +80 -0
  4. package/dist/activity-resource.d.ts +3 -0
  5. package/dist/activity-resource.js +114 -0
  6. package/dist/case-link-provider.d.ts +3 -0
  7. package/dist/case-link-provider.js +13 -0
  8. package/dist/configuration.d.ts +22 -0
  9. package/dist/configuration.js +85 -0
  10. package/dist/constants.d.ts +30 -0
  11. package/dist/constants.js +37 -0
  12. package/dist/contracts.d.ts +30 -0
  13. package/dist/contracts.js +51 -0
  14. package/dist/domain.d.ts +61 -0
  15. package/dist/domain.js +274 -0
  16. package/dist/engine/canonicalisation.d.ts +9 -0
  17. package/dist/engine/canonicalisation.js +150 -0
  18. package/dist/engine/compile.d.ts +4 -0
  19. package/dist/engine/compile.js +169 -0
  20. package/dist/engine/confidence.d.ts +13 -0
  21. package/dist/engine/confidence.js +57 -0
  22. package/dist/engine/configuration-cache.d.ts +29 -0
  23. package/dist/engine/configuration-cache.js +115 -0
  24. package/dist/engine/contracts.d.ts +82 -0
  25. package/dist/engine/contracts.js +25 -0
  26. package/dist/engine/index.d.ts +11 -0
  27. package/dist/engine/index.js +11 -0
  28. package/dist/engine/matcher.d.ts +3 -0
  29. package/dist/engine/matcher.js +206 -0
  30. package/dist/engine/observations.d.ts +45 -0
  31. package/dist/engine/observations.js +105 -0
  32. package/dist/engine/operation-id.d.ts +18 -0
  33. package/dist/engine/operation-id.js +24 -0
  34. package/dist/engine/similar-message-window.d.ts +68 -0
  35. package/dist/engine/similar-message-window.js +259 -0
  36. package/dist/engine/term-import.d.ts +15 -0
  37. package/dist/engine/term-import.js +43 -0
  38. package/dist/enhanced-configuration.d.ts +16 -0
  39. package/dist/enhanced-configuration.js +91 -0
  40. package/dist/events.d.ts +4 -0
  41. package/dist/events.js +371 -0
  42. package/dist/health.d.ts +9 -0
  43. package/dist/health.js +45 -0
  44. package/dist/index.d.ts +10 -0
  45. package/dist/index.js +141 -0
  46. package/dist/policy-provider.d.ts +3 -0
  47. package/dist/policy-provider.js +66 -0
  48. package/dist/publication-support.d.ts +14 -0
  49. package/dist/publication-support.js +69 -0
  50. package/dist/publication.d.ts +32 -0
  51. package/dist/publication.js +333 -0
  52. package/dist/receipt-statistics.d.ts +11 -0
  53. package/dist/receipt-statistics.js +58 -0
  54. package/dist/records.d.ts +299 -0
  55. package/dist/records.js +288 -0
  56. package/dist/repository-model.d.ts +54 -0
  57. package/dist/repository-model.js +132 -0
  58. package/dist/repository.d.ts +133 -0
  59. package/dist/repository.js +523 -0
  60. package/dist/resource-cursor.d.ts +5 -0
  61. package/dist/resource-cursor.js +45 -0
  62. package/dist/resource-presentation.d.ts +21 -0
  63. package/dist/resource-presentation.js +111 -0
  64. package/dist/resource-validation.d.ts +6 -0
  65. package/dist/resource-validation.js +80 -0
  66. package/dist/resources.d.ts +4 -0
  67. package/dist/resources.js +319 -0
  68. package/dist/scanner-configuration.d.ts +10 -0
  69. package/dist/scanner-configuration.js +146 -0
  70. package/dist/scanner-provider.d.ts +5 -0
  71. package/dist/scanner-provider.js +310 -0
  72. package/dist/scanner-result-validation.d.ts +3 -0
  73. package/dist/scanner-result-validation.js +61 -0
  74. package/dist/settings-preview.d.ts +6 -0
  75. package/dist/settings-preview.js +84 -0
  76. package/manifest.json +1601 -0
  77. package/migrations/0001_automod_standard.sql +114 -0
  78. package/migrations/0002_automod_enhanced_receipts.sql +40 -0
  79. package/package.json +61 -0
package/manifest.json ADDED
@@ -0,0 +1,1601 @@
1
+ {
2
+ "manifestVersion": "0.5",
3
+ "releaseStatus": "live",
4
+ "deploymentSupport": "both",
5
+ "credentialRisk": "none",
6
+ "deploymentReason": "Standard rules use Discord and Helyx runtime capabilities available in both Hosted and Self-Hosted installations.",
7
+ "tier": "free",
8
+ "moduleKind": "customer_feature",
9
+ "customerSelectable": true,
10
+ "countsAsInstalledModule": true,
11
+ "healthExposure": "direct",
12
+ "id": "helyx.automod",
13
+ "name": "Auto Moderation",
14
+ "version": "1.0.1",
15
+ "description": "Manage transparent Discord-native Auto Moderation rules and structural enforcement receipts.",
16
+ "entry": "./dist/index.js",
17
+ "framework": "^0.1.0",
18
+ "dependencies": [],
19
+ "permissions": ["MANAGE_GUILD", "MODERATE_MEMBERS"],
20
+ "features": [
21
+ {
22
+ "id": "automod.enhanced-scanning",
23
+ "hostedAccess": "helyx.premium",
24
+ "selfHostedAccess": "included",
25
+ "requiredDiscordCapabilities": ["message-content"],
26
+ "adapterRisk": {
27
+ "hosted": "high",
28
+ "selfHosted": "none",
29
+ "reason": "Hosted Enhanced scanning uses Ecko while Self-Hosted Enhanced scanning remains inside the existing bot process."
30
+ }
31
+ }
32
+ ],
33
+ "permissionOperations": [
34
+ {
35
+ "id": "automod.rules.read",
36
+ "label": "Read Auto Moderation rules",
37
+ "description": "List and inspect Auto Moderation rule definitions.",
38
+ "defaultPreset": "administrators"
39
+ },
40
+ {
41
+ "id": "automod.rules.manage",
42
+ "label": "Manage Auto Moderation rules",
43
+ "description": "Review rule imports and delete unmapped unpublished Auto Moderation rules.",
44
+ "defaultPreset": "administrators"
45
+ },
46
+ {
47
+ "id": "automod.rules.publish",
48
+ "label": "Save or disable Auto Moderation rules",
49
+ "description": "Validate and apply new or edited Helyx-owned Discord rules with Save, or disable their protection.",
50
+ "defaultPreset": "administrators"
51
+ },
52
+ {
53
+ "id": "automod.rules.reconcile",
54
+ "label": "Reconcile Auto Moderation rules",
55
+ "description": "Adopt, restore or clean up a mapped Helyx-owned Discord rule.",
56
+ "defaultPreset": "administrators"
57
+ },
58
+ {
59
+ "id": "automod.activity.read",
60
+ "label": "Read Auto Moderation activity",
61
+ "description": "Read content-free native Auto Moderation activity receipts.",
62
+ "defaultPreset": "administrators"
63
+ },
64
+ {
65
+ "id": "automod.settings.manage",
66
+ "label": "Manage Auto Moderation settings",
67
+ "description": "Change native defaults, exemptions and repeat-offender settings.",
68
+ "defaultPreset": "administrators"
69
+ }
70
+ ],
71
+ "privacy": {
72
+ "containsPersonalData": true,
73
+ "providerVersion": 1,
74
+ "collections": [
75
+ {
76
+ "name": "rules",
77
+ "createdAtField": "created_at",
78
+ "subjectFields": [
79
+ {
80
+ "field": "created_by_user_id",
81
+ "role": "rule-creator",
82
+ "erasure": "set-null"
83
+ },
84
+ {
85
+ "field": "updated_by_user_id",
86
+ "role": "rule-updater",
87
+ "erasure": "set-null"
88
+ }
89
+ ],
90
+ "subjectRelationships": [],
91
+ "contentFields": [
92
+ {
93
+ "field": "document_json",
94
+ "mayIdentifySubject": true,
95
+ "erasure": "manual-review"
96
+ }
97
+ ]
98
+ },
99
+ {
100
+ "name": "action_receipts",
101
+ "createdAtField": "created_at",
102
+ "subjectFields": [
103
+ {
104
+ "field": "subject_user_id",
105
+ "role": "detected-member",
106
+ "erasure": "set-null"
107
+ }
108
+ ],
109
+ "subjectRelationships": [],
110
+ "contentFields": [
111
+ {
112
+ "field": "channel_id",
113
+ "mayIdentifySubject": true,
114
+ "erasure": "manual-review"
115
+ },
116
+ {
117
+ "field": "message_id",
118
+ "mayIdentifySubject": true,
119
+ "erasure": "manual-review"
120
+ },
121
+ {
122
+ "field": "alert_system_message_id",
123
+ "mayIdentifySubject": true,
124
+ "erasure": "manual-review"
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ "name": "enforcement_claims",
130
+ "createdAtField": "created_at",
131
+ "subjectFields": [
132
+ {
133
+ "field": "subject_user_id",
134
+ "role": "enforced-member",
135
+ "erasure": "set-null"
136
+ }
137
+ ],
138
+ "subjectRelationships": [],
139
+ "contentFields": [
140
+ {
141
+ "field": "request_fingerprint",
142
+ "mayIdentifySubject": true,
143
+ "erasure": "manual-review"
144
+ },
145
+ {
146
+ "field": "channel_id",
147
+ "mayIdentifySubject": true,
148
+ "erasure": "manual-review"
149
+ },
150
+ {
151
+ "field": "message_id",
152
+ "mayIdentifySubject": true,
153
+ "erasure": "manual-review"
154
+ }
155
+ ]
156
+ }
157
+ ]
158
+ },
159
+ "dashboard": {
160
+ "category": "safety",
161
+ "icon": "shield",
162
+ "settingsMutationPermissionId": "automod.settings.manage",
163
+ "settingsPreview": {
164
+ "permissionId": "automod.settings.manage",
165
+ "featureId": "automod.enhanced-scanning",
166
+ "label": "Preview Enhanced detection",
167
+ "description": "Test unsaved confidence settings against fictional content without sending it to Discord.",
168
+ "inputs": [
169
+ {
170
+ "key": "content",
171
+ "type": "string",
172
+ "label": "Fictional content",
173
+ "description": "A fictional message tested in memory against currently published rules.",
174
+ "required": true,
175
+ "supportVisibility": "hidden",
176
+ "minLength": 1,
177
+ "maxLength": 2000,
178
+ "multiline": true
179
+ }
180
+ ]
181
+ },
182
+ "settings": [
183
+ {
184
+ "key": "configuredMode",
185
+ "type": "select",
186
+ "label": "Detection mode",
187
+ "description": "Use Discord-native Standard rules or add feature-scoped Enhanced matching.",
188
+ "group": "Mode",
189
+ "required": true,
190
+ "supportVisibility": "value",
191
+ "options": [
192
+ {
193
+ "value": "standard",
194
+ "label": "Standard"
195
+ },
196
+ {
197
+ "value": "enhanced",
198
+ "label": "Enhanced",
199
+ "featureId": "automod.enhanced-scanning",
200
+ "requiredDiscordCapability": "message-content"
201
+ }
202
+ ],
203
+ "default": "standard"
204
+ },
205
+ {
206
+ "key": "enhancedSensitivityProfile",
207
+ "type": "select",
208
+ "label": "Detection sensitivity",
209
+ "description": "Choose the Enhanced transformed-match sensitivity profile.",
210
+ "group": "Enhanced scanning",
211
+ "required": true,
212
+ "supportVisibility": "value",
213
+ "featureId": "automod.enhanced-scanning",
214
+ "options": [
215
+ {
216
+ "value": "strict",
217
+ "label": "Strict (90)"
218
+ },
219
+ {
220
+ "value": "balanced",
221
+ "label": "Balanced (80)"
222
+ },
223
+ {
224
+ "value": "sensitive",
225
+ "label": "Sensitive (70)"
226
+ },
227
+ {
228
+ "value": "custom",
229
+ "label": "Custom"
230
+ }
231
+ ],
232
+ "default": "balanced"
233
+ },
234
+ {
235
+ "key": "enhancedMinimumConfidence",
236
+ "type": "integer",
237
+ "label": "Minimum match confidence",
238
+ "description": "Lowest versioned matching score accepted in Custom mode; it is not a probability of harm.",
239
+ "group": "Enhanced scanning",
240
+ "required": true,
241
+ "supportVisibility": "value",
242
+ "featureId": "automod.enhanced-scanning",
243
+ "minimum": 60,
244
+ "maximum": 95,
245
+ "default": 80,
246
+ "display": "range",
247
+ "step": 1,
248
+ "visibleWhen": {
249
+ "field": "enhancedSensitivityProfile",
250
+ "equals": "custom"
251
+ }
252
+ },
253
+ {
254
+ "key": "enhancedCategoryOverridesEnabled",
255
+ "type": "boolean",
256
+ "label": "Category confidence overrides",
257
+ "description": "Use separate Enhanced confidence thresholds for Low, Medium, High and Critical.",
258
+ "group": "Enhanced scanning",
259
+ "required": true,
260
+ "supportVisibility": "value",
261
+ "featureId": "automod.enhanced-scanning",
262
+ "default": false
263
+ },
264
+ {
265
+ "key": "enhancedLowMinimumConfidence",
266
+ "type": "integer",
267
+ "label": "Low confidence",
268
+ "description": "Minimum transformed-match confidence for Low rules.",
269
+ "group": "Enhanced category thresholds",
270
+ "required": true,
271
+ "supportVisibility": "value",
272
+ "featureId": "automod.enhanced-scanning",
273
+ "minimum": 60,
274
+ "maximum": 95,
275
+ "default": 80,
276
+ "display": "range",
277
+ "step": 1,
278
+ "visibleWhen": {
279
+ "field": "enhancedCategoryOverridesEnabled",
280
+ "equals": true
281
+ }
282
+ },
283
+ {
284
+ "key": "enhancedMediumMinimumConfidence",
285
+ "type": "integer",
286
+ "label": "Medium confidence",
287
+ "description": "Minimum transformed-match confidence for Medium rules.",
288
+ "group": "Enhanced category thresholds",
289
+ "required": true,
290
+ "supportVisibility": "value",
291
+ "featureId": "automod.enhanced-scanning",
292
+ "minimum": 60,
293
+ "maximum": 95,
294
+ "default": 80,
295
+ "display": "range",
296
+ "step": 1,
297
+ "visibleWhen": {
298
+ "field": "enhancedCategoryOverridesEnabled",
299
+ "equals": true
300
+ }
301
+ },
302
+ {
303
+ "key": "enhancedHighMinimumConfidence",
304
+ "type": "integer",
305
+ "label": "High confidence",
306
+ "description": "Minimum transformed-match confidence for High rules.",
307
+ "group": "Enhanced category thresholds",
308
+ "required": true,
309
+ "supportVisibility": "value",
310
+ "featureId": "automod.enhanced-scanning",
311
+ "minimum": 60,
312
+ "maximum": 95,
313
+ "default": 80,
314
+ "display": "range",
315
+ "step": 1,
316
+ "visibleWhen": {
317
+ "field": "enhancedCategoryOverridesEnabled",
318
+ "equals": true
319
+ }
320
+ },
321
+ {
322
+ "key": "enhancedCriticalMinimumConfidence",
323
+ "type": "integer",
324
+ "label": "Critical confidence",
325
+ "description": "Minimum transformed-match confidence for Critical rules.",
326
+ "group": "Enhanced category thresholds",
327
+ "required": true,
328
+ "supportVisibility": "value",
329
+ "featureId": "automod.enhanced-scanning",
330
+ "minimum": 60,
331
+ "maximum": 95,
332
+ "default": 80,
333
+ "display": "range",
334
+ "step": 1,
335
+ "visibleWhen": {
336
+ "field": "enhancedCategoryOverridesEnabled",
337
+ "equals": true
338
+ }
339
+ },
340
+ {
341
+ "key": "presetRuleEnabled",
342
+ "type": "boolean",
343
+ "label": "Allow preset rule configuration",
344
+ "description": "Allow Helyx-managed rules using Discord's preset profanity, sexual-content and slur filters.",
345
+ "group": "Native rules",
346
+ "required": true,
347
+ "supportVisibility": "value",
348
+ "default": false
349
+ },
350
+ {
351
+ "key": "genericSpamRuleEnabled",
352
+ "type": "boolean",
353
+ "label": "Allow generic spam rule configuration",
354
+ "description": "Allow Helyx-managed rules using Discord's automatic spam detection.",
355
+ "group": "Native rules",
356
+ "required": true,
357
+ "supportVisibility": "value",
358
+ "default": false
359
+ },
360
+ {
361
+ "key": "mentionSpamRuleEnabled",
362
+ "type": "boolean",
363
+ "label": "Allow mention spam rule configuration",
364
+ "description": "Allow Helyx-managed rules limiting mentions and using Discord's mention-raid protection.",
365
+ "group": "Native rules",
366
+ "required": true,
367
+ "supportVisibility": "value",
368
+ "default": false
369
+ },
370
+ {
371
+ "key": "defaultNativeAlertChannelId",
372
+ "type": "discord-channel",
373
+ "label": "Default Discord alert channel",
374
+ "description": "Default staff channel for native alerts; Discord may copy triggering content there.",
375
+ "group": "Native defaults",
376
+ "required": false,
377
+ "supportVisibility": "value",
378
+ "channelTypes": ["guild_text", "guild_announcement"],
379
+ "multiple": false
380
+ },
381
+ {
382
+ "key": "defaultExemptRoleIds",
383
+ "type": "discord-role",
384
+ "label": "Globally ignored roles",
385
+ "description": "Roles exempt from every Helyx-owned Auto Moderation rule.",
386
+ "group": "Global exclusions",
387
+ "required": false,
388
+ "supportVisibility": "value",
389
+ "multiple": true,
390
+ "minItems": 0,
391
+ "maxItems": 20
392
+ },
393
+ {
394
+ "key": "defaultExemptChannelIds",
395
+ "type": "discord-channel",
396
+ "label": "Globally ignored channels",
397
+ "description": "Channels exempt from every Helyx-owned Auto Moderation rule.",
398
+ "group": "Global exclusions",
399
+ "required": false,
400
+ "supportVisibility": "value",
401
+ "channelTypes": [
402
+ "guild_text",
403
+ "guild_announcement",
404
+ "guild_forum",
405
+ "guild_media"
406
+ ],
407
+ "multiple": true,
408
+ "minItems": 0,
409
+ "maxItems": 50
410
+ },
411
+ {
412
+ "key": "repeatOffenderWindowDays",
413
+ "type": "integer",
414
+ "label": "Repeat-offender window",
415
+ "description": "Rolling number of days used for first, second and third-or-later offence selection.",
416
+ "group": "Repeat offenders",
417
+ "required": true,
418
+ "supportVisibility": "value",
419
+ "minimum": 1,
420
+ "maximum": 365,
421
+ "default": 30
422
+ }
423
+ ],
424
+ "resources": [
425
+ {
426
+ "id": "automod-rules",
427
+ "name": "Rules",
428
+ "singularName": "Rule",
429
+ "description": "Save validated rules to create or update their Helyx-owned Discord Auto Moderation settings immediately.",
430
+ "operations": [
431
+ "list",
432
+ "read",
433
+ "preview",
434
+ "import",
435
+ "create",
436
+ "update",
437
+ "delete",
438
+ "action"
439
+ ],
440
+ "creationIntents": ["publish"],
441
+ "publicationMode": "validate_on_publish",
442
+ "permissions": {
443
+ "list": "automod.rules.read",
444
+ "read": "automod.rules.read",
445
+ "preview": "automod.rules.read",
446
+ "import": "automod.rules.manage",
447
+ "create": "automod.rules.publish",
448
+ "update": "automod.rules.publish",
449
+ "delete": "automod.rules.manage"
450
+ },
451
+ "tablePresentation": {
452
+ "primaryColumnLabel": "Rule name",
453
+ "showStatus": true,
454
+ "showUpdated": true,
455
+ "openAction": "primary",
456
+ "responsive": "stacked"
457
+ },
458
+ "columns": [
459
+ {
460
+ "key": "filter",
461
+ "label": "Filter",
462
+ "format": "text"
463
+ },
464
+ {
465
+ "key": "action",
466
+ "label": "Action",
467
+ "format": "text"
468
+ },
469
+ {
470
+ "key": "moderationThread",
471
+ "label": "Moderation thread",
472
+ "format": "text"
473
+ },
474
+ {
475
+ "key": "ignoredRoles",
476
+ "label": "Ignored roles",
477
+ "format": "role_list"
478
+ },
479
+ {
480
+ "key": "repeatOffender",
481
+ "label": "Repeat offender",
482
+ "format": "text"
483
+ }
484
+ ],
485
+ "fields": [
486
+ {
487
+ "key": "name",
488
+ "type": "string",
489
+ "label": "Rule name",
490
+ "description": "Unique logical rule name in this server.",
491
+ "required": true,
492
+ "supportVisibility": "value",
493
+ "minLength": 1,
494
+ "maxLength": 80
495
+ },
496
+ {
497
+ "key": "category",
498
+ "type": "select",
499
+ "label": "Category",
500
+ "description": "Low, Medium, High or Critical classification.",
501
+ "required": true,
502
+ "supportVisibility": "value",
503
+ "options": [
504
+ {
505
+ "value": "low",
506
+ "label": "Low"
507
+ },
508
+ {
509
+ "value": "medium",
510
+ "label": "Medium"
511
+ },
512
+ {
513
+ "value": "high",
514
+ "label": "High"
515
+ },
516
+ {
517
+ "value": "critical",
518
+ "label": "Critical"
519
+ }
520
+ ],
521
+ "default": "medium"
522
+ },
523
+ {
524
+ "key": "ruleKind",
525
+ "type": "select",
526
+ "label": "Filter type",
527
+ "description": "Discord-native trigger used by this rule.",
528
+ "required": true,
529
+ "supportVisibility": "value",
530
+ "options": [
531
+ {
532
+ "value": "keyword",
533
+ "label": "Keywords and phrases"
534
+ },
535
+ {
536
+ "value": "keyword_preset",
537
+ "label": "Discord preset"
538
+ },
539
+ {
540
+ "value": "spam",
541
+ "label": "Generic spam"
542
+ },
543
+ {
544
+ "value": "mention_spam",
545
+ "label": "Mention spam"
546
+ }
547
+ ],
548
+ "default": "keyword"
549
+ },
550
+ {
551
+ "key": "keywordFilter",
552
+ "type": "string",
553
+ "label": "Keywords and phrases",
554
+ "description": "Comma or newline-separated Discord keyword, phrase and wildcard entries.",
555
+ "required": false,
556
+ "supportVisibility": "hidden",
557
+ "minLength": 0,
558
+ "maxLength": 4000,
559
+ "multiline": true,
560
+ "visibleWhen": {
561
+ "field": "ruleKind",
562
+ "equals": "keyword"
563
+ }
564
+ },
565
+ {
566
+ "key": "regexPatterns",
567
+ "type": "string",
568
+ "label": "Safe regex patterns",
569
+ "description": "Newline-separated Discord-compatible Rust regex patterns.",
570
+ "required": false,
571
+ "supportVisibility": "hidden",
572
+ "minLength": 0,
573
+ "maxLength": 4000,
574
+ "multiline": true,
575
+ "visibleWhen": {
576
+ "field": "ruleKind",
577
+ "equals": "keyword"
578
+ }
579
+ },
580
+ {
581
+ "key": "allowList",
582
+ "type": "string",
583
+ "label": "Allow list",
584
+ "description": "Comma or newline-separated entries excluded from keyword or preset matching.",
585
+ "required": false,
586
+ "supportVisibility": "hidden",
587
+ "minLength": 0,
588
+ "maxLength": 4000,
589
+ "multiline": true,
590
+ "visibleWhen": {
591
+ "field": "ruleKind",
592
+ "oneOf": ["keyword", "keyword_preset"]
593
+ }
594
+ },
595
+ {
596
+ "key": "enhancedNormalisedMatching",
597
+ "type": "boolean",
598
+ "label": "Enhanced normalised matching",
599
+ "description": "Match reviewed Unicode, visual substitution and separator variants for this bounded term group.",
600
+ "required": true,
601
+ "supportVisibility": "value",
602
+ "featureId": "automod.enhanced-scanning",
603
+ "default": true,
604
+ "visibleWhen": {
605
+ "field": "ruleKind",
606
+ "equals": "keyword"
607
+ }
608
+ },
609
+ {
610
+ "key": "enhancedFuzzyMatching",
611
+ "type": "boolean",
612
+ "label": "Enhanced fuzzy matching",
613
+ "description": "Opt this rule's keyword group into bounded approximate matching. Short terms remain protected.",
614
+ "required": true,
615
+ "supportVisibility": "value",
616
+ "featureId": "automod.enhanced-scanning",
617
+ "default": false,
618
+ "visibleWhen": {
619
+ "field": "ruleKind",
620
+ "equals": "keyword"
621
+ }
622
+ },
623
+ {
624
+ "key": "presetProfanity",
625
+ "type": "boolean",
626
+ "label": "Profanity preset",
627
+ "description": "Include Discord's profanity preset.",
628
+ "required": true,
629
+ "supportVisibility": "value",
630
+ "default": false,
631
+ "visibleWhen": {
632
+ "field": "ruleKind",
633
+ "equals": "keyword_preset"
634
+ }
635
+ },
636
+ {
637
+ "key": "presetSexualContent",
638
+ "type": "boolean",
639
+ "label": "Sexual-content preset",
640
+ "description": "Include Discord's sexual-content preset.",
641
+ "required": true,
642
+ "supportVisibility": "value",
643
+ "default": false,
644
+ "visibleWhen": {
645
+ "field": "ruleKind",
646
+ "equals": "keyword_preset"
647
+ }
648
+ },
649
+ {
650
+ "key": "presetSlurs",
651
+ "type": "boolean",
652
+ "label": "Slurs preset",
653
+ "description": "Include Discord's slurs preset.",
654
+ "required": true,
655
+ "supportVisibility": "value",
656
+ "default": false,
657
+ "visibleWhen": {
658
+ "field": "ruleKind",
659
+ "equals": "keyword_preset"
660
+ }
661
+ },
662
+ {
663
+ "key": "mentionTotalLimit",
664
+ "type": "integer",
665
+ "label": "Mention limit",
666
+ "description": "Maximum mentions allowed in one message.",
667
+ "required": true,
668
+ "supportVisibility": "value",
669
+ "minimum": 1,
670
+ "maximum": 50,
671
+ "default": 5,
672
+ "visibleWhen": {
673
+ "field": "ruleKind",
674
+ "equals": "mention_spam"
675
+ }
676
+ },
677
+ {
678
+ "key": "mentionRaidProtectionEnabled",
679
+ "type": "boolean",
680
+ "label": "Mention raid protection",
681
+ "description": "Use Discord's mention raid protection.",
682
+ "required": true,
683
+ "supportVisibility": "value",
684
+ "default": false,
685
+ "visibleWhen": {
686
+ "field": "ruleKind",
687
+ "equals": "mention_spam"
688
+ }
689
+ },
690
+ {
691
+ "key": "blockMessage",
692
+ "type": "boolean",
693
+ "label": "Block message",
694
+ "description": "Block native matches in Discord and remove posted Enhanced matches after detection.",
695
+ "required": true,
696
+ "supportVisibility": "value",
697
+ "default": true
698
+ },
699
+ {
700
+ "key": "blockExplanation",
701
+ "type": "string",
702
+ "label": "Block explanation",
703
+ "description": "Optional explanation Discord shows after blocking.",
704
+ "required": false,
705
+ "supportVisibility": "value",
706
+ "minLength": 0,
707
+ "maxLength": 150
708
+ },
709
+ {
710
+ "key": "alertChannelId",
711
+ "type": "discord-channel",
712
+ "label": "Discord alert channel",
713
+ "description": "Optional alert channel; Discord may copy triggering content there.",
714
+ "required": false,
715
+ "supportVisibility": "value",
716
+ "channelTypes": ["guild_text", "guild_announcement"],
717
+ "multiple": false
718
+ },
719
+ {
720
+ "key": "nativeTimeoutSeconds",
721
+ "type": "select",
722
+ "label": "Discord mute",
723
+ "description": "Optional Discord-native timeout applied before Helyx follow-up.",
724
+ "required": true,
725
+ "supportVisibility": "value",
726
+ "options": [
727
+ {
728
+ "value": "0",
729
+ "label": "Off"
730
+ },
731
+ {
732
+ "value": "60",
733
+ "label": "60 seconds"
734
+ },
735
+ {
736
+ "value": "300",
737
+ "label": "5 minutes"
738
+ },
739
+ {
740
+ "value": "600",
741
+ "label": "10 minutes"
742
+ },
743
+ {
744
+ "value": "3600",
745
+ "label": "1 hour"
746
+ },
747
+ {
748
+ "value": "86400",
749
+ "label": "1 day"
750
+ },
751
+ {
752
+ "value": "604800",
753
+ "label": "1 week"
754
+ }
755
+ ],
756
+ "default": "0",
757
+ "visibleWhen": {
758
+ "field": "ruleKind",
759
+ "oneOf": ["keyword", "mention_spam"]
760
+ }
761
+ },
762
+ {
763
+ "key": "ignoredRoleIds",
764
+ "type": "discord-role",
765
+ "label": "Ignored roles",
766
+ "description": "Roles ignored in addition to the global list.",
767
+ "required": false,
768
+ "supportVisibility": "value",
769
+ "multiple": true,
770
+ "minItems": 0,
771
+ "maxItems": 20
772
+ },
773
+ {
774
+ "key": "ignoredChannelIds",
775
+ "type": "discord-channel",
776
+ "label": "Ignored channels",
777
+ "description": "Channels ignored in addition to the global list.",
778
+ "required": false,
779
+ "supportVisibility": "value",
780
+ "channelTypes": [
781
+ "guild_text",
782
+ "guild_announcement",
783
+ "guild_forum",
784
+ "guild_media"
785
+ ],
786
+ "multiple": true,
787
+ "minItems": 0,
788
+ "maxItems": 50
789
+ },
790
+ {
791
+ "key": "openViolationThread",
792
+ "type": "boolean",
793
+ "label": "Moderation thread",
794
+ "description": "Ask the optional Moderation module to open a user-visible violation thread.",
795
+ "required": true,
796
+ "supportVisibility": "value",
797
+ "default": false
798
+ },
799
+ {
800
+ "key": "firstAction",
801
+ "type": "select",
802
+ "label": "First offence",
803
+ "description": "Action selected for the first confirmed offence.",
804
+ "group": "Repeat offender",
805
+ "required": true,
806
+ "supportVisibility": "value",
807
+ "options": [
808
+ {
809
+ "value": "case_only",
810
+ "label": "Case only"
811
+ },
812
+ {
813
+ "value": "delete_message",
814
+ "label": "Delete message"
815
+ },
816
+ {
817
+ "value": "warn",
818
+ "label": "Warn"
819
+ },
820
+ {
821
+ "value": "timeout",
822
+ "label": "Mute"
823
+ },
824
+ {
825
+ "value": "kick",
826
+ "label": "Kick"
827
+ },
828
+ {
829
+ "value": "ban",
830
+ "label": "Ban"
831
+ },
832
+ {
833
+ "value": "demote",
834
+ "label": "Demote"
835
+ }
836
+ ],
837
+ "default": "case_only"
838
+ },
839
+ {
840
+ "key": "secondAction",
841
+ "type": "select",
842
+ "label": "Second offence",
843
+ "description": "Action selected for the second confirmed offence.",
844
+ "group": "Repeat offender",
845
+ "required": true,
846
+ "supportVisibility": "value",
847
+ "options": [
848
+ {
849
+ "value": "case_only",
850
+ "label": "Case only"
851
+ },
852
+ {
853
+ "value": "delete_message",
854
+ "label": "Delete message"
855
+ },
856
+ {
857
+ "value": "warn",
858
+ "label": "Warn"
859
+ },
860
+ {
861
+ "value": "timeout",
862
+ "label": "Mute"
863
+ },
864
+ {
865
+ "value": "kick",
866
+ "label": "Kick"
867
+ },
868
+ {
869
+ "value": "ban",
870
+ "label": "Ban"
871
+ },
872
+ {
873
+ "value": "demote",
874
+ "label": "Demote"
875
+ }
876
+ ],
877
+ "default": "case_only"
878
+ },
879
+ {
880
+ "key": "thirdAction",
881
+ "type": "select",
882
+ "label": "Third offence and later",
883
+ "description": "Action selected for every third-or-later confirmed offence.",
884
+ "group": "Repeat offender",
885
+ "required": true,
886
+ "supportVisibility": "value",
887
+ "options": [
888
+ {
889
+ "value": "case_only",
890
+ "label": "Case only"
891
+ },
892
+ {
893
+ "value": "delete_message",
894
+ "label": "Delete message"
895
+ },
896
+ {
897
+ "value": "warn",
898
+ "label": "Warn"
899
+ },
900
+ {
901
+ "value": "timeout",
902
+ "label": "Mute"
903
+ },
904
+ {
905
+ "value": "kick",
906
+ "label": "Kick"
907
+ },
908
+ {
909
+ "value": "ban",
910
+ "label": "Ban"
911
+ },
912
+ {
913
+ "value": "demote",
914
+ "label": "Demote"
915
+ }
916
+ ],
917
+ "default": "case_only"
918
+ },
919
+ {
920
+ "key": "firstTimeoutSeconds",
921
+ "type": "select",
922
+ "label": "First-offence mute",
923
+ "description": "Mute duration when the first action is Mute.",
924
+ "group": "Repeat offender",
925
+ "required": true,
926
+ "supportVisibility": "value",
927
+ "options": [
928
+ {
929
+ "value": "60",
930
+ "label": "60 seconds"
931
+ },
932
+ {
933
+ "value": "300",
934
+ "label": "5 minutes"
935
+ },
936
+ {
937
+ "value": "600",
938
+ "label": "10 minutes"
939
+ },
940
+ {
941
+ "value": "3600",
942
+ "label": "1 hour"
943
+ },
944
+ {
945
+ "value": "86400",
946
+ "label": "1 day"
947
+ },
948
+ {
949
+ "value": "604800",
950
+ "label": "1 week"
951
+ }
952
+ ],
953
+ "default": "300",
954
+ "visibleWhen": {
955
+ "field": "firstAction",
956
+ "equals": "timeout"
957
+ }
958
+ },
959
+ {
960
+ "key": "secondTimeoutSeconds",
961
+ "type": "select",
962
+ "label": "Second-offence mute",
963
+ "description": "Mute duration when the second action is Mute.",
964
+ "group": "Repeat offender",
965
+ "required": true,
966
+ "supportVisibility": "value",
967
+ "options": [
968
+ {
969
+ "value": "60",
970
+ "label": "60 seconds"
971
+ },
972
+ {
973
+ "value": "300",
974
+ "label": "5 minutes"
975
+ },
976
+ {
977
+ "value": "600",
978
+ "label": "10 minutes"
979
+ },
980
+ {
981
+ "value": "3600",
982
+ "label": "1 hour"
983
+ },
984
+ {
985
+ "value": "86400",
986
+ "label": "1 day"
987
+ },
988
+ {
989
+ "value": "604800",
990
+ "label": "1 week"
991
+ }
992
+ ],
993
+ "default": "300",
994
+ "visibleWhen": {
995
+ "field": "secondAction",
996
+ "equals": "timeout"
997
+ }
998
+ },
999
+ {
1000
+ "key": "thirdTimeoutSeconds",
1001
+ "type": "select",
1002
+ "label": "Third-offence mute",
1003
+ "description": "Mute duration when the third action is Mute.",
1004
+ "group": "Repeat offender",
1005
+ "required": true,
1006
+ "supportVisibility": "value",
1007
+ "options": [
1008
+ {
1009
+ "value": "60",
1010
+ "label": "60 seconds"
1011
+ },
1012
+ {
1013
+ "value": "300",
1014
+ "label": "5 minutes"
1015
+ },
1016
+ {
1017
+ "value": "600",
1018
+ "label": "10 minutes"
1019
+ },
1020
+ {
1021
+ "value": "3600",
1022
+ "label": "1 hour"
1023
+ },
1024
+ {
1025
+ "value": "86400",
1026
+ "label": "1 day"
1027
+ },
1028
+ {
1029
+ "value": "604800",
1030
+ "label": "1 week"
1031
+ }
1032
+ ],
1033
+ "default": "300",
1034
+ "visibleWhen": {
1035
+ "field": "thirdAction",
1036
+ "equals": "timeout"
1037
+ }
1038
+ },
1039
+ {
1040
+ "key": "firstBanDeleteMessageSeconds",
1041
+ "type": "select",
1042
+ "label": "First-offence ban deletion",
1043
+ "description": "Message history removed when the first action is Ban.",
1044
+ "group": "Repeat offender",
1045
+ "required": true,
1046
+ "supportVisibility": "value",
1047
+ "options": [
1048
+ {
1049
+ "value": "0",
1050
+ "label": "None"
1051
+ },
1052
+ {
1053
+ "value": "3600",
1054
+ "label": "1 hour"
1055
+ },
1056
+ {
1057
+ "value": "21600",
1058
+ "label": "6 hours"
1059
+ },
1060
+ {
1061
+ "value": "86400",
1062
+ "label": "1 day"
1063
+ },
1064
+ {
1065
+ "value": "259200",
1066
+ "label": "3 days"
1067
+ },
1068
+ {
1069
+ "value": "604800",
1070
+ "label": "1 week"
1071
+ }
1072
+ ],
1073
+ "default": "0",
1074
+ "visibleWhen": {
1075
+ "field": "firstAction",
1076
+ "equals": "ban"
1077
+ }
1078
+ },
1079
+ {
1080
+ "key": "secondBanDeleteMessageSeconds",
1081
+ "type": "select",
1082
+ "label": "Second-offence ban deletion",
1083
+ "description": "Message history removed when the second action is Ban.",
1084
+ "group": "Repeat offender",
1085
+ "required": true,
1086
+ "supportVisibility": "value",
1087
+ "options": [
1088
+ {
1089
+ "value": "0",
1090
+ "label": "None"
1091
+ },
1092
+ {
1093
+ "value": "3600",
1094
+ "label": "1 hour"
1095
+ },
1096
+ {
1097
+ "value": "21600",
1098
+ "label": "6 hours"
1099
+ },
1100
+ {
1101
+ "value": "86400",
1102
+ "label": "1 day"
1103
+ },
1104
+ {
1105
+ "value": "259200",
1106
+ "label": "3 days"
1107
+ },
1108
+ {
1109
+ "value": "604800",
1110
+ "label": "1 week"
1111
+ }
1112
+ ],
1113
+ "default": "0",
1114
+ "visibleWhen": {
1115
+ "field": "secondAction",
1116
+ "equals": "ban"
1117
+ }
1118
+ },
1119
+ {
1120
+ "key": "thirdBanDeleteMessageSeconds",
1121
+ "type": "select",
1122
+ "label": "Third-offence ban deletion",
1123
+ "description": "Message history removed when the third action is Ban.",
1124
+ "group": "Repeat offender",
1125
+ "required": true,
1126
+ "supportVisibility": "value",
1127
+ "options": [
1128
+ {
1129
+ "value": "0",
1130
+ "label": "None"
1131
+ },
1132
+ {
1133
+ "value": "3600",
1134
+ "label": "1 hour"
1135
+ },
1136
+ {
1137
+ "value": "21600",
1138
+ "label": "6 hours"
1139
+ },
1140
+ {
1141
+ "value": "86400",
1142
+ "label": "1 day"
1143
+ },
1144
+ {
1145
+ "value": "259200",
1146
+ "label": "3 days"
1147
+ },
1148
+ {
1149
+ "value": "604800",
1150
+ "label": "1 week"
1151
+ }
1152
+ ],
1153
+ "default": "0",
1154
+ "visibleWhen": {
1155
+ "field": "thirdAction",
1156
+ "equals": "ban"
1157
+ }
1158
+ },
1159
+ {
1160
+ "key": "firstDemoteRoleIds",
1161
+ "type": "discord-role",
1162
+ "label": "First-offence demotion roles",
1163
+ "description": "Eligible roles removed when the first action is Demote.",
1164
+ "group": "Repeat offender",
1165
+ "required": false,
1166
+ "supportVisibility": "value",
1167
+ "multiple": true,
1168
+ "minItems": 0,
1169
+ "maxItems": 10,
1170
+ "visibleWhen": {
1171
+ "field": "firstAction",
1172
+ "equals": "demote"
1173
+ }
1174
+ },
1175
+ {
1176
+ "key": "secondDemoteRoleIds",
1177
+ "type": "discord-role",
1178
+ "label": "Second-offence demotion roles",
1179
+ "description": "Eligible roles removed when the second action is Demote.",
1180
+ "group": "Repeat offender",
1181
+ "required": false,
1182
+ "supportVisibility": "value",
1183
+ "multiple": true,
1184
+ "minItems": 0,
1185
+ "maxItems": 10,
1186
+ "visibleWhen": {
1187
+ "field": "secondAction",
1188
+ "equals": "demote"
1189
+ }
1190
+ },
1191
+ {
1192
+ "key": "thirdDemoteRoleIds",
1193
+ "type": "discord-role",
1194
+ "label": "Third-offence demotion roles",
1195
+ "description": "Eligible roles removed when the third action is Demote.",
1196
+ "group": "Repeat offender",
1197
+ "required": false,
1198
+ "supportVisibility": "value",
1199
+ "multiple": true,
1200
+ "minItems": 0,
1201
+ "maxItems": 10,
1202
+ "visibleWhen": {
1203
+ "field": "thirdAction",
1204
+ "equals": "demote"
1205
+ }
1206
+ }
1207
+ ],
1208
+ "editableFields": [
1209
+ {
1210
+ "statuses": ["draft", "published", "disabled", "review"],
1211
+ "fields": [
1212
+ "name",
1213
+ "category",
1214
+ "ruleKind",
1215
+ "keywordFilter",
1216
+ "regexPatterns",
1217
+ "allowList",
1218
+ "enhancedNormalisedMatching",
1219
+ "enhancedFuzzyMatching",
1220
+ "presetProfanity",
1221
+ "presetSexualContent",
1222
+ "presetSlurs",
1223
+ "mentionTotalLimit",
1224
+ "mentionRaidProtectionEnabled",
1225
+ "blockMessage",
1226
+ "blockExplanation",
1227
+ "alertChannelId",
1228
+ "nativeTimeoutSeconds",
1229
+ "ignoredRoleIds",
1230
+ "ignoredChannelIds",
1231
+ "openViolationThread",
1232
+ "firstAction",
1233
+ "secondAction",
1234
+ "thirdAction",
1235
+ "firstTimeoutSeconds",
1236
+ "secondTimeoutSeconds",
1237
+ "thirdTimeoutSeconds",
1238
+ "firstBanDeleteMessageSeconds",
1239
+ "secondBanDeleteMessageSeconds",
1240
+ "thirdBanDeleteMessageSeconds",
1241
+ "firstDemoteRoleIds",
1242
+ "secondDemoteRoleIds",
1243
+ "thirdDemoteRoleIds"
1244
+ ]
1245
+ }
1246
+ ],
1247
+ "deleteStatuses": ["draft"],
1248
+ "actions": [
1249
+ {
1250
+ "id": "disable",
1251
+ "permissionId": "automod.rules.publish",
1252
+ "label": "Disable",
1253
+ "description": "Disable the owned Discord rule while preserving its mapping.",
1254
+ "style": "secondary",
1255
+ "confirmationPolicy": "required",
1256
+ "confirmation": "Disable this Helyx-owned Discord rule?",
1257
+ "statuses": ["published", "drifted"]
1258
+ },
1259
+ {
1260
+ "id": "adopt-discord",
1261
+ "permissionId": "automod.rules.reconcile",
1262
+ "label": "Adopt current Discord values",
1263
+ "description": "Replace native portions of the Helyx definition with the current owned Discord rule.",
1264
+ "style": "secondary",
1265
+ "confirmationPolicy": "required",
1266
+ "confirmation": "Adopt the current Discord values for this mapped rule?",
1267
+ "statuses": ["drifted"]
1268
+ },
1269
+ {
1270
+ "id": "restore-helyx",
1271
+ "permissionId": "automod.rules.reconcile",
1272
+ "label": "Restore Helyx values",
1273
+ "description": "Restore the published Helyx definition to the owned Discord rule.",
1274
+ "style": "primary",
1275
+ "confirmationPolicy": "required",
1276
+ "confirmation": "Overwrite the current owned Discord rule with the Helyx definition?",
1277
+ "statuses": ["drifted"]
1278
+ },
1279
+ {
1280
+ "id": "cleanup-owned-discord",
1281
+ "permissionId": "automod.rules.reconcile",
1282
+ "label": "Clean up owned Discord rule",
1283
+ "description": "Delete only the mapped Helyx-owned Discord rule and retain this unpublished rule record.",
1284
+ "style": "danger",
1285
+ "confirmationPolicy": "required",
1286
+ "confirmation": "Delete this mapped Helyx-owned Discord rule? Helyx never deletes unowned rules.",
1287
+ "statuses": ["disabled", "missing", "review"]
1288
+ }
1289
+ ],
1290
+ "import": {
1291
+ "label": "Import terms",
1292
+ "description": "Parse a bounded comma or newline-separated UTF-8 term list into a reviewable draft.",
1293
+ "inputFormat": "utf8_text",
1294
+ "maxBytes": 4000
1295
+ },
1296
+ "query": {
1297
+ "search": {
1298
+ "label": "Find rule",
1299
+ "placeholder": "Rule name"
1300
+ },
1301
+ "filters": [
1302
+ {
1303
+ "key": "status",
1304
+ "label": "Status",
1305
+ "type": "select",
1306
+ "options": [
1307
+ {
1308
+ "value": "draft",
1309
+ "label": "Draft"
1310
+ },
1311
+ {
1312
+ "value": "published",
1313
+ "label": "Published"
1314
+ },
1315
+ {
1316
+ "value": "disabled",
1317
+ "label": "Disabled"
1318
+ },
1319
+ {
1320
+ "value": "drifted",
1321
+ "label": "Drifted"
1322
+ },
1323
+ {
1324
+ "value": "missing",
1325
+ "label": "Missing"
1326
+ },
1327
+ {
1328
+ "value": "review",
1329
+ "label": "Review"
1330
+ }
1331
+ ]
1332
+ }
1333
+ ]
1334
+ }
1335
+ },
1336
+ {
1337
+ "id": "automod-activity",
1338
+ "name": "Activity",
1339
+ "singularName": "Activity receipt",
1340
+ "description": "Content-free structural receipts from owned Discord Auto Moderation rules.",
1341
+ "operations": ["list", "read"],
1342
+ "permissions": {
1343
+ "list": "automod.activity.read",
1344
+ "read": "automod.activity.read"
1345
+ },
1346
+ "tablePresentation": {
1347
+ "primaryColumnLabel": "Receipt",
1348
+ "showStatus": true,
1349
+ "showUpdated": true,
1350
+ "openAction": "primary",
1351
+ "responsive": "stacked"
1352
+ },
1353
+ "columns": [
1354
+ {
1355
+ "key": "ruleId",
1356
+ "label": "Rule",
1357
+ "format": "text"
1358
+ },
1359
+ {
1360
+ "key": "source",
1361
+ "label": "Source",
1362
+ "format": "text"
1363
+ },
1364
+ {
1365
+ "key": "action",
1366
+ "label": "Action",
1367
+ "format": "text"
1368
+ },
1369
+ {
1370
+ "key": "outcome",
1371
+ "label": "Outcome",
1372
+ "format": "text"
1373
+ },
1374
+ {
1375
+ "key": "caseId",
1376
+ "label": "Moderation case",
1377
+ "format": "text"
1378
+ }
1379
+ ],
1380
+ "fields": [
1381
+ {
1382
+ "key": "ruleId",
1383
+ "type": "string",
1384
+ "label": "Rule ID",
1385
+ "description": "Logical Helyx rule identifier.",
1386
+ "required": true,
1387
+ "supportVisibility": "value",
1388
+ "minLength": 1,
1389
+ "maxLength": 100
1390
+ },
1391
+ {
1392
+ "key": "ruleRevision",
1393
+ "type": "integer",
1394
+ "label": "Rule revision",
1395
+ "description": "Rule revision active for this event.",
1396
+ "required": true,
1397
+ "supportVisibility": "value",
1398
+ "minimum": 1,
1399
+ "maximum": 2147483647
1400
+ },
1401
+ {
1402
+ "key": "configurationRevision",
1403
+ "type": "string",
1404
+ "label": "Configuration revision",
1405
+ "description": "Configuration revision active for this event.",
1406
+ "required": true,
1407
+ "supportVisibility": "value",
1408
+ "minLength": 1,
1409
+ "maxLength": 200
1410
+ },
1411
+ {
1412
+ "key": "source",
1413
+ "type": "string",
1414
+ "label": "Detection source",
1415
+ "description": "Discord-native or Enhanced detection source.",
1416
+ "required": true,
1417
+ "supportVisibility": "value",
1418
+ "minLength": 1,
1419
+ "maxLength": 20
1420
+ },
1421
+ {
1422
+ "key": "action",
1423
+ "type": "string",
1424
+ "label": "Action",
1425
+ "description": "Structural detection or Discord action family.",
1426
+ "required": true,
1427
+ "supportVisibility": "value",
1428
+ "minLength": 1,
1429
+ "maxLength": 50
1430
+ },
1431
+ {
1432
+ "key": "detectionMethod",
1433
+ "type": "string",
1434
+ "label": "Detection method",
1435
+ "description": "Enhanced matching method when applicable.",
1436
+ "required": false,
1437
+ "supportVisibility": "value",
1438
+ "minLength": 0,
1439
+ "maxLength": 50
1440
+ },
1441
+ {
1442
+ "key": "confidenceClass",
1443
+ "type": "string",
1444
+ "label": "Confidence class",
1445
+ "description": "Bounded Enhanced confidence class when applicable.",
1446
+ "required": false,
1447
+ "supportVisibility": "value",
1448
+ "minLength": 0,
1449
+ "maxLength": 50
1450
+ },
1451
+ {
1452
+ "key": "confidenceScore",
1453
+ "type": "integer",
1454
+ "label": "Confidence score",
1455
+ "description": "Enhanced confidence score from 0 to 100.",
1456
+ "required": false,
1457
+ "supportVisibility": "value",
1458
+ "minimum": 0,
1459
+ "maximum": 100
1460
+ },
1461
+ {
1462
+ "key": "effectiveThreshold",
1463
+ "type": "integer",
1464
+ "label": "Effective threshold",
1465
+ "description": "Confidence threshold required for this detection.",
1466
+ "required": false,
1467
+ "supportVisibility": "value",
1468
+ "minimum": 60,
1469
+ "maximum": 100
1470
+ },
1471
+ {
1472
+ "key": "outcome",
1473
+ "type": "string",
1474
+ "label": "Outcome",
1475
+ "description": "Safe processing outcome.",
1476
+ "required": true,
1477
+ "supportVisibility": "value",
1478
+ "minLength": 1,
1479
+ "maxLength": 100
1480
+ },
1481
+ {
1482
+ "key": "safeCode",
1483
+ "type": "string",
1484
+ "label": "Processing reason",
1485
+ "description": "Automatic diagnostic code explaining a duplicate, skipped action or processing failure. Blank when no additional reason was recorded; this is not a safety or confidence rating.",
1486
+ "required": false,
1487
+ "supportVisibility": "value",
1488
+ "minLength": 0,
1489
+ "maxLength": 100
1490
+ },
1491
+ {
1492
+ "key": "moderationCaseId",
1493
+ "type": "string",
1494
+ "label": "Moderation case",
1495
+ "description": "Optional linked Moderation case ID.",
1496
+ "required": false,
1497
+ "supportVisibility": "value",
1498
+ "minLength": 0,
1499
+ "maxLength": 100
1500
+ },
1501
+ {
1502
+ "key": "occurredAt",
1503
+ "type": "string",
1504
+ "label": "Occurred at",
1505
+ "description": "Discord structural event time.",
1506
+ "required": true,
1507
+ "supportVisibility": "value",
1508
+ "minLength": 1,
1509
+ "maxLength": 50
1510
+ }
1511
+ ],
1512
+ "query": {
1513
+ "filters": [
1514
+ {
1515
+ "key": "ruleId",
1516
+ "label": "Rule ID",
1517
+ "type": "string",
1518
+ "placeholder": "Exact logical rule ID"
1519
+ },
1520
+ {
1521
+ "key": "outcome",
1522
+ "label": "Outcome",
1523
+ "type": "select",
1524
+ "options": [
1525
+ {
1526
+ "value": "applied",
1527
+ "label": "Applied"
1528
+ },
1529
+ {
1530
+ "value": "already_applied",
1531
+ "label": "Already applied"
1532
+ },
1533
+ {
1534
+ "value": "native_only",
1535
+ "label": "Native only"
1536
+ },
1537
+ {
1538
+ "value": "rejected",
1539
+ "label": "Rejected"
1540
+ },
1541
+ {
1542
+ "value": "failed",
1543
+ "label": "Failed"
1544
+ },
1545
+ {
1546
+ "value": "ambiguous",
1547
+ "label": "Ambiguous"
1548
+ },
1549
+ {
1550
+ "value": "review_required",
1551
+ "label": "Review required"
1552
+ }
1553
+ ]
1554
+ }
1555
+ ]
1556
+ }
1557
+ }
1558
+ ],
1559
+ "workspace": {
1560
+ "type": "managed_resources_v1",
1561
+ "tabs": [
1562
+ {
1563
+ "id": "rules",
1564
+ "label": "Rules",
1565
+ "panels": [
1566
+ {
1567
+ "resources": ["automod-rules"]
1568
+ }
1569
+ ]
1570
+ },
1571
+ {
1572
+ "id": "activity",
1573
+ "label": "Activity",
1574
+ "panels": [
1575
+ {
1576
+ "resources": ["automod-activity"],
1577
+ "mode": "list-only"
1578
+ }
1579
+ ]
1580
+ }
1581
+ ]
1582
+ }
1583
+ },
1584
+ "contributions": {
1585
+ "commands": [],
1586
+ "events": [
1587
+ "discord.auto-moderation-rule-create",
1588
+ "discord.auto-moderation-rule-update",
1589
+ "discord.auto-moderation-rule-delete",
1590
+ "discord.auto-moderation-action-execution",
1591
+ "discord.privileged-message-create",
1592
+ "discord.privileged-message-update"
1593
+ ],
1594
+ "logs": [
1595
+ "automod-rule-triggered",
1596
+ "automod-rule-drifted",
1597
+ "automod-enhanced-detected",
1598
+ "automod-enforcement-failed"
1599
+ ]
1600
+ }
1601
+ }