@oxidezap/baileyrs 0.2.1 → 0.2.3
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/lib/Bridge/schema.js +1 -4
- package/lib/Bridge/types.d.ts +1 -6
- package/lib/Compatibility/all-encryptions-failed.d.ts +11 -0
- package/lib/Compatibility/all-encryptions-failed.js +49 -0
- package/lib/Socket/bridge-error-boundary.d.ts +38 -0
- package/lib/Socket/bridge-error-boundary.js +80 -0
- package/lib/Socket/events.js +28 -2
- package/lib/Socket/index.js +11 -7
- package/lib/Socket/messages.js +6 -9
- package/package.json +2 -2
package/lib/Bridge/schema.js
CHANGED
|
@@ -179,10 +179,6 @@ const ADAPTERS = {
|
|
|
179
179
|
};
|
|
180
180
|
},
|
|
181
181
|
// ── Contacts ──
|
|
182
|
-
push_name_update: data => {
|
|
183
|
-
const jid = asJidString(data?.jid);
|
|
184
|
-
return jid ? { type: 'pushNameUpdate', jid, newPushName: asString(data?.new_push_name) } : null;
|
|
185
|
-
},
|
|
186
182
|
contact_update: data => adaptContactUpdate(data),
|
|
187
183
|
contact_updated: data => adaptContactUpdate(data),
|
|
188
184
|
picture_update: data => {
|
|
@@ -456,6 +452,7 @@ const ADAPTERS = {
|
|
|
456
452
|
},
|
|
457
453
|
// ── Acknowledged but no Baileys equivalent (noop) ──
|
|
458
454
|
self_push_name_updated: () => ({ type: 'noop', bridgeType: 'self_push_name_updated' }),
|
|
455
|
+
client_expiration_changed: () => ({ type: 'noop', bridgeType: 'client_expiration_changed' }),
|
|
459
456
|
offline_sync_completed: data => ({
|
|
460
457
|
type: 'offlineSyncCompleted',
|
|
461
458
|
count: asNumber(data?.count) ?? 0
|
package/lib/Bridge/types.d.ts
CHANGED
|
@@ -173,11 +173,6 @@ export interface CanonicalReceipt {
|
|
|
173
173
|
*/
|
|
174
174
|
receiptType?: 'delivered' | 'sent' | 'sender' | 'retry' | 'enc-rekey-retry' | 'read' | 'read-self' | 'played' | 'played-self' | 'inactive' | 'peer-msg' | 'history-sync' | 'server-error' | 'other';
|
|
175
175
|
}
|
|
176
|
-
export interface CanonicalPushNameUpdate {
|
|
177
|
-
type: 'pushNameUpdate';
|
|
178
|
-
jid: string;
|
|
179
|
-
newPushName?: string;
|
|
180
|
-
}
|
|
181
176
|
export interface CanonicalContactUpdate {
|
|
182
177
|
type: 'contactUpdate';
|
|
183
178
|
jid: string;
|
|
@@ -713,5 +708,5 @@ export interface CanonicalServerAck {
|
|
|
713
708
|
timestamp?: number;
|
|
714
709
|
error?: string;
|
|
715
710
|
}
|
|
716
|
-
export type CanonicalEvent = CanonicalConnected | CanonicalDisconnected | CanonicalQR | CanonicalPairSuccess | CanonicalPairError | CanonicalLoggedOut | CanonicalConnectFailure | CanonicalStreamError | CanonicalStreamReplaced | CanonicalClientOutdated | CanonicalTemporaryBan | CanonicalQrScannedWithoutMultidevice | CanonicalMessage | CanonicalReceipt |
|
|
711
|
+
export type CanonicalEvent = CanonicalConnected | CanonicalDisconnected | CanonicalQR | CanonicalPairSuccess | CanonicalPairError | CanonicalLoggedOut | CanonicalConnectFailure | CanonicalStreamError | CanonicalStreamReplaced | CanonicalClientOutdated | CanonicalTemporaryBan | CanonicalQrScannedWithoutMultidevice | CanonicalMessage | CanonicalReceipt | CanonicalContactUpdate | CanonicalPictureUpdate | CanonicalPresence | CanonicalChatPresence | CanonicalGroupUpdate | CanonicalArchiveUpdate | CanonicalPinUpdate | CanonicalMuteUpdate | CanonicalStarUpdate | CanonicalMarkChatAsReadUpdate | CanonicalLabelEdit | CanonicalLabelAssociation | CanonicalAppStateSyncFailed | CanonicalQrCodesExhausted | CanonicalSettingUpdate | CanonicalIncomingCall | CanonicalUndecryptableMessage | CanonicalLidMappingUpdate | CanonicalNewsletterLiveUpdate | CanonicalChatDelete | CanonicalChatClear | CanonicalMessageDelete | CanonicalDisappearingModeChanged | CanonicalHistorySync | CanonicalOfflineSyncCompleted | CanonicalDirtyState | CanonicalRawNode | CanonicalNotification | CanonicalMexNotification | CanonicalServerAck | CanonicalNoop;
|
|
717
712
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rejection Baileys code expects, or the original error untouched.
|
|
3
|
+
*
|
|
4
|
+
* Narrow on purpose: only the engine's own `no-recipient-device` is rewritten,
|
|
5
|
+
* so every other failure still reaches the caller with the shape the bridge
|
|
6
|
+
* gave it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const asAllEncryptionsFailed: (error: unknown) => unknown;
|
|
9
|
+
/** Run a send, translating only that one rejection. */
|
|
10
|
+
export declare const sendReportingUpstreamFailure: <T>(send: () => Promise<T>) => Promise<T>;
|
|
11
|
+
//# sourceMappingURL=all-encryptions-failed.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Boom } from '../Utils/boom.js';
|
|
2
|
+
/**
|
|
3
|
+
* A send that produced no ciphertext for its recipient.
|
|
4
|
+
*
|
|
5
|
+
* Upstream Baileys drops a device it cannot encrypt for and, when that leaves
|
|
6
|
+
* nothing, throws `Boom('All encryptions failed', { statusCode: 500 })`
|
|
7
|
+
* (`Socket/messages-send.ts`, in `createParticipantNodes`). The engine reached
|
|
8
|
+
* the same conclusion later: until `whatsapp-rust-bridge` 0.13.0 it returned a
|
|
9
|
+
* message id for a send that was never transmitted, and it now rejects with
|
|
10
|
+
* `no-recipient-device` instead.
|
|
11
|
+
*
|
|
12
|
+
* Same condition, different spelling, so it is translated here rather than
|
|
13
|
+
* left for the caller: Baileys code that branches on `statusCode === 500` or
|
|
14
|
+
* matches the message keeps working, and `attempted` rides along as extra
|
|
15
|
+
* data upstream has no way to carry.
|
|
16
|
+
*/
|
|
17
|
+
const NO_RECIPIENT_DEVICE = 'no-recipient-device';
|
|
18
|
+
/** How many recipient devices the engine tried, when it said. */
|
|
19
|
+
const attemptedFrom = (error) => {
|
|
20
|
+
const { attempted } = error;
|
|
21
|
+
return typeof attempted === 'number' ? attempted : undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The rejection Baileys code expects, or the original error untouched.
|
|
25
|
+
*
|
|
26
|
+
* Narrow on purpose: only the engine's own `no-recipient-device` is rewritten,
|
|
27
|
+
* so every other failure still reaches the caller with the shape the bridge
|
|
28
|
+
* gave it.
|
|
29
|
+
*/
|
|
30
|
+
export const asAllEncryptionsFailed = (error) => {
|
|
31
|
+
if (typeof error !== 'object' || error === null)
|
|
32
|
+
return error;
|
|
33
|
+
if (error.kind !== NO_RECIPIENT_DEVICE)
|
|
34
|
+
return error;
|
|
35
|
+
return new Boom('All encryptions failed', {
|
|
36
|
+
statusCode: 500,
|
|
37
|
+
data: { attempted: attemptedFrom(error) }
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
/** Run a send, translating only that one rejection. */
|
|
41
|
+
export const sendReportingUpstreamFailure = async (send) => {
|
|
42
|
+
try {
|
|
43
|
+
return await send();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw asAllEncryptionsFailed(error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=all-encryptions-failed.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recreate a bridge rejection at the JS boundary so its stack names the caller.
|
|
3
|
+
*
|
|
4
|
+
* The engine constructs its errors inside wasm, in a microtask continuation
|
|
5
|
+
* that runs after the server response arrives. V8 captures the stack at
|
|
6
|
+
* construction, so what reaches the consumer is glue plus `wasm://` frames
|
|
7
|
+
* and never the code that made the call. Recreating the error inside a catch
|
|
8
|
+
* the caller is awaiting fixes that: V8's async stack walk names the awaiting
|
|
9
|
+
* frames, so a bot sees `at async itsOwnCommand` instead of function indices.
|
|
10
|
+
*
|
|
11
|
+
* What survives translation, and how:
|
|
12
|
+
*
|
|
13
|
+
* - The engine sets its payload (`kind`, `serverCode`, `serverText`,
|
|
14
|
+
* `errorType`, `backoffSeconds`, and whatever it adds next) and `name`
|
|
15
|
+
* (`WhatsAppError`) as own enumerable properties, while `message` and
|
|
16
|
+
* `stack` are own non-enumerable ones. `Object.assign` therefore copies
|
|
17
|
+
* exactly the payload and the name, never touches the fresh stack, and
|
|
18
|
+
* cannot invent a key the rejection did not carry.
|
|
19
|
+
* - The original error becomes `cause`, so the wasm-side stack stays
|
|
20
|
+
* reachable for whoever wants the engine's half of the story.
|
|
21
|
+
* - Anything that is not the bridge's shape (an `Error` carrying a string
|
|
22
|
+
* `kind`) passes through with the same identity it arrived with.
|
|
23
|
+
*
|
|
24
|
+
* The wrap happens once per client: `getClient()`/`getClientSync()` hand out
|
|
25
|
+
* a `Proxy` whose method wrappers are built on first access and cached, so
|
|
26
|
+
* the happy path pays one property trap and one extra promise layer, and the
|
|
27
|
+
* error path pays for everything else.
|
|
28
|
+
*/
|
|
29
|
+
import type { WasmWhatsAppClient } from '@oxidezap/whatsapp-rust-bridge';
|
|
30
|
+
/** Rebuild a bridge rejection here; return anything else untouched. */
|
|
31
|
+
export declare const withCallerStack: (error: unknown) => unknown;
|
|
32
|
+
/**
|
|
33
|
+
* The client the socket hands out. Methods bind to the raw client (wasm
|
|
34
|
+
* bindings need their own `this`), sync returns pass through untouched, and
|
|
35
|
+
* promise returns reject through `withCallerStack`.
|
|
36
|
+
*/
|
|
37
|
+
export declare const wrapBridgeClient: (client: WasmWhatsAppClient) => WasmWhatsAppClient;
|
|
38
|
+
//# sourceMappingURL=bridge-error-boundary.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recreate a bridge rejection at the JS boundary so its stack names the caller.
|
|
3
|
+
*
|
|
4
|
+
* The engine constructs its errors inside wasm, in a microtask continuation
|
|
5
|
+
* that runs after the server response arrives. V8 captures the stack at
|
|
6
|
+
* construction, so what reaches the consumer is glue plus `wasm://` frames
|
|
7
|
+
* and never the code that made the call. Recreating the error inside a catch
|
|
8
|
+
* the caller is awaiting fixes that: V8's async stack walk names the awaiting
|
|
9
|
+
* frames, so a bot sees `at async itsOwnCommand` instead of function indices.
|
|
10
|
+
*
|
|
11
|
+
* What survives translation, and how:
|
|
12
|
+
*
|
|
13
|
+
* - The engine sets its payload (`kind`, `serverCode`, `serverText`,
|
|
14
|
+
* `errorType`, `backoffSeconds`, and whatever it adds next) and `name`
|
|
15
|
+
* (`WhatsAppError`) as own enumerable properties, while `message` and
|
|
16
|
+
* `stack` are own non-enumerable ones. `Object.assign` therefore copies
|
|
17
|
+
* exactly the payload and the name, never touches the fresh stack, and
|
|
18
|
+
* cannot invent a key the rejection did not carry.
|
|
19
|
+
* - The original error becomes `cause`, so the wasm-side stack stays
|
|
20
|
+
* reachable for whoever wants the engine's half of the story.
|
|
21
|
+
* - Anything that is not the bridge's shape (an `Error` carrying a string
|
|
22
|
+
* `kind`) passes through with the same identity it arrived with.
|
|
23
|
+
*
|
|
24
|
+
* The wrap happens once per client: `getClient()`/`getClientSync()` hand out
|
|
25
|
+
* a `Proxy` whose method wrappers are built on first access and cached, so
|
|
26
|
+
* the happy path pays one property trap and one extra promise layer, and the
|
|
27
|
+
* error path pays for everything else.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* The bridge's rejection shape: a real `Error` the glue named `WhatsAppError`,
|
|
31
|
+
* carrying a string `kind`. The name check keeps consumer-owned errors that
|
|
32
|
+
* also discriminate on `kind` (a custom store or cache thrown through a
|
|
33
|
+
* bridge call) crossing by identity instead of being rebuilt.
|
|
34
|
+
*/
|
|
35
|
+
const isBridgeRejection = (error) => error instanceof Error && error.name === 'WhatsAppError' && typeof error.kind === 'string';
|
|
36
|
+
/** Rebuild a bridge rejection here; return anything else untouched. */
|
|
37
|
+
export const withCallerStack = (error) => {
|
|
38
|
+
if (!isBridgeRejection(error))
|
|
39
|
+
return error;
|
|
40
|
+
return Object.assign(new Error(error.message, { cause: error }), error);
|
|
41
|
+
};
|
|
42
|
+
const rethrowWithCallerStack = async (call) => {
|
|
43
|
+
try {
|
|
44
|
+
return await call;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw withCallerStack(error);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const wrappedClients = new WeakMap();
|
|
51
|
+
/**
|
|
52
|
+
* The client the socket hands out. Methods bind to the raw client (wasm
|
|
53
|
+
* bindings need their own `this`), sync returns pass through untouched, and
|
|
54
|
+
* promise returns reject through `withCallerStack`.
|
|
55
|
+
*/
|
|
56
|
+
export const wrapBridgeClient = (client) => {
|
|
57
|
+
const memo = wrappedClients.get(client);
|
|
58
|
+
if (memo)
|
|
59
|
+
return memo;
|
|
60
|
+
const methods = new Map();
|
|
61
|
+
const wrapped = new Proxy(client, {
|
|
62
|
+
get(target, prop) {
|
|
63
|
+
const hit = methods.get(prop);
|
|
64
|
+
if (hit !== undefined)
|
|
65
|
+
return hit;
|
|
66
|
+
const value = Reflect.get(target, prop);
|
|
67
|
+
if (typeof value !== 'function')
|
|
68
|
+
return value;
|
|
69
|
+
const method = (...args) => {
|
|
70
|
+
const out = value.apply(target, args);
|
|
71
|
+
return out instanceof Promise ? rethrowWithCallerStack(out) : out;
|
|
72
|
+
};
|
|
73
|
+
methods.set(prop, method);
|
|
74
|
+
return method;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
wrappedClients.set(client, wrapped);
|
|
78
|
+
return wrapped;
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=bridge-error-boundary.js.map
|
package/lib/Socket/events.js
CHANGED
|
@@ -17,7 +17,7 @@ import { LabelAssociationType } from '../Types/LabelAssociation.js';
|
|
|
17
17
|
import { Boom } from '../Utils/boom.js';
|
|
18
18
|
import { toNumber } from '../Utils/generics.js';
|
|
19
19
|
import { CONVERSATION_HISTORY_SYNC_TYPES } from '../Utils/process-history-message.js';
|
|
20
|
-
import { isJidGroup } from '../WABinary/jid-utils.js';
|
|
20
|
+
import { isJidBroadcast, isJidGroup } from '../WABinary/jid-utils.js';
|
|
21
21
|
import { buildGroupCreateStubMessage, buildGroupJoinRequestEvents, buildGroupNotificationChatUpdates, buildGroupNotificationDomainEvent, buildGroupNotificationStubMessages } from '../Compatibility/group-notifications.js';
|
|
22
22
|
import { emitMessageUpsert } from '../Compatibility/message-upsert.js';
|
|
23
23
|
import { extractMessageCappingPayload } from './message-capping.js';
|
|
@@ -99,6 +99,20 @@ const messageUpsertMetadata = (message) => ({
|
|
|
99
99
|
requestId: message.unavailableRequestId
|
|
100
100
|
});
|
|
101
101
|
const hasMessageSideEffects = (message) => message.messageProto.reactionMessage != null || message.messageProto.protocolMessage != null;
|
|
102
|
+
// Mirror upstream `messages-recv.ts`: every inbound envelope that carries a
|
|
103
|
+
// push name surfaces it as `contacts.update` with `notify`. This is also what
|
|
104
|
+
// replaces the bridge's dedicated `push_name_update` event, gone in 0.14.0.
|
|
105
|
+
const emitInboundPushName = (ctx, evt) => {
|
|
106
|
+
if (!evt.pushName || evt.isFromMe)
|
|
107
|
+
return;
|
|
108
|
+
const id = evt.senderJid ?? evt.chatJid;
|
|
109
|
+
// A broadcast envelope resolves to the pseudo-contact (the canonical layer
|
|
110
|
+
// drops the participant for non-groups); naming `status@broadcast` after
|
|
111
|
+
// whoever posted last would corrupt it, so stay silent instead.
|
|
112
|
+
if (isJidBroadcast(id))
|
|
113
|
+
return;
|
|
114
|
+
ctx.ev.emit('contacts.update', [{ id, notify: evt.pushName }]);
|
|
115
|
+
};
|
|
102
116
|
const hasSameUpsertMetadata = (left, right) => left.type === right.type && left.requestId === right.requestId;
|
|
103
117
|
const clearHistorySyncPausedTimeout = (state) => {
|
|
104
118
|
if (state.pausedTimeout)
|
|
@@ -315,6 +329,7 @@ const DISPATCHERS = {
|
|
|
315
329
|
message: (evt, { ctx }) => {
|
|
316
330
|
if (ctx.fullConfig.shouldIgnoreJid?.(evt.chatJid))
|
|
317
331
|
return;
|
|
332
|
+
emitInboundPushName(ctx, evt);
|
|
318
333
|
// Note: `emitOwnEvents=false` is NOT applied here. Upstream Baileys
|
|
319
334
|
// uses that flag to suppress the local echo when `sendMessage()`
|
|
320
335
|
// succeeds, not to drop inbound `fromMe` messages from other linked
|
|
@@ -422,6 +437,13 @@ const DISPATCHERS = {
|
|
|
422
437
|
// can request a resend (PLACEHOLDER_MESSAGE_RESEND PDO). Logging
|
|
423
438
|
// happens at debug — upstream considers this routine.
|
|
424
439
|
ctx.logger.debug({ id: evt.id, chat: evt.chatJid, isUnavailable: evt.isUnavailable, fail: evt.decryptFailMode }, 'undecryptable message received');
|
|
440
|
+
// The name is envelope metadata, independent of whether the failure
|
|
441
|
+
// may be surfaced, so it goes out even for `hide`. The stub below
|
|
442
|
+
// predates `shouldIgnoreJid` and stays unguarded; the push name
|
|
443
|
+
// emission is new, so it honors the predicate like the other two
|
|
444
|
+
// call sites do.
|
|
445
|
+
if (!ctx.fullConfig.shouldIgnoreJid?.(evt.chatJid))
|
|
446
|
+
emitInboundPushName(ctx, evt);
|
|
425
447
|
// `decrypt_fail_mode === 'hide'` means the server told us to
|
|
426
448
|
// silently drop — match that by NOT emitting an upsert.
|
|
427
449
|
if (evt.decryptFailMode === 'hide')
|
|
@@ -445,7 +467,6 @@ const DISPATCHERS = {
|
|
|
445
467
|
ctx.ev.emit('messages.upsert', { messages: [stubMsg], type: 'notify' });
|
|
446
468
|
},
|
|
447
469
|
// ── Contacts ──
|
|
448
|
-
pushNameUpdate: (evt, { ctx }) => ctx.ev.emit('contacts.update', [{ id: evt.jid, notify: evt.newPushName }]),
|
|
449
470
|
contactUpdate: (evt, { ctx }) => {
|
|
450
471
|
// Promote ContactAction fields into upstream's `Partial<Contact>`
|
|
451
472
|
// shape so consumers (sidebar UIs, contact pickers) see real names
|
|
@@ -869,6 +890,11 @@ const dispatchCanonicalBatch = (ctx, dispatchCtx, count, canonicalAt) => {
|
|
|
869
890
|
if (ctx.fullConfig.shouldIgnoreJid?.(canonical.chatJid))
|
|
870
891
|
continue;
|
|
871
892
|
try {
|
|
893
|
+
// This branch bypasses the single-message dispatcher, so the push
|
|
894
|
+
// name has to be surfaced here too. Inside the try: a throwing
|
|
895
|
+
// consumer listener skips this message only, like the containment
|
|
896
|
+
// in dispatchCanonicalEvent.
|
|
897
|
+
emitInboundPushName(ctx, canonical);
|
|
872
898
|
const metadata = messageUpsertMetadata(canonical);
|
|
873
899
|
const message = canonicalMessageToWAMessage(canonical);
|
|
874
900
|
if (pending && hasSameUpsertMetadata(pending, metadata)) {
|
package/lib/Socket/index.js
CHANGED
|
@@ -28,6 +28,7 @@ import { makeChatActionMethods } from './chat-actions.js';
|
|
|
28
28
|
import { makeContactMethods } from './contacts.js';
|
|
29
29
|
import { makeCommunityMethods } from './communities.js';
|
|
30
30
|
import { makeBridgeClientOwner } from './bridge-client-owner.js';
|
|
31
|
+
import { wrapBridgeClient } from './bridge-error-boundary.js';
|
|
31
32
|
import { makeTerminalCloseReporter } from './terminal-close-reporter.js';
|
|
32
33
|
import { makeEventHandlers } from './events.js';
|
|
33
34
|
import { makeGroupMethods } from './groups.js';
|
|
@@ -224,7 +225,7 @@ const makeWASocket = (config) => {
|
|
|
224
225
|
// Unregister before freeing: `free()` is swallowed, so ordering it
|
|
225
226
|
// last would leave the module-level pointer aimed at a client that is
|
|
226
227
|
// already gone if anything between them threw.
|
|
227
|
-
_unregisterActiveBridgeClient(client);
|
|
228
|
+
_unregisterActiveBridgeClient(wrapBridgeClient(client));
|
|
228
229
|
try {
|
|
229
230
|
client.free();
|
|
230
231
|
}
|
|
@@ -298,7 +299,7 @@ const makeWASocket = (config) => {
|
|
|
298
299
|
if (initialized) {
|
|
299
300
|
const ready = owner.peek();
|
|
300
301
|
if (ready)
|
|
301
|
-
return Promise.resolve(ready);
|
|
302
|
+
return Promise.resolve(wrapBridgeClient(ready));
|
|
302
303
|
}
|
|
303
304
|
return initPromise.then(() => {
|
|
304
305
|
// Rechecked after the await: a close landing while startup was
|
|
@@ -319,7 +320,7 @@ const makeWASocket = (config) => {
|
|
|
319
320
|
const built = owner.peek();
|
|
320
321
|
if (!built)
|
|
321
322
|
throw new Boom('Client not initialized', { statusCode: 500 });
|
|
322
|
-
return built;
|
|
323
|
+
return wrapBridgeClient(built);
|
|
323
324
|
});
|
|
324
325
|
},
|
|
325
326
|
getClientSync: () => {
|
|
@@ -330,7 +331,7 @@ const makeWASocket = (config) => {
|
|
|
330
331
|
const built = owner.peek();
|
|
331
332
|
if (!built)
|
|
332
333
|
throw new Boom('Client not initialized', { statusCode: 500 });
|
|
333
|
-
return built;
|
|
334
|
+
return wrapBridgeClient(built);
|
|
334
335
|
}
|
|
335
336
|
};
|
|
336
337
|
// The native repository delegates Signal state directly to the core and does
|
|
@@ -459,8 +460,10 @@ const makeWASocket = (config) => {
|
|
|
459
460
|
if (!owner.adopt(created))
|
|
460
461
|
return owner.settled();
|
|
461
462
|
// Fallback for standalone helpers like `downloadContentFromMessage`
|
|
462
|
-
// that carry no socket reference.
|
|
463
|
-
|
|
463
|
+
// that carry no socket reference. Registered wrapped so those helpers
|
|
464
|
+
// reject with a caller stack too; the memoized wrap keeps the
|
|
465
|
+
// unregister identity check working.
|
|
466
|
+
_registerActiveBridgeClient(wrapBridgeClient(created), logger);
|
|
464
467
|
// Replay a preference set before the client existed. `setAutoReconnect`
|
|
465
468
|
// forwards through `client?.`, so `makeWASocket(cfg).setAutoReconnect(false)`
|
|
466
469
|
// — the idiomatic first line — used to move only the JS mirror and leave
|
|
@@ -757,7 +760,8 @@ const makeWASocket = (config) => {
|
|
|
757
760
|
};
|
|
758
761
|
},
|
|
759
762
|
get waClient() {
|
|
760
|
-
|
|
763
|
+
const live = owner.peek();
|
|
764
|
+
return live && wrapBridgeClient(live);
|
|
761
765
|
},
|
|
762
766
|
get isConnected() {
|
|
763
767
|
return owner.peek()?.isConnected() ?? false;
|
package/lib/Socket/messages.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sendReportingUpstreamFailure } from '../Compatibility/all-encryptions-failed.js';
|
|
1
2
|
import { sendDroppingDerivedNodes } from '../Compatibility/derived-stanza-nodes.js';
|
|
2
3
|
import { encodeProtoCompat } from '../Compatibility/encode-proto.js';
|
|
3
4
|
import { planMessageRelay } from '../Compatibility/message-relay.js';
|
|
@@ -69,14 +70,10 @@ export const makeMessageMethods = (ctx) => ({
|
|
|
69
70
|
return fullMsg;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
let msgId;
|
|
73
73
|
const msgBytes = encodeProtoCompat('Message', msg);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
else {
|
|
78
|
-
msgId = await client.sendMessageBytes(jid, msgBytes);
|
|
79
|
-
}
|
|
74
|
+
const msgId = await sendReportingUpstreamFailure(() => jid === 'status@broadcast' && options?.statusJidList?.length
|
|
75
|
+
? client.sendStatusMessageBytes(msgBytes, options.statusJidList)
|
|
76
|
+
: client.sendMessageBytes(jid, msgBytes));
|
|
80
77
|
fullMsg.key.id = msgId || fullMsg.key.id;
|
|
81
78
|
// Local echo of the message we just sent. Suppressed when
|
|
82
79
|
// `emitOwnEvents=false` so callers that explicitly opted out of seeing
|
|
@@ -150,9 +147,9 @@ export const makeMessageMethods = (ctx) => ({
|
|
|
150
147
|
// either way.
|
|
151
148
|
const drop = (tag) => ctx.logger.debug({ jid, messageId: plan.messageId, tag }, 'dropped an additionalNodes entry the engine derives from the message');
|
|
152
149
|
if (plan.kind === 'status') {
|
|
153
|
-
return sendDroppingDerivedNodes(plan.nodes, nodes => client.sendStatusMessageBytesWithOptions(bytes, plan.recipients, plan.messageId, nodes, plan.refreshDevices), drop);
|
|
150
|
+
return sendReportingUpstreamFailure(() => sendDroppingDerivedNodes(plan.nodes, nodes => client.sendStatusMessageBytesWithOptions(bytes, plan.recipients, plan.messageId, nodes, plan.refreshDevices), drop));
|
|
154
151
|
}
|
|
155
|
-
return sendDroppingDerivedNodes(plan.nodes, nodes => client.relayMessageBytesWithOptions(jid, bytes, plan.messageId, nodes, plan.refreshGroupMetadata, plan.refreshDevices), drop);
|
|
152
|
+
return sendReportingUpstreamFailure(() => sendDroppingDerivedNodes(plan.nodes, nodes => client.relayMessageBytesWithOptions(jid, bytes, plan.messageId, nodes, plan.refreshGroupMetadata, plan.refreshDevices), drop));
|
|
156
153
|
},
|
|
157
154
|
readMessages: async (keys) => {
|
|
158
155
|
const receiptKeys = receiptMessageKeys(keys);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxidezap/baileyrs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"description": "A Rust-powered WhatsApp Web library for JavaScript, with a Baileys-compatible API",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@hapi/boom": "^9.1.4",
|
|
85
|
-
"@oxidezap/whatsapp-rust-bridge": "0.
|
|
85
|
+
"@oxidezap/whatsapp-rust-bridge": "0.14.0",
|
|
86
86
|
"long": "^5.3.2",
|
|
87
87
|
"pino": "^10.3.1",
|
|
88
88
|
"protobufjs": "^7.6.5"
|