@atproto/ozone 0.1.68 → 0.1.70

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 (135) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/api/moderation/queryStatuses.d.ts.map +1 -1
  3. package/dist/api/moderation/queryStatuses.js +1 -33
  4. package/dist/api/moderation/queryStatuses.js.map +1 -1
  5. package/dist/background.d.ts +49 -6
  6. package/dist/background.d.ts.map +1 -1
  7. package/dist/background.js +149 -14
  8. package/dist/background.js.map +1 -1
  9. package/dist/config/config.d.ts +1 -0
  10. package/dist/config/config.d.ts.map +1 -1
  11. package/dist/config/config.js +1 -0
  12. package/dist/config/config.js.map +1 -1
  13. package/dist/config/env.d.ts +1 -0
  14. package/dist/config/env.d.ts.map +1 -1
  15. package/dist/config/env.js +1 -0
  16. package/dist/config/env.js.map +1 -1
  17. package/dist/daemon/context.d.ts +9 -3
  18. package/dist/daemon/context.d.ts.map +1 -1
  19. package/dist/daemon/context.js +33 -3
  20. package/dist/daemon/context.js.map +1 -1
  21. package/dist/daemon/index.d.ts.map +1 -1
  22. package/dist/daemon/index.js +3 -6
  23. package/dist/daemon/index.js.map +1 -1
  24. package/dist/daemon/materialized-view-refresher.d.ts +5 -0
  25. package/dist/daemon/materialized-view-refresher.d.ts.map +1 -0
  26. package/dist/daemon/materialized-view-refresher.js +29 -0
  27. package/dist/daemon/materialized-view-refresher.js.map +1 -0
  28. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.d.ts +5 -0
  29. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.d.ts.map +1 -0
  30. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.js +158 -0
  31. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.js.map +1 -0
  32. package/dist/db/migrations/index.d.ts +1 -0
  33. package/dist/db/migrations/index.d.ts.map +1 -1
  34. package/dist/db/migrations/index.js +2 -1
  35. package/dist/db/migrations/index.js.map +1 -1
  36. package/dist/db/schema/account_events_stats.d.ts +15 -0
  37. package/dist/db/schema/account_events_stats.d.ts.map +1 -0
  38. package/dist/db/schema/account_events_stats.js +5 -0
  39. package/dist/db/schema/account_events_stats.js.map +1 -0
  40. package/dist/db/schema/account_record_events_stats.d.ts +15 -0
  41. package/dist/db/schema/account_record_events_stats.d.ts.map +1 -0
  42. package/dist/db/schema/account_record_events_stats.js +5 -0
  43. package/dist/db/schema/account_record_events_stats.js.map +1 -0
  44. package/dist/db/schema/account_record_status_stats.d.ts +15 -0
  45. package/dist/db/schema/account_record_status_stats.d.ts.map +1 -0
  46. package/dist/db/schema/account_record_status_stats.js +5 -0
  47. package/dist/db/schema/account_record_status_stats.js.map +1 -0
  48. package/dist/db/schema/index.d.ts +5 -1
  49. package/dist/db/schema/index.d.ts.map +1 -1
  50. package/dist/db/schema/record_events_stats.d.ts +14 -0
  51. package/dist/db/schema/record_events_stats.d.ts.map +1 -0
  52. package/dist/db/schema/record_events_stats.js +5 -0
  53. package/dist/db/schema/record_events_stats.js.map +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +1 -4
  56. package/dist/index.js.map +1 -1
  57. package/dist/lexicon/index.d.ts +2 -0
  58. package/dist/lexicon/index.d.ts.map +1 -1
  59. package/dist/lexicon/index.js +2 -0
  60. package/dist/lexicon/index.js.map +1 -1
  61. package/dist/lexicon/lexicons.d.ts +230 -2
  62. package/dist/lexicon/lexicons.d.ts.map +1 -1
  63. package/dist/lexicon/lexicons.js +126 -1
  64. package/dist/lexicon/lexicons.js.map +1 -1
  65. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +5 -0
  66. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  67. package/dist/lexicon/types/app/bsky/feed/defs.js +5 -1
  68. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  69. package/dist/lexicon/types/app/bsky/feed/generator.d.ts +1 -0
  70. package/dist/lexicon/types/app/bsky/feed/generator.d.ts.map +1 -1
  71. package/dist/lexicon/types/app/bsky/feed/generator.js.map +1 -1
  72. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +40 -0
  73. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  74. package/dist/lexicon/types/tools/ozone/moderation/defs.js +20 -0
  75. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  76. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +7 -1
  77. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  78. package/dist/mod-service/index.d.ts +4 -62
  79. package/dist/mod-service/index.d.ts.map +1 -1
  80. package/dist/mod-service/index.js +80 -74
  81. package/dist/mod-service/index.js.map +1 -1
  82. package/dist/mod-service/status.d.ts +115 -4
  83. package/dist/mod-service/status.d.ts.map +1 -1
  84. package/dist/mod-service/status.js +51 -34
  85. package/dist/mod-service/status.js.map +1 -1
  86. package/dist/mod-service/types.d.ts +16 -1
  87. package/dist/mod-service/types.d.ts.map +1 -1
  88. package/dist/mod-service/views.d.ts.map +1 -1
  89. package/dist/mod-service/views.js +49 -41
  90. package/dist/mod-service/views.js.map +1 -1
  91. package/dist/util.d.ts +34 -0
  92. package/dist/util.d.ts.map +1 -1
  93. package/dist/util.js +132 -0
  94. package/dist/util.js.map +1 -1
  95. package/package.json +3 -3
  96. package/src/api/moderation/queryStatuses.ts +1 -63
  97. package/src/background.ts +140 -14
  98. package/src/config/config.ts +2 -0
  99. package/src/config/env.ts +4 -0
  100. package/src/daemon/context.ts +43 -5
  101. package/src/daemon/index.ts +3 -6
  102. package/src/daemon/materialized-view-refresher.ts +27 -0
  103. package/src/db/migrations/20241220T144630860Z-stats-materialized-views.ts +218 -0
  104. package/src/db/migrations/index.ts +1 -0
  105. package/src/db/schema/account_events_stats.ts +16 -0
  106. package/src/db/schema/account_record_events_stats.ts +15 -0
  107. package/src/db/schema/account_record_status_stats.ts +15 -0
  108. package/src/db/schema/index.ts +10 -1
  109. package/src/db/schema/record_events_stats.ts +15 -0
  110. package/src/index.ts +1 -7
  111. package/src/lexicon/index.ts +2 -0
  112. package/src/lexicon/lexicons.ts +138 -1
  113. package/src/lexicon/types/app/bsky/feed/defs.ts +9 -0
  114. package/src/lexicon/types/app/bsky/feed/generator.ts +4 -0
  115. package/src/lexicon/types/tools/ozone/moderation/defs.ts +62 -0
  116. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +11 -1
  117. package/src/mod-service/index.ts +181 -118
  118. package/src/mod-service/status.ts +55 -28
  119. package/src/mod-service/types.ts +22 -1
  120. package/src/mod-service/views.ts +64 -50
  121. package/src/util.ts +145 -0
  122. package/tests/__snapshots__/get-record.test.ts.snap +28 -0
  123. package/tests/__snapshots__/get-records.test.ts.snap +14 -0
  124. package/tests/__snapshots__/get-repo.test.ts.snap +11 -0
  125. package/tests/__snapshots__/get-repos.test.ts.snap +11 -0
  126. package/tests/__snapshots__/moderation-events.test.ts.snap +19 -0
  127. package/tests/__snapshots__/moderation-statuses.test.ts.snap +114 -0
  128. package/tests/get-record.test.ts +4 -0
  129. package/tests/get-records.test.ts +4 -0
  130. package/tests/get-repo.test.ts +4 -0
  131. package/tests/get-repos.test.ts +4 -0
  132. package/tests/moderation-events.test.ts +4 -0
  133. package/tests/moderation-statuses.test.ts +4 -0
  134. package/tests/query-labels.test.ts +1 -0
  135. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,218 @@
