@atproto/bsky 0.0.25 → 0.0.26
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 +7 -0
- package/buf.gen.yaml +12 -0
- package/dist/api/app/bsky/unspecced/getTaggedSuggestions.d.ts +3 -0
- package/dist/bsync.d.ts +8 -0
- package/dist/config.d.ts +20 -0
- package/dist/context.d.ts +6 -3
- package/dist/courier.d.ts +8 -0
- package/dist/db/database-schema.d.ts +2 -1
- package/dist/db/index.js +15 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20240124T023719200Z-tagged-suggestions.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/tables/tagged-suggestion.d.ts +9 -0
- package/dist/index.js +47930 -16807
- package/dist/index.js.map +3 -3
- package/dist/indexer/config.d.ts +8 -0
- package/dist/indexer/context.d.ts +3 -0
- package/dist/ingester/config.d.ts +8 -0
- package/dist/ingester/context.d.ts +3 -0
- package/dist/ingester/mute-subscription.d.ts +22 -0
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/lexicons.d.ts +48 -0
- package/dist/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.d.ts +39 -0
- package/dist/notifications.d.ts +27 -16
- package/dist/proto/bsync_connect.d.ts +25 -0
- package/dist/proto/bsync_pb.d.ts +90 -0
- package/dist/proto/courier_connect.d.ts +25 -0
- package/dist/proto/courier_pb.d.ts +91 -0
- package/dist/services/actor/index.d.ts +2 -2
- package/dist/services/indexing/index.d.ts +2 -2
- package/dist/services/util/post.d.ts +6 -6
- package/dist/util/retry.d.ts +2 -0
- package/package.json +15 -7
- package/proto/courier.proto +56 -0
- package/src/api/app/bsky/graph/muteActor.ts +32 -5
- package/src/api/app/bsky/graph/muteActorList.ts +32 -5
- package/src/api/app/bsky/graph/unmuteActor.ts +32 -5
- package/src/api/app/bsky/graph/unmuteActorList.ts +32 -5
- package/src/api/app/bsky/notification/registerPush.ts +42 -8
- package/src/api/app/bsky/unspecced/getTaggedSuggestions.ts +21 -0
- package/src/api/index.ts +2 -0
- package/src/bsync.ts +41 -0
- package/src/config.ts +79 -0
- package/src/context.ts +12 -6
- package/src/courier.ts +41 -0
- package/src/db/database-schema.ts +2 -0
- package/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts +15 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/tables/tagged-suggestion.ts +11 -0
- package/src/index.ts +26 -3
- package/src/indexer/config.ts +36 -0
- package/src/indexer/context.ts +6 -0
- package/src/indexer/index.ts +27 -3
- package/src/ingester/config.ts +34 -0
- package/src/ingester/context.ts +6 -0
- package/src/ingester/index.ts +18 -0
- package/src/ingester/mute-subscription.ts +213 -0
- package/src/lexicon/index.ts +12 -0
- package/src/lexicon/lexicons.ts +50 -0
- package/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts +65 -0
- package/src/notifications.ts +165 -149
- package/src/proto/bsync_connect.ts +54 -0
- package/src/proto/bsync_pb.ts +459 -0
- package/src/proto/courier_connect.ts +50 -0
- package/src/proto/courier_pb.ts +473 -0
- package/src/services/actor/index.ts +17 -2
- package/src/services/indexing/processor.ts +1 -1
- package/src/util/retry.ts +12 -0
- package/tests/notification-server.test.ts +59 -19
- package/tests/subscription/mutes.test.ts +170 -0
- package/tests/views/suggestions.test.ts +22 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
// @generated by protoc-gen-es v1.6.0 with parameter "target=ts,import_extension=.ts"
|
|
2
|
+
// @generated from file courier.proto (package courier, syntax proto3)
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
BinaryReadOptions,
|
|
8
|
+
FieldList,
|
|
9
|
+
JsonReadOptions,
|
|
10
|
+
JsonValue,
|
|
11
|
+
PartialMessage,
|
|
12
|
+
PlainMessage,
|
|
13
|
+
} from '@bufbuild/protobuf'
|
|
14
|
+
import { Message, proto3, Struct, Timestamp } from '@bufbuild/protobuf'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @generated from enum courier.AppPlatform
|
|
18
|
+
*/
|
|
19
|
+
export enum AppPlatform {
|
|
20
|
+
/**
|
|
21
|
+
* @generated from enum value: APP_PLATFORM_UNSPECIFIED = 0;
|
|
22
|
+
*/
|
|
23
|
+
UNSPECIFIED = 0,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @generated from enum value: APP_PLATFORM_IOS = 1;
|
|
27
|
+
*/
|
|
28
|
+
IOS = 1,
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @generated from enum value: APP_PLATFORM_ANDROID = 2;
|
|
32
|
+
*/
|
|
33
|
+
ANDROID = 2,
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @generated from enum value: APP_PLATFORM_WEB = 3;
|
|
37
|
+
*/
|
|
38
|
+
WEB = 3,
|
|
39
|
+
}
|
|
40
|
+
// Retrieve enum metadata with: proto3.getEnumType(AppPlatform)
|
|
41
|
+
proto3.util.setEnumType(AppPlatform, 'courier.AppPlatform', [
|
|
42
|
+
{ no: 0, name: 'APP_PLATFORM_UNSPECIFIED' },
|
|
43
|
+
{ no: 1, name: 'APP_PLATFORM_IOS' },
|
|
44
|
+
{ no: 2, name: 'APP_PLATFORM_ANDROID' },
|
|
45
|
+
{ no: 3, name: 'APP_PLATFORM_WEB' },
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Ping
|
|
50
|
+
*
|
|
51
|
+
* @generated from message courier.PingRequest
|
|
52
|
+
*/
|
|
53
|
+
export class PingRequest extends Message<PingRequest> {
|
|
54
|
+
constructor(data?: PartialMessage<PingRequest>) {
|
|
55
|
+
super()
|
|
56
|
+
proto3.util.initPartial(data, this)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static readonly runtime: typeof proto3 = proto3
|
|
60
|
+
static readonly typeName = 'courier.PingRequest'
|
|
61
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [])
|
|
62
|
+
|
|
63
|
+
static fromBinary(
|
|
64
|
+
bytes: Uint8Array,
|
|
65
|
+
options?: Partial<BinaryReadOptions>,
|
|
66
|
+
): PingRequest {
|
|
67
|
+
return new PingRequest().fromBinary(bytes, options)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static fromJson(
|
|
71
|
+
jsonValue: JsonValue,
|
|
72
|
+
options?: Partial<JsonReadOptions>,
|
|
73
|
+
): PingRequest {
|
|
74
|
+
return new PingRequest().fromJson(jsonValue, options)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static fromJsonString(
|
|
78
|
+
jsonString: string,
|
|
79
|
+
options?: Partial<JsonReadOptions>,
|
|
80
|
+
): PingRequest {
|
|
81
|
+
return new PingRequest().fromJsonString(jsonString, options)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static equals(
|
|
85
|
+
a: PingRequest | PlainMessage<PingRequest> | undefined,
|
|
86
|
+
b: PingRequest | PlainMessage<PingRequest> | undefined,
|
|
87
|
+
): boolean {
|
|
88
|
+
return proto3.util.equals(PingRequest, a, b)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @generated from message courier.PingResponse
|
|
94
|
+
*/
|
|
95
|
+
export class PingResponse extends Message<PingResponse> {
|
|
96
|
+
constructor(data?: PartialMessage<PingResponse>) {
|
|
97
|
+
super()
|
|
98
|
+
proto3.util.initPartial(data, this)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static readonly runtime: typeof proto3 = proto3
|
|
102
|
+
static readonly typeName = 'courier.PingResponse'
|
|
103
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [])
|
|
104
|
+
|
|
105
|
+
static fromBinary(
|
|
106
|
+
bytes: Uint8Array,
|
|
107
|
+
options?: Partial<BinaryReadOptions>,
|
|
108
|
+
): PingResponse {
|
|
109
|
+
return new PingResponse().fromBinary(bytes, options)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static fromJson(
|
|
113
|
+
jsonValue: JsonValue,
|
|
114
|
+
options?: Partial<JsonReadOptions>,
|
|
115
|
+
): PingResponse {
|
|
116
|
+
return new PingResponse().fromJson(jsonValue, options)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static fromJsonString(
|
|
120
|
+
jsonString: string,
|
|
121
|
+
options?: Partial<JsonReadOptions>,
|
|
122
|
+
): PingResponse {
|
|
123
|
+
return new PingResponse().fromJsonString(jsonString, options)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static equals(
|
|
127
|
+
a: PingResponse | PlainMessage<PingResponse> | undefined,
|
|
128
|
+
b: PingResponse | PlainMessage<PingResponse> | undefined,
|
|
129
|
+
): boolean {
|
|
130
|
+
return proto3.util.equals(PingResponse, a, b)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @generated from message courier.Notification
|
|
136
|
+
*/
|
|
137
|
+
export class Notification extends Message<Notification> {
|
|
138
|
+
/**
|
|
139
|
+
* @generated from field: string id = 1;
|
|
140
|
+
*/
|
|
141
|
+
id = ''
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @generated from field: string recipient_did = 2;
|
|
145
|
+
*/
|
|
146
|
+
recipientDid = ''
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @generated from field: string title = 3;
|
|
150
|
+
*/
|
|
151
|
+
title = ''
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @generated from field: string message = 4;
|
|
155
|
+
*/
|
|
156
|
+
message = ''
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @generated from field: string collapse_key = 5;
|
|
160
|
+
*/
|
|
161
|
+
collapseKey = ''
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @generated from field: bool always_deliver = 6;
|
|
165
|
+
*/
|
|
166
|
+
alwaysDeliver = false
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @generated from field: google.protobuf.Timestamp timestamp = 7;
|
|
170
|
+
*/
|
|
171
|
+
timestamp?: Timestamp
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @generated from field: google.protobuf.Struct additional = 8;
|
|
175
|
+
*/
|
|
176
|
+
additional?: Struct
|
|
177
|
+
|
|
178
|
+
constructor(data?: PartialMessage<Notification>) {
|
|
179
|
+
super()
|
|
180
|
+
proto3.util.initPartial(data, this)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static readonly runtime: typeof proto3 = proto3
|
|
184
|
+
static readonly typeName = 'courier.Notification'
|
|
185
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
186
|
+
{ no: 1, name: 'id', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
187
|
+
{
|
|
188
|
+
no: 2,
|
|
189
|
+
name: 'recipient_did',
|
|
190
|
+
kind: 'scalar',
|
|
191
|
+
T: 9 /* ScalarType.STRING */,
|
|
192
|
+
},
|
|
193
|
+
{ no: 3, name: 'title', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
194
|
+
{ no: 4, name: 'message', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
195
|
+
{
|
|
196
|
+
no: 5,
|
|
197
|
+
name: 'collapse_key',
|
|
198
|
+
kind: 'scalar',
|
|
199
|
+
T: 9 /* ScalarType.STRING */,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
no: 6,
|
|
203
|
+
name: 'always_deliver',
|
|
204
|
+
kind: 'scalar',
|
|
205
|
+
T: 8 /* ScalarType.BOOL */,
|
|
206
|
+
},
|
|
207
|
+
{ no: 7, name: 'timestamp', kind: 'message', T: Timestamp },
|
|
208
|
+
{ no: 8, name: 'additional', kind: 'message', T: Struct },
|
|
209
|
+
])
|
|
210
|
+
|
|
211
|
+
static fromBinary(
|
|
212
|
+
bytes: Uint8Array,
|
|
213
|
+
options?: Partial<BinaryReadOptions>,
|
|
214
|
+
): Notification {
|
|
215
|
+
return new Notification().fromBinary(bytes, options)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static fromJson(
|
|
219
|
+
jsonValue: JsonValue,
|
|
220
|
+
options?: Partial<JsonReadOptions>,
|
|
221
|
+
): Notification {
|
|
222
|
+
return new Notification().fromJson(jsonValue, options)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static fromJsonString(
|
|
226
|
+
jsonString: string,
|
|
227
|
+
options?: Partial<JsonReadOptions>,
|
|
228
|
+
): Notification {
|
|
229
|
+
return new Notification().fromJsonString(jsonString, options)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
static equals(
|
|
233
|
+
a: Notification | PlainMessage<Notification> | undefined,
|
|
234
|
+
b: Notification | PlainMessage<Notification> | undefined,
|
|
235
|
+
): boolean {
|
|
236
|
+
return proto3.util.equals(Notification, a, b)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* @generated from message courier.PushNotificationsRequest
|
|
242
|
+
*/
|
|
243
|
+
export class PushNotificationsRequest extends Message<PushNotificationsRequest> {
|
|
244
|
+
/**
|
|
245
|
+
* @generated from field: repeated courier.Notification notifications = 1;
|
|
246
|
+
*/
|
|
247
|
+
notifications: Notification[] = []
|
|
248
|
+
|
|
249
|
+
constructor(data?: PartialMessage<PushNotificationsRequest>) {
|
|
250
|
+
super()
|
|
251
|
+
proto3.util.initPartial(data, this)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static readonly runtime: typeof proto3 = proto3
|
|
255
|
+
static readonly typeName = 'courier.PushNotificationsRequest'
|
|
256
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
257
|
+
{
|
|
258
|
+
no: 1,
|
|
259
|
+
name: 'notifications',
|
|
260
|
+
kind: 'message',
|
|
261
|
+
T: Notification,
|
|
262
|
+
repeated: true,
|
|
263
|
+
},
|
|
264
|
+
])
|
|
265
|
+
|
|
266
|
+
static fromBinary(
|
|
267
|
+
bytes: Uint8Array,
|
|
268
|
+
options?: Partial<BinaryReadOptions>,
|
|
269
|
+
): PushNotificationsRequest {
|
|
270
|
+
return new PushNotificationsRequest().fromBinary(bytes, options)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
static fromJson(
|
|
274
|
+
jsonValue: JsonValue,
|
|
275
|
+
options?: Partial<JsonReadOptions>,
|
|
276
|
+
): PushNotificationsRequest {
|
|
277
|
+
return new PushNotificationsRequest().fromJson(jsonValue, options)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static fromJsonString(
|
|
281
|
+
jsonString: string,
|
|
282
|
+
options?: Partial<JsonReadOptions>,
|
|
283
|
+
): PushNotificationsRequest {
|
|
284
|
+
return new PushNotificationsRequest().fromJsonString(jsonString, options)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
static equals(
|
|
288
|
+
a:
|
|
289
|
+
| PushNotificationsRequest
|
|
290
|
+
| PlainMessage<PushNotificationsRequest>
|
|
291
|
+
| undefined,
|
|
292
|
+
b:
|
|
293
|
+
| PushNotificationsRequest
|
|
294
|
+
| PlainMessage<PushNotificationsRequest>
|
|
295
|
+
| undefined,
|
|
296
|
+
): boolean {
|
|
297
|
+
return proto3.util.equals(PushNotificationsRequest, a, b)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @generated from message courier.PushNotificationsResponse
|
|
303
|
+
*/
|
|
304
|
+
export class PushNotificationsResponse extends Message<PushNotificationsResponse> {
|
|
305
|
+
constructor(data?: PartialMessage<PushNotificationsResponse>) {
|
|
306
|
+
super()
|
|
307
|
+
proto3.util.initPartial(data, this)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static readonly runtime: typeof proto3 = proto3
|
|
311
|
+
static readonly typeName = 'courier.PushNotificationsResponse'
|
|
312
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [])
|
|
313
|
+
|
|
314
|
+
static fromBinary(
|
|
315
|
+
bytes: Uint8Array,
|
|
316
|
+
options?: Partial<BinaryReadOptions>,
|
|
317
|
+
): PushNotificationsResponse {
|
|
318
|
+
return new PushNotificationsResponse().fromBinary(bytes, options)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static fromJson(
|
|
322
|
+
jsonValue: JsonValue,
|
|
323
|
+
options?: Partial<JsonReadOptions>,
|
|
324
|
+
): PushNotificationsResponse {
|
|
325
|
+
return new PushNotificationsResponse().fromJson(jsonValue, options)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static fromJsonString(
|
|
329
|
+
jsonString: string,
|
|
330
|
+
options?: Partial<JsonReadOptions>,
|
|
331
|
+
): PushNotificationsResponse {
|
|
332
|
+
return new PushNotificationsResponse().fromJsonString(jsonString, options)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
static equals(
|
|
336
|
+
a:
|
|
337
|
+
| PushNotificationsResponse
|
|
338
|
+
| PlainMessage<PushNotificationsResponse>
|
|
339
|
+
| undefined,
|
|
340
|
+
b:
|
|
341
|
+
| PushNotificationsResponse
|
|
342
|
+
| PlainMessage<PushNotificationsResponse>
|
|
343
|
+
| undefined,
|
|
344
|
+
): boolean {
|
|
345
|
+
return proto3.util.equals(PushNotificationsResponse, a, b)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* @generated from message courier.RegisterDeviceTokenRequest
|
|
351
|
+
*/
|
|
352
|
+
export class RegisterDeviceTokenRequest extends Message<RegisterDeviceTokenRequest> {
|
|
353
|
+
/**
|
|
354
|
+
* @generated from field: string did = 1;
|
|
355
|
+
*/
|
|
356
|
+
did = ''
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @generated from field: string token = 2;
|
|
360
|
+
*/
|
|
361
|
+
token = ''
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* @generated from field: string app_id = 3;
|
|
365
|
+
*/
|
|
366
|
+
appId = ''
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @generated from field: courier.AppPlatform platform = 4;
|
|
370
|
+
*/
|
|
371
|
+
platform = AppPlatform.UNSPECIFIED
|
|
372
|
+
|
|
373
|
+
constructor(data?: PartialMessage<RegisterDeviceTokenRequest>) {
|
|
374
|
+
super()
|
|
375
|
+
proto3.util.initPartial(data, this)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
static readonly runtime: typeof proto3 = proto3
|
|
379
|
+
static readonly typeName = 'courier.RegisterDeviceTokenRequest'
|
|
380
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
381
|
+
{ no: 1, name: 'did', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
382
|
+
{ no: 2, name: 'token', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
383
|
+
{ no: 3, name: 'app_id', kind: 'scalar', T: 9 /* ScalarType.STRING */ },
|
|
384
|
+
{
|
|
385
|
+
no: 4,
|
|
386
|
+
name: 'platform',
|
|
387
|
+
kind: 'enum',
|
|
388
|
+
T: proto3.getEnumType(AppPlatform),
|
|
389
|
+
},
|
|
390
|
+
])
|
|
391
|
+
|
|
392
|
+
static fromBinary(
|
|
393
|
+
bytes: Uint8Array,
|
|
394
|
+
options?: Partial<BinaryReadOptions>,
|
|
395
|
+
): RegisterDeviceTokenRequest {
|
|
396
|
+
return new RegisterDeviceTokenRequest().fromBinary(bytes, options)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
static fromJson(
|
|
400
|
+
jsonValue: JsonValue,
|
|
401
|
+
options?: Partial<JsonReadOptions>,
|
|
402
|
+
): RegisterDeviceTokenRequest {
|
|
403
|
+
return new RegisterDeviceTokenRequest().fromJson(jsonValue, options)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static fromJsonString(
|
|
407
|
+
jsonString: string,
|
|
408
|
+
options?: Partial<JsonReadOptions>,
|
|
409
|
+
): RegisterDeviceTokenRequest {
|
|
410
|
+
return new RegisterDeviceTokenRequest().fromJsonString(jsonString, options)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
static equals(
|
|
414
|
+
a:
|
|
415
|
+
| RegisterDeviceTokenRequest
|
|
416
|
+
| PlainMessage<RegisterDeviceTokenRequest>
|
|
417
|
+
| undefined,
|
|
418
|
+
b:
|
|
419
|
+
| RegisterDeviceTokenRequest
|
|
420
|
+
| PlainMessage<RegisterDeviceTokenRequest>
|
|
421
|
+
| undefined,
|
|
422
|
+
): boolean {
|
|
423
|
+
return proto3.util.equals(RegisterDeviceTokenRequest, a, b)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* @generated from message courier.RegisterDeviceTokenResponse
|
|
429
|
+
*/
|
|
430
|
+
export class RegisterDeviceTokenResponse extends Message<RegisterDeviceTokenResponse> {
|
|
431
|
+
constructor(data?: PartialMessage<RegisterDeviceTokenResponse>) {
|
|
432
|
+
super()
|
|
433
|
+
proto3.util.initPartial(data, this)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
static readonly runtime: typeof proto3 = proto3
|
|
437
|
+
static readonly typeName = 'courier.RegisterDeviceTokenResponse'
|
|
438
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [])
|
|
439
|
+
|
|
440
|
+
static fromBinary(
|
|
441
|
+
bytes: Uint8Array,
|
|
442
|
+
options?: Partial<BinaryReadOptions>,
|
|
443
|
+
): RegisterDeviceTokenResponse {
|
|
444
|
+
return new RegisterDeviceTokenResponse().fromBinary(bytes, options)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
static fromJson(
|
|
448
|
+
jsonValue: JsonValue,
|
|
449
|
+
options?: Partial<JsonReadOptions>,
|
|
450
|
+
): RegisterDeviceTokenResponse {
|
|
451
|
+
return new RegisterDeviceTokenResponse().fromJson(jsonValue, options)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
static fromJsonString(
|
|
455
|
+
jsonString: string,
|
|
456
|
+
options?: Partial<JsonReadOptions>,
|
|
457
|
+
): RegisterDeviceTokenResponse {
|
|
458
|
+
return new RegisterDeviceTokenResponse().fromJsonString(jsonString, options)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
static equals(
|
|
462
|
+
a:
|
|
463
|
+
| RegisterDeviceTokenResponse
|
|
464
|
+
| PlainMessage<RegisterDeviceTokenResponse>
|
|
465
|
+
| undefined,
|
|
466
|
+
b:
|
|
467
|
+
| RegisterDeviceTokenResponse
|
|
468
|
+
| PlainMessage<RegisterDeviceTokenResponse>
|
|
469
|
+
| undefined,
|
|
470
|
+
): boolean {
|
|
471
|
+
return proto3.util.equals(RegisterDeviceTokenResponse, a, b)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
@@ -12,6 +12,7 @@ import { GraphService } from '../graph'
|
|
|
12
12
|
import { LabelService } from '../label'
|
|
13
13
|
import { AtUri } from '@atproto/syntax'
|
|
14
14
|
import { ids } from '../../lexicon/lexicons'
|
|
15
|
+
import { Platform } from '../../notifications'
|
|
15
16
|
|
|
16
17
|
export * from './types'
|
|
17
18
|
|
|
@@ -21,8 +22,8 @@ export class ActorService {
|
|
|
21
22
|
constructor(
|
|
22
23
|
public db: Database,
|
|
23
24
|
public imgUriBuilder: ImageUriBuilder,
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
graph: FromDb<GraphService>,
|
|
26
|
+
label: FromDb<LabelService>,
|
|
26
27
|
) {
|
|
27
28
|
this.views = new ActorViews(this.db, this.imgUriBuilder, graph, label)
|
|
28
29
|
}
|
|
@@ -214,6 +215,20 @@ export class ActorService {
|
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
}
|
|
218
|
+
|
|
219
|
+
async registerPushDeviceToken(
|
|
220
|
+
did: string,
|
|
221
|
+
token: string,
|
|
222
|
+
platform: Platform,
|
|
223
|
+
appId: string,
|
|
224
|
+
) {
|
|
225
|
+
await this.db
|
|
226
|
+
.asPrimary()
|
|
227
|
+
.db.insertInto('notification_push_token')
|
|
228
|
+
.values({ did, token, platform, appId })
|
|
229
|
+
.onConflict((oc) => oc.doNothing())
|
|
230
|
+
.execute()
|
|
231
|
+
}
|
|
217
232
|
}
|
|
218
233
|
|
|
219
234
|
type ActorResult = Actor
|
|
@@ -257,7 +257,7 @@ export class RecordProcessor<T, S> {
|
|
|
257
257
|
const notifServer = this.notifServer
|
|
258
258
|
sendOnCommit.push(async () => {
|
|
259
259
|
try {
|
|
260
|
-
const preparedNotifs = await notifServer.
|
|
260
|
+
const preparedNotifs = await notifServer.prepareNotifications(chunk)
|
|
261
261
|
await notifServer.processNotifications(preparedNotifs)
|
|
262
262
|
} catch (error) {
|
|
263
263
|
dbLogger.error({ error }, 'error sending push notifications')
|
package/src/util/retry.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AxiosError } from 'axios'
|
|
2
2
|
import { XRPCError, ResponseType } from '@atproto/xrpc'
|
|
3
3
|
import { RetryOptions, retry } from '@atproto/common'
|
|
4
|
+
import { Code, ConnectError } from '@connectrpc/connect'
|
|
4
5
|
|
|
5
6
|
export async function retryHttp<T>(
|
|
6
7
|
fn: () => Promise<T>,
|
|
@@ -24,3 +25,14 @@ export function retryableHttp(err: unknown) {
|
|
|
24
25
|
const retryableHttpStatusCodes = new Set([
|
|
25
26
|
408, 425, 429, 500, 502, 503, 504, 522, 524,
|
|
26
27
|
])
|
|
28
|
+
|
|
29
|
+
export async function retryConnect<T>(
|
|
30
|
+
fn: () => Promise<T>,
|
|
31
|
+
opts: RetryOptions = {},
|
|
32
|
+
): Promise<T> {
|
|
33
|
+
return retry(fn, { retryable: retryableConnect, ...opts })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function retryableConnect(err: unknown) {
|
|
37
|
+
return err instanceof ConnectError && err.code === Code.Unavailable
|
|
38
|
+
}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import AtpAgent, { AtUri } from '@atproto/api'
|
|
2
2
|
import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CourierNotificationServer,
|
|
5
|
+
GorushNotificationServer,
|
|
6
|
+
} from '../src/notifications'
|
|
4
7
|
import { Database } from '../src'
|
|
8
|
+
import { createCourierClient } from '../src/courier'
|
|
5
9
|
|
|
6
10
|
describe('notification server', () => {
|
|
7
11
|
let network: TestNetwork
|
|
8
12
|
let agent: AtpAgent
|
|
9
13
|
let pdsAgent: AtpAgent
|
|
10
14
|
let sc: SeedClient
|
|
11
|
-
let notifServer:
|
|
15
|
+
let notifServer: GorushNotificationServer
|
|
12
16
|
|
|
13
17
|
// account dids, for convenience
|
|
14
18
|
let alice: string
|
|
@@ -24,14 +28,17 @@ describe('notification server', () => {
|
|
|
24
28
|
await network.processAll()
|
|
25
29
|
await network.bsky.processAll()
|
|
26
30
|
alice = sc.dids.alice
|
|
27
|
-
notifServer =
|
|
31
|
+
notifServer = new GorushNotificationServer(
|
|
32
|
+
network.bsky.ctx.db.getPrimary(),
|
|
33
|
+
'http://mock',
|
|
34
|
+
)
|
|
28
35
|
})
|
|
29
36
|
|
|
30
37
|
afterAll(async () => {
|
|
31
38
|
await network.close()
|
|
32
39
|
})
|
|
33
40
|
|
|
34
|
-
describe('
|
|
41
|
+
describe('registerPush', () => {
|
|
35
42
|
it('registers push notification token and device.', async () => {
|
|
36
43
|
const res = await agent.api.app.bsky.notification.registerPush(
|
|
37
44
|
{
|
|
@@ -95,19 +102,14 @@ describe('notification server', () => {
|
|
|
95
102
|
})
|
|
96
103
|
|
|
97
104
|
describe('NotificationServer', () => {
|
|
98
|
-
it('gets user tokens from db', async () => {
|
|
99
|
-
const tokens = await notifServer.getTokensByDid([alice])
|
|
100
|
-
expect(tokens[alice][0].token).toEqual('123')
|
|
101
|
-
})
|
|
102
|
-
|
|
103
105
|
it('gets notification display attributes: title and body', async () => {
|
|
104
106
|
const db = network.bsky.ctx.db.getPrimary()
|
|
105
107
|
const notif = await getLikeNotification(db, alice)
|
|
106
108
|
if (!notif) throw new Error('no notification found')
|
|
107
|
-
const
|
|
108
|
-
if (!
|
|
109
|
+
const views = await notifServer.getNotificationViews([notif])
|
|
110
|
+
if (!views.length)
|
|
109
111
|
throw new Error('no notification display attributes found')
|
|
110
|
-
expect(
|
|
112
|
+
expect(views[0].title).toEqual('bobby liked your post')
|
|
111
113
|
})
|
|
112
114
|
|
|
113
115
|
it('filters notifications that violate blocks', async () => {
|
|
@@ -126,11 +128,11 @@ describe('notification server', () => {
|
|
|
126
128
|
did: notif.author,
|
|
127
129
|
author: notif.did,
|
|
128
130
|
}
|
|
129
|
-
const
|
|
131
|
+
const views = await notifServer.getNotificationViews([
|
|
130
132
|
notif,
|
|
131
133
|
flippedNotif,
|
|
132
134
|
])
|
|
133
|
-
expect(
|
|
135
|
+
expect(views.length).toBe(0)
|
|
134
136
|
const uri = new AtUri(blockRef.uri)
|
|
135
137
|
await pdsAgent.api.app.bsky.graph.block.delete(
|
|
136
138
|
{ repo: alice, rkey: uri.rkey },
|
|
@@ -147,8 +149,8 @@ describe('notification server', () => {
|
|
|
147
149
|
{ actor: notif.author },
|
|
148
150
|
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
|
149
151
|
)
|
|
150
|
-
const
|
|
151
|
-
expect(
|
|
152
|
+
const views = await notifServer.getNotificationViews([notif])
|
|
153
|
+
expect(views.length).toBe(0)
|
|
152
154
|
await pdsAgent.api.app.bsky.graph.unmuteActor(
|
|
153
155
|
{ actor: notif.author },
|
|
154
156
|
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
|
@@ -182,13 +184,20 @@ describe('notification server', () => {
|
|
|
182
184
|
{ list: listRef.uri },
|
|
183
185
|
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
|
184
186
|
)
|
|
185
|
-
const
|
|
186
|
-
expect(
|
|
187
|
+
const views = await notifServer.getNotificationViews([notif])
|
|
188
|
+
expect(views.length).toBe(0)
|
|
187
189
|
await pdsAgent.api.app.bsky.graph.unmuteActorList(
|
|
188
190
|
{ list: listRef.uri },
|
|
189
191
|
{ headers: sc.getHeaders(alice), encoding: 'application/json' },
|
|
190
192
|
)
|
|
191
193
|
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe('GorushNotificationServer', () => {
|
|
197
|
+
it('gets user tokens from db', async () => {
|
|
198
|
+
const tokens = await notifServer.getTokensByDid([alice])
|
|
199
|
+
expect(tokens[alice][0].token).toEqual('123')
|
|
200
|
+
})
|
|
192
201
|
|
|
193
202
|
it('prepares notification to be sent', async () => {
|
|
194
203
|
const db = network.bsky.ctx.db.getPrimary()
|
|
@@ -198,7 +207,7 @@ describe('notification server', () => {
|
|
|
198
207
|
notif,
|
|
199
208
|
notif /* second one will get dropped by rate limit */,
|
|
200
209
|
]
|
|
201
|
-
const prepared = await notifServer.
|
|
210
|
+
const prepared = await notifServer.prepareNotifications(notifAsArray)
|
|
202
211
|
expect(prepared).toEqual([
|
|
203
212
|
{
|
|
204
213
|
collapse_id: 'like',
|
|
@@ -218,6 +227,37 @@ describe('notification server', () => {
|
|
|
218
227
|
})
|
|
219
228
|
})
|
|
220
229
|
|
|
230
|
+
describe('CourierNotificationServer', () => {
|
|
231
|
+
it('prepares notification to be sent', async () => {
|
|
232
|
+
const db = network.bsky.ctx.db.getPrimary()
|
|
233
|
+
const notif = await getLikeNotification(db, alice)
|
|
234
|
+
if (!notif) throw new Error('no notification found')
|
|
235
|
+
const courierNotifServer = new CourierNotificationServer(
|
|
236
|
+
db,
|
|
237
|
+
createCourierClient({ baseUrl: 'http://mock', httpVersion: '2' }),
|
|
238
|
+
)
|
|
239
|
+
const prepared = await courierNotifServer.prepareNotifications([notif])
|
|
240
|
+
expect(prepared[0]?.id).toBeTruthy()
|
|
241
|
+
expect(prepared.map((p) => p.toJson())).toEqual([
|
|
242
|
+
{
|
|
243
|
+
id: prepared[0].id, // already ensured it exists
|
|
244
|
+
recipientDid: notif.did,
|
|
245
|
+
title: 'bobby liked your post',
|
|
246
|
+
message: 'again',
|
|
247
|
+
collapseKey: 'like',
|
|
248
|
+
timestamp: notif.sortAt,
|
|
249
|
+
// this is missing, appears to be a quirk of toJson()
|
|
250
|
+
// alwaysDeliver: false,
|
|
251
|
+
additional: {
|
|
252
|
+
reason: notif.reason,
|
|
253
|
+
uri: notif.recordUri,
|
|
254
|
+
subject: notif.reasonSubject,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
])
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
|
|
221
261
|
async function getLikeNotification(db: Database, did: string) {
|
|
222
262
|
return await db.db
|
|
223
263
|
.selectFrom('notification')
|