@atproto/ozone 0.1.52 → 0.1.54

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 (142) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +6 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/queryStatuses.d.ts.map +1 -1
  6. package/dist/api/moderation/queryStatuses.js +6 -1
  7. package/dist/api/moderation/queryStatuses.js.map +1 -1
  8. package/dist/api/setting/listOptions.d.ts +4 -0
  9. package/dist/api/setting/listOptions.d.ts.map +1 -0
  10. package/dist/api/setting/listOptions.js +38 -0
  11. package/dist/api/setting/listOptions.js.map +1 -0
  12. package/dist/api/setting/removeOptions.d.ts +4 -0
  13. package/dist/api/setting/removeOptions.d.ts.map +1 -0
  14. package/dist/api/setting/removeOptions.js +55 -0
  15. package/dist/api/setting/removeOptions.js.map +1 -0
  16. package/dist/api/setting/upsertOption.d.ts +4 -0
  17. package/dist/api/setting/upsertOption.d.ts.map +1 -0
  18. package/dist/api/setting/upsertOption.js +109 -0
  19. package/dist/api/setting/upsertOption.js.map +1 -0
  20. package/dist/api/util.d.ts.map +1 -1
  21. package/dist/api/util.js +3 -1
  22. package/dist/api/util.js.map +1 -1
  23. package/dist/context.d.ts +3 -0
  24. package/dist/context.d.ts.map +1 -1
  25. package/dist/context.js +6 -0
  26. package/dist/context.js.map +1 -1
  27. package/dist/db/migrations/20241018T205730722Z-setting.d.ts +4 -0
  28. package/dist/db/migrations/20241018T205730722Z-setting.d.ts.map +1 -0
  29. package/dist/db/migrations/20241018T205730722Z-setting.js +26 -0
  30. package/dist/db/migrations/20241018T205730722Z-setting.js.map +1 -0
  31. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.d.ts +4 -0
  32. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.d.ts.map +1 -0
  33. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.js +57 -0
  34. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.js.map +1 -0
  35. package/dist/db/migrations/index.d.ts +2 -0
  36. package/dist/db/migrations/index.d.ts.map +1 -1
  37. package/dist/db/migrations/index.js +3 -1
  38. package/dist/db/migrations/index.js.map +1 -1
  39. package/dist/db/schema/index.d.ts +2 -1
  40. package/dist/db/schema/index.d.ts.map +1 -1
  41. package/dist/db/schema/moderation_event.d.ts +1 -1
  42. package/dist/db/schema/moderation_event.d.ts.map +1 -1
  43. package/dist/db/schema/moderation_subject_status.d.ts +6 -0
  44. package/dist/db/schema/moderation_subject_status.d.ts.map +1 -1
  45. package/dist/db/schema/setting.d.ts +21 -0
  46. package/dist/db/schema/setting.d.ts.map +1 -0
  47. package/dist/db/schema/setting.js +5 -0
  48. package/dist/db/schema/setting.js.map +1 -0
  49. package/dist/lexicon/index.d.ts +13 -0
  50. package/dist/lexicon/index.d.ts.map +1 -1
  51. package/dist/lexicon/index.js +36 -1
  52. package/dist/lexicon/index.js.map +1 -1
  53. package/dist/lexicon/lexicons.d.ts +396 -0
  54. package/dist/lexicon/lexicons.d.ts.map +1 -1
  55. package/dist/lexicon/lexicons.js +439 -3
  56. package/dist/lexicon/lexicons.js.map +1 -1
  57. package/dist/lexicon/types/app/bsky/unspecced/getConfig.d.ts +34 -0
  58. package/dist/lexicon/types/app/bsky/unspecced/getConfig.d.ts.map +1 -0
  59. package/dist/lexicon/types/app/bsky/unspecced/getConfig.js +3 -0
  60. package/dist/lexicon/types/app/bsky/unspecced/getConfig.js.map +1 -0
  61. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +60 -2
  62. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  63. package/dist/lexicon/types/tools/ozone/moderation/defs.js +50 -0
  64. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  65. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
  66. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  67. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +10 -0
  68. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  69. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts +20 -0
  70. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts.map +1 -0
  71. package/dist/lexicon/types/tools/ozone/setting/defs.js +15 -0
  72. package/dist/lexicon/types/tools/ozone/setting/defs.js.map +1 -0
  73. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts +43 -0
  74. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts.map +1 -0
  75. package/dist/lexicon/types/tools/ozone/setting/listOptions.js +3 -0
  76. package/dist/lexicon/types/tools/ozone/setting/listOptions.js.map +1 -0
  77. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts +40 -0
  78. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts.map +1 -0
  79. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js +3 -0
  80. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js.map +1 -0
  81. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts +45 -0
  82. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts.map +1 -0
  83. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js +3 -0
  84. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js.map +1 -0
  85. package/dist/mod-service/index.d.ts +19 -2
  86. package/dist/mod-service/index.d.ts.map +1 -1
  87. package/dist/mod-service/index.js +37 -1
  88. package/dist/mod-service/index.js.map +1 -1
  89. package/dist/mod-service/status.d.ts +2 -22
  90. package/dist/mod-service/status.d.ts.map +1 -1
  91. package/dist/mod-service/status.js +91 -1
  92. package/dist/mod-service/status.js.map +1 -1
  93. package/dist/mod-service/types.d.ts +19 -1
  94. package/dist/mod-service/types.d.ts.map +1 -1
  95. package/dist/mod-service/views.d.ts.map +1 -1
  96. package/dist/mod-service/views.js +36 -1
  97. package/dist/mod-service/views.js.map +1 -1
  98. package/dist/setting/service.d.ts +33 -0
  99. package/dist/setting/service.d.ts.map +1 -0
  100. package/dist/setting/service.js +101 -0
  101. package/dist/setting/service.js.map +1 -0
  102. package/package.json +6 -6
  103. package/src/api/index.ts +6 -0
  104. package/src/api/moderation/queryStatuses.ts +10 -0
  105. package/src/api/setting/listOptions.ts +44 -0
  106. package/src/api/setting/removeOptions.ts +63 -0
  107. package/src/api/setting/upsertOption.ts +142 -0
  108. package/src/api/util.ts +3 -0
  109. package/src/context.ts +8 -0
  110. package/src/db/migrations/20241018T205730722Z-setting.ts +27 -0
  111. package/src/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.ts +57 -0
  112. package/src/db/migrations/index.ts +2 -0
  113. package/src/db/schema/index.ts +3 -1
  114. package/src/db/schema/moderation_event.ts +3 -0
  115. package/src/db/schema/moderation_subject_status.ts +6 -0
  116. package/src/db/schema/setting.ts +24 -0
  117. package/src/lexicon/index.ts +58 -0
  118. package/src/lexicon/lexicons.ts +449 -3
  119. package/src/lexicon/types/app/bsky/unspecced/getConfig.ts +43 -0
  120. package/src/lexicon/types/tools/ozone/moderation/defs.ts +132 -0
  121. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +3 -0
  122. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +10 -0
  123. package/src/lexicon/types/tools/ozone/setting/defs.ts +37 -0
  124. package/src/lexicon/types/tools/ozone/setting/listOptions.ts +53 -0
  125. package/src/lexicon/types/tools/ozone/setting/removeOptions.ts +49 -0
  126. package/src/lexicon/types/tools/ozone/setting/upsertOption.ts +58 -0
  127. package/src/mod-service/index.ts +52 -0
  128. package/src/mod-service/status.ts +114 -2
  129. package/src/mod-service/types.ts +25 -0
  130. package/src/mod-service/views.ts +45 -2
  131. package/src/setting/service.ts +148 -0
  132. package/tests/__snapshots__/get-record.test.ts.snap +8 -0
  133. package/tests/__snapshots__/get-records.test.ts.snap +4 -0
  134. package/tests/__snapshots__/get-repo.test.ts.snap +4 -0
  135. package/tests/__snapshots__/get-repos.test.ts.snap +4 -0
  136. package/tests/__snapshots__/moderation-events.test.ts.snap +4 -0
  137. package/tests/__snapshots__/moderation-statuses.test.ts.snap +24 -0
  138. package/tests/__snapshots__/settings.test.ts.snap +52 -0
  139. package/tests/record-and-account-events.test.ts +185 -0
  140. package/tests/settings.test.ts +310 -0
  141. package/tsconfig.build.tsbuildinfo +1 -1
  142. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -30,6 +30,9 @@ export interface ModEventView {
30
30
  | ModEventResolveAppeal
31
31
  | ModEventDivert
32
32
  | ModEventTag
33
+ | AccountEvent
34
+ | IdentityEvent
35
+ | RecordEvent
33
36
  | { $type: string; [k: string]: unknown }
34
37
  subject:
35
38
  | ComAtprotoAdminDefs.RepoRef
@@ -74,6 +77,9 @@ export interface ModEventViewDetail {
74
77
  | ModEventResolveAppeal
75
78
  | ModEventDivert
76
79
  | ModEventTag
80
+ | AccountEvent
81
+ | IdentityEvent
82
+ | RecordEvent
77
83
  | { $type: string; [k: string]: unknown }
78
84
  subject:
79
85
  | RepoView
@@ -105,6 +111,10 @@ export interface SubjectStatusView {
105
111
  | ComAtprotoAdminDefs.RepoRef
106
112
  | ComAtprotoRepoStrongRef.Main
107
113
  | { $type: string; [k: string]: unknown }
114
+ hosting?:
115
+ | AccountHosting
116
+ | RecordHosting
117
+ | { $type: string; [k: string]: unknown }
108
118
  subjectBlobCids?: string[]
109
119
  subjectRepoHandle?: string
110
120
  /** Timestamp referencing when the last update was made to the moderation status of the subject */
@@ -472,6 +482,78 @@ export function validateModEventTag(v: unknown): ValidationResult {
472
482
  return lexicons.validate('tools.ozone.moderation.defs#modEventTag', v)
473
483
  }
474
484
 
485
+ /** Logs account status related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */
486
+ export interface AccountEvent {
487
+ comment?: string
488
+ /** Indicates that the account has a repository which can be fetched from the host that emitted this event. */
489
+ active: boolean
490
+ status?:
491
+ | 'unknown'
492
+ | 'deactivated'
493
+ | 'deleted'
494
+ | 'takendown'
495
+ | 'suspended'
496
+ | 'tombstoned'
497
+ | (string & {})
498
+ timestamp: string
499
+ [k: string]: unknown
500
+ }
501
+
502
+ export function isAccountEvent(v: unknown): v is AccountEvent {
503
+ return (
504
+ isObj(v) &&
505
+ hasProp(v, '$type') &&
506
+ v.$type === 'tools.ozone.moderation.defs#accountEvent'
507
+ )
508
+ }
509
+
510
+ export function validateAccountEvent(v: unknown): ValidationResult {
511
+ return lexicons.validate('tools.ozone.moderation.defs#accountEvent', v)
512
+ }
513
+
514
+ /** Logs identity related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */
515
+ export interface IdentityEvent {
516
+ comment?: string
517
+ handle?: string
518
+ pdsHost?: string
519
+ tombstone?: boolean
520
+ timestamp: string
521
+ [k: string]: unknown
522
+ }
523
+
524
+ export function isIdentityEvent(v: unknown): v is IdentityEvent {
525
+ return (
526
+ isObj(v) &&
527
+ hasProp(v, '$type') &&
528
+ v.$type === 'tools.ozone.moderation.defs#identityEvent'
529
+ )
530
+ }
531
+
532
+ export function validateIdentityEvent(v: unknown): ValidationResult {
533
+ return lexicons.validate('tools.ozone.moderation.defs#identityEvent', v)
534
+ }
535
+
536
+ /** Logs lifecycle event on a record subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */
537
+ export interface RecordEvent {
538
+ comment?: string
539
+ op: 'create' | 'update' | 'delete' | (string & {})
540
+ cid?: string
541
+ timestamp: string
542
+ [k: string]: unknown
543
+ }
544
+
545
+ export function isRecordEvent(v: unknown): v is RecordEvent {
546
+ return (
547
+ isObj(v) &&
548
+ hasProp(v, '$type') &&
549
+ v.$type === 'tools.ozone.moderation.defs#recordEvent'
550
+ )
551
+ }
552
+
553
+ export function validateRecordEvent(v: unknown): ValidationResult {
554
+ return lexicons.validate('tools.ozone.moderation.defs#recordEvent', v)
555
+ }
556
+
475
557
  export interface RepoView {
476
558
  did: string
477
559
  handle: string
@@ -483,6 +565,7 @@ export interface RepoView {
483
565
  invitesDisabled?: boolean
484
566
  inviteNote?: string
485
567
  deactivatedAt?: string
568
+ threatSignatures?: ComAtprotoAdminDefs.ThreatSignature[]
486
569
  [k: string]: unknown
487
570
  }
488
571
 
@@ -512,6 +595,7 @@ export interface RepoViewDetail {
512
595
  inviteNote?: string
513
596
  emailConfirmedAt?: string
514
597
  deactivatedAt?: string
598
+ threatSignatures?: ComAtprotoAdminDefs.ThreatSignature[]
515
599
  [k: string]: unknown
516
600
  }
517
601
 
@@ -703,3 +787,51 @@ export function isVideoDetails(v: unknown): v is VideoDetails {
703
787
  export function validateVideoDetails(v: unknown): ValidationResult {
704
788
  return lexicons.validate('tools.ozone.moderation.defs#videoDetails', v)
705
789
  }
790
+
791
+ export interface AccountHosting {
792
+ status:
793
+ | 'takendown'
794
+ | 'suspended'
795
+ | 'deleted'
796
+ | 'deactivated'
797
+ | 'unknown'
798
+ | (string & {})
799
+ updatedAt?: string
800
+ createdAt?: string
801
+ deletedAt?: string
802
+ deactivatedAt?: string
803
+ reactivatedAt?: string
804
+ [k: string]: unknown
805
+ }
806
+
807
+ export function isAccountHosting(v: unknown): v is AccountHosting {
808
+ return (
809
+ isObj(v) &&
810
+ hasProp(v, '$type') &&
811
+ v.$type === 'tools.ozone.moderation.defs#accountHosting'
812
+ )
813
+ }
814
+
815
+ export function validateAccountHosting(v: unknown): ValidationResult {
816
+ return lexicons.validate('tools.ozone.moderation.defs#accountHosting', v)
817
+ }
818
+
819
+ export interface RecordHosting {
820
+ status: 'deleted' | 'unknown' | (string & {})
821
+ updatedAt?: string
822
+ createdAt?: string
823
+ deletedAt?: string
824
+ [k: string]: unknown
825
+ }
826
+
827
+ export function isRecordHosting(v: unknown): v is RecordHosting {
828
+ return (
829
+ isObj(v) &&
830
+ hasProp(v, '$type') &&
831
+ v.$type === 'tools.ozone.moderation.defs#recordHosting'
832
+ )
833
+ }
834
+
835
+ export function validateRecordHosting(v: unknown): ValidationResult {
836
+ return lexicons.validate('tools.ozone.moderation.defs#recordHosting', v)
837
+ }
@@ -29,6 +29,9 @@ export interface InputSchema {
29
29
  | ToolsOzoneModerationDefs.ModEventResolveAppeal
30
30
  | ToolsOzoneModerationDefs.ModEventEmail
31
31
  | ToolsOzoneModerationDefs.ModEventTag
32
+ | ToolsOzoneModerationDefs.AccountEvent
33
+ | ToolsOzoneModerationDefs.IdentityEvent
34
+ | ToolsOzoneModerationDefs.RecordEvent
32
35
  | { $type: string; [k: string]: unknown }
33
36
  subject:
34
37
  | ComAtprotoAdminDefs.RepoRef
@@ -22,6 +22,16 @@ export interface QueryParams {
22
22
  reportedBefore?: string
23
23
  /** Search subjects reviewed after a given timestamp */
24
24
  reviewedAfter?: string
25
+ /** Search subjects where the associated record/account was deleted after a given timestamp */
26
+ hostingDeletedAfter?: string
27
+ /** Search subjects where the associated record/account was deleted before a given timestamp */
28
+ hostingDeletedBefore?: string
29
+ /** Search subjects where the associated record/account was updated after a given timestamp */
30
+ hostingUpdatedAfter?: string
31
+ /** Search subjects where the associated record/account was updated before a given timestamp */
32
+ hostingUpdatedBefore?: string
33
+ /** Search subjects by the status of the associated record/account */
34
+ hostingStatuses?: string[]
25
35
  /** Search subjects reviewed before a given timestamp */
26
36
  reviewedBefore?: string
27
37
  /** By default, we don't include muted subjects in the results. Set this to true to include them. */
@@ -0,0 +1,37 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
5
+ import { lexicons } from '../../../../lexicons'
6
+ import { isObj, hasProp } from '../../../../util'
7
+ import { CID } from 'multiformats/cid'
8
+
9
+ export interface Option {
10
+ key: string
11
+ did: string
12
+ value: {}
13
+ description?: string
14
+ createdAt?: string
15
+ updatedAt?: string
16
+ managerRole?:
17
+ | 'tools.ozone.team.defs#roleModerator'
18
+ | 'tools.ozone.team.defs#roleTriage'
19
+ | 'tools.ozone.team.defs#roleAdmin'
20
+ | (string & {})
21
+ scope: 'instance' | 'personal' | (string & {})
22
+ createdBy: string
23
+ lastUpdatedBy: string
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export function isOption(v: unknown): v is Option {
28
+ return (
29
+ isObj(v) &&
30
+ hasProp(v, '$type') &&
31
+ v.$type === 'tools.ozone.setting.defs#option'
32
+ )
33
+ }
34
+
35
+ export function validateOption(v: unknown): ValidationResult {
36
+ return lexicons.validate('tools.ozone.setting.defs#option', v)
37
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneSettingDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ limit: number
14
+ cursor?: string
15
+ scope: 'instance' | 'personal' | (string & {})
16
+ /** Filter keys by prefix */
17
+ prefix?: string
18
+ /** Filter for only the specified keys. Ignored if prefix is provided */
19
+ keys?: string[]
20
+ }
21
+
22
+ export type InputSchema = undefined
23
+
24
+ export interface OutputSchema {
25
+ cursor?: string
26
+ options: ToolsOzoneSettingDefs.Option[]
27
+ [k: string]: unknown
28
+ }
29
+
30
+ export type HandlerInput = undefined
31
+
32
+ export interface HandlerSuccess {
33
+ encoding: 'application/json'
34
+ body: OutputSchema
35
+ headers?: { [key: string]: string }
36
+ }
37
+
38
+ export interface HandlerError {
39
+ status: number
40
+ message?: string
41
+ }
42
+
43
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
44
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
45
+ auth: HA
46
+ params: QueryParams
47
+ input: HandlerInput
48
+ req: express.Request
49
+ res: express.Response
50
+ }
51
+ export type Handler<HA extends HandlerAuth = never> = (
52
+ ctx: HandlerReqCtx<HA>,
53
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,49 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+
11
+ export interface QueryParams {}
12
+
13
+ export interface InputSchema {
14
+ keys: string[]
15
+ scope: 'instance' | 'personal' | (string & {})
16
+ [k: string]: unknown
17
+ }
18
+
19
+ export interface OutputSchema {
20
+ [k: string]: unknown
21
+ }
22
+
23
+ export interface HandlerInput {
24
+ encoding: 'application/json'
25
+ body: InputSchema
26
+ }
27
+
28
+ export interface HandlerSuccess {
29
+ encoding: 'application/json'
30
+ body: OutputSchema
31
+ headers?: { [key: string]: string }
32
+ }
33
+
34
+ export interface HandlerError {
35
+ status: number
36
+ message?: string
37
+ }
38
+
39
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
40
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
41
+ auth: HA
42
+ params: QueryParams
43
+ input: HandlerInput
44
+ req: express.Request
45
+ res: express.Response
46
+ }
47
+ export type Handler<HA extends HandlerAuth = never> = (
48
+ ctx: HandlerReqCtx<HA>,
49
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,58 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneSettingDefs from './defs'
11
+
12
+ export interface QueryParams {}
13
+
14
+ export interface InputSchema {
15
+ key: string
16
+ scope: 'instance' | 'personal' | (string & {})
17
+ value: {}
18
+ description?: string
19
+ managerRole?:
20
+ | 'tools.ozone.team.defs#roleModerator'
21
+ | 'tools.ozone.team.defs#roleTriage'
22
+ | 'tools.ozone.team.defs#roleAdmin'
23
+ | (string & {})
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export interface OutputSchema {
28
+ option: ToolsOzoneSettingDefs.Option
29
+ [k: string]: unknown
30
+ }
31
+
32
+ export interface HandlerInput {
33
+ encoding: 'application/json'
34
+ body: InputSchema
35
+ }
36
+
37
+ export interface HandlerSuccess {
38
+ encoding: 'application/json'
39
+ body: OutputSchema
40
+ headers?: { [key: string]: string }
41
+ }
42
+
43
+ export interface HandlerError {
44
+ status: number
45
+ message?: string
46
+ }
47
+
48
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
49
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
50
+ auth: HA
51
+ params: QueryParams
52
+ input: HandlerInput
53
+ req: express.Request
54
+ res: express.Response
55
+ }
56
+ export type Handler<HA extends HandlerAuth = never> = (
57
+ ctx: HandlerReqCtx<HA>,
58
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -18,6 +18,9 @@ import {
18
18
  isModEventTakedown,
19
19
  isModEventEmail,
20
20
  isModEventTag,
21
+ isAccountEvent,
22
+ isIdentityEvent,
23
+ isRecordEvent,
21
24
  REVIEWESCALATED,
22
25
  REVIEWOPEN,
23
26
  } from '../lexicon/types/tools/ozone/moderation/defs'
@@ -386,6 +389,25 @@ export class ModerationService {
386
389
  }
387
390
  }
388
391
 
392
+ if (isAccountEvent(event)) {
393
+ meta.active = event.active
394
+ meta.timestamp = event.timestamp
395
+ if (event.status) meta.status = event.status
396
+ }
397
+
398
+ if (isIdentityEvent(event)) {
399
+ meta.timestamp = event.timestamp
400
+ if (event.handle) meta.handle = event.handle
401
+ if (event.pdsHost) meta.pdsHost = event.pdsHost
402
+ if (event.tombstone) meta.tombstone = event.tombstone
403
+ }
404
+
405
+ if (isRecordEvent(event)) {
406
+ meta.timestamp = event.timestamp
407
+ meta.op = event.op
408
+ if (event.cid) meta.cid = event.cid
409
+ }
410
+
389
411
  if (isModEventTakedown(event) && event.acknowledgeAccountSubjects) {
390
412
  meta.acknowledgeAccountSubjects = true
391
413
  }
@@ -758,6 +780,11 @@ export class ModerationService {
758
780
  reportedAfter,
759
781
  reportedBefore,
760
782
  includeMuted,
783
+ hostingDeletedBefore,
784
+ hostingDeletedAfter,
785
+ hostingUpdatedBefore,
786
+ hostingUpdatedAfter,
787
+ hostingStatuses,
761
788
  onlyMuted,
762
789
  ignoreSubjects,
763
790
  sortDirection,
@@ -780,6 +807,11 @@ export class ModerationService {
780
807
  reportedAfter?: string
781
808
  reportedBefore?: string
782
809
  includeMuted?: boolean
810
+ hostingDeletedBefore?: string
811
+ hostingDeletedAfter?: string
812
+ hostingUpdatedBefore?: string
813
+ hostingUpdatedAfter?: string
814
+ hostingStatuses?: string[]
783
815
  onlyMuted?: boolean
784
816
  subject?: string
785
817
  ignoreSubjects?: string[]
@@ -847,6 +879,26 @@ export class ModerationService {
847
879
  builder = builder.where('lastReviewedAt', '<', reviewedBefore)
848
880
  }
849
881
 
882
+ if (hostingUpdatedAfter) {
883
+ builder = builder.where('hostingUpdatedAt', '>', hostingUpdatedAfter)
884
+ }
885
+
886
+ if (hostingUpdatedBefore) {
887
+ builder = builder.where('hostingUpdatedAt', '<', hostingUpdatedBefore)
888
+ }
889
+
890
+ if (hostingDeletedAfter) {
891
+ builder = builder.where('hostingDeletedAt', '>', hostingDeletedAfter)
892
+ }
893
+
894
+ if (hostingDeletedBefore) {
895
+ builder = builder.where('hostingDeletedAt', '<', hostingDeletedBefore)
896
+ }
897
+
898
+ if (hostingStatuses?.length) {
899
+ builder = builder.where('hostingStatus', 'in', hostingStatuses)
900
+ }
901
+
850
902
  if (reportedAfter) {
851
903
  builder = builder.where('lastReviewedAt', '>', reportedAfter)
852
904
  }
@@ -126,6 +126,83 @@ const getSubjectStatusForModerationEvent = ({
126
126
  }
127
127
  }
128
128
 
129
+ const hostingEvents = [
130
+ 'tools.ozone.moderation.defs#accountEvent',
131
+ 'tools.ozone.moderation.defs#identityEvent',
132
+ 'tools.ozone.moderation.defs#recordEvent',
133
+ ]
134
+
135
+ const getSubjectStatusForRecordEvent = ({
136
+ event,
137
+ currentStatus,
138
+ }: {
139
+ event: ModerationEventRow
140
+ currentStatus?: ModerationSubjectStatusRow
141
+ }): Partial<ModerationSubjectStatusRow> => {
142
+ const timestamp =
143
+ typeof event.meta?.timestamp === 'string'
144
+ ? event.meta?.timestamp
145
+ : event.createdAt
146
+
147
+ if (event.action === 'tools.ozone.moderation.defs#recordEvent') {
148
+ if (event.meta?.op === 'delete') {
149
+ return {
150
+ hostingStatus: 'deleted',
151
+ hostingDeletedAt: timestamp,
152
+ }
153
+ } else if (event.meta?.op === 'update') {
154
+ return {
155
+ hostingStatus: 'active',
156
+ hostingUpdatedAt: timestamp,
157
+ }
158
+ }
159
+ return {}
160
+ }
161
+
162
+ if (event.action === 'tools.ozone.moderation.defs#accountEvent') {
163
+ const status: Partial<ModerationSubjectStatusRow> = {
164
+ hostingUpdatedAt: timestamp,
165
+ }
166
+
167
+ if (event.meta?.status) {
168
+ status.hostingStatus = `${event.meta?.status}`
169
+ }
170
+
171
+ if (event.meta?.status === 'deleted') {
172
+ status.hostingDeletedAt = timestamp
173
+ } else if (event.meta?.status === 'deactivated') {
174
+ status.hostingDeactivatedAt = timestamp
175
+ } else {
176
+ // When deactivated accounts are re-activated, we receive the event with just the active flag set to true
177
+ // so we want to make sure that the hostingStatus is not set to an outdated value
178
+ if (
179
+ currentStatus?.hostingStatus === 'deactivated' &&
180
+ event.meta?.active
181
+ ) {
182
+ status.hostingStatus = 'active'
183
+ status.hostingReactivatedAt = timestamp
184
+ }
185
+ }
186
+
187
+ return status
188
+ }
189
+
190
+ if (event.action === 'tools.ozone.moderation.defs#identityEvent') {
191
+ const status: Partial<ModerationSubjectStatusRow> = {
192
+ hostingUpdatedAt: timestamp,
193
+ }
194
+
195
+ if (event.meta?.tombstone) {
196
+ status.hostingStatus = 'tombstoned'
197
+ status.hostingDeletedAt = timestamp
198
+ }
199
+
200
+ return status
201
+ }
202
+
203
+ return {}
204
+ }
205
+
129
206
  // Based on a given moderation action event, this function will update the moderation status of the subject
130
207
  // If there's no existing status, it will create one
131
208
  // If the action event does not affect the status, it will do nothing
@@ -133,7 +210,7 @@ export const adjustModerationSubjectStatus = async (
133
210
  db: Database,
134
211
  moderationEvent: ModerationEventRow,
135
212
  blobCids?: string[],
136
- ) => {
213
+ ): Promise<ModerationSubjectStatusRow | null> => {
137
214
  const {
138
215
  action,
139
216
  subjectDid,
@@ -152,6 +229,7 @@ export const adjustModerationSubjectStatus = async (
152
229
 
153
230
  db.assertTransaction()
154
231
 
232
+ const now = new Date().toISOString()
155
233
  const currentStatus = await db.db
156
234
  .selectFrom('moderation_subject_status')
157
235
  .where('did', '=', identifier.did)
@@ -161,6 +239,41 @@ export const adjustModerationSubjectStatus = async (
161
239
  .selectAll()
162
240
  .executeTakeFirst()
163
241
 
242
+ if (hostingEvents.includes(action)) {
243
+ const newStatus = getSubjectStatusForRecordEvent({
244
+ event: moderationEvent,
245
+ currentStatus,
246
+ })
247
+ if (!Object.keys(newStatus).length) {
248
+ return currentStatus || null
249
+ }
250
+
251
+ const status = await db.db
252
+ .insertInto('moderation_subject_status')
253
+ .values({
254
+ ...identifier,
255
+ ...newStatus,
256
+ // newStatus doesn't contain a reviewState or takendown so in case this is a new entry
257
+ // we need to set a default values so that the insert doesn't fail
258
+ reviewState: currentStatus ? currentStatus.reviewState : REVIEWNONE,
259
+ // @TODO: should we try to update this based on status property of account event?
260
+ // For now we're the only one emitting takedowns so i don't think it makes too much of a difference
261
+ takendown: currentStatus ? currentStatus.takendown : false,
262
+ createdAt: now,
263
+ updatedAt: now,
264
+ })
265
+ .onConflict((oc) =>
266
+ oc.constraint('moderation_status_unique_idx').doUpdateSet({
267
+ ...newStatus,
268
+ updatedAt: now,
269
+ }),
270
+ )
271
+ .returningAll()
272
+ .executeTakeFirst()
273
+
274
+ return status || null
275
+ }
276
+
164
277
  // If reporting is muted for this reporter, we don't want to update the subject status
165
278
  if (meta?.isReporterMuted) {
166
279
  return currentStatus || null
@@ -178,7 +291,6 @@ export const adjustModerationSubjectStatus = async (
178
291
  durationInHours: moderationEvent.durationInHours,
179
292
  })
180
293
 
181
- const now = new Date().toISOString()
182
294
  if (
183
295
  currentStatus?.reviewState === REVIEWESCALATED &&
184
296
  subjectStatus.reviewState !== REVIEWCLOSED
@@ -31,3 +31,28 @@ export type ModEventType =
31
31
  | ToolsOzoneModerationDefs.ModEventMute
32
32
  | ToolsOzoneModerationDefs.ModEventReverseTakedown
33
33
  | ToolsOzoneModerationDefs.ModEventTag
34
+ | ToolsOzoneModerationDefs.AccountEvent
35
+ | ToolsOzoneModerationDefs.IdentityEvent
36
+ | ToolsOzoneModerationDefs.RecordEvent
37
+
38
+ type AccountHostingView = {
39
+ $type: 'tools.ozone.moderation.defs#accountHosting'
40
+ status: 'active' | 'takendown' | 'suspended' | 'deleted' | 'deactivated'
41
+ createdAt?: Date
42
+ updatedAt?: Date
43
+ deletedAt?: Date
44
+ deactivatedAt?: Date
45
+ reactivatedAt?: Date
46
+ }
47
+
48
+ type RecordHostingView = {
49
+ $type: 'tools.ozone.moderation.defs#recordHosting'
50
+ status: 'active' | 'deleted'
51
+ createdAt?: Date
52
+ updatedAt?: Date
53
+ deletedAt?: Date
54
+ }
55
+
56
+ export type ModerationSubjectHostingView =
57
+ | AccountHostingView
58
+ | RecordHostingView