@atproto/bsky 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/api/com/atproto/moderation/util.d.ts +4 -3
- package/dist/context.d.ts +15 -0
- package/dist/db/index.js +26 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20231003T202833377Z-create-moderation-subject-status.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/pagination.d.ts +2 -1
- package/dist/db/{periodic-moderation-action-reversal.d.ts → periodic-moderation-event-reversal.d.ts} +3 -5
- package/dist/db/tables/moderation.d.ts +24 -34
- package/dist/feed-gen/types.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2750 -2121
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +11 -18
- package/dist/lexicon/lexicons.d.ts +414 -399
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -7
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +114 -48
- package/dist/lexicon/types/com/atproto/admin/{takeModerationAction.d.ts → emitModerationEvent.d.ts} +5 -6
- package/dist/lexicon/types/com/atproto/admin/{getModerationAction.d.ts → getModerationEvent.d.ts} +1 -1
- package/dist/lexicon/types/com/atproto/admin/{getModerationActions.d.ts → queryModerationEvents.d.ts} +5 -1
- package/dist/lexicon/types/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +12 -6
- package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
- package/dist/migrate-moderation-data.d.ts +1 -0
- package/dist/services/actor/views.d.ts +2 -5
- package/dist/services/feed/index.d.ts +1 -0
- package/dist/services/feed/util.d.ts +9 -1
- package/dist/services/feed/views.d.ts +6 -17
- package/dist/services/graph/index.d.ts +5 -29
- package/dist/services/graph/types.d.ts +1 -0
- package/dist/services/moderation/index.d.ts +135 -72
- package/dist/services/moderation/pagination.d.ts +36 -0
- package/dist/services/moderation/status.d.ts +13 -0
- package/dist/services/moderation/types.d.ts +35 -0
- package/dist/services/moderation/views.d.ts +18 -14
- package/dist/util/debug.d.ts +1 -1
- package/package.json +11 -11
- package/src/api/app/bsky/feed/getActorFeeds.ts +2 -1
- package/src/api/app/bsky/feed/getActorLikes.ts +1 -3
- package/src/api/app/bsky/feed/getAuthorFeed.ts +1 -3
- package/src/api/app/bsky/feed/getFeed.ts +9 -9
- package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -0
- package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -1
- package/src/api/app/bsky/feed/getListFeed.ts +1 -3
- package/src/api/app/bsky/feed/getPostThread.ts +15 -54
- package/src/api/app/bsky/feed/getPosts.ts +21 -18
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +2 -1
- package/src/api/app/bsky/feed/getTimeline.ts +1 -3
- package/src/api/app/bsky/feed/searchPosts.ts +20 -17
- package/src/api/app/bsky/graph/getList.ts +6 -3
- package/src/api/app/bsky/graph/getListBlocks.ts +3 -2
- package/src/api/app/bsky/graph/getListMutes.ts +2 -1
- package/src/api/app/bsky/graph/getLists.ts +2 -1
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +3 -1
- package/src/api/blob-resolver.ts +6 -11
- package/src/api/com/atproto/admin/emitModerationEvent.ts +220 -0
- package/src/api/com/atproto/admin/{getModerationActions.ts → getModerationEvent.ts} +5 -11
- package/src/api/com/atproto/admin/getRecord.ts +1 -0
- package/src/api/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +13 -16
- package/src/api/com/atproto/admin/queryModerationStatuses.ts +55 -0
- package/src/api/com/atproto/moderation/createReport.ts +9 -7
- package/src/api/com/atproto/moderation/util.ts +38 -20
- package/src/api/index.ts +8 -14
- package/src/auth.ts +29 -21
- package/src/auto-moderator/index.ts +26 -19
- package/src/context.ts +4 -0
- package/src/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +123 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/pagination.ts +26 -3
- package/src/db/{periodic-moderation-action-reversal.ts → periodic-moderation-event-reversal.ts} +50 -46
- package/src/db/tables/moderation.ts +35 -52
- package/src/feed-gen/best-of-follows.ts +6 -3
- package/src/feed-gen/bsky-team.ts +1 -1
- package/src/feed-gen/hot-classic.ts +1 -1
- package/src/feed-gen/mutuals.ts +6 -2
- package/src/feed-gen/types.ts +1 -1
- package/src/feed-gen/whats-hot.ts +1 -1
- package/src/feed-gen/with-friends.ts +7 -3
- package/src/index.ts +2 -1
- package/src/lexicon/index.ts +30 -67
- package/src/lexicon/lexicons.ts +526 -491
- package/src/lexicon/types/app/bsky/feed/defs.ts +1 -18
- package/src/lexicon/types/app/bsky/graph/defs.ts +1 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +276 -84
- package/src/lexicon/types/com/atproto/admin/{takeModerationAction.ts → emitModerationEvent.ts} +13 -11
- package/src/lexicon/types/com/atproto/admin/{getModerationReport.ts → getModerationEvent.ts} +1 -1
- package/src/lexicon/types/com/atproto/admin/{getModerationActions.ts → queryModerationEvents.ts} +8 -1
- package/src/lexicon/types/com/atproto/admin/{getModerationReports.ts → queryModerationStatuses.ts} +21 -14
- package/src/lexicon/types/com/atproto/admin/sendEmail.ts +1 -0
- package/src/migrate-moderation-data.ts +414 -0
- package/src/services/actor/views.ts +5 -14
- package/src/services/feed/index.ts +26 -7
- package/src/services/feed/util.ts +47 -19
- package/src/services/feed/views.ts +68 -4
- package/src/services/graph/index.ts +21 -3
- package/src/services/graph/types.ts +1 -0
- package/src/services/indexing/plugins/block.ts +2 -3
- package/src/services/indexing/plugins/feed-generator.ts +2 -3
- package/src/services/indexing/plugins/follow.ts +2 -3
- package/src/services/indexing/plugins/like.ts +2 -3
- package/src/services/indexing/plugins/list-block.ts +2 -3
- package/src/services/indexing/plugins/list-item.ts +2 -3
- package/src/services/indexing/plugins/list.ts +2 -3
- package/src/services/indexing/plugins/post.ts +3 -4
- package/src/services/indexing/plugins/repost.ts +2 -3
- package/src/services/indexing/plugins/thread-gate.ts +2 -3
- package/src/services/label/index.ts +2 -3
- package/src/services/moderation/index.ts +380 -395
- package/src/services/moderation/pagination.ts +96 -0
- package/src/services/moderation/status.ts +244 -0
- package/src/services/moderation/types.ts +49 -0
- package/src/services/moderation/views.ts +278 -329
- package/src/util/debug.ts +2 -2
- package/tests/__snapshots__/feed-generation.test.ts.snap +322 -6
- package/tests/__snapshots__/indexing.test.ts.snap +0 -6
- package/tests/admin/__snapshots__/get-record.test.ts.snap +30 -132
- package/tests/admin/__snapshots__/get-repo.test.ts.snap +14 -60
- package/tests/admin/__snapshots__/moderation-events.test.ts.snap +146 -0
- package/tests/admin/__snapshots__/moderation-statuses.test.ts.snap +64 -0
- package/tests/admin/__snapshots__/moderation.test.ts.snap +0 -125
- package/tests/admin/get-record.test.ts +5 -9
- package/tests/admin/get-repo.test.ts +5 -9
- package/tests/admin/moderation-events.test.ts +221 -0
- package/tests/admin/moderation-statuses.test.ts +145 -0
- package/tests/admin/moderation.test.ts +512 -860
- package/tests/admin/repo-search.test.ts +2 -3
- package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -1
- package/tests/auto-moderator/takedowns.test.ts +45 -18
- package/tests/feed-generation.test.ts +57 -9
- package/tests/views/__snapshots__/block-lists.test.ts.snap +3 -9
- package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +5 -5
- package/tests/views/__snapshots__/mutes.test.ts.snap +0 -3
- package/tests/views/__snapshots__/thread.test.ts.snap +0 -30
- package/tests/views/actor-search.test.ts +2 -3
- package/tests/views/author-feed.test.ts +42 -36
- package/tests/views/follows.test.ts +40 -35
- package/tests/views/list-feed.test.ts +17 -9
- package/tests/views/notifications.test.ts +13 -9
- package/tests/views/profile.test.ts +20 -18
- package/tests/views/thread.test.ts +54 -26
- package/tests/views/threadgating.test.ts +51 -19
- package/tests/views/timeline.test.ts +21 -13
- package/dist/api/com/atproto/admin/resolveModerationReports.d.ts +0 -3
- package/dist/api/com/atproto/admin/reverseModerationAction.d.ts +0 -3
- package/dist/api/com/atproto/admin/takeModerationAction.d.ts +0 -3
- package/dist/lexicon/types/com/atproto/admin/getModerationReport.d.ts +0 -29
- package/dist/lexicon/types/com/atproto/admin/resolveModerationReports.d.ts +0 -36
- package/dist/lexicon/types/com/atproto/admin/reverseModerationAction.d.ts +0 -36
- package/src/api/com/atproto/admin/getModerationAction.ts +0 -44
- package/src/api/com/atproto/admin/getModerationReport.ts +0 -43
- package/src/api/com/atproto/admin/resolveModerationReports.ts +0 -24
- package/src/api/com/atproto/admin/reverseModerationAction.ts +0 -115
- package/src/api/com/atproto/admin/takeModerationAction.ts +0 -156
- package/src/lexicon/types/com/atproto/admin/getModerationAction.ts +0 -41
- package/src/lexicon/types/com/atproto/admin/resolveModerationReports.ts +0 -49
- package/src/lexicon/types/com/atproto/admin/reverseModerationAction.ts +0 -49
- package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +0 -172
- package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +0 -178
- package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +0 -177
- package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +0 -307
- package/tests/admin/get-moderation-action.test.ts +0 -100
- package/tests/admin/get-moderation-actions.test.ts +0 -164
- package/tests/admin/get-moderation-report.test.ts +0 -100
- package/tests/admin/get-moderation-reports.test.ts +0 -332
- /package/dist/api/com/atproto/admin/{getModerationAction.d.ts → emitModerationEvent.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationActions.d.ts → getModerationEvent.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationReport.d.ts → queryModerationEvents.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sql } from 'kysely'
|
|
2
2
|
import { ArrayEl } from '@atproto/common'
|
|
3
3
|
import { AtUri } from '@atproto/syntax'
|
|
4
4
|
import { INVALID_HANDLE } from '@atproto/syntax'
|
|
@@ -6,22 +6,25 @@ import { BlobRef, jsonStringToLex } from '@atproto/lexicon'
|
|
|
6
6
|
import { Database } from '../../db'
|
|
7
7
|
import { Actor } from '../../db/tables/actor'
|
|
8
8
|
import { Record as RecordRow } from '../../db/tables/record'
|
|
9
|
-
import { ModerationAction } from '../../db/tables/moderation'
|
|
10
9
|
import {
|
|
10
|
+
ModEventView,
|
|
11
11
|
RepoView,
|
|
12
12
|
RepoViewDetail,
|
|
13
13
|
RecordView,
|
|
14
14
|
RecordViewDetail,
|
|
15
|
-
ActionView,
|
|
16
|
-
ActionViewDetail,
|
|
17
|
-
ReportView,
|
|
18
15
|
ReportViewDetail,
|
|
19
16
|
BlobView,
|
|
17
|
+
SubjectStatusView,
|
|
18
|
+
ModEventViewDetail,
|
|
20
19
|
} from '../../lexicon/types/com/atproto/admin/defs'
|
|
21
20
|
import { OutputSchema as ReportOutput } from '../../lexicon/types/com/atproto/moderation/createReport'
|
|
22
21
|
import { Label } from '../../lexicon/types/com/atproto/label/defs'
|
|
23
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
ModerationEventRowWithHandle,
|
|
24
|
+
ModerationSubjectStatusRowWithHandle,
|
|
25
|
+
} from './types'
|
|
24
26
|
import { getSelfLabels } from '../label'
|
|
27
|
+
import { REASONOTHER } from '../../lexicon/types/com/atproto/moderation/defs'
|
|
25
28
|
|
|
26
29
|
export class ModerationViews {
|
|
27
30
|
constructor(private db: Database) {}
|
|
@@ -34,7 +37,7 @@ export class ModerationViews {
|
|
|
34
37
|
const results = Array.isArray(result) ? result : [result]
|
|
35
38
|
if (results.length === 0) return []
|
|
36
39
|
|
|
37
|
-
const [info,
|
|
40
|
+
const [info, subjectStatuses] = await Promise.all([
|
|
38
41
|
await this.db.db
|
|
39
42
|
.selectFrom('actor')
|
|
40
43
|
.leftJoin('profile', 'profile.creator', 'actor.did')
|
|
@@ -50,31 +53,21 @@ export class ModerationViews {
|
|
|
50
53
|
)
|
|
51
54
|
.select(['actor.did as did', 'profile_record.json as profileJson'])
|
|
52
55
|
.execute(),
|
|
53
|
-
this.
|
|
54
|
-
.selectFrom('moderation_action')
|
|
55
|
-
.where('reversedAt', 'is', null)
|
|
56
|
-
.where('subjectType', '=', 'com.atproto.admin.defs#repoRef')
|
|
57
|
-
.where(
|
|
58
|
-
'subjectDid',
|
|
59
|
-
'in',
|
|
60
|
-
results.map((r) => r.did),
|
|
61
|
-
)
|
|
62
|
-
.select(['id', 'action', 'durationInHours', 'subjectDid'])
|
|
63
|
-
.execute(),
|
|
56
|
+
this.getSubjectStatus(results.map((r) => ({ did: r.did }))),
|
|
64
57
|
])
|
|
65
58
|
|
|
66
59
|
const infoByDid = info.reduce(
|
|
67
60
|
(acc, cur) => Object.assign(acc, { [cur.did]: cur }),
|
|
68
61
|
{} as Record<string, ArrayEl<typeof info>>,
|
|
69
62
|
)
|
|
70
|
-
const
|
|
71
|
-
(acc, cur) =>
|
|
72
|
-
|
|
63
|
+
const subjectStatusByDid = subjectStatuses.reduce(
|
|
64
|
+
(acc, cur) =>
|
|
65
|
+
Object.assign(acc, { [cur.did ?? '']: this.subjectStatus(cur) }),
|
|
66
|
+
{},
|
|
73
67
|
)
|
|
74
68
|
|
|
75
69
|
const views = results.map((r) => {
|
|
76
70
|
const { profileJson } = infoByDid[r.did] ?? {}
|
|
77
|
-
const action = actionByDid[r.did]
|
|
78
71
|
const relatedRecords: object[] = []
|
|
79
72
|
if (profileJson) {
|
|
80
73
|
relatedRecords.push(
|
|
@@ -88,49 +81,148 @@ export class ModerationViews {
|
|
|
88
81
|
relatedRecords,
|
|
89
82
|
indexedAt: r.indexedAt,
|
|
90
83
|
moderation: {
|
|
91
|
-
|
|
84
|
+
subjectStatus: subjectStatusByDid[r.did] ?? undefined,
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return Array.isArray(result) ? views : views[0]
|
|
90
|
+
}
|
|
91
|
+
event(result: EventResult): Promise<ModEventView>
|
|
92
|
+
event(result: EventResult[]): Promise<ModEventView[]>
|
|
93
|
+
async event(
|
|
94
|
+
result: EventResult | EventResult[],
|
|
95
|
+
): Promise<ModEventView | ModEventView[]> {
|
|
96
|
+
const results = Array.isArray(result) ? result : [result]
|
|
97
|
+
if (results.length === 0) return []
|
|
98
|
+
|
|
99
|
+
const views = results.map((res) => {
|
|
100
|
+
const eventView: ModEventView = {
|
|
101
|
+
id: res.id,
|
|
102
|
+
event: {
|
|
103
|
+
$type: res.action,
|
|
104
|
+
comment: res.comment ?? undefined,
|
|
105
|
+
},
|
|
106
|
+
subject:
|
|
107
|
+
res.subjectType === 'com.atproto.admin.defs#repoRef'
|
|
92
108
|
? {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
durationInHours: action.durationInHours ?? undefined,
|
|
109
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
110
|
+
did: res.subjectDid,
|
|
96
111
|
}
|
|
97
|
-
:
|
|
98
|
-
|
|
112
|
+
: {
|
|
113
|
+
$type: 'com.atproto.repo.strongRef',
|
|
114
|
+
uri: res.subjectUri,
|
|
115
|
+
cid: res.subjectCid,
|
|
116
|
+
},
|
|
117
|
+
subjectBlobCids: [],
|
|
118
|
+
createdBy: res.createdBy,
|
|
119
|
+
createdAt: res.createdAt,
|
|
120
|
+
subjectHandle: res.subjectHandle ?? undefined,
|
|
121
|
+
creatorHandle: res.creatorHandle ?? undefined,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
[
|
|
126
|
+
'com.atproto.admin.defs#modEventTakedown',
|
|
127
|
+
'com.atproto.admin.defs#modEventMute',
|
|
128
|
+
].includes(res.action)
|
|
129
|
+
) {
|
|
130
|
+
eventView.event = {
|
|
131
|
+
...eventView.event,
|
|
132
|
+
durationInHours: res.durationInHours ?? undefined,
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (res.action === 'com.atproto.admin.defs#modEventLabel') {
|
|
137
|
+
eventView.event = {
|
|
138
|
+
...eventView.event,
|
|
139
|
+
createLabelVals: res.createLabelVals?.length
|
|
140
|
+
? res.createLabelVals.split(' ')
|
|
141
|
+
: [],
|
|
142
|
+
negateLabelVals: res.negateLabelVals?.length
|
|
143
|
+
? res.negateLabelVals.split(' ')
|
|
144
|
+
: [],
|
|
145
|
+
}
|
|
99
146
|
}
|
|
147
|
+
|
|
148
|
+
// This is for legacy data only, for new events, these types of events won't have labels attached
|
|
149
|
+
if (
|
|
150
|
+
[
|
|
151
|
+
'com.atproto.admin.defs#modEventAcknowledge',
|
|
152
|
+
'com.atproto.admin.defs#modEventTakedown',
|
|
153
|
+
'com.atproto.admin.defs#modEventEscalate',
|
|
154
|
+
].includes(res.action)
|
|
155
|
+
) {
|
|
156
|
+
if (res.createLabelVals?.length) {
|
|
157
|
+
eventView.event = {
|
|
158
|
+
...eventView.event,
|
|
159
|
+
createLabelVals: res.createLabelVals.split(' '),
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (res.negateLabelVals?.length) {
|
|
164
|
+
eventView.event = {
|
|
165
|
+
...eventView.event,
|
|
166
|
+
negateLabelVals: res.negateLabelVals.split(' '),
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (res.action === 'com.atproto.admin.defs#modEventReport') {
|
|
172
|
+
eventView.event = {
|
|
173
|
+
...eventView.event,
|
|
174
|
+
reportType: res.meta?.reportType ?? undefined,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (res.action === 'com.atproto.admin.defs#modEventEmail') {
|
|
179
|
+
eventView.event = {
|
|
180
|
+
...eventView.event,
|
|
181
|
+
subjectLine: res.meta?.subjectLine ?? '',
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
res.action === 'com.atproto.admin.defs#modEventComment' &&
|
|
187
|
+
res.meta?.sticky
|
|
188
|
+
) {
|
|
189
|
+
eventView.event.sticky = true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return eventView
|
|
100
193
|
})
|
|
101
194
|
|
|
102
195
|
return Array.isArray(result) ? views : views[0]
|
|
103
196
|
}
|
|
104
197
|
|
|
105
|
-
async
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
this.
|
|
109
|
-
.selectFrom('moderation_report')
|
|
110
|
-
.where('subjectType', '=', 'com.atproto.admin.defs#repoRef')
|
|
111
|
-
.where('subjectDid', '=', repo.did)
|
|
112
|
-
.orderBy('id', 'desc')
|
|
113
|
-
.selectAll()
|
|
114
|
-
.execute(),
|
|
115
|
-
this.db.db
|
|
116
|
-
.selectFrom('moderation_action')
|
|
117
|
-
.where('subjectType', '=', 'com.atproto.admin.defs#repoRef')
|
|
118
|
-
.where('subjectDid', '=', repo.did)
|
|
119
|
-
.orderBy('id', 'desc')
|
|
120
|
-
.selectAll()
|
|
121
|
-
.execute(),
|
|
198
|
+
async eventDetail(result: EventResult): Promise<ModEventViewDetail> {
|
|
199
|
+
const [event, subject] = await Promise.all([
|
|
200
|
+
this.event(result),
|
|
201
|
+
this.subject(result),
|
|
122
202
|
])
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
203
|
+
const allBlobs = findBlobRefs(subject.value)
|
|
204
|
+
const subjectBlobs = await this.blob(
|
|
205
|
+
allBlobs.filter((blob) =>
|
|
206
|
+
event.subjectBlobCids.includes(blob.ref.toString()),
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
return {
|
|
210
|
+
...event,
|
|
211
|
+
subject,
|
|
212
|
+
subjectBlobs,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async repoDetail(result: RepoResult): Promise<RepoViewDetail> {
|
|
217
|
+
const [repo, labels] = await Promise.all([
|
|
218
|
+
this.repo(result),
|
|
219
|
+
this.labels(result.did),
|
|
127
220
|
])
|
|
221
|
+
|
|
128
222
|
return {
|
|
129
223
|
...repo,
|
|
130
224
|
moderation: {
|
|
131
225
|
...repo.moderation,
|
|
132
|
-
reports,
|
|
133
|
-
actions,
|
|
134
226
|
},
|
|
135
227
|
labels,
|
|
136
228
|
}
|
|
@@ -144,7 +236,7 @@ export class ModerationViews {
|
|
|
144
236
|
const results = Array.isArray(result) ? result : [result]
|
|
145
237
|
if (results.length === 0) return []
|
|
146
238
|
|
|
147
|
-
const [repoResults,
|
|
239
|
+
const [repoResults, subjectStatuses] = await Promise.all([
|
|
148
240
|
this.db.db
|
|
149
241
|
.selectFrom('actor')
|
|
150
242
|
.where(
|
|
@@ -154,17 +246,7 @@ export class ModerationViews {
|
|
|
154
246
|
)
|
|
155
247
|
.selectAll()
|
|
156
248
|
.execute(),
|
|
157
|
-
this.
|
|
158
|
-
.selectFrom('moderation_action')
|
|
159
|
-
.where('reversedAt', 'is', null)
|
|
160
|
-
.where('subjectType', '=', 'com.atproto.repo.strongRef')
|
|
161
|
-
.where(
|
|
162
|
-
'subjectUri',
|
|
163
|
-
'in',
|
|
164
|
-
results.map((r) => r.uri),
|
|
165
|
-
)
|
|
166
|
-
.select(['id', 'action', 'durationInHours', 'subjectUri'])
|
|
167
|
-
.execute(),
|
|
249
|
+
this.getSubjectStatus(results.map((r) => didAndRecordPathFromUri(r.uri))),
|
|
168
250
|
])
|
|
169
251
|
const repos = await this.repo(repoResults)
|
|
170
252
|
|
|
@@ -172,14 +254,18 @@ export class ModerationViews {
|
|
|
172
254
|
(acc, cur) => Object.assign(acc, { [cur.did]: cur }),
|
|
173
255
|
{} as Record<string, ArrayEl<typeof repos>>,
|
|
174
256
|
)
|
|
175
|
-
const
|
|
176
|
-
(acc, cur) =>
|
|
177
|
-
|
|
257
|
+
const subjectStatusByUri = subjectStatuses.reduce(
|
|
258
|
+
(acc, cur) =>
|
|
259
|
+
Object.assign(acc, {
|
|
260
|
+
[`${cur.did}/${cur.recordPath}` ?? '']: this.subjectStatus(cur),
|
|
261
|
+
}),
|
|
262
|
+
{},
|
|
178
263
|
)
|
|
179
264
|
|
|
180
265
|
const views = results.map((res) => {
|
|
181
266
|
const repo = reposByDid[didFromUri(res.uri)]
|
|
182
|
-
const
|
|
267
|
+
const { did, recordPath } = didAndRecordPathFromUri(res.uri)
|
|
268
|
+
const subjectStatus = subjectStatusByUri[`${did}/${recordPath}`]
|
|
183
269
|
if (!repo) throw new Error(`Record repo is missing: ${res.uri}`)
|
|
184
270
|
const value = jsonStringToLex(res.json) as Record<string, unknown>
|
|
185
271
|
return {
|
|
@@ -190,13 +276,7 @@ export class ModerationViews {
|
|
|
190
276
|
indexedAt: res.indexedAt,
|
|
191
277
|
repo,
|
|
192
278
|
moderation: {
|
|
193
|
-
|
|
194
|
-
? {
|
|
195
|
-
id: action.id,
|
|
196
|
-
action: action.action,
|
|
197
|
-
durationInHours: action.durationInHours ?? undefined,
|
|
198
|
-
}
|
|
199
|
-
: undefined,
|
|
279
|
+
subjectStatus,
|
|
200
280
|
},
|
|
201
281
|
}
|
|
202
282
|
})
|
|
@@ -205,29 +285,17 @@ export class ModerationViews {
|
|
|
205
285
|
}
|
|
206
286
|
|
|
207
287
|
async recordDetail(result: RecordResult): Promise<RecordViewDetail> {
|
|
208
|
-
const [record,
|
|
288
|
+
const [record, subjectStatusResult] = await Promise.all([
|
|
209
289
|
this.record(result),
|
|
210
|
-
this.
|
|
211
|
-
.selectFrom('moderation_report')
|
|
212
|
-
.where('subjectType', '=', 'com.atproto.repo.strongRef')
|
|
213
|
-
.where('subjectUri', '=', result.uri)
|
|
214
|
-
.leftJoin('actor', 'actor.did', 'moderation_report.subjectDid')
|
|
215
|
-
.orderBy('id', 'desc')
|
|
216
|
-
.selectAll()
|
|
217
|
-
.execute(),
|
|
218
|
-
this.db.db
|
|
219
|
-
.selectFrom('moderation_action')
|
|
220
|
-
.where('subjectType', '=', 'com.atproto.repo.strongRef')
|
|
221
|
-
.where('subjectUri', '=', result.uri)
|
|
222
|
-
.orderBy('id', 'desc')
|
|
223
|
-
.selectAll()
|
|
224
|
-
.execute(),
|
|
290
|
+
this.getSubjectStatus(didAndRecordPathFromUri(result.uri)),
|
|
225
291
|
])
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
this.action(actionResults),
|
|
292
|
+
|
|
293
|
+
const [blobs, labels, subjectStatus] = await Promise.all([
|
|
229
294
|
this.blob(findBlobRefs(record.value)),
|
|
230
295
|
this.labels(record.uri),
|
|
296
|
+
subjectStatusResult?.length
|
|
297
|
+
? this.subjectStatus(subjectStatusResult[0])
|
|
298
|
+
: Promise.resolve(undefined),
|
|
231
299
|
])
|
|
232
300
|
const selfLabels = getSelfLabels({
|
|
233
301
|
uri: result.uri,
|
|
@@ -239,196 +307,22 @@ export class ModerationViews {
|
|
|
239
307
|
blobs,
|
|
240
308
|
moderation: {
|
|
241
309
|
...record.moderation,
|
|
242
|
-
|
|
243
|
-
actions,
|
|
310
|
+
subjectStatus,
|
|
244
311
|
},
|
|
245
312
|
labels: [...labels, ...selfLabels],
|
|
246
313
|
}
|
|
247
314
|
}
|
|
248
|
-
|
|
249
|
-
action(result: ActionResult): Promise<ActionView>
|
|
250
|
-
action(result: ActionResult[]): Promise<ActionView[]>
|
|
251
|
-
async action(
|
|
252
|
-
result: ActionResult | ActionResult[],
|
|
253
|
-
): Promise<ActionView | ActionView[]> {
|
|
254
|
-
const results = Array.isArray(result) ? result : [result]
|
|
255
|
-
if (results.length === 0) return []
|
|
256
|
-
|
|
257
|
-
const [resolutions, subjectBlobResults] = await Promise.all([
|
|
258
|
-
this.db.db
|
|
259
|
-
.selectFrom('moderation_report_resolution')
|
|
260
|
-
.select(['reportId as id', 'actionId'])
|
|
261
|
-
.where(
|
|
262
|
-
'actionId',
|
|
263
|
-
'in',
|
|
264
|
-
results.map((r) => r.id),
|
|
265
|
-
)
|
|
266
|
-
.orderBy('id', 'desc')
|
|
267
|
-
.execute(),
|
|
268
|
-
await this.db.db
|
|
269
|
-
.selectFrom('moderation_action_subject_blob')
|
|
270
|
-
.selectAll()
|
|
271
|
-
.where(
|
|
272
|
-
'actionId',
|
|
273
|
-
'in',
|
|
274
|
-
results.map((r) => r.id),
|
|
275
|
-
)
|
|
276
|
-
.execute(),
|
|
277
|
-
])
|
|
278
|
-
|
|
279
|
-
const reportIdsByActionId = resolutions.reduce((acc, cur) => {
|
|
280
|
-
acc[cur.actionId] ??= []
|
|
281
|
-
acc[cur.actionId].push(cur.id)
|
|
282
|
-
return acc
|
|
283
|
-
}, {} as Record<string, number[]>)
|
|
284
|
-
const subjectBlobCidsByActionId = subjectBlobResults.reduce((acc, cur) => {
|
|
285
|
-
acc[cur.actionId] ??= []
|
|
286
|
-
acc[cur.actionId].push(cur.cid)
|
|
287
|
-
return acc
|
|
288
|
-
}, {} as Record<string, string[]>)
|
|
289
|
-
|
|
290
|
-
const views = results.map((res) => ({
|
|
291
|
-
id: res.id,
|
|
292
|
-
action: res.action,
|
|
293
|
-
durationInHours: res.durationInHours ?? undefined,
|
|
294
|
-
subject:
|
|
295
|
-
res.subjectType === 'com.atproto.admin.defs#repoRef'
|
|
296
|
-
? {
|
|
297
|
-
$type: 'com.atproto.admin.defs#repoRef',
|
|
298
|
-
did: res.subjectDid,
|
|
299
|
-
}
|
|
300
|
-
: {
|
|
301
|
-
$type: 'com.atproto.repo.strongRef',
|
|
302
|
-
uri: res.subjectUri,
|
|
303
|
-
cid: res.subjectCid,
|
|
304
|
-
},
|
|
305
|
-
subjectBlobCids: subjectBlobCidsByActionId[res.id] ?? [],
|
|
306
|
-
reason: res.reason,
|
|
307
|
-
createdAt: res.createdAt,
|
|
308
|
-
createdBy: res.createdBy,
|
|
309
|
-
createLabelVals:
|
|
310
|
-
res.createLabelVals && res.createLabelVals.length > 0
|
|
311
|
-
? res.createLabelVals.split(' ')
|
|
312
|
-
: undefined,
|
|
313
|
-
negateLabelVals:
|
|
314
|
-
res.negateLabelVals && res.negateLabelVals.length > 0
|
|
315
|
-
? res.negateLabelVals.split(' ')
|
|
316
|
-
: undefined,
|
|
317
|
-
reversal:
|
|
318
|
-
res.reversedAt !== null &&
|
|
319
|
-
res.reversedBy !== null &&
|
|
320
|
-
res.reversedReason !== null
|
|
321
|
-
? {
|
|
322
|
-
createdAt: res.reversedAt,
|
|
323
|
-
createdBy: res.reversedBy,
|
|
324
|
-
reason: res.reversedReason,
|
|
325
|
-
}
|
|
326
|
-
: undefined,
|
|
327
|
-
resolvedReportIds: reportIdsByActionId[res.id] ?? [],
|
|
328
|
-
}))
|
|
329
|
-
|
|
330
|
-
return Array.isArray(result) ? views : views[0]
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
async actionDetail(result: ActionResult): Promise<ActionViewDetail> {
|
|
334
|
-
const action = await this.action(result)
|
|
335
|
-
const reportResults = action.resolvedReportIds.length
|
|
336
|
-
? await this.db.db
|
|
337
|
-
.selectFrom('moderation_report')
|
|
338
|
-
.where('id', 'in', action.resolvedReportIds)
|
|
339
|
-
.orderBy('id', 'desc')
|
|
340
|
-
.selectAll()
|
|
341
|
-
.execute()
|
|
342
|
-
: []
|
|
343
|
-
const [subject, resolvedReports] = await Promise.all([
|
|
344
|
-
this.subject(result),
|
|
345
|
-
this.report(reportResults),
|
|
346
|
-
])
|
|
347
|
-
const allBlobs = findBlobRefs(subject.value)
|
|
348
|
-
const subjectBlobs = await this.blob(
|
|
349
|
-
allBlobs.filter((blob) =>
|
|
350
|
-
action.subjectBlobCids.includes(blob.ref.toString()),
|
|
351
|
-
),
|
|
352
|
-
)
|
|
353
|
-
return {
|
|
354
|
-
id: action.id,
|
|
355
|
-
action: action.action,
|
|
356
|
-
durationInHours: action.durationInHours,
|
|
357
|
-
subject,
|
|
358
|
-
subjectBlobs,
|
|
359
|
-
createLabelVals: action.createLabelVals,
|
|
360
|
-
negateLabelVals: action.negateLabelVals,
|
|
361
|
-
reason: action.reason,
|
|
362
|
-
createdAt: action.createdAt,
|
|
363
|
-
createdBy: action.createdBy,
|
|
364
|
-
reversal: action.reversal,
|
|
365
|
-
resolvedReports,
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
report(result: ReportResult): Promise<ReportView>
|
|
370
|
-
report(result: ReportResult[]): Promise<ReportView[]>
|
|
371
|
-
async report(
|
|
372
|
-
result: ReportResult | ReportResult[],
|
|
373
|
-
): Promise<ReportView | ReportView[]> {
|
|
374
|
-
const results = Array.isArray(result) ? result : [result]
|
|
375
|
-
if (results.length === 0) return []
|
|
376
|
-
|
|
377
|
-
const resolutions = await this.db.db
|
|
378
|
-
.selectFrom('moderation_report_resolution')
|
|
379
|
-
.select(['actionId as id', 'reportId'])
|
|
380
|
-
.where(
|
|
381
|
-
'reportId',
|
|
382
|
-
'in',
|
|
383
|
-
results.map((r) => r.id),
|
|
384
|
-
)
|
|
385
|
-
.orderBy('id', 'desc')
|
|
386
|
-
.execute()
|
|
387
|
-
|
|
388
|
-
const actionIdsByReportId = resolutions.reduce((acc, cur) => {
|
|
389
|
-
acc[cur.reportId] ??= []
|
|
390
|
-
acc[cur.reportId].push(cur.id)
|
|
391
|
-
return acc
|
|
392
|
-
}, {} as Record<string, number[]>)
|
|
393
|
-
|
|
394
|
-
const views: ReportView[] = results.map((res) => {
|
|
395
|
-
const decoratedView: ReportView = {
|
|
396
|
-
id: res.id,
|
|
397
|
-
createdAt: res.createdAt,
|
|
398
|
-
reasonType: res.reasonType,
|
|
399
|
-
reason: res.reason ?? undefined,
|
|
400
|
-
reportedBy: res.reportedByDid,
|
|
401
|
-
subject:
|
|
402
|
-
res.subjectType === 'com.atproto.admin.defs#repoRef'
|
|
403
|
-
? {
|
|
404
|
-
$type: 'com.atproto.admin.defs#repoRef',
|
|
405
|
-
did: res.subjectDid,
|
|
406
|
-
}
|
|
407
|
-
: {
|
|
408
|
-
$type: 'com.atproto.repo.strongRef',
|
|
409
|
-
uri: res.subjectUri,
|
|
410
|
-
cid: res.subjectCid,
|
|
411
|
-
},
|
|
412
|
-
resolvedByActionIds: actionIdsByReportId[res.id] ?? [],
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (res.handle) {
|
|
416
|
-
decoratedView.subjectRepoHandle = res.handle
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return decoratedView
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
return Array.isArray(result) ? views : views[0]
|
|
423
|
-
}
|
|
424
|
-
|
|
425
315
|
reportPublic(report: ReportResult): ReportOutput {
|
|
426
316
|
return {
|
|
427
317
|
id: report.id,
|
|
428
318
|
createdAt: report.createdAt,
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
319
|
+
// Ideally, we would never have a report entry that does not have a reasonType but at the schema level
|
|
320
|
+
// we are not guarantying that so in whatever case, if we end up with such entries, default to 'other'
|
|
321
|
+
reasonType: report.meta?.reportType
|
|
322
|
+
? (report.meta?.reportType as string)
|
|
323
|
+
: REASONOTHER,
|
|
324
|
+
reason: report.comment ?? undefined,
|
|
325
|
+
reportedBy: report.createdBy,
|
|
432
326
|
subject:
|
|
433
327
|
report.subjectType === 'com.atproto.admin.defs#repoRef'
|
|
434
328
|
? {
|
|
@@ -442,32 +336,6 @@ export class ModerationViews {
|
|
|
442
336
|
},
|
|
443
337
|
}
|
|
444
338
|
}
|
|
445
|
-
|
|
446
|
-
async reportDetail(result: ReportResult): Promise<ReportViewDetail> {
|
|
447
|
-
const report = await this.report(result)
|
|
448
|
-
const actionResults = report.resolvedByActionIds.length
|
|
449
|
-
? await this.db.db
|
|
450
|
-
.selectFrom('moderation_action')
|
|
451
|
-
.where('id', 'in', report.resolvedByActionIds)
|
|
452
|
-
.orderBy('id', 'desc')
|
|
453
|
-
.selectAll()
|
|
454
|
-
.execute()
|
|
455
|
-
: []
|
|
456
|
-
const [subject, resolvedByActions] = await Promise.all([
|
|
457
|
-
this.subject(result),
|
|
458
|
-
this.action(actionResults),
|
|
459
|
-
])
|
|
460
|
-
return {
|
|
461
|
-
id: report.id,
|
|
462
|
-
createdAt: report.createdAt,
|
|
463
|
-
reasonType: report.reasonType,
|
|
464
|
-
reason: report.reason ?? undefined,
|
|
465
|
-
reportedBy: report.reportedBy,
|
|
466
|
-
subject,
|
|
467
|
-
resolvedByActions,
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
339
|
// Partial view for subjects
|
|
472
340
|
|
|
473
341
|
async subject(result: SubjectResult): Promise<SubjectView> {
|
|
@@ -511,44 +379,35 @@ export class ModerationViews {
|
|
|
511
379
|
|
|
512
380
|
async blob(blobs: BlobRef[]): Promise<BlobView[]> {
|
|
513
381
|
if (!blobs.length) return []
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
.
|
|
517
|
-
.innerJoin(
|
|
518
|
-
'moderation_action_subject_blob as subject_blob',
|
|
519
|
-
'subject_blob.actionId',
|
|
520
|
-
'moderation_action.id',
|
|
521
|
-
)
|
|
382
|
+
const { ref } = this.db.db.dynamic
|
|
383
|
+
const modStatusResults = await this.db.db
|
|
384
|
+
.selectFrom('moderation_subject_status')
|
|
522
385
|
.where(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
blobs.map((blob) => blob.ref.toString())
|
|
386
|
+
sql<string>`${ref(
|
|
387
|
+
'moderation_subject_status.blobCids',
|
|
388
|
+
)} @> ${JSON.stringify(blobs.map((blob) => blob.ref.toString()))}`,
|
|
526
389
|
)
|
|
527
|
-
.
|
|
528
|
-
.
|
|
529
|
-
const
|
|
530
|
-
(acc, cur) => Object.assign(acc, { [cur
|
|
531
|
-
{}
|
|
390
|
+
.selectAll()
|
|
391
|
+
.executeTakeFirst()
|
|
392
|
+
const statusByCid = (modStatusResults?.blobCids || [])?.reduce(
|
|
393
|
+
(acc, cur) => Object.assign(acc, { [cur]: modStatusResults }),
|
|
394
|
+
{},
|
|
532
395
|
)
|
|
533
396
|
// Intentionally missing details field, since we don't have any on appview.
|
|
534
397
|
// We also don't know when the blob was created, so we use a canned creation time.
|
|
535
398
|
const unknownTime = new Date(0).toISOString()
|
|
536
399
|
return blobs.map((blob) => {
|
|
537
400
|
const cid = blob.ref.toString()
|
|
538
|
-
const
|
|
401
|
+
const subjectStatus = statusByCid[cid]
|
|
402
|
+
? this.subjectStatus(statusByCid[cid])
|
|
403
|
+
: undefined
|
|
539
404
|
return {
|
|
540
405
|
cid,
|
|
541
406
|
mimeType: blob.mimeType,
|
|
542
407
|
size: blob.size,
|
|
543
408
|
createdAt: unknownTime,
|
|
544
409
|
moderation: {
|
|
545
|
-
|
|
546
|
-
? {
|
|
547
|
-
id: action.id,
|
|
548
|
-
action: action.action,
|
|
549
|
-
durationInHours: action.durationInHours ?? undefined,
|
|
550
|
-
}
|
|
551
|
-
: undefined,
|
|
410
|
+
subjectStatus,
|
|
552
411
|
},
|
|
553
412
|
}
|
|
554
413
|
})
|
|
@@ -567,27 +426,117 @@ export class ModerationViews {
|
|
|
567
426
|
neg: l.neg,
|
|
568
427
|
}))
|
|
569
428
|
}
|
|
429
|
+
|
|
430
|
+
async getSubjectStatus(
|
|
431
|
+
subject:
|
|
432
|
+
| { did: string; recordPath?: string }
|
|
433
|
+
| { did: string; recordPath?: string }[],
|
|
434
|
+
): Promise<ModerationSubjectStatusRowWithHandle[]> {
|
|
435
|
+
const subjectFilters = Array.isArray(subject) ? subject : [subject]
|
|
436
|
+
const filterForSubject =
|
|
437
|
+
({ did, recordPath }: { did: string; recordPath?: string }) =>
|
|
438
|
+
// TODO: Fix the typing here?
|
|
439
|
+
(clause: any) => {
|
|
440
|
+
clause = clause
|
|
441
|
+
.where('moderation_subject_status.did', '=', did)
|
|
442
|
+
.where('moderation_subject_status.recordPath', '=', recordPath || '')
|
|
443
|
+
return clause
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const builder = this.db.db
|
|
447
|
+
.selectFrom('moderation_subject_status')
|
|
448
|
+
.leftJoin('actor', 'actor.did', 'moderation_subject_status.did')
|
|
449
|
+
.where((clause) => {
|
|
450
|
+
subjectFilters.forEach(({ did, recordPath }, i) => {
|
|
451
|
+
const applySubjectFilter = filterForSubject({ did, recordPath })
|
|
452
|
+
if (i === 0) {
|
|
453
|
+
clause = clause.where(applySubjectFilter)
|
|
454
|
+
} else {
|
|
455
|
+
clause = clause.orWhere(applySubjectFilter)
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
return clause
|
|
460
|
+
})
|
|
461
|
+
.selectAll('moderation_subject_status')
|
|
462
|
+
.select('actor.handle as handle')
|
|
463
|
+
|
|
464
|
+
return builder.execute()
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
subjectStatus(result: ModerationSubjectStatusRowWithHandle): SubjectStatusView
|
|
468
|
+
subjectStatus(
|
|
469
|
+
result: ModerationSubjectStatusRowWithHandle[],
|
|
470
|
+
): SubjectStatusView[]
|
|
471
|
+
subjectStatus(
|
|
472
|
+
result:
|
|
473
|
+
| ModerationSubjectStatusRowWithHandle
|
|
474
|
+
| ModerationSubjectStatusRowWithHandle[],
|
|
475
|
+
): SubjectStatusView | SubjectStatusView[] {
|
|
476
|
+
const results = Array.isArray(result) ? result : [result]
|
|
477
|
+
if (results.length === 0) return []
|
|
478
|
+
|
|
479
|
+
const decoratedSubjectStatuses = results.map((subjectStatus) => ({
|
|
480
|
+
id: subjectStatus.id,
|
|
481
|
+
reviewState: subjectStatus.reviewState,
|
|
482
|
+
createdAt: subjectStatus.createdAt,
|
|
483
|
+
updatedAt: subjectStatus.updatedAt,
|
|
484
|
+
comment: subjectStatus.comment ?? undefined,
|
|
485
|
+
lastReviewedBy: subjectStatus.lastReviewedBy ?? undefined,
|
|
486
|
+
lastReviewedAt: subjectStatus.lastReviewedAt ?? undefined,
|
|
487
|
+
lastReportedAt: subjectStatus.lastReportedAt ?? undefined,
|
|
488
|
+
muteUntil: subjectStatus.muteUntil ?? undefined,
|
|
489
|
+
suspendUntil: subjectStatus.suspendUntil ?? undefined,
|
|
490
|
+
takendown: subjectStatus.takendown ?? undefined,
|
|
491
|
+
subjectRepoHandle: subjectStatus.handle ?? undefined,
|
|
492
|
+
subjectBlobCids: subjectStatus.blobCids || [],
|
|
493
|
+
subject: !subjectStatus.recordPath
|
|
494
|
+
? {
|
|
495
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
496
|
+
did: subjectStatus.did,
|
|
497
|
+
}
|
|
498
|
+
: {
|
|
499
|
+
$type: 'com.atproto.repo.strongRef',
|
|
500
|
+
uri: AtUri.make(
|
|
501
|
+
subjectStatus.did,
|
|
502
|
+
// Not too intuitive but the recordpath is basically <collection>/<rkey>
|
|
503
|
+
// which is what the last 2 params of .make() arguments are
|
|
504
|
+
...subjectStatus.recordPath.split('/'),
|
|
505
|
+
).toString(),
|
|
506
|
+
cid: subjectStatus.recordCid,
|
|
507
|
+
},
|
|
508
|
+
}))
|
|
509
|
+
|
|
510
|
+
return Array.isArray(result)
|
|
511
|
+
? decoratedSubjectStatuses
|
|
512
|
+
: decoratedSubjectStatuses[0]
|
|
513
|
+
}
|
|
570
514
|
}
|
|
571
515
|
|
|
572
516
|
type RepoResult = Actor
|
|
573
517
|
|
|
574
|
-
type
|
|
518
|
+
type EventResult = ModerationEventRowWithHandle
|
|
575
519
|
|
|
576
|
-
type ReportResult =
|
|
520
|
+
type ReportResult = ModerationEventRowWithHandle
|
|
577
521
|
|
|
578
522
|
type RecordResult = RecordRow
|
|
579
523
|
|
|
580
524
|
type SubjectResult = Pick<
|
|
581
|
-
|
|
525
|
+
EventResult & ReportResult,
|
|
582
526
|
'id' | 'subjectType' | 'subjectDid' | 'subjectUri' | 'subjectCid'
|
|
583
527
|
>
|
|
584
528
|
|
|
585
|
-
type SubjectView =
|
|
529
|
+
type SubjectView = ModEventViewDetail['subject'] & ReportViewDetail['subject']
|
|
586
530
|
|
|
587
531
|
function didFromUri(uri: string) {
|
|
588
532
|
return new AtUri(uri).host
|
|
589
533
|
}
|
|
590
534
|
|
|
535
|
+
function didAndRecordPathFromUri(uri: string) {
|
|
536
|
+
const atUri = new AtUri(uri)
|
|
537
|
+
return { did: atUri.host, recordPath: `${atUri.collection}/${atUri.rkey}` }
|
|
538
|
+
}
|
|
539
|
+
|
|
591
540
|
function findBlobRefs(value: unknown, refs: BlobRef[] = []) {
|
|
592
541
|
if (value instanceof BlobRef) {
|
|
593
542
|
refs.push(value)
|