1
+ import { Kysely, sql } from 'kysely'
2
+
3
+ import { REASONAPPEAL } from '../../lexicon/types/com/atproto/moderation/defs'
4
+ import { DatabaseSchemaType } from '../schema'
5
+
6
+ import {
7
+ REVIEWESCALATED,
8
+ REVIEWOPEN,
9
+ } from '../../lexicon/types/tools/ozone/moderation/defs'
10
+ import * as modEvent from '../schema/moderation_event'
11
+ import * as modStatus from '../schema/moderation_subject_status'
12
+ import * as recordEventsStats from '../schema/record_events_stats'
13
+
14
+ export async function up(db: Kysely<any>): Promise<void> {
15
+ // Used by "tools.ozone.moderation.queryStatuses". Reduces query cost by two
16
+ // order of magnitudes when sorting using "reportedRecordsCount" or
17
+ // "takendownRecordsCount" and filtering by "reviewState".
18
+ await db.schema
19
+ .createIndex('moderation_subject_status_did_id_review_state_idx')
20
+ .on('moderation_subject_status')
21
+ .column('did')
22
+ .expression(sql`"id" ASC NULLS FIRST`)
23
+ .column('reviewState')
24
+ .execute()
25
+
26
+ // ~6sec for 16M events
27
+ await db.schema
28
+ .createView('account_events_stats')
29
+ .materialized()
30
+ .ifNotExists()
31
+ .as(
32
+ (db as Kysely<modEvent.PartialDB>)
33
+ .selectFrom('moderation_event')
34
+ .where('subjectType', '=', 'com.atproto.admin.defs#repoRef')
35
+ .where('subjectUri', 'is', null)
36
+ .select('subjectDid')
37
+ .select([
38
+ (eb) =>
39
+ sql<number>`COUNT(*) FILTER(
40
+ WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventTakedown'
41
+ AND ${eb.ref('durationInHours')} IS NULL
42
+ )`.as('takedownCount'),
43
+ (eb) =>
44
+ sql<number>`COUNT(*) FILTER(
45
+ WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventTakedown'
46
+ AND ${eb.ref('durationInHours')} IS NOT NULL
47
+ )`.as('suspendCount'),
48
+ (eb) =>
49
+ sql<number>`COUNT(*) FILTER(
50
+ WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventEscalate'
51
+ )`.as('escalateCount'),
52
+ (eb) =>
53
+ sql<number>`COUNT(*) FILTER(
54
+ WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport'
55
+ AND ${eb.ref('meta')} ->> 'reportType' != ${REASONAPPEAL}
56
+ )`.as('reportCount'),
57
+ (eb) =>
58
+ sql<number>`COUNT(*) FILTER(
59
+ WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport'
60
+ AND ${eb.ref('meta')} ->> 'reportType' = ${REASONAPPEAL}
61
+ )`.as('appealCount'),
62
+ ])
63
+ .groupBy('subjectDid'),
64
+ )
65
+ .execute()
66
+
67
+ await db.schema
68
+ .createIndex('account_events_stats_did_idx')
69
+ .unique()
70
+ .on('account_events_stats')
71
+ .column('subjectDid')
72
+ .execute()
73
+
74
+ await db.schema
75
+ .createIndex('account_events_stats_suspend_count_idx')
76
+ .on('account_events_stats')
77
+ .expression(sql`"suspendCount" ASC NULLS FIRST`)
78
+ .column('subjectDid')
79
+ .execute()
80
+
81
+ // ~50sec for 16M events
82
+ await db.schema
83
+ .createView('record_events_stats')
84
+ .materialized()
85
+ .ifNotExists()
86
+ .as(
87
+ (db as Kysely<modEvent.PartialDB>)
88
+ .selectFrom('moderation_event')
89
+ .select([
90
+ 'subjectDid',
91
+ 'subjectUri',
92
+ (eb) =>
93
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventEscalate')`.as(
94
+ 'escalateCount',
95
+ ),
96
+ (eb) =>
97
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport' AND ${eb.ref('meta')} ->> 'reportType' != 'com.atproto.moderation.defs#reasonAppeal')`.as(
98
+ 'reportCount',
99
+ ),
100
+ (eb) =>
101
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport' AND ${eb.ref('meta')} ->> 'reportType' = 'com.atproto.moderation.defs#reasonAppeal')`.as(
102
+ 'appealCount',
103
+ ),
104
+ ])
105
+ .where('subjectType', '=', 'com.atproto.repo.strongRef')
106
+ .where('subjectUri', 'is not', null)
107
+ .groupBy(['subjectDid', 'subjectUri']),
108
+ )
109
+ .execute()
110
+
111
+ await db.schema
112
+ .createIndex('record_events_stats_uri_idx')
113
+ .unique()
114
+ .on('record_events_stats')
115
+ .column('subjectUri')
116
+ .execute()
117
+
118
+ await db.schema
119
+ .createIndex('record_events_stats_did_idx')
120
+ .on('record_events_stats')
121
+ .column('subjectDid')
122
+ .execute()
123
+
124
+ await db.schema
125
+ .createView('account_record_events_stats')
126
+ .materialized()
127
+ .ifNotExists()
128
+ .as(
129
+ (db as Kysely<recordEventsStats.PartialDB>)
130
+ .selectFrom('record_events_stats')
131
+ .select([
132
+ 'subjectDid',
133
+ (eb) =>
134
+ // Casting to "bigint" because "numeric" gets casted to a string
135
+ // by default by postgres-node.
136
+ sql<number>`SUM(${eb.ref('reportCount')})::bigint`.as(
137
+ 'totalReports',
138
+ ),
139
+ (eb) =>
140
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('reportCount')} > 0)`.as(
141
+ 'reportedCount',
142
+ ),
143
+ (eb) =>
144
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('escalateCount')} > 0)`.as(
145
+ 'escalatedCount',
146
+ ),
147
+ (eb) =>
148
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('appealCount')} > 0)`.as(
149
+ 'appealedCount',
150
+ ),
151
+ ])
152
+ .groupBy('subjectDid'),
153
+ )
154
+ .execute()
155
+
156
+ await db.schema
157
+ .createIndex('account_record_events_stats_did_idx')
158
+ .unique()
159
+ .on('account_record_events_stats')
160
+ .column('subjectDid')
161
+ .execute()
162
+
163
+ await db.schema
164
+ .createIndex('account_record_events_stats_reported_count_idx')
165
+ .on('account_record_events_stats')
166
+ .expression(sql`"reportedCount" ASC NULLS FIRST`)
167
+ .column('subjectDid')
168
+ .execute()
169
+
170
+ await db.schema
171
+ .createView('account_record_status_stats')
172
+ .materialized()
173
+ .ifNotExists()
174
+ .as(
175
+ (db as Kysely<modStatus.PartialDB>)
176
+ .selectFrom('moderation_subject_status')
177
+ .select('did')
178
+ .select([
179
+ sql<number>`COUNT(*)`.as('subjectCount'),
180
+ (eb) =>
181
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('reviewState')} IN (${REVIEWOPEN}, ${REVIEWESCALATED}))`.as(
182
+ 'pendingCount',
183
+ ),
184
+ (eb) =>
185
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('reviewState')} NOT IN (${REVIEWOPEN}, ${REVIEWESCALATED}))`.as(
186
+ 'processedCount',
187
+ ),
188
+ (eb) =>
189
+ sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('takendown')})`.as(
190
+ 'takendownCount',
191
+ ),
192
+ ])
193
+ .where('recordPath', '!=', '')
194
+ .groupBy('did'),
195
+ )
196
+ .execute()
197
+
198
+ await db.schema
199
+ .createIndex('account_record_status_stats_did_idx')
200
+ .unique()
201
+ .on('account_record_status_stats')
202
+ .column('did')
203
+ .execute()
204
+
205
+ await db.schema
206
+ .createIndex('account_record_status_stats_takendown_count_idx')
207
+ .on('account_record_status_stats')
208
+ .expression(sql`"takendownCount" ASC NULLS FIRST`)
209
+ .column('did')
210
+ .execute()
211
+ }
212
+
213
+ export async function down(db: Kysely<DatabaseSchemaType>): Promise<void> {
214
+ db.schema.dropView('account_record_status_stats').materialized().execute()
215
+ db.schema.dropView('account_record_events_stats').materialized().execute()
216
+ db.schema.dropView('record_events_stats').materialized().execute()
217
+ db.schema.dropView('account_events_stats').materialized().execute()
218
+ }
@@ -17,3 +17,4 @@ export * as _20241001T205730722Z from './20241001T205730722Z-subject-status-revi
17
17
  export * as _20241008T205730722Z from './20241008T205730722Z-sets'
