@atproto/bsky 0.0.18 → 0.0.20
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 +18 -0
- package/dist/index.js +32 -10
- package/dist/index.js.map +2 -2
- package/dist/lexicon/lexicons.d.ts +4 -0
- package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +1 -0
- package/dist/services/moderation/index.d.ts +1 -0
- package/package.json +6 -6
- package/src/api/app/bsky/actor/getProfile.ts +17 -6
- package/src/api/app/bsky/feed/getAuthorFeed.ts +7 -0
- package/src/api/app/bsky/notification/listNotifications.ts +1 -1
- package/src/lexicon/lexicons.ts +5 -0
- package/src/lexicon/types/app/bsky/feed/getAuthorFeed.ts +1 -0
- package/src/lexicon/types/app/bsky/notification/listNotifications.ts +1 -0
- package/src/services/moderation/index.ts +12 -0
- package/tests/seeds/author-feed.ts +95 -0
- package/tests/views/__snapshots__/author-feed.test.ts.snap +10 -10
- package/tests/views/author-feed.test.ts +20 -2
- package/tests/views/notifications.test.ts +7 -0
- package/tests/views/profile.test.ts +46 -0
|
@@ -5,7 +5,7 @@ export interface QueryParams {
|
|
|
5
5
|
actor: string;
|
|
6
6
|
limit: number;
|
|
7
7
|
cursor?: string;
|
|
8
|
-
filter: 'posts_with_replies' | 'posts_no_replies' | 'posts_with_media' | (string & {});
|
|
8
|
+
filter: 'posts_with_replies' | 'posts_no_replies' | 'posts_with_media' | 'posts_and_author_threads' | (string & {});
|
|
9
9
|
}
|
|
10
10
|
export declare type InputSchema = undefined;
|
|
11
11
|
export interface OutputSchema {
|
|
@@ -89,6 +89,7 @@ export declare class ModerationService {
|
|
|
89
89
|
legacyRefId: number | null;
|
|
90
90
|
} | null | undefined>;
|
|
91
91
|
getSubjectsDueForReversal(): Promise<ModerationSubjectStatusRow[]>;
|
|
92
|
+
isSubjectSuspended(did: string): Promise<boolean>;
|
|
92
93
|
revertState({ createdBy, createdAt, comment, action, subject, }: ReversibleModerationEvent): Promise<{
|
|
93
94
|
result: ModerationEventRow;
|
|
94
95
|
restored?: TakedownSubjects;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"sharp": "^0.32.6",
|
|
36
36
|
"typed-emitter": "^2.1.0",
|
|
37
37
|
"uint8arrays": "3.0.0",
|
|
38
|
-
"@atproto/api": "^0.7.
|
|
38
|
+
"@atproto/api": "^0.7.3",
|
|
39
39
|
"@atproto/common": "^0.3.3",
|
|
40
40
|
"@atproto/crypto": "^0.3.0",
|
|
41
41
|
"@atproto/syntax": "^0.1.5",
|
|
42
|
+
"@atproto/identity": "^0.3.2",
|
|
42
43
|
"@atproto/lexicon": "^0.3.1",
|
|
43
44
|
"@atproto/repo": "^0.3.6",
|
|
44
|
-
"@atproto/identity": "^0.3.2",
|
|
45
45
|
"@atproto/xrpc-server": "^0.4.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"@types/pg": "^8.6.6",
|
|
53
53
|
"@types/qs": "^6.9.7",
|
|
54
54
|
"axios": "^0.27.2",
|
|
55
|
-
"@atproto/api": "^0.7.
|
|
56
|
-
"@atproto/dev-env": "^0.2.
|
|
55
|
+
"@atproto/api": "^0.7.3",
|
|
56
|
+
"@atproto/dev-env": "^0.2.20",
|
|
57
57
|
"@atproto/lex-cli": "^0.2.5",
|
|
58
|
-
"@atproto/pds": "^0.3.
|
|
58
|
+
"@atproto/pds": "^0.3.8",
|
|
59
59
|
"@atproto/xrpc": "^0.4.1"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../../../../services/actor'
|
|
12
12
|
import { setRepoRev } from '../../../util'
|
|
13
13
|
import { createPipeline, noRules } from '../../../../pipeline'
|
|
14
|
+
import { ModerationService } from '../../../../services/moderation'
|
|
14
15
|
|
|
15
16
|
export default function (server: Server, ctx: AppContext) {
|
|
16
17
|
const getProfile = createPipeline(skeleton, hydration, noRules, presentation)
|
|
@@ -19,6 +20,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
19
20
|
handler: async ({ auth, params, res }) => {
|
|
20
21
|
const db = ctx.db.getReplica()
|
|
21
22
|
const actorService = ctx.services.actor(db)
|
|
23
|
+
const modService = ctx.services.moderation(ctx.db.getPrimary())
|
|
22
24
|
const viewer = 'did' in auth.credentials ? auth.credentials.did : null
|
|
23
25
|
const canViewTakendownProfile =
|
|
24
26
|
auth.credentials.type === 'role' && auth.credentials.triage
|
|
@@ -26,7 +28,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
26
28
|
const [result, repoRev] = await Promise.allSettled([
|
|
27
29
|
getProfile(
|
|
28
30
|
{ ...params, viewer, canViewTakendownProfile },
|
|
29
|
-
{ db, actorService },
|
|
31
|
+
{ db, actorService, modService },
|
|
30
32
|
),
|
|
31
33
|
actorService.getRepoRev(viewer),
|
|
32
34
|
])
|
|
@@ -50,17 +52,25 @@ const skeleton = async (
|
|
|
50
52
|
params: Params,
|
|
51
53
|
ctx: Context,
|
|
52
54
|
): Promise<SkeletonState> => {
|
|
53
|
-
const { actorService } = ctx
|
|
55
|
+
const { actorService, modService } = ctx
|
|
54
56
|
const { canViewTakendownProfile } = params
|
|
55
57
|
const actor = await actorService.getActor(params.actor, true)
|
|
56
58
|
if (!actor) {
|
|
57
59
|
throw new InvalidRequestError('Profile not found')
|
|
58
60
|
}
|
|
59
61
|
if (!canViewTakendownProfile && softDeleted(actor)) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
const isSuspended = await modService.isSubjectSuspended(actor.did)
|
|
63
|
+
if (isSuspended) {
|
|
64
|
+
throw new InvalidRequestError(
|
|
65
|
+
'Account has been temporarily suspended',
|
|
66
|
+
'AccountTakedown',
|
|
67
|
+
)
|
|
68
|
+
} else {
|
|
69
|
+
throw new InvalidRequestError(
|
|
70
|
+
'Account has been taken down',
|
|
71
|
+
'AccountTakedown',
|
|
72
|
+
)
|
|
73
|
+
}
|
|
64
74
|
}
|
|
65
75
|
return { params, actor }
|
|
66
76
|
}
|
|
@@ -95,6 +105,7 @@ const presentation = (state: HydrationState, ctx: Context) => {
|
|
|
95
105
|
type Context = {
|
|
96
106
|
db: Database
|
|
97
107
|
actorService: ActorService
|
|
108
|
+
modService: ModerationService
|
|
98
109
|
}
|
|
99
110
|
|
|
100
111
|
type Params = QueryParams & {
|
|
@@ -102,6 +102,13 @@ export const skeleton = async (
|
|
|
102
102
|
feedItemsQb = feedItemsQb.where((qb) =>
|
|
103
103
|
qb.where('post.replyParent', 'is', null).orWhere('type', '=', 'repost'),
|
|
104
104
|
)
|
|
105
|
+
} else if (filter === 'posts_and_author_threads') {
|
|
106
|
+
feedItemsQb = feedItemsQb.where((qb) =>
|
|
107
|
+
qb
|
|
108
|
+
.where('type', '=', 'repost')
|
|
109
|
+
.orWhere('post.replyParent', 'is', null)
|
|
110
|
+
.orWhere('post.replyRoot', 'like', `at://${actorDid}/%`),
|
|
111
|
+
)
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
const keyset = new FeedKeyset(ref('feed_item.sortAt'), ref('feed_item.cid'))
|
|
@@ -148,7 +148,7 @@ const presentation = (state: HydrationState) => {
|
|
|
148
148
|
labels: [...recordLabels, ...recordSelfLabels],
|
|
149
149
|
}
|
|
150
150
|
})
|
|
151
|
-
return { notifications, cursor }
|
|
151
|
+
return { notifications, cursor, seenAt: lastSeenNotifs }
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const getRecordMap = async (
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -5572,6 +5572,7 @@ export const schemaDict = {
|
|
|
5572
5572
|
'posts_with_replies',
|
|
5573
5573
|
'posts_no_replies',
|
|
5574
5574
|
'posts_with_media',
|
|
5575
|
+
'posts_and_author_threads',
|
|
5575
5576
|
],
|
|
5576
5577
|
default: 'posts_with_replies',
|
|
5577
5578
|
},
|
|
@@ -7324,6 +7325,10 @@ export const schemaDict = {
|
|
|
7324
7325
|
ref: 'lex:app.bsky.notification.listNotifications#notification',
|
|
7325
7326
|
},
|
|
7326
7327
|
},
|
|
7328
|
+
seenAt: {
|
|
7329
|
+
type: 'string',
|
|
7330
|
+
format: 'datetime',
|
|
7331
|
+
},
|
|
7327
7332
|
},
|
|
7328
7333
|
},
|
|
7329
7334
|
},
|
|
@@ -323,6 +323,18 @@ export class ModerationService {
|
|
|
323
323
|
return subjectsDueForReversal
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
async isSubjectSuspended(did: string): Promise<boolean> {
|
|
327
|
+
const res = await this.db.db
|
|
328
|
+
.selectFrom('moderation_subject_status')
|
|
329
|
+
.where('did', '=', did)
|
|
330
|
+
.where('recordPath', '=', '')
|
|
331
|
+
.where('suspendUntil', '>', new Date().toISOString())
|
|
332
|
+
.select('did')
|
|
333
|
+
.limit(1)
|
|
334
|
+
.executeTakeFirst()
|
|
335
|
+
return !!res
|
|
336
|
+
}
|
|
337
|
+
|
|
326
338
|
async revertState({
|
|
327
339
|
createdBy,
|
|
328
340
|
createdAt,
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { SeedClient } from '@atproto/dev-env'
|
|
2
|
+
import basicSeed from './basic'
|
|
3
|
+
|
|
4
|
+
export default async (sc: SeedClient) => {
|
|
5
|
+
await basicSeed(sc)
|
|
6
|
+
await sc.createAccount('eve', {
|
|
7
|
+
email: 'eve@test.com',
|
|
8
|
+
handle: 'eve.test',
|
|
9
|
+
password: 'eve-pass',
|
|
10
|
+
})
|
|
11
|
+
await sc.createAccount('fred', {
|
|
12
|
+
email: 'fred@test.com',
|
|
13
|
+
handle: 'fred.test',
|
|
14
|
+
password: 'fred-pass',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const alice = sc.dids.alice
|
|
18
|
+
const eve = sc.dids.eve
|
|
19
|
+
const fred = sc.dids.fred
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* Self thread
|
|
23
|
+
*/
|
|
24
|
+
await sc.post(eve, evePosts[0])
|
|
25
|
+
await sc.reply(
|
|
26
|
+
eve,
|
|
27
|
+
sc.posts[eve][0].ref,
|
|
28
|
+
sc.posts[eve][0].ref,
|
|
29
|
+
eveOwnThreadReplies[0],
|
|
30
|
+
)
|
|
31
|
+
await sc.reply(
|
|
32
|
+
eve,
|
|
33
|
+
sc.posts[eve][0].ref,
|
|
34
|
+
sc.replies[eve][0].ref,
|
|
35
|
+
eveOwnThreadReplies[1],
|
|
36
|
+
)
|
|
37
|
+
await sc.reply(
|
|
38
|
+
eve,
|
|
39
|
+
sc.posts[eve][0].ref,
|
|
40
|
+
sc.replies[eve][1].ref,
|
|
41
|
+
eveOwnThreadReplies[2],
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Two replies to Alice
|
|
46
|
+
*/
|
|
47
|
+
await sc.reply(
|
|
48
|
+
eve,
|
|
49
|
+
sc.posts[alice][1].ref,
|
|
50
|
+
sc.posts[alice][1].ref,
|
|
51
|
+
eveAliceReplies[0],
|
|
52
|
+
)
|
|
53
|
+
await sc.reply(
|
|
54
|
+
eve,
|
|
55
|
+
sc.posts[alice][1].ref,
|
|
56
|
+
sc.replies[eve][3].ref,
|
|
57
|
+
eveAliceReplies[1],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Two replies to Fred, who replied to Eve's root post. This creates a
|
|
62
|
+
* "detached" thread, where one Fred post breaks the continuity.
|
|
63
|
+
*/
|
|
64
|
+
await sc.post(eve, evePosts[1])
|
|
65
|
+
await sc.reply(
|
|
66
|
+
fred,
|
|
67
|
+
sc.posts[eve][1].ref,
|
|
68
|
+
sc.posts[eve][1].ref,
|
|
69
|
+
fredReplies[0],
|
|
70
|
+
)
|
|
71
|
+
await sc.reply(
|
|
72
|
+
eve,
|
|
73
|
+
sc.posts[eve][1].ref,
|
|
74
|
+
sc.replies[fred][0].ref,
|
|
75
|
+
eveFredReplies[0],
|
|
76
|
+
)
|
|
77
|
+
await sc.reply(
|
|
78
|
+
eve,
|
|
79
|
+
sc.posts[eve][1].ref,
|
|
80
|
+
sc.replies[eve][4].ref,
|
|
81
|
+
eveFredReplies[1],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return sc
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const evePosts = ['eve own thread', 'eve detached thread']
|
|
88
|
+
const eveOwnThreadReplies = [
|
|
89
|
+
'eve own reply 1',
|
|
90
|
+
'eve own reply 2',
|
|
91
|
+
'eve own reply 3',
|
|
92
|
+
]
|
|
93
|
+
const eveAliceReplies = ['eve reply to alice 1', 'eve reply to alice 2']
|
|
94
|
+
const eveFredReplies = ['eve reply to fred 1', 'eve reply to fred 2']
|
|
95
|
+
const fredReplies = ['fred reply to eve 1']
|
|
@@ -178,7 +178,7 @@ Array [
|
|
|
178
178
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
179
179
|
"text": "again",
|
|
180
180
|
},
|
|
181
|
-
"replyCount":
|
|
181
|
+
"replyCount": 3,
|
|
182
182
|
"repostCount": 1,
|
|
183
183
|
"uri": "record(2)",
|
|
184
184
|
"viewer": Object {},
|
|
@@ -395,7 +395,7 @@ Array [
|
|
|
395
395
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
396
396
|
"text": "again",
|
|
397
397
|
},
|
|
398
|
-
"replyCount":
|
|
398
|
+
"replyCount": 3,
|
|
399
399
|
"repostCount": 1,
|
|
400
400
|
"uri": "record(2)",
|
|
401
401
|
"viewer": Object {},
|
|
@@ -590,7 +590,7 @@ Array [
|
|
|
590
590
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
591
591
|
"text": "again",
|
|
592
592
|
},
|
|
593
|
-
"replyCount":
|
|
593
|
+
"replyCount": 3,
|
|
594
594
|
"repostCount": 1,
|
|
595
595
|
"uri": "record(1)",
|
|
596
596
|
"viewer": Object {
|
|
@@ -638,7 +638,7 @@ Array [
|
|
|
638
638
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
639
639
|
"text": "again",
|
|
640
640
|
},
|
|
641
|
-
"replyCount":
|
|
641
|
+
"replyCount": 3,
|
|
642
642
|
"repostCount": 1,
|
|
643
643
|
"uri": "record(1)",
|
|
644
644
|
"viewer": Object {
|
|
@@ -961,7 +961,7 @@ Array [
|
|
|
961
961
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
962
962
|
"text": "again",
|
|
963
963
|
},
|
|
964
|
-
"replyCount":
|
|
964
|
+
"replyCount": 3,
|
|
965
965
|
"repostCount": 1,
|
|
966
966
|
"uri": "record(6)",
|
|
967
967
|
"viewer": Object {
|
|
@@ -1009,7 +1009,7 @@ Array [
|
|
|
1009
1009
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
1010
1010
|
"text": "again",
|
|
1011
1011
|
},
|
|
1012
|
-
"replyCount":
|
|
1012
|
+
"replyCount": 3,
|
|
1013
1013
|
"repostCount": 1,
|
|
1014
1014
|
"uri": "record(6)",
|
|
1015
1015
|
"viewer": Object {
|
|
@@ -1326,7 +1326,7 @@ Array [
|
|
|
1326
1326
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
1327
1327
|
"text": "again",
|
|
1328
1328
|
},
|
|
1329
|
-
"replyCount":
|
|
1329
|
+
"replyCount": 3,
|
|
1330
1330
|
"repostCount": 1,
|
|
1331
1331
|
"uri": "record(3)",
|
|
1332
1332
|
"viewer": Object {
|
|
@@ -1376,7 +1376,7 @@ Array [
|
|
|
1376
1376
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
1377
1377
|
"text": "again",
|
|
1378
1378
|
},
|
|
1379
|
-
"replyCount":
|
|
1379
|
+
"replyCount": 3,
|
|
1380
1380
|
"repostCount": 1,
|
|
1381
1381
|
"uri": "record(3)",
|
|
1382
1382
|
"viewer": Object {
|
|
@@ -1765,7 +1765,7 @@ Array [
|
|
|
1765
1765
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
1766
1766
|
"text": "again",
|
|
1767
1767
|
},
|
|
1768
|
-
"replyCount":
|
|
1768
|
+
"replyCount": 3,
|
|
1769
1769
|
"repostCount": 1,
|
|
1770
1770
|
"uri": "record(4)",
|
|
1771
1771
|
"viewer": Object {
|
|
@@ -1987,7 +1987,7 @@ Array [
|
|
|
1987
1987
|
"createdAt": "1970-01-01T00:00:00.000000Z",
|
|
1988
1988
|
"text": "again",
|
|
1989
1989
|
},
|
|
1990
|
-
"replyCount":
|
|
1990
|
+
"replyCount": 3,
|
|
1991
1991
|
"repostCount": 1,
|
|
1992
1992
|
"uri": "record(4)",
|
|
1993
1993
|
"viewer": Object {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AtpAgent from '@atproto/api'
|
|
2
2
|
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
3
3
|
import { forSnapshot, paginateAll, stripViewerFromPost } from '../_util'
|
|
4
|
-
import
|
|
4
|
+
import authorFeedSeed from '../seeds/author-feed'
|
|
5
5
|
import { isRecord } from '../../src/lexicon/types/app/bsky/feed/post'
|
|
6
6
|
import { isView as isEmbedRecordWithMedia } from '../../src/lexicon/types/app/bsky/embed/recordWithMedia'
|
|
7
7
|
import { isView as isImageEmbed } from '../../src/lexicon/types/app/bsky/embed/images'
|
|
@@ -16,6 +16,7 @@ describe('pds author feed views', () => {
|
|
|
16
16
|
let bob: string
|
|
17
17
|
let carol: string
|
|
18
18
|
let dan: string
|
|
19
|
+
let eve: string
|
|
19
20
|
|
|
20
21
|
beforeAll(async () => {
|
|
21
22
|
network = await TestNetwork.create({
|
|
@@ -23,12 +24,13 @@ describe('pds author feed views', () => {
|
|
|
23
24
|
})
|
|
24
25
|
agent = network.bsky.getClient()
|
|
25
26
|
sc = network.getSeedClient()
|
|
26
|
-
await
|
|
27
|
+
await authorFeedSeed(sc)
|
|
27
28
|
await network.processAll()
|
|
28
29
|
alice = sc.dids.alice
|
|
29
30
|
bob = sc.dids.bob
|
|
30
31
|
carol = sc.dids.carol
|
|
31
32
|
dan = sc.dids.dan
|
|
33
|
+
eve = sc.dids.eve
|
|
32
34
|
})
|
|
33
35
|
|
|
34
36
|
afterAll(async () => {
|
|
@@ -305,4 +307,20 @@ describe('pds author feed views', () => {
|
|
|
305
307
|
}),
|
|
306
308
|
).toBeTruthy()
|
|
307
309
|
})
|
|
310
|
+
|
|
311
|
+
it('posts_and_author_threads includes self-replies', async () => {
|
|
312
|
+
const { data: eveFeed } = await agent.api.app.bsky.feed.getAuthorFeed({
|
|
313
|
+
actor: eve,
|
|
314
|
+
filter: 'posts_and_author_threads',
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
expect(eveFeed.feed.length).toEqual(7)
|
|
318
|
+
expect(
|
|
319
|
+
eveFeed.feed.some(({ post }) => {
|
|
320
|
+
return (
|
|
321
|
+
isRecord(post.record) && post.record.reply && post.author.did === eve
|
|
322
|
+
)
|
|
323
|
+
}),
|
|
324
|
+
).toBeTruthy()
|
|
325
|
+
})
|
|
308
326
|
})
|
|
@@ -176,6 +176,13 @@ describe('notification views', () => {
|
|
|
176
176
|
encoding: 'application/json',
|
|
177
177
|
},
|
|
178
178
|
)
|
|
179
|
+
const full2 = await agent.api.app.bsky.notification.listNotifications(
|
|
180
|
+
{},
|
|
181
|
+
{ headers: await network.serviceHeaders(alice) },
|
|
182
|
+
)
|
|
183
|
+
expect(full2.data.notifications.length).toBe(full.data.notifications.length)
|
|
184
|
+
expect(full2.data.seenAt).toEqual(seenAt)
|
|
185
|
+
|
|
179
186
|
const notifCount = await agent.api.app.bsky.notification.getUnreadCount(
|
|
180
187
|
{},
|
|
181
188
|
{ headers: await network.serviceHeaders(alice) },
|
|
@@ -224,6 +224,52 @@ describe('pds profile views', () => {
|
|
|
224
224
|
)
|
|
225
225
|
})
|
|
226
226
|
|
|
227
|
+
it('blocked by actor suspension', async () => {
|
|
228
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
229
|
+
{
|
|
230
|
+
event: {
|
|
231
|
+
$type: 'com.atproto.admin.defs#modEventTakedown',
|
|
232
|
+
durationInHours: 1,
|
|
233
|
+
},
|
|
234
|
+
subject: {
|
|
235
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
236
|
+
did: alice,
|
|
237
|
+
},
|
|
238
|
+
createdBy: 'did:example:admin',
|
|
239
|
+
reason: 'Y',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
encoding: 'application/json',
|
|
243
|
+
headers: network.pds.adminAuthHeaders(),
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
const promise = agent.api.app.bsky.actor.getProfile(
|
|
247
|
+
{ actor: alice },
|
|
248
|
+
{ headers: await network.serviceHeaders(bob) },
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
await expect(promise).rejects.toThrow(
|
|
252
|
+
'Account has been temporarily suspended',
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
// Cleanup
|
|
256
|
+
await agent.api.com.atproto.admin.emitModerationEvent(
|
|
257
|
+
{
|
|
258
|
+
event: { $type: 'com.atproto.admin.defs#modEventReverseTakedown' },
|
|
259
|
+
subject: {
|
|
260
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
261
|
+
did: alice,
|
|
262
|
+
},
|
|
263
|
+
createdBy: 'did:example:admin',
|
|
264
|
+
reason: 'Y',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
encoding: 'application/json',
|
|
268
|
+
headers: network.pds.adminAuthHeaders(),
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
})
|
|
272
|
+
|
|
227
273
|
async function updateProfile(did: string, record: Record<string, unknown>) {
|
|
228
274
|
return await pdsAgent.api.com.atproto.repo.putRecord(
|
|
229
275
|
{
|