@atproto/bsky 0.0.165 → 0.0.167
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 +16 -0
- package/dist/api/app/bsky/notification/listActivitySubscriptions.d.ts +4 -0
- package/dist/api/app/bsky/notification/listActivitySubscriptions.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/listActivitySubscriptions.js +63 -0
- package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -0
- package/dist/api/app/bsky/notification/putActivitySubscription.d.ts +4 -0
- package/dist/api/app/bsky/notification/putActivitySubscription.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/putActivitySubscription.js +63 -0
- package/dist/api/app/bsky/notification/putActivitySubscription.js.map +1 -0
- package/dist/api/app/bsky/notification/putPreferencesV2.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/putPreferencesV2.js +2 -1
- package/dist/api/app/bsky/notification/putPreferencesV2.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +4 -0
- package/dist/api/index.js.map +1 -1
- package/dist/data-plane/bsync/index.d.ts.map +1 -1
- package/dist/data-plane/bsync/index.js +52 -38
- package/dist/data-plane/bsync/index.js.map +1 -1
- package/dist/data-plane/server/db/database-schema.d.ts +2 -1
- package/dist/data-plane/server/db/database-schema.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/20250611T140649895Z-add-activity-subscription.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20250611T140649895Z-add-activity-subscription.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20250611T140649895Z-add-activity-subscription.js +24 -0
- package/dist/data-plane/server/db/migrations/20250611T140649895Z-add-activity-subscription.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -1
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/pagination.d.ts +22 -0
- package/dist/data-plane/server/db/pagination.d.ts.map +1 -1
- package/dist/data-plane/server/db/pagination.js +30 -1
- package/dist/data-plane/server/db/pagination.js.map +1 -1
- package/dist/data-plane/server/db/tables/activity-subscription.d.ts +13 -0
- package/dist/data-plane/server/db/tables/activity-subscription.d.ts.map +1 -0
- package/dist/data-plane/server/db/tables/activity-subscription.js +5 -0
- package/dist/data-plane/server/db/tables/activity-subscription.js.map +1 -0
- package/dist/data-plane/server/indexing/index.d.ts +2 -0
- package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
- package/dist/data-plane/server/indexing/index.js +2 -0
- package/dist/data-plane/server/indexing/index.js.map +1 -1
- package/dist/data-plane/server/indexing/plugins/notif-declaration.d.ts +7 -0
- package/dist/data-plane/server/indexing/plugins/notif-declaration.d.ts.map +1 -0
- package/dist/data-plane/server/indexing/plugins/notif-declaration.js +72 -0
- package/dist/data-plane/server/indexing/plugins/notif-declaration.js.map +1 -0
- package/dist/data-plane/server/routes/activity-subscription.d.ts +6 -0
- package/dist/data-plane/server/routes/activity-subscription.d.ts.map +1 -0
- package/dist/data-plane/server/routes/activity-subscription.js +67 -0
- package/dist/data-plane/server/routes/activity-subscription.js.map +1 -0
- package/dist/data-plane/server/routes/index.js +2 -2
- package/dist/data-plane/server/routes/index.js.map +1 -1
- package/dist/data-plane/server/routes/notifs.d.ts +3 -0
- package/dist/data-plane/server/routes/notifs.d.ts.map +1 -1
- package/dist/data-plane/server/routes/notifs.js +64 -0
- package/dist/data-plane/server/routes/notifs.js.map +1 -1
- package/dist/data-plane/server/routes/profile.d.ts.map +1 -1
- package/dist/data-plane/server/routes/profile.js +20 -1
- package/dist/data-plane/server/routes/profile.js.map +1 -1
- package/dist/data-plane/server/routes/records.d.ts.map +1 -1
- package/dist/data-plane/server/routes/records.js +1 -0
- package/dist/data-plane/server/routes/records.js.map +1 -1
- package/dist/data-plane/server/util.d.ts +6 -6
- package/dist/hydration/actor.d.ts +20 -6
- package/dist/hydration/actor.d.ts.map +1 -1
- package/dist/hydration/actor.js +44 -1
- package/dist/hydration/actor.js.map +1 -1
- package/dist/hydration/hydrator.d.ts +4 -3
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +16 -2
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/hydration/util.d.ts +4 -0
- package/dist/hydration/util.d.ts.map +1 -1
- package/dist/hydration/util.js +3 -1
- package/dist/hydration/util.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +6 -2
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +3 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/proto/bsky_connect.d.ts +28 -1
- package/dist/proto/bsky_connect.d.ts.map +1 -1
- package/dist/proto/bsky_connect.js +27 -0
- package/dist/proto/bsky_connect.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +189 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +598 -5
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/stash.d.ts +1 -0
- package/dist/stash.d.ts.map +1 -1
- package/dist/stash.js +1 -0
- package/dist/stash.js.map +1 -1
- package/dist/views/index.d.ts +5 -3
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +29 -9
- package/dist/views/index.js.map +1 -1
- package/package.json +4 -4
- package/proto/bsky.proto +45 -0
- package/src/api/app/bsky/notification/listActivitySubscriptions.ts +110 -0
- package/src/api/app/bsky/notification/putActivitySubscription.ts +69 -0
- package/src/api/app/bsky/notification/putPreferencesV2.ts +2 -1
- package/src/api/index.ts +4 -0
- package/src/data-plane/bsync/index.ts +75 -44
- package/src/data-plane/server/db/database-schema.ts +3 -1
- package/src/data-plane/server/db/migrations/20250611T140649895Z-add-activity-subscription.ts +22 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/pagination.ts +37 -0
- package/src/data-plane/server/db/tables/activity-subscription.ts +12 -0
- package/src/data-plane/server/indexing/index.ts +3 -0
- package/src/data-plane/server/indexing/plugins/notif-declaration.ts +59 -0
- package/src/data-plane/server/routes/activity-subscription.ts +83 -0
- package/src/data-plane/server/routes/index.ts +2 -2
- package/src/data-plane/server/routes/notifs.ts +95 -0
- package/src/data-plane/server/routes/profile.ts +33 -1
- package/src/data-plane/server/routes/records.ts +4 -0
- package/src/hydration/actor.ts +97 -10
- package/src/hydration/hydrator.ts +32 -6
- package/src/hydration/util.ts +8 -0
- package/src/lexicon/lexicons.ts +5 -0
- package/src/lexicon/types/app/bsky/notification/listNotifications.ts +1 -0
- package/src/proto/bsky_connect.ts +33 -0
- package/src/proto/bsky_pb.ts +648 -0
- package/src/stash.ts +6 -1
- package/src/views/index.ts +48 -11
- package/tests/__snapshots__/feed-generation.test.ts.snap +213 -0
- package/tests/data-plane/__snapshots__/indexing.test.ts.snap +88 -0
- package/tests/views/__snapshots__/author-feed.test.ts.snap +498 -0
- package/tests/views/__snapshots__/block-lists.test.ts.snap +56 -0
- package/tests/views/__snapshots__/blocks.test.ts.snap +28 -0
- package/tests/views/__snapshots__/follows.test.ts.snap +170 -0
- package/tests/views/__snapshots__/labeler-service.test.ts.snap +15 -0
- package/tests/views/__snapshots__/likes.test.ts.snap +23 -0
- package/tests/views/__snapshots__/list-feed.test.ts.snap +68 -0
- package/tests/views/__snapshots__/lists.test.ts.snap +120 -0
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +63 -0
- package/tests/views/__snapshots__/mutes.test.ts.snap +55 -0
- package/tests/views/__snapshots__/notifications.test.ts.snap +299 -0
- package/tests/views/__snapshots__/posts.test.ts.snap +58 -0
- package/tests/views/__snapshots__/profile.test.ts.snap +74 -0
- package/tests/views/__snapshots__/quotes.test.ts.snap +35 -0
- package/tests/views/__snapshots__/reposts.test.ts.snap +26 -0
- package/tests/views/__snapshots__/starter-packs.test.ts.snap +113 -0
- package/tests/views/__snapshots__/thread-v2.test.ts.snap +115 -0
- package/tests/views/__snapshots__/thread.test.ts.snap +145 -0
- package/tests/views/__snapshots__/timeline.test.ts.snap +566 -0
- package/tests/views/notifications.test.ts +355 -19
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/data-plane/server/routes/private-data.d.ts +0 -9
- package/dist/data-plane/server/routes/private-data.d.ts.map +0 -1
- package/dist/data-plane/server/routes/private-data.js +0 -65
- package/dist/data-plane/server/routes/private-data.js.map +0 -1
- package/src/data-plane/server/routes/private-data.ts +0 -95
|
@@ -2,6 +2,7 @@ import { ConnectRouter } from '@connectrpc/connect'
|
|
|
2
2
|
import { IdResolver } from '@atproto/identity'
|
|
3
3
|
import { Service } from '../../../proto/bsky_connect'
|
|
4
4
|
import { Database } from '../db'
|
|
5
|
+
import activitySubscription from './activity-subscription'
|
|
5
6
|
import blocks from './blocks'
|
|
6
7
|
import feedGens from './feed-gens'
|
|
7
8
|
import feeds from './feeds'
|
|
@@ -14,7 +15,6 @@ import lists from './lists'
|
|
|
14
15
|
import moderation from './moderation'
|
|
15
16
|
import mutes from './mutes'
|
|
16
17
|
import notifs from './notifs'
|
|
17
|
-
import privateData from './private-data'
|
|
18
18
|
import profile from './profile'
|
|
19
19
|
import quotes from './quotes'
|
|
20
20
|
import records from './records'
|
|
@@ -29,6 +29,7 @@ import threads from './threads'
|
|
|
29
29
|
export default (db: Database, idResolver: IdResolver) =>
|
|
30
30
|
(router: ConnectRouter) =>
|
|
31
31
|
router.service(Service, {
|
|
32
|
+
...activitySubscription(db),
|
|
32
33
|
...blocks(db),
|
|
33
34
|
...feedGens(db),
|
|
34
35
|
...feeds(db),
|
|
@@ -41,7 +42,6 @@ export default (db: Database, idResolver: IdResolver) =>
|
|
|
41
42
|
...moderation(db),
|
|
42
43
|
...mutes(db),
|
|
43
44
|
...notifs(db),
|
|
44
|
-
...privateData(db),
|
|
45
45
|
...profile(db),
|
|
46
46
|
...quotes(db),
|
|
47
47
|
...records(db),
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { Timestamp } from '@bufbuild/protobuf'
|
|
2
2
|
import { ServiceImpl } from '@connectrpc/connect'
|
|
3
3
|
import { sql } from 'kysely'
|
|
4
|
+
import { keyBy } from '@atproto/common'
|
|
5
|
+
import { jsonStringToLex } from '@atproto/lexicon'
|
|
6
|
+
import {
|
|
7
|
+
ChatPreference,
|
|
8
|
+
FilterablePreference,
|
|
9
|
+
Preference,
|
|
10
|
+
Preferences,
|
|
11
|
+
} from '../../../lexicon/types/app/bsky/notification/defs'
|
|
4
12
|
import { Service } from '../../../proto/bsky_connect'
|
|
13
|
+
import {
|
|
14
|
+
ChatNotificationInclude,
|
|
15
|
+
ChatNotificationPreference,
|
|
16
|
+
FilterableNotificationPreference,
|
|
17
|
+
NotificationInclude,
|
|
18
|
+
NotificationPreference,
|
|
19
|
+
NotificationPreferences,
|
|
20
|
+
} from '../../../proto/bsky_pb'
|
|
21
|
+
import { Namespaces } from '../../../stash'
|
|
5
22
|
import { Database } from '../db'
|
|
6
23
|
import { IsoSortAtKey } from '../db/pagination'
|
|
7
24
|
import { countAll, notSoftDeletedClause } from '../db/util'
|
|
@@ -151,4 +168,82 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
151
168
|
.onConflict((oc) => oc.doNothing())
|
|
152
169
|
.executeTakeFirst()
|
|
153
170
|
},
|
|
171
|
+
|
|
172
|
+
async getNotificationPreferences(req) {
|
|
173
|
+
const { dids } = req
|
|
174
|
+
if (dids.length === 0) {
|
|
175
|
+
return { preferences: [] }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const res = await db.db
|
|
179
|
+
.selectFrom('private_data')
|
|
180
|
+
.selectAll()
|
|
181
|
+
.where('actorDid', 'in', dids)
|
|
182
|
+
.where('namespace', '=', Namespaces.AppBskyNotificationDefsPreferences)
|
|
183
|
+
.where('key', '=', 'self')
|
|
184
|
+
.execute()
|
|
185
|
+
|
|
186
|
+
const byDid = keyBy(res, 'actorDid')
|
|
187
|
+
const preferences = dids.map((did) => {
|
|
188
|
+
const row = byDid.get(did)
|
|
189
|
+
if (!row) {
|
|
190
|
+
return {}
|
|
191
|
+
}
|
|
192
|
+
const p = jsonStringToLex(row.payload) as Preferences
|
|
193
|
+
return notificationPreferencesLexToProtobuf(p, row.payload)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
return { preferences }
|
|
197
|
+
},
|
|
154
198
|
})
|
|
199
|
+
|
|
200
|
+
export const notificationPreferencesLexToProtobuf = (
|
|
201
|
+
p: Preferences,
|
|
202
|
+
json: string,
|
|
203
|
+
): NotificationPreferences => {
|
|
204
|
+
const lexChatPreferenceToProtobuf = (
|
|
205
|
+
p: ChatPreference,
|
|
206
|
+
): ChatNotificationPreference =>
|
|
207
|
+
new ChatNotificationPreference({
|
|
208
|
+
include:
|
|
209
|
+
p.include === 'accepted'
|
|
210
|
+
? ChatNotificationInclude.ACCEPTED
|
|
211
|
+
: ChatNotificationInclude.ALL,
|
|
212
|
+
push: { enabled: p.push ?? true },
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
const lexFilterablePreferenceToProtobuf = (
|
|
216
|
+
p: FilterablePreference,
|
|
217
|
+
): FilterableNotificationPreference =>
|
|
218
|
+
new FilterableNotificationPreference({
|
|
219
|
+
include:
|
|
220
|
+
p.include === 'follows'
|
|
221
|
+
? NotificationInclude.FOLLOWS
|
|
222
|
+
: NotificationInclude.ALL,
|
|
223
|
+
list: { enabled: p.list ?? true },
|
|
224
|
+
push: { enabled: p.push ?? true },
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
const lexPreferenceToProtobuf = (p: Preference): NotificationPreference =>
|
|
228
|
+
new NotificationPreference({
|
|
229
|
+
list: { enabled: p.list ?? true },
|
|
230
|
+
push: { enabled: p.push ?? true },
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
return new NotificationPreferences({
|
|
234
|
+
entry: Buffer.from(json),
|
|
235
|
+
chat: lexChatPreferenceToProtobuf(p.chat),
|
|
236
|
+
follow: lexFilterablePreferenceToProtobuf(p.follow),
|
|
237
|
+
like: lexFilterablePreferenceToProtobuf(p.like),
|
|
238
|
+
likeViaRepost: lexFilterablePreferenceToProtobuf(p.likeViaRepost),
|
|
239
|
+
mention: lexFilterablePreferenceToProtobuf(p.mention),
|
|
240
|
+
quote: lexFilterablePreferenceToProtobuf(p.quote),
|
|
241
|
+
reply: lexFilterablePreferenceToProtobuf(p.reply),
|
|
242
|
+
repost: lexFilterablePreferenceToProtobuf(p.repost),
|
|
243
|
+
repostViaRepost: lexFilterablePreferenceToProtobuf(p.repostViaRepost),
|
|
244
|
+
starterpackJoined: lexPreferenceToProtobuf(p.starterpackJoined),
|
|
245
|
+
subscribedPost: lexPreferenceToProtobuf(p.subscribedPost),
|
|
246
|
+
unverified: lexPreferenceToProtobuf(p.unverified),
|
|
247
|
+
verified: lexPreferenceToProtobuf(p.verified),
|
|
248
|
+
})
|
|
249
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Timestamp } from '@bufbuild/protobuf'
|
|
2
2
|
import { ServiceImpl } from '@connectrpc/connect'
|
|
3
3
|
import { Selectable, sql } from 'kysely'
|
|
4
|
+
import {
|
|
5
|
+
AppBskyNotificationDeclaration,
|
|
6
|
+
ChatBskyActorDeclaration,
|
|
7
|
+
} from '@atproto/api'
|
|
4
8
|
import { keyBy } from '@atproto/common'
|
|
5
9
|
import { parseRecordBytes } from '../../../hydration/util'
|
|
6
10
|
import { Service } from '../../../proto/bsky_connect'
|
|
@@ -31,6 +35,9 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
31
35
|
const chatDeclarationUris = dids.map(
|
|
32
36
|
(did) => `at://${did}/chat.bsky.actor.declaration/self`,
|
|
33
37
|
)
|
|
38
|
+
const notifDeclarationUris = dids.map(
|
|
39
|
+
(did) => `at://${did}/app.bsky.notification.declaration/self`,
|
|
40
|
+
)
|
|
34
41
|
const { ref } = db.db.dynamic
|
|
35
42
|
const [
|
|
36
43
|
handlesRes,
|
|
@@ -38,6 +45,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
38
45
|
profiles,
|
|
39
46
|
statuses,
|
|
40
47
|
chatDeclarations,
|
|
48
|
+
notifDeclarations,
|
|
41
49
|
] = await Promise.all([
|
|
42
50
|
db.db
|
|
43
51
|
.selectFrom('actor')
|
|
@@ -64,6 +72,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
64
72
|
getRecords(db)({ uris: profileUris }),
|
|
65
73
|
getRecords(db)({ uris: statusUris }),
|
|
66
74
|
getRecords(db)({ uris: chatDeclarationUris }),
|
|
75
|
+
getRecords(db)({ uris: notifDeclarationUris }),
|
|
67
76
|
])
|
|
68
77
|
|
|
69
78
|
const verificationsBySubjectDid = verificationsReceived.reduce(
|
|
@@ -82,7 +91,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
82
91
|
|
|
83
92
|
const status = statuses.records[i]
|
|
84
93
|
|
|
85
|
-
const chatDeclaration = parseRecordBytes(
|
|
94
|
+
const chatDeclaration = parseRecordBytes<ChatBskyActorDeclaration.Record>(
|
|
86
95
|
chatDeclarations.records[i].record,
|
|
87
96
|
)
|
|
88
97
|
|
|
@@ -97,6 +106,28 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
97
106
|
return acc
|
|
98
107
|
}, {} as VerifiedBy)
|
|
99
108
|
|
|
109
|
+
const activitySubscription = () => {
|
|
110
|
+
const record = parseRecordBytes<AppBskyNotificationDeclaration.Record>(
|
|
111
|
+
notifDeclarations.records[i].record,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
// The dataplane is responsible for setting the default of "followers" (default according to the lexicon).
|
|
115
|
+
const defaultVal = 'followers'
|
|
116
|
+
|
|
117
|
+
if (typeof record?.allowSubscriptions !== 'string') {
|
|
118
|
+
return defaultVal
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
switch (record.allowSubscriptions) {
|
|
122
|
+
case 'followers':
|
|
123
|
+
case 'mutuals':
|
|
124
|
+
case 'none':
|
|
125
|
+
return record.allowSubscriptions
|
|
126
|
+
default:
|
|
127
|
+
return defaultVal
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
100
131
|
return {
|
|
101
132
|
exists: !!row,
|
|
102
133
|
handle: row?.handle ?? undefined,
|
|
@@ -117,6 +148,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
117
148
|
statusRecord: status,
|
|
118
149
|
tags: [],
|
|
119
150
|
profileTags: [],
|
|
151
|
+
allowActivitySubscriptionsFrom: activitySubscription(),
|
|
120
152
|
}
|
|
121
153
|
})
|
|
122
154
|
return { actors }
|
|
@@ -23,6 +23,10 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
23
23
|
getPostgateRecords: getRecords(db, ids.AppBskyFeedPostgate),
|
|
24
24
|
getLabelerRecords: getRecords(db, ids.AppBskyLabelerService),
|
|
25
25
|
getActorChatDeclarationRecords: getRecords(db, ids.ChatBskyActorDeclaration),
|
|
26
|
+
getNotificationDeclarationRecords: getRecords(
|
|
27
|
+
db,
|
|
28
|
+
ids.AppBskyNotificationDeclaration,
|
|
29
|
+
),
|
|
26
30
|
getStarterPackRecords: getRecords(db, ids.AppBskyGraphStarterpack),
|
|
27
31
|
getVerificationRecords: getRecords(db, ids.AppBskyGraphVerification),
|
|
28
32
|
getStatusRecords: getRecords(db, ids.AppBskyActorStatus),
|
package/src/hydration/actor.ts
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
+
import { AppBskyNotificationDeclaration } from '@atproto/api'
|
|
1
2
|
import { mapDefined } from '@atproto/common'
|
|
2
3
|
import { DataPlaneClient } from '../data-plane/client'
|
|
3
4
|
import { Record as ProfileRecord } from '../lexicon/types/app/bsky/actor/profile'
|
|
4
5
|
import { Record as StatusRecord } from '../lexicon/types/app/bsky/actor/status'
|
|
6
|
+
import { Record as NotificationDeclarationRecord } from '../lexicon/types/app/bsky/notification/declaration'
|
|
5
7
|
import { Record as ChatDeclarationRecord } from '../lexicon/types/chat/bsky/actor/declaration'
|
|
6
|
-
import { VerificationMeta } from '../proto/bsky_pb'
|
|
8
|
+
import { ActivitySubscription, VerificationMeta } from '../proto/bsky_pb'
|
|
7
9
|
import {
|
|
8
10
|
HydrationMap,
|
|
9
11
|
RecordInfo,
|
|
12
|
+
isActivitySubscriptionEnabled,
|
|
10
13
|
parseRecord,
|
|
11
14
|
parseString,
|
|
12
15
|
safeTakedownRef,
|
|
13
16
|
} from './util'
|
|
14
17
|
|
|
18
|
+
type AllowActivitySubscriptions = Extract<
|
|
19
|
+
AppBskyNotificationDeclaration.Record['allowSubscriptions'],
|
|
20
|
+
'followers' | 'mutuals' | 'none'
|
|
21
|
+
>
|
|
22
|
+
|
|
15
23
|
export type Actor = {
|
|
16
24
|
did: string
|
|
17
25
|
handle?: string
|
|
@@ -29,6 +37,7 @@ export type Actor = {
|
|
|
29
37
|
trustedVerifier?: boolean
|
|
30
38
|
verifications: VerificationHydrationState[]
|
|
31
39
|
status?: RecordInfo<StatusRecord>
|
|
40
|
+
allowActivitySubscriptionsFrom: AllowActivitySubscriptions
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
export type VerificationHydrationState = {
|
|
@@ -46,6 +55,9 @@ export type Actors = HydrationMap<Actor>
|
|
|
46
55
|
export type ChatDeclaration = RecordInfo<ChatDeclarationRecord>
|
|
47
56
|
|
|
48
57
|
export type ChatDeclarations = HydrationMap<ChatDeclaration>
|
|
58
|
+
export type NotificationDeclaration = RecordInfo<NotificationDeclarationRecord>
|
|
59
|
+
|
|
60
|
+
export type NotificationDeclarations = HydrationMap<NotificationDeclaration>
|
|
49
61
|
|
|
50
62
|
export type Status = RecordInfo<StatusRecord>
|
|
51
63
|
export type Statuses = HydrationMap<Status>
|
|
@@ -59,15 +71,25 @@ export type ProfileViewerState = {
|
|
|
59
71
|
blockingByList?: string
|
|
60
72
|
following?: string
|
|
61
73
|
followedBy?: string
|
|
62
|
-
knownFollowers?: {
|
|
63
|
-
count: number
|
|
64
|
-
followers: string[]
|
|
65
|
-
}
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
export type ProfileViewerStates = HydrationMap<ProfileViewerState>
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
type ActivitySubscriptionState = {
|
|
79
|
+
post: boolean
|
|
80
|
+
reply: boolean
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type ActivitySubscriptionStates = HydrationMap<
|
|
84
|
+
ActivitySubscriptionState | undefined
|
|
85
|
+
>
|
|
86
|
+
|
|
87
|
+
type KnownFollowersState = {
|
|
88
|
+
count: number
|
|
89
|
+
followers: string[]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type KnownFollowersStates = HydrationMap<KnownFollowersState | undefined>
|
|
71
93
|
|
|
72
94
|
export type ProfileAgg = {
|
|
73
95
|
followers: number
|
|
@@ -177,6 +199,20 @@ export class ActorHydrator {
|
|
|
177
199
|
},
|
|
178
200
|
)
|
|
179
201
|
|
|
202
|
+
const allowActivitySubscriptionsFrom = (
|
|
203
|
+
val: string,
|
|
204
|
+
): AllowActivitySubscriptions => {
|
|
205
|
+
switch (val) {
|
|
206
|
+
case 'followers':
|
|
207
|
+
case 'mutuals':
|
|
208
|
+
case 'none':
|
|
209
|
+
return val
|
|
210
|
+
default:
|
|
211
|
+
// The dataplane should set the default of "FOLLOWERS". Just in case.
|
|
212
|
+
return 'followers'
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
180
216
|
return acc.set(did, {
|
|
181
217
|
did,
|
|
182
218
|
handle: parseString(actor.handle),
|
|
@@ -194,6 +230,9 @@ export class ActorHydrator {
|
|
|
194
230
|
trustedVerifier: actor.trustedVerifier,
|
|
195
231
|
verifications,
|
|
196
232
|
status: status,
|
|
233
|
+
allowActivitySubscriptionsFrom: allowActivitySubscriptionsFrom(
|
|
234
|
+
actor.allowActivitySubscriptionsFrom,
|
|
235
|
+
),
|
|
197
236
|
})
|
|
198
237
|
}, new HydrationMap<Actor>())
|
|
199
238
|
}
|
|
@@ -213,6 +252,21 @@ export class ActorHydrator {
|
|
|
213
252
|
}, new HydrationMap<ChatDeclaration>())
|
|
214
253
|
}
|
|
215
254
|
|
|
255
|
+
async getNotificationDeclarations(
|
|
256
|
+
uris: string[],
|
|
257
|
+
includeTakedowns = false,
|
|
258
|
+
): Promise<NotificationDeclarations> {
|
|
259
|
+
if (!uris.length) return new HydrationMap<NotificationDeclaration>()
|
|
260
|
+
const res = await this.dataplane.getActorChatDeclarationRecords({ uris })
|
|
261
|
+
return uris.reduce((acc, uri, i) => {
|
|
262
|
+
const record = parseRecord<NotificationDeclarationRecord>(
|
|
263
|
+
res.records[i],
|
|
264
|
+
includeTakedowns,
|
|
265
|
+
)
|
|
266
|
+
return acc.set(uri, record ?? null)
|
|
267
|
+
}, new HydrationMap<NotificationDeclaration>())
|
|
268
|
+
}
|
|
269
|
+
|
|
216
270
|
async getStatus(uris: string[], includeTakedowns = false): Promise<Statuses> {
|
|
217
271
|
if (!uris.length) return new HydrationMap<Status>()
|
|
218
272
|
const res = await this.dataplane.getStatusRecords({ uris })
|
|
@@ -234,10 +288,11 @@ export class ActorHydrator {
|
|
|
234
288
|
actorDid: viewer,
|
|
235
289
|
targetDids: dids,
|
|
236
290
|
})
|
|
291
|
+
|
|
237
292
|
return dids.reduce((acc, did, i) => {
|
|
238
293
|
const rels = res.relationships[i]
|
|
239
294
|
if (viewer === did) {
|
|
240
|
-
// ignore self-follows, self-mutes, self-blocks
|
|
295
|
+
// ignore self-follows, self-mutes, self-blocks, self-activity-subscriptions
|
|
241
296
|
return acc.set(did, {})
|
|
242
297
|
}
|
|
243
298
|
return acc.set(did, {
|
|
@@ -256,8 +311,8 @@ export class ActorHydrator {
|
|
|
256
311
|
async getKnownFollowers(
|
|
257
312
|
dids: string[],
|
|
258
313
|
viewer: string | null,
|
|
259
|
-
): Promise<
|
|
260
|
-
if (!viewer) return new HydrationMap<
|
|
314
|
+
): Promise<KnownFollowersStates> {
|
|
315
|
+
if (!viewer) return new HydrationMap<KnownFollowersState | undefined>()
|
|
261
316
|
const { results: knownFollowersResults } = await this.dataplane
|
|
262
317
|
.getFollowsFollowing(
|
|
263
318
|
{
|
|
@@ -280,7 +335,39 @@ export class ActorHydrator {
|
|
|
280
335
|
}
|
|
281
336
|
: undefined,
|
|
282
337
|
)
|
|
283
|
-
}, new HydrationMap<
|
|
338
|
+
}, new HydrationMap<KnownFollowersState | undefined>())
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async getActivitySubscriptions(
|
|
342
|
+
dids: string[],
|
|
343
|
+
viewer: string | null,
|
|
344
|
+
): Promise<ActivitySubscriptionStates> {
|
|
345
|
+
if (!viewer) {
|
|
346
|
+
return new HydrationMap<ActivitySubscriptionState | undefined>()
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const activitySubscription = (val: ActivitySubscription | undefined) => {
|
|
350
|
+
if (!val) return undefined
|
|
351
|
+
|
|
352
|
+
const result = {
|
|
353
|
+
post: !!val.post,
|
|
354
|
+
reply: !!val.reply,
|
|
355
|
+
}
|
|
356
|
+
if (!isActivitySubscriptionEnabled(result)) return undefined
|
|
357
|
+
|
|
358
|
+
return result
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const { subscriptions } = await this.dataplane
|
|
362
|
+
.getActivitySubscriptionsByActorAndSubjects(
|
|
363
|
+
{ actorDid: viewer, subjectDids: dids },
|
|
364
|
+
{ signal: AbortSignal.timeout(100) },
|
|
365
|
+
)
|
|
366
|
+
.catch(() => ({ subscriptions: [] }))
|
|
367
|
+
|
|
368
|
+
return dids.reduce((acc, did, i) => {
|
|
369
|
+
return acc.set(did, activitySubscription(subscriptions[i]))
|
|
370
|
+
}, new HydrationMap<ActivitySubscriptionState | undefined>())
|
|
284
371
|
}
|
|
285
372
|
|
|
286
373
|
async getProfileAggregates(dids: string[]): Promise<ProfileAggs> {
|
|
@@ -12,9 +12,10 @@ import { Notification } from '../proto/bsky_pb'
|
|
|
12
12
|
import { ParsedLabelers } from '../util'
|
|
13
13
|
import { uriToDid, uriToDid as didFromUri } from '../util/uris'
|
|
14
14
|
import {
|
|
15
|
+
ActivitySubscriptionStates,
|
|
15
16
|
ActorHydrator,
|
|
16
17
|
Actors,
|
|
17
|
-
|
|
18
|
+
KnownFollowersStates,
|
|
18
19
|
ProfileAggs,
|
|
19
20
|
ProfileViewerState,
|
|
20
21
|
ProfileViewerStates,
|
|
@@ -121,7 +122,8 @@ export type HydrationState = {
|
|
|
121
122
|
labelers?: Labelers
|
|
122
123
|
labelerViewers?: LabelerViewerStates
|
|
123
124
|
labelerAggs?: LabelerAggs
|
|
124
|
-
knownFollowers?:
|
|
125
|
+
knownFollowers?: KnownFollowersStates
|
|
126
|
+
activitySubscriptions?: ActivitySubscriptionStates
|
|
125
127
|
bidirectionalBlocks?: BidirectionalBlocks
|
|
126
128
|
verifications?: Verifications
|
|
127
129
|
}
|
|
@@ -175,12 +177,12 @@ export class Hydrator {
|
|
|
175
177
|
viewer,
|
|
176
178
|
)
|
|
177
179
|
const listUris: string[] = []
|
|
178
|
-
profileViewers
|
|
180
|
+
profileViewers.forEach((item) => {
|
|
179
181
|
listUris.push(...listUrisFromProfileViewer(item))
|
|
180
182
|
})
|
|
181
183
|
const listState = await this.hydrateListsBasic(listUris, ctx)
|
|
182
184
|
// if a list no longer exists or is not a mod list, then remove from viewer state
|
|
183
|
-
profileViewers
|
|
185
|
+
profileViewers.forEach((item) => {
|
|
184
186
|
removeNonModListsFromProfileViewer(item, listState)
|
|
185
187
|
})
|
|
186
188
|
|
|
@@ -239,8 +241,7 @@ export class Hydrator {
|
|
|
239
241
|
dids: string[],
|
|
240
242
|
ctx: HydrateCtx,
|
|
241
243
|
): Promise<HydrationState> {
|
|
242
|
-
let knownFollowers:
|
|
243
|
-
|
|
244
|
+
let knownFollowers: KnownFollowersStates = new HydrationMap()
|
|
244
245
|
try {
|
|
245
246
|
knownFollowers = await this.actor.getKnownFollowers(dids, ctx.viewer)
|
|
246
247
|
} catch (err) {
|
|
@@ -250,6 +251,19 @@ export class Hydrator {
|
|
|
250
251
|
)
|
|
251
252
|
}
|
|
252
253
|
|
|
254
|
+
let activitySubscriptions: ActivitySubscriptionStates = new HydrationMap()
|
|
255
|
+
try {
|
|
256
|
+
activitySubscriptions = await this.actor.getActivitySubscriptions(
|
|
257
|
+
dids,
|
|
258
|
+
ctx.viewer,
|
|
259
|
+
)
|
|
260
|
+
} catch (err) {
|
|
261
|
+
hydrationLogger.error(
|
|
262
|
+
{ err },
|
|
263
|
+
'Failed to get activity subscriptions state for profiles',
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
253
267
|
const subjectsToKnownFollowersMap = Array.from(
|
|
254
268
|
knownFollowers.keys(),
|
|
255
269
|
).reduce((acc, did) => {
|
|
@@ -281,6 +295,7 @@ export class Hydrator {
|
|
|
281
295
|
return mergeManyStates(state, starterPackState, {
|
|
282
296
|
profileAggs,
|
|
283
297
|
knownFollowers,
|
|
298
|
+
activitySubscriptions,
|
|
284
299
|
ctx,
|
|
285
300
|
bidirectionalBlocks,
|
|
286
301
|
})
|
|
@@ -1130,6 +1145,13 @@ export class Hydrator {
|
|
|
1130
1145
|
uri,
|
|
1131
1146
|
) ?? undefined
|
|
1132
1147
|
)
|
|
1148
|
+
} else if (collection === ids.AppBskyNotificationDeclaration) {
|
|
1149
|
+
if (parsed.rkey !== 'self') return
|
|
1150
|
+
return (
|
|
1151
|
+
(
|
|
1152
|
+
await this.actor.getNotificationDeclarations([uri], includeTakedowns)
|
|
1153
|
+
).get(uri) ?? undefined
|
|
1154
|
+
)
|
|
1133
1155
|
} else if (collection === ids.AppBskyActorStatus) {
|
|
1134
1156
|
if (parsed.rkey !== 'self') return
|
|
1135
1157
|
return (
|
|
@@ -1348,6 +1370,10 @@ export const mergeStates = (
|
|
|
1348
1370
|
labelerAggs: mergeMaps(stateA.labelerAggs, stateB.labelerAggs),
|
|
1349
1371
|
labelerViewers: mergeMaps(stateA.labelerViewers, stateB.labelerViewers),
|
|
1350
1372
|
knownFollowers: mergeMaps(stateA.knownFollowers, stateB.knownFollowers),
|
|
1373
|
+
activitySubscriptions: mergeMaps(
|
|
1374
|
+
stateA.activitySubscriptions,
|
|
1375
|
+
stateB.activitySubscriptions,
|
|
1376
|
+
),
|
|
1351
1377
|
bidirectionalBlocks: mergeNestedMaps(
|
|
1352
1378
|
stateA.bidirectionalBlocks,
|
|
1353
1379
|
stateB.bidirectionalBlocks,
|
package/src/hydration/util.ts
CHANGED
|
@@ -156,3 +156,11 @@ export const safeTakedownRef = (obj?: {
|
|
|
156
156
|
if (obj.takedownRef) return obj.takedownRef
|
|
157
157
|
if (obj.takenDown) return 'BSKY-TAKEDOWN-UNKNOWN'
|
|
158
158
|
}
|
|
159
|
+
|
|
160
|
+
export const isActivitySubscriptionEnabled = ({
|
|
161
|
+
post,
|
|
162
|
+
reply,
|
|
163
|
+
}: {
|
|
164
|
+
post: boolean
|
|
165
|
+
reply: boolean
|
|
166
|
+
}): boolean => post || reply
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -4790,10 +4790,14 @@ export const schemaDict = {
|
|
|
4790
4790
|
format: 'at-uri',
|
|
4791
4791
|
},
|
|
4792
4792
|
knownFollowers: {
|
|
4793
|
+
description:
|
|
4794
|
+
'This property is present only in selected cases, as an optimization.',
|
|
4793
4795
|
type: 'ref',
|
|
4794
4796
|
ref: 'lex:app.bsky.actor.defs#knownFollowers',
|
|
4795
4797
|
},
|
|
4796
4798
|
activitySubscription: {
|
|
4799
|
+
description:
|
|
4800
|
+
'This property is present only in selected cases, as an optimization.',
|
|
4797
4801
|
type: 'ref',
|
|
4798
4802
|
ref: 'lex:app.bsky.notification.defs#activitySubscription',
|
|
4799
4803
|
},
|
|
@@ -10129,6 +10133,7 @@ export const schemaDict = {
|
|
|
10129
10133
|
'unverified',
|
|
10130
10134
|
'like-via-repost',
|
|
10131
10135
|
'repost-via-repost',
|
|
10136
|
+
'subscribed-post',
|
|
10132
10137
|
],
|
|
10133
10138
|
},
|
|
10134
10139
|
reasonSubject: {
|
|
@@ -22,6 +22,10 @@ import {
|
|
|
22
22
|
DeleteActorMuteResponse,
|
|
23
23
|
DeleteThreadMuteRequest,
|
|
24
24
|
DeleteThreadMuteResponse,
|
|
25
|
+
GetActivitySubscriptionDidsRequest,
|
|
26
|
+
GetActivitySubscriptionDidsResponse,
|
|
27
|
+
GetActivitySubscriptionsByActorAndSubjectsRequest,
|
|
28
|
+
GetActivitySubscriptionsByActorAndSubjectsResponse,
|
|
25
29
|
GetActorChatDeclarationRecordsRequest,
|
|
26
30
|
GetActorChatDeclarationRecordsResponse,
|
|
27
31
|
GetActorFeedsRequest,
|
|
@@ -126,6 +130,8 @@ import {
|
|
|
126
130
|
GetMutesResponse,
|
|
127
131
|
GetNewUserCountForRangeRequest,
|
|
128
132
|
GetNewUserCountForRangeResponse,
|
|
133
|
+
GetNotificationDeclarationRecordsRequest,
|
|
134
|
+
GetNotificationDeclarationRecordsResponse,
|
|
129
135
|
GetNotificationPreferencesRequest,
|
|
130
136
|
GetNotificationPreferencesResponse,
|
|
131
137
|
GetNotificationSeenRequest,
|
|
@@ -307,6 +313,15 @@ export const Service = {
|
|
|
307
313
|
O: GetActorChatDeclarationRecordsResponse,
|
|
308
314
|
kind: MethodKind.Unary,
|
|
309
315
|
},
|
|
316
|
+
/**
|
|
317
|
+
* @generated from rpc bsky.Service.GetNotificationDeclarationRecords
|
|
318
|
+
*/
|
|
319
|
+
getNotificationDeclarationRecords: {
|
|
320
|
+
name: 'GetNotificationDeclarationRecords',
|
|
321
|
+
I: GetNotificationDeclarationRecordsRequest,
|
|
322
|
+
O: GetNotificationDeclarationRecordsResponse,
|
|
323
|
+
kind: MethodKind.Unary,
|
|
324
|
+
},
|
|
310
325
|
/**
|
|
311
326
|
* @generated from rpc bsky.Service.GetStatusRecords
|
|
312
327
|
*/
|
|
@@ -769,6 +784,24 @@ export const Service = {
|
|
|
769
784
|
O: GetUnreadNotificationCountResponse,
|
|
770
785
|
kind: MethodKind.Unary,
|
|
771
786
|
},
|
|
787
|
+
/**
|
|
788
|
+
* @generated from rpc bsky.Service.GetActivitySubscriptionDids
|
|
789
|
+
*/
|
|
790
|
+
getActivitySubscriptionDids: {
|
|
791
|
+
name: 'GetActivitySubscriptionDids',
|
|
792
|
+
I: GetActivitySubscriptionDidsRequest,
|
|
793
|
+
O: GetActivitySubscriptionDidsResponse,
|
|
794
|
+
kind: MethodKind.Unary,
|
|
795
|
+
},
|
|
796
|
+
/**
|
|
797
|
+
* @generated from rpc bsky.Service.GetActivitySubscriptionsByActorAndSubjects
|
|
798
|
+
*/
|
|
799
|
+
getActivitySubscriptionsByActorAndSubjects: {
|
|
800
|
+
name: 'GetActivitySubscriptionsByActorAndSubjects',
|
|
801
|
+
I: GetActivitySubscriptionsByActorAndSubjectsRequest,
|
|
802
|
+
O: GetActivitySubscriptionsByActorAndSubjectsResponse,
|
|
803
|
+
kind: MethodKind.Unary,
|
|
804
|
+
},
|
|
772
805
|
/**
|
|
773
806
|
* @generated from rpc bsky.Service.UpdateNotificationSeen
|
|
774
807
|
*/
|