18
18
  export * as _20241018T205730722Z from './20241018T205730722Z-setting'
19
19
  export * as _20241026T205730722Z from './20241026T205730722Z-add-hosting-status-to-subject-status'
20
+ export * as _20241220T144630860Z from './20241220T144630860Z-stats-materialized-views'
@@ -0,0 +1,16 @@
1
+ import { GeneratedAlways, Selectable } from 'kysely'
2
+
3
+ export const tableName = 'account_events_stats'
4
+
5
+ export type AccountEventsStats = {
6
+ subjectDid: GeneratedAlways<string>
7
+ takedownCount: GeneratedAlways<number>
8
+ suspendCount: GeneratedAlways<number>
9
+ escalateCount: GeneratedAlways<number>
10
+ reportCount: GeneratedAlways<number>
11
+ appealCount: GeneratedAlways<number>
12
+ }
13
+
14
+ export type AccountEventsStatsRow = Selectable<AccountEventsStats>
15
+
16
+ export type PartialDB = { [tableName]: AccountEventsStats }
@@ -0,0 +1,15 @@
1
+ import { GeneratedAlways, Selectable } from 'kysely'
2
+
3
+ export const tableName = 'account_record_events_stats'
4
+
5
+ type AccountRecordEventsStats = {
6
+ subjectDid: GeneratedAlways<string>
7
+ totalReports: GeneratedAlways<number>
8
+ reportedCount: GeneratedAlways<number>
9
+ escalatedCount: GeneratedAlways<number>
10
+ appealedCount: GeneratedAlways<number>
11
+ }
12
+
13
+ export type AccountRecordEventsStatsRow = Selectable<AccountRecordEventsStats>
14
+
15
+ export type PartialDB = { [tableName]: AccountRecordEventsStats }
@@ -0,0 +1,15 @@
1
+ import { GeneratedAlways, Selectable } from 'kysely'
2
+
3
+ export const tableName = 'account_record_status_stats'
4
+
5
+ type AccountRecordStatusStats = {
6
+ did: GeneratedAlways<string>
7
+ subjectCount: GeneratedAlways<number>
8
+ pendingCount: GeneratedAlways<number>
9
+ processedCount: GeneratedAlways<number>
10
+ takendownCount: GeneratedAlways<number>
11
+ }
12
+
13
+ export type AccountRecordStatusStatsRow = Selectable<AccountRecordStatusStats>
14
+
15
+ export type PartialDB = { [tableName]: AccountRecordStatusStats }
@@ -11,6 +11,11 @@ import * as set from './ozone_set'
11
11
  import * as member from './member'
