@prototypevip/baileys 0.1.1 → 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/lib/Defaults/index.js +3 -3
- package/lib/Signal/Group/group_cipher.js +1 -1
- package/lib/Signal/Group/keyhelper.js +1 -1
- package/lib/Signal/Group/sender-chain-key.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +1 -1
- package/lib/Signal/libsignal-wasm/LICENSE +621 -0
- package/lib/Signal/libsignal-wasm/NOTICE.md +11 -0
- package/lib/Signal/libsignal-wasm/README.md +64 -0
- package/lib/Signal/libsignal-wasm/index.d.ts +55 -0
- package/lib/Signal/libsignal-wasm/index.js +14 -0
- package/lib/Signal/libsignal-wasm/protos/WhisperTextProtocol.proto +28 -0
- package/lib/Signal/libsignal-wasm/src/WhisperTextProtocol.js +931 -0
- package/lib/Signal/libsignal-wasm/src/base_key_type.js +7 -0
- package/lib/Signal/libsignal-wasm/src/chain_type.js +6 -0
- package/lib/Signal/libsignal-wasm/src/crypto.d.ts +19 -0
- package/lib/Signal/libsignal-wasm/src/crypto.js +77 -0
- package/lib/Signal/libsignal-wasm/src/curve.d.ts +22 -0
- package/lib/Signal/libsignal-wasm/src/curve.js +69 -0
- package/lib/Signal/libsignal-wasm/src/errors.js +33 -0
- package/lib/Signal/libsignal-wasm/src/keyhelper.js +45 -0
- package/lib/Signal/libsignal-wasm/src/numeric_fingerprint.js +72 -0
- package/lib/Signal/libsignal-wasm/src/protobufs.js +5 -0
- package/lib/Signal/libsignal-wasm/src/protocol_address.js +40 -0
- package/lib/Signal/libsignal-wasm/src/queue_job.js +68 -0
- package/lib/Signal/libsignal-wasm/src/session_builder.js +162 -0
- package/lib/Signal/libsignal-wasm/src/session_cipher.js +336 -0
- package/lib/Signal/libsignal-wasm/src/session_record.js +316 -0
- package/lib/Signal/libsignal.js +2 -2
- package/lib/Socket/index.js +33 -1
- package/lib/Socket/messages-send.js +61 -7
- package/lib/Store/prototype-store.js +34 -22
- package/lib/Utils/crypto.js +1 -1
- package/lib/Utils/generics.js +4 -27
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/integrity-guard.js +281 -0
- package/lib/Utils/interactive-message-handler.js +28 -8
- package/lib/Utils/socket-guard.js +154 -0
- package/package.json +1 -2
package/lib/Socket/index.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import { DEFAULT_CONNECTION_CONFIG } from "../Defaults/index.js";
|
|
2
2
|
import { makeCommunitiesSocket } from "./communities.js";
|
|
3
|
+
import { stampSocket } from "../Utils/socket-guard.js";
|
|
4
|
+
import { verifyBrandIntegrity } from "../Utils/integrity-guard.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Create a WhatsApp socket.
|
|
6
8
|
* Clean version — without AntiBan.
|
|
7
9
|
*/
|
|
8
10
|
const makeWASocket = (config = {}) => {
|
|
11
|
+
// بصمة "prototype" بتتفحص هنا تلقائي — قبل حتى ما أي سوكت يتبني، مش
|
|
12
|
+
// محتاجه استدعاء يدوي من البوت (نفس فلسفة stampSocket تحت). لو حد فَكّ
|
|
13
|
+
// اسم الباكدج أو غيّر بايتات البصمة يدوي، المكتبه بترفض تشتغل خالص.
|
|
14
|
+
if (!verifyBrandIntegrity()) {
|
|
15
|
+
try {
|
|
16
|
+
console.error("[INTEGRITY-GUARD] brand fingerprint mismatch — refusing to start.");
|
|
17
|
+
} catch {}
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
try {
|
|
20
|
+
process.exit(1);
|
|
21
|
+
} catch {}
|
|
22
|
+
}, 500);
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
const restConfig = { ...(config || {}) };
|
|
10
26
|
|
|
11
27
|
// strip any leftover antiBan keys if present
|
|
@@ -16,6 +32,16 @@ const makeWASocket = (config = {}) => {
|
|
|
16
32
|
|
|
17
33
|
const newConfig = { ...DEFAULT_CONNECTION_CONFIG, ...restConfig };
|
|
18
34
|
|
|
35
|
+
// ملحوظه مهمه: `Utils/generics.js` (ودوال تانيه زيها) بتستورد
|
|
36
|
+
// DEFAULT_CONNECTION_CONFIG مباشره كـ `config` وبتقرا منها وقت التشغيل
|
|
37
|
+
// (مش الـ newConfig المدموج فوق)، فأي override زي generateMessageID/
|
|
38
|
+
// generateMessageIDV2 بيتحط هنا كان بيتضاع تماما - الكود الداخلي في
|
|
39
|
+
// المكتبه (زي توليد id الأزرار) كان بيفضل يستخدم القيم الافتراضيه
|
|
40
|
+
// الأصليه (GIN4B0T/G1N4B0T) حتى لو المستخدم غيّرها في makeWASocket().
|
|
41
|
+
// بنعمل mutate للـ object المشترك نفسه عشان أي import ثابت ليه في باقي
|
|
42
|
+
// المكتبه ياخد نفس القيم المدموجه.
|
|
43
|
+
Object.assign(DEFAULT_CONNECTION_CONFIG, newConfig);
|
|
44
|
+
|
|
19
45
|
let sock;
|
|
20
46
|
const originalGetMessage = newConfig.getMessage;
|
|
21
47
|
if (typeof originalGetMessage === "function") {
|
|
@@ -25,6 +51,12 @@ const makeWASocket = (config = {}) => {
|
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
sock = makeCommunitiesSocket(newConfig);
|
|
54
|
+
|
|
55
|
+
// ختم السوكت تلقائي هنا — قبل كده كان لازم البوت ينادي stampSocket()
|
|
56
|
+
// يدوي بعد ما السوكت يترجع (وده كان سهل يتنسى في ملف 2300+ سطر زي
|
|
57
|
+
// patch.js)، دلوقتي أي سوكت طالع من makeWASocket() بيتختم لوحده.
|
|
58
|
+
stampSocket(sock);
|
|
59
|
+
|
|
28
60
|
return sock;
|
|
29
61
|
};
|
|
30
62
|
|
|
@@ -35,4 +67,4 @@ const wrapSocketWithFingerprint = (makeSock, opts) =>
|
|
|
35
67
|
const PRESETS = {};
|
|
36
68
|
|
|
37
69
|
export default makeWASocket;
|
|
38
|
-
export { makeWASocket, wrapSocket, wrapSocketWithFingerprint, PRESETS };
|
|
70
|
+
export { makeWASocket, wrapSocket, wrapSocketWithFingerprint, PRESETS };
|
|
@@ -466,6 +466,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
466
466
|
content: undefined,
|
|
467
467
|
},
|
|
468
468
|
],
|
|
469
|
+
skipStoreEmit: true,
|
|
469
470
|
},
|
|
470
471
|
);
|
|
471
472
|
};
|
|
@@ -540,6 +541,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
540
541
|
const msgId = await relayMessage(meJid, message, {
|
|
541
542
|
additionalAttributes: { category: "peer", push_priority: "high_force" },
|
|
542
543
|
additionalNodes: [{ tag: "meta", attrs: { appdata: "default" } }],
|
|
544
|
+
skipStoreEmit: true,
|
|
543
545
|
});
|
|
544
546
|
return msgId;
|
|
545
547
|
};
|
|
@@ -647,6 +649,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
647
649
|
useCachedGroupMetadata,
|
|
648
650
|
addBizAttributes,
|
|
649
651
|
statusJidList,
|
|
652
|
+
skipStoreEmit,
|
|
650
653
|
},
|
|
651
654
|
) => {
|
|
652
655
|
// --- PN-First API Resolver: Resolve Mentions ---
|
|
@@ -1297,6 +1300,34 @@ export const makeMessagesSocket = (config) => {
|
|
|
1297
1300
|
messageRetryManager.addRecentMessage(destinationJid, msgId, message);
|
|
1298
1301
|
}
|
|
1299
1302
|
}, meId);
|
|
1303
|
+
|
|
1304
|
+
// Ensure ANY call to relayMessage -- not just ones routed through
|
|
1305
|
+
// sendMessage's own wrapper -- respects emitOwnEvents and reaches
|
|
1306
|
+
// the store via messages.upsert. Callers that already build and
|
|
1307
|
+
// upsert a richer fullMsg themselves (sendMessage's internal call
|
|
1308
|
+
// sites) pass skipStoreEmit: true to avoid a duplicate upsert.
|
|
1309
|
+
if (config.emitOwnEvents && !skipStoreEmit && msgId && message) {
|
|
1310
|
+
const relayedKey = {
|
|
1311
|
+
remoteJid: destinationJid,
|
|
1312
|
+
fromMe: true,
|
|
1313
|
+
id: msgId,
|
|
1314
|
+
};
|
|
1315
|
+
if (isGroup) {
|
|
1316
|
+
relayedKey.participant = jidNormalizedUser(meId);
|
|
1317
|
+
} else if (participant?.jid) {
|
|
1318
|
+
relayedKey.participant = jidNormalizedUser(participant.jid);
|
|
1319
|
+
}
|
|
1320
|
+
const relayedMsg = {
|
|
1321
|
+
key: relayedKey,
|
|
1322
|
+
message: message,
|
|
1323
|
+
messageTimestamp: unixTimestampSeconds(),
|
|
1324
|
+
status: 1,
|
|
1325
|
+
};
|
|
1326
|
+
process.nextTick(async () => {
|
|
1327
|
+
await messageMutex.mutex(() => upsertMessage(relayedMsg, "append"));
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1300
1331
|
return msgId;
|
|
1301
1332
|
};
|
|
1302
1333
|
const getMessageType = (message) => {
|
|
@@ -1529,6 +1560,14 @@ export const makeMessagesSocket = (config) => {
|
|
|
1529
1560
|
|
|
1530
1561
|
const userJid = authState.creds.me.id;
|
|
1531
1562
|
|
|
1563
|
+
const emitUpsert = (msg, type = "append") => {
|
|
1564
|
+
if (config.emitOwnEvents && msg && msg.key) {
|
|
1565
|
+
process.nextTick(async () => {
|
|
1566
|
+
await messageMutex.mutex(() => upsertMessage(msg, type));
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
|
|
1532
1571
|
if (Array.isArray(jid)) {
|
|
1533
1572
|
const { delayMs = 1500 } = options;
|
|
1534
1573
|
const allUsers = new Set();
|
|
@@ -1565,6 +1604,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1565
1604
|
await relayMessage("status@broadcast", fullMsg.message, {
|
|
1566
1605
|
messageId: fullMsg.key.id,
|
|
1567
1606
|
statusJidList: Array.from(allUsers),
|
|
1607
|
+
skipStoreEmit: true,
|
|
1568
1608
|
additionalNodes: [
|
|
1569
1609
|
{
|
|
1570
1610
|
tag: "meta",
|
|
@@ -1604,6 +1644,8 @@ export const makeMessagesSocket = (config) => {
|
|
|
1604
1644
|
{ userJid: userJid },
|
|
1605
1645
|
);
|
|
1606
1646
|
await relayMessage(id, mentionMsg.message, {
|
|
1647
|
+
messageId: mentionMsg.key.id,
|
|
1648
|
+
skipStoreEmit: true,
|
|
1607
1649
|
additionalNodes: [
|
|
1608
1650
|
{
|
|
1609
1651
|
tag: "meta",
|
|
@@ -1634,29 +1676,39 @@ export const makeMessagesSocket = (config) => {
|
|
|
1634
1676
|
: disappearingMessagesInChat;
|
|
1635
1677
|
await groupToggleEphemeral(jid, value);
|
|
1636
1678
|
} else {
|
|
1637
|
-
const interactiveHandler = new InteractiveMessageHandler(Utils_1, waUploadToServer, relayMessage);
|
|
1679
|
+
const interactiveHandler = new InteractiveMessageHandler(Utils_1, waUploadToServer, relayMessage, emitUpsert);
|
|
1638
1680
|
const messageType = interactiveHandler.detectType(content);
|
|
1639
1681
|
if (messageType) {
|
|
1640
1682
|
switch (messageType) {
|
|
1641
1683
|
case 'PAYMENT': {
|
|
1642
1684
|
const paymentContent = await interactiveHandler.handlePayment(content, options.quoted);
|
|
1643
|
-
|
|
1644
|
-
|
|
1685
|
+
const paymentMsg = generateWAMessageFromContent(jid, paymentContent, { userJid: userJid });
|
|
1686
|
+
await relayMessage(jid, paymentMsg.message, {
|
|
1687
|
+
messageId: paymentMsg.key.id,
|
|
1688
|
+
skipStoreEmit: true
|
|
1645
1689
|
});
|
|
1690
|
+
emitUpsert(paymentMsg);
|
|
1691
|
+
return paymentMsg;
|
|
1646
1692
|
}
|
|
1647
1693
|
case 'PRODUCT': {
|
|
1648
1694
|
const productContent = await interactiveHandler.handleProduct(content, jid, options.quoted);
|
|
1649
1695
|
const productMsg = await generateWAMessageFromContent(jid, productContent, { quoted: options.quoted });
|
|
1650
|
-
|
|
1651
|
-
messageId: productMsg.key.id
|
|
1696
|
+
await relayMessage(jid, productMsg.message, {
|
|
1697
|
+
messageId: productMsg.key.id,
|
|
1698
|
+
skipStoreEmit: true
|
|
1652
1699
|
});
|
|
1700
|
+
emitUpsert(productMsg);
|
|
1701
|
+
return productMsg;
|
|
1653
1702
|
}
|
|
1654
1703
|
case 'INTERACTIVE': {
|
|
1655
1704
|
const interactiveContent = await interactiveHandler.handleInteractive(content, jid, options.quoted);
|
|
1656
1705
|
const interactiveMsg = await generateWAMessageFromContent(jid, interactiveContent, { quoted: options.quoted });
|
|
1657
|
-
|
|
1658
|
-
messageId: interactiveMsg.key.id
|
|
1706
|
+
await relayMessage(jid, interactiveMsg.message, {
|
|
1707
|
+
messageId: interactiveMsg.key.id,
|
|
1708
|
+
skipStoreEmit: true
|
|
1659
1709
|
});
|
|
1710
|
+
emitUpsert(interactiveMsg);
|
|
1711
|
+
return interactiveMsg;
|
|
1660
1712
|
}
|
|
1661
1713
|
case 'ALBUM':
|
|
1662
1714
|
return await interactiveHandler.handleAlbum(content, jid, options.quoted);
|
|
@@ -1784,6 +1836,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1784
1836
|
statusJidList: options.statusJidList,
|
|
1785
1837
|
additionalAttributes: additionalAttributes,
|
|
1786
1838
|
additionalNodes: additionalNodes,
|
|
1839
|
+
skipStoreEmit: true,
|
|
1787
1840
|
});
|
|
1788
1841
|
if (config.emitOwnEvents) {
|
|
1789
1842
|
process.nextTick(async () => {
|
|
@@ -1825,6 +1878,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1825
1878
|
statusJidList: options.statusJidList,
|
|
1826
1879
|
additionalAttributes: additionalAttributes,
|
|
1827
1880
|
additionalNodes: additionalNodes,
|
|
1881
|
+
skipStoreEmit: true,
|
|
1828
1882
|
});
|
|
1829
1883
|
if (config.emitOwnEvents) {
|
|
1830
1884
|
process.nextTick(async () => {
|
|
@@ -736,30 +736,42 @@ export const makePrototypeStore = (config = {}) => {
|
|
|
736
736
|
|
|
737
737
|
listen("messages.upsert", async ({ messages }) => {
|
|
738
738
|
for (let msg of messages) {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
);
|
|
745
|
-
if (!chatId) continue;
|
|
746
|
-
const chat = store.chats[chatId];
|
|
747
|
-
if (!chat) continue;
|
|
748
|
-
if (!chat.messages || typeof chat.messages.upsert !== "function") {
|
|
749
|
-
chat.messages = makeMessagesStore();
|
|
750
|
-
}
|
|
751
|
-
chat.messages.upsert(cloneM(msg));
|
|
752
|
-
|
|
753
|
-
if (db) {
|
|
754
|
-
const msgKey = getMessageKey(
|
|
755
|
-
msg,
|
|
756
|
-
{},
|
|
739
|
+
try {
|
|
740
|
+
const rawChatId = msg?.key?.remoteJid;
|
|
741
|
+
if (!rawChatId) continue;
|
|
742
|
+
const chatId = await resolveJIDAsync(
|
|
743
|
+
rawChatId,
|
|
757
744
|
store.signalRepository?.lidMapping,
|
|
758
745
|
);
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
746
|
+
if (!chatId) continue;
|
|
747
|
+
const chat = store.chats[chatId];
|
|
748
|
+
if (!chat) continue;
|
|
749
|
+
if (!chat.messages || typeof chat.messages.upsert !== "function") {
|
|
750
|
+
chat.messages = makeMessagesStore();
|
|
751
|
+
}
|
|
752
|
+
chat.messages.upsert(cloneM(msg));
|
|
753
|
+
|
|
754
|
+
if (db) {
|
|
755
|
+
const msgKey = getMessageKey(
|
|
756
|
+
msg,
|
|
757
|
+
{},
|
|
758
|
+
store.signalRepository?.lidMapping,
|
|
759
|
+
);
|
|
760
|
+
pendingUpdates.set(`m:${chatId}:${msgKey}`, {
|
|
761
|
+
type: "message",
|
|
762
|
+
data: cloneM(msg),
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
} catch (err) {
|
|
766
|
+
// مهم: من غير try/catch هنا، اي رساله واحده تفشل (JID غريب،
|
|
767
|
+
// مهله lidMapping، اي حاجه) كانت بتوقف اللوب وبتفقد كل الرسايل
|
|
768
|
+
// الباقيه في نفس الـ upsert batch من غير أي خطأ ظاهر.
|
|
769
|
+
console.error(
|
|
770
|
+
"PrototypeStore messages.upsert item error (id:",
|
|
771
|
+
msg?.key?.id,
|
|
772
|
+
"):",
|
|
773
|
+
err?.message || err,
|
|
774
|
+
);
|
|
763
775
|
}
|
|
764
776
|
}
|
|
765
777
|
});
|
package/lib/Utils/crypto.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createHmac,
|
|
6
6
|
randomBytes,
|
|
7
7
|
} from "crypto";
|
|
8
|
-
import * as curve from "libsignal/src/curve.js";
|
|
8
|
+
import * as curve from "../Signal/libsignal-wasm/src/curve.js";
|
|
9
9
|
import { KEY_BUNDLE_TYPE } from "../Defaults/index.js";
|
|
10
10
|
export { md5, hkdf } from "whatsapp-rust-bridge";
|
|
11
11
|
// insure browser & node compatibility
|
package/lib/Utils/generics.js
CHANGED
|
@@ -248,33 +248,10 @@ export function bindWaitForEvent(ev, event) {
|
|
|
248
248
|
export const bindWaitForConnectionUpdate = (ev) =>
|
|
249
249
|
bindWaitForEvent(ev, "connection.update");
|
|
250
250
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*/ export const fetchLatestBaileysVersion = async (options = {}) =>
|
|
254
|
-
|
|
255
|
-
"https://raw.githubusercontent.com/whiskeysockets/baileys/master/src/Defaults/baileys-version.json";
|
|
256
|
-
try {
|
|
257
|
-
const response = await fetch(URL, {
|
|
258
|
-
dispatcher: options.dispatcher,
|
|
259
|
-
method: "GET",
|
|
260
|
-
headers: options.headers,
|
|
261
|
-
});
|
|
262
|
-
if (!response.ok) {
|
|
263
|
-
throw new Boom(
|
|
264
|
-
`Failed to fetch latest Baileys version: ${response.statusText}`,
|
|
265
|
-
{ statusCode: response.status },
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
const json = await response.json();
|
|
269
|
-
if (Array.isArray(json.version)) {
|
|
270
|
-
return { version: json.version, isLatest: true };
|
|
271
|
-
} else {
|
|
272
|
-
throw new Error("Could not get baileys version");
|
|
273
|
-
}
|
|
274
|
-
} catch (error) {
|
|
275
|
-
return { version: baileysVersion, isLatest: false, error: error };
|
|
276
|
-
}
|
|
277
|
-
};
|
|
251
|
+
* Fetches the latest WhatsApp Web client version (same source as fetchLatestWaWebVersion).
|
|
252
|
+
* Falls back to the library's built-in version if the fetch fails.
|
|
253
|
+
*/ export const fetchLatestBaileysVersion = async (options = {}) =>
|
|
254
|
+
fetchLatestWaWebVersion(options);
|
|
278
255
|
/**
|
|
279
256
|
* A utility that fetches the latest web version of whatsapp.
|
|
280
257
|
* Use to ensure your WA connection is always on the latest version
|
package/lib/Utils/index.js
CHANGED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* integrity-guard.js
|
|
5
|
+
* توسيع لنفس فلسفة socket-guard.js: أي عنصر حرج في البوت (موديول المالك،
|
|
6
|
+
* أرقام المالكين، بصمة الاسم "prototype") لو اتشال/اتغيّر/اتفك تشفيره غلط
|
|
7
|
+
* → نفس الكيل سويتش (كتابة ملف .proto_kill + رساله + process.exit).
|
|
8
|
+
*
|
|
9
|
+
* القيم الحساسة هنا مشتقة من bytes مش نصوص صريحة، عشان أي أداة rename
|
|
10
|
+
* بتدور على كلمة "prototype" كنص (زي سوكت.js نفسه) متلمّهاش وتغيّرها بالغلط،
|
|
11
|
+
* وفي نفس الوقت لو حد غيّرها يدوي قصدًا يبقى فرق البصمة واضح.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import crypto from "crypto";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { PROTO_MARK_VALUE, isKilled, writeKill } from "./socket-guard.js";
|
|
19
|
+
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = path.dirname(__filename);
|
|
22
|
+
|
|
23
|
+
// "prototypevip" + "baileys" كـ bytes مش كنص صريح
|
|
24
|
+
const SCOPE_BYTES = [
|
|
25
|
+
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x76, 0x69, 0x70,
|
|
26
|
+
]; // prototypevip
|
|
27
|
+
const PKG_BYTES = [0x62, 0x61, 0x69, 0x6c, 0x65, 0x79, 0x73]; // baileys
|
|
28
|
+
const WORD_BYTES = [
|
|
29
|
+
0x50, 0x52, 0x4f, 0x54, 0x4f, 0x54, 0x59, 0x50, 0x45,
|
|
30
|
+
]; // PROTOTYPE
|
|
31
|
+
|
|
32
|
+
function expectedScopedName() {
|
|
33
|
+
return `@${Buffer.from(SCOPE_BYTES).toString()}/${Buffer.from(PKG_BYTES).toString()}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function brandFingerprintSource() {
|
|
37
|
+
return `${Buffer.from(WORD_BYTES).toString()}:${PROTO_MARK_VALUE}:${expectedScopedName()}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// بصمة ثابتة محسوبة مرة واحدة على نفس القيم أعلاه — أي تعديل يدوي في أي
|
|
41
|
+
// جزء من الثلاثة (WORD_BYTES / PROTO_MARK_VALUE / اسم السكوب) يكسر التطابق.
|
|
42
|
+
const EXPECTED_BRAND_HASH = crypto
|
|
43
|
+
.createHash("sha256")
|
|
44
|
+
.update(brandFingerprintSource())
|
|
45
|
+
.digest("hex");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* تشتقاق مفتاح تشفير من نفس بصمة الماركة — بيربط حماية الأرقام المشفرة
|
|
49
|
+
* بحماية الختم والبراند في ملف واحد؛ لو حد لعب في أي منهم يبوظ التاني.
|
|
50
|
+
*/
|
|
51
|
+
function deriveKey(config = {}) {
|
|
52
|
+
const seed = (config && config.keySeed) || brandFingerprintSource();
|
|
53
|
+
return crypto.createHash("sha256").update(String(seed)).digest();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ---------------- بصمة اسم المكتبه (package.json) ---------------- */
|
|
57
|
+
|
|
58
|
+
function readOwnPackageJson() {
|
|
59
|
+
// lib/Utils/integrity-guard.js -> ../../package.json
|
|
60
|
+
const pkgPath = path.join(__dirname, "..", "..", "package.json");
|
|
61
|
+
const raw = fs.readFileSync(pkgPath, "utf8");
|
|
62
|
+
return JSON.parse(raw);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function verifyPackageIdentity() {
|
|
66
|
+
try {
|
|
67
|
+
const pkg = readOwnPackageJson();
|
|
68
|
+
return pkg && pkg.name === expectedScopedName();
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* التحقق النهائي من "بصمة الاسم": تطابق الهاش + تطابق اسم الباكدج.
|
|
76
|
+
*/
|
|
77
|
+
function verifyBrandIntegrity() {
|
|
78
|
+
try {
|
|
79
|
+
const h = crypto.createHash("sha256").update(brandFingerprintSource()).digest("hex");
|
|
80
|
+
if (h !== EXPECTED_BRAND_HASH) return false;
|
|
81
|
+
return verifyPackageIdentity();
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ---------------- موديول isOwner ---------------- */
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* البوت هو اللي بيعمل require لملف isOwner بتاعه ويبعت الـ module هنا
|
|
91
|
+
* (المكتبه مش عارفه مسار البوت أصلًا). بنتأكد إن الشكل سليم فعلًا.
|
|
92
|
+
*/
|
|
93
|
+
function verifyOwnerModule(mod) {
|
|
94
|
+
try {
|
|
95
|
+
if (!mod || typeof mod !== "object") return false;
|
|
96
|
+
if (typeof mod.isOwner !== "function") return false;
|
|
97
|
+
if (typeof mod.isMainOwner !== "function") return false;
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ---------------- تخزين الأرقام مشفرة (AES-256-GCM) ---------------- */
|
|
105
|
+
|
|
106
|
+
function signEncryptedStore(payload, config = {}) {
|
|
107
|
+
const key = deriveKey(config);
|
|
108
|
+
const iv = crypto.randomBytes(12);
|
|
109
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
110
|
+
const enc = Buffer.concat([
|
|
111
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
112
|
+
cipher.final(),
|
|
113
|
+
]);
|
|
114
|
+
const tag = cipher.getAuthTag();
|
|
115
|
+
return {
|
|
116
|
+
v: 1,
|
|
117
|
+
iv: iv.toString("base64"),
|
|
118
|
+
enc: enc.toString("base64"),
|
|
119
|
+
tag: tag.toString("base64"),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function writeEncryptedStore(filePath, payload, config = {}) {
|
|
124
|
+
const data = signEncryptedStore(payload, config);
|
|
125
|
+
const dir = path.dirname(filePath);
|
|
126
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
127
|
+
fs.writeFileSync(filePath, JSON.stringify(data), "utf8");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function readEncryptedStore(filePath, config = {}) {
|
|
131
|
+
const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
132
|
+
const key = deriveKey(config);
|
|
133
|
+
const decipher = crypto.createDecipheriv(
|
|
134
|
+
"aes-256-gcm",
|
|
135
|
+
key,
|
|
136
|
+
Buffer.from(raw.iv, "base64")
|
|
137
|
+
);
|
|
138
|
+
decipher.setAuthTag(Buffer.from(raw.tag, "base64"));
|
|
139
|
+
const dec = Buffer.concat([
|
|
140
|
+
decipher.update(Buffer.from(raw.enc, "base64")),
|
|
141
|
+
decipher.final(),
|
|
142
|
+
]);
|
|
143
|
+
return JSON.parse(dec.toString("utf8"));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* أي تعديل يدوي في الملف (نص أو byte واحد) بيكسر GCM auth tag ويرجع false،
|
|
148
|
+
* زي ما الملف الناقص خالص بيرجع false.
|
|
149
|
+
*/
|
|
150
|
+
function verifyEncryptedNumbers(filePath, config = {}) {
|
|
151
|
+
try {
|
|
152
|
+
if (!filePath || !fs.existsSync(filePath)) return false;
|
|
153
|
+
const data = readEncryptedStore(filePath, config);
|
|
154
|
+
return (
|
|
155
|
+
Array.isArray(data) &&
|
|
156
|
+
data.length > 0 &&
|
|
157
|
+
data.every((n) => /^\d{6,}$/.test(String(n)))
|
|
158
|
+
);
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* ---------------- كيل سويتش عام لأي عنصر حرج ---------------- */
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* enforceOrDie: نفس آلية enforceSocketOrDie بالظبط لكن عامّه لأي فحص
|
|
168
|
+
* (اسم العنصر + نتيجة boolean بدل ما تبقى مربوطة بالسوكت بس).
|
|
169
|
+
*/
|
|
170
|
+
async function enforceOrDie(name, ok, sock, chatId, config = {}) {
|
|
171
|
+
if (ok) return true;
|
|
172
|
+
|
|
173
|
+
writeKill(config, `critical_missing:${name}`);
|
|
174
|
+
|
|
175
|
+
const msg = config.killMessage || "لا يمكنك استخدام هذا بوت";
|
|
176
|
+
try {
|
|
177
|
+
if (sock && typeof sock.sendMessage === "function" && chatId) {
|
|
178
|
+
await sock.sendMessage(chatId, { text: msg });
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
console.error(`[INTEGRITY-GUARD] "${name}" check failed — bot disabled permanently.`);
|
|
184
|
+
} catch {}
|
|
185
|
+
|
|
186
|
+
setTimeout(() => {
|
|
187
|
+
try {
|
|
188
|
+
process.exit(1);
|
|
189
|
+
} catch {}
|
|
190
|
+
}, 1500);
|
|
191
|
+
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* فحص دوري (بعد الإقلاع) بيعيد التأكد من نفس العناصر كل فترة، عشان لو حد
|
|
197
|
+
* مسح/عدّل الملفات وهو البوت شغال (مش بس وقت الإقلاع) يتوقف برضه.
|
|
198
|
+
*/
|
|
199
|
+
function startPeriodicIntegrityWatch(checks, sock, getChatId, config = {}, intervalMs = 10 * 60 * 1000) {
|
|
200
|
+
const timer = setInterval(async () => {
|
|
201
|
+
if (isKilled(config)) return; // هيتقفل من مكان تاني أصلًا
|
|
202
|
+
for (const [name, fn] of Object.entries(checks)) {
|
|
203
|
+
let ok = false;
|
|
204
|
+
try {
|
|
205
|
+
ok = !!(await fn());
|
|
206
|
+
} catch {
|
|
207
|
+
ok = false;
|
|
208
|
+
}
|
|
209
|
+
if (!ok) {
|
|
210
|
+
const chatId = typeof getChatId === "function" ? getChatId() : getChatId;
|
|
211
|
+
await enforceOrDie(name, false, sock, chatId, config);
|
|
212
|
+
clearInterval(timer);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}, intervalMs);
|
|
217
|
+
if (timer.unref) timer.unref();
|
|
218
|
+
return timer;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ---------------- بصمة ملفات حرجه (manifest موقّع) ---------------- */
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* بديل عام لأي محاولة تعديل "منطق" ملف حرج (مش بس حذفه) — زي حد يفتح
|
|
225
|
+
* isOwner.js ويحط `return true` ثابت، أو يفتح protoGuard.js ويعطل فحص.
|
|
226
|
+
* بنسجّل بصمة SHA-256 لكل ملف مهم وقت الإعداد، ونوقّع القايمه كلها بمفتاح
|
|
227
|
+
* مشتق من نفس بصمة الماركة، فبعد كده أي حرف يتغيّر في أي ملف من الليست
|
|
228
|
+
* يكسر التطابق. المانفست نفسه لو اتلمس (بدون التوقيع الصح) بيفشل فك التشفير.
|
|
229
|
+
*/
|
|
230
|
+
function hashFile(absPath) {
|
|
231
|
+
const buf = fs.readFileSync(absPath);
|
|
232
|
+
return crypto.createHash("sha256").update(buf).digest("hex");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* files: { "relative/or/absolute/path.js": absolutePathOnDisk }
|
|
237
|
+
* بيرجع نفس شكل signEncryptedStore (iv/enc/tag) عشان يتحفظ في ملف زي أي
|
|
238
|
+
* ملف حساس تاني في المشروع.
|
|
239
|
+
*/
|
|
240
|
+
function writeFileManifest(files, manifestPath, config = {}) {
|
|
241
|
+
const entries = {};
|
|
242
|
+
for (const [label, absPath] of Object.entries(files)) {
|
|
243
|
+
entries[label] = hashFile(absPath);
|
|
244
|
+
}
|
|
245
|
+
writeEncryptedStore(manifestPath, entries, config);
|
|
246
|
+
return entries;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* files: نفس الشكل المستخدم وقت الكتابة. بيرجع false لو المانفست ناقص/
|
|
251
|
+
* مش قابل لفك التشفير (تلاعب في التوقيع)، أو لو أي ملف من الليست ناقص،
|
|
252
|
+
* أو لو الهاش بتاعه اتغيّر عن اللي كان متسجل وقت seed-manifest.
|
|
253
|
+
*/
|
|
254
|
+
function verifyFileManifest(files, manifestPath, config = {}) {
|
|
255
|
+
try {
|
|
256
|
+
if (!fs.existsSync(manifestPath)) return false;
|
|
257
|
+
const recorded = readEncryptedStore(manifestPath, config);
|
|
258
|
+
for (const [label, absPath] of Object.entries(files)) {
|
|
259
|
+
if (!fs.existsSync(absPath)) return false;
|
|
260
|
+
if (recorded[label] !== hashFile(absPath)) return false;
|
|
261
|
+
}
|
|
262
|
+
return true;
|
|
263
|
+
} catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export {
|
|
269
|
+
verifyBrandIntegrity,
|
|
270
|
+
verifyPackageIdentity,
|
|
271
|
+
verifyOwnerModule,
|
|
272
|
+
signEncryptedStore,
|
|
273
|
+
writeEncryptedStore,
|
|
274
|
+
readEncryptedStore,
|
|
275
|
+
verifyEncryptedNumbers,
|
|
276
|
+
enforceOrDie,
|
|
277
|
+
startPeriodicIntegrityWatch,
|
|
278
|
+
deriveKey,
|
|
279
|
+
writeFileManifest,
|
|
280
|
+
verifyFileManifest,
|
|
281
|
+
};
|