@atproto/dev-env 0.0.0-spaces-alpha-20260818163953
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 +3424 -0
- package/LICENSE.txt +7 -0
- package/README.md +39 -0
- package/bin.js +7 -0
- package/dist/bin-multi-pds.d.ts +2 -0
- package/dist/bin-multi-pds.d.ts.map +1 -0
- package/dist/bin-multi-pds.js +130 -0
- package/dist/bin-multi-pds.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +56 -0
- package/dist/bin.js.map +1 -0
- package/dist/bsky.d.ts +27 -0
- package/dist/bsky.d.ts.map +1 -0
- package/dist/bsky.js +165 -0
- package/dist/bsky.js.map +1 -0
- package/dist/bsync.d.ts +22 -0
- package/dist/bsync.d.ts.map +1 -0
- package/dist/bsync.js +54 -0
- package/dist/bsync.js.map +1 -0
- package/dist/const.d.ts +4 -0
- package/dist/const.d.ts.map +1 -0
- package/dist/const.js +4 -0
- package/dist/const.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/feed-gen.d.ts +15 -0
- package/dist/feed-gen.d.ts.map +1 -0
- package/dist/feed-gen.js +73 -0
- package/dist/feed-gen.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/introspect.d.ts +22 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +65 -0
- package/dist/introspect.js.map +1 -0
- package/dist/mock/data.d.ts +3 -0
- package/dist/mock/data.d.ts.map +1 -0
- package/dist/mock/data.js +46 -0
- package/dist/mock/data.js.map +1 -0
- package/dist/mock/img/blur-hash-avatar-b64.d.ts +3 -0
- package/dist/mock/img/blur-hash-avatar-b64.d.ts.map +1 -0
- package/dist/mock/img/blur-hash-avatar-b64.js +2 -0
- package/dist/mock/img/blur-hash-avatar-b64.js.map +1 -0
- package/dist/mock/img/labeled-img-b64.d.ts +3 -0
- package/dist/mock/img/labeled-img-b64.d.ts.map +1 -0
- package/dist/mock/img/labeled-img-b64.js +2 -0
- package/dist/mock/img/labeled-img-b64.js.map +1 -0
- package/dist/mock/index.d.ts +3 -0
- package/dist/mock/index.d.ts.map +1 -0
- package/dist/mock/index.js +574 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/moderator-client.d.ts +58 -0
- package/dist/moderator-client.d.ts.map +1 -0
- package/dist/moderator-client.js +110 -0
- package/dist/moderator-client.js.map +1 -0
- package/dist/network-no-appview.d.ts +21 -0
- package/dist/network-no-appview.d.ts.map +1 -0
- package/dist/network-no-appview.js +74 -0
- package/dist/network-no-appview.js.map +1 -0
- package/dist/network.d.ts +32 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +190 -0
- package/dist/network.js.map +1 -0
- package/dist/ozone.d.ts +31 -0
- package/dist/ozone.d.ts.map +1 -0
- package/dist/ozone.js +254 -0
- package/dist/ozone.js.map +1 -0
- package/dist/pds.d.ts +23 -0
- package/dist/pds.d.ts.map +1 -0
- package/dist/pds.js +97 -0
- package/dist/pds.js.map +1 -0
- package/dist/plc.d.ts +14 -0
- package/dist/plc.d.ts.map +1 -0
- package/dist/plc.js +28 -0
- package/dist/plc.js.map +1 -0
- package/dist/seed/author-feed.d.ts +4 -0
- package/dist/seed/author-feed.d.ts.map +1 -0
- package/dist/seed/author-feed.js +50 -0
- package/dist/seed/author-feed.js.map +1 -0
- package/dist/seed/basic.d.ts +16 -0
- package/dist/seed/basic.d.ts.map +1 -0
- package/dist/seed/basic.js +113 -0
- package/dist/seed/basic.js.map +1 -0
- package/dist/seed/client.d.ts +151 -0
- package/dist/seed/client.d.ts.map +1 -0
- package/dist/seed/client.js +365 -0
- package/dist/seed/client.js.map +1 -0
- package/dist/seed/follows.d.ts +4 -0
- package/dist/seed/follows.d.ts.map +1 -0
- package/dist/seed/follows.js +55 -0
- package/dist/seed/follows.js.map +1 -0
- package/dist/seed/index.d.ts +11 -0
- package/dist/seed/index.d.ts.map +1 -0
- package/dist/seed/index.js +11 -0
- package/dist/seed/index.js.map +1 -0
- package/dist/seed/likes.d.ts +4 -0
- package/dist/seed/likes.d.ts.map +1 -0
- package/dist/seed/likes.js +37 -0
- package/dist/seed/likes.js.map +1 -0
- package/dist/seed/quotes.d.ts +4 -0
- package/dist/seed/quotes.d.ts.map +1 -0
- package/dist/seed/quotes.js +21 -0
- package/dist/seed/quotes.js.map +1 -0
- package/dist/seed/reposts.d.ts +4 -0
- package/dist/seed/reposts.d.ts.map +1 -0
- package/dist/seed/reposts.js +17 -0
- package/dist/seed/reposts.js.map +1 -0
- package/dist/seed/thread-v2.d.ts +227 -0
- package/dist/seed/thread-v2.d.ts.map +1 -0
- package/dist/seed/thread-v2.js +755 -0
- package/dist/seed/thread-v2.js.map +1 -0
- package/dist/seed/users-bulk.d.ts +3 -0
- package/dist/seed/users-bulk.d.ts.map +1 -0
- package/dist/seed/users-bulk.js +224 -0
- package/dist/seed/users-bulk.js.map +1 -0
- package/dist/seed/users.d.ts +3 -0
- package/dist/seed/users.d.ts.map +1 -0
- package/dist/seed/users.js +45 -0
- package/dist/seed/users.js.map +1 -0
- package/dist/seed/verifications.d.ts +5 -0
- package/dist/seed/verifications.d.ts.map +1 -0
- package/dist/seed/verifications.js +164 -0
- package/dist/seed/verifications.js.map +1 -0
- package/dist/service-profile-lexicon.d.ts +11 -0
- package/dist/service-profile-lexicon.d.ts.map +1 -0
- package/dist/service-profile-lexicon.js +115 -0
- package/dist/service-profile-lexicon.js.map +1 -0
- package/dist/service-profile-ozone.d.ts +18 -0
- package/dist/service-profile-ozone.d.ts.map +1 -0
- package/dist/service-profile-ozone.js +333 -0
- package/dist/service-profile-ozone.js.map +1 -0
- package/dist/service-profile.d.ts +24 -0
- package/dist/service-profile.d.ts.map +1 -0
- package/dist/service-profile.js +61 -0
- package/dist/service-profile.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +14 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +86 -0
- package/dist/util.js.map +1 -0
- package/package.json +69 -0
- package/src/bin-multi-pds.ts +166 -0
- package/src/bin.ts +64 -0
- package/src/bsky.ts +198 -0
- package/src/bsync.ts +66 -0
- package/src/const.ts +3 -0
- package/src/env.ts +9 -0
- package/src/feed-gen.ts +96 -0
- package/src/index.ts +17 -0
- package/src/introspect.ts +91 -0
- package/src/mock/data.ts +46 -0
- package/src/mock/img/blur-hash-avatar-b64.ts +1 -0
- package/src/mock/img/labeled-img-b64.ts +1 -0
- package/src/mock/index.ts +702 -0
- package/src/moderator-client.ts +267 -0
- package/src/network-no-appview.ts +89 -0
- package/src/network.ts +243 -0
- package/src/ozone.ts +287 -0
- package/src/pds.ts +118 -0
- package/src/plc.ts +33 -0
- package/src/seed/author-feed.ts +98 -0
- package/src/seed/basic.ts +189 -0
- package/src/seed/client.ts +676 -0
- package/src/seed/follows.ts +57 -0
- package/src/seed/index.ts +11 -0
- package/src/seed/likes.ts +43 -0
- package/src/seed/quotes.ts +53 -0
- package/src/seed/reposts.ts +18 -0
- package/src/seed/thread-v2.ts +925 -0
- package/src/seed/users-bulk.ts +229 -0
- package/src/seed/users.ts +64 -0
- package/src/seed/verifications.ts +254 -0
- package/src/service-profile-lexicon.ts +127 -0
- package/src/service-profile-ozone.ts +375 -0
- package/src/service-profile.ts +95 -0
- package/src/types.ts +68 -0
- package/src/util.ts +118 -0
- package/tsconfig.build.json +25 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { CID } from 'multiformats/cid'
|
|
5
|
+
import type {
|
|
6
|
+
$Typed,
|
|
7
|
+
AppBskyActorProfile,
|
|
8
|
+
AppBskyFeedLike,
|
|
9
|
+
AppBskyFeedPost,
|
|
10
|
+
AppBskyFeedRepost,
|
|
11
|
+
AppBskyGraphBlock,
|
|
12
|
+
AppBskyGraphFollow,
|
|
13
|
+
AppBskyGraphList,
|
|
14
|
+
AppBskyGraphVerification,
|
|
15
|
+
AppBskyRichtextFacet,
|
|
16
|
+
AtpAgent,
|
|
17
|
+
ChatBskyConvoDefs,
|
|
18
|
+
ComAtprotoAdminDefs,
|
|
19
|
+
ComAtprotoModerationCreateReport,
|
|
20
|
+
ComAtprotoRepoStrongRef,
|
|
21
|
+
} from '@atproto/api'
|
|
22
|
+
import type { CidString, Client } from '@atproto/lex'
|
|
23
|
+
import type { BlobRef } from '@atproto/lexicon'
|
|
24
|
+
import {
|
|
25
|
+
AtUri,
|
|
26
|
+
type AtUriString,
|
|
27
|
+
type DidString,
|
|
28
|
+
type HandleString,
|
|
29
|
+
} from '@atproto/syntax'
|
|
30
|
+
import type { TestNetworkNoAppView } from '../network-no-appview.js'
|
|
31
|
+
|
|
32
|
+
// Makes it simple to create data via the XRPC client,
|
|
33
|
+
// and keeps track of all created data in memory for convenience.
|
|
34
|
+
|
|
35
|
+
let AVATAR_IMG: Uint8Array | undefined
|
|
36
|
+
|
|
37
|
+
// AVATAR_PATH is defined in a non-CWD-dependant way, so this works
|
|
38
|
+
// for any consumer of this package, even outside the atproto repo.
|
|
39
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
40
|
+
const AVATAR_PATH = path.resolve(
|
|
41
|
+
__dirname,
|
|
42
|
+
'../../assets/key-portrait-small.jpg',
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
export type ImageRef = {
|
|
46
|
+
image: BlobRef
|
|
47
|
+
alt: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class RecordRef {
|
|
51
|
+
uri: AtUri
|
|
52
|
+
cid: CID
|
|
53
|
+
|
|
54
|
+
constructor(uri: AtUri | string, cid: CID | string) {
|
|
55
|
+
this.uri = new AtUri(uri.toString())
|
|
56
|
+
this.cid = CID.parse(cid.toString())
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get raw(): { uri: AtUriString; cid: CidString } {
|
|
60
|
+
return {
|
|
61
|
+
uri: this.uri.toString(),
|
|
62
|
+
cid: this.cid.toString(),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get uriStr(): AtUriString {
|
|
67
|
+
return this.uri.toString()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get cidStr(): CidString {
|
|
71
|
+
return this.cid.toString()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type Account = {
|
|
76
|
+
did: DidString
|
|
77
|
+
accessJwt: string
|
|
78
|
+
refreshJwt: string
|
|
79
|
+
handle: HandleString
|
|
80
|
+
email: string
|
|
81
|
+
password: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class SeedClient<
|
|
85
|
+
Network extends TestNetworkNoAppView = TestNetworkNoAppView,
|
|
86
|
+
> {
|
|
87
|
+
accounts: Record<DidString, Account>
|
|
88
|
+
profiles: Record<
|
|
89
|
+
DidString,
|
|
90
|
+
{
|
|
91
|
+
displayName: string
|
|
92
|
+
description: string
|
|
93
|
+
avatar: { cid: string; mimeType: string }
|
|
94
|
+
joinedViaStarterPack: RecordRef | undefined
|
|
95
|
+
ref: RecordRef
|
|
96
|
+
}
|
|
97
|
+
>
|
|
98
|
+
follows: Record<DidString, Record<DidString, RecordRef>>
|
|
99
|
+
blocks: Record<DidString, Record<DidString, RecordRef>>
|
|
100
|
+
mutes: Record<DidString, Set<DidString>>
|
|
101
|
+
posts: Record<
|
|
102
|
+
DidString,
|
|
103
|
+
{ text: string; ref: RecordRef; images: ImageRef[]; quote?: RecordRef }[]
|
|
104
|
+
>
|
|
105
|
+
likes: Record<DidString, Record<string, AtUri>>
|
|
106
|
+
replies: Record<
|
|
107
|
+
string,
|
|
108
|
+
{ text: string; ref: RecordRef; images: ImageRef[] }[]
|
|
109
|
+
>
|
|
110
|
+
reposts: Record<DidString, RecordRef[]>
|
|
111
|
+
lists: Record<
|
|
112
|
+
DidString,
|
|
113
|
+
Record<
|
|
114
|
+
AtUriString,
|
|
115
|
+
{
|
|
116
|
+
ref: RecordRef
|
|
117
|
+
items: Record<DidString, RecordRef>
|
|
118
|
+
}
|
|
119
|
+
>
|
|
120
|
+
>
|
|
121
|
+
feedgens: Record<
|
|
122
|
+
DidString,
|
|
123
|
+
Record<
|
|
124
|
+
AtUriString,
|
|
125
|
+
{
|
|
126
|
+
ref: RecordRef
|
|
127
|
+
items: Record<string, RecordRef>
|
|
128
|
+
}
|
|
129
|
+
>
|
|
130
|
+
>
|
|
131
|
+
starterpacks: Record<
|
|
132
|
+
DidString,
|
|
133
|
+
Record<
|
|
134
|
+
AtUriString,
|
|
135
|
+
{
|
|
136
|
+
ref: RecordRef
|
|
137
|
+
name: string
|
|
138
|
+
list: RecordRef
|
|
139
|
+
feeds: readonly AtUriString[]
|
|
140
|
+
}
|
|
141
|
+
>
|
|
142
|
+
>
|
|
143
|
+
|
|
144
|
+
verifications: Record<DidString, Record<DidString, AtUri>>
|
|
145
|
+
|
|
146
|
+
dids: Record<string, DidString>
|
|
147
|
+
|
|
148
|
+
constructor(
|
|
149
|
+
public network: Network,
|
|
150
|
+
public agent: AtpAgent,
|
|
151
|
+
public client: Client,
|
|
152
|
+
) {
|
|
153
|
+
this.accounts = {}
|
|
154
|
+
this.profiles = {}
|
|
155
|
+
this.follows = {}
|
|
156
|
+
this.blocks = {}
|
|
157
|
+
this.mutes = {}
|
|
158
|
+
this.posts = {}
|
|
159
|
+
this.likes = {}
|
|
160
|
+
this.replies = {}
|
|
161
|
+
this.reposts = {}
|
|
162
|
+
this.lists = {}
|
|
163
|
+
this.feedgens = {}
|
|
164
|
+
this.starterpacks = {}
|
|
165
|
+
this.verifications = {}
|
|
166
|
+
this.dids = {}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async createAccount(
|
|
170
|
+
shortName: string,
|
|
171
|
+
params: {
|
|
172
|
+
handle: HandleString
|
|
173
|
+
email: string
|
|
174
|
+
password: string
|
|
175
|
+
inviteCode?: string
|
|
176
|
+
},
|
|
177
|
+
): Promise<Account> {
|
|
178
|
+
const { data: account } =
|
|
179
|
+
await this.agent.com.atproto.server.createAccount(params)
|
|
180
|
+
const did = account.did as DidString
|
|
181
|
+
this.dids[shortName] = did
|
|
182
|
+
this.accounts[account.did] = {
|
|
183
|
+
// @NOTE type case needed until we move test to lex-sdk
|
|
184
|
+
...(account as typeof account & { handle: HandleString }),
|
|
185
|
+
did,
|
|
186
|
+
email: params.email,
|
|
187
|
+
password: params.password,
|
|
188
|
+
}
|
|
189
|
+
return this.accounts[account.did]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async updateHandle(by: DidString, handle: HandleString) {
|
|
193
|
+
await this.agent.com.atproto.identity.updateHandle(
|
|
194
|
+
{ handle },
|
|
195
|
+
{ encoding: 'application/json', headers: this.getHeaders(by) },
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async createProfile(
|
|
200
|
+
by: DidString,
|
|
201
|
+
displayName: string,
|
|
202
|
+
description: string,
|
|
203
|
+
selfLabels?: readonly string[],
|
|
204
|
+
joinedViaStarterPack?: RecordRef,
|
|
205
|
+
overrides?: Partial<AppBskyActorProfile.Record>,
|
|
206
|
+
): Promise<{
|
|
207
|
+
displayName: string
|
|
208
|
+
description: string
|
|
209
|
+
avatar: { cid: string; mimeType: string }
|
|
210
|
+
ref: RecordRef
|
|
211
|
+
joinedViaStarterPack?: RecordRef
|
|
212
|
+
}> {
|
|
213
|
+
AVATAR_IMG ??= await fs.readFile(AVATAR_PATH)
|
|
214
|
+
|
|
215
|
+
let avatarBlob
|
|
216
|
+
{
|
|
217
|
+
const res = await this.agent.com.atproto.repo.uploadBlob(AVATAR_IMG, {
|
|
218
|
+
encoding: 'image/jpeg',
|
|
219
|
+
headers: this.getHeaders(by),
|
|
220
|
+
} as any)
|
|
221
|
+
avatarBlob = res.data.blob
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
{
|
|
225
|
+
const res = await this.agent.app.bsky.actor.profile.create(
|
|
226
|
+
{ repo: by },
|
|
227
|
+
{
|
|
228
|
+
displayName,
|
|
229
|
+
description,
|
|
230
|
+
avatar: avatarBlob,
|
|
231
|
+
labels: selfLabels
|
|
232
|
+
? {
|
|
233
|
+
$type: 'com.atproto.label.defs#selfLabels',
|
|
234
|
+
values: selfLabels.map((val) => ({ val })),
|
|
235
|
+
}
|
|
236
|
+
: undefined,
|
|
237
|
+
joinedViaStarterPack: joinedViaStarterPack?.raw,
|
|
238
|
+
createdAt: new Date().toISOString(),
|
|
239
|
+
...overrides,
|
|
240
|
+
},
|
|
241
|
+
this.getHeaders(by),
|
|
242
|
+
)
|
|
243
|
+
this.profiles[by] = {
|
|
244
|
+
displayName,
|
|
245
|
+
description,
|
|
246
|
+
avatar: avatarBlob,
|
|
247
|
+
joinedViaStarterPack,
|
|
248
|
+
ref: new RecordRef(res.uri, res.cid),
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return this.profiles[by]
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async updateProfile(by: DidString, record: Record<string, unknown>) {
|
|
255
|
+
const res = await this.agent.com.atproto.repo.putRecord(
|
|
256
|
+
{
|
|
257
|
+
repo: by,
|
|
258
|
+
collection: 'app.bsky.actor.profile',
|
|
259
|
+
rkey: 'self',
|
|
260
|
+
record,
|
|
261
|
+
},
|
|
262
|
+
{ headers: this.getHeaders(by), encoding: 'application/json' },
|
|
263
|
+
)
|
|
264
|
+
this.profiles[by] = {
|
|
265
|
+
...(this.profiles[by] ?? {}),
|
|
266
|
+
...record,
|
|
267
|
+
ref: new RecordRef(res.data.uri, res.data.cid),
|
|
268
|
+
}
|
|
269
|
+
return this.profiles[by]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async follow(
|
|
273
|
+
from: DidString,
|
|
274
|
+
to: DidString,
|
|
275
|
+
overrides?: Partial<AppBskyGraphFollow.Record>,
|
|
276
|
+
) {
|
|
277
|
+
const res = await this.agent.app.bsky.graph.follow.create(
|
|
278
|
+
{ repo: from },
|
|
279
|
+
{
|
|
280
|
+
subject: to,
|
|
281
|
+
createdAt: new Date().toISOString(),
|
|
282
|
+
...overrides,
|
|
283
|
+
},
|
|
284
|
+
this.getHeaders(from),
|
|
285
|
+
)
|
|
286
|
+
this.follows[from] ??= {}
|
|
287
|
+
this.follows[from][to] = new RecordRef(res.uri, res.cid)
|
|
288
|
+
return this.follows[from][to]
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async unfollow(from: DidString, to: DidString) {
|
|
292
|
+
const follow = this.follows[from][to]
|
|
293
|
+
if (!follow) {
|
|
294
|
+
throw new Error('follow does not exist')
|
|
295
|
+
}
|
|
296
|
+
await this.agent.app.bsky.graph.follow.delete(
|
|
297
|
+
{ repo: from, rkey: follow.uri.rkey },
|
|
298
|
+
this.getHeaders(from),
|
|
299
|
+
)
|
|
300
|
+
delete this.follows[from][to]
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async block(
|
|
304
|
+
from: DidString,
|
|
305
|
+
to: DidString,
|
|
306
|
+
overrides?: Partial<AppBskyGraphBlock.Record>,
|
|
307
|
+
) {
|
|
308
|
+
const res = await this.agent.app.bsky.graph.block.create(
|
|
309
|
+
{ repo: from },
|
|
310
|
+
{
|
|
311
|
+
subject: to,
|
|
312
|
+
createdAt: new Date().toISOString(),
|
|
313
|
+
...overrides,
|
|
314
|
+
},
|
|
315
|
+
this.getHeaders(from),
|
|
316
|
+
)
|
|
317
|
+
this.blocks[from] ??= {}
|
|
318
|
+
this.blocks[from][to] = new RecordRef(res.uri, res.cid)
|
|
319
|
+
return this.blocks[from][to]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async unblock(from: DidString, to: DidString) {
|
|
323
|
+
const block = this.blocks[from][to]
|
|
324
|
+
if (!block) {
|
|
325
|
+
throw new Error('block does not exist')
|
|
326
|
+
}
|
|
327
|
+
await this.agent.app.bsky.graph.block.delete(
|
|
328
|
+
{ repo: from, rkey: block.uri.rkey },
|
|
329
|
+
this.getHeaders(from),
|
|
330
|
+
)
|
|
331
|
+
delete this.blocks[from][to]
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async mute(from: DidString, to: DidString) {
|
|
335
|
+
await this.agent.app.bsky.graph.muteActor(
|
|
336
|
+
{
|
|
337
|
+
actor: to,
|
|
338
|
+
},
|
|
339
|
+
{ headers: this.getHeaders(from) },
|
|
340
|
+
)
|
|
341
|
+
this.mutes[from] ??= new Set()
|
|
342
|
+
this.mutes[from].add(to)
|
|
343
|
+
return this.mutes[from][to]
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async post(
|
|
347
|
+
by: DidString,
|
|
348
|
+
text: string,
|
|
349
|
+
facets?: AppBskyRichtextFacet.Main[],
|
|
350
|
+
images?: ImageRef[],
|
|
351
|
+
quote?: RecordRef,
|
|
352
|
+
overrides?: Partial<AppBskyFeedPost.Record>,
|
|
353
|
+
) {
|
|
354
|
+
const imageEmbed = images && {
|
|
355
|
+
$type: 'app.bsky.embed.images',
|
|
356
|
+
images,
|
|
357
|
+
}
|
|
358
|
+
const recordEmbed = quote && {
|
|
359
|
+
record: { uri: quote.uriStr, cid: quote.cidStr },
|
|
360
|
+
}
|
|
361
|
+
const embed =
|
|
362
|
+
imageEmbed && recordEmbed
|
|
363
|
+
? {
|
|
364
|
+
$type: 'app.bsky.embed.recordWithMedia',
|
|
365
|
+
record: recordEmbed,
|
|
366
|
+
media: imageEmbed,
|
|
367
|
+
}
|
|
368
|
+
: recordEmbed
|
|
369
|
+
? { $type: 'app.bsky.embed.record', ...recordEmbed }
|
|
370
|
+
: imageEmbed
|
|
371
|
+
const res = await this.agent.app.bsky.feed.post.create(
|
|
372
|
+
{ repo: by },
|
|
373
|
+
{
|
|
374
|
+
text: text,
|
|
375
|
+
facets,
|
|
376
|
+
embed,
|
|
377
|
+
createdAt: new Date().toISOString(),
|
|
378
|
+
...overrides,
|
|
379
|
+
},
|
|
380
|
+
this.getHeaders(by),
|
|
381
|
+
)
|
|
382
|
+
this.posts[by] ??= []
|
|
383
|
+
const post = {
|
|
384
|
+
text,
|
|
385
|
+
ref: new RecordRef(res.uri, res.cid),
|
|
386
|
+
images: images ?? [],
|
|
387
|
+
quote,
|
|
388
|
+
}
|
|
389
|
+
this.posts[by].push(post)
|
|
390
|
+
return post
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async deletePost(by: DidString, uri: AtUri) {
|
|
394
|
+
await this.agent.app.bsky.feed.post.delete(
|
|
395
|
+
{
|
|
396
|
+
repo: by,
|
|
397
|
+
rkey: uri.rkey,
|
|
398
|
+
},
|
|
399
|
+
this.getHeaders(by),
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async uploadFile(
|
|
404
|
+
by: DidString,
|
|
405
|
+
filePath: string,
|
|
406
|
+
encoding: string,
|
|
407
|
+
): Promise<ImageRef> {
|
|
408
|
+
const file = await fs.readFile(filePath)
|
|
409
|
+
const res = await this.agent.com.atproto.repo.uploadBlob(file, {
|
|
410
|
+
headers: this.getHeaders(by),
|
|
411
|
+
encoding,
|
|
412
|
+
} as any)
|
|
413
|
+
return { image: res.data.blob, alt: filePath }
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async like(
|
|
417
|
+
by: DidString,
|
|
418
|
+
subject: RecordRef,
|
|
419
|
+
overrides?: Partial<AppBskyFeedLike.Record>,
|
|
420
|
+
) {
|
|
421
|
+
const res = await this.agent.app.bsky.feed.like.create(
|
|
422
|
+
{ repo: by },
|
|
423
|
+
{
|
|
424
|
+
subject: subject.raw,
|
|
425
|
+
createdAt: new Date().toISOString(),
|
|
426
|
+
...overrides,
|
|
427
|
+
},
|
|
428
|
+
this.getHeaders(by),
|
|
429
|
+
)
|
|
430
|
+
this.likes[by] ??= {}
|
|
431
|
+
this.likes[by][subject.uriStr] = new AtUri(res.uri)
|
|
432
|
+
return this.likes[by][subject.uriStr]
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async reply(
|
|
436
|
+
by: DidString,
|
|
437
|
+
root: RecordRef,
|
|
438
|
+
parent: RecordRef,
|
|
439
|
+
text: string,
|
|
440
|
+
facets?: AppBskyRichtextFacet.Main[],
|
|
441
|
+
images?: ImageRef[],
|
|
442
|
+
overrides?: Partial<AppBskyFeedPost.Record>,
|
|
443
|
+
) {
|
|
444
|
+
const embed = images
|
|
445
|
+
? {
|
|
446
|
+
$type: 'app.bsky.embed.images',
|
|
447
|
+
images,
|
|
448
|
+
}
|
|
449
|
+
: undefined
|
|
450
|
+
const res = await this.agent.app.bsky.feed.post.create(
|
|
451
|
+
{ repo: by },
|
|
452
|
+
{
|
|
453
|
+
text: text,
|
|
454
|
+
reply: {
|
|
455
|
+
root: root.raw,
|
|
456
|
+
parent: parent.raw,
|
|
457
|
+
},
|
|
458
|
+
facets,
|
|
459
|
+
embed,
|
|
460
|
+
createdAt: new Date().toISOString(),
|
|
461
|
+
...overrides,
|
|
462
|
+
},
|
|
463
|
+
this.getHeaders(by),
|
|
464
|
+
)
|
|
465
|
+
this.replies[by] ??= []
|
|
466
|
+
const reply = {
|
|
467
|
+
text,
|
|
468
|
+
ref: new RecordRef(res.uri, res.cid),
|
|
469
|
+
images: images ?? [],
|
|
470
|
+
}
|
|
471
|
+
this.replies[by].push(reply)
|
|
472
|
+
return reply
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async repost(
|
|
476
|
+
by: DidString,
|
|
477
|
+
subject: RecordRef,
|
|
478
|
+
overrides?: Partial<AppBskyFeedRepost.Record>,
|
|
479
|
+
) {
|
|
480
|
+
const res = await this.agent.app.bsky.feed.repost.create(
|
|
481
|
+
{ repo: by },
|
|
482
|
+
{
|
|
483
|
+
subject: subject.raw,
|
|
484
|
+
createdAt: new Date().toISOString(),
|
|
485
|
+
...overrides,
|
|
486
|
+
},
|
|
487
|
+
this.getHeaders(by),
|
|
488
|
+
)
|
|
489
|
+
this.reposts[by] ??= []
|
|
490
|
+
const repost = new RecordRef(res.uri, res.cid)
|
|
491
|
+
this.reposts[by].push(repost)
|
|
492
|
+
return repost
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async createList(
|
|
496
|
+
by: DidString,
|
|
497
|
+
name: string,
|
|
498
|
+
purpose: 'mod' | 'curate' | 'reference',
|
|
499
|
+
overrides?: Partial<AppBskyGraphList.Record>,
|
|
500
|
+
) {
|
|
501
|
+
const res = await this.agent.app.bsky.graph.list.create(
|
|
502
|
+
{ repo: by },
|
|
503
|
+
{
|
|
504
|
+
name,
|
|
505
|
+
purpose:
|
|
506
|
+
purpose === 'mod'
|
|
507
|
+
? 'app.bsky.graph.defs#modlist'
|
|
508
|
+
: purpose === 'curate'
|
|
509
|
+
? 'app.bsky.graph.defs#curatelist'
|
|
510
|
+
: 'app.bsky.graph.defs#referencelist',
|
|
511
|
+
createdAt: new Date().toISOString(),
|
|
512
|
+
...(overrides || {}),
|
|
513
|
+
},
|
|
514
|
+
this.getHeaders(by),
|
|
515
|
+
)
|
|
516
|
+
this.lists[by] ??= {}
|
|
517
|
+
const ref = new RecordRef(res.uri, res.cid)
|
|
518
|
+
this.lists[by][ref.uriStr] = {
|
|
519
|
+
ref: ref,
|
|
520
|
+
items: {},
|
|
521
|
+
}
|
|
522
|
+
return ref
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async createFeedGen(by: DidString, feedDid: string, name: string) {
|
|
526
|
+
const res = await this.agent.app.bsky.feed.generator.create(
|
|
527
|
+
{ repo: by },
|
|
528
|
+
{
|
|
529
|
+
did: feedDid,
|
|
530
|
+
displayName: name,
|
|
531
|
+
createdAt: new Date().toISOString(),
|
|
532
|
+
},
|
|
533
|
+
this.getHeaders(by),
|
|
534
|
+
)
|
|
535
|
+
this.feedgens[by] ??= {}
|
|
536
|
+
const ref = new RecordRef(res.uri, res.cid)
|
|
537
|
+
this.feedgens[by][ref.uriStr] = {
|
|
538
|
+
ref: ref,
|
|
539
|
+
items: {},
|
|
540
|
+
}
|
|
541
|
+
return ref
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
async createStarterPack(
|
|
545
|
+
by: DidString,
|
|
546
|
+
name: string,
|
|
547
|
+
actors: readonly DidString[],
|
|
548
|
+
feeds?: readonly AtUriString[],
|
|
549
|
+
) {
|
|
550
|
+
const list = await this.createList(by, 'n/a', 'reference')
|
|
551
|
+
for (const did of actors) {
|
|
552
|
+
await this.addToList(by, did, list)
|
|
553
|
+
}
|
|
554
|
+
const res = await this.agent.app.bsky.graph.starterpack.create(
|
|
555
|
+
{ repo: by },
|
|
556
|
+
{
|
|
557
|
+
name,
|
|
558
|
+
list: list.uriStr,
|
|
559
|
+
feeds: feeds?.map((uri) => ({ uri })),
|
|
560
|
+
createdAt: new Date().toISOString(),
|
|
561
|
+
},
|
|
562
|
+
this.getHeaders(by),
|
|
563
|
+
)
|
|
564
|
+
this.starterpacks[by] ??= {}
|
|
565
|
+
const ref = new RecordRef(res.uri, res.cid)
|
|
566
|
+
this.starterpacks[by][ref.uriStr] = {
|
|
567
|
+
ref: ref,
|
|
568
|
+
list,
|
|
569
|
+
feeds: feeds ?? [],
|
|
570
|
+
name,
|
|
571
|
+
}
|
|
572
|
+
return ref
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async addToList(by: DidString, subject: DidString, list: RecordRef) {
|
|
576
|
+
const res = await this.agent.app.bsky.graph.listitem.create(
|
|
577
|
+
{ repo: by },
|
|
578
|
+
{ subject, list: list.uriStr, createdAt: new Date().toISOString() },
|
|
579
|
+
this.getHeaders(by),
|
|
580
|
+
)
|
|
581
|
+
const ref = new RecordRef(res.uri, res.cid)
|
|
582
|
+
const found = (this.lists[by] ?? {})[list.uriStr]
|
|
583
|
+
if (found) {
|
|
584
|
+
found.items[subject] = ref
|
|
585
|
+
}
|
|
586
|
+
return ref
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async rmFromList(by: DidString, subject: DidString, list: RecordRef) {
|
|
590
|
+
const foundList = (this.lists[by] ?? {})[list.uriStr] ?? {}
|
|
591
|
+
if (!foundList) return
|
|
592
|
+
const foundItem = foundList.items[subject]
|
|
593
|
+
if (!foundItem) return
|
|
594
|
+
await this.agent.app.bsky.graph.listitem.delete(
|
|
595
|
+
{ repo: by, rkey: foundItem.uri.rkey },
|
|
596
|
+
this.getHeaders(by),
|
|
597
|
+
)
|
|
598
|
+
delete foundList.items[subject]
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// override public signature to add support for convos and messages
|
|
602
|
+
async createReport(opts: {
|
|
603
|
+
reasonType: ComAtprotoModerationCreateReport.InputSchema['reasonType']
|
|
604
|
+
subject:
|
|
605
|
+
| $Typed<ComAtprotoAdminDefs.RepoRef>
|
|
606
|
+
| $Typed<ComAtprotoRepoStrongRef.Main>
|
|
607
|
+
| $Typed<ChatBskyConvoDefs.MessageRef>
|
|
608
|
+
| $Typed<ChatBskyConvoDefs.ConvoRef>
|
|
609
|
+
| { $type: string }
|
|
610
|
+
reason?: string
|
|
611
|
+
reportedBy: DidString
|
|
612
|
+
}): Promise<
|
|
613
|
+
ComAtprotoModerationCreateReport.OutputSchema & {
|
|
614
|
+
subject:
|
|
615
|
+
| $Typed<ComAtprotoAdminDefs.RepoRef>
|
|
616
|
+
| $Typed<ComAtprotoRepoStrongRef.Main>
|
|
617
|
+
| $Typed<ChatBskyConvoDefs.MessageRef>
|
|
618
|
+
| $Typed<ChatBskyConvoDefs.ConvoRef>
|
|
619
|
+
| { $type: string }
|
|
620
|
+
}
|
|
621
|
+
> {
|
|
622
|
+
const { reasonType, subject, reason, reportedBy } = opts
|
|
623
|
+
const result = await this.agent.com.atproto.moderation.createReport(
|
|
624
|
+
{ reasonType, subject, reason },
|
|
625
|
+
{
|
|
626
|
+
encoding: 'application/json',
|
|
627
|
+
headers: this.getHeaders(reportedBy),
|
|
628
|
+
},
|
|
629
|
+
)
|
|
630
|
+
return result.data
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async verify(
|
|
634
|
+
by: DidString,
|
|
635
|
+
subject: DidString,
|
|
636
|
+
handle: HandleString,
|
|
637
|
+
displayName: string,
|
|
638
|
+
overrides?: Partial<AppBskyGraphVerification.Record>,
|
|
639
|
+
) {
|
|
640
|
+
const res = await this.agent.app.bsky.graph.verification.create(
|
|
641
|
+
{ repo: by },
|
|
642
|
+
{
|
|
643
|
+
subject,
|
|
644
|
+
createdAt: new Date().toISOString(),
|
|
645
|
+
handle,
|
|
646
|
+
displayName,
|
|
647
|
+
...overrides,
|
|
648
|
+
},
|
|
649
|
+
this.getHeaders(by),
|
|
650
|
+
)
|
|
651
|
+
this.verifications[by] ??= {}
|
|
652
|
+
this.verifications[by][subject] = new AtUri(res.uri)
|
|
653
|
+
return this.verifications[by][subject]
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async unverify(by: DidString, subject: DidString) {
|
|
657
|
+
const verification = this.verifications[by]?.[subject]
|
|
658
|
+
if (!verification) {
|
|
659
|
+
throw new Error('verification does not exist')
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
await this.agent.app.bsky.graph.verification.delete(
|
|
663
|
+
{ repo: by, rkey: verification.rkey },
|
|
664
|
+
this.getHeaders(by),
|
|
665
|
+
)
|
|
666
|
+
delete this.verifications[by][subject]
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
getHeaders(did: DidString) {
|
|
670
|
+
return SeedClient.getHeaders(this.accounts[did].accessJwt)
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
static getHeaders(jwt: string) {
|
|
674
|
+
return { authorization: `Bearer ${jwt}` }
|
|
675
|
+
}
|
|
676
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { SeedClient } from './client.js'
|
|
2
|
+
|
|
3
|
+
export default async (sc: SeedClient) => {
|
|
4
|
+
await sc.createAccount('alice', users.alice)
|
|
5
|
+
await sc.createAccount('bob', users.bob)
|
|
6
|
+
await sc.createAccount('carol', users.carol)
|
|
7
|
+
await sc.createAccount('dan', users.dan)
|
|
8
|
+
await sc.createAccount('eve', users.eve)
|
|
9
|
+
for (const name in sc.dids) {
|
|
10
|
+
await sc.createProfile(sc.dids[name], `display-${name}`, `descript-${name}`)
|
|
11
|
+
}
|
|
12
|
+
const alice = sc.dids.alice
|
|
13
|
+
const bob = sc.dids.bob
|
|
14
|
+
const carol = sc.dids.carol
|
|
15
|
+
const dan = sc.dids.dan
|
|
16
|
+
const eve = sc.dids.eve
|
|
17
|
+
await sc.follow(alice, bob)
|
|
18
|
+
await sc.follow(alice, carol)
|
|
19
|
+
await sc.follow(alice, dan)
|
|
20
|
+
await sc.follow(alice, eve)
|
|
21
|
+
await sc.follow(carol, alice)
|
|
22
|
+
await sc.follow(bob, alice)
|
|
23
|
+
await sc.follow(bob, carol)
|
|
24
|
+
await sc.follow(dan, alice)
|
|
25
|
+
await sc.follow(dan, bob)
|
|
26
|
+
await sc.follow(dan, eve)
|
|
27
|
+
await sc.follow(eve, alice)
|
|
28
|
+
await sc.follow(eve, carol)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const users = {
|
|
32
|
+
alice: {
|
|
33
|
+
email: 'alice@test.com',
|
|
34
|
+
handle: 'alice.test',
|
|
35
|
+
password: 'alice-pass',
|
|
36
|
+
},
|
|
37
|
+
bob: {
|
|
38
|
+
email: 'bob@test.com',
|
|
39
|
+
handle: 'bob.test',
|
|
40
|
+
password: 'bob-pass',
|
|
41
|
+
},
|
|
42
|
+
carol: {
|
|
43
|
+
email: 'carol@test.com',
|
|
44
|
+
handle: 'carol.test',
|
|
45
|
+
password: 'carol-pass',
|
|
46
|
+
},
|
|
47
|
+
dan: {
|
|
48
|
+
email: 'dan@test.com',
|
|
49
|
+
handle: 'dan.test',
|
|
50
|
+
password: 'dan-pass',
|
|
51
|
+
},
|
|
52
|
+
eve: {
|
|
53
|
+
email: 'eve@test.com',
|
|
54
|
+
handle: 'eve.test',
|
|
55
|
+
password: 'eve-pass',
|
|
56
|
+
},
|
|
57
|
+
} as const
|