12
12
  import * as setting from './setting'
13
13
 
14
+ import * as recordEventsStats from './record_events_stats'
15
+ import * as accountEventsStats from './account_events_stats'
16
+ import * as accountRecordEventsStats from './account_record_events_stats'
17
+ import * as accountRecordStatusStats from './account_record_status_stats'
18
+
14
19
  export type DatabaseSchemaType = modEvent.PartialDB &
15
20
  modSubjectStatus.PartialDB &
16
21
  label.PartialDB &
@@ -21,7 +26,11 @@ export type DatabaseSchemaType = modEvent.PartialDB &
21
26
  communicationTemplate.PartialDB &
22
27
  set.PartialDB &
23
28
  member.PartialDB &
24
- setting.PartialDB
29
+ setting.PartialDB &
30
+ accountEventsStats.PartialDB &
31
+ recordEventsStats.PartialDB &
32
+ accountRecordEventsStats.PartialDB &
33
+ accountRecordStatusStats.PartialDB
25
34
 
26
35
  export type DatabaseSchema = Kysely<DatabaseSchemaType>
27
36
 
@@ -0,0 +1,15 @@
1
+ import { GeneratedAlways, Selectable } from 'kysely'
2
+
3
+ export const tableName = 'record_events_stats'
4
+
5
+ export type RecordEventsStats = {
6
+ subjectDid: GeneratedAlways<string>
7
+ subjectUri: GeneratedAlways<string>
8
+ escalateCount: GeneratedAlways<number>
9
+ reportCount: GeneratedAlways<number>
10
+ appealCount: GeneratedAlways<number>
11
+ }
12
+
13
+ export type RecordEventsStatsRow = Selectable<RecordEventsStats>
14
+
15
+ export type PartialDB = { [tableName]: RecordEventsStats }
package/src/index.ts CHANGED
@@ -114,13 +114,7 @@ export class OzoneService {
114
114
  },
