@bsv/message-box-client 1.2.3 → 1.2.5
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/dist/cjs/package.json +1 -1
- package/dist/cjs/src/MessageBoxClient.js +175 -43
- package/dist/cjs/src/MessageBoxClient.js.map +1 -1
- package/dist/cjs/src/PeerPayClient.js +8 -5
- package/dist/cjs/src/PeerPayClient.js.map +1 -1
- package/dist/cjs/src/__tests/MessageBoxClient.test.js +0 -15
- package/dist/cjs/src/__tests/MessageBoxClient.test.js.map +1 -1
- package/dist/cjs/src/__tests/PeerPayClientUnit.test.js +2 -2
- package/dist/cjs/src/__tests/PeerPayClientUnit.test.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/MessageBoxClient.js +174 -43
- package/dist/esm/src/MessageBoxClient.js.map +1 -1
- package/dist/esm/src/PeerPayClient.js +8 -5
- package/dist/esm/src/PeerPayClient.js.map +1 -1
- package/dist/esm/src/__tests/MessageBoxClient.test.js +0 -15
- package/dist/esm/src/__tests/MessageBoxClient.test.js.map +1 -1
- package/dist/esm/src/__tests/PeerPayClientUnit.test.js +2 -2
- package/dist/esm/src/__tests/PeerPayClientUnit.test.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/MessageBoxClient.d.ts +101 -11
- package/dist/types/src/MessageBoxClient.d.ts.map +1 -1
- package/dist/types/src/PeerPayClient.d.ts +6 -3
- package/dist/types/src/PeerPayClient.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/package.json +1 -1
- package/src/MessageBoxClient.ts +235 -77
- package/src/PeerPayClient.ts +21 -14
- package/src/__tests/MessageBoxClient.test.ts +0 -23
- package/src/__tests/PeerPayClientUnit.test.ts +2 -2
package/package.json
CHANGED
package/src/MessageBoxClient.ts
CHANGED
|
@@ -116,7 +116,7 @@ export class MessageBoxClient {
|
|
|
116
116
|
* })
|
|
117
117
|
* await client.init()
|
|
118
118
|
*/
|
|
119
|
-
constructor(options: MessageBoxClientOptions = {}) {
|
|
119
|
+
constructor (options: MessageBoxClientOptions = {}) {
|
|
120
120
|
const {
|
|
121
121
|
host,
|
|
122
122
|
walletClient,
|
|
@@ -168,7 +168,7 @@ export class MessageBoxClient {
|
|
|
168
168
|
* await client.init()
|
|
169
169
|
* await client.sendMessage({ recipient, messageBox: 'inbox', body: 'Hello' })
|
|
170
170
|
*/
|
|
171
|
-
async init(targetHost: string = this.host, originator?: string): Promise<void> {
|
|
171
|
+
async init (targetHost: string = this.host, originator?: string): Promise<void> {
|
|
172
172
|
const normalizedHost = targetHost?.trim()
|
|
173
173
|
if (normalizedHost === '') {
|
|
174
174
|
throw new Error('Cannot anoint host: No valid host provided')
|
|
@@ -189,9 +189,14 @@ export class MessageBoxClient {
|
|
|
189
189
|
// 3. If none our found, anoint this host
|
|
190
190
|
if (firstAdvertisement == null || firstAdvertisement?.host?.trim() === '' || firstAdvertisement?.host !== normalizedHost) {
|
|
191
191
|
Logger.log('[MB CLIENT] Anointing host:', normalizedHost)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
try {
|
|
193
|
+
const { txid } = await this.anointHost(normalizedHost, originator)
|
|
194
|
+
if (txid == null || txid.trim() === '') {
|
|
195
|
+
throw new Error('Failed to anoint host: No transaction ID returned')
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
Logger.log('[MB CLIENT] Failed to anoint host, continuing with default functionality:', error)
|
|
199
|
+
// Continue with default host - client can still function for basic operations
|
|
195
200
|
}
|
|
196
201
|
}
|
|
197
202
|
this.initialized = true
|
|
@@ -208,7 +213,7 @@ export class MessageBoxClient {
|
|
|
208
213
|
*
|
|
209
214
|
* Used automatically by all public methods that require initialization.
|
|
210
215
|
*/
|
|
211
|
-
private async assertInitialized(): Promise<void> {
|
|
216
|
+
private async assertInitialized (): Promise<void> {
|
|
212
217
|
if (!this.initialized || this.host == null || this.host.trim() === '') {
|
|
213
218
|
await this.init()
|
|
214
219
|
}
|
|
@@ -221,7 +226,7 @@ export class MessageBoxClient {
|
|
|
221
226
|
* Returns a live list of WebSocket rooms the client is subscribed to.
|
|
222
227
|
* Useful for inspecting state or ensuring no duplicates are joined.
|
|
223
228
|
*/
|
|
224
|
-
public getJoinedRooms(): Set<string> {
|
|
229
|
+
public getJoinedRooms (): Set<string> {
|
|
225
230
|
return this.joinedRooms
|
|
226
231
|
}
|
|
227
232
|
|
|
@@ -233,7 +238,7 @@ export class MessageBoxClient {
|
|
|
233
238
|
* Returns the client's identity key, used for signing, encryption, and addressing.
|
|
234
239
|
* If not already loaded, it will fetch and cache it.
|
|
235
240
|
*/
|
|
236
|
-
public async getIdentityKey(originator?: string): Promise<string> {
|
|
241
|
+
public async getIdentityKey (originator?: string): Promise<string> {
|
|
237
242
|
if (this.myIdentityKey != null && this.myIdentityKey.trim() !== '') {
|
|
238
243
|
return this.myIdentityKey
|
|
239
244
|
}
|
|
@@ -261,7 +266,7 @@ export class MessageBoxClient {
|
|
|
261
266
|
* Note: Do not interact with the socket directly unless necessary.
|
|
262
267
|
* Use the provided `sendLiveMessage`, `listenForLiveMessages`, and related methods.
|
|
263
268
|
*/
|
|
264
|
-
public get testSocket(): ReturnType<typeof AuthSocketClient> | undefined {
|
|
269
|
+
public get testSocket (): ReturnType<typeof AuthSocketClient> | undefined {
|
|
265
270
|
return this.socket
|
|
266
271
|
}
|
|
267
272
|
|
|
@@ -289,8 +294,7 @@ export class MessageBoxClient {
|
|
|
289
294
|
* await mb.initializeConnection()
|
|
290
295
|
* // WebSocket is now ready for use
|
|
291
296
|
*/
|
|
292
|
-
async initializeConnection(originator?: string): Promise<void> {
|
|
293
|
-
await this.assertInitialized()
|
|
297
|
+
async initializeConnection (originator?: string, overrideHost?: string): Promise<void> {
|
|
294
298
|
Logger.log('[MB CLIENT] initializeConnection() STARTED')
|
|
295
299
|
|
|
296
300
|
if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
|
|
@@ -305,10 +309,11 @@ export class MessageBoxClient {
|
|
|
305
309
|
Logger.log('[MB CLIENT] Setting up WebSocket connection...')
|
|
306
310
|
|
|
307
311
|
if (this.socket == null) {
|
|
308
|
-
|
|
309
|
-
|
|
312
|
+
const targetHost = overrideHost ?? this.host
|
|
313
|
+
if (typeof targetHost !== 'string' || targetHost.trim() === '') {
|
|
314
|
+
throw new Error('Cannot initialize WebSocket: No valid host provided')
|
|
310
315
|
}
|
|
311
|
-
this.socket = AuthSocketClient(
|
|
316
|
+
this.socket = AuthSocketClient(targetHost, { wallet: this.walletClient })
|
|
312
317
|
|
|
313
318
|
let identitySent = false
|
|
314
319
|
let authenticated = false
|
|
@@ -383,7 +388,7 @@ export class MessageBoxClient {
|
|
|
383
388
|
* @example
|
|
384
389
|
* const host = await resolveHostForRecipient('028d...') // → returns either overlay host or this.host
|
|
385
390
|
*/
|
|
386
|
-
async resolveHostForRecipient(identityKey: string, originator?: string): Promise<string> {
|
|
391
|
+
async resolveHostForRecipient (identityKey: string, originator?: string): Promise<string> {
|
|
387
392
|
const advertisementTokens = await this.queryAdvertisements(identityKey, undefined, originator)
|
|
388
393
|
if (advertisementTokens.length === 0) {
|
|
389
394
|
Logger.warn(`[MB CLIENT] No advertisements for ${identityKey}, using default host ${this.host}`)
|
|
@@ -401,7 +406,7 @@ export class MessageBoxClient {
|
|
|
401
406
|
* @param host? if passed, only look for adverts anointed at that host
|
|
402
407
|
* @returns 0-length array if nothing valid was found
|
|
403
408
|
*/
|
|
404
|
-
async queryAdvertisements(
|
|
409
|
+
async queryAdvertisements (
|
|
405
410
|
identityKey?: string,
|
|
406
411
|
host?: string,
|
|
407
412
|
originator?: string
|
|
@@ -466,14 +471,13 @@ export class MessageBoxClient {
|
|
|
466
471
|
* await client.joinRoom('payment_inbox')
|
|
467
472
|
* // Now listening for real-time messages in room '028d...-payment_inbox'
|
|
468
473
|
*/
|
|
469
|
-
async joinRoom(messageBox: string): Promise<void> {
|
|
470
|
-
await this.assertInitialized()
|
|
474
|
+
async joinRoom (messageBox: string, originator?: string, overrideHost?: string): Promise<void> {
|
|
471
475
|
Logger.log(`[MB CLIENT] Attempting to join WebSocket room: ${messageBox}`)
|
|
472
476
|
|
|
473
477
|
// Ensure WebSocket connection is established first
|
|
474
478
|
if (this.socket == null) {
|
|
475
479
|
Logger.log('[MB CLIENT] No WebSocket connection. Initializing...')
|
|
476
|
-
await this.initializeConnection()
|
|
480
|
+
await this.initializeConnection(originator, overrideHost)
|
|
477
481
|
}
|
|
478
482
|
|
|
479
483
|
if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
|
|
@@ -525,20 +529,27 @@ export class MessageBoxClient {
|
|
|
525
529
|
* onMessage: (msg) => console.log('Received live message:', msg)
|
|
526
530
|
* })
|
|
527
531
|
*/
|
|
528
|
-
async listenForLiveMessages({
|
|
532
|
+
async listenForLiveMessages ({
|
|
529
533
|
onMessage,
|
|
530
534
|
messageBox,
|
|
531
|
-
originator
|
|
535
|
+
originator,
|
|
536
|
+
overrideHost
|
|
532
537
|
}: {
|
|
533
538
|
onMessage: (message: PeerMessage) => void
|
|
534
539
|
messageBox: string
|
|
535
540
|
originator?: string
|
|
541
|
+
overrideHost?: string
|
|
536
542
|
}): Promise<void> {
|
|
537
|
-
await this.assertInitialized()
|
|
538
543
|
Logger.log(`[MB CLIENT] Setting up listener for WebSocket room: ${messageBox}`)
|
|
539
544
|
|
|
540
|
-
// Ensure WebSocket connection
|
|
541
|
-
|
|
545
|
+
// Ensure WebSocket connection is established first
|
|
546
|
+
if (this.socket == null) {
|
|
547
|
+
Logger.log('[MB CLIENT] No WebSocket connection. Initializing...')
|
|
548
|
+
await this.initializeConnection(originator, overrideHost)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Join the room
|
|
552
|
+
await this.joinRoom(messageBox, originator, overrideHost)
|
|
542
553
|
|
|
543
554
|
// Ensure identity key is available before creating roomId
|
|
544
555
|
if (this.myIdentityKey == null || this.myIdentityKey.trim() === '') {
|
|
@@ -623,7 +634,7 @@ export class MessageBoxClient {
|
|
|
623
634
|
* body: { amount: 1000 }
|
|
624
635
|
* })
|
|
625
636
|
*/
|
|
626
|
-
async sendLiveMessage({
|
|
637
|
+
async sendLiveMessage ({
|
|
627
638
|
recipient,
|
|
628
639
|
messageBox,
|
|
629
640
|
body,
|
|
@@ -781,7 +792,7 @@ export class MessageBoxClient {
|
|
|
781
792
|
* @example
|
|
782
793
|
* await client.leaveRoom('payment_inbox')
|
|
783
794
|
*/
|
|
784
|
-
async leaveRoom(messageBox: string): Promise<void> {
|
|
795
|
+
async leaveRoom (messageBox: string): Promise<void> {
|
|
785
796
|
await this.assertInitialized()
|
|
786
797
|
if (this.socket == null) {
|
|
787
798
|
Logger.warn('[MB CLIENT] Attempted to leave a room but WebSocket is not connected.')
|
|
@@ -813,7 +824,7 @@ export class MessageBoxClient {
|
|
|
813
824
|
* @example
|
|
814
825
|
* await client.disconnectWebSocket()
|
|
815
826
|
*/
|
|
816
|
-
async disconnectWebSocket(): Promise<void> {
|
|
827
|
+
async disconnectWebSocket (): Promise<void> {
|
|
817
828
|
await this.assertInitialized()
|
|
818
829
|
if (this.socket != null) {
|
|
819
830
|
Logger.log('[MB CLIENT] Closing WebSocket connection...')
|
|
@@ -851,7 +862,7 @@ export class MessageBoxClient {
|
|
|
851
862
|
* body: { type: 'ping' }
|
|
852
863
|
* })
|
|
853
864
|
*/
|
|
854
|
-
async sendMessage(
|
|
865
|
+
async sendMessage (
|
|
855
866
|
message: SendMessageParams,
|
|
856
867
|
overrideHost?: string,
|
|
857
868
|
originator?: string
|
|
@@ -1015,7 +1026,7 @@ export class MessageBoxClient {
|
|
|
1015
1026
|
* @example
|
|
1016
1027
|
* const { txid } = await client.anointHost('https://my-messagebox.io')
|
|
1017
1028
|
*/
|
|
1018
|
-
async anointHost(host: string, originator?: string): Promise<{ txid: string }> {
|
|
1029
|
+
async anointHost (host: string, originator?: string): Promise<{ txid: string }> {
|
|
1019
1030
|
Logger.log('[MB CLIENT] Starting anointHost...')
|
|
1020
1031
|
try {
|
|
1021
1032
|
if (!host.startsWith('http')) {
|
|
@@ -1097,7 +1108,7 @@ export class MessageBoxClient {
|
|
|
1097
1108
|
* @example
|
|
1098
1109
|
* const { txid } = await client.revokeHost('https://my-messagebox.io')
|
|
1099
1110
|
*/
|
|
1100
|
-
async revokeHostAdvertisement(advertisementToken: AdvertisementToken, originator?: string): Promise<{ txid: string }> {
|
|
1111
|
+
async revokeHostAdvertisement (advertisementToken: AdvertisementToken, originator?: string): Promise<{ txid: string }> {
|
|
1101
1112
|
Logger.log('[MB CLIENT] Starting revokeHost...')
|
|
1102
1113
|
const outpoint = `${advertisementToken.txid}.${advertisementToken.outputIndex}`
|
|
1103
1114
|
try {
|
|
@@ -1213,7 +1224,11 @@ export class MessageBoxClient {
|
|
|
1213
1224
|
|
|
1214
1225
|
let hosts: string[] = host != null ? [host] : []
|
|
1215
1226
|
if (hosts.length === 0) {
|
|
1216
|
-
const advertisedHosts = await this.queryAdvertisements(
|
|
1227
|
+
const advertisedHosts = await this.queryAdvertisements(
|
|
1228
|
+
await this.getIdentityKey(originator),
|
|
1229
|
+
undefined,
|
|
1230
|
+
originator
|
|
1231
|
+
)
|
|
1217
1232
|
hosts = Array.from(new Set([this.host, ...advertisedHosts.map(h => h.host)]))
|
|
1218
1233
|
}
|
|
1219
1234
|
|
|
@@ -1266,20 +1281,12 @@ export class MessageBoxClient {
|
|
|
1266
1281
|
// 6. Early‑out: no messages but at least one host succeeded → []
|
|
1267
1282
|
if (dedupMap.size === 0) return []
|
|
1268
1283
|
|
|
1269
|
-
const tryParse = (raw: string): any => {
|
|
1270
|
-
try {
|
|
1271
|
-
return JSON.parse(raw)
|
|
1272
|
-
} catch {
|
|
1273
|
-
return raw
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
1284
|
const messages: PeerMessage[] = Array.from(dedupMap.values())
|
|
1278
1285
|
|
|
1279
1286
|
for (const message of messages) {
|
|
1280
1287
|
try {
|
|
1281
1288
|
const parsedBody: unknown =
|
|
1282
|
-
typeof message.body === 'string' ? tryParse(message.body) : message.body
|
|
1289
|
+
typeof message.body === 'string' ? this.tryParse(message.body) : message.body
|
|
1283
1290
|
|
|
1284
1291
|
let messageContent: any = parsedBody
|
|
1285
1292
|
let paymentData: Payment | undefined
|
|
@@ -1292,7 +1299,7 @@ export class MessageBoxClient {
|
|
|
1292
1299
|
// Handle wrapped message format (with payment data)
|
|
1293
1300
|
const wrappedMessage = (parsedBody as any).message
|
|
1294
1301
|
messageContent = typeof wrappedMessage === 'string'
|
|
1295
|
-
? tryParse(wrappedMessage)
|
|
1302
|
+
? this.tryParse(wrappedMessage)
|
|
1296
1303
|
: wrappedMessage
|
|
1297
1304
|
paymentData = (parsedBody as any).payment
|
|
1298
1305
|
}
|
|
@@ -1348,7 +1355,7 @@ export class MessageBoxClient {
|
|
|
1348
1355
|
if (
|
|
1349
1356
|
messageContent != null &&
|
|
1350
1357
|
typeof messageContent === 'object' &&
|
|
1351
|
-
typeof (messageContent
|
|
1358
|
+
typeof (messageContent).encryptedMessage === 'string'
|
|
1352
1359
|
) {
|
|
1353
1360
|
Logger.log(
|
|
1354
1361
|
`[MB CLIENT] Decrypting message from ${String(message.sender)}…`
|
|
@@ -1365,7 +1372,7 @@ export class MessageBoxClient {
|
|
|
1365
1372
|
}, originator)
|
|
1366
1373
|
|
|
1367
1374
|
const decryptedText = Utils.toUTF8(decrypted.plaintext)
|
|
1368
|
-
message.body = tryParse(decryptedText)
|
|
1375
|
+
message.body = this.tryParse(decryptedText)
|
|
1369
1376
|
} else {
|
|
1370
1377
|
// For non-encrypted messages, use the processed content
|
|
1371
1378
|
message.body = messageContent ?? parsedBody
|
|
@@ -1388,6 +1395,37 @@ export class MessageBoxClient {
|
|
|
1388
1395
|
return messages
|
|
1389
1396
|
}
|
|
1390
1397
|
|
|
1398
|
+
/**
|
|
1399
|
+
* @method listMessagesLite
|
|
1400
|
+
* @async
|
|
1401
|
+
* @param {ListMessagesParams} params - Contains the `messageBox` to read from and the `host` to query.
|
|
1402
|
+
* @returns {Promise<PeerMessage[]>} - Returns an array of decrypted `PeerMessage` objects with minimal processing.
|
|
1403
|
+
*
|
|
1404
|
+
* @description
|
|
1405
|
+
* A lightweight variant of {@link listMessages} that fetches and decrypts messages
|
|
1406
|
+
* from a specific host without performing:
|
|
1407
|
+
* - Overlay host resolution
|
|
1408
|
+
* - Payment acceptance or internalization
|
|
1409
|
+
* - Cross-host deduplication
|
|
1410
|
+
*
|
|
1411
|
+
* This method:
|
|
1412
|
+
* - Sends a direct POST request to the specified host's `/listMessages` endpoint.
|
|
1413
|
+
* - Parses message bodies as JSON when possible.
|
|
1414
|
+
* - Decrypts messages if they contain an `encryptedMessage` field, using AES-256-GCM via BRC-2-compliant ECDH key derivation.
|
|
1415
|
+
* - Returns messages in the order provided by the host.
|
|
1416
|
+
*
|
|
1417
|
+
* This is intended for cases where you already know the host and need faster,
|
|
1418
|
+
* simpler retrieval without the additional processing overhead of `listMessages`.
|
|
1419
|
+
*
|
|
1420
|
+
* @throws {Error} If the host returns an error status or decryption fails.
|
|
1421
|
+
*
|
|
1422
|
+
* @example
|
|
1423
|
+
* const messages = await client.listMessagesLite({
|
|
1424
|
+
* messageBox: 'notifications',
|
|
1425
|
+
* host: 'https://messagebox.babbage.systems'
|
|
1426
|
+
* })
|
|
1427
|
+
* console.log(messages)
|
|
1428
|
+
*/
|
|
1391
1429
|
async listMessagesLite ({ messageBox, host }: ListMessagesParams): Promise<PeerMessage[]> {
|
|
1392
1430
|
const res = await this.authFetch.fetch(`${host as string}/listMessages`, {
|
|
1393
1431
|
method: 'POST',
|
|
@@ -1452,6 +1490,121 @@ export class MessageBoxClient {
|
|
|
1452
1490
|
return messages
|
|
1453
1491
|
}
|
|
1454
1492
|
|
|
1493
|
+
/**
|
|
1494
|
+
* @method tryParse
|
|
1495
|
+
* @private
|
|
1496
|
+
* @param {string} raw - A raw string value that may contain JSON.
|
|
1497
|
+
* @returns {any} - The parsed JavaScript object if valid JSON, or the original string if parsing fails.
|
|
1498
|
+
*
|
|
1499
|
+
* @description
|
|
1500
|
+
* Attempts to parse a string as JSON. If the string is valid JSON, returns the parsed object;
|
|
1501
|
+
* otherwise returns the original string unchanged.
|
|
1502
|
+
*
|
|
1503
|
+
* This method is used throughout the client to safely handle message bodies that may or may not be
|
|
1504
|
+
* JSON-encoded without throwing parsing errors.
|
|
1505
|
+
*
|
|
1506
|
+
* @example
|
|
1507
|
+
* tryParse('{"hello":"world"}') // → { hello: "world" }
|
|
1508
|
+
* tryParse('plain text') // → "plain text"
|
|
1509
|
+
*/
|
|
1510
|
+
tryParse (raw: string): any {
|
|
1511
|
+
try {
|
|
1512
|
+
return JSON.parse(raw)
|
|
1513
|
+
} catch {
|
|
1514
|
+
return raw
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* @method acknowledgeNotification
|
|
1520
|
+
* @async
|
|
1521
|
+
* @param {PeerMessage} message - The peer message object to acknowledge.
|
|
1522
|
+
* @returns {Promise<boolean>} - Resolves to `true` if the message included a recipient payment and it was successfully internalized, otherwise `false`.
|
|
1523
|
+
*
|
|
1524
|
+
* @description
|
|
1525
|
+
* Acknowledges receipt of a specific notification message and, if applicable, processes any recipient
|
|
1526
|
+
* payment contained within it.
|
|
1527
|
+
*
|
|
1528
|
+
* This method:
|
|
1529
|
+
* 1. Calls `acknowledgeMessage()` to remove the message from the server's queue.
|
|
1530
|
+
* 2. Checks the message body for embedded payment data.
|
|
1531
|
+
* 3. If a recipient payment exists, attempts to internalize it into the wallet.
|
|
1532
|
+
*
|
|
1533
|
+
* This is a convenience wrapper for acknowledgment and payment handling specifically for messages
|
|
1534
|
+
* representing notifications.
|
|
1535
|
+
*
|
|
1536
|
+
* @example
|
|
1537
|
+
* const success = await client.acknowledgeNotification(message)
|
|
1538
|
+
* console.log(success ? 'Payment received' : 'No payment or failed')
|
|
1539
|
+
*/
|
|
1540
|
+
async acknowledgeNotification (message: PeerMessage): Promise<boolean> {
|
|
1541
|
+
await this.acknowledgeMessage({ messageIds: [message.messageId] })
|
|
1542
|
+
|
|
1543
|
+
const parsedBody: unknown =
|
|
1544
|
+
typeof message.body === 'string' ? this.tryParse(message.body) : message.body
|
|
1545
|
+
|
|
1546
|
+
let paymentData: Payment | undefined
|
|
1547
|
+
|
|
1548
|
+
if (
|
|
1549
|
+
parsedBody != null &&
|
|
1550
|
+
typeof parsedBody === 'object' &&
|
|
1551
|
+
'message' in parsedBody
|
|
1552
|
+
) {
|
|
1553
|
+
paymentData = (parsedBody as any).payment
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// Process payment if present - server now only stores recipient payments
|
|
1557
|
+
if (paymentData?.tx != null && paymentData.outputs != null) {
|
|
1558
|
+
try {
|
|
1559
|
+
Logger.log(
|
|
1560
|
+
`[MB CLIENT] Processing recipient payment in message from ${String(message.sender)}…`
|
|
1561
|
+
)
|
|
1562
|
+
|
|
1563
|
+
// All outputs in the stored payment data are for the recipient
|
|
1564
|
+
// (delivery fees are already processed by the server)
|
|
1565
|
+
const recipientOutputs = paymentData.outputs.filter(
|
|
1566
|
+
output => output.protocol === 'wallet payment'
|
|
1567
|
+
)
|
|
1568
|
+
|
|
1569
|
+
if (recipientOutputs.length < 1) {
|
|
1570
|
+
Logger.log(
|
|
1571
|
+
'[MB CLIENT] No wallet payment outputs found in payment data'
|
|
1572
|
+
)
|
|
1573
|
+
return false
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
Logger.log(
|
|
1577
|
+
`[MB CLIENT] Internalizing ${recipientOutputs.length} recipient payment output(s)…`
|
|
1578
|
+
)
|
|
1579
|
+
|
|
1580
|
+
const internalizeResult = await this.walletClient.internalizeAction({
|
|
1581
|
+
tx: paymentData.tx,
|
|
1582
|
+
outputs: recipientOutputs,
|
|
1583
|
+
description: paymentData.description ?? 'MessageBox recipient payment'
|
|
1584
|
+
})
|
|
1585
|
+
|
|
1586
|
+
if (internalizeResult.accepted) {
|
|
1587
|
+
Logger.log(
|
|
1588
|
+
'[MB CLIENT] Successfully internalized recipient payment'
|
|
1589
|
+
)
|
|
1590
|
+
return true
|
|
1591
|
+
} else {
|
|
1592
|
+
Logger.warn(
|
|
1593
|
+
'[MB CLIENT] Recipient payment internalization was not accepted'
|
|
1594
|
+
)
|
|
1595
|
+
return false
|
|
1596
|
+
}
|
|
1597
|
+
} catch (paymentError) {
|
|
1598
|
+
Logger.error(
|
|
1599
|
+
'[MB CLIENT ERROR] Failed to internalize recipient payment:',
|
|
1600
|
+
paymentError
|
|
1601
|
+
)
|
|
1602
|
+
return false
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return false
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1455
1608
|
/**
|
|
1456
1609
|
* @method acknowledgeMessage
|
|
1457
1610
|
* @async
|
|
@@ -1473,8 +1626,7 @@ export class MessageBoxClient {
|
|
|
1473
1626
|
* @example
|
|
1474
1627
|
* await client.acknowledgeMessage({ messageIds: ['msg123', 'msg456'] })
|
|
1475
1628
|
*/
|
|
1476
|
-
async acknowledgeMessage({ messageIds, host, originator }: AcknowledgeMessageParams): Promise<string> {
|
|
1477
|
-
await this.assertInitialized()
|
|
1629
|
+
async acknowledgeMessage ({ messageIds, host, originator }: AcknowledgeMessageParams): Promise<string> {
|
|
1478
1630
|
if (!Array.isArray(messageIds) || messageIds.length === 0) {
|
|
1479
1631
|
throw new Error('Message IDs array cannot be empty')
|
|
1480
1632
|
}
|
|
@@ -1556,11 +1708,10 @@ export class MessageBoxClient {
|
|
|
1556
1708
|
* recipientFee: -1
|
|
1557
1709
|
* })
|
|
1558
1710
|
*/
|
|
1559
|
-
async setMessageBoxPermission(
|
|
1711
|
+
async setMessageBoxPermission (
|
|
1560
1712
|
params: SetMessageBoxPermissionParams,
|
|
1561
1713
|
overrideHost?: string
|
|
1562
1714
|
): Promise<void> {
|
|
1563
|
-
await this.assertInitialized()
|
|
1564
1715
|
const finalHost = overrideHost ?? this.host
|
|
1565
1716
|
|
|
1566
1717
|
Logger.log('[MB CLIENT] Setting messageBox permission...')
|
|
@@ -1604,12 +1755,10 @@ export class MessageBoxClient {
|
|
|
1604
1755
|
* sender: '03abc123...'
|
|
1605
1756
|
* })
|
|
1606
1757
|
*/
|
|
1607
|
-
async getMessageBoxPermission(
|
|
1758
|
+
async getMessageBoxPermission (
|
|
1608
1759
|
params: GetMessageBoxPermissionParams,
|
|
1609
1760
|
overrideHost?: string
|
|
1610
1761
|
): Promise<MessageBoxPermission | null> {
|
|
1611
|
-
await this.assertInitialized()
|
|
1612
|
-
|
|
1613
1762
|
const finalHost = overrideHost ?? await this.resolveHostForRecipient(params.recipient)
|
|
1614
1763
|
const queryParams = new URLSearchParams({
|
|
1615
1764
|
recipient: params.recipient,
|
|
@@ -1651,9 +1800,7 @@ export class MessageBoxClient {
|
|
|
1651
1800
|
* messageBox: 'notifications'
|
|
1652
1801
|
* })
|
|
1653
1802
|
*/
|
|
1654
|
-
async getMessageBoxQuote(params: GetQuoteParams, overrideHost?: string): Promise<MessageBoxQuote> {
|
|
1655
|
-
await this.assertInitialized()
|
|
1656
|
-
|
|
1803
|
+
async getMessageBoxQuote (params: GetQuoteParams, overrideHost?: string): Promise<MessageBoxQuote> {
|
|
1657
1804
|
const finalHost = overrideHost ?? await this.resolveHostForRecipient(params.recipient)
|
|
1658
1805
|
const queryParams = new URLSearchParams({
|
|
1659
1806
|
recipient: params.recipient,
|
|
@@ -1709,9 +1856,7 @@ export class MessageBoxClient {
|
|
|
1709
1856
|
* offset: 0
|
|
1710
1857
|
* })
|
|
1711
1858
|
*/
|
|
1712
|
-
async listMessageBoxPermissions(params?: ListPermissionsParams, overrideHost?: string): Promise<MessageBoxPermission[]> {
|
|
1713
|
-
await this.assertInitialized()
|
|
1714
|
-
|
|
1859
|
+
async listMessageBoxPermissions (params?: ListPermissionsParams, overrideHost?: string): Promise<MessageBoxPermission[]> {
|
|
1715
1860
|
const finalHost = overrideHost ?? this.host
|
|
1716
1861
|
const queryParams = new URLSearchParams()
|
|
1717
1862
|
|
|
@@ -1770,7 +1915,7 @@ export class MessageBoxClient {
|
|
|
1770
1915
|
* await client.allowNotificationsFromPeer('03abc123...') // Always allow
|
|
1771
1916
|
* await client.allowNotificationsFromPeer('03def456...', 5) // Allow for 5 sats
|
|
1772
1917
|
*/
|
|
1773
|
-
async allowNotificationsFromPeer(identityKey: PubKeyHex, recipientFee: number = 0, overrideHost?: string): Promise<void> {
|
|
1918
|
+
async allowNotificationsFromPeer (identityKey: PubKeyHex, recipientFee: number = 0, overrideHost?: string): Promise<void> {
|
|
1774
1919
|
await this.setMessageBoxPermission({
|
|
1775
1920
|
messageBox: 'notifications',
|
|
1776
1921
|
sender: identityKey,
|
|
@@ -1790,7 +1935,7 @@ export class MessageBoxClient {
|
|
|
1790
1935
|
* @example
|
|
1791
1936
|
* await client.denyNotificationsFromPeer('03spam123...')
|
|
1792
1937
|
*/
|
|
1793
|
-
async denyNotificationsFromPeer(identityKey: PubKeyHex, overrideHost?: string): Promise<void> {
|
|
1938
|
+
async denyNotificationsFromPeer (identityKey: PubKeyHex, overrideHost?: string): Promise<void> {
|
|
1794
1939
|
await this.setMessageBoxPermission({
|
|
1795
1940
|
messageBox: 'notifications',
|
|
1796
1941
|
sender: identityKey,
|
|
@@ -1811,7 +1956,7 @@ export class MessageBoxClient {
|
|
|
1811
1956
|
* const status = await client.checkPeerNotificationStatus('03abc123...')
|
|
1812
1957
|
* console.log(status.allowed) // true/false
|
|
1813
1958
|
*/
|
|
1814
|
-
async checkPeerNotificationStatus(identityKey: PubKeyHex, overrideHost?: string): Promise<MessageBoxPermission | null> {
|
|
1959
|
+
async checkPeerNotificationStatus (identityKey: PubKeyHex, overrideHost?: string): Promise<MessageBoxPermission | null> {
|
|
1815
1960
|
const myIdentityKey = await this.getIdentityKey()
|
|
1816
1961
|
return await this.getMessageBoxPermission({
|
|
1817
1962
|
recipient: myIdentityKey,
|
|
@@ -1831,7 +1976,7 @@ export class MessageBoxClient {
|
|
|
1831
1976
|
* @example
|
|
1832
1977
|
* const notifications = await client.listPeerNotifications()
|
|
1833
1978
|
*/
|
|
1834
|
-
async listPeerNotifications(overrideHost?: string): Promise<MessageBoxPermission[]> {
|
|
1979
|
+
async listPeerNotifications (overrideHost?: string): Promise<MessageBoxPermission[]> {
|
|
1835
1980
|
return await this.listMessageBoxPermissions({ messageBox: 'notifications' }, overrideHost)
|
|
1836
1981
|
}
|
|
1837
1982
|
|
|
@@ -1854,7 +1999,7 @@ export class MessageBoxClient {
|
|
|
1854
1999
|
* // Send with maximum payment limit for safety
|
|
1855
2000
|
* await client.sendNotification('03def456...', { title: 'Alert', body: 'Important update' }, 50)
|
|
1856
2001
|
*/
|
|
1857
|
-
async sendNotification(
|
|
2002
|
+
async sendNotification (
|
|
1858
2003
|
recipient: PubKeyHex,
|
|
1859
2004
|
body: string | object,
|
|
1860
2005
|
overrideHost?: string
|
|
@@ -1890,12 +2035,10 @@ export class MessageBoxClient {
|
|
|
1890
2035
|
* deviceId: 'iPhone15Pro'
|
|
1891
2036
|
* })
|
|
1892
2037
|
*/
|
|
1893
|
-
async registerDevice(
|
|
2038
|
+
async registerDevice (
|
|
1894
2039
|
params: DeviceRegistrationParams,
|
|
1895
2040
|
overrideHost?: string
|
|
1896
2041
|
): Promise<DeviceRegistrationResponse> {
|
|
1897
|
-
await this.assertInitialized()
|
|
1898
|
-
|
|
1899
2042
|
if (params.fcmToken == null || params.fcmToken.trim() === '') {
|
|
1900
2043
|
throw new Error('fcmToken is required and must be a non-empty string')
|
|
1901
2044
|
}
|
|
@@ -1957,11 +2100,9 @@ export class MessageBoxClient {
|
|
|
1957
2100
|
* console.log(`Device: ${device.platform} - ${device.fcmToken}`)
|
|
1958
2101
|
* })
|
|
1959
2102
|
*/
|
|
1960
|
-
async listRegisteredDevices(
|
|
2103
|
+
async listRegisteredDevices (
|
|
1961
2104
|
overrideHost?: string
|
|
1962
2105
|
): Promise<RegisteredDevice[]> {
|
|
1963
|
-
await this.assertInitialized()
|
|
1964
|
-
|
|
1965
2106
|
const finalHost = overrideHost ?? this.host
|
|
1966
2107
|
|
|
1967
2108
|
Logger.log('[MB CLIENT] Listing registered devices...')
|
|
@@ -1989,22 +2130,39 @@ export class MessageBoxClient {
|
|
|
1989
2130
|
// PRIVATE HELPER METHODS
|
|
1990
2131
|
// ===========================
|
|
1991
2132
|
|
|
1992
|
-
private static getStatusFromFee(fee: number): 'always_allow' | 'blocked' | 'payment_required' {
|
|
2133
|
+
private static getStatusFromFee (fee: number): 'always_allow' | 'blocked' | 'payment_required' {
|
|
1993
2134
|
if (fee === -1) return 'blocked'
|
|
1994
2135
|
if (fee === 0) return 'always_allow'
|
|
1995
2136
|
return 'payment_required'
|
|
1996
2137
|
}
|
|
1997
2138
|
|
|
1998
2139
|
/**
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2140
|
+
* @method createMessagePayment
|
|
2141
|
+
* @private
|
|
2142
|
+
* @param {string} recipient - Recipient's identity key.
|
|
2143
|
+
* @param {MessageBoxQuote} quote - Quote object containing recipient and delivery fees.
|
|
2144
|
+
* @param {string} [description='MessageBox delivery payment'] - Description for the payment action.
|
|
2145
|
+
* @param {string} [originator] - Optional originator to use for wallet operations.
|
|
2146
|
+
* @returns {Promise<Payment>} - Payment data including the transaction and remittance outputs.
|
|
2147
|
+
*
|
|
2148
|
+
* @description
|
|
2149
|
+
* Constructs and signs a payment transaction covering both delivery and recipient fees for
|
|
2150
|
+
* message delivery, based on a previously obtained quote.
|
|
2151
|
+
*
|
|
2152
|
+
* The transaction includes:
|
|
2153
|
+
* - An optional delivery fee output for the MessageBox server.
|
|
2154
|
+
* - An optional recipient fee output for the message recipient.
|
|
2155
|
+
*
|
|
2156
|
+
* Payment remittance metadata (derivation prefix/suffix, sender identity) is embedded to allow
|
|
2157
|
+
* the payee to derive their private key and spend the output.
|
|
2158
|
+
*
|
|
2159
|
+
* @throws {Error} If no payment is required, key derivation fails, or the action creation fails.
|
|
2160
|
+
*
|
|
2161
|
+
* @example
|
|
2162
|
+
* const payment = await client.createMessagePayment(recipientKey, quote)
|
|
2163
|
+
* await client.sendMessage({ recipient, messageBox, body, payment })
|
|
2164
|
+
*/
|
|
2165
|
+
private async createMessagePayment (
|
|
2008
2166
|
recipient: string,
|
|
2009
2167
|
quote: MessageBoxQuote,
|
|
2010
2168
|
description: string = 'MessageBox delivery payment',
|