@whanext/core 0.17.0 → 0.17.1
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/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/dist/index.js +243 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.1
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Compatibilidade com o novo formato criptografado de edições do WhatsApp (`secretEncryptedMessage`).
|
|
8
|
+
- O provider interpreta `targetMessageKey.fromMe` pela perspectiva do editor, deriva a chave com `messageSecret` e descriptografa edições com AES-256-GCM.
|
|
9
|
+
- Edições em conversas privadas preservam a chave local da mensagem original após a descriptografia.
|
|
10
|
+
- Edições consecutivas preservam o `messageSecret` no cache para que a versão anterior continue disponível.
|
|
11
|
+
- `messageEdited` continua com a mesma API pública da 0.17.0.
|
|
12
|
+
|
|
3
13
|
## 0.17.0
|
|
4
14
|
|
|
5
15
|
### Adicionado
|
package/README.md
CHANGED
|
@@ -175,7 +175,7 @@ O evento também informa `deletedById` quando o WhatsApp fornece a identidade re
|
|
|
175
175
|
|
|
176
176
|
## Mensagens editadas
|
|
177
177
|
|
|
178
|
-
Edições recebidas pelo WhatsApp são expostas pelo evento `messageEdited`. Quando a versão anterior ainda estiver no cache recente, o payload inclui `previous`; `message` contém a versão atual.
|
|
178
|
+
Edições recebidas pelo WhatsApp são expostas pelo evento `messageEdited`. A partir da 0.17.1, o provider inclui suporte ao envelope criptografado `secretEncryptedMessage` usado atualmente pelo WhatsApp para edições. Quando a versão anterior ainda estiver no cache recente, o payload inclui `previous`; `message` contém a versão atual.
|
|
179
179
|
|
|
180
180
|
```ts
|
|
181
181
|
app.on('messageEdited', async ({ previous, message, editedByMe }) => {
|
package/dist/index.js
CHANGED
|
@@ -2305,7 +2305,7 @@ import {
|
|
|
2305
2305
|
generateWAMessageFromContent,
|
|
2306
2306
|
isJidGroup,
|
|
2307
2307
|
makeWASocket,
|
|
2308
|
-
proto,
|
|
2308
|
+
proto as proto2,
|
|
2309
2309
|
useMultiFileAuthState
|
|
2310
2310
|
} from "@whiskeysockets/baileys";
|
|
2311
2311
|
|
|
@@ -2587,6 +2587,179 @@ function toDate(value) {
|
|
|
2587
2587
|
return new Date(seconds * 1e3);
|
|
2588
2588
|
}
|
|
2589
2589
|
|
|
2590
|
+
// src/provider/baileys/secret-edit.ts
|
|
2591
|
+
import { createDecipheriv, hkdfSync } from "crypto";
|
|
2592
|
+
import {
|
|
2593
|
+
jidNormalizedUser,
|
|
2594
|
+
normalizeMessageContent as normalizeMessageContent2,
|
|
2595
|
+
proto
|
|
2596
|
+
} from "@whiskeysockets/baileys";
|
|
2597
|
+
function isSecretEncryptedEdit(message) {
|
|
2598
|
+
const content = normalizeMessageContent2(message.message);
|
|
2599
|
+
return content?.secretEncryptedMessage?.secretEncType === proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT;
|
|
2600
|
+
}
|
|
2601
|
+
function secretEditTargetKey(message) {
|
|
2602
|
+
const content = normalizeMessageContent2(message.message);
|
|
2603
|
+
const secret = content?.secretEncryptedMessage;
|
|
2604
|
+
if (secret?.secretEncType !== proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT || !secret.targetMessageKey?.id) {
|
|
2605
|
+
return void 0;
|
|
2606
|
+
}
|
|
2607
|
+
return secret.targetMessageKey;
|
|
2608
|
+
}
|
|
2609
|
+
function decryptSecretEncryptedEdit(envelope, original, meId, meLid) {
|
|
2610
|
+
const content = normalizeMessageContent2(envelope.message);
|
|
2611
|
+
const secret = content?.secretEncryptedMessage;
|
|
2612
|
+
const targetKey = secret?.targetMessageKey;
|
|
2613
|
+
if (secret?.secretEncType !== proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT || !targetKey?.id || !secret.encPayload?.length || secret.encIv?.length !== 12) {
|
|
2614
|
+
return void 0;
|
|
2615
|
+
}
|
|
2616
|
+
const messageSecret = findMessageSecret(original.message);
|
|
2617
|
+
if (messageSecret?.length !== 32) {
|
|
2618
|
+
return void 0;
|
|
2619
|
+
}
|
|
2620
|
+
const editorCandidates = envelope.key.fromMe ? uniqueUserJids([meId, meLid]) : uniqueUserJids([
|
|
2621
|
+
envelope.key.participant,
|
|
2622
|
+
envelope.key.participantAlt,
|
|
2623
|
+
envelope.key.remoteJid,
|
|
2624
|
+
envelope.key.remoteJidAlt
|
|
2625
|
+
]);
|
|
2626
|
+
const originalSenderCandidates = targetKey.fromMe ? editorCandidates : isUserJid(envelope.key.remoteJid) ? uniqueUserJids([
|
|
2627
|
+
targetKey.remoteJid,
|
|
2628
|
+
targetKey.remoteJidAlt,
|
|
2629
|
+
original.key.remoteJid,
|
|
2630
|
+
original.key.remoteJidAlt
|
|
2631
|
+
]) : uniqueUserJids([
|
|
2632
|
+
targetKey.participant,
|
|
2633
|
+
targetKey.participantAlt,
|
|
2634
|
+
original.key.participant,
|
|
2635
|
+
original.key.participantAlt
|
|
2636
|
+
]);
|
|
2637
|
+
const fallbackOriginalCandidates = original.key.fromMe ? uniqueUserJids([meId, meLid]) : uniqueUserJids([
|
|
2638
|
+
original.key.participant,
|
|
2639
|
+
original.key.participantAlt,
|
|
2640
|
+
original.key.remoteJid,
|
|
2641
|
+
original.key.remoteJidAlt
|
|
2642
|
+
]);
|
|
2643
|
+
const senders = uniqueUserJids([
|
|
2644
|
+
...originalSenderCandidates,
|
|
2645
|
+
...fallbackOriginalCandidates
|
|
2646
|
+
]);
|
|
2647
|
+
if (!editorCandidates.length || !senders.length) {
|
|
2648
|
+
return void 0;
|
|
2649
|
+
}
|
|
2650
|
+
let decoded;
|
|
2651
|
+
for (const originalSender of senders) {
|
|
2652
|
+
for (const editor of editorCandidates) {
|
|
2653
|
+
try {
|
|
2654
|
+
decoded = decryptPayload(
|
|
2655
|
+
targetKey.id,
|
|
2656
|
+
originalSender,
|
|
2657
|
+
editor,
|
|
2658
|
+
messageSecret,
|
|
2659
|
+
secret.encIv,
|
|
2660
|
+
secret.encPayload
|
|
2661
|
+
);
|
|
2662
|
+
break;
|
|
2663
|
+
} catch {
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
if (decoded) {
|
|
2668
|
+
break;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
const protocol = decoded?.protocolMessage;
|
|
2672
|
+
if (protocol?.type !== proto.Message.ProtocolMessage.Type.MESSAGE_EDIT || !protocol.editedMessage || protocol.key?.id && protocol.key.id !== targetKey.id) {
|
|
2673
|
+
return void 0;
|
|
2674
|
+
}
|
|
2675
|
+
const edited = protocol.editedMessage;
|
|
2676
|
+
if (!edited.messageContextInfo?.messageSecret?.length) {
|
|
2677
|
+
edited.messageContextInfo = {
|
|
2678
|
+
...edited.messageContextInfo,
|
|
2679
|
+
messageSecret
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
return {
|
|
2683
|
+
message: edited,
|
|
2684
|
+
...protocol.timestampMs ? { timestamp: Math.floor(numberValue(protocol.timestampMs) / 1e3) } : {}
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
function decryptPayload(messageId, originalSender, editor, messageSecret, iv, payload) {
|
|
2688
|
+
const info = Buffer.concat([
|
|
2689
|
+
Buffer.from(messageId, "utf8"),
|
|
2690
|
+
Buffer.from(originalSender, "utf8"),
|
|
2691
|
+
Buffer.from(editor, "utf8"),
|
|
2692
|
+
Buffer.from("Message Edit", "utf8")
|
|
2693
|
+
]);
|
|
2694
|
+
const key = Buffer.from(hkdfSync(
|
|
2695
|
+
"sha256",
|
|
2696
|
+
Buffer.from(messageSecret),
|
|
2697
|
+
Buffer.alloc(32),
|
|
2698
|
+
info,
|
|
2699
|
+
32
|
|
2700
|
+
));
|
|
2701
|
+
const encrypted = Buffer.from(payload);
|
|
2702
|
+
if (encrypted.length <= 16) {
|
|
2703
|
+
throw new Error("INVALID_EDIT_PAYLOAD");
|
|
2704
|
+
}
|
|
2705
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv));
|
|
2706
|
+
decipher.setAAD(Buffer.alloc(0));
|
|
2707
|
+
decipher.setAuthTag(encrypted.subarray(encrypted.length - 16));
|
|
2708
|
+
return proto.Message.decode(Buffer.concat([
|
|
2709
|
+
decipher.update(encrypted.subarray(0, encrypted.length - 16)),
|
|
2710
|
+
decipher.final()
|
|
2711
|
+
]));
|
|
2712
|
+
}
|
|
2713
|
+
function findMessageSecret(message) {
|
|
2714
|
+
if (!message) {
|
|
2715
|
+
return void 0;
|
|
2716
|
+
}
|
|
2717
|
+
const direct = message.messageContextInfo?.messageSecret;
|
|
2718
|
+
if (direct?.length) {
|
|
2719
|
+
return direct;
|
|
2720
|
+
}
|
|
2721
|
+
const normalized = normalizeMessageContent2(message);
|
|
2722
|
+
const normalizedSecret = normalized?.messageContextInfo?.messageSecret;
|
|
2723
|
+
if (normalizedSecret?.length) {
|
|
2724
|
+
return normalizedSecret;
|
|
2725
|
+
}
|
|
2726
|
+
const deviceSecret = message.deviceSentMessage?.message?.messageContextInfo?.messageSecret;
|
|
2727
|
+
if (deviceSecret?.length) {
|
|
2728
|
+
return deviceSecret;
|
|
2729
|
+
}
|
|
2730
|
+
return void 0;
|
|
2731
|
+
}
|
|
2732
|
+
function uniqueUserJids(values) {
|
|
2733
|
+
return [...new Set(
|
|
2734
|
+
values.map(normalizeUserJid).filter((value) => value !== void 0)
|
|
2735
|
+
)];
|
|
2736
|
+
}
|
|
2737
|
+
function normalizeUserJid(value) {
|
|
2738
|
+
if (!isUserJid(value)) {
|
|
2739
|
+
return void 0;
|
|
2740
|
+
}
|
|
2741
|
+
return jidNormalizedUser(value);
|
|
2742
|
+
}
|
|
2743
|
+
function isUserJid(value) {
|
|
2744
|
+
if (!value) {
|
|
2745
|
+
return false;
|
|
2746
|
+
}
|
|
2747
|
+
const normalized = jidNormalizedUser(value);
|
|
2748
|
+
return normalized.endsWith("@s.whatsapp.net") || normalized.endsWith("@lid") || normalized.endsWith("@hosted") || normalized.endsWith("@hosted.lid");
|
|
2749
|
+
}
|
|
2750
|
+
function numberValue(value) {
|
|
2751
|
+
if (typeof value === "number") {
|
|
2752
|
+
return value;
|
|
2753
|
+
}
|
|
2754
|
+
if (typeof value === "bigint") {
|
|
2755
|
+
return Number(value);
|
|
2756
|
+
}
|
|
2757
|
+
if (value && typeof value === "object" && "toString" in value) {
|
|
2758
|
+
return Number(String(value));
|
|
2759
|
+
}
|
|
2760
|
+
return Number(value);
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2590
2763
|
// src/provider/baileys/baileys-provider.ts
|
|
2591
2764
|
var BaileysProvider = class {
|
|
2592
2765
|
#options;
|
|
@@ -2827,7 +3000,7 @@ var BaileysProvider = class {
|
|
|
2827
3000
|
async setMessagePin(groupId, key, pinned) {
|
|
2828
3001
|
await this.#requireSocket().sendMessage(groupId, {
|
|
2829
3002
|
pin: this.#toWaKey(key),
|
|
2830
|
-
type: pinned ?
|
|
3003
|
+
type: pinned ? proto2.PinInChat.Type.PIN_FOR_ALL : proto2.PinInChat.Type.UNPIN_FOR_ALL,
|
|
2831
3004
|
...pinned ? { time: 604800 } : {}
|
|
2832
3005
|
});
|
|
2833
3006
|
}
|
|
@@ -2859,6 +3032,7 @@ var BaileysProvider = class {
|
|
|
2859
3032
|
socket.ev.on("messages.upsert", ({ messages, type }) => {
|
|
2860
3033
|
if (type !== "notify") return;
|
|
2861
3034
|
for (const raw of messages) {
|
|
3035
|
+
if (this.#handleSecretEncryptedEdit(raw)) continue;
|
|
2862
3036
|
if (raw.key.id && raw.message) this.#remember(raw);
|
|
2863
3037
|
const quoted = extractQuotedBaileysMessage(raw);
|
|
2864
3038
|
if (quoted?.key.id && quoted.message) this.#remember(quoted);
|
|
@@ -2871,13 +3045,13 @@ var BaileysProvider = class {
|
|
|
2871
3045
|
if (!key.id || !key.remoteJid) continue;
|
|
2872
3046
|
const stored = this.#messageStore.get(this.#messageStoreKey(key));
|
|
2873
3047
|
if (update.message === null) {
|
|
2874
|
-
const
|
|
3048
|
+
const message = stored ? normalizeBaileysMessage(stored) : void 0;
|
|
2875
3049
|
const deletionKey = update.key;
|
|
2876
3050
|
const deletedByMe = deletionKey?.fromMe === true;
|
|
2877
3051
|
const deletedById = deletionKey?.participant ?? deletionKey?.participantAlt ?? (deletedByMe ? this.#socket?.user?.id : deletionKey?.remoteJid ?? void 0);
|
|
2878
3052
|
void this.#events.emit("messageDeleted", {
|
|
2879
|
-
key:
|
|
2880
|
-
...
|
|
3053
|
+
key: message?.keys ?? normalizeKey(key),
|
|
3054
|
+
...message ? { message } : {},
|
|
2881
3055
|
deletedByMe,
|
|
2882
3056
|
...deletedById ? { deletedById } : {},
|
|
2883
3057
|
deletedAt: /* @__PURE__ */ new Date()
|
|
@@ -2894,20 +3068,7 @@ var BaileysProvider = class {
|
|
|
2894
3068
|
message: update.message,
|
|
2895
3069
|
messageTimestamp: update.messageTimestamp ?? stored?.messageTimestamp ?? Math.floor(Date.now() / 1e3)
|
|
2896
3070
|
};
|
|
2897
|
-
|
|
2898
|
-
const message = normalizeBaileysMessage(editedRaw);
|
|
2899
|
-
if (!message) continue;
|
|
2900
|
-
this.#remember(editedRaw);
|
|
2901
|
-
const editedByMe = key.fromMe === true;
|
|
2902
|
-
const editedById = key.participant ?? key.participantAlt ?? (editedByMe ? this.#socket?.user?.id : key.remoteJid ?? void 0);
|
|
2903
|
-
void this.#events.emit("messageEdited", {
|
|
2904
|
-
key: message.keys,
|
|
2905
|
-
...previous ? { previous } : {},
|
|
2906
|
-
message,
|
|
2907
|
-
editedByMe,
|
|
2908
|
-
...editedById ? { editedById } : {},
|
|
2909
|
-
editedAt: message.timestamp
|
|
2910
|
-
});
|
|
3071
|
+
this.#emitMessageEdited(key, editedRaw, stored);
|
|
2911
3072
|
}
|
|
2912
3073
|
});
|
|
2913
3074
|
socket.ev.on("groups.update", (groups) => {
|
|
@@ -3007,7 +3168,7 @@ var BaileysProvider = class {
|
|
|
3007
3168
|
"WhatsApp did not expose the current account identity for the interactive message."
|
|
3008
3169
|
);
|
|
3009
3170
|
}
|
|
3010
|
-
const interactiveMessage =
|
|
3171
|
+
const interactiveMessage = proto2.Message.InteractiveMessage.create({
|
|
3011
3172
|
...content.title !== void 0 ? {
|
|
3012
3173
|
header: {
|
|
3013
3174
|
title: content.title,
|
|
@@ -3199,6 +3360,68 @@ var BaileysProvider = class {
|
|
|
3199
3360
|
];
|
|
3200
3361
|
return known.find((value) => value === status) ?? "timeout";
|
|
3201
3362
|
}
|
|
3363
|
+
#handleSecretEncryptedEdit(raw) {
|
|
3364
|
+
if (!isSecretEncryptedEdit(raw)) return false;
|
|
3365
|
+
const targetKey = secretEditTargetKey(raw);
|
|
3366
|
+
if (!targetKey?.id) return true;
|
|
3367
|
+
const stored = this.#findStoredMessage(targetKey);
|
|
3368
|
+
if (!stored) return true;
|
|
3369
|
+
try {
|
|
3370
|
+
const decrypted = decryptSecretEncryptedEdit(
|
|
3371
|
+
raw,
|
|
3372
|
+
stored,
|
|
3373
|
+
this.#socket?.user?.id,
|
|
3374
|
+
this.#socket?.user?.lid
|
|
3375
|
+
);
|
|
3376
|
+
if (!decrypted) return true;
|
|
3377
|
+
const key = {
|
|
3378
|
+
...stored.key,
|
|
3379
|
+
id: targetKey.id
|
|
3380
|
+
};
|
|
3381
|
+
const editedRaw = {
|
|
3382
|
+
...stored,
|
|
3383
|
+
key,
|
|
3384
|
+
message: {
|
|
3385
|
+
editedMessage: {
|
|
3386
|
+
message: decrypted.message
|
|
3387
|
+
}
|
|
3388
|
+
},
|
|
3389
|
+
messageTimestamp: decrypted.timestamp ?? raw.messageTimestamp ?? stored.messageTimestamp ?? Math.floor(Date.now() / 1e3)
|
|
3390
|
+
};
|
|
3391
|
+
this.#emitMessageEdited(key, editedRaw, stored);
|
|
3392
|
+
} catch (error) {
|
|
3393
|
+
this.#logger.warn("Could not decrypt the encrypted message edit.", {
|
|
3394
|
+
messageId: targetKey.id,
|
|
3395
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3396
|
+
});
|
|
3397
|
+
}
|
|
3398
|
+
return true;
|
|
3399
|
+
}
|
|
3400
|
+
#emitMessageEdited(key, editedRaw, stored) {
|
|
3401
|
+
const previous = stored ? normalizeBaileysMessage(stored) : void 0;
|
|
3402
|
+
const message = normalizeBaileysMessage(editedRaw);
|
|
3403
|
+
if (!message) return;
|
|
3404
|
+
this.#remember(editedRaw);
|
|
3405
|
+
const editedByMe = key.fromMe === true;
|
|
3406
|
+
const editedById = key.participant ?? key.participantAlt ?? (editedByMe ? this.#socket?.user?.id : key.remoteJid ?? void 0);
|
|
3407
|
+
void this.#events.emit("messageEdited", {
|
|
3408
|
+
key: message.keys,
|
|
3409
|
+
...previous ? { previous } : {},
|
|
3410
|
+
message,
|
|
3411
|
+
editedByMe,
|
|
3412
|
+
...editedById ? { editedById } : {},
|
|
3413
|
+
editedAt: message.timestamp
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
#findStoredMessage(key) {
|
|
3417
|
+
const direct = this.#messageStore.get(this.#messageStoreKey(key));
|
|
3418
|
+
if (direct) return direct;
|
|
3419
|
+
if (!key.id) return void 0;
|
|
3420
|
+
for (const message of this.#messageStore.values()) {
|
|
3421
|
+
if (message.key.id === key.id) return message;
|
|
3422
|
+
}
|
|
3423
|
+
return void 0;
|
|
3424
|
+
}
|
|
3202
3425
|
#remember(message) {
|
|
3203
3426
|
const key = this.#messageStoreKey(message.key);
|
|
3204
3427
|
this.#messageStore.delete(key);
|