115
115
  'db pool stats',
116
116
  )
117
- dbLogger.info(
118
- {
119
- runningCount: backgroundQueue.queue.pending,
120
- waitingCount: backgroundQueue.queue.size,
121
- },
122
- 'background queue stats',
123
- )
117
+ dbLogger.info(backgroundQueue.getStats(), 'background queue stats')
124
118
  }, 10000)
125
119
  await this.ctx.sequencer.start()
126
120
  const server = this.app.listen(this.ctx.cfg.service.port)
@@ -211,6 +211,8 @@ export const APP_BSKY_FEED = {
211
211
  DefsClickthroughAuthor: 'app.bsky.feed.defs#clickthroughAuthor',
212
212
  DefsClickthroughReposter: 'app.bsky.feed.defs#clickthroughReposter',
213
213
  DefsClickthroughEmbed: 'app.bsky.feed.defs#clickthroughEmbed',
214
+ DefsContentModeUnspecified: 'app.bsky.feed.defs#contentModeUnspecified',
215
+ DefsContentModeVideo: 'app.bsky.feed.defs#contentModeVideo',
214
216
  DefsInteractionSeen: 'app.bsky.feed.defs#interactionSeen',
215
217
  DefsInteractionLike: 'app.bsky.feed.defs#interactionLike',
216
218
  DefsInteractionRepost: 'app.bsky.feed.defs#interactionRepost',
@@ -4851,6 +4851,11 @@ export const schemaDict = {
4851
4851
  ref: 'lex:app.bsky.actor.defs#profileView',
4852
4852
  },
4853
4853
  },
4854
+ recId: {
4855
+ type: 'integer',
4856
+ description:
4857
+ 'Snowflake for this recommendation, use when submitting recommendation events.',
4858
+ },
4854
4859
  },
4855
4860
  },
4856
4861
  },
