fb-messenger-e2ee 0.1.4 → 0.1.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/index.d.cts CHANGED
@@ -225,6 +225,13 @@ interface SendMessageInput {
225
225
  threadId: string;
226
226
  text: string;
227
227
  replyToMessageId?: string;
228
+ /**
229
+ * JID of the original message sender for E2EE reply (QuotedMessage.participant field).
230
+ * - For DM: the bare JID of the peer (e.g. "123456789.0@msgr"); if omitted, defaults to the peer's JID.
231
+ * - For group: the full member JID who sent the original message (e.g. "123456789.160@msgr"); required
232
+ * for the server to correctly thread the reply in the group conversation.
233
+ */
234
+ replyToSenderJid?: string;
228
235
  }
229
236
  interface SendMediaInput {
230
237
  threadId: string;
@@ -234,6 +241,11 @@ interface SendMediaInput {
234
241
  mimeType?: string;
235
242
  caption?: string;
236
243
  replyToMessageId?: string;
244
+ /**
245
+ * JID of the original message sender for E2EE reply (QuotedMessage.participant field).
246
+ * See SendMessageInput.replyToSenderJid for details.
247
+ */
248
+ replyToSenderJid?: string;
237
249
  /** Optional media width in pixels for E2EE image/video/sticker payloads. */
238
250
  width?: number;
239
251
  /** Optional media height in pixels for E2EE image/video/sticker payloads. */
@@ -749,6 +761,38 @@ declare class DeviceStore implements IdentityKeyStore, SessionStore, PreKeyStore
749
761
  getPreKeyCount(): number;
750
762
  }
751
763
 
764
+ /**
765
+ * Reply-to (quoted message) metadata for MessageApplication.Metadata.QuotedMessage.
766
+ *
767
+ * Proto schema (WAMsgApplication.proto):
768
+ * message QuotedMessage {
769
+ * optional string stanzaID = 1; // message ID of the quoted message
770
+ * optional string remoteJID = 2; // chat JID (thread) that contains the quoted message
771
+ * optional string participant = 3; // sender JID of the quoted message (required for groups)
772
+ * }
773
+ */
774
+ interface ReplyToMeta {
775
+ /** Message ID (stanzaID) of the quoted message. */
776
+ messageId: string;
777
+ /** Chat JID (remoteJID) — the thread where the quoted message lives. */
778
+ chatJid: string;
779
+ /**
780
+ * Sender JID (participant) of the quoted message.
781
+ * - For DM: same as chatJid (the peer's bare JID).
782
+ * - For group: the specific member JID who sent the original message.
783
+ */
784
+ senderJid: string;
785
+ }
786
+ /**
787
+ * Wrap a ConsumerApplication payload into a MessageApplication.
788
+ * Returns (messageApp bytes, frankingKey, frankingTag).
789
+ */
790
+ declare function encodeMessageApplication(consumerAppBytes: Buffer, replyTo?: ReplyToMeta): {
791
+ messageApp: Buffer;
792
+ frankingKey: Buffer;
793
+ frankingTag: Buffer;
794
+ };
795
+
752
796
  /**
753
797
  * Encode a ConsumerApplication text message.
754
798
  * Field 1 = Payload { field 1 = Content { field 1 = MessageText { field 1 = text } } }
@@ -779,19 +823,6 @@ declare function encodeEditMessage(targetMessageId: string, newText: string): Bu
779
823
  /** Encode a revoke (unsend) message. */
780
824
  declare function encodeRevokeMessage(messageId: string, keyOptsOrFromMe?: MessageKeyOptions | boolean): Buffer;
781
825
 
782
- /**
783
- * Wrap a ConsumerApplication payload into a MessageApplication.
784
- * Returns (messageApp bytes, frankingKey, frankingTag).
785
- */
786
- declare function encodeMessageApplication(consumerAppBytes: Buffer, replyTo?: {
787
- id: string;
788
- senderJid: string;
789
- }): {
790
- messageApp: Buffer;
791
- frankingKey: Buffer;
792
- frankingTag: Buffer;
793
- };
794
-
795
826
  /**
796
827
  * Encode the MessageTransport protobuf that will be fed into Signal cipher.
797
828
  */
package/dist/index.d.ts CHANGED
@@ -225,6 +225,13 @@ interface SendMessageInput {
225
225
  threadId: string;
226
226
  text: string;
227
227
  replyToMessageId?: string;
228
+ /**
229
+ * JID of the original message sender for E2EE reply (QuotedMessage.participant field).
230
+ * - For DM: the bare JID of the peer (e.g. "123456789.0@msgr"); if omitted, defaults to the peer's JID.
231
+ * - For group: the full member JID who sent the original message (e.g. "123456789.160@msgr"); required
232
+ * for the server to correctly thread the reply in the group conversation.
233
+ */
234
+ replyToSenderJid?: string;
228
235
  }
229
236
  interface SendMediaInput {
230
237
  threadId: string;
@@ -234,6 +241,11 @@ interface SendMediaInput {
234
241
  mimeType?: string;
235
242
  caption?: string;
236
243
  replyToMessageId?: string;
244
+ /**
245
+ * JID of the original message sender for E2EE reply (QuotedMessage.participant field).
246
+ * See SendMessageInput.replyToSenderJid for details.
247
+ */
248
+ replyToSenderJid?: string;
237
249
  /** Optional media width in pixels for E2EE image/video/sticker payloads. */
238
250
  width?: number;
239
251
  /** Optional media height in pixels for E2EE image/video/sticker payloads. */
@@ -749,6 +761,38 @@ declare class DeviceStore implements IdentityKeyStore, SessionStore, PreKeyStore
749
761
  getPreKeyCount(): number;
750
762
  }
751
763
 
764
+ /**
765
+ * Reply-to (quoted message) metadata for MessageApplication.Metadata.QuotedMessage.
766
+ *
767
+ * Proto schema (WAMsgApplication.proto):
768
+ * message QuotedMessage {
769
+ * optional string stanzaID = 1; // message ID of the quoted message
770
+ * optional string remoteJID = 2; // chat JID (thread) that contains the quoted message
771
+ * optional string participant = 3; // sender JID of the quoted message (required for groups)
772
+ * }
773
+ */
774
+ interface ReplyToMeta {
775
+ /** Message ID (stanzaID) of the quoted message. */
776
+ messageId: string;
777
+ /** Chat JID (remoteJID) — the thread where the quoted message lives. */
778
+ chatJid: string;
779
+ /**
780
+ * Sender JID (participant) of the quoted message.
781
+ * - For DM: same as chatJid (the peer's bare JID).
782
+ * - For group: the specific member JID who sent the original message.
783
+ */
784
+ senderJid: string;
785
+ }
786
+ /**
787
+ * Wrap a ConsumerApplication payload into a MessageApplication.
788
+ * Returns (messageApp bytes, frankingKey, frankingTag).
789
+ */
790
+ declare function encodeMessageApplication(consumerAppBytes: Buffer, replyTo?: ReplyToMeta): {
791
+ messageApp: Buffer;
792
+ frankingKey: Buffer;
793
+ frankingTag: Buffer;
794
+ };
795
+
752
796
  /**
753
797
  * Encode a ConsumerApplication text message.
754
798
  * Field 1 = Payload { field 1 = Content { field 1 = MessageText { field 1 = text } } }
@@ -779,19 +823,6 @@ declare function encodeEditMessage(targetMessageId: string, newText: string): Bu
779
823
  /** Encode a revoke (unsend) message. */
780
824
  declare function encodeRevokeMessage(messageId: string, keyOptsOrFromMe?: MessageKeyOptions | boolean): Buffer;
781
825
 
782
- /**
783
- * Wrap a ConsumerApplication payload into a MessageApplication.
784
- * Returns (messageApp bytes, frankingKey, frankingTag).
785
- */
786
- declare function encodeMessageApplication(consumerAppBytes: Buffer, replyTo?: {
787
- id: string;
788
- senderJid: string;
789
- }): {
790
- messageApp: Buffer;
791
- frankingKey: Buffer;
792
- frankingTag: Buffer;
793
- };
794
-
795
826
  /**
796
827
  * Encode the MessageTransport protobuf that will be fed into Signal cipher.
797
828
  */
package/dist/index.js CHANGED
@@ -3969,8 +3969,8 @@ init_esm_shims();
3969
3969
  var MessageBuilder = class {
3970
3970
  content;
3971
3971
  replyTo;
3972
- setReply(id, senderJid) {
3973
- this.replyTo = { id, senderJid };
3972
+ setReply(replyTo) {
3973
+ this.replyTo = replyTo;
3974
3974
  return this;
3975
3975
  }
3976
3976
  getReply() {
@@ -4194,7 +4194,7 @@ function encodeMessageApplication(consumerAppBytes, replyTo) {
4194
4194
  const appPayload = new ProtoWriter().bytes(4, payloadSubProto).build();
4195
4195
  let metadataWriter = new ProtoWriter().bytes(8, frankingKey).varint(9, 0);
4196
4196
  if (replyTo) {
4197
- const quoted = new ProtoWriter().string(1, replyTo.id).string(2, replyTo.senderJid).build();
4197
+ const quoted = new ProtoWriter().string(1, replyTo.messageId).string(2, replyTo.chatJid).string(3, replyTo.senderJid).build();
4198
4198
  metadataWriter = metadataWriter.bytes(10, quoted);
4199
4199
  }
4200
4200
  const metadata = metadataWriter.build();
@@ -5597,7 +5597,16 @@ var E2EEClient = class {
5597
5597
  async buildDMTextFanoutPayloads(opts) {
5598
5598
  const builder = new MessageBuilder().setText(opts.text);
5599
5599
  if (opts.replyToId && opts.replyToSenderJid) {
5600
- builder.setReply(opts.replyToId, opts.replyToSenderJid);
5600
+ const replyTo = {
5601
+ messageId: opts.replyToId,
5602
+ // chatJid = remoteJID: the thread/chat that holds the quoted message
5603
+ // For DM, this is the peer's bare JID (toJid).
5604
+ chatJid: opts.toJid,
5605
+ // senderJid = participant: who sent the original message.
5606
+ // Caller must supply the correct sender JID via replyToSenderJid.
5607
+ senderJid: opts.replyToSenderJid
5608
+ };
5609
+ builder.setReply(replyTo);
5601
5610
  }
5602
5611
  const consumerApp = builder.build();
5603
5612
  const { messageApp, frankingTag } = encodeMessageApplication(consumerApp, builder.getReply());
@@ -5632,7 +5641,14 @@ var E2EEClient = class {
5632
5641
  async encryptGroupText(groupJid, selfJid, text, messageId, replyToId, replyToSenderJid) {
5633
5642
  const builder = new MessageBuilder().setText(text);
5634
5643
  if (replyToId && replyToSenderJid) {
5635
- builder.setReply(replyToId, replyToSenderJid);
5644
+ const replyTo = {
5645
+ messageId: replyToId,
5646
+ // chatJid = remoteJID: for a group message, this is the group JID
5647
+ chatJid: groupJid,
5648
+ // participant: the specific member who sent the original message
5649
+ senderJid: replyToSenderJid
5650
+ };
5651
+ builder.setReply(replyTo);
5636
5652
  }
5637
5653
  const consumerApp = builder.build();
5638
5654
  const { messageApp, frankingTag } = encodeMessageApplication(consumerApp, builder.getReply());
@@ -8241,15 +8257,25 @@ var ClientController = class {
8241
8257
  if (this.e2eeConnected && isE2EE) {
8242
8258
  let messageId;
8243
8259
  if (isGroup) {
8244
- messageId = await this.sendE2EEGroupText(input.threadId, input.text, input.replyToMessageId);
8260
+ messageId = await this.sendE2EEGroupText(
8261
+ input.threadId,
8262
+ input.text,
8263
+ input.replyToMessageId,
8264
+ input.replyToSenderJid
8265
+ );
8245
8266
  } else {
8246
- messageId = await this.sendE2EEText(input.threadId, input.text, input.replyToMessageId);
8267
+ messageId = await this.sendE2EEText(
8268
+ input.threadId,
8269
+ input.text,
8270
+ input.replyToMessageId,
8271
+ input.replyToSenderJid
8272
+ );
8247
8273
  }
8248
8274
  return { messageId, timestampMs: now() };
8249
8275
  }
8250
8276
  return this.messagingService.sendText(this.requireApi(), input);
8251
8277
  }
8252
- async sendE2EEText(threadId, text, replyToMessageId) {
8278
+ async sendE2EEText(threadId, text, replyToMessageId, replyToSenderJid) {
8253
8279
  if (!this.e2eeSocket) throw new Error("E2EE not connected");
8254
8280
  const e2eeClient = this.e2eeService.getClient();
8255
8281
  const selfJid = this.getSelfE2EEJid();
@@ -8261,7 +8287,9 @@ var ClientController = class {
8261
8287
  text,
8262
8288
  isGroup: false,
8263
8289
  replyToId: replyToMessageId,
8264
- replyToSenderJid: replyToMessageId ? toJid : void 0
8290
+ // For DM: participant = sender of original msg. If caller knows who sent it, use that;
8291
+ // otherwise fall back to the peer's JID (correct for messages they sent to us).
8292
+ replyToSenderJid: replyToMessageId ? replyToSenderJid ?? toJid : void 0
8265
8293
  });
8266
8294
  const participantNodes = [];
8267
8295
  const deviceJids = uniqueJids(await this.e2eeHandler.getDeviceList([toJid, toBareMessengerJid(selfJid)]));
@@ -8307,7 +8335,7 @@ var ClientController = class {
8307
8335
  logger.info("ClientController", `E2EE DM message sent to ${toJid} with ${participantNodes.length} devices`);
8308
8336
  return messageId;
8309
8337
  }
8310
- async sendE2EEGroupText(groupJid, text, replyToMessageId) {
8338
+ async sendE2EEGroupText(groupJid, text, replyToMessageId, replyToSenderJid) {
8311
8339
  if (!this.e2eeSocket) throw new Error("E2EE not connected");
8312
8340
  const e2eeClient = this.e2eeService.getClient();
8313
8341
  const selfJid = this.getSelfE2EEJid();
@@ -8323,7 +8351,8 @@ var ClientController = class {
8323
8351
  text,
8324
8352
  messageId,
8325
8353
  replyToMessageId,
8326
- void 0
8354
+ replyToSenderJid
8355
+ // pass through — undefined when not replying
8327
8356
  );
8328
8357
  const participantNodes = [];
8329
8358
  for (const deviceJid of deviceJids) {
@@ -8663,7 +8692,11 @@ var ClientController = class {
8663
8692
  const consumerApp = buildMessage(media.mediaFields);
8664
8693
  const { messageApp, frankingTag } = e2eeClient.buildMessageApplication(
8665
8694
  consumerApp,
8666
- input.replyToMessageId ? { id: input.replyToMessageId, senderJid: toJid } : void 0
8695
+ input.replyToMessageId ? {
8696
+ messageId: input.replyToMessageId,
8697
+ chatJid: toJid,
8698
+ senderJid: input.replyToSenderJid ?? toJid
8699
+ } : void 0
8667
8700
  );
8668
8701
  const devicePayload = e2eeClient.buildMessageTransport({ messageApp });
8669
8702
  const selfDevicePayload = e2eeClient.buildMessageTransport({