@oxidezap/baileyrs 0.0.29 → 0.0.30
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/Compatibility/proto-runtime.js +14 -3
- package/lib/Socket/events.js +66 -16
- package/lib/Utils/messages.js +31 -15
- package/package.json +2 -2
|
@@ -199,15 +199,21 @@ class ProtoCompatibilityRuntime {
|
|
|
199
199
|
makeConstructor(schemaId, fields, sourceCodec) {
|
|
200
200
|
const convertToObject = (message) => this.toObject(schemaId, message, JSON_OPTIONS);
|
|
201
201
|
const path = PROTO_MESSAGE_SCHEMAS[schemaId][0];
|
|
202
|
+
// Indexed loops: these run for every constructed instance and stay on
|
|
203
|
+
// the megamorphic path at runtime, where V8 does not elide for..of
|
|
204
|
+
// iterator allocations.
|
|
202
205
|
const constructor = function (properties) {
|
|
203
|
-
for (
|
|
206
|
+
for (let i = 0; i < fields.length; i++) {
|
|
207
|
+
const field = fields[i];
|
|
204
208
|
if (field[3] & PROTO_FIELD_FLAG.repeated)
|
|
205
209
|
this[field[0]] = [];
|
|
206
210
|
else if (field[3] & PROTO_FIELD_FLAG.map)
|
|
207
211
|
this[field[0]] = {};
|
|
208
212
|
}
|
|
209
213
|
if (properties) {
|
|
210
|
-
|
|
214
|
+
const keys = Object.keys(properties);
|
|
215
|
+
for (let i = 0; i < keys.length; i++) {
|
|
216
|
+
const key = keys[i];
|
|
211
217
|
const value = properties[key];
|
|
212
218
|
if (value !== null && value !== undefined)
|
|
213
219
|
this[key] = value;
|
|
@@ -311,7 +317,12 @@ class ProtoCompatibilityRuntime {
|
|
|
311
317
|
return input;
|
|
312
318
|
const data = input;
|
|
313
319
|
const instance = new constructor();
|
|
314
|
-
|
|
320
|
+
// Indexed loop: this walks the full schema per converted node on the
|
|
321
|
+
// megamorphic path, where V8 does not elide for..of iterator
|
|
322
|
+
// allocations (~90 iterator results per WebMessageInfo envelope).
|
|
323
|
+
const fields = PROTO_MESSAGE_SCHEMAS[schemaId][1];
|
|
324
|
+
for (let i = 0; i < fields.length; i++) {
|
|
325
|
+
const field = fields[i];
|
|
315
326
|
const value = data[field[0]];
|
|
316
327
|
if (value === null || value === undefined)
|
|
317
328
|
continue;
|
package/lib/Socket/events.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* mapped type so TS forces a handler per variant — a missing handler
|
|
7
7
|
* is a compile error.
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
9
|
+
import Long from 'long';
|
|
10
|
+
import { BinaryReader, decodeMessageWireInfos, decodeReceiptWireBatch, decodeServerAckWireBatch } from 'whatsapp-rust-bridge';
|
|
10
11
|
import { adaptBridgeEvent, adaptBridgeMessageWire } from '../Bridge/index.js';
|
|
11
12
|
import { decodeHistorySyncWireBatch } from '../Bridge/history-sync-wire.js';
|
|
12
13
|
import { bridgeGroupMetadataToBaileys } from '../Compatibility/group-metadata.js';
|
|
@@ -56,18 +57,29 @@ const emitCBEvents = (ctx, node) => {
|
|
|
56
57
|
* bridge → canonical → Baileys pipeline. Adapters never reach for the proto.
|
|
57
58
|
*/
|
|
58
59
|
const canonicalMessageToWAMessage = (m) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
// Direct construction instead of a `WebMessageInfo.fromObject` envelope:
|
|
61
|
+
// every input is already in its wire-correct runtime type, so the schema
|
|
62
|
+
// walk would only re-derive them field by field on the hottest path. `new`
|
|
63
|
+
// keeps protobufjs parity (instanceof, prototype defaults and own
|
|
64
|
+
// repeated/map fields), and `Message.fromObject` still normalizes the
|
|
65
|
+
// payload while short-circuiting the already-decoded runtime instances.
|
|
66
|
+
const key = new WAProto.MessageKey();
|
|
67
|
+
key.remoteJid = m.chatJid;
|
|
68
|
+
key.fromMe = m.isFromMe;
|
|
69
|
+
key.id = m.id;
|
|
70
|
+
if (m.senderJid !== undefined)
|
|
71
|
+
key.participant = m.senderJid;
|
|
72
|
+
const wm = new WAProto.WebMessageInfo();
|
|
73
|
+
wm.key = key;
|
|
74
|
+
if (m.messageProto !== undefined)
|
|
75
|
+
wm.message = WAProto.Message.fromObject(m.messageProto);
|
|
76
|
+
// Source-side WAProto types 64-bit fields via the bridge shapes (plain
|
|
77
|
+
// number); the published facade and the previous fromObject path both
|
|
78
|
+
// carry a protobufjs `Long` here, so keep that runtime shape.
|
|
79
|
+
wm.messageTimestamp = Long.fromValue(m.timestamp);
|
|
80
|
+
if (m.pushName !== undefined)
|
|
81
|
+
wm.pushName = m.pushName;
|
|
82
|
+
wm.status = WAProto.WebMessageInfo.Status.SERVER_ACK;
|
|
71
83
|
if (m.participantAlt)
|
|
72
84
|
wm.key.participantAlt = m.participantAlt;
|
|
73
85
|
if (m.remoteJidAlt)
|
|
@@ -784,8 +796,16 @@ export const makeEventHandlers = (ctx, callbacks) => {
|
|
|
784
796
|
return;
|
|
785
797
|
}
|
|
786
798
|
const messageCount = batch.messageOffsets.length - MESSAGE_WIRE_OFFSET_SENTINEL_COUNT;
|
|
787
|
-
|
|
788
|
-
|
|
799
|
+
let infos;
|
|
800
|
+
try {
|
|
801
|
+
infos = decodeMessageWireInfos(batch);
|
|
802
|
+
}
|
|
803
|
+
catch (err) {
|
|
804
|
+
ctx.logger.error({ err }, 'failed to decode message wire metadata records');
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (infos.length !== messageCount) {
|
|
808
|
+
ctx.logger.error({ infoCount: infos.length, messageCount }, 'message wire batch metadata count does not match its payload count');
|
|
789
809
|
return;
|
|
790
810
|
}
|
|
791
811
|
// Decode through the runtime facade so each payload materializes as a
|
|
@@ -808,17 +828,47 @@ export const makeEventHandlers = (ctx, callbacks) => {
|
|
|
808
828
|
ctx.logger.warn({ err, index }, 'skipping malformed wire message');
|
|
809
829
|
}
|
|
810
830
|
}
|
|
811
|
-
dispatchCanonicalBatch(ctx, dispatchCtx, messageCount, index => adaptBridgeMessageWire(messages[index],
|
|
831
|
+
dispatchCanonicalBatch(ctx, dispatchCtx, messageCount, index => adaptBridgeMessageWire(messages[index], infos[index], ctx.logger));
|
|
812
832
|
};
|
|
813
833
|
const onHistorySyncBatch = (batch) => {
|
|
814
834
|
const decoded = decodeHistorySyncWireBatch(batch, ctx.logger);
|
|
815
835
|
onEvent(decoded.event);
|
|
816
836
|
return decoded.skippedConversations;
|
|
817
837
|
};
|
|
838
|
+
// Packed receipt/ack batches reconstruct the single-event wire shape and
|
|
839
|
+
// reuse the same adapter path, so transport is the only thing that changes.
|
|
840
|
+
const onReceiptBatch = (batch) => {
|
|
841
|
+
let receipts;
|
|
842
|
+
try {
|
|
843
|
+
receipts = decodeReceiptWireBatch(batch);
|
|
844
|
+
}
|
|
845
|
+
catch (err) {
|
|
846
|
+
ctx.logger.error({ err }, 'failed to decode receipt wire batch');
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
// The decoded payload matches the single-event wire shape; the union's
|
|
850
|
+
// declared type is narrower than the adapter's tolerance.
|
|
851
|
+
for (const data of receipts)
|
|
852
|
+
onEvent({ type: 'receipt', data });
|
|
853
|
+
};
|
|
854
|
+
const onServerAckBatch = (batch) => {
|
|
855
|
+
let acks;
|
|
856
|
+
try {
|
|
857
|
+
acks = decodeServerAckWireBatch(batch);
|
|
858
|
+
}
|
|
859
|
+
catch (err) {
|
|
860
|
+
ctx.logger.error({ err }, 'failed to decode server-ack wire batch');
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
for (const data of acks)
|
|
864
|
+
onEvent({ type: 'server_ack', data });
|
|
865
|
+
};
|
|
818
866
|
return {
|
|
819
867
|
onEvent,
|
|
820
868
|
onMessageBatch,
|
|
821
869
|
onHistorySyncBatch,
|
|
870
|
+
onReceiptBatch,
|
|
871
|
+
onServerAckBatch,
|
|
822
872
|
historySyncConversationTypes: CONVERSATION_HISTORY_SYNC_TYPES
|
|
823
873
|
};
|
|
824
874
|
};
|
package/lib/Utils/messages.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
+
import LongRuntime from 'long';
|
|
2
3
|
import { Readable } from 'node:stream';
|
|
3
4
|
import { toBridgeMediaType } from '../Compatibility/media-type.js';
|
|
4
5
|
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
@@ -261,7 +262,10 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
261
262
|
if (hasNonNullishProperty(message, 'text')) {
|
|
262
263
|
const extContent = { text: message.text };
|
|
263
264
|
let urlInfo = message.linkPreview;
|
|
264
|
-
|
|
265
|
+
// Gate on the resolver before awaiting: without one the helper resolves
|
|
266
|
+
// to undefined anyway, and every plain text send would still pay for a
|
|
267
|
+
// promise plus a microtask turn.
|
|
268
|
+
if (typeof urlInfo === 'undefined' && options.getUrlInfo) {
|
|
265
269
|
urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
|
|
266
270
|
}
|
|
267
271
|
if (urlInfo) {
|
|
@@ -546,20 +550,32 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
546
550
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
547
551
|
};
|
|
548
552
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
553
|
+
// `create` copies into a fresh instance; content built by
|
|
554
|
+
// `generateWAMessageContent` already is one, and the branches above mutate
|
|
555
|
+
// it in place regardless, so copying it again only duplicates the tree.
|
|
556
|
+
if (!(message instanceof WAProto.Message))
|
|
557
|
+
message = WAProto.Message.create(message);
|
|
558
|
+
// Direct construction instead of a `WebMessageInfo.fromObject` envelope:
|
|
559
|
+
// every value is already in its runtime type (`message` came from
|
|
560
|
+
// `Message.create` above), so the schema walk would only re-derive them.
|
|
561
|
+
// `new` keeps protobufjs parity, including the constructor's own
|
|
562
|
+
// `messageStubParameters` array. The `Long` cast mirrors the published
|
|
563
|
+
// facade shape for 64-bit fields (source-side WAProto types them as plain
|
|
564
|
+
// numbers via the bridge shapes).
|
|
565
|
+
const messageKey = new WAProto.MessageKey();
|
|
566
|
+
messageKey.remoteJid = jid;
|
|
567
|
+
messageKey.fromMe = true;
|
|
568
|
+
messageKey.id = options?.messageId || ''; // Rust generates the real message ID
|
|
569
|
+
const wm = new WAProto.WebMessageInfo();
|
|
570
|
+
wm.key = messageKey;
|
|
571
|
+
wm.message = message;
|
|
572
|
+
wm.messageTimestamp = LongRuntime.fromValue(timestamp);
|
|
573
|
+
// TODO: Add support for LIDs
|
|
574
|
+
const participant = isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined;
|
|
575
|
+
if (participant !== undefined)
|
|
576
|
+
wm.participant = participant;
|
|
577
|
+
wm.status = WAMessageStatus.PENDING;
|
|
578
|
+
return wm;
|
|
563
579
|
};
|
|
564
580
|
export const generateWAMessage = async (jid, content, options) => {
|
|
565
581
|
const contentOptions = options.messageId && options.logger
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxidezap/baileyrs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.30",
|
|
5
5
|
"description": "A Rust-powered WhatsApp Web library for JavaScript, with a Baileys-compatible API",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"long": "^5.3.2",
|
|
79
79
|
"pino": "^10.3.1",
|
|
80
80
|
"protobufjs": "^7.6.5",
|
|
81
|
-
"whatsapp-rust-bridge": "0.6.0-alpha.
|
|
81
|
+
"whatsapp-rust-bridge": "0.6.0-alpha.40"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@bufbuild/protobuf": "^2.13.0",
|