@@ -5773,6 +5778,13 @@ export const schemaDict = {
5773
5778
  type: 'ref',
5774
5779
  ref: 'lex:app.bsky.feed.defs#generatorViewerState',
5775
5780
  },
5781
+ contentMode: {
5782
+ type: 'string',
5783
+ knownValues: [
5784
+ 'app.bsky.feed.defs#contentModeUnspecified',
5785
+ 'app.bsky.feed.defs#contentModeVideo',
5786
+ ],
5787
+ },
5776
5788
  indexedAt: {
5777
5789
  type: 'string',
5778
5790
  format: 'datetime',
@@ -5907,6 +5919,15 @@ export const schemaDict = {
5907
5919
  description:
5908
5920
  'User clicked through to the embedded content of the feed item',
5909
5921
  },
5922
+ contentModeUnspecified: {
5923
+ type: 'token',
5924
+ description: 'Declares the feed generator returns any types of posts.',
5925
+ },
5926
+ contentModeVideo: {
5927
+ type: 'token',
5928
+ description:
5929
+ 'Declares the feed generator returns posts containing app.bsky.embed.video embeds.',
5930
+ },
5910
5931
  interactionSeen: {
5911
5932
  type: 'token',
5912
5933
  description: 'Feed item was seen by user',
@@ -6038,6 +6059,13 @@ export const schemaDict = {
6038
6059
  description: 'Self-label values',
6039
6060
  refs: ['lex:com.atproto.label.defs#selfLabels'],
6040
6061
  },
6062
+ contentMode: {
6063
+ type: 'string',
6064
+ knownValues: [
6065
+ 'app.bsky.feed.defs#contentModeUnspecified',
6066
+ 'app.bsky.feed.defs#contentModeVideo',
6067
+ ],
6068
+ },
6041
6069
  createdAt: {
6042
6070
  type: 'string',
6043
6071
  format: 'datetime',
@@ -8358,6 +8386,11 @@ export const schemaDict = {
8358
8386
  'If true, response has fallen-back to generic results, and is not scoped using relativeToDid',
8359
8387
  default: false,
8360
8388
  },
8389
+ recId: {
8390
+ type: 'integer',
8391
+ description:
8392
+ 'Snowflake for this recommendation, use when submitting recommendation events.',
8393
+ },
8361
8394
  },
8362
8395
  },
8363
8396
  },
@@ -9429,6 +9462,11 @@ export const schemaDict = {
9429
9462
  description:
9430
9463
  'DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer.',
9431
9464
  },
9465
+ recId: {
9466
+ type: 'integer',
9467
+ description:
9468
+ 'Snowflake for this recommendation, use when submitting recommendation events.',
9469
+ },
9432
9470
  },
9433
9471
  },
9434
9472
  },
@@ -11284,6 +11322,85 @@ export const schemaDict = {
11284
11322
  type: 'string',
11285
11323
  },
11286
11324
  },
11325
+ accountStats: {
11326
+ description: 'Statistics related to the account subject',
11327
+ type: 'ref',
11328
+ ref: 'lex:tools.ozone.moderation.defs#accountStats',
11329
+ },
11330
+ recordsStats: {
11331
+ description:
11332
+ "Statistics related to the record subjects authored by the subject's account",
11333
+ type: 'ref',
11334
+ ref: 'lex:tools.ozone.moderation.defs#recordsStats',
11335
+ },
11336
+ },
11337
+ },
11338
+ accountStats: {
11339
+ description: 'Statistics about a particular account subject',
11340
+ type: 'object',
11341
+ properties: {
11342
+ reportCount: {
11343
+ description: 'Total number of reports on the account',
11344
+ type: 'integer',
11345
+ },
11346
+ appealCount: {
11347
+ description:
11348
+ 'Total number of appeals against a moderation action on the account',
11349
+ type: 'integer',
11350
+ },
11351
+ suspendCount: {
11352
+ description: 'Number of times the account was suspended',
11353
+ type: 'integer',
11354
+ },
11355
+ escalateCount: {
11356
+ description: 'Number of times the account was escalated',
11357
+ type: 'integer',
11358
+ },
11359
+ takedownCount: {
11360
+ description: 'Number of times the account was taken down',
11361
+ type: 'integer',
11362
+ },
11363
+ },
11364
+ },
11365
+ recordsStats: {
11366
+ description: 'Statistics about a set of record subject items',
11367
+ type: 'object',
11368
+ properties: {
11369
+ totalReports: {
11370
+ description:
11371
+ 'Cumulative sum of the number of reports on the items in the set',
11372
+ type: 'integer',
11373
+ },
11374
+ reportedCount: {
11375
+ description: 'Number of items that were reported at least once',
11376
+ type: 'integer',
11377
+ },
11378
+ escalatedCount: {
11379
+ description: 'Number of items that were escalated at least once',
11380
+ type: 'integer',
11381
+ },
11382
+ appealedCount: {
11383
+ description: 'Number of items that were appealed at least once',
11384
+ type: 'integer',
11385
+ },
11386
+ subjectCount: {
11387
+ description: 'Total number of item in the set',
11388
+ type: 'integer',
11389
+ },
11390
+ pendingCount: {
11391
+ description:
11392
+ 'Number of item currently in "reviewOpen" or "reviewEscalated" state',
11393
+ type: 'integer',
11394
+ },
11395
+ processedCount: {
11396
+ description:
11397
+ 'Number of item currently in "reviewNone" or "reviewClosed" state',
11398
+ type: 'integer',
11399
+ },
11400
+ takendownCount: {
11401
+ description: 'Number of item currently taken down',
11402
+ type: 'integer',
11403
+ },
11287
11404
  },
11288
11405
  },
