fb-messenger-e2ee 0.1.2 → 0.1.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/DOCS.md +41 -3
- package/dist/index.cjs +216 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +216 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -259,6 +259,30 @@ interface SendReactionInput {
|
|
|
259
259
|
/** Alias for senderJid for callers that prefer explicit target naming. */
|
|
260
260
|
targetSenderJid?: string;
|
|
261
261
|
}
|
|
262
|
+
interface UnsendMessageInput {
|
|
263
|
+
/** The message ID to revoke/unsend. */
|
|
264
|
+
messageId: string;
|
|
265
|
+
/**
|
|
266
|
+
* The thread ID (or E2EE chat JID) that contains the message.
|
|
267
|
+
* Required to route the revoke through the correct channel:
|
|
268
|
+
* - E2EE threads → encrypted revoke via Noise socket.
|
|
269
|
+
* - Non-E2EE threads → HTTP unsend via fca-unofficial.
|
|
270
|
+
*/
|
|
271
|
+
threadId: string;
|
|
272
|
+
/**
|
|
273
|
+
* Whether the message was sent by the current user.
|
|
274
|
+
* Defaults to true. Set to false for admin revoke of others' messages.
|
|
275
|
+
*/
|
|
276
|
+
fromMe?: boolean;
|
|
277
|
+
}
|
|
278
|
+
interface E2EEEditMessageInput {
|
|
279
|
+
/** The thread ID (or E2EE chat JID) containing the message. */
|
|
280
|
+
threadId: string;
|
|
281
|
+
/** The ID of the message to edit. */
|
|
282
|
+
messageId: string;
|
|
283
|
+
/** The new text content. */
|
|
284
|
+
newText: string;
|
|
285
|
+
}
|
|
262
286
|
|
|
263
287
|
interface ClientOptions {
|
|
264
288
|
appStatePath?: string;
|
|
@@ -314,7 +338,8 @@ declare class FBClient {
|
|
|
314
338
|
sendNoiseKeepAlive(): Promise<void>;
|
|
315
339
|
sendMessage(input: SendMessageInput): Promise<Record<string, unknown>>;
|
|
316
340
|
sendReaction(input: SendReactionInput): Promise<void>;
|
|
317
|
-
unsendMessage(
|
|
341
|
+
unsendMessage(input: UnsendMessageInput): Promise<void>;
|
|
342
|
+
editMessage(input: E2EEEditMessageInput): Promise<void>;
|
|
318
343
|
sendTyping(input: TypingInput): Promise<void>;
|
|
319
344
|
sendImage(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
320
345
|
sendVideo(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
@@ -882,4 +907,4 @@ interface AuthConfig {
|
|
|
882
907
|
platform: Platform;
|
|
883
908
|
}
|
|
884
909
|
|
|
885
|
-
export { type AppEnv, type Attachment, type AuthConfig, type ClientOptions, type E2EEDecryptMediaOptions, type E2EEDownloadOptions, type E2EEDownloadResult, 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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -259,6 +259,30 @@ interface SendReactionInput {
|
|
|
259
259
|
/** Alias for senderJid for callers that prefer explicit target naming. */
|
|
260
260
|
targetSenderJid?: string;
|
|
261
261
|
}
|
|
262
|
+
interface UnsendMessageInput {
|
|
263
|
+
/** The message ID to revoke/unsend. */
|
|
264
|
+
messageId: string;
|
|
265
|
+
/**
|
|
266
|
+
* The thread ID (or E2EE chat JID) that contains the message.
|
|
267
|
+
* Required to route the revoke through the correct channel:
|
|
268
|
+
* - E2EE threads → encrypted revoke via Noise socket.
|
|
269
|
+
* - Non-E2EE threads → HTTP unsend via fca-unofficial.
|
|
270
|
+
*/
|
|
271
|
+
threadId: string;
|
|
272
|
+
/**
|
|
273
|
+
* Whether the message was sent by the current user.
|
|
274
|
+
* Defaults to true. Set to false for admin revoke of others' messages.
|
|
275
|
+
*/
|
|
276
|
+
fromMe?: boolean;
|
|
277
|
+
}
|
|
278
|
+
interface E2EEEditMessageInput {
|
|
279
|
+
/** The thread ID (or E2EE chat JID) containing the message. */
|
|
280
|
+
threadId: string;
|
|
281
|
+
/** The ID of the message to edit. */
|
|
282
|
+
messageId: string;
|
|
283
|
+
/** The new text content. */
|
|
284
|
+
newText: string;
|
|
285
|
+
}
|
|
262
286
|
|
|
263
287
|
interface ClientOptions {
|
|
264
288
|
appStatePath?: string;
|
|
@@ -314,7 +338,8 @@ declare class FBClient {
|
|
|
314
338
|
sendNoiseKeepAlive(): Promise<void>;
|
|
315
339
|
sendMessage(input: SendMessageInput): Promise<Record<string, unknown>>;
|
|
316
340
|
sendReaction(input: SendReactionInput): Promise<void>;
|
|
317
|
-
unsendMessage(
|
|
341
|
+
unsendMessage(input: UnsendMessageInput): Promise<void>;
|
|
342
|
+
editMessage(input: E2EEEditMessageInput): Promise<void>;
|
|
318
343
|
sendTyping(input: TypingInput): Promise<void>;
|
|
319
344
|
sendImage(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
320
345
|
sendVideo(input: SendMediaInput): Promise<Record<string, unknown>>;
|
|
@@ -882,4 +907,4 @@ interface AuthConfig {
|
|
|
882
907
|
platform: Platform;
|
|
883
908
|
}
|
|
884
909
|
|
|
885
|
-
export { type AppEnv, type Attachment, type AuthConfig, type ClientOptions, type E2EEDecryptMediaOptions, type E2EEDownloadOptions, type E2EEDownloadResult, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -8208,12 +8208,13 @@ var ClientController = class {
|
|
|
8208
8208
|
const isE2EE = this.isE2EEThreadId(input.threadId);
|
|
8209
8209
|
const isGroup = input.threadId.includes("@g.us") || input.threadId.includes(".g.");
|
|
8210
8210
|
if (this.e2eeConnected && isE2EE) {
|
|
8211
|
+
let messageId;
|
|
8211
8212
|
if (isGroup) {
|
|
8212
|
-
await this.sendE2EEGroupText(input.threadId, input.text, input.replyToMessageId);
|
|
8213
|
+
messageId = await this.sendE2EEGroupText(input.threadId, input.text, input.replyToMessageId);
|
|
8213
8214
|
} else {
|
|
8214
|
-
await this.sendE2EEText(input.threadId, input.text, input.replyToMessageId);
|
|
8215
|
+
messageId = await this.sendE2EEText(input.threadId, input.text, input.replyToMessageId);
|
|
8215
8216
|
}
|
|
8216
|
-
return { messageId
|
|
8217
|
+
return { messageId, timestampMs: now() };
|
|
8217
8218
|
}
|
|
8218
8219
|
return this.messagingService.sendText(this.requireApi(), input);
|
|
8219
8220
|
}
|
|
@@ -8273,6 +8274,7 @@ var ClientController = class {
|
|
|
8273
8274
|
createdAtMs: now()
|
|
8274
8275
|
});
|
|
8275
8276
|
logger.info("ClientController", `E2EE DM message sent to ${toJid} with ${participantNodes.length} devices`);
|
|
8277
|
+
return messageId;
|
|
8276
8278
|
}
|
|
8277
8279
|
async sendE2EEGroupText(groupJid, text, replyToMessageId) {
|
|
8278
8280
|
if (!this.e2eeSocket) throw new Error("E2EE not connected");
|
|
@@ -8335,6 +8337,7 @@ var ClientController = class {
|
|
|
8335
8337
|
createdAtMs: now()
|
|
8336
8338
|
});
|
|
8337
8339
|
logger.info("ClientController", `E2EE Group message sent to ${groupJid} with ${participantNodes.length} devices`);
|
|
8340
|
+
return messageId;
|
|
8338
8341
|
}
|
|
8339
8342
|
getSelfE2EEJid() {
|
|
8340
8343
|
const device = this.activeDeviceStore?.jidDevice ?? 0;
|
|
@@ -8346,8 +8349,210 @@ var ClientController = class {
|
|
|
8346
8349
|
async sendReaction(input) {
|
|
8347
8350
|
await this.messagingService.react(this.requireApi(), input);
|
|
8348
8351
|
}
|
|
8349
|
-
|
|
8350
|
-
|
|
8352
|
+
/**
|
|
8353
|
+
* Unsend/revoke a message.
|
|
8354
|
+
*
|
|
8355
|
+
* - **E2EE threads**: Sends an encrypted `ConsumerApplication { applicationData { revoke } }`
|
|
8356
|
+
* message over the Noise socket. The `fromMe` flag in the revoke key determines
|
|
8357
|
+
* whether it is a sender revoke (`true`, default) or an admin revoke (`false`).
|
|
8358
|
+
* - **Non-E2EE threads**: Falls back to `fca-unofficial` HTTP unsend.
|
|
8359
|
+
*/
|
|
8360
|
+
async unsendMessage(input) {
|
|
8361
|
+
const isE2EE = this.isE2EEThreadId(input.threadId);
|
|
8362
|
+
if (this.e2eeConnected && isE2EE) {
|
|
8363
|
+
await this.sendE2EERevoke(input.threadId, input.messageId, input.fromMe ?? true);
|
|
8364
|
+
return;
|
|
8365
|
+
}
|
|
8366
|
+
await this.messagingService.unsend(this.requireApi(), input.messageId);
|
|
8367
|
+
}
|
|
8368
|
+
/**
|
|
8369
|
+
* Send an E2EE revoke (unsend) for a DM or group message.
|
|
8370
|
+
*
|
|
8371
|
+
* Builds a `ConsumerApplication { applicationData { revoke { key { id, fromMe } } } }`
|
|
8372
|
+
* payload, wraps it in MessageApplication + MessageTransport, then fans out to all
|
|
8373
|
+
* participant devices exactly like a normal DM or group text — but with the
|
|
8374
|
+
* correct edit attribute on the `<message>` node so the server and
|
|
8375
|
+
* receiving clients apply the correct revoke semantics.
|
|
8376
|
+
*/
|
|
8377
|
+
async sendE2EERevoke(threadId, messageId, fromMe) {
|
|
8378
|
+
if (!this.e2eeSocket) throw new Error("E2EE not connected");
|
|
8379
|
+
const e2eeClient = this.e2eeService.getClient();
|
|
8380
|
+
const selfJid = this.getSelfE2EEJid();
|
|
8381
|
+
const isGroup = threadId.includes("@g.us") || threadId.includes(".g.");
|
|
8382
|
+
const editAttr = fromMe ? "7" : "8";
|
|
8383
|
+
const toJid = isGroup ? threadId : normalizeDMThreadToJid(threadId);
|
|
8384
|
+
const consumerApp = e2eeClient.buildRevokeMessage(messageId, { fromMe, remoteJid: toJid });
|
|
8385
|
+
const { messageApp, frankingTag } = e2eeClient.buildMessageApplication(consumerApp);
|
|
8386
|
+
const newMessageId = String(BigInt(Math.floor(Math.random() * 1e15)));
|
|
8387
|
+
if (isGroup) {
|
|
8388
|
+
const memberJids = await this.e2eeHandler.getGroupParticipants(threadId);
|
|
8389
|
+
const deviceUsers = uniqueJids([...memberJids, toBareMessengerJid(selfJid)]);
|
|
8390
|
+
const deviceJids = uniqueJids(await this.e2eeHandler.getDeviceList(deviceUsers)).filter((jid) => !sameMessengerDevice(jid, selfJid));
|
|
8391
|
+
const groupResult = await e2eeClient.encryptGroupMessageApplication(
|
|
8392
|
+
threadId,
|
|
8393
|
+
selfJid,
|
|
8394
|
+
messageApp,
|
|
8395
|
+
newMessageId
|
|
8396
|
+
);
|
|
8397
|
+
const participantNodes = [];
|
|
8398
|
+
for (const deviceJid of deviceJids) {
|
|
8399
|
+
try {
|
|
8400
|
+
if (!await e2eeClient.hasSession(deviceJid)) {
|
|
8401
|
+
const bundle = await this.e2eeHandler.getPreKeyBundle(deviceJid);
|
|
8402
|
+
await e2eeClient.establishSession(deviceJid, bundle);
|
|
8403
|
+
}
|
|
8404
|
+
const payload = sameMessengerUser(deviceJid, selfJid) ? groupResult.selfDevicePayload : groupResult.devicePayload;
|
|
8405
|
+
const skdmEnc = await e2eeClient.encryptDevicePayload(deviceJid, selfJid, payload);
|
|
8406
|
+
participantNodes.push(encodeNode("to", { jid: deviceJid }, [
|
|
8407
|
+
encodeNode("enc", { v: "3", type: skdmEnc.type }, skdmEnc.ciphertext)
|
|
8408
|
+
]));
|
|
8409
|
+
} catch (err) {
|
|
8410
|
+
logger.error("ClientController", `Failed to distribute revoke SKDM to ${deviceJid}:`, err);
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
const phash = buildParticipantListHash(deviceJids);
|
|
8414
|
+
const msgNode = encodeNode("message", { to: threadId, type: "text", id: newMessageId, phash, edit: editAttr }, [
|
|
8415
|
+
encodeNode("participants", {}, participantNodes),
|
|
8416
|
+
encodeNode("franking", {}, [encodeNode("franking_tag", {}, frankingTag)]),
|
|
8417
|
+
encodeNode("trace", {}, [encodeNode("request_id", {}, Buffer.from(randomUUID3().replace(/-/g, ""), "hex"))]),
|
|
8418
|
+
encodeNode("enc", { v: "3", type: "skmsg", "decrypt-fail": "hide" }, groupResult.groupCiphertext)
|
|
8419
|
+
]);
|
|
8420
|
+
await this.e2eeSocket.sendFrame(marshal(msgNode));
|
|
8421
|
+
logger.info("ClientController", `E2EE group revoke sent for message ${messageId} in ${threadId}`);
|
|
8422
|
+
} else {
|
|
8423
|
+
const devicePayload = e2eeClient.buildMessageTransport({ messageApp });
|
|
8424
|
+
const selfDevicePayload = e2eeClient.buildMessageTransport({
|
|
8425
|
+
messageApp,
|
|
8426
|
+
dsm: { destinationJid: toJid, phash: "" }
|
|
8427
|
+
});
|
|
8428
|
+
const participantNodes = [];
|
|
8429
|
+
const deviceJids = uniqueJids(await this.e2eeHandler.getDeviceList([toJid, toBareMessengerJid(selfJid)]));
|
|
8430
|
+
for (const deviceJid of deviceJids) {
|
|
8431
|
+
if (sameMessengerDevice(deviceJid, selfJid)) continue;
|
|
8432
|
+
try {
|
|
8433
|
+
if (!await e2eeClient.hasSession(deviceJid)) {
|
|
8434
|
+
const bundle = await this.e2eeHandler.getPreKeyBundle(deviceJid);
|
|
8435
|
+
await e2eeClient.establishSession(deviceJid, bundle);
|
|
8436
|
+
}
|
|
8437
|
+
const payload = sameMessengerUser(deviceJid, selfJid) ? selfDevicePayload : devicePayload;
|
|
8438
|
+
const encrypted = await e2eeClient.encryptDevicePayload(deviceJid, selfJid, payload);
|
|
8439
|
+
participantNodes.push(encodeNode("to", { jid: deviceJid }, [
|
|
8440
|
+
encodeNode("enc", { v: "3", type: encrypted.type, "decrypt-fail": "hide" }, encrypted.ciphertext)
|
|
8441
|
+
]));
|
|
8442
|
+
} catch (err) {
|
|
8443
|
+
logger.error("ClientController", `Failed to encrypt revoke to ${deviceJid}:`, err);
|
|
8444
|
+
}
|
|
8445
|
+
}
|
|
8446
|
+
const msgNode = encodeNode("message", { to: toJid, type: "text", id: newMessageId, edit: editAttr }, [
|
|
8447
|
+
encodeNode("participants", {}, participantNodes),
|
|
8448
|
+
encodeNode("franking", {}, [encodeNode("franking_tag", {}, frankingTag)]),
|
|
8449
|
+
encodeNode("trace", {}, [encodeNode("request_id", {}, Buffer.from(randomUUID3().replace(/-/g, ""), "hex"))])
|
|
8450
|
+
]);
|
|
8451
|
+
await this.e2eeSocket.sendFrame(marshal(msgNode));
|
|
8452
|
+
logger.info("ClientController", `E2EE DM revoke sent for message ${messageId} to ${toJid}`);
|
|
8453
|
+
}
|
|
8454
|
+
}
|
|
8455
|
+
/**
|
|
8456
|
+
* Edit an E2EE message (change its text).
|
|
8457
|
+
*
|
|
8458
|
+
* - **E2EE threads**: Sends an encrypted `ConsumerApplication { content { editMessage { key, message, timestampMS } } }`
|
|
8459
|
+
* payload with the edit message attribute set.
|
|
8460
|
+
* - **Non-E2EE threads**: Falls back to `fca-unofficial` HTTP edit.
|
|
8461
|
+
*/
|
|
8462
|
+
async editMessage(input) {
|
|
8463
|
+
const isE2EE = this.isE2EEThreadId(input.threadId);
|
|
8464
|
+
if (this.e2eeConnected && isE2EE) {
|
|
8465
|
+
await this.sendE2EEEdit(input.threadId, input.messageId, input.newText);
|
|
8466
|
+
return;
|
|
8467
|
+
}
|
|
8468
|
+
await this.threadService.editMessage(this.requireApi(), {
|
|
8469
|
+
messageId: input.messageId,
|
|
8470
|
+
newText: input.newText
|
|
8471
|
+
});
|
|
8472
|
+
}
|
|
8473
|
+
/**
|
|
8474
|
+
* Send an E2EE message edit for a DM or group message.
|
|
8475
|
+
*
|
|
8476
|
+
* Builds a `ConsumerApplication { payload { content { editMessage { key, message, timestampMS } } } }`
|
|
8477
|
+
* payload, fanned out to all participant devices exactly like a normal message send.
|
|
8478
|
+
*/
|
|
8479
|
+
async sendE2EEEdit(threadId, messageId, newText) {
|
|
8480
|
+
if (!this.e2eeSocket) throw new Error("E2EE not connected");
|
|
8481
|
+
const e2eeClient = this.e2eeService.getClient();
|
|
8482
|
+
const selfJid = this.getSelfE2EEJid();
|
|
8483
|
+
const isGroup = threadId.includes("@g.us") || threadId.includes(".g.");
|
|
8484
|
+
const editAttr = "1";
|
|
8485
|
+
const toJid = isGroup ? threadId : normalizeDMThreadToJid(threadId);
|
|
8486
|
+
const consumerApp = e2eeClient.buildEditMessage(messageId, newText);
|
|
8487
|
+
const { messageApp, frankingTag } = e2eeClient.buildMessageApplication(consumerApp);
|
|
8488
|
+
const newMessageId = String(BigInt(Math.floor(Math.random() * 1e15)));
|
|
8489
|
+
if (isGroup) {
|
|
8490
|
+
const memberJids = await this.e2eeHandler.getGroupParticipants(threadId);
|
|
8491
|
+
const deviceUsers = uniqueJids([...memberJids, toBareMessengerJid(selfJid)]);
|
|
8492
|
+
const deviceJids = uniqueJids(await this.e2eeHandler.getDeviceList(deviceUsers)).filter((jid) => !sameMessengerDevice(jid, selfJid));
|
|
8493
|
+
const groupResult = await e2eeClient.encryptGroupMessageApplication(
|
|
8494
|
+
threadId,
|
|
8495
|
+
selfJid,
|
|
8496
|
+
messageApp,
|
|
8497
|
+
newMessageId
|
|
8498
|
+
);
|
|
8499
|
+
const participantNodes = [];
|
|
8500
|
+
for (const deviceJid of deviceJids) {
|
|
8501
|
+
try {
|
|
8502
|
+
if (!await e2eeClient.hasSession(deviceJid)) {
|
|
8503
|
+
const bundle = await this.e2eeHandler.getPreKeyBundle(deviceJid);
|
|
8504
|
+
await e2eeClient.establishSession(deviceJid, bundle);
|
|
8505
|
+
}
|
|
8506
|
+
const payload = sameMessengerUser(deviceJid, selfJid) ? groupResult.selfDevicePayload : groupResult.devicePayload;
|
|
8507
|
+
const skdmEnc = await e2eeClient.encryptDevicePayload(deviceJid, selfJid, payload);
|
|
8508
|
+
participantNodes.push(encodeNode("to", { jid: deviceJid }, [
|
|
8509
|
+
encodeNode("enc", { v: "3", type: skdmEnc.type }, skdmEnc.ciphertext)
|
|
8510
|
+
]));
|
|
8511
|
+
} catch (err) {
|
|
8512
|
+
logger.error("ClientController", `Failed to distribute edit SKDM to ${deviceJid}:`, err);
|
|
8513
|
+
}
|
|
8514
|
+
}
|
|
8515
|
+
const phash = buildParticipantListHash(deviceJids);
|
|
8516
|
+
const msgNode = encodeNode("message", { to: threadId, type: "text", id: newMessageId, phash, edit: editAttr }, [
|
|
8517
|
+
encodeNode("participants", {}, participantNodes),
|
|
8518
|
+
encodeNode("franking", {}, [encodeNode("franking_tag", {}, frankingTag)]),
|
|
8519
|
+
encodeNode("trace", {}, [encodeNode("request_id", {}, Buffer.from(randomUUID3().replace(/-/g, ""), "hex"))]),
|
|
8520
|
+
encodeNode("enc", { v: "3", type: "skmsg", "decrypt-fail": "hide" }, groupResult.groupCiphertext)
|
|
8521
|
+
]);
|
|
8522
|
+
await this.e2eeSocket.sendFrame(marshal(msgNode));
|
|
8523
|
+
logger.info("ClientController", `E2EE group edit sent for message ${messageId} in ${threadId}`);
|
|
8524
|
+
} else {
|
|
8525
|
+
const devicePayload = e2eeClient.buildMessageTransport({ messageApp });
|
|
8526
|
+
const selfDevicePayload = e2eeClient.buildMessageTransport({
|
|
8527
|
+
messageApp,
|
|
8528
|
+
dsm: { destinationJid: toJid, phash: "" }
|
|
8529
|
+
});
|
|
8530
|
+
const participantNodes = [];
|
|
8531
|
+
const deviceJids = uniqueJids(await this.e2eeHandler.getDeviceList([toJid, toBareMessengerJid(selfJid)]));
|
|
8532
|
+
for (const deviceJid of deviceJids) {
|
|
8533
|
+
if (sameMessengerDevice(deviceJid, selfJid)) continue;
|
|
8534
|
+
try {
|
|
8535
|
+
if (!await e2eeClient.hasSession(deviceJid)) {
|
|
8536
|
+
const bundle = await this.e2eeHandler.getPreKeyBundle(deviceJid);
|
|
8537
|
+
await e2eeClient.establishSession(deviceJid, bundle);
|
|
8538
|
+
}
|
|
8539
|
+
const payload = sameMessengerUser(deviceJid, selfJid) ? selfDevicePayload : devicePayload;
|
|
8540
|
+
const encrypted = await e2eeClient.encryptDevicePayload(deviceJid, selfJid, payload);
|
|
8541
|
+
participantNodes.push(encodeNode("to", { jid: deviceJid }, [
|
|
8542
|
+
encodeNode("enc", { v: "3", type: encrypted.type, "decrypt-fail": "hide" }, encrypted.ciphertext)
|
|
8543
|
+
]));
|
|
8544
|
+
} catch (err) {
|
|
8545
|
+
logger.error("ClientController", `Failed to encrypt edit to ${deviceJid}:`, err);
|
|
8546
|
+
}
|
|
8547
|
+
}
|
|
8548
|
+
const msgNode = encodeNode("message", { to: toJid, type: "text", id: newMessageId, edit: editAttr }, [
|
|
8549
|
+
encodeNode("participants", {}, participantNodes),
|
|
8550
|
+
encodeNode("franking", {}, [encodeNode("franking_tag", {}, frankingTag)]),
|
|
8551
|
+
encodeNode("trace", {}, [encodeNode("request_id", {}, Buffer.from(randomUUID3().replace(/-/g, ""), "hex"))])
|
|
8552
|
+
]);
|
|
8553
|
+
await this.e2eeSocket.sendFrame(marshal(msgNode));
|
|
8554
|
+
logger.info("ClientController", `E2EE DM edit sent for message ${messageId} to ${toJid}`);
|
|
8555
|
+
}
|
|
8351
8556
|
}
|
|
8352
8557
|
async sendTyping(input) {
|
|
8353
8558
|
await this.messagingService.sendTyping(this.requireApi(), input);
|
|
@@ -8541,9 +8746,7 @@ var ClientController = class {
|
|
|
8541
8746
|
async createPoll(input) {
|
|
8542
8747
|
await this.threadService.createPoll(this.requireApi(), input);
|
|
8543
8748
|
}
|
|
8544
|
-
|
|
8545
|
-
return this.threadService.editMessage(this.requireApi(), input);
|
|
8546
|
-
}
|
|
8749
|
+
// editMessage is now handled by the E2EE-aware method above (routes E2EE → sendE2EEEdit, else → threadService.editMessage).
|
|
8547
8750
|
async addGroupMember(input) {
|
|
8548
8751
|
await this.threadService.addGroupMember(this.requireApi(), input);
|
|
8549
8752
|
}
|
|
@@ -8800,8 +9003,11 @@ var FBClient = class {
|
|
|
8800
9003
|
async sendReaction(input) {
|
|
8801
9004
|
await this.controller.sendReaction(input);
|
|
8802
9005
|
}
|
|
8803
|
-
async unsendMessage(
|
|
8804
|
-
await this.controller.unsendMessage(
|
|
9006
|
+
async unsendMessage(input) {
|
|
9007
|
+
await this.controller.unsendMessage(input);
|
|
9008
|
+
}
|
|
9009
|
+
async editMessage(input) {
|
|
9010
|
+
await this.controller.editMessage(input);
|
|
8805
9011
|
}
|
|
8806
9012
|
async sendTyping(input) {
|
|
8807
9013
|
await this.controller.sendTyping(input);
|