fb-messenger-e2ee 0.1.3 → 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/DOCS.md +41 -0
- package/dist/index.cjs +155 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -14
- package/dist/index.d.ts +85 -14
- package/dist/index.js +155 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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. */
|
|
@@ -245,6 +257,37 @@ interface SendMediaInput {
|
|
|
245
257
|
/** Whether E2EE audio should be sent as push-to-talk/voice. Defaults to true for sendAudio. */
|
|
246
258
|
ptt?: boolean;
|
|
247
259
|
}
|
|
260
|
+
/** A single attachment item inside a multi-attachment message. */
|
|
261
|
+
interface SendAttachmentItem {
|
|
262
|
+
data: Buffer;
|
|
263
|
+
fileName: string;
|
|
264
|
+
/** Optional MIME type; inferred from fileName extension when omitted. */
|
|
265
|
+
mimeType?: string;
|
|
266
|
+
/** Optional width/height for image or video payloads. */
|
|
267
|
+
width?: number;
|
|
268
|
+
height?: number;
|
|
269
|
+
/** Optional duration in seconds for video/audio payloads. */
|
|
270
|
+
seconds?: number;
|
|
271
|
+
/** Whether audio should be sent as push-to-talk/voice. */
|
|
272
|
+
ptt?: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Send multiple attachments in a single Messenger message.
|
|
276
|
+
*
|
|
277
|
+
* - **Non-E2EE threads**: All attachments are bundled into one FCA message,
|
|
278
|
+
* delivered as a multi-attachment post.
|
|
279
|
+
* - **E2EE threads**: Each attachment is sent as a separate encrypted E2EE
|
|
280
|
+
* media message (the protocol does not support multi-attachment in one
|
|
281
|
+
* encrypted frame), so `results` will contain one entry per attachment.
|
|
282
|
+
*/
|
|
283
|
+
interface SendMultipleMediaInput {
|
|
284
|
+
threadId: string;
|
|
285
|
+
/** Array of file attachments to send. At least one entry is required. */
|
|
286
|
+
attachments: SendAttachmentItem[];
|
|
287
|
+
/** Optional caption applied to the first attachment (or to the bundled non-E2EE message). */
|
|
288
|
+
caption?: string;
|
|
289
|
+
replyToMessageId?: string;
|
|
290
|
+
}
|
|
248
291
|
interface TypingInput {
|
|
249
292
|
threadId: string;
|
|
250
293
|
isTyping: boolean;
|
|
@@ -345,6 +388,15 @@ declare class FBClient {
|
|
|
345
388
|
sendVideo(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
346
389
|
sendAudio(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
347
390
|
sendFile(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
391
|
+
/**
|
|
392
|
+
* Send multiple files/attachments in one call.
|
|
393
|
+
*
|
|
394
|
+
* - **Non-E2EE threads**: All attachments land in a single Messenger message.
|
|
395
|
+
* - **E2EE threads**: Each attachment is sent as a separate encrypted message
|
|
396
|
+
* (E2EE does not support multi-attachment in one frame). Returns an array
|
|
397
|
+
* with one result per attachment.
|
|
398
|
+
*/
|
|
399
|
+
sendFiles(input: SendMultipleMediaInput): Promise<Record<string, unknown> | Record<string, unknown>[]>;
|
|
348
400
|
}
|
|
349
401
|
|
|
350
402
|
interface EncryptMediaResult {
|
|
@@ -709,6 +761,38 @@ declare class DeviceStore implements IdentityKeyStore, SessionStore, PreKeyStore
|
|
|
709
761
|
getPreKeyCount(): number;
|
|
710
762
|
}
|
|
711
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
|
+
|
|
712
796
|
/**
|
|
713
797
|
* Encode a ConsumerApplication text message.
|
|
714
798
|
* Field 1 = Payload { field 1 = Content { field 1 = MessageText { field 1 = text } } }
|
|
@@ -739,19 +823,6 @@ declare function encodeEditMessage(targetMessageId: string, newText: string): Bu
|
|
|
739
823
|
/** Encode a revoke (unsend) message. */
|
|
740
824
|
declare function encodeRevokeMessage(messageId: string, keyOptsOrFromMe?: MessageKeyOptions | boolean): Buffer;
|
|
741
825
|
|
|
742
|
-
/**
|
|
743
|
-
* Wrap a ConsumerApplication payload into a MessageApplication.
|
|
744
|
-
* Returns (messageApp bytes, frankingKey, frankingTag).
|
|
745
|
-
*/
|
|
746
|
-
declare function encodeMessageApplication(consumerAppBytes: Buffer, replyTo?: {
|
|
747
|
-
id: string;
|
|
748
|
-
senderJid: string;
|
|
749
|
-
}): {
|
|
750
|
-
messageApp: Buffer;
|
|
751
|
-
frankingKey: Buffer;
|
|
752
|
-
frankingTag: Buffer;
|
|
753
|
-
};
|
|
754
|
-
|
|
755
826
|
/**
|
|
756
827
|
* Encode the MessageTransport protobuf that will be fed into Signal cipher.
|
|
757
828
|
*/
|
|
@@ -907,4 +978,4 @@ interface AuthConfig {
|
|
|
907
978
|
platform: Platform;
|
|
908
979
|
}
|
|
909
980
|
|
|
910
|
-
export { type AppEnv, type Attachment, type AuthConfig, type ClientOptions, type E2EEDecryptMediaOptions, type E2EEDownloadOptions, type E2EEDownloadResult, type E2EEEditMessageInput, type E2EEEncryptMediaOptions, type E2EEEncryptMediaResult, type E2EEMessage, type E2EEMessageKind, type E2EESendTextOptions, type E2EESendTextResult, E2EEService, FBClient, type MediaUploadConfig, type MediaUploadResult, type Mention, type MessengerEvent, type MessengerEventMap, type MmsTypeStr, type Platform, type ReplyTo, type SendMediaInput, type SendMessageInput, type SendReactionInput, type SessionData, type TypingInput, type UnsendMessageInput };
|
|
981
|
+
export { type AppEnv, type Attachment, type AuthConfig, type ClientOptions, type E2EEDecryptMediaOptions, type E2EEDownloadOptions, type E2EEDownloadResult, type E2EEEditMessageInput, type E2EEEncryptMediaOptions, type E2EEEncryptMediaResult, type E2EEMessage, type E2EEMessageKind, type E2EESendTextOptions, type E2EESendTextResult, E2EEService, FBClient, type MediaUploadConfig, type MediaUploadResult, type Mention, type MessengerEvent, type MessengerEventMap, type MmsTypeStr, type Platform, type ReplyTo, type SendAttachmentItem, type SendMediaInput, type SendMessageInput, type SendMultipleMediaInput, type SendReactionInput, type SessionData, type TypingInput, type UnsendMessageInput };
|
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. */
|
|
@@ -245,6 +257,37 @@ interface SendMediaInput {
|
|
|
245
257
|
/** Whether E2EE audio should be sent as push-to-talk/voice. Defaults to true for sendAudio. */
|
|
246
258
|
ptt?: boolean;
|
|
247
259
|
}
|
|
260
|
+
/** A single attachment item inside a multi-attachment message. */
|
|
261
|
+
interface SendAttachmentItem {
|
|
262
|
+
data: Buffer;
|
|
263
|
+
fileName: string;
|
|
264
|
+
/** Optional MIME type; inferred from fileName extension when omitted. */
|
|
265
|
+
mimeType?: string;
|
|
266
|
+
/** Optional width/height for image or video payloads. */
|
|
267
|
+
width?: number;
|
|
268
|
+
height?: number;
|
|
269
|
+
/** Optional duration in seconds for video/audio payloads. */
|
|
270
|
+
seconds?: number;
|
|
271
|
+
/** Whether audio should be sent as push-to-talk/voice. */
|
|
272
|
+
ptt?: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Send multiple attachments in a single Messenger message.
|
|
276
|
+
*
|
|
277
|
+
* - **Non-E2EE threads**: All attachments are bundled into one FCA message,
|
|
278
|
+
* delivered as a multi-attachment post.
|
|
279
|
+
* - **E2EE threads**: Each attachment is sent as a separate encrypted E2EE
|
|
280
|
+
* media message (the protocol does not support multi-attachment in one
|
|
281
|
+
* encrypted frame), so `results` will contain one entry per attachment.
|
|
282
|
+
*/
|
|
283
|
+
interface SendMultipleMediaInput {
|
|
284
|
+
threadId: string;
|
|
285
|
+
/** Array of file attachments to send. At least one entry is required. */
|
|
286
|
+
attachments: SendAttachmentItem[];
|
|
287
|
+
/** Optional caption applied to the first attachment (or to the bundled non-E2EE message). */
|
|
288
|
+
caption?: string;
|
|
289
|
+
replyToMessageId?: string;
|
|
290
|
+
}
|
|
248
291
|
interface TypingInput {
|
|
249
292
|
threadId: string;
|
|
250
293
|
isTyping: boolean;
|
|
@@ -345,6 +388,15 @@ declare class FBClient {
|
|
|
345
388
|
sendVideo(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
346
389
|
sendAudio(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
347
390
|
sendFile(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
391
|
+
/**
|
|
392
|
+
* Send multiple files/attachments in one call.
|
|
393
|
+
*
|
|
394
|
+
* - **Non-E2EE threads**: All attachments land in a single Messenger message.
|
|
395
|
+
* - **E2EE threads**: Each attachment is sent as a separate encrypted message
|
|
396
|
+
* (E2EE does not support multi-attachment in one frame). Returns an array
|
|
397
|
+
* with one result per attachment.
|
|
398
|
+
*/
|
|
399
|
+
sendFiles(input: SendMultipleMediaInput): Promise<Record<string, unknown> | Record<string, unknown>[]>;
|
|
348
400
|
}
|
|
349
401
|
|
|
350
402
|
interface EncryptMediaResult {
|
|
@@ -709,6 +761,38 @@ declare class DeviceStore implements IdentityKeyStore, SessionStore, PreKeyStore
|
|
|
709
761
|
getPreKeyCount(): number;
|
|
710
762
|
}
|
|
711
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
|
+
|
|
712
796
|
/**
|
|
713
797
|
* Encode a ConsumerApplication text message.
|
|
714
798
|
* Field 1 = Payload { field 1 = Content { field 1 = MessageText { field 1 = text } } }
|
|
@@ -739,19 +823,6 @@ declare function encodeEditMessage(targetMessageId: string, newText: string): Bu
|
|
|
739
823
|
/** Encode a revoke (unsend) message. */
|
|
740
824
|
declare function encodeRevokeMessage(messageId: string, keyOptsOrFromMe?: MessageKeyOptions | boolean): Buffer;
|
|
741
825
|
|
|
742
|
-
/**
|
|
743
|
-
* Wrap a ConsumerApplication payload into a MessageApplication.
|
|
744
|
-
* Returns (messageApp bytes, frankingKey, frankingTag).
|
|
745
|
-
*/
|
|
746
|
-
declare function encodeMessageApplication(consumerAppBytes: Buffer, replyTo?: {
|
|
747
|
-
id: string;
|
|
748
|
-
senderJid: string;
|
|
749
|
-
}): {
|
|
750
|
-
messageApp: Buffer;
|
|
751
|
-
frankingKey: Buffer;
|
|
752
|
-
frankingTag: Buffer;
|
|
753
|
-
};
|
|
754
|
-
|
|
755
826
|
/**
|
|
756
827
|
* Encode the MessageTransport protobuf that will be fed into Signal cipher.
|
|
757
828
|
*/
|
|
@@ -907,4 +978,4 @@ interface AuthConfig {
|
|
|
907
978
|
platform: Platform;
|
|
908
979
|
}
|
|
909
980
|
|
|
910
|
-
export { type AppEnv, type Attachment, type AuthConfig, type ClientOptions, type E2EEDecryptMediaOptions, type E2EEDownloadOptions, type E2EEDownloadResult, type E2EEEditMessageInput, type E2EEEncryptMediaOptions, type E2EEEncryptMediaResult, type E2EEMessage, type E2EEMessageKind, type E2EESendTextOptions, type E2EESendTextResult, E2EEService, FBClient, type MediaUploadConfig, type MediaUploadResult, type Mention, type MessengerEvent, type MessengerEventMap, type MmsTypeStr, type Platform, type ReplyTo, type SendMediaInput, type SendMessageInput, type SendReactionInput, type SessionData, type TypingInput, type UnsendMessageInput };
|
|
981
|
+
export { type AppEnv, type Attachment, type AuthConfig, type ClientOptions, type E2EEDecryptMediaOptions, type E2EEDownloadOptions, type E2EEDownloadResult, type E2EEEditMessageInput, type E2EEEncryptMediaOptions, type E2EEEncryptMediaResult, type E2EEMessage, type E2EEMessageKind, type E2EESendTextOptions, type E2EESendTextResult, E2EEService, FBClient, type MediaUploadConfig, type MediaUploadResult, type Mention, type MessengerEvent, type MessengerEventMap, type MmsTypeStr, type Platform, type ReplyTo, type SendAttachmentItem, type SendMediaInput, type SendMessageInput, type SendMultipleMediaInput, type SendReactionInput, type SessionData, type TypingInput, type UnsendMessageInput };
|
package/dist/index.js
CHANGED
|
@@ -3465,6 +3465,30 @@ var FacebookGatewayService = class {
|
|
|
3465
3465
|
);
|
|
3466
3466
|
return response ?? {};
|
|
3467
3467
|
}
|
|
3468
|
+
/**
|
|
3469
|
+
* Send multiple attachments in a single FCA message.
|
|
3470
|
+
* FCA-unofficial accepts an array in `attachment` field, which bundles all
|
|
3471
|
+
* files into one Messenger message on the wire.
|
|
3472
|
+
*/
|
|
3473
|
+
async sendMultipleAttachmentsMessage(api, input) {
|
|
3474
|
+
if (input.attachments.length === 0) {
|
|
3475
|
+
throw new Error("sendMultipleAttachmentsMessage requires at least one attachment");
|
|
3476
|
+
}
|
|
3477
|
+
const streams = input.attachments.map(({ data, fileName }) => {
|
|
3478
|
+
const stream = Readable.from(data);
|
|
3479
|
+
Object.assign(stream, { path: fileName });
|
|
3480
|
+
return stream;
|
|
3481
|
+
});
|
|
3482
|
+
const payload = {
|
|
3483
|
+
body: input.caption ?? "",
|
|
3484
|
+
// FCA-unofficial accepts a single Readable or an array of Readables
|
|
3485
|
+
attachment: streams.length === 1 ? streams[0] : streams
|
|
3486
|
+
};
|
|
3487
|
+
const response = await Promise.resolve(
|
|
3488
|
+
api.sendMessage(payload, input.threadId, void 0, input.replyToMessageId)
|
|
3489
|
+
);
|
|
3490
|
+
return response ?? {};
|
|
3491
|
+
}
|
|
3468
3492
|
async sendReaction(api, messageId, reaction) {
|
|
3469
3493
|
if (!api.setMessageReaction) {
|
|
3470
3494
|
throw new Error("setMessageReaction is not available in fca-unofficial");
|
|
@@ -3641,6 +3665,13 @@ var MediaService = class {
|
|
|
3641
3665
|
replyToMessageId: input.replyToMessageId
|
|
3642
3666
|
});
|
|
3643
3667
|
}
|
|
3668
|
+
/**
|
|
3669
|
+
* Send multiple attachments bundled into a single FCA message.
|
|
3670
|
+
* Non-E2EE only — for E2EE threads use the controller which sends them sequentially.
|
|
3671
|
+
*/
|
|
3672
|
+
async sendFiles(api, input) {
|
|
3673
|
+
return this.gateway.sendMultipleAttachmentsMessage(api, input);
|
|
3674
|
+
}
|
|
3644
3675
|
async sendSticker(api, input) {
|
|
3645
3676
|
return this.gateway.sendStickerMessage(api, {
|
|
3646
3677
|
threadId: input.threadId,
|
|
@@ -3938,8 +3969,8 @@ init_esm_shims();
|
|
|
3938
3969
|
var MessageBuilder = class {
|
|
3939
3970
|
content;
|
|
3940
3971
|
replyTo;
|
|
3941
|
-
setReply(
|
|
3942
|
-
this.replyTo =
|
|
3972
|
+
setReply(replyTo) {
|
|
3973
|
+
this.replyTo = replyTo;
|
|
3943
3974
|
return this;
|
|
3944
3975
|
}
|
|
3945
3976
|
getReply() {
|
|
@@ -4163,7 +4194,7 @@ function encodeMessageApplication(consumerAppBytes, replyTo) {
|
|
|
4163
4194
|
const appPayload = new ProtoWriter().bytes(4, payloadSubProto).build();
|
|
4164
4195
|
let metadataWriter = new ProtoWriter().bytes(8, frankingKey).varint(9, 0);
|
|
4165
4196
|
if (replyTo) {
|
|
4166
|
-
const quoted = new ProtoWriter().string(1, replyTo.
|
|
4197
|
+
const quoted = new ProtoWriter().string(1, replyTo.messageId).string(2, replyTo.chatJid).string(3, replyTo.senderJid).build();
|
|
4167
4198
|
metadataWriter = metadataWriter.bytes(10, quoted);
|
|
4168
4199
|
}
|
|
4169
4200
|
const metadata = metadataWriter.build();
|
|
@@ -5566,7 +5597,16 @@ var E2EEClient = class {
|
|
|
5566
5597
|
async buildDMTextFanoutPayloads(opts) {
|
|
5567
5598
|
const builder = new MessageBuilder().setText(opts.text);
|
|
5568
5599
|
if (opts.replyToId && opts.replyToSenderJid) {
|
|
5569
|
-
|
|
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);
|
|
5570
5610
|
}
|
|
5571
5611
|
const consumerApp = builder.build();
|
|
5572
5612
|
const { messageApp, frankingTag } = encodeMessageApplication(consumerApp, builder.getReply());
|
|
@@ -5601,7 +5641,14 @@ var E2EEClient = class {
|
|
|
5601
5641
|
async encryptGroupText(groupJid, selfJid, text, messageId, replyToId, replyToSenderJid) {
|
|
5602
5642
|
const builder = new MessageBuilder().setText(text);
|
|
5603
5643
|
if (replyToId && replyToSenderJid) {
|
|
5604
|
-
|
|
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);
|
|
5605
5652
|
}
|
|
5606
5653
|
const consumerApp = builder.build();
|
|
5607
5654
|
const { messageApp, frankingTag } = encodeMessageApplication(consumerApp, builder.getReply());
|
|
@@ -8210,15 +8257,25 @@ var ClientController = class {
|
|
|
8210
8257
|
if (this.e2eeConnected && isE2EE) {
|
|
8211
8258
|
let messageId;
|
|
8212
8259
|
if (isGroup) {
|
|
8213
|
-
messageId = await this.sendE2EEGroupText(
|
|
8260
|
+
messageId = await this.sendE2EEGroupText(
|
|
8261
|
+
input.threadId,
|
|
8262
|
+
input.text,
|
|
8263
|
+
input.replyToMessageId,
|
|
8264
|
+
input.replyToSenderJid
|
|
8265
|
+
);
|
|
8214
8266
|
} else {
|
|
8215
|
-
messageId = await this.sendE2EEText(
|
|
8267
|
+
messageId = await this.sendE2EEText(
|
|
8268
|
+
input.threadId,
|
|
8269
|
+
input.text,
|
|
8270
|
+
input.replyToMessageId,
|
|
8271
|
+
input.replyToSenderJid
|
|
8272
|
+
);
|
|
8216
8273
|
}
|
|
8217
8274
|
return { messageId, timestampMs: now() };
|
|
8218
8275
|
}
|
|
8219
8276
|
return this.messagingService.sendText(this.requireApi(), input);
|
|
8220
8277
|
}
|
|
8221
|
-
async sendE2EEText(threadId, text, replyToMessageId) {
|
|
8278
|
+
async sendE2EEText(threadId, text, replyToMessageId, replyToSenderJid) {
|
|
8222
8279
|
if (!this.e2eeSocket) throw new Error("E2EE not connected");
|
|
8223
8280
|
const e2eeClient = this.e2eeService.getClient();
|
|
8224
8281
|
const selfJid = this.getSelfE2EEJid();
|
|
@@ -8230,7 +8287,9 @@ var ClientController = class {
|
|
|
8230
8287
|
text,
|
|
8231
8288
|
isGroup: false,
|
|
8232
8289
|
replyToId: replyToMessageId,
|
|
8233
|
-
|
|
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
|
|
8234
8293
|
});
|
|
8235
8294
|
const participantNodes = [];
|
|
8236
8295
|
const deviceJids = uniqueJids(await this.e2eeHandler.getDeviceList([toJid, toBareMessengerJid(selfJid)]));
|
|
@@ -8276,7 +8335,7 @@ var ClientController = class {
|
|
|
8276
8335
|
logger.info("ClientController", `E2EE DM message sent to ${toJid} with ${participantNodes.length} devices`);
|
|
8277
8336
|
return messageId;
|
|
8278
8337
|
}
|
|
8279
|
-
async sendE2EEGroupText(groupJid, text, replyToMessageId) {
|
|
8338
|
+
async sendE2EEGroupText(groupJid, text, replyToMessageId, replyToSenderJid) {
|
|
8280
8339
|
if (!this.e2eeSocket) throw new Error("E2EE not connected");
|
|
8281
8340
|
const e2eeClient = this.e2eeService.getClient();
|
|
8282
8341
|
const selfJid = this.getSelfE2EEJid();
|
|
@@ -8292,7 +8351,8 @@ var ClientController = class {
|
|
|
8292
8351
|
text,
|
|
8293
8352
|
messageId,
|
|
8294
8353
|
replyToMessageId,
|
|
8295
|
-
|
|
8354
|
+
replyToSenderJid
|
|
8355
|
+
// pass through — undefined when not replying
|
|
8296
8356
|
);
|
|
8297
8357
|
const participantNodes = [];
|
|
8298
8358
|
for (const deviceJid of deviceJids) {
|
|
@@ -8560,7 +8620,7 @@ var ClientController = class {
|
|
|
8560
8620
|
async markAsRead(input) {
|
|
8561
8621
|
await this.messagingService.markAsRead(this.requireApi(), input);
|
|
8562
8622
|
}
|
|
8563
|
-
//
|
|
8623
|
+
// E2EE Media Upload Config
|
|
8564
8624
|
async getE2EEMediaUploadConfig() {
|
|
8565
8625
|
if (this.e2eeUploadConfig && !this.isMediaUploadConfigExpired(this.e2eeUploadConfig)) {
|
|
8566
8626
|
return this.e2eeUploadConfig;
|
|
@@ -8632,7 +8692,11 @@ var ClientController = class {
|
|
|
8632
8692
|
const consumerApp = buildMessage(media.mediaFields);
|
|
8633
8693
|
const { messageApp, frankingTag } = e2eeClient.buildMessageApplication(
|
|
8634
8694
|
consumerApp,
|
|
8635
|
-
input.replyToMessageId ? {
|
|
8695
|
+
input.replyToMessageId ? {
|
|
8696
|
+
messageId: input.replyToMessageId,
|
|
8697
|
+
chatJid: toJid,
|
|
8698
|
+
senderJid: input.replyToSenderJid ?? toJid
|
|
8699
|
+
} : void 0
|
|
8636
8700
|
);
|
|
8637
8701
|
const devicePayload = e2eeClient.buildMessageTransport({ messageApp });
|
|
8638
8702
|
const selfDevicePayload = e2eeClient.buildMessageTransport({
|
|
@@ -8707,6 +8771,62 @@ var ClientController = class {
|
|
|
8707
8771
|
}
|
|
8708
8772
|
return this.mediaService.sendFile(this.requireApi(), input);
|
|
8709
8773
|
}
|
|
8774
|
+
/**
|
|
8775
|
+
* Send multiple files/attachments.
|
|
8776
|
+
*
|
|
8777
|
+
* - **Non-E2EE threads**: All files are bundled into a single FCA message.
|
|
8778
|
+
* - **E2EE threads**: Files are sent as separate sequential encrypted E2EE
|
|
8779
|
+
* media messages (the protocol does not support multi-attachment in one
|
|
8780
|
+
* encrypted frame). Returns an array with one result per attachment.
|
|
8781
|
+
*/
|
|
8782
|
+
async sendFiles(input) {
|
|
8783
|
+
if (input.attachments.length === 0) {
|
|
8784
|
+
throw new Error("sendFiles requires at least one attachment");
|
|
8785
|
+
}
|
|
8786
|
+
if (this.e2eeConnected && this.isE2EEThreadId(input.threadId)) {
|
|
8787
|
+
const results = [];
|
|
8788
|
+
for (let i = 0; i < input.attachments.length; i++) {
|
|
8789
|
+
const att = input.attachments[i];
|
|
8790
|
+
const mediaInput = {
|
|
8791
|
+
threadId: input.threadId,
|
|
8792
|
+
data: att.data,
|
|
8793
|
+
fileName: att.fileName,
|
|
8794
|
+
mimeType: att.mimeType,
|
|
8795
|
+
// Caption on first attachment only
|
|
8796
|
+
caption: i === 0 ? input.caption : void 0,
|
|
8797
|
+
replyToMessageId: i === 0 ? input.replyToMessageId : void 0,
|
|
8798
|
+
width: att.width,
|
|
8799
|
+
height: att.height,
|
|
8800
|
+
seconds: att.seconds,
|
|
8801
|
+
ptt: att.ptt
|
|
8802
|
+
};
|
|
8803
|
+
const mediaType = inferMediaType(att.fileName, att.mimeType);
|
|
8804
|
+
let result;
|
|
8805
|
+
switch (mediaType) {
|
|
8806
|
+
case "image":
|
|
8807
|
+
result = await this.sendE2EEImage(mediaInput);
|
|
8808
|
+
break;
|
|
8809
|
+
case "video":
|
|
8810
|
+
result = await this.sendE2EEVideo(mediaInput);
|
|
8811
|
+
break;
|
|
8812
|
+
case "audio":
|
|
8813
|
+
result = await this.sendE2EEAudio(mediaInput);
|
|
8814
|
+
break;
|
|
8815
|
+
default:
|
|
8816
|
+
result = await this.sendE2EEFile(mediaInput);
|
|
8817
|
+
break;
|
|
8818
|
+
}
|
|
8819
|
+
results.push(result);
|
|
8820
|
+
}
|
|
8821
|
+
return results;
|
|
8822
|
+
}
|
|
8823
|
+
return this.mediaService.sendFiles(this.requireApi(), {
|
|
8824
|
+
threadId: input.threadId,
|
|
8825
|
+
attachments: input.attachments.map((a) => ({ data: a.data, fileName: a.fileName })),
|
|
8826
|
+
caption: input.caption,
|
|
8827
|
+
replyToMessageId: input.replyToMessageId
|
|
8828
|
+
});
|
|
8829
|
+
}
|
|
8710
8830
|
async sendSticker(input) {
|
|
8711
8831
|
return this.mediaService.sendSticker(this.requireApi(), input);
|
|
8712
8832
|
}
|
|
@@ -8746,7 +8866,6 @@ var ClientController = class {
|
|
|
8746
8866
|
async createPoll(input) {
|
|
8747
8867
|
await this.threadService.createPoll(this.requireApi(), input);
|
|
8748
8868
|
}
|
|
8749
|
-
// editMessage is now handled by the E2EE-aware method above (routes E2EE → sendE2EEEdit, else → threadService.editMessage).
|
|
8750
8869
|
async addGroupMember(input) {
|
|
8751
8870
|
await this.threadService.addGroupMember(this.requireApi(), input);
|
|
8752
8871
|
}
|
|
@@ -8764,6 +8883,17 @@ var ClientController = class {
|
|
|
8764
8883
|
return this.api;
|
|
8765
8884
|
}
|
|
8766
8885
|
};
|
|
8886
|
+
function inferMediaType(fileName, mimeType) {
|
|
8887
|
+
const mime = (mimeType ?? "").toLowerCase();
|
|
8888
|
+
if (mime.startsWith("image/")) return "image";
|
|
8889
|
+
if (mime.startsWith("video/")) return "video";
|
|
8890
|
+
if (mime.startsWith("audio/")) return "audio";
|
|
8891
|
+
const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
8892
|
+
if (["jpg", "jpeg", "png", "gif", "webp", "heic", "heif", "bmp"].includes(ext)) return "image";
|
|
8893
|
+
if (["mp4", "mov", "avi", "mkv", "webm", "3gp", "m4v"].includes(ext)) return "video";
|
|
8894
|
+
if (["mp3", "ogg", "aac", "flac", "wav", "m4a", "opus"].includes(ext)) return "audio";
|
|
8895
|
+
return "document";
|
|
8896
|
+
}
|
|
8767
8897
|
|
|
8768
8898
|
// src/repositories/session.repository.ts
|
|
8769
8899
|
init_esm_shims();
|
|
@@ -9025,6 +9155,17 @@ var FBClient = class {
|
|
|
9025
9155
|
async sendFile(input) {
|
|
9026
9156
|
return this.controller.sendFile(input);
|
|
9027
9157
|
}
|
|
9158
|
+
/**
|
|
9159
|
+
* Send multiple files/attachments in one call.
|
|
9160
|
+
*
|
|
9161
|
+
* - **Non-E2EE threads**: All attachments land in a single Messenger message.
|
|
9162
|
+
* - **E2EE threads**: Each attachment is sent as a separate encrypted message
|
|
9163
|
+
* (E2EE does not support multi-attachment in one frame). Returns an array
|
|
9164
|
+
* with one result per attachment.
|
|
9165
|
+
*/
|
|
9166
|
+
async sendFiles(input) {
|
|
9167
|
+
return this.controller.sendFiles(input);
|
|
9168
|
+
}
|
|
9028
9169
|
};
|
|
9029
9170
|
export {
|
|
9030
9171
|
E2EEService,
|