11289
11406
  subjectReviewState: {
@@ -12532,7 +12649,12 @@ export const schemaDict = {
12532
12649
  sortField: {
12533
12650
  type: 'string',
12534
12651
  default: 'lastReportedAt',
12535
- enum: ['lastReviewedAt', 'lastReportedAt'],
12652
+ enum: [
12653
+ 'lastReviewedAt',
12654
+ 'lastReportedAt',
12655
+ 'reportedRecordsCount',
12656
+ 'takendownRecordsCount',
12657
+ ],
12536
12658
  },
12537
12659
  sortDirection: {
12538
12660
  type: 'string',
@@ -12587,6 +12709,21 @@ export const schemaDict = {
12587
12709
  "If specified, subjects of the given type (account or record) will be returned. When this is set to 'account' the 'collections' parameter will be ignored. When includeAllUserRecords or subject is set, this will be ignored.",
12588
12710
  knownValues: ['account', 'record'],
12589
12711
  },
12712
+ minAccountSuspendCount: {
12713
+ type: 'integer',
12714
+ description:
12715
+ 'If specified, only subjects that belong to an account that has at least this many suspensions will be returned.',
12716
+ },
12717
+ minReportedRecordsCount: {
12718
+ type: 'integer',
12719
+ description:
12720
+ 'If specified, only subjects that belong to an account that has at least this many reported records will be returned.',
12721
+ },
12722
+ minTakendownRecordsCount: {
12723
+ type: 'integer',
12724
+ description:
12725
+ 'If specified, only subjects that belong to an account that has at least this many taken down records will be returned.',
12726
+ },
12590
12727
  },
12591
12728
  },
12592
12729
  output: {
@@ -247,6 +247,10 @@ export interface GeneratorView {
247
247
  acceptsInteractions?: boolean
248
248
  labels?: ComAtprotoLabelDefs.Label[]
249
249
  viewer?: GeneratorViewerState
250
+ contentMode?:
251
+ | 'app.bsky.feed.defs#contentModeUnspecified'
252
+ | 'app.bsky.feed.defs#contentModeVideo'
253
+ | (string & {})
250
254
  indexedAt: string
251
255
  [k: string]: unknown
252
256
  }
@@ -401,6 +405,11 @@ export const CLICKTHROUGHAUTHOR = 'app.bsky.feed.defs#clickthroughAuthor'
401
405
  export const CLICKTHROUGHREPOSTER = 'app.bsky.feed.defs#clickthroughReposter'
402
406
  /** User clicked through to the embedded content of the feed item */
403
407
  export const CLICKTHROUGHEMBED = 'app.bsky.feed.defs#clickthroughEmbed'
408
+ /** Declares the feed generator returns any types of posts. */
409
+ export const CONTENTMODEUNSPECIFIED =
410
+ 'app.bsky.feed.defs#contentModeUnspecified'
411
+ /** Declares the feed generator returns posts containing app.bsky.embed.video embeds. */
412
+ export const CONTENTMODEVIDEO = 'app.bsky.feed.defs#contentModeVideo'
404
413
  /** Feed item was seen by user */
405
414
  export const INTERACTIONSEEN = 'app.bsky.feed.defs#interactionSeen'
406
415
  /** User liked the feed item */
@@ -19,6 +19,10 @@ export interface Record {
19
19
  labels?:
20
20
  | ComAtprotoLabelDefs.SelfLabels
21
21
  | { $type: string; [k: string]: unknown }
22
+ contentMode?:
23
+ | 'app.bsky.feed.defs#contentModeUnspecified'
24
+ | 'app.bsky.feed.defs#contentModeVideo'
25
+ | (string & {})
22
26
  createdAt: string
23
27
  [k: string]: unknown
24
28
  }
@@ -136,6 +136,8 @@ export interface SubjectStatusView {
136
136
  appealed?: boolean
137
137
  suspendUntil?: string
138
138
  tags?: string[]
139
+ accountStats?: AccountStats
140
+ recordsStats?: RecordsStats
139
141
  [k: string]: unknown
140
142
  }
141
143
 
@@ -151,6 +153,66 @@ export function validateSubjectStatusView(v: unknown): ValidationResult {
151
153
  return lexicons.validate('tools.ozone.moderation.defs#subjectStatusView', v)
152
154
  }
153
155
 
156
+ /** Statistics about a particular account subject */
157
+ export interface AccountStats {
158
+ /** Total number of reports on the account */
159
+ reportCount?: number
160
+ /** Total number of appeals against a moderation action on the account */
161
+ appealCount?: number
162
+ /** Number of times the account was suspended */
163
+ suspendCount?: number
164
+ /** Number of times the account was escalated */
165
+ escalateCount?: number
166
+ /** Number of times the account was taken down */
167
+ takedownCount?: number
168
+ [k: string]: unknown
169
+ }
170
+
171
+ export function isAccountStats(v: unknown): v is AccountStats {
172
+ return (
173
+ isObj(v) &&
174
+ hasProp(v, '$type') &&
175
+ v.$type === 'tools.ozone.moderation.defs#accountStats'
176
+ )
177
+ }
178
+
179
+ export function validateAccountStats(v: unknown): ValidationResult {
180
+ return lexicons.validate('tools.ozone.moderation.defs#accountStats', v)
181
+ }
182
+
183
+ /** Statistics about a set of record subject items */
184
+ export interface RecordsStats {
185
+ /** Cumulative sum of the number of reports on the items in the set */
186
+ totalReports?: number
187
+ /** Number of items that were reported at least once */
188
+ reportedCount?: number
189
+ /** Number of items that were escalated at least once */
190
+ escalatedCount?: number
191
+ /** Number of items that were appealed at least once */
192
+ appealedCount?: number
193
+ /** Total number of item in the set */
194
+ subjectCount?: number
195
+ /** Number of item currently in "reviewOpen" or "reviewEscalated" state */
196
+ pendingCount?: number
197
+ /** Number of item currently in "reviewNone" or "reviewClosed" state */
198
+ processedCount?: number
199
+ /** Number of item currently taken down */
200
+ takendownCount?: number
201
+ [k: string]: unknown
202
+ }
203
+
204
+ export function isRecordsStats(v: unknown): v is RecordsStats {
205
+ return (
206
+ isObj(v) &&
207
+ hasProp(v, '$type') &&
208
+ v.$type === 'tools.ozone.moderation.defs#recordsStats'
209
+ )
210
+ }
211
+
212
+ export function validateRecordsStats(v: unknown): ValidationResult {
213
+ return lexicons.validate('tools.ozone.moderation.defs#recordsStats', v)
214
+ }
215
+
154
216
  export type SubjectReviewState =
155
217
  | 'lex:tools.ozone.moderation.defs#reviewOpen'
156
218
  | 'lex:tools.ozone.moderation.defs#reviewEscalated'
@@ -49,7 +49,11 @@ export interface QueryParams {
49
49
  ignoreSubjects?: string[]
50
50
  /** Get all subject statuses that were reviewed by a specific moderator */
51
51
  lastReviewedBy?: string
52
- sortField: 'lastReviewedAt' | 'lastReportedAt'
52
+ sortField:
53
+ | 'lastReviewedAt'
54
+ | 'lastReportedAt'
55
+ | 'reportedRecordsCount'
56
+ | 'takendownRecordsCount'
53
57
  sortDirection: 'asc' | 'desc'
54
58
  /** Get subjects that were taken down */
55
59
  takendown?: boolean
@@ -63,6 +67,12 @@ export interface QueryParams {
63
67
  collections?: string[]
64
68
  /** If specified, subjects of the given type (account or record) will be returned. When this is set to 'account' the 'collections' parameter will be ignored. When includeAllUserRecords or subject is set, this will be ignored. */
65
69
  subjectType?: 'account' | 'record' | (string & {})
70
+ /** If specified, only subjects that belong to an account that has at least this many suspensions will be returned. */
71
+ minAccountSuspendCount?: number
72
+ /** If specified, only subjects that belong to an account that has at least this many reported records will be returned. */
73
+ minReportedRecordsCount?: number
74
+ /** If specified, only subjects that belong to an account that has at least this many taken down records will be returned. */
75
+ minTakendownRecordsCount?: number
66
76
  }
67
77
 
68
78
  export type InputSchema = undefined