@atproto/bsky 0.0.67 → 0.0.69
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 +21 -0
- package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getPostThread.js +7 -2
- package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
- package/dist/api/app/bsky/notification/getUnreadCount.js +6 -0
- package/dist/api/app/bsky/notification/getUnreadCount.js.map +1 -1
- package/dist/api/app/bsky/notification/listNotifications.js +15 -2
- package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/api/app/bsky/notification/putPreferences.d.ts +4 -0
- package/dist/api/app/bsky/notification/putPreferences.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/putPreferences.js +17 -0
- package/dist/api/app/bsky/notification/putPreferences.js.map +1 -0
- package/dist/api/app/bsky/notification/updateSeen.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/updateSeen.js +14 -5
- package/dist/api/app/bsky/notification/updateSeen.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -1
- package/dist/auth-verifier.d.ts +1 -0
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +3 -2
- package/dist/auth-verifier.js.map +1 -1
- package/dist/data-plane/bsync/index.js +18 -0
- package/dist/data-plane/bsync/index.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20240719T203853939Z-priority-notifs.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20240719T203853939Z-priority-notifs.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20240719T203853939Z-priority-notifs.js +26 -0
- package/dist/data-plane/server/db/migrations/20240719T203853939Z-priority-notifs.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/actor-state.d.ts +2 -0
- package/dist/data-plane/server/db/tables/actor-state.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/actor-state.js.map +1 -1
- package/dist/data-plane/server/routes/notifs.d.ts.map +1 -1
- package/dist/data-plane/server/routes/notifs.js +55 -12
- 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 +4 -1
- package/dist/data-plane/server/routes/profile.js.map +1 -1
- package/dist/hydration/actor.d.ts +1 -0
- package/dist/hydration/actor.d.ts.map +1 -1
- package/dist/hydration/actor.js +1 -0
- package/dist/hydration/actor.js.map +1 -1
- package/dist/hydration/hydrator.d.ts +2 -0
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +7 -0
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +32 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +32 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/getUnreadCount.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/notification/getUnreadCount.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +2 -0
- 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/lexicon/types/app/bsky/notification/putPreferences.d.ts +29 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferences.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferences.js +3 -0
- package/dist/lexicon/types/app/bsky/notification/putPreferences.js.map +1 -0
- package/dist/logger.d.ts +3 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +9 -76
- package/dist/logger.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +24 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +65 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/proto/bsync_connect.d.ts +19 -1
- package/dist/proto/bsync_connect.d.ts.map +1 -1
- package/dist/proto/bsync_connect.js +18 -0
- package/dist/proto/bsync_connect.js.map +1 -1
- package/dist/proto/bsync_pb.d.ts +105 -0
- package/dist/proto/bsync_pb.d.ts.map +1 -1
- package/dist/proto/bsync_pb.js +324 -1
- package/dist/proto/bsync_pb.js.map +1 -1
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +6 -4
- package/dist/views/index.js.map +1 -1
- package/package.json +7 -7
- package/proto/bsky.proto +7 -1
- package/src/api/app/bsky/feed/getPostThread.ts +8 -3
- package/src/api/app/bsky/notification/getUnreadCount.ts +7 -0
- package/src/api/app/bsky/notification/listNotifications.ts +17 -2
- package/src/api/app/bsky/notification/putPreferences.ts +16 -0
- package/src/api/app/bsky/notification/updateSeen.ts +14 -5
- package/src/api/index.ts +2 -0
- package/src/auth-verifier.ts +3 -2
- package/src/data-plane/bsync/index.ts +22 -0
- package/src/data-plane/server/db/migrations/20240719T203853939Z-priority-notifs.ts +25 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/actor-state.ts +2 -0
- package/src/data-plane/server/routes/notifs.ts +61 -17
- package/src/data-plane/server/routes/profile.ts +4 -1
- package/src/hydration/actor.ts +2 -1
- package/src/hydration/hydrator.ts +3 -1
- package/src/lexicon/index.ts +12 -0
- package/src/lexicon/lexicons.ts +33 -0
- package/src/lexicon/types/app/bsky/notification/getUnreadCount.ts +1 -0
- package/src/lexicon/types/app/bsky/notification/listNotifications.ts +2 -0
- package/src/lexicon/types/app/bsky/notification/putPreferences.ts +38 -0
- package/src/logger.ts +11 -81
- package/src/proto/bsky_pb.ts +41 -0
- package/src/proto/bsync_connect.ts +22 -0
- package/src/proto/bsync_pb.ts +317 -0
- package/src/views/index.ts +8 -4
- package/tests/views/__snapshots__/notifications.test.ts.snap +415 -0
- package/tests/views/notifications.test.ts +42 -1
|
@@ -3,13 +3,20 @@ import { ServiceImpl } from '@connectrpc/connect'
|
|
|
3
3
|
import { Timestamp } from '@bufbuild/protobuf'
|
|
4
4
|
import { Service } from '../../../proto/bsky_connect'
|
|
5
5
|
import { Database } from '../db'
|
|
6
|
-
import { countAll,
|
|
6
|
+
import { countAll, notSoftDeletedClause } from '../db/util'
|
|
7
7
|
import { TimeCidKeyset, paginate } from '../db/pagination'
|
|
8
8
|
|
|
9
9
|
export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
10
10
|
async getNotifications(req) {
|
|
11
|
-
const { actorDid, limit, cursor } = req
|
|
11
|
+
const { actorDid, limit, cursor, priority } = req
|
|
12
12
|
const { ref } = db.db.dynamic
|
|
13
|
+
const priorityFollowQb = db.db
|
|
14
|
+
.selectFrom('follow')
|
|
15
|
+
.select(sql<boolean>`${true}`.as('val'))
|
|
16
|
+
.where('creator', '=', actorDid)
|
|
17
|
+
.whereRef('subjectDid', '=', ref('notif.author'))
|
|
18
|
+
.limit(1)
|
|
19
|
+
|
|
13
20
|
let builder = db.db
|
|
14
21
|
.selectFrom('notification as notif')
|
|
15
22
|
.where('notif.did', '=', actorDid)
|
|
@@ -23,6 +30,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
23
30
|
.whereRef('subject.uri', '=', ref('notif.reasonSubject')),
|
|
24
31
|
),
|
|
25
32
|
)
|
|
33
|
+
.if(priority, (qb) => qb.whereExists(priorityFollowQb))
|
|
26
34
|
.select([
|
|
27
35
|
'notif.author as authorDid',
|
|
28
36
|
'notif.recordUri as uri',
|
|
@@ -31,6 +39,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
31
39
|
'notif.reasonSubject as reasonSubject',
|
|
32
40
|
'notif.sortAt as sortAt',
|
|
33
41
|
])
|
|
42
|
+
.select(priorityFollowQb.as('priority'))
|
|
34
43
|
|
|
35
44
|
const keyset = new TimeCidKeyset(
|
|
36
45
|
ref('notif.sortAt'),
|
|
@@ -50,6 +59,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
50
59
|
reason: notif.reason,
|
|
51
60
|
reasonSubject: notif.reasonSubject ?? undefined,
|
|
52
61
|
timestamp: Timestamp.fromDate(new Date(notif.sortAt)),
|
|
62
|
+
priority: notif.priority ?? false,
|
|
53
63
|
}))
|
|
54
64
|
return {
|
|
55
65
|
notifications,
|
|
@@ -58,22 +68,37 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
58
68
|
},
|
|
59
69
|
|
|
60
70
|
async getNotificationSeen(req) {
|
|
71
|
+
const { actorDid, priority } = req
|
|
61
72
|
const res = await db.db
|
|
62
73
|
.selectFrom('actor_state')
|
|
63
|
-
.where('did', '=',
|
|
74
|
+
.where('did', '=', actorDid)
|
|
64
75
|
.selectAll()
|
|
65
76
|
.executeTakeFirst()
|
|
66
77
|
if (!res) {
|
|
67
78
|
return {}
|
|
68
79
|
}
|
|
80
|
+
const lastSeen =
|
|
81
|
+
priority && res.lastSeenPriorityNotifs
|
|
82
|
+
? res.lastSeenPriorityNotifs
|
|
83
|
+
: res.lastSeenNotifs
|
|
69
84
|
return {
|
|
70
|
-
timestamp: Timestamp.fromDate(new Date(
|
|
85
|
+
timestamp: Timestamp.fromDate(new Date(lastSeen)),
|
|
71
86
|
}
|
|
72
87
|
},
|
|
73
88
|
|
|
74
89
|
async getUnreadNotificationCount(req) {
|
|
75
|
-
const { actorDid } = req
|
|
90
|
+
const { actorDid, priority } = req
|
|
76
91
|
const { ref } = db.db.dynamic
|
|
92
|
+
const lastSeenRes = await db.db
|
|
93
|
+
.selectFrom('actor_state')
|
|
94
|
+
.where('did', '=', actorDid)
|
|
95
|
+
.selectAll()
|
|
96
|
+
.executeTakeFirst()
|
|
97
|
+
const lastSeen =
|
|
98
|
+
priority && lastSeenRes?.lastSeenPriorityNotifs
|
|
99
|
+
? lastSeenRes.lastSeenPriorityNotifs
|
|
100
|
+
: lastSeenRes?.lastSeenNotifs
|
|
101
|
+
|
|
77
102
|
const result = await db.db
|
|
78
103
|
.selectFrom('notification')
|
|
79
104
|
.select(countAll.as('count'))
|
|
@@ -84,10 +109,15 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
84
109
|
.where(notSoftDeletedClause(ref('actor')))
|
|
85
110
|
// Ensure to hit notification_did_sortat_idx, handling case where lastSeenNotifs is null.
|
|
86
111
|
.where('notification.did', '=', actorDid)
|
|
87
|
-
.where(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
.where('notification.sortAt', '>', lastSeen ?? '')
|
|
113
|
+
.if(priority, (qb) =>
|
|
114
|
+
qb.whereExists(
|
|
115
|
+
db.db
|
|
116
|
+
.selectFrom('follow')
|
|
117
|
+
.select(sql<boolean>`${true}`.as('val'))
|
|
118
|
+
.where('creator', '=', actorDid)
|
|
119
|
+
.whereRef('subjectDid', '=', ref('notif.author')),
|
|
120
|
+
),
|
|
91
121
|
)
|
|
92
122
|
.executeTakeFirst()
|
|
93
123
|
|
|
@@ -97,19 +127,33 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
97
127
|
},
|
|
98
128
|
|
|
99
129
|
async updateNotificationSeen(req) {
|
|
100
|
-
const { actorDid, timestamp } = req
|
|
130
|
+
const { actorDid, timestamp, priority } = req
|
|
101
131
|
if (!timestamp) {
|
|
102
132
|
return
|
|
103
133
|
}
|
|
104
|
-
const
|
|
134
|
+
const timestampIso = timestamp.toDate().toISOString()
|
|
135
|
+
let builder = db.db
|
|
136
|
+
.updateTable('actor_state')
|
|
137
|
+
.where('did', '=', actorDid)
|
|
138
|
+
.returningAll()
|
|
139
|
+
if (priority) {
|
|
140
|
+
builder = builder.set({ lastSeenPriorityNotifs: timestampIso })
|
|
141
|
+
} else {
|
|
142
|
+
builder = builder.set({ lastSeenNotifs: timestampIso })
|
|
143
|
+
}
|
|
144
|
+
const updateRes = await builder.executeTakeFirst()
|
|
145
|
+
if (updateRes) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
105
148
|
await db.db
|
|
106
149
|
.insertInto('actor_state')
|
|
107
|
-
.values({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
150
|
+
.values({
|
|
151
|
+
did: actorDid,
|
|
152
|
+
lastSeenNotifs: timestampIso,
|
|
153
|
+
priorityNotifs: priority,
|
|
154
|
+
lastSeenPriorityNotifs: priority ? timestampIso : undefined,
|
|
155
|
+
})
|
|
156
|
+
.onConflict((oc) => oc.doNothing())
|
|
113
157
|
.executeTakeFirst()
|
|
114
158
|
},
|
|
115
159
|
})
|
|
@@ -22,8 +22,10 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
22
22
|
const [handlesRes, profiles, chatDeclarations] = await Promise.all([
|
|
23
23
|
db.db
|
|
24
24
|
.selectFrom('actor')
|
|
25
|
-
.
|
|
25
|
+
.leftJoin('actor_state', 'actor_state.did', 'actor.did')
|
|
26
|
+
.where('actor.did', 'in', dids)
|
|
26
27
|
.selectAll('actor')
|
|
28
|
+
.select('actor_state.priorityNotifs')
|
|
27
29
|
.select([
|
|
28
30
|
db.db
|
|
29
31
|
.selectFrom('labeler')
|
|
@@ -55,6 +57,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
55
57
|
: undefined,
|
|
56
58
|
upstreamStatus: row?.upstreamStatus ?? '',
|
|
57
59
|
createdAt: profiles.records[i].createdAt, // @NOTE profile creation date not trusted in production
|
|
60
|
+
priorityNotifications: row?.priorityNotifs ?? false,
|
|
58
61
|
}
|
|
59
62
|
})
|
|
60
63
|
return { actors }
|
package/src/hydration/actor.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Timestamp } from '@bufbuild/protobuf'
|
|
2
1
|
import { DataPlaneClient } from '../data-plane/client'
|
|
3
2
|
import { Record as ProfileRecord } from '../lexicon/types/app/bsky/actor/profile'
|
|
4
3
|
import { Record as ChatDeclarationRecord } from '../lexicon/types/chat/bsky/actor/declaration'
|
|
@@ -23,6 +22,7 @@ export type Actor = {
|
|
|
23
22
|
allowIncomingChatsFrom?: string
|
|
24
23
|
upstreamStatus?: string
|
|
25
24
|
createdAt?: Date
|
|
25
|
+
priorityNotifications: boolean
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export type Actors = HydrationMap<Actor>
|
|
@@ -131,6 +131,7 @@ export class ActorHydrator {
|
|
|
131
131
|
allowIncomingChatsFrom: actor.allowIncomingChatsFrom || undefined,
|
|
132
132
|
upstreamStatus: actor.upstreamStatus || undefined,
|
|
133
133
|
createdAt: actor.createdAt?.toDate(),
|
|
134
|
+
priorityNotifications: actor.priorityNotifications,
|
|
134
135
|
})
|
|
135
136
|
}, new HydrationMap<Actor>())
|
|
136
137
|
}
|
|
@@ -59,12 +59,12 @@ import {
|
|
|
59
59
|
FeedItem,
|
|
60
60
|
} from './feed'
|
|
61
61
|
import { ParsedLabelers } from '../util'
|
|
62
|
-
import starterPack from '../data-plane/server/indexing/plugins/starter-pack'
|
|
63
62
|
|
|
64
63
|
export class HydrateCtx {
|
|
65
64
|
labelers = this.vals.labelers
|
|
66
65
|
viewer = this.vals.viewer !== null ? serviceRefToDid(this.vals.viewer) : null
|
|
67
66
|
includeTakedowns = this.vals.includeTakedowns
|
|
67
|
+
include3pBlocks = this.vals.include3pBlocks
|
|
68
68
|
constructor(private vals: HydrateCtxVals) {}
|
|
69
69
|
copy<V extends Partial<HydrateCtxVals>>(vals?: V): HydrateCtx & V {
|
|
70
70
|
return new HydrateCtx({ ...this.vals, ...vals }) as HydrateCtx & V
|
|
@@ -75,6 +75,7 @@ export type HydrateCtxVals = {
|
|
|
75
75
|
labelers: ParsedLabelers
|
|
76
76
|
viewer: string | null
|
|
77
77
|
includeTakedowns?: boolean
|
|
78
|
+
include3pBlocks?: boolean
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
export type HydrationState = {
|
|
@@ -914,6 +915,7 @@ export class Hydrator {
|
|
|
914
915
|
labelers: availableLabelers,
|
|
915
916
|
viewer: vals.viewer,
|
|
916
917
|
includeTakedowns: vals.includeTakedowns,
|
|
918
|
+
include3pBlocks: vals.include3pBlocks,
|
|
917
919
|
})
|
|
918
920
|
}
|
|
919
921
|
|
package/src/lexicon/index.ts
CHANGED
|
@@ -130,6 +130,7 @@ import * as AppBskyGraphUnmuteThread from './types/app/bsky/graph/unmuteThread'
|
|
|
130
130
|
import * as AppBskyLabelerGetServices from './types/app/bsky/labeler/getServices'
|
|
131
131
|
import * as AppBskyNotificationGetUnreadCount from './types/app/bsky/notification/getUnreadCount'
|
|
132
132
|
import * as AppBskyNotificationListNotifications from './types/app/bsky/notification/listNotifications'
|
|
133
|
+
import * as AppBskyNotificationPutPreferences from './types/app/bsky/notification/putPreferences'
|
|
133
134
|
import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush'
|
|
134
135
|
import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen'
|
|
135
136
|
import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators'
|
|
@@ -1716,6 +1717,17 @@ export class AppBskyNotificationNS {
|
|
|
1716
1717
|
return this._server.xrpc.method(nsid, cfg)
|
|
1717
1718
|
}
|
|
1718
1719
|
|
|
1720
|
+
putPreferences<AV extends AuthVerifier>(
|
|
1721
|
+
cfg: ConfigOf<
|
|
1722
|
+
AV,
|
|
1723
|
+
AppBskyNotificationPutPreferences.Handler<ExtractAuth<AV>>,
|
|
1724
|
+
AppBskyNotificationPutPreferences.HandlerReqCtx<ExtractAuth<AV>>
|
|
1725
|
+
>,
|
|
1726
|
+
) {
|
|
1727
|
+
const nsid = 'app.bsky.notification.putPreferences' // @ts-ignore
|
|
1728
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1719
1731
|
registerPush<AV extends AuthVerifier>(
|
|
1720
1732
|
cfg: ConfigOf<
|
|
1721
1733
|
AV,
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -8341,6 +8341,9 @@ export const schemaDict = {
|
|
|
8341
8341
|
parameters: {
|
|
8342
8342
|
type: 'params',
|
|
8343
8343
|
properties: {
|
|
8344
|
+
priority: {
|
|
8345
|
+
type: 'boolean',
|
|
8346
|
+
},
|
|
8344
8347
|
seenAt: {
|
|
8345
8348
|
type: 'string',
|
|
8346
8349
|
format: 'datetime',
|
|
@@ -8379,6 +8382,9 @@ export const schemaDict = {
|
|
|
8379
8382
|
maximum: 100,
|
|
8380
8383
|
default: 50,
|
|
8381
8384
|
},
|
|
8385
|
+
priority: {
|
|
8386
|
+
type: 'boolean',
|
|
8387
|
+
},
|
|
8382
8388
|
cursor: {
|
|
8383
8389
|
type: 'string',
|
|
8384
8390
|
},
|
|
@@ -8404,6 +8410,9 @@ export const schemaDict = {
|
|
|
8404
8410
|
ref: 'lex:app.bsky.notification.listNotifications#notification',
|
|
8405
8411
|
},
|
|
8406
8412
|
},
|
|
8413
|
+
priority: {
|
|
8414
|
+
type: 'boolean',
|
|
8415
|
+
},
|
|
8407
8416
|
seenAt: {
|
|
8408
8417
|
type: 'string',
|
|
8409
8418
|
format: 'datetime',
|
|
@@ -8475,6 +8484,29 @@ export const schemaDict = {
|
|
|
8475
8484
|
},
|
|
8476
8485
|
},
|
|
8477
8486
|
},
|
|
8487
|
+
AppBskyNotificationPutPreferences: {
|
|
8488
|
+
lexicon: 1,
|
|
8489
|
+
id: 'app.bsky.notification.putPreferences',
|
|
8490
|
+
defs: {
|
|
8491
|
+
main: {
|
|
8492
|
+
type: 'procedure',
|
|
8493
|
+
description:
|
|
8494
|
+
'Set notification-related preferences for an account. Requires auth.',
|
|
8495
|
+
input: {
|
|
8496
|
+
encoding: 'application/json',
|
|
8497
|
+
schema: {
|
|
8498
|
+
type: 'object',
|
|
8499
|
+
required: ['priority'],
|
|
8500
|
+
properties: {
|
|
8501
|
+
priority: {
|
|
8502
|
+
type: 'boolean',
|
|
8503
|
+
},
|
|
8504
|
+
},
|
|
8505
|
+
},
|
|
8506
|
+
},
|
|
8507
|
+
},
|
|
8508
|
+
},
|
|
8509
|
+
},
|
|
8478
8510
|
AppBskyNotificationRegisterPush: {
|
|
8479
8511
|
lexicon: 1,
|
|
8480
8512
|
id: 'app.bsky.notification.registerPush',
|
|
@@ -10051,6 +10083,7 @@ export const ids = {
|
|
|
10051
10083
|
AppBskyNotificationGetUnreadCount: 'app.bsky.notification.getUnreadCount',
|
|
10052
10084
|
AppBskyNotificationListNotifications:
|
|
10053
10085
|
'app.bsky.notification.listNotifications',
|
|
10086
|
+
AppBskyNotificationPutPreferences: 'app.bsky.notification.putPreferences',
|
|
10054
10087
|
AppBskyNotificationRegisterPush: 'app.bsky.notification.registerPush',
|
|
10055
10088
|
AppBskyNotificationUpdateSeen: 'app.bsky.notification.updateSeen',
|
|
10056
10089
|
AppBskyRichtextFacet: 'app.bsky.richtext.facet',
|
|
@@ -12,6 +12,7 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs'
|
|
|
12
12
|
|
|
13
13
|
export interface QueryParams {
|
|
14
14
|
limit: number
|
|
15
|
+
priority?: boolean
|
|
15
16
|
cursor?: string
|
|
16
17
|
seenAt?: string
|
|
17
18
|
}
|
|
@@ -21,6 +22,7 @@ export type InputSchema = undefined
|
|
|
21
22
|
export interface OutputSchema {
|
|
22
23
|
cursor?: string
|
|
23
24
|
notifications: Notification[]
|
|
25
|
+
priority?: boolean
|
|
24
26
|
seenAt?: string
|
|
25
27
|
[k: string]: unknown
|
|
26
28
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import express from 'express'
|
|
5
|
+
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
6
|
+
import { lexicons } from '../../../../lexicons'
|
|
7
|
+
import { isObj, hasProp } from '../../../../util'
|
|
8
|
+
import { CID } from 'multiformats/cid'
|
|
9
|
+
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
|
10
|
+
|
|
11
|
+
export interface QueryParams {}
|
|
12
|
+
|
|
13
|
+
export interface InputSchema {
|
|
14
|
+
priority: boolean
|
|
15
|
+
[k: string]: unknown
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface HandlerInput {
|
|
19
|
+
encoding: 'application/json'
|
|
20
|
+
body: InputSchema
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface HandlerError {
|
|
24
|
+
status: number
|
|
25
|
+
message?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type HandlerOutput = HandlerError | void
|
|
29
|
+
export type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
|
30
|
+
auth: HA
|
|
31
|
+
params: QueryParams
|
|
32
|
+
input: HandlerInput
|
|
33
|
+
req: express.Request
|
|
34
|
+
res: express.Response
|
|
35
|
+
}
|
|
36
|
+
export type Handler<HA extends HandlerAuth = never> = (
|
|
37
|
+
ctx: HandlerReqCtx<HA>,
|
|
38
|
+
) => Promise<HandlerOutput> | HandlerOutput
|
package/src/logger.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { IncomingMessage } from 'node:http'
|
|
1
2
|
import { stdSerializers } from 'pino'
|
|
2
3
|
import pinoHttp from 'pino-http'
|
|
3
|
-
import { subsystemLogger } from '@atproto/common'
|
|
4
|
+
import { obfuscateHeaders, subsystemLogger } from '@atproto/common'
|
|
4
5
|
|
|
5
6
|
export const dbLogger: ReturnType<typeof subsystemLogger> =
|
|
6
7
|
subsystemLogger('bsky:db')
|
|
@@ -20,85 +21,14 @@ export const httpLogger: ReturnType<typeof subsystemLogger> =
|
|
|
20
21
|
export const loggerMiddleware = pinoHttp({
|
|
21
22
|
logger: httpLogger,
|
|
22
23
|
serializers: {
|
|
23
|
-
err:
|
|
24
|
-
|
|
24
|
+
err: (err: unknown) => ({
|
|
25
|
+
code: err?.['code'],
|
|
26
|
+
message: err?.['message'],
|
|
27
|
+
}),
|
|
28
|
+
req: (req: IncomingMessage) => {
|
|
29
|
+
const serialized = stdSerializers.req(req)
|
|
30
|
+
const headers = obfuscateHeaders(serialized.headers)
|
|
31
|
+
return { ...serialized, headers }
|
|
32
|
+
},
|
|
25
33
|
},
|
|
26
34
|
})
|
|
27
|
-
|
|
28
|
-
function errSerializer(err: any) {
|
|
29
|
-
return {
|
|
30
|
-
code: err?.code,
|
|
31
|
-
message: err?.message,
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function reqSerializer(req: any) {
|
|
36
|
-
const serialized = stdSerializers.req(req)
|
|
37
|
-
serialized.headers = obfuscateHeaders(serialized.headers)
|
|
38
|
-
return serialized
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function obfuscateHeaders(headers: Record<string, string>) {
|
|
42
|
-
const obfuscatedHeaders: Record<string, string> = {}
|
|
43
|
-
for (const key in headers) {
|
|
44
|
-
if (key.toLowerCase() === 'authorization') {
|
|
45
|
-
obfuscatedHeaders[key] = obfuscateAuthHeader(headers[key])
|
|
46
|
-
} else if (key.toLowerCase() === 'dpop') {
|
|
47
|
-
obfuscatedHeaders[key] = obfuscateJws(headers[key]) || 'Invalid'
|
|
48
|
-
} else {
|
|
49
|
-
obfuscatedHeaders[key] = headers[key]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return obfuscatedHeaders
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function obfuscateAuthHeader(authHeader: string): string {
|
|
56
|
-
// This is a hot path (runs on every request). Avoid using split() or regex.
|
|
57
|
-
|
|
58
|
-
const spaceIdx = authHeader.indexOf(' ')
|
|
59
|
-
if (spaceIdx === -1) return 'Invalid'
|
|
60
|
-
|
|
61
|
-
const type = authHeader.slice(0, spaceIdx)
|
|
62
|
-
switch (type.toLowerCase()) {
|
|
63
|
-
case 'bearer':
|
|
64
|
-
return `${type} ${obfuscateBearer(authHeader.slice(spaceIdx + 1))}`
|
|
65
|
-
case 'dpop':
|
|
66
|
-
return `${type} ${obfuscateJws(authHeader.slice(spaceIdx + 1)) || 'Invalid'}`
|
|
67
|
-
case 'basic':
|
|
68
|
-
return `${type} ${obfuscateBasic(authHeader.slice(spaceIdx + 1)) || 'Invalid'}`
|
|
69
|
-
default:
|
|
70
|
-
return `Invalid`
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function obfuscateBasic(token: string): null | string {
|
|
75
|
-
if (!token) return null
|
|
76
|
-
const buffer = Buffer.from(token, 'base64')
|
|
77
|
-
if (!buffer.length) return null // Buffer.from will silently ignore invalid base64 chars
|
|
78
|
-
const authHeader = buffer.toString('utf8')
|
|
79
|
-
const colIdx = authHeader.indexOf(':')
|
|
80
|
-
if (colIdx === -1) return null
|
|
81
|
-
const username = authHeader.slice(0, colIdx)
|
|
82
|
-
return `${username}:***`
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function obfuscateBearer(token: string): string {
|
|
86
|
-
return obfuscateJws(token) || obfuscateToken(token)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function obfuscateToken(token: string): string {
|
|
90
|
-
return token ? '***' : ''
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function obfuscateJws(token: string): null | string {
|
|
94
|
-
const firstDot = token.indexOf('.')
|
|
95
|
-
if (firstDot === -1) return null
|
|
96
|
-
|
|
97
|
-
const secondDot = token.indexOf('.', firstDot + 1)
|
|
98
|
-
if (secondDot === -1) return null
|
|
99
|
-
|
|
100
|
-
if (token.indexOf('.', secondDot + 1) !== -1) return null
|
|
101
|
-
|
|
102
|
-
// Strip the signature
|
|
103
|
-
return token.slice(0, secondDot) + '.obfuscated'
|
|
104
|
-
}
|
package/src/proto/bsky_pb.ts
CHANGED
|
@@ -4139,6 +4139,11 @@ export class ActorInfo extends Message<ActorInfo> {
|
|
|
4139
4139
|
*/
|
|
4140
4140
|
createdAt?: Timestamp
|
|
4141
4141
|
|
|
4142
|
+
/**
|
|
4143
|
+
* @generated from field: bool priority_notifications = 11;
|
|
4144
|
+
*/
|
|
4145
|
+
priorityNotifications = false
|
|
4146
|
+
|
|
4142
4147
|
constructor(data?: PartialMessage<ActorInfo>) {
|
|
4143
4148
|
super()
|
|
4144
4149
|
proto3.util.initPartial(data, this)
|
|
@@ -4172,6 +4177,12 @@ export class ActorInfo extends Message<ActorInfo> {
|
|
|
4172
4177
|
T: 9 /* ScalarType.STRING */,
|
|
4173
4178
|
},
|
|
4174
4179
|
{ no: 10, name: 'created_at', kind: 'message', T: Timestamp },
|
|
4180
|
+
{
|
|
4181
|
+
no: 11,
|
|
4182
|
+
name: 'priority_notifications',
|
|
4183
|
+
kind: 'scalar',
|
|
4184
|
+
T: 8 /* ScalarType.BOOL */,
|
|
4185
|
+
},
|
|
4175
4186
|
])
|
|
4176
4187
|
|
|
4177
4188
|
static fromBinary(
|
|
@@ -6884,6 +6895,11 @@ export class GetNotificationsRequest extends Message<GetNotificationsRequest> {
|
|
|
6884
6895
|
*/
|
|
6885
6896
|
cursor = ''
|
|
6886
6897
|
|
|
6898
|
+
/**
|
|
6899
|
+
* @generated from field: bool priority = 4;
|
|
6900
|
+
*/
|
|
6901
|
+
priority = false
|
|
6902
|
+
|
|
6887
6903
|
constructor(data?: PartialMessage<GetNotificationsRequest>) {
|
|
6888
6904
|
super()
|
|
6889
6905
|
proto3.util.initPartial(data, this)
|
|
@@ -6895,6 +6911,7 @@ export class GetNotificationsRequest extends Message<GetNotificationsRequest> {
|
|
|
6895
6911
|
{ no: 1, name: 'actor_did', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
6896
6912
|
{ no: 2, name: 'limit', kind: 'scalar', T: 5 /* ScalarType.INT32 */ },
|
|
6897
6913
|
{ no: 3, name: 'cursor', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
6914
|
+
{ no: 4, name: 'priority', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
|
|
6898
6915
|
])
|
|
6899
6916
|
|
|
6900
6917
|
static fromBinary(
|
|
@@ -6961,6 +6978,11 @@ export class Notification extends Message<Notification> {
|
|
|
6961
6978
|
*/
|
|
6962
6979
|
timestamp?: Timestamp
|
|
6963
6980
|
|
|
6981
|
+
/**
|
|
6982
|
+
* @generated from field: bool priority = 6;
|
|
6983
|
+
*/
|
|
6984
|
+
priority = false
|
|
6985
|
+
|
|
6964
6986
|
constructor(data?: PartialMessage<Notification>) {
|
|
6965
6987
|
super()
|
|
6966
6988
|
proto3.util.initPartial(data, this)
|
|
@@ -6984,6 +7006,7 @@ export class Notification extends Message<Notification> {
|
|
|
6984
7006
|
T: 9 /* ScalarType.STRING */,
|
|
6985
7007
|
},
|
|
6986
7008
|
{ no: 5, name: 'timestamp', kind: 'message', T: Timestamp },
|
|
7009
|
+
{ no: 6, name: 'priority', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
|
|
6987
7010
|
])
|
|
6988
7011
|
|
|
6989
7012
|
static fromBinary(
|
|
@@ -7099,6 +7122,11 @@ export class UpdateNotificationSeenRequest extends Message<UpdateNotificationSee
|
|
|
7099
7122
|
*/
|
|
7100
7123
|
timestamp?: Timestamp
|
|
7101
7124
|
|
|
7125
|
+
/**
|
|
7126
|
+
* @generated from field: bool priority = 3;
|
|
7127
|
+
*/
|
|
7128
|
+
priority = false
|
|
7129
|
+
|
|
7102
7130
|
constructor(data?: PartialMessage<UpdateNotificationSeenRequest>) {
|
|
7103
7131
|
super()
|
|
7104
7132
|
proto3.util.initPartial(data, this)
|
|
@@ -7109,6 +7137,7 @@ export class UpdateNotificationSeenRequest extends Message<UpdateNotificationSee
|
|
|
7109
7137
|
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
7110
7138
|
{ no: 1, name: 'actor_did', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
7111
7139
|
{ no: 2, name: 'timestamp', kind: 'message', T: Timestamp },
|
|
7140
|
+
{ no: 3, name: 'priority', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
|
|
7112
7141
|
])
|
|
7113
7142
|
|
|
7114
7143
|
static fromBinary(
|
|
@@ -7212,6 +7241,11 @@ export class GetNotificationSeenRequest extends Message<GetNotificationSeenReque
|
|
|
7212
7241
|
*/
|
|
7213
7242
|
actorDid = ''
|
|
7214
7243
|
|
|
7244
|
+
/**
|
|
7245
|
+
* @generated from field: bool priority = 2;
|
|
7246
|
+
*/
|
|
7247
|
+
priority = false
|
|
7248
|
+
|
|
7215
7249
|
constructor(data?: PartialMessage<GetNotificationSeenRequest>) {
|
|
7216
7250
|
super()
|
|
7217
7251
|
proto3.util.initPartial(data, this)
|
|
@@ -7221,6 +7255,7 @@ export class GetNotificationSeenRequest extends Message<GetNotificationSeenReque
|
|
|
7221
7255
|
static readonly typeName = 'bsky.GetNotificationSeenRequest'
|
|
7222
7256
|
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
7223
7257
|
{ no: 1, name: 'actor_did', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
7258
|
+
{ no: 2, name: 'priority', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
|
|
7224
7259
|
])
|
|
7225
7260
|
|
|
7226
7261
|
static fromBinary(
|
|
@@ -7325,6 +7360,11 @@ export class GetUnreadNotificationCountRequest extends Message<GetUnreadNotifica
|
|
|
7325
7360
|
*/
|
|
7326
7361
|
actorDid = ''
|
|
7327
7362
|
|
|
7363
|
+
/**
|
|
7364
|
+
* @generated from field: bool priority = 2;
|
|
7365
|
+
*/
|
|
7366
|
+
priority = false
|
|
7367
|
+
|
|
7328
7368
|
constructor(data?: PartialMessage<GetUnreadNotificationCountRequest>) {
|
|
7329
7369
|
super()
|
|
7330
7370
|
proto3.util.initPartial(data, this)
|
|
@@ -7334,6 +7374,7 @@ export class GetUnreadNotificationCountRequest extends Message<GetUnreadNotifica
|
|
|
7334
7374
|
static readonly typeName = 'bsky.GetUnreadNotificationCountRequest'
|
|
7335
7375
|
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
7336
7376
|
{ no: 1, name: 'actor_did', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
7377
|
+
{ no: 2, name: 'priority', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
|
|
7337
7378
|
])
|
|
7338
7379
|
|
|
7339
7380
|
static fromBinary(
|
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
import {
|
|
7
7
|
AddMuteOperationRequest,
|
|
8
8
|
AddMuteOperationResponse,
|
|
9
|
+
AddNotifOperationRequest,
|
|
10
|
+
AddNotifOperationResponse,
|
|
9
11
|
PingRequest,
|
|
10
12
|
PingResponse,
|
|
11
13
|
ScanMuteOperationsRequest,
|
|
12
14
|
ScanMuteOperationsResponse,
|
|
15
|
+
ScanNotifOperationsRequest,
|
|
16
|
+
ScanNotifOperationsResponse,
|
|
13
17
|
} from './bsync_pb'
|
|
14
18
|
import { MethodKind } from '@bufbuild/protobuf'
|
|
15
19
|
|
|
@@ -39,6 +43,24 @@ export const Service = {
|
|
|
39
43
|
O: ScanMuteOperationsResponse,
|
|
40
44
|
kind: MethodKind.Unary,
|
|
41
45
|
},
|
|
46
|
+
/**
|
|
47
|
+
* @generated from rpc bsync.Service.AddNotifOperation
|
|
48
|
+
*/
|
|
49
|
+
addNotifOperation: {
|
|
50
|
+
name: 'AddNotifOperation',
|
|
51
|
+
I: AddNotifOperationRequest,
|
|
52
|
+
O: AddNotifOperationResponse,
|
|
53
|
+
kind: MethodKind.Unary,
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* @generated from rpc bsync.Service.ScanNotifOperations
|
|
57
|
+
*/
|
|
58
|
+
scanNotifOperations: {
|
|
59
|
+
name: 'ScanNotifOperations',
|
|
60
|
+
I: ScanNotifOperationsRequest,
|
|
61
|
+
O: ScanNotifOperationsResponse,
|
|
62
|
+
kind: MethodKind.Unary,
|
|
63
|
+
},
|
|
42
64
|
/**
|
|
43
65
|
* Ping
|
|
44
66
|
*
|