@atproto/ozone 0.1.113 → 0.1.115
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 +14 -0
- package/dist/api/moderation/getRecord.d.ts.map +1 -1
- package/dist/api/moderation/getRecord.js +1 -1
- package/dist/api/moderation/getRecord.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +116 -124
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +68 -66
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts +33 -0
- package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/defs.js +36 -0
- package/dist/lexicon/types/app/bsky/unspecced/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +5 -13
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js +0 -9
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts +2 -29
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js +0 -36
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +5 -21
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/util.d.ts +10 -0
- package/dist/mod-service/util.d.ts.map +1 -1
- package/dist/mod-service/util.js +25 -1
- package/dist/mod-service/util.js.map +1 -1
- package/dist/mod-service/views.d.ts +6 -2
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js +46 -15
- package/dist/mod-service/views.js.map +1 -1
- package/package.json +4 -4
- package/src/api/moderation/getRecord.ts +4 -1
- package/src/lexicon/lexicons.ts +74 -71
- package/src/lexicon/types/app/bsky/unspecced/defs.ts +73 -0
- package/src/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.ts +5 -22
- package/src/lexicon/types/app/bsky/unspecced/getPostThreadV2.ts +5 -72
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +1 -0
- package/src/mod-service/index.ts +17 -15
- package/src/mod-service/util.ts +24 -0
- package/src/mod-service/views.ts +52 -17
- package/tests/__snapshots__/get-record.test.ts.snap +26 -0
- package/tests/get-record.test.ts +16 -6
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -10201,6 +10201,66 @@ export const schemaDict = {
|
|
|
10201
10201
|
},
|
|
10202
10202
|
},
|
|
10203
10203
|
},
|
|
10204
|
+
threadItemPost: {
|
|
10205
|
+
type: 'object',
|
|
10206
|
+
required: [
|
|
10207
|
+
'post',
|
|
10208
|
+
'moreParents',
|
|
10209
|
+
'moreReplies',
|
|
10210
|
+
'opThread',
|
|
10211
|
+
'hiddenByThreadgate',
|
|
10212
|
+
'mutedByViewer',
|
|
10213
|
+
],
|
|
10214
|
+
properties: {
|
|
10215
|
+
post: {
|
|
10216
|
+
type: 'ref',
|
|
10217
|
+
ref: 'lex:app.bsky.feed.defs#postView',
|
|
10218
|
+
},
|
|
10219
|
+
moreParents: {
|
|
10220
|
+
type: 'boolean',
|
|
10221
|
+
description:
|
|
10222
|
+
'This post has more parents that were not present in the response. This is just a boolean, without the number of parents.',
|
|
10223
|
+
},
|
|
10224
|
+
moreReplies: {
|
|
10225
|
+
type: 'integer',
|
|
10226
|
+
description:
|
|
10227
|
+
'This post has more replies that were not present in the response. This is a numeric value, which is best-effort and might not be accurate.',
|
|
10228
|
+
},
|
|
10229
|
+
opThread: {
|
|
10230
|
+
type: 'boolean',
|
|
10231
|
+
description:
|
|
10232
|
+
'This post is part of a contiguous thread by the OP from the thread root. Many different OP threads can happen in the same thread.',
|
|
10233
|
+
},
|
|
10234
|
+
hiddenByThreadgate: {
|
|
10235
|
+
type: 'boolean',
|
|
10236
|
+
description:
|
|
10237
|
+
'The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread.',
|
|
10238
|
+
},
|
|
10239
|
+
mutedByViewer: {
|
|
10240
|
+
type: 'boolean',
|
|
10241
|
+
description:
|
|
10242
|
+
'This is by an account muted by the viewer requesting it.',
|
|
10243
|
+
},
|
|
10244
|
+
},
|
|
10245
|
+
},
|
|
10246
|
+
threadItemNoUnauthenticated: {
|
|
10247
|
+
type: 'object',
|
|
10248
|
+
properties: {},
|
|
10249
|
+
},
|
|
10250
|
+
threadItemNotFound: {
|
|
10251
|
+
type: 'object',
|
|
10252
|
+
properties: {},
|
|
10253
|
+
},
|
|
10254
|
+
threadItemBlocked: {
|
|
10255
|
+
type: 'object',
|
|
10256
|
+
required: ['author'],
|
|
10257
|
+
properties: {
|
|
10258
|
+
author: {
|
|
10259
|
+
type: 'ref',
|
|
10260
|
+
ref: 'lex:app.bsky.feed.defs#blockedAuthor',
|
|
10261
|
+
},
|
|
10262
|
+
},
|
|
10263
|
+
},
|
|
10204
10264
|
},
|
|
10205
10265
|
},
|
|
10206
10266
|
AppBskyUnspeccedGetConfig: {
|
|
@@ -10312,6 +10372,12 @@ export const schemaDict = {
|
|
|
10312
10372
|
description:
|
|
10313
10373
|
'Reference (AT-URI) to post record. This is the anchor post.',
|
|
10314
10374
|
},
|
|
10375
|
+
prioritizeFollowedUsers: {
|
|
10376
|
+
type: 'boolean',
|
|
10377
|
+
description:
|
|
10378
|
+
'Whether to prioritize posts from followed users. It only has effect when the user is authenticated.',
|
|
10379
|
+
default: false,
|
|
10380
|
+
},
|
|
10315
10381
|
},
|
|
10316
10382
|
},
|
|
10317
10383
|
output: {
|
|
@@ -10323,7 +10389,7 @@ export const schemaDict = {
|
|
|
10323
10389
|
thread: {
|
|
10324
10390
|
type: 'array',
|
|
10325
10391
|
description:
|
|
10326
|
-
'A flat list of thread
|
|
10392
|
+
'A flat list of hidden thread items. The depth of each item is indicated by the depth property inside the item.',
|
|
10327
10393
|
items: {
|
|
10328
10394
|
type: 'ref',
|
|
10329
10395
|
ref: 'lex:app.bsky.unspecced.getPostThreadHiddenV2#threadHiddenItem',
|
|
@@ -10348,29 +10414,7 @@ export const schemaDict = {
|
|
|
10348
10414
|
},
|
|
10349
10415
|
value: {
|
|
10350
10416
|
type: 'union',
|
|
10351
|
-
refs: [
|
|
10352
|
-
'lex:app.bsky.unspecced.getPostThreadHiddenV2#threadHiddenItemPost',
|
|
10353
|
-
],
|
|
10354
|
-
},
|
|
10355
|
-
},
|
|
10356
|
-
},
|
|
10357
|
-
threadHiddenItemPost: {
|
|
10358
|
-
type: 'object',
|
|
10359
|
-
required: ['post', 'hiddenByThreadgate', 'mutedByViewer'],
|
|
10360
|
-
properties: {
|
|
10361
|
-
post: {
|
|
10362
|
-
type: 'ref',
|
|
10363
|
-
ref: 'lex:app.bsky.feed.defs#postView',
|
|
10364
|
-
},
|
|
10365
|
-
hiddenByThreadgate: {
|
|
10366
|
-
type: 'boolean',
|
|
10367
|
-
description:
|
|
10368
|
-
'The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread.',
|
|
10369
|
-
},
|
|
10370
|
-
mutedByViewer: {
|
|
10371
|
-
type: 'boolean',
|
|
10372
|
-
description:
|
|
10373
|
-
'This is by an account muted by the viewer requesting it.',
|
|
10417
|
+
refs: ['lex:app.bsky.unspecced.defs#threadItemPost'],
|
|
10374
10418
|
},
|
|
10375
10419
|
},
|
|
10376
10420
|
},
|
|
@@ -10473,57 +10517,14 @@ export const schemaDict = {
|
|
|
10473
10517
|
value: {
|
|
10474
10518
|
type: 'union',
|
|
10475
10519
|
refs: [
|
|
10476
|
-
'lex:app.bsky.unspecced.
|
|
10477
|
-
'lex:app.bsky.unspecced.
|
|
10478
|
-
'lex:app.bsky.unspecced.
|
|
10479
|
-
'lex:app.bsky.unspecced.
|
|
10520
|
+
'lex:app.bsky.unspecced.defs#threadItemPost',
|
|
10521
|
+
'lex:app.bsky.unspecced.defs#threadItemNoUnauthenticated',
|
|
10522
|
+
'lex:app.bsky.unspecced.defs#threadItemNotFound',
|
|
10523
|
+
'lex:app.bsky.unspecced.defs#threadItemBlocked',
|
|
10480
10524
|
],
|
|
10481
10525
|
},
|
|
10482
10526
|
},
|
|
10483
10527
|
},
|
|
10484
|
-
threadItemPost: {
|
|
10485
|
-
type: 'object',
|
|
10486
|
-
required: ['post', 'moreParents', 'moreReplies', 'opThread'],
|
|
10487
|
-
properties: {
|
|
10488
|
-
post: {
|
|
10489
|
-
type: 'ref',
|
|
10490
|
-
ref: 'lex:app.bsky.feed.defs#postView',
|
|
10491
|
-
},
|
|
10492
|
-
moreParents: {
|
|
10493
|
-
type: 'boolean',
|
|
10494
|
-
description:
|
|
10495
|
-
'This post has more parents that were not present in the response. This is just a boolean, without the number of parents.',
|
|
10496
|
-
},
|
|
10497
|
-
moreReplies: {
|
|
10498
|
-
type: 'integer',
|
|
10499
|
-
description:
|
|
10500
|
-
'This post has more replies that were not present in the response. This is a numeric value, which is best-effort and might not be accurate.',
|
|
10501
|
-
},
|
|
10502
|
-
opThread: {
|
|
10503
|
-
type: 'boolean',
|
|
10504
|
-
description:
|
|
10505
|
-
'This post is part of a contiguous thread by the OP from the thread root. Many different OP threads can happen in the same thread.',
|
|
10506
|
-
},
|
|
10507
|
-
},
|
|
10508
|
-
},
|
|
10509
|
-
threadItemNoUnauthenticated: {
|
|
10510
|
-
type: 'object',
|
|
10511
|
-
properties: {},
|
|
10512
|
-
},
|
|
10513
|
-
threadItemNotFound: {
|
|
10514
|
-
type: 'object',
|
|
10515
|
-
properties: {},
|
|
10516
|
-
},
|
|
10517
|
-
threadItemBlocked: {
|
|
10518
|
-
type: 'object',
|
|
10519
|
-
required: ['author'],
|
|
10520
|
-
properties: {
|
|
10521
|
-
author: {
|
|
10522
|
-
type: 'ref',
|
|
10523
|
-
ref: 'lex:app.bsky.feed.defs#blockedAuthor',
|
|
10524
|
-
},
|
|
10525
|
-
},
|
|
10526
|
-
},
|
|
10527
10528
|
},
|
|
10528
10529
|
},
|
|
10529
10530
|
AppBskyUnspeccedGetSuggestedFeeds: {
|
|
@@ -13229,6 +13230,7 @@ export const schemaDict = {
|
|
|
13229
13230
|
refs: [
|
|
13230
13231
|
'lex:com.atproto.admin.defs#repoRef',
|
|
13231
13232
|
'lex:com.atproto.repo.strongRef',
|
|
13233
|
+
'lex:chat.bsky.convo.defs#messageRef',
|
|
13232
13234
|
],
|
|
13233
13235
|
},
|
|
13234
13236
|
hosting: {
|
|
@@ -16154,6 +16156,7 @@ export const schemaDict = {
|
|
|
16154
16156
|
},
|
|
16155
16157
|
createdAt: {
|
|
16156
16158
|
type: 'string',
|
|
16159
|
+
format: 'datetime',
|
|
16157
16160
|
description:
|
|
16158
16161
|
'Timestamp for verification record. Defaults to current time when not specified.',
|
|
16159
16162
|
},
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type OmitKey,
|
|
11
11
|
} from '../../../../util'
|
|
12
12
|
import type * as AppBskyActorDefs from '../actor/defs.js'
|
|
13
|
+
import type * as AppBskyFeedDefs from '../feed/defs.js'
|
|
13
14
|
|
|
14
15
|
const is$typed = _is$typed,
|
|
15
16
|
validate = _validate
|
|
@@ -125,3 +126,75 @@ export function isTrendView<V>(v: V) {
|
|
|
125
126
|
export function validateTrendView<V>(v: V) {
|
|
126
127
|
return validate<TrendView & V>(v, id, hashTrendView)
|
|
127
128
|
}
|
|
129
|
+
|
|
130
|
+
export interface ThreadItemPost {
|
|
131
|
+
$type?: 'app.bsky.unspecced.defs#threadItemPost'
|
|
132
|
+
post: AppBskyFeedDefs.PostView
|
|
133
|
+
/** This post has more parents that were not present in the response. This is just a boolean, without the number of parents. */
|
|
134
|
+
moreParents: boolean
|
|
135
|
+
/** This post has more replies that were not present in the response. This is a numeric value, which is best-effort and might not be accurate. */
|
|
136
|
+
moreReplies: number
|
|
137
|
+
/** This post is part of a contiguous thread by the OP from the thread root. Many different OP threads can happen in the same thread. */
|
|
138
|
+
opThread: boolean
|
|
139
|
+
/** The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread. */
|
|
140
|
+
hiddenByThreadgate: boolean
|
|
141
|
+
/** This is by an account muted by the viewer requesting it. */
|
|
142
|
+
mutedByViewer: boolean
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const hashThreadItemPost = 'threadItemPost'
|
|
146
|
+
|
|
147
|
+
export function isThreadItemPost<V>(v: V) {
|
|
148
|
+
return is$typed(v, id, hashThreadItemPost)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function validateThreadItemPost<V>(v: V) {
|
|
152
|
+
return validate<ThreadItemPost & V>(v, id, hashThreadItemPost)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface ThreadItemNoUnauthenticated {
|
|
156
|
+
$type?: 'app.bsky.unspecced.defs#threadItemNoUnauthenticated'
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const hashThreadItemNoUnauthenticated = 'threadItemNoUnauthenticated'
|
|
160
|
+
|
|
161
|
+
export function isThreadItemNoUnauthenticated<V>(v: V) {
|
|
162
|
+
return is$typed(v, id, hashThreadItemNoUnauthenticated)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function validateThreadItemNoUnauthenticated<V>(v: V) {
|
|
166
|
+
return validate<ThreadItemNoUnauthenticated & V>(
|
|
167
|
+
v,
|
|
168
|
+
id,
|
|
169
|
+
hashThreadItemNoUnauthenticated,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ThreadItemNotFound {
|
|
174
|
+
$type?: 'app.bsky.unspecced.defs#threadItemNotFound'
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const hashThreadItemNotFound = 'threadItemNotFound'
|
|
178
|
+
|
|
179
|
+
export function isThreadItemNotFound<V>(v: V) {
|
|
180
|
+
return is$typed(v, id, hashThreadItemNotFound)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function validateThreadItemNotFound<V>(v: V) {
|
|
184
|
+
return validate<ThreadItemNotFound & V>(v, id, hashThreadItemNotFound)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface ThreadItemBlocked {
|
|
188
|
+
$type?: 'app.bsky.unspecced.defs#threadItemBlocked'
|
|
189
|
+
author: AppBskyFeedDefs.BlockedAuthor
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const hashThreadItemBlocked = 'threadItemBlocked'
|
|
193
|
+
|
|
194
|
+
export function isThreadItemBlocked<V>(v: V) {
|
|
195
|
+
return is$typed(v, id, hashThreadItemBlocked)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function validateThreadItemBlocked<V>(v: V) {
|
|
199
|
+
return validate<ThreadItemBlocked & V>(v, id, hashThreadItemBlocked)
|
|
200
|
+
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type OmitKey,
|
|
12
12
|
} from '../../../../util'
|
|
13
13
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
14
|
-
import type * as
|
|
14
|
+
import type * as AppBskyUnspeccedDefs from './defs.js'
|
|
15
15
|
|
|
16
16
|
const is$typed = _is$typed,
|
|
17
17
|
validate = _validate
|
|
@@ -20,12 +20,14 @@ const id = 'app.bsky.unspecced.getPostThreadHiddenV2'
|
|
|
20
20
|
export interface QueryParams {
|
|
21
21
|
/** Reference (AT-URI) to post record. This is the anchor post. */
|
|
22
22
|
anchor: string
|
|
23
|
+
/** Whether to prioritize posts from followed users. It only has effect when the user is authenticated. */
|
|
24
|
+
prioritizeFollowedUsers: boolean
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export type InputSchema = undefined
|
|
26
28
|
|
|
27
29
|
export interface OutputSchema {
|
|
28
|
-
/** A flat list of thread
|
|
30
|
+
/** A flat list of hidden thread items. The depth of each item is indicated by the depth property inside the item. */
|
|
29
31
|
thread: ThreadHiddenItem[]
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -60,7 +62,7 @@ export interface ThreadHiddenItem {
|
|
|
60
62
|
uri: string
|
|
61
63
|
/** The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths. */
|
|
62
64
|
depth: number
|
|
63
|
-
value: $Typed<
|
|
65
|
+
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost> | { $type: string }
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
const hashThreadHiddenItem = 'threadHiddenItem'
|
|
@@ -72,22 +74,3 @@ export function isThreadHiddenItem<V>(v: V) {
|
|
|
72
74
|
export function validateThreadHiddenItem<V>(v: V) {
|
|
73
75
|
return validate<ThreadHiddenItem & V>(v, id, hashThreadHiddenItem)
|
|
74
76
|
}
|
|
75
|
-
|
|
76
|
-
export interface ThreadHiddenItemPost {
|
|
77
|
-
$type?: 'app.bsky.unspecced.getPostThreadHiddenV2#threadHiddenItemPost'
|
|
78
|
-
post: AppBskyFeedDefs.PostView
|
|
79
|
-
/** The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread. */
|
|
80
|
-
hiddenByThreadgate: boolean
|
|
81
|
-
/** This is by an account muted by the viewer requesting it. */
|
|
82
|
-
mutedByViewer: boolean
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const hashThreadHiddenItemPost = 'threadHiddenItemPost'
|
|
86
|
-
|
|
87
|
-
export function isThreadHiddenItemPost<V>(v: V) {
|
|
88
|
-
return is$typed(v, id, hashThreadHiddenItemPost)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function validateThreadHiddenItemPost<V>(v: V) {
|
|
92
|
-
return validate<ThreadHiddenItemPost & V>(v, id, hashThreadHiddenItemPost)
|
|
93
|
-
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from '../../../../util'
|
|
13
13
|
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
14
14
|
import type * as AppBskyFeedDefs from '../feed/defs.js'
|
|
15
|
+
import type * as AppBskyUnspeccedDefs from './defs.js'
|
|
15
16
|
|
|
16
17
|
const is$typed = _is$typed,
|
|
17
18
|
validate = _validate
|
|
@@ -74,10 +75,10 @@ export interface ThreadItem {
|
|
|
74
75
|
/** The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths. */
|
|
75
76
|
depth: number
|
|
76
77
|
value:
|
|
77
|
-
| $Typed<ThreadItemPost>
|
|
78
|
-
| $Typed<ThreadItemNoUnauthenticated>
|
|
79
|
-
| $Typed<ThreadItemNotFound>
|
|
80
|
-
| $Typed<ThreadItemBlocked>
|
|
78
|
+
| $Typed<AppBskyUnspeccedDefs.ThreadItemPost>
|
|
79
|
+
| $Typed<AppBskyUnspeccedDefs.ThreadItemNoUnauthenticated>
|
|
80
|
+
| $Typed<AppBskyUnspeccedDefs.ThreadItemNotFound>
|
|
81
|
+
| $Typed<AppBskyUnspeccedDefs.ThreadItemBlocked>
|
|
81
82
|
| { $type: string }
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -90,71 +91,3 @@ export function isThreadItem<V>(v: V) {
|
|
|
90
91
|
export function validateThreadItem<V>(v: V) {
|
|
91
92
|
return validate<ThreadItem & V>(v, id, hashThreadItem)
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
-
export interface ThreadItemPost {
|
|
95
|
-
$type?: 'app.bsky.unspecced.getPostThreadV2#threadItemPost'
|
|
96
|
-
post: AppBskyFeedDefs.PostView
|
|
97
|
-
/** This post has more parents that were not present in the response. This is just a boolean, without the number of parents. */
|
|
98
|
-
moreParents: boolean
|
|
99
|
-
/** This post has more replies that were not present in the response. This is a numeric value, which is best-effort and might not be accurate. */
|
|
100
|
-
moreReplies: number
|
|
101
|
-
/** This post is part of a contiguous thread by the OP from the thread root. Many different OP threads can happen in the same thread. */
|
|
102
|
-
opThread: boolean
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const hashThreadItemPost = 'threadItemPost'
|
|
106
|
-
|
|
107
|
-
export function isThreadItemPost<V>(v: V) {
|
|
108
|
-
return is$typed(v, id, hashThreadItemPost)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function validateThreadItemPost<V>(v: V) {
|
|
112
|
-
return validate<ThreadItemPost & V>(v, id, hashThreadItemPost)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface ThreadItemNoUnauthenticated {
|
|
116
|
-
$type?: 'app.bsky.unspecced.getPostThreadV2#threadItemNoUnauthenticated'
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const hashThreadItemNoUnauthenticated = 'threadItemNoUnauthenticated'
|
|
120
|
-
|
|
121
|
-
export function isThreadItemNoUnauthenticated<V>(v: V) {
|
|
122
|
-
return is$typed(v, id, hashThreadItemNoUnauthenticated)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export function validateThreadItemNoUnauthenticated<V>(v: V) {
|
|
126
|
-
return validate<ThreadItemNoUnauthenticated & V>(
|
|
127
|
-
v,
|
|
128
|
-
id,
|
|
129
|
-
hashThreadItemNoUnauthenticated,
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface ThreadItemNotFound {
|
|
134
|
-
$type?: 'app.bsky.unspecced.getPostThreadV2#threadItemNotFound'
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const hashThreadItemNotFound = 'threadItemNotFound'
|
|
138
|
-
|
|
139
|
-
export function isThreadItemNotFound<V>(v: V) {
|
|
140
|
-
return is$typed(v, id, hashThreadItemNotFound)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function validateThreadItemNotFound<V>(v: V) {
|
|
144
|
-
return validate<ThreadItemNotFound & V>(v, id, hashThreadItemNotFound)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export interface ThreadItemBlocked {
|
|
148
|
-
$type?: 'app.bsky.unspecced.getPostThreadV2#threadItemBlocked'
|
|
149
|
-
author: AppBskyFeedDefs.BlockedAuthor
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const hashThreadItemBlocked = 'threadItemBlocked'
|
|
153
|
-
|
|
154
|
-
export function isThreadItemBlocked<V>(v: V) {
|
|
155
|
-
return is$typed(v, id, hashThreadItemBlocked)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export function validateThreadItemBlocked<V>(v: V) {
|
|
159
|
-
return validate<ThreadItemBlocked & V>(v, id, hashThreadItemBlocked)
|
|
160
|
-
}
|
|
@@ -117,6 +117,7 @@ export interface SubjectStatusView {
|
|
|
117
117
|
subject:
|
|
118
118
|
| $Typed<ComAtprotoAdminDefs.RepoRef>
|
|
119
119
|
| $Typed<ComAtprotoRepoStrongRef.Main>
|
|
120
|
+
| $Typed<ChatBskyConvoDefs.MessageRef>
|
|
120
121
|
| { $type: string }
|
|
121
122
|
hosting?: $Typed<AccountHosting> | $Typed<RecordHosting> | { $type: string }
|
|
122
123
|
subjectBlobCids?: string[]
|
package/src/mod-service/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import net from 'node:net'
|
|
2
1
|
import { Insertable, RawBuilder, sql } from 'kysely'
|
|
3
2
|
import { CID } from 'multiformats/cid'
|
|
4
3
|
import { AtpAgent } from '@atproto/api'
|
|
@@ -62,7 +61,12 @@ import {
|
|
|
62
61
|
ReporterStatsResult,
|
|
63
62
|
ReversibleModerationEvent,
|
|
64
63
|
} from './types'
|
|
65
|
-
import {
|
|
64
|
+
import {
|
|
65
|
+
formatLabel,
|
|
66
|
+
formatLabelRow,
|
|
67
|
+
getPdsAgentForRepo,
|
|
68
|
+
signLabel,
|
|
69
|
+
} from './util'
|
|
66
70
|
import { AuthHeaders, ModerationViews } from './views'
|
|
67
71
|
|
|
68
72
|
export type ModerationServiceCreator = (db: Database) => ModerationService
|
|
@@ -125,6 +129,8 @@ export class ModerationService {
|
|
|
125
129
|
}
|
|
126
130
|
return authHeaders
|
|
127
131
|
},
|
|
132
|
+
this.idResolver,
|
|
133
|
+
this.cfg.service.devMode,
|
|
128
134
|
)
|
|
129
135
|
|
|
130
136
|
async getEvent(id: number): Promise<ModerationEventRow | undefined> {
|
|
@@ -1243,19 +1249,22 @@ export class ModerationService {
|
|
|
1243
1249
|
subject: string
|
|
1244
1250
|
}) {
|
|
1245
1251
|
const { subject, content, recipientDid } = opts
|
|
1246
|
-
const {
|
|
1247
|
-
|
|
1248
|
-
|
|
1252
|
+
const { agent: pdsAgent, url } = await getPdsAgentForRepo(
|
|
1253
|
+
this.idResolver,
|
|
1254
|
+
recipientDid,
|
|
1255
|
+
this.cfg.service.devMode,
|
|
1256
|
+
)
|
|
1257
|
+
if (!pdsAgent) {
|
|
1249
1258
|
throw new InvalidRequestError('Invalid pds service in DID doc')
|
|
1250
1259
|
}
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1260
|
+
const { data: serverInfo } =
|
|
1261
|
+
await pdsAgent.com.atproto.server.describeServer()
|
|
1253
1262
|
if (serverInfo.did !== `did:web:${url.hostname}`) {
|
|
1254
1263
|
// @TODO do bidirectional check once implemented. in the meantime,
|
|
1255
1264
|
// matching did to hostname we're talking to is pretty good.
|
|
1256
1265
|
throw new InvalidRequestError('Invalid pds service in DID doc')
|
|
1257
1266
|
}
|
|
1258
|
-
const { data: delivery } = await
|
|
1267
|
+
const { data: delivery } = await pdsAgent.com.atproto.admin.sendEmail(
|
|
1259
1268
|
{
|
|
1260
1269
|
subject,
|
|
1261
1270
|
content,
|
|
@@ -1465,13 +1474,6 @@ const parseTags = (tags?: string[]) =>
|
|
|
1465
1474
|
// Ignore invalid items
|
|
1466
1475
|
.filter((subTags): subTags is [string, ...string[]] => subTags.length > 0)
|
|
1467
1476
|
|
|
1468
|
-
const isSafeUrl = (url: URL) => {
|
|
1469
|
-
if (url.protocol !== 'https:') return false
|
|
1470
|
-
if (!url.hostname || url.hostname === 'localhost') return false
|
|
1471
|
-
if (net.isIP(url.hostname) !== 0) return false
|
|
1472
|
-
return true
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
1477
|
const TAKEDOWNS = ['pds_takedown' as const, 'appview_takedown' as const]
|
|
1476
1478
|
|
|
1477
1479
|
export const TAKEDOWN_LABEL = '!takedown'
|
package/src/mod-service/util.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import net from 'node:net'
|
|
2
|
+
import AtpAgent from '@atproto/api'
|
|
1
3
|
import { cborEncode, noUndefinedVals } from '@atproto/common'
|
|
2
4
|
import { Keypair } from '@atproto/crypto'
|
|
5
|
+
import { IdResolver } from '@atproto/identity'
|
|
3
6
|
import { LabelRow } from '../db/schema/label'
|
|
4
7
|
import { Label } from '../lexicon/types/com/atproto/label/defs'
|
|
5
8
|
|
|
@@ -59,3 +62,24 @@ export const signLabel = async (
|
|
|
59
62
|
sig,
|
|
60
63
|
}
|
|
61
64
|
}
|
|
65
|
+
|
|
66
|
+
export const isSafeUrl = (url: URL) => {
|
|
67
|
+
if (url.protocol !== 'https:') return false
|
|
68
|
+
if (!url.hostname || url.hostname === 'localhost') return false
|
|
69
|
+
if (net.isIP(url.hostname) !== 0) return false
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const getPdsAgentForRepo = async (
|
|
74
|
+
idResolver: IdResolver,
|
|
75
|
+
did: string,
|
|
76
|
+
devMode?: boolean,
|
|
77
|
+
) => {
|
|
78
|
+
const { pds } = await idResolver.did.resolveAtprotoData(did)
|
|
79
|
+
const url = new URL(pds)
|
|
80
|
+
if (!devMode && !isSafeUrl(url)) {
|
|
81
|
+
return { url, agent: null }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { url, agent: new AtpAgent({ service: url }) }
|
|
85
|
+
}
|
package/src/mod-service/views.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { sql } from 'kysely'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AppBskyActorDefs,
|
|
4
|
+
AtpAgent,
|
|
5
|
+
ComAtprotoRepoGetRecord,
|
|
6
|
+
} from '@atproto/api'
|
|
3
7
|
import { chunkArray, dedupeStrs } from '@atproto/common'
|
|
4
8
|
import { Keypair } from '@atproto/crypto'
|
|
9
|
+
import { IdResolver } from '@atproto/identity'
|
|
5
10
|
import { BlobRef } from '@atproto/lexicon'
|
|
6
11
|
import { AtUri, INVALID_HANDLE, normalizeDatetimeAlways } from '@atproto/syntax'
|
|
7
12
|
import { Database } from '../db'
|
|
@@ -47,7 +52,7 @@ import {
|
|
|
47
52
|
ModerationEventRowWithHandle,
|
|
48
53
|
ModerationSubjectStatusRowWithHandle,
|
|
49
54
|
} from './types'
|
|
50
|
-
import { formatLabel, signLabel } from './util'
|
|
55
|
+
import { formatLabel, getPdsAgentForRepo, signLabel } from './util'
|
|
51
56
|
|
|
52
57
|
const isValidSelfLabels = asPredicate(validateSelfLabels)
|
|
53
58
|
|
|
@@ -72,6 +77,8 @@ export class ModerationViews {
|
|
|
72
77
|
private signingKeyId: number,
|
|
73
78
|
private appviewAgent: AtpAgent,
|
|
74
79
|
private appviewAuth: (method: string) => Promise<AuthHeaders>,
|
|
80
|
+
public idResolver: IdResolver,
|
|
81
|
+
public devMode?: boolean,
|
|
75
82
|
) {}
|
|
76
83
|
|
|
77
84
|
async getAccoutInfosByDid(dids: string[]): Promise<Map<string, AccountView>> {
|
|
@@ -294,28 +301,56 @@ export class ModerationViews {
|
|
|
294
301
|
return results
|
|
295
302
|
}
|
|
296
303
|
|
|
304
|
+
async fetchRecord(
|
|
305
|
+
params: ComAtprotoRepoGetRecord.QueryParams,
|
|
306
|
+
appviewAuth: AuthHeaders,
|
|
307
|
+
) {
|
|
308
|
+
try {
|
|
309
|
+
const record = await this.appviewAgent.com.atproto.repo.getRecord(
|
|
310
|
+
params,
|
|
311
|
+
appviewAuth,
|
|
312
|
+
)
|
|
313
|
+
return record
|
|
314
|
+
} catch (err) {
|
|
315
|
+
if (err instanceof ComAtprotoRepoGetRecord.RecordNotFoundError) {
|
|
316
|
+
// If pds fetch fails, just return null regardless of the error
|
|
317
|
+
try {
|
|
318
|
+
const { agent: pdsAgent } = await getPdsAgentForRepo(
|
|
319
|
+
this.idResolver,
|
|
320
|
+
params.repo,
|
|
321
|
+
this.devMode,
|
|
322
|
+
)
|
|
323
|
+
if (!pdsAgent) {
|
|
324
|
+
return null
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const record = await pdsAgent.com.atproto.repo.getRecord(params)
|
|
328
|
+
return record
|
|
329
|
+
} catch (error) {
|
|
330
|
+
return null
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return null
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
297
338
|
async fetchRecords(
|
|
298
339
|
subjects: RecordSubject[],
|
|
299
340
|
): Promise<Map<string, RecordInfo>> {
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
341
|
+
const appviewAuth = await this.appviewAuth(ids.ComAtprotoRepoGetRecord)
|
|
342
|
+
if (!appviewAuth) return new Map()
|
|
343
|
+
|
|
302
344
|
const fetched = await Promise.all(
|
|
303
345
|
subjects.map(async (subject) => {
|
|
304
346
|
const uri = new AtUri(subject.uri)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
rkey: uri.rkey,
|
|
311
|
-
cid: subject.cid,
|
|
312
|
-
},
|
|
313
|
-
auth,
|
|
314
|
-
)
|
|
315
|
-
return record
|
|
316
|
-
} catch {
|
|
317
|
-
return null
|
|
347
|
+
const params = {
|
|
348
|
+
repo: uri.hostname,
|
|
349
|
+
collection: uri.collection,
|
|
350
|
+
rkey: uri.rkey,
|
|
351
|
+
cid: subject.cid,
|
|
318
352
|
}
|
|
353
|
+
return this.fetchRecord(params, appviewAuth)
|
|
319
354
|
}),
|
|
320
355
|
)
|
|
321
356
|
return fetched.reduce((acc, cur) => {
|
|
@@ -241,3 +241,29 @@ Object {
|
|
|
241
241
|
},
|
|
242
242
|
}
|
|
243
243
|
`;
|
|
244
|
+
|
|
245
|
+
exports[`admin get record view gets record from pds if appview does not have it. 1`] = `
|
|
246
|
+
Object {
|
|
247
|
+
"blobCids": Array [],
|
|
248
|
+
"blobs": Array [],
|
|
249
|
+
"cid": "cids(0)",
|
|
250
|
+
"indexedAt": "1970-01-01T00:00:00.000Z",
|
|
251
|
+
"labels": Array [],
|
|
252
|
+
"moderation": Object {},
|
|
253
|
+
"repo": Object {
|
|
254
|
+
"did": "user(0)",
|
|
255
|
+
"email": "carol@test.com",
|
|
256
|
+
"handle": "carol.test",
|
|
257
|
+
"indexedAt": "1970-01-01T00:00:00.000Z",
|
|
258
|
+
"invitesDisabled": false,
|
|
259
|
+
"moderation": Object {},
|
|
260
|
+
"relatedRecords": Array [],
|
|
261
|
+
},
|
|
262
|
+
"uri": "record(0)",
|
|
263
|
+
"value": Object {
|
|
264
|
+
"$type": "app.bsky.feed.post",
|
|
265
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
266
|
+
"text": "this is test",
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
`;
|