@atproto/bsky 0.0.156 → 0.0.158
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/api/app/bsky/notification/getPreferences.d.ts +4 -0
- package/dist/api/app/bsky/notification/getPreferences.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/getPreferences.js +39 -0
- package/dist/api/app/bsky/notification/getPreferences.js.map +1 -0
- package/dist/api/app/bsky/notification/putPreferencesV2.d.ts +4 -0
- package/dist/api/app/bsky/notification/putPreferencesV2.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/putPreferencesV2.js +47 -0
- package/dist/api/app/bsky/notification/putPreferencesV2.js.map +1 -0
- package/dist/api/app/bsky/notification/util.d.ts +9 -0
- package/dist/api/app/bsky/notification/util.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/util.js +84 -0
- package/dist/api/app/bsky/notification/util.js.map +1 -0
- 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/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -0
- package/dist/context.js.map +1 -1
- package/dist/data-plane/bsync/index.d.ts.map +1 -1
- package/dist/data-plane/bsync/index.js +84 -0
- 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/db.d.ts +1 -0
- package/dist/data-plane/server/db/db.d.ts.map +1 -1
- package/dist/data-plane/server/db/db.js.map +1 -1
- package/dist/data-plane/server/db/index.d.ts +1 -0
- package/dist/data-plane/server/db/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/20250602T190357447Z-add-private-data.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20250602T190357447Z-add-private-data.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20250602T190357447Z-add-private-data.js +24 -0
- package/dist/data-plane/server/db/migrations/20250602T190357447Z-add-private-data.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/tables/private-data.d.ts +13 -0
- package/dist/data-plane/server/db/tables/private-data.d.ts.map +1 -0
- package/dist/data-plane/server/db/tables/private-data.js +5 -0
- package/dist/data-plane/server/db/tables/private-data.js.map +1 -0
- package/dist/data-plane/server/db/tables/record.d.ts +2 -1
- package/dist/data-plane/server/db/tables/record.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/record.js.map +1 -1
- package/dist/data-plane/server/index.d.ts +2 -1
- package/dist/data-plane/server/index.d.ts.map +1 -1
- package/dist/data-plane/server/index.js.map +1 -1
- package/dist/data-plane/server/routes/index.d.ts.map +1 -1
- package/dist/data-plane/server/routes/index.js +2 -0
- package/dist/data-plane/server/routes/index.js.map +1 -1
- package/dist/data-plane/server/routes/private-data.d.ts +9 -0
- package/dist/data-plane/server/routes/private-data.d.ts.map +1 -0
- package/dist/data-plane/server/routes/private-data.js +64 -0
- package/dist/data-plane/server/routes/private-data.js.map +1 -0
- package/dist/data-plane/server/util.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +4 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +8 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +418 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +225 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/defs.d.ts +40 -0
- package/dist/lexicon/types/app/bsky/notification/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/defs.js +36 -0
- package/dist/lexicon/types/app/bsky/notification/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/getPreferences.d.ts +35 -0
- package/dist/lexicon/types/app/bsky/notification/getPreferences.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/notification/getPreferences.js +7 -0
- package/dist/lexicon/types/app/bsky/notification/getPreferences.js.map +1 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferencesV2.d.ts +52 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferencesV2.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferencesV2.js +7 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferencesV2.js.map +1 -0
- package/dist/proto/bsky_connect.d.ts +10 -1
- package/dist/proto/bsky_connect.d.ts.map +1 -1
- package/dist/proto/bsky_connect.js +9 -0
- package/dist/proto/bsky_connect.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +238 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +703 -5
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/proto/bsync_pb.d.ts +10 -10
- package/dist/proto/bsync_pb.d.ts.map +1 -1
- package/dist/proto/bsync_pb.js +15 -15
- package/dist/proto/bsync_pb.js.map +1 -1
- package/dist/stash.d.ts +26 -0
- package/dist/stash.d.ts.map +1 -0
- package/dist/stash.js +56 -0
- package/dist/stash.js.map +1 -0
- package/package.json +8 -8
- package/proto/bsky.proto +62 -0
- package/src/api/app/bsky/notification/getPreferences.ts +50 -0
- package/src/api/app/bsky/notification/putPreferencesV2.ts +62 -0
- package/src/api/app/bsky/notification/util.ts +123 -0
- package/src/api/index.ts +4 -0
- package/src/context.ts +6 -0
- package/src/data-plane/bsync/index.ts +109 -1
- package/src/data-plane/server/db/database-schema.ts +3 -1
- package/src/data-plane/server/db/db.ts +2 -0
- package/src/data-plane/server/db/index.ts +1 -0
- package/src/data-plane/server/db/migrations/20250602T190357447Z-add-private-data.ts +22 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/private-data.ts +13 -0
- package/src/data-plane/server/db/tables/record.ts +3 -1
- package/src/data-plane/server/index.ts +3 -1
- package/src/data-plane/server/routes/index.ts +2 -0
- package/src/data-plane/server/routes/private-data.ts +94 -0
- package/src/index.ts +4 -0
- package/src/lexicon/index.ts +24 -0
- package/src/lexicon/lexicons.ts +227 -0
- package/src/lexicon/types/app/bsky/notification/defs.ts +76 -0
- package/src/lexicon/types/app/bsky/notification/getPreferences.ts +52 -0
- package/src/lexicon/types/app/bsky/notification/putPreferencesV2.ts +69 -0
- package/src/proto/bsky_connect.ts +11 -0
- package/src/proto/bsky_pb.ts +675 -0
- package/src/proto/bsync_pb.ts +15 -15
- package/src/stash.ts +75 -0
- package/tests/stash.test.ts +156 -0
- package/tests/views/notifications.test.ts +221 -0
- package/tests/views/thread-v2.test.ts +31 -33
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
- package/tests/seed/thread-v2.ts +0 -874
- package/tests/seed/util.ts +0 -52
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Un$Typed } from '@atproto/api'
|
|
2
|
+
import {
|
|
3
|
+
ChatPreference,
|
|
4
|
+
FilterablePreference,
|
|
5
|
+
Preference,
|
|
6
|
+
Preferences,
|
|
7
|
+
} from '../../../../lexicon/types/app/bsky/notification/defs'
|
|
8
|
+
import {
|
|
9
|
+
ChatNotificationFilter,
|
|
10
|
+
ChatNotificationPreference,
|
|
11
|
+
FilterableNotificationPreference,
|
|
12
|
+
NotificationFilter,
|
|
13
|
+
NotificationPreference,
|
|
14
|
+
NotificationPreferences,
|
|
15
|
+
} from '../../../../proto/bsky_pb'
|
|
16
|
+
|
|
17
|
+
type DeepPartial<T> = T extends object
|
|
18
|
+
? {
|
|
19
|
+
[P in keyof T]?: DeepPartial<T[P]>
|
|
20
|
+
}
|
|
21
|
+
: T
|
|
22
|
+
|
|
23
|
+
const ensureChatPreference = (
|
|
24
|
+
p?: DeepPartial<ChatPreference>,
|
|
25
|
+
): ChatPreference => {
|
|
26
|
+
const filters = ['all', 'accepted']
|
|
27
|
+
return {
|
|
28
|
+
filter:
|
|
29
|
+
typeof p?.filter === 'string' && filters.includes(p.filter)
|
|
30
|
+
? p.filter
|
|
31
|
+
: 'all',
|
|
32
|
+
push: p?.push ?? true,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ensureFilterablePreference = (
|
|
37
|
+
p?: DeepPartial<FilterablePreference>,
|
|
38
|
+
): FilterablePreference => {
|
|
39
|
+
const filters = ['all', 'follows']
|
|
40
|
+
return {
|
|
41
|
+
filter:
|
|
42
|
+
typeof p?.filter === 'string' && filters.includes(p.filter)
|
|
43
|
+
? p.filter
|
|
44
|
+
: 'all',
|
|
45
|
+
list: p?.list ?? true,
|
|
46
|
+
push: p?.push ?? true,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ensurePreference = (p?: DeepPartial<Preference>): Preference => {
|
|
51
|
+
return {
|
|
52
|
+
list: p?.list ?? true,
|
|
53
|
+
push: p?.push ?? true,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ensurePreferences = (
|
|
58
|
+
p: DeepPartial<Preferences>,
|
|
59
|
+
): Un$Typed<Preferences> => {
|
|
60
|
+
return {
|
|
61
|
+
chat: ensureChatPreference(p.chat),
|
|
62
|
+
follow: ensureFilterablePreference(p.follow),
|
|
63
|
+
like: ensureFilterablePreference(p.like),
|
|
64
|
+
likeViaRepost: ensureFilterablePreference(p.likeViaRepost),
|
|
65
|
+
mention: ensureFilterablePreference(p.mention),
|
|
66
|
+
quote: ensureFilterablePreference(p.quote),
|
|
67
|
+
reply: ensureFilterablePreference(p.reply),
|
|
68
|
+
repost: ensureFilterablePreference(p.repost),
|
|
69
|
+
repostViaRepost: ensureFilterablePreference(p.repostViaRepost),
|
|
70
|
+
starterpackJoined: ensurePreference(p.starterpackJoined),
|
|
71
|
+
subscribedPost: ensurePreference(p.subscribedPost),
|
|
72
|
+
unverified: ensurePreference(p.unverified),
|
|
73
|
+
verified: ensurePreference(p.verified),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const protobufChatPreferenceToLex = (
|
|
78
|
+
p?: DeepPartial<ChatNotificationPreference>,
|
|
79
|
+
): DeepPartial<ChatPreference> => {
|
|
80
|
+
return {
|
|
81
|
+
filter: p?.filter === ChatNotificationFilter.ACCEPTED ? 'accepted' : 'all',
|
|
82
|
+
push: p?.push?.enabled,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const protobufFilterablePreferenceToLex = (
|
|
87
|
+
p?: DeepPartial<FilterableNotificationPreference>,
|
|
88
|
+
): DeepPartial<FilterablePreference> => {
|
|
89
|
+
return {
|
|
90
|
+
filter: p?.filter === NotificationFilter.FOLLOWS ? 'follows' : 'all',
|
|
91
|
+
list: p?.list?.enabled,
|
|
92
|
+
push: p?.push?.enabled,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const protobufPreferenceToLex = (
|
|
97
|
+
p?: DeepPartial<NotificationPreference>,
|
|
98
|
+
): DeepPartial<Preference> => {
|
|
99
|
+
return {
|
|
100
|
+
list: p?.list?.enabled,
|
|
101
|
+
push: p?.push?.enabled,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const protobufToLex = (
|
|
106
|
+
res: DeepPartial<NotificationPreferences>,
|
|
107
|
+
): Un$Typed<Preferences> => {
|
|
108
|
+
return ensurePreferences({
|
|
109
|
+
chat: protobufChatPreferenceToLex(res.chat),
|
|
110
|
+
follow: protobufFilterablePreferenceToLex(res.follow),
|
|
111
|
+
like: protobufFilterablePreferenceToLex(res.like),
|
|
112
|
+
likeViaRepost: protobufFilterablePreferenceToLex(res.likeViaRepost),
|
|
113
|
+
mention: protobufFilterablePreferenceToLex(res.mention),
|
|
114
|
+
quote: protobufFilterablePreferenceToLex(res.quote),
|
|
115
|
+
reply: protobufFilterablePreferenceToLex(res.reply),
|
|
116
|
+
repost: protobufFilterablePreferenceToLex(res.repost),
|
|
117
|
+
repostViaRepost: protobufFilterablePreferenceToLex(res.repostViaRepost),
|
|
118
|
+
starterpackJoined: protobufPreferenceToLex(res.starterpackJoined),
|
|
119
|
+
subscribedPost: protobufPreferenceToLex(res.subscribedPost),
|
|
120
|
+
unverified: protobufPreferenceToLex(res.unverified),
|
|
121
|
+
verified: protobufPreferenceToLex(res.verified),
|
|
122
|
+
})
|
|
123
|
+
}
|
package/src/api/index.ts
CHANGED
|
@@ -42,9 +42,11 @@ import unmuteActor from './app/bsky/graph/unmuteActor'
|
|
|
42
42
|
import unmuteActorList from './app/bsky/graph/unmuteActorList'
|
|
43
43
|
import unmuteThread from './app/bsky/graph/unmuteThread'
|
|
44
44
|
import getLabelerServices from './app/bsky/labeler/getServices'
|
|
45
|
+
import getPreferences from './app/bsky/notification/getPreferences'
|
|
45
46
|
import getUnreadCount from './app/bsky/notification/getUnreadCount'
|
|
46
47
|
import listNotifications from './app/bsky/notification/listNotifications'
|
|
47
48
|
import putPreferences from './app/bsky/notification/putPreferences'
|
|
49
|
+
import putPreferencesV2 from './app/bsky/notification/putPreferencesV2'
|
|
48
50
|
import registerPush from './app/bsky/notification/registerPush'
|
|
49
51
|
import updateSeen from './app/bsky/notification/updateSeen'
|
|
50
52
|
import getConfig from './app/bsky/unspecced/getConfig'
|
|
@@ -122,10 +124,12 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
122
124
|
searchActors(server, ctx)
|
|
123
125
|
searchActorsTypeahead(server, ctx)
|
|
124
126
|
getSuggestions(server, ctx)
|
|
127
|
+
getPreferences(server, ctx)
|
|
125
128
|
getUnreadCount(server, ctx)
|
|
126
129
|
listNotifications(server, ctx)
|
|
127
130
|
updateSeen(server, ctx)
|
|
128
131
|
putPreferences(server, ctx)
|
|
132
|
+
putPreferencesV2(server, ctx)
|
|
129
133
|
registerPush(server, ctx)
|
|
130
134
|
getConfig(server, ctx)
|
|
131
135
|
getPopularFeedGenerators(server, ctx)
|
package/src/context.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { DataPlaneClient, HostList } from './data-plane/client'
|
|
|
13
13
|
import { FeatureGates } from './feature-gates'
|
|
14
14
|
import { Hydrator } from './hydration/hydrator'
|
|
15
15
|
import { httpLogger as log } from './logger'
|
|
16
|
+
import { StashClient } from './stash'
|
|
16
17
|
import {
|
|
17
18
|
ParsedLabelers,
|
|
18
19
|
defaultLabelerHeader,
|
|
@@ -35,6 +36,7 @@ export class AppContext {
|
|
|
35
36
|
signingKey: Keypair
|
|
36
37
|
idResolver: IdResolver
|
|
37
38
|
bsyncClient: BsyncClient
|
|
39
|
+
stashClient: StashClient
|
|
38
40
|
courierClient: CourierClient | undefined
|
|
39
41
|
authVerifier: AuthVerifier
|
|
40
42
|
featureGates: FeatureGates
|
|
@@ -94,6 +96,10 @@ export class AppContext {
|
|
|
94
96
|
return this.opts.bsyncClient
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
get stashClient(): StashClient {
|
|
100
|
+
return this.opts.stashClient
|
|
101
|
+
}
|
|
102
|
+
|
|
97
103
|
get courierClient(): CourierClient | undefined {
|
|
98
104
|
return this.opts.courierClient
|
|
99
105
|
}
|
|
@@ -4,10 +4,15 @@ import http from 'node:http'
|
|
|
4
4
|
import { ConnectRouter } from '@connectrpc/connect'
|
|
5
5
|
import { expressConnectMiddleware } from '@connectrpc/connect-express'
|
|
6
6
|
import express from 'express'
|
|
7
|
+
import { TID } from '@atproto/common'
|
|
7
8
|
import { AtUri } from '@atproto/syntax'
|
|
8
9
|
import { ids } from '../../lexicon/lexicons'
|
|
9
10
|
import { Service } from '../../proto/bsync_connect'
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
Method,
|
|
13
|
+
MuteOperation_Type,
|
|
14
|
+
PutOperationRequest,
|
|
15
|
+
} from '../../proto/bsync_pb'
|
|
11
16
|
import { Database } from '../server/db'
|
|
12
17
|
|
|
13
18
|
export class MockBsync {
|
|
@@ -138,7 +143,110 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
138
143
|
throw new Error('not implemented')
|
|
139
144
|
},
|
|
140
145
|
|
|
146
|
+
async putOperation(req) {
|
|
147
|
+
const { actorDid, namespace, key, method, payload } = req
|
|
148
|
+
if (
|
|
149
|
+
method !== Method.CREATE &&
|
|
150
|
+
method !== Method.UPDATE &&
|
|
151
|
+
method !== Method.DELETE
|
|
152
|
+
) {
|
|
153
|
+
throw new Error(`Unsupported method: ${method}`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const now = new Date().toISOString()
|
|
157
|
+
if (namespace === 'app.bsky.notification.defs#preferences') {
|
|
158
|
+
await handleNotificationPreferencesOperation(db, req, now)
|
|
159
|
+
} else {
|
|
160
|
+
await handleGenericOperation(db, req, now)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
operation: {
|
|
165
|
+
id: TID.nextStr(),
|
|
166
|
+
actorDid,
|
|
167
|
+
namespace,
|
|
168
|
+
key,
|
|
169
|
+
method,
|
|
170
|
+
payload,
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
async scanOperations() {
|
|
176
|
+
throw new Error('not implemented')
|
|
177
|
+
},
|
|
178
|
+
|
|
141
179
|
async ping() {
|
|
142
180
|
return {}
|
|
143
181
|
},
|
|
144
182
|
})
|
|
183
|
+
|
|
184
|
+
const handleNotificationPreferencesOperation = async (
|
|
185
|
+
db: Database,
|
|
186
|
+
req: PutOperationRequest,
|
|
187
|
+
now: string,
|
|
188
|
+
) => {
|
|
189
|
+
const { actorDid, namespace, key, method, payload } = req
|
|
190
|
+
if (method === Method.CREATE || method === Method.UPDATE) {
|
|
191
|
+
return db.db
|
|
192
|
+
.insertInto('private_data')
|
|
193
|
+
.values({
|
|
194
|
+
actorDid,
|
|
195
|
+
namespace,
|
|
196
|
+
key,
|
|
197
|
+
payload: Buffer.from(payload).toString('utf8'),
|
|
198
|
+
indexedAt: now,
|
|
199
|
+
updatedAt: now,
|
|
200
|
+
})
|
|
201
|
+
.onConflict((oc) =>
|
|
202
|
+
oc.columns(['actorDid', 'namespace', 'key']).doUpdateSet({
|
|
203
|
+
payload: Buffer.from(payload).toString('utf8'),
|
|
204
|
+
updatedAt: now,
|
|
205
|
+
}),
|
|
206
|
+
)
|
|
207
|
+
.execute()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return handleGenericOperation(db, req, now)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const handleGenericOperation = async (
|
|
214
|
+
db: Database,
|
|
215
|
+
req: PutOperationRequest,
|
|
216
|
+
now: string,
|
|
217
|
+
) => {
|
|
218
|
+
const { actorDid, namespace, key, method, payload } = req
|
|
219
|
+
if (method === Method.CREATE) {
|
|
220
|
+
return db.db
|
|
221
|
+
.insertInto('private_data')
|
|
222
|
+
.values({
|
|
223
|
+
actorDid,
|
|
224
|
+
namespace,
|
|
225
|
+
key,
|
|
226
|
+
payload: Buffer.from(payload).toString('utf8'),
|
|
227
|
+
indexedAt: now,
|
|
228
|
+
updatedAt: now,
|
|
229
|
+
})
|
|
230
|
+
.execute()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (method === Method.UPDATE) {
|
|
234
|
+
return db.db
|
|
235
|
+
.updateTable('private_data')
|
|
236
|
+
.where('actorDid', '=', actorDid)
|
|
237
|
+
.where('namespace', '=', namespace)
|
|
238
|
+
.where('key', '=', key)
|
|
239
|
+
.set({
|
|
240
|
+
payload: Buffer.from(payload).toString('utf8'),
|
|
241
|
+
updatedAt: now,
|
|
242
|
+
})
|
|
243
|
+
.execute()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return db.db
|
|
247
|
+
.deleteFrom('private_data')
|
|
248
|
+
.where('actorDid', '=', actorDid)
|
|
249
|
+
.where('namespace', '=', namespace)
|
|
250
|
+
.where('key', '=', key)
|
|
251
|
+
.execute()
|
|
252
|
+
}
|
|
@@ -24,6 +24,7 @@ import * as post from './tables/post'
|
|
|
24
24
|
import * as postAgg from './tables/post-agg'
|
|
25
25
|
import * as postEmbed from './tables/post-embed'
|
|
26
26
|
import * as postgate from './tables/post-gate'
|
|
27
|
+
import * as privateData from './tables/private-data'
|
|
27
28
|
import * as profile from './tables/profile'
|
|
28
29
|
import * as profileAgg from './tables/profile-agg'
|
|
29
30
|
import * as quote from './tables/quote'
|
|
@@ -77,6 +78,7 @@ export type DatabaseSchemaType = duplicateRecord.PartialDB &
|
|
|
77
78
|
starterPack.PartialDB &
|
|
78
79
|
taggedSuggestion.PartialDB &
|
|
79
80
|
quote.PartialDB &
|
|
80
|
-
verification.PartialDB
|
|
81
|
+
verification.PartialDB &
|
|
82
|
+
privateData.PartialDB
|
|
81
83
|
|
|
82
84
|
export type DatabaseSchema = Kysely<DatabaseSchemaType>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable('private_data')
|
|
6
|
+
.addColumn('actorDid', 'varchar', (col) => col.notNull())
|
|
7
|
+
.addColumn('namespace', 'varchar', (col) => col.notNull())
|
|
8
|
+
.addColumn('key', 'varchar', (col) => col.notNull())
|
|
9
|
+
.addColumn('payload', 'text', (col) => col.notNull())
|
|
10
|
+
.addColumn('indexedAt', 'varchar', (col) => col.notNull())
|
|
11
|
+
.addColumn('updatedAt', 'varchar', (col) => col.notNull())
|
|
12
|
+
.addPrimaryKeyConstraint('private_data_pkey', [
|
|
13
|
+
'actorDid',
|
|
14
|
+
'namespace',
|
|
15
|
+
'key',
|
|
16
|
+
])
|
|
17
|
+
.execute()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
21
|
+
await db.schema.dropTable('private_data').execute()
|
|
22
|
+
}
|
|
@@ -50,3 +50,4 @@ export * as _20250207T174822012Z from './20250207T174822012Z-add-label-exp'
|
|
|
50
50
|
export * as _20250404T163421487Z from './20250404T163421487Z-verifications'
|
|
51
51
|
export * as _20250526T023712742Z from './20250526T023712742Z-like-repost-via'
|
|
52
52
|
export * as _20250528T221913281Z from './20250528T221913281Z-add-record-tags'
|
|
53
|
+
export * as _20250602T190357447Z from './20250602T190357447Z-add-private-data'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface PrivateData {
|
|
2
|
+
actorDid: string
|
|
3
|
+
namespace: string
|
|
4
|
+
key: string
|
|
5
|
+
// JSON-encoded
|
|
6
|
+
payload: string
|
|
7
|
+
indexedAt: string
|
|
8
|
+
updatedAt: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const tableName = 'private_data'
|
|
12
|
+
|
|
13
|
+
export type PartialDB = { [tableName]: PrivateData }
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ColumnType } from 'kysely'
|
|
2
|
+
|
|
1
3
|
export interface Record {
|
|
2
4
|
uri: string
|
|
3
5
|
cid: string
|
|
@@ -5,7 +7,7 @@ export interface Record {
|
|
|
5
7
|
json: string
|
|
6
8
|
indexedAt: string
|
|
7
9
|
takedownRef: string | null
|
|
8
|
-
tags: string[] | null
|
|
10
|
+
tags: ColumnType<string[] | null, string | undefined, string> | null
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export const tableName = 'record'
|
|
@@ -3,9 +3,11 @@ import http from 'node:http'
|
|
|
3
3
|
import { expressConnectMiddleware } from '@connectrpc/connect-express'
|
|
4
4
|
import express from 'express'
|
|
5
5
|
import { IdResolver, MemoryCache } from '@atproto/identity'
|
|
6
|
-
import { Database } from './db'
|
|
6
|
+
import { Database, DatabaseSchema } from './db'
|
|
7
7
|
import createRoutes from './routes'
|
|
8
8
|
|
|
9
|
+
export type { DatabaseSchema }
|
|
10
|
+
|
|
9
11
|
export { RepoSubscription } from './subscription'
|
|
10
12
|
|
|
11
13
|
export class DataPlaneServer {
|
|
@@ -14,6 +14,7 @@ import lists from './lists'
|
|
|
14
14
|
import moderation from './moderation'
|
|
15
15
|
import mutes from './mutes'
|
|
16
16
|
import notifs from './notifs'
|
|
17
|
+
import privateData from './private-data'
|
|
17
18
|
import profile from './profile'
|
|
18
19
|
import quotes from './quotes'
|
|
19
20
|
import records from './records'
|
|
@@ -40,6 +41,7 @@ export default (db: Database, idResolver: IdResolver) =>
|
|
|
40
41
|
...moderation(db),
|
|
41
42
|
...mutes(db),
|
|
42
43
|
...notifs(db),
|
|
44
|
+
...privateData(db),
|
|
43
45
|
...profile(db),
|
|
44
46
|
...quotes(db),
|
|
45
47
|
...records(db),
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ServiceImpl } from '@connectrpc/connect'
|
|
2
|
+
import { keyBy } from '@atproto/common'
|
|
3
|
+
import {
|
|
4
|
+
ChatPreference,
|
|
5
|
+
FilterablePreference,
|
|
6
|
+
Preference,
|
|
7
|
+
Preferences,
|
|
8
|
+
} from '../../../lexicon/types/app/bsky/notification/defs'
|
|
9
|
+
import { Service } from '../../../proto/bsky_connect'
|
|
10
|
+
import {
|
|
11
|
+
ChatNotificationFilter,
|
|
12
|
+
ChatNotificationPreference,
|
|
13
|
+
FilterableNotificationPreference,
|
|
14
|
+
NotificationFilter,
|
|
15
|
+
NotificationPreference,
|
|
16
|
+
NotificationPreferences,
|
|
17
|
+
} from '../../../proto/bsky_pb'
|
|
18
|
+
import { Database } from '../db'
|
|
19
|
+
|
|
20
|
+
export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
21
|
+
async getNotificationPreferences(req) {
|
|
22
|
+
const { dids } = req
|
|
23
|
+
const res = await db.db
|
|
24
|
+
.selectFrom('private_data')
|
|
25
|
+
.selectAll()
|
|
26
|
+
.where('actorDid', 'in', dids)
|
|
27
|
+
.where('namespace', '=', 'app.bsky.notification.defs#preferences')
|
|
28
|
+
.where('key', '=', 'self')
|
|
29
|
+
.execute()
|
|
30
|
+
|
|
31
|
+
const byDid = keyBy(res, 'actorDid')
|
|
32
|
+
const preferences = dids.map((did) => {
|
|
33
|
+
const row = byDid.get(did)
|
|
34
|
+
if (!row) {
|
|
35
|
+
return {}
|
|
36
|
+
}
|
|
37
|
+
const p: Preferences = JSON.parse(row.payload)
|
|
38
|
+
return lexToProtobuf(p, row.payload)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return { preferences }
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const lexToProtobuf = (
|
|
46
|
+
p: Preferences,
|
|
47
|
+
json: string,
|
|
48
|
+
): NotificationPreferences => {
|
|
49
|
+
return new NotificationPreferences({
|
|
50
|
+
entry: Buffer.from(json),
|
|
51
|
+
chat: lexChatPreferenceToProtobuf(p.chat),
|
|
52
|
+
follow: lexFilterablePreferenceToProtobuf(p.follow),
|
|
53
|
+
like: lexFilterablePreferenceToProtobuf(p.like),
|
|
54
|
+
likeViaRepost: lexFilterablePreferenceToProtobuf(p.likeViaRepost),
|
|
55
|
+
mention: lexFilterablePreferenceToProtobuf(p.mention),
|
|
56
|
+
quote: lexFilterablePreferenceToProtobuf(p.quote),
|
|
57
|
+
reply: lexFilterablePreferenceToProtobuf(p.reply),
|
|
58
|
+
repost: lexFilterablePreferenceToProtobuf(p.repost),
|
|
59
|
+
repostViaRepost: lexFilterablePreferenceToProtobuf(p.repostViaRepost),
|
|
60
|
+
starterpackJoined: lexPreferenceToProtobuf(p.starterpackJoined),
|
|
61
|
+
subscribedPost: lexPreferenceToProtobuf(p.subscribedPost),
|
|
62
|
+
unverified: lexPreferenceToProtobuf(p.unverified),
|
|
63
|
+
verified: lexPreferenceToProtobuf(p.verified),
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const lexChatPreferenceToProtobuf = (
|
|
68
|
+
p: ChatPreference,
|
|
69
|
+
): ChatNotificationPreference =>
|
|
70
|
+
new ChatNotificationPreference({
|
|
71
|
+
filter:
|
|
72
|
+
p.filter === 'accepted'
|
|
73
|
+
? ChatNotificationFilter.ACCEPTED
|
|
74
|
+
: ChatNotificationFilter.ALL,
|
|
75
|
+
push: { enabled: p.push ?? true },
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const lexFilterablePreferenceToProtobuf = (
|
|
79
|
+
p: FilterablePreference,
|
|
80
|
+
): FilterableNotificationPreference =>
|
|
81
|
+
new FilterableNotificationPreference({
|
|
82
|
+
filter:
|
|
83
|
+
p.filter === 'follows'
|
|
84
|
+
? NotificationFilter.FOLLOWS
|
|
85
|
+
: NotificationFilter.ALL,
|
|
86
|
+
list: { enabled: p.list ?? true },
|
|
87
|
+
push: { enabled: p.push ?? true },
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const lexPreferenceToProtobuf = (p: Preference): NotificationPreference =>
|
|
91
|
+
new NotificationPreference({
|
|
92
|
+
list: { enabled: p.list ?? true },
|
|
93
|
+
push: { enabled: p.push ?? true },
|
|
94
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import * as imageServer from './image/server'
|
|
|
29
29
|
import { ImageUriBuilder } from './image/uri'
|
|
30
30
|
import { createServer } from './lexicon'
|
|
31
31
|
import { loggerMiddleware } from './logger'
|
|
32
|
+
import { createStashClient } from './stash'
|
|
32
33
|
import { Views } from './views'
|
|
33
34
|
import { VideoUriBuilder } from './views/util'
|
|
34
35
|
|
|
@@ -136,6 +137,8 @@ export class BskyAppView {
|
|
|
136
137
|
interceptors: config.bsyncApiKey ? [bsyncAuth(config.bsyncApiKey)] : [],
|
|
137
138
|
})
|
|
138
139
|
|
|
140
|
+
const stashClient = createStashClient(bsyncClient)
|
|
141
|
+
|
|
139
142
|
const courierClient = config.courierUrl
|
|
140
143
|
? createCourierClient({
|
|
141
144
|
baseUrl: config.courierUrl,
|
|
@@ -178,6 +181,7 @@ export class BskyAppView {
|
|
|
178
181
|
signingKey,
|
|
179
182
|
idResolver,
|
|
180
183
|
bsyncClient,
|
|
184
|
+
stashClient,
|
|
181
185
|
courierClient,
|
|
182
186
|
authVerifier,
|
|
183
187
|
featureGates,
|
package/src/lexicon/index.ts
CHANGED
|
@@ -138,9 +138,11 @@ import * as AppBskyGraphUnmuteActor from './types/app/bsky/graph/unmuteActor.js'
|
|
|
138
138
|
import * as AppBskyGraphUnmuteActorList from './types/app/bsky/graph/unmuteActorList.js'
|
|
139
139
|
import * as AppBskyGraphUnmuteThread from './types/app/bsky/graph/unmuteThread.js'
|
|
140
140
|
import * as AppBskyLabelerGetServices from './types/app/bsky/labeler/getServices.js'
|
|
141
|
+
import * as AppBskyNotificationGetPreferences from './types/app/bsky/notification/getPreferences.js'
|
|
141
142
|
import * as AppBskyNotificationGetUnreadCount from './types/app/bsky/notification/getUnreadCount.js'
|
|
142
143
|
import * as AppBskyNotificationListNotifications from './types/app/bsky/notification/listNotifications.js'
|
|
143
144
|
import * as AppBskyNotificationPutPreferences from './types/app/bsky/notification/putPreferences.js'
|
|
145
|
+
import * as AppBskyNotificationPutPreferencesV2 from './types/app/bsky/notification/putPreferencesV2.js'
|
|
144
146
|
import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush.js'
|
|
145
147
|
import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen.js'
|
|
146
148
|
import * as AppBskyUnspeccedGetConfig from './types/app/bsky/unspecced/getConfig.js'
|
|
@@ -1853,6 +1855,17 @@ export class AppBskyNotificationNS {
|
|
|
1853
1855
|
this._server = server
|
|
1854
1856
|
}
|
|
1855
1857
|
|
|
1858
|
+
getPreferences<AV extends AuthVerifier>(
|
|
1859
|
+
cfg: ConfigOf<
|
|
1860
|
+
AV,
|
|
1861
|
+
AppBskyNotificationGetPreferences.Handler<ExtractAuth<AV>>,
|
|
1862
|
+
AppBskyNotificationGetPreferences.HandlerReqCtx<ExtractAuth<AV>>
|
|
1863
|
+
>,
|
|
1864
|
+
) {
|
|
1865
|
+
const nsid = 'app.bsky.notification.getPreferences' // @ts-ignore
|
|
1866
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1856
1869
|
getUnreadCount<AV extends AuthVerifier>(
|
|
1857
1870
|
cfg: ConfigOf<
|
|
1858
1871
|
AV,
|
|
@@ -1886,6 +1899,17 @@ export class AppBskyNotificationNS {
|
|
|
1886
1899
|
return this._server.xrpc.method(nsid, cfg)
|
|
1887
1900
|
}
|
|
1888
1901
|
|
|
1902
|
+
putPreferencesV2<AV extends AuthVerifier>(
|
|
1903
|
+
cfg: ConfigOf<
|
|
1904
|
+
AV,
|
|
1905
|
+
AppBskyNotificationPutPreferencesV2.Handler<ExtractAuth<AV>>,
|
|
1906
|
+
AppBskyNotificationPutPreferencesV2.HandlerReqCtx<ExtractAuth<AV>>
|
|
1907
|
+
>,
|
|
1908
|
+
) {
|
|
1909
|
+
const nsid = 'app.bsky.notification.putPreferencesV2' // @ts-ignore
|
|
1910
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1889
1913
|
registerPush<AV extends AuthVerifier>(
|
|
1890
1914
|
cfg: ConfigOf<
|
|
1891
1915
|
AV,
|