@ryuu-reinzz/haruka-lib 1.0.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/main/index.js ADDED
@@ -0,0 +1,50 @@
1
+ import addProperty from './socket.js';
2
+ import useSQLiteAuthState from './sqliteAuth.js';
3
+
4
+ const haruka = console.log(`
5
+ ╭──────────────────────────────────────╮
6
+ │ @ryuu-reinzz/haruka-lib │
7
+ │ Small helper for WhatsApp Baileys │
8
+ ╰──────────────────────────────────────╯
9
+
10
+ Usage:
11
+ haruka.extendSocketBot(socket, store, smsg, baileys)
12
+ → extend socket with helper methods
13
+
14
+ haruka.useSQLiteAuthState()
15
+ → SQLite-based auth state for your bot
16
+
17
+ Examples:
18
+ Add socket:
19
+ import makeWASocket, {
20
+ proto,
21
+ generateWAMessageFromContent,
22
+ jidDecode,
23
+ downloadContentFromMessage,
24
+ prepareWAMessageMedia,
25
+ generateMessageID
26
+ } from "baileys";
27
+ const conn = makeWASocket({});
28
+ const baileys = {
29
+ proto,
30
+ generateWAMessageFromContent,
31
+ jidDecode,
32
+ downloadContentFromMessage,
33
+ prepareWAMessageMedia,
34
+ generateMessageID
35
+ }
36
+ import haruka from "@ryuu-reinzz/haruka-lib";
37
+ haruka.extendSocketBot(conn, store, smsg, baileys);
38
+
39
+ SQLite session:
40
+ const sessionPath = "./session"
41
+ if (!fs.existsSync(sessionPath)) fs.mkdirSync(sessionPath, { recursive: true });
42
+ const useSQLiteAuthState = haruka.useSQLiteAuthState;
43
+ const { state, saveCreds } = await useSQLiteAuthState(sessionPath + \"auth.db\");
44
+
45
+ Made by Ryuu
46
+ `);
47
+ haruka.useSQLiteAuthState = useSQLiteAuthState;
48
+ haruka.addProperty = addProperty;
49
+
50
+ export default haruka;
package/main/socket.js ADDED
@@ -0,0 +1,693 @@
1
+ import crypto from "crypto";
2
+ import fetch from "node-fetch";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname } from 'path';
7
+ import stkPkg from 'wa-sticker-formatter';
8
+ const { Sticker } = stkPkg;
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ /**
13
+ * @param {import('baileys').WASocket} socket
14
+ */
15
+
16
+ export default function addProperty(socket, store, smsg, baileys) {
17
+ const {
18
+ proto,
19
+ generateWAMessageFromContent,
20
+ jidDecode,
21
+ downloadContentFromMessage,
22
+ prepareWAMessageMedia,
23
+ generateMessageID
24
+ } = baileys;
25
+
26
+ Object.assign(socket, {
27
+ sendCard: async (jid, options = {}) => {
28
+ const {
29
+ text = "",
30
+ footer = "",
31
+ cards = [],
32
+ quoted = null,
33
+ sender = jid
34
+ } = options
35
+
36
+ if (!cards.length) throw new Error("cards cannot be empty")
37
+
38
+ let carouselCards = []
39
+ const getImageMedia = async (image) => {
40
+ if (!image) throw new Error("Image cannot be empty")
41
+
42
+ if (typeof image === "string") {
43
+ return await prepareWAMessageMedia(
44
+ { image: { url: image } },
45
+ { upload: socket.waUploadToServer }
46
+ )
47
+ }
48
+
49
+ if (Buffer.isBuffer(image)) {
50
+ return await prepareWAMessageMedia(
51
+ { image },
52
+ { upload: socket.waUploadToServer }
53
+ )
54
+ }
55
+
56
+ if (typeof image === "object") {
57
+ return await prepareWAMessageMedia(
58
+ { image },
59
+ { upload: socket.waUploadToServer }
60
+ )
61
+ }
62
+
63
+ throw new Error("Format image tidak didukung")
64
+ }
65
+
66
+ for (let i = 0; i < cards.length; i++) {
67
+ const item = cards[i]
68
+
69
+ let img = await getImageMedia(item.image)
70
+
71
+ carouselCards.push({
72
+ header: proto.Message.InteractiveMessage.Header.fromObject({
73
+ title: item.caption || `Card ${i + 1}`,
74
+ hasMediaAttachment: true,
75
+ ...img
76
+ }),
77
+ nativeFlowMessage: proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
78
+ buttons: Array.isArray(item.buttons) ? item.buttons : []
79
+ }),
80
+ footer: proto.Message.InteractiveMessage.Footer.create({
81
+ text: footer
82
+ })
83
+ })
84
+ }
85
+
86
+ const msg = await generateWAMessageFromContent(
87
+ jid,
88
+ {
89
+ viewOnceMessage: {
90
+ message: {
91
+ messageContextInfo: {
92
+ deviceListMetadata: {},
93
+ deviceListMetadataVersion: 2
94
+ },
95
+ interactiveMessage: proto.Message.InteractiveMessage.fromObject({
96
+ body: proto.Message.InteractiveMessage.Body.fromObject({
97
+ text
98
+ }),
99
+ carouselMessage: proto.Message.InteractiveMessage.CarouselMessage.fromObject({
100
+ cards: carouselCards
101
+ })
102
+ })
103
+ }
104
+ }
105
+ },
106
+ {
107
+ userJid: sender,
108
+ quoted
109
+ }
110
+ )
111
+
112
+ return await socket.relayMessage(jid, msg.message, {
113
+ messageId: msg.key.id
114
+ })
115
+ },
116
+
117
+ sendSticker: async (jid, options = {}) => {
118
+ try {
119
+ if (!options.sticker)
120
+ throw new Error('Please enter the path or buffer of the sticker.')
121
+
122
+ const tmpDir = path.join(__dirname, './tmp')
123
+ if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true })
124
+
125
+ let stickerPath
126
+
127
+ if (Buffer.isBuffer(options.sticker)) {
128
+ stickerPath = path.join(tmpDir, `sticker_${Date.now()}.webp`)
129
+ fs.writeFileSync(stickerPath, options.sticker)
130
+ } else if (typeof options.sticker === 'string') {
131
+ if (!fs.existsSync(options.sticker))
132
+ throw new Error(`File not found: ${options.sticker}`)
133
+ stickerPath = options.sticker
134
+ } else {
135
+ throw new Error('Sticker format not recognized (must be buffer or file path).')
136
+ }
137
+
138
+ const sticker = new Sticker(stickerPath, {
139
+ pack: options.packname || "Made By",
140
+ author: options.author || "漏 饾檷廷饾櫘饾櫔饾櫔 饾檷廷饾櫄饾櫈饾櫍饾櫙饾櫙",
141
+ type: options.type || 'full',
142
+ categories: options.categories || ['馃椏'],
143
+ quality: options.quality || 80
144
+ })
145
+
146
+ const buffer = await sticker.build()
147
+ const result = await socket.sendMessage(jid, { sticker: buffer })
148
+
149
+ if (Buffer.isBuffer(options.sticker)) fs.unlinkSync(stickerPath)
150
+
151
+ return result
152
+ } catch (e) {
153
+ console.error('[sendSticker Error]', e)
154
+ }
155
+ },
156
+
157
+ sendButton: async (jid, content = {}, options = {}) => {
158
+ if (!socket.user?.id) {
159
+ throw new Error("User not authenticated");
160
+ }
161
+
162
+ const {
163
+ text = "",
164
+ caption = "",
165
+ title = "",
166
+ footer = "",
167
+ buttons = [],
168
+ hasMediaAttachment = false,
169
+ image = null,
170
+ video = null,
171
+ document = null,
172
+ mimetype = null,
173
+ jpegThumbnail = null,
174
+ location = null,
175
+ product = null,
176
+ businessOwnerJid = null,
177
+ } = content;
178
+
179
+ if (!Array.isArray(buttons) || buttons.length ===
180
+ 0) {
181
+ throw new Error(
182
+ "buttons must be a non-empty array");
183
+ }
184
+
185
+ const interactiveButtons = [];
186
+
187
+ for (let i = 0; i < buttons.length; i++) {
188
+ const btn = buttons[i];
189
+
190
+ if (!btn || typeof btn !== "object") {
191
+ throw new Error(
192
+ `button[${i}] must be an object`);
193
+ }
194
+
195
+ if (btn.name && btn.buttonParamsJson) {
196
+ interactiveButtons.push(btn);
197
+ continue;
198
+ }
199
+
200
+ if (btn.id || btn.text || btn.displayText) {
201
+ interactiveButtons.push({
202
+ name: "quick_reply",
203
+ buttonParamsJson: JSON
204
+ .stringify({
205
+ display_text: btn
206
+ .text || btn
207
+ .displayText ||
208
+ `Button ${i + 1}`,
209
+ id: btn.id ||
210
+ `quick_${i + 1}`,
211
+ }),
212
+ });
213
+ continue;
214
+ }
215
+
216
+ if (btn.buttonId && btn.buttonText
217
+ ?.displayText) {
218
+ interactiveButtons.push({
219
+ name: "quick_reply",
220
+ buttonParamsJson: JSON
221
+ .stringify({
222
+ display_text: btn
223
+ .buttonText
224
+ .displayText,
225
+ id: btn.buttonId,
226
+ }),
227
+ });
228
+ continue;
229
+ }
230
+
231
+ throw new Error(
232
+ `button[${i}] has invalid shape`);
233
+ }
234
+
235
+ let messageContent = {};
236
+ if (image) {
237
+ const mediaInput = {};
238
+ if (Buffer.isBuffer(image)) {
239
+ mediaInput.image = image;
240
+ } else if (typeof image === "object" && image
241
+ .url) {
242
+ mediaInput.image = {
243
+ url: image.url
244
+ };
245
+ } else if (typeof image === "string") {
246
+ mediaInput.image = {
247
+ url: image
248
+ };
249
+ }
250
+
251
+ const preparedMedia =
252
+ await prepareWAMessageMedia(mediaInput, {
253
+ upload: socket.waUploadToServer,
254
+ });
255
+
256
+ messageContent.header = {
257
+ title: title || "",
258
+ hasMediaAttachment: hasMediaAttachment,
259
+ imageMessage: preparedMedia
260
+ .imageMessage,
261
+ };
262
+ } else if (video) {
263
+ const mediaInput = {};
264
+ if (Buffer.isBuffer(video)) {
265
+ mediaInput.video = video;
266
+ } else if (typeof video === "object" && video
267
+ .url) {
268
+ mediaInput.video = {
269
+ url: video.url
270
+ };
271
+ } else if (typeof video === "string") {
272
+ mediaInput.video = {
273
+ url: video
274
+ };
275
+ }
276
+
277
+ const preparedMedia =
278
+ await prepareWAMessageMedia(mediaInput, {
279
+ upload: socket.waUploadToServer,
280
+ });
281
+
282
+ messageContent.header = {
283
+ title: title || "",
284
+ hasMediaAttachment: hasMediaAttachment,
285
+ videoMessage: preparedMedia
286
+ .videoMessage,
287
+ };
288
+ } else if (document) {
289
+ const mediaInput = {
290
+ document: {}
291
+ };
292
+
293
+ if (Buffer.isBuffer(document)) {
294
+ mediaInput.document = document;
295
+ } else if (typeof document === "object" &&
296
+ document.url) {
297
+ mediaInput.document = {
298
+ url: document.url
299
+ };
300
+ } else if (typeof document === "string") {
301
+ mediaInput.document = {
302
+ url: document
303
+ };
304
+ }
305
+
306
+ if (mimetype) {
307
+ if (typeof mediaInput.document ===
308
+ "object") {
309
+ mediaInput.document.mimetype = mimetype;
310
+ }
311
+ }
312
+
313
+ if (jpegThumbnail) {
314
+ if (typeof mediaInput.document ===
315
+ "object") {
316
+ if (Buffer.isBuffer(jpegThumbnail)) {
317
+ mediaInput.document.jpegThumbnail =
318
+ jpegThumbnail;
319
+ } else if (typeof jpegThumbnail ===
320
+ "string") {
321
+ try {
322
+ const response = await fetch(
323
+ jpegThumbnail);
324
+ const arrayBuffer =
325
+ await response
326
+ .arrayBuffer();
327
+ mediaInput.document
328
+ .jpegThumbnail = Buffer
329
+ .from(arrayBuffer);
330
+ } catch {
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ const preparedMedia =
337
+ await prepareWAMessageMedia(mediaInput, {
338
+ upload: socket.waUploadToServer,
339
+ });
340
+
341
+ messageContent.header = {
342
+ title: title || "",
343
+ hasMediaAttachment: hasMediaAttachment,
344
+ documentMessage: preparedMedia
345
+ .documentMessage,
346
+ };
347
+ } else if (location && typeof location ===
348
+ "object") {
349
+ messageContent.header = {
350
+ title: title || location.name ||
351
+ "Location",
352
+ hasMediaAttachment: hasMediaAttachment,
353
+ locationMessage: {
354
+ degreesLatitude: location
355
+ .degressLatitude || location
356
+ .degreesLatitude || 0,
357
+ degreesLongitude: location
358
+ .degressLongitude || location
359
+ .degreesLongitude || 0,
360
+ name: location.name || "",
361
+ address: location.address || "",
362
+ },
363
+ };
364
+ } else if (product && typeof product === "object") {
365
+ let productImageMessage = null;
366
+ if (product.productImage) {
367
+ const mediaInput = {};
368
+ if (Buffer.isBuffer(product.productImage)) {
369
+ mediaInput.image = product.productImage;
370
+ } else if (
371
+ typeof product.productImage ===
372
+ "object" &&
373
+ product.productImage.url
374
+ ) {
375
+ mediaInput.image = {
376
+ url: product.productImage.url,
377
+ };
378
+ } else if (typeof product.productImage ===
379
+ "string") {
380
+ mediaInput.image = {
381
+ url: product.productImage,
382
+ };
383
+ }
384
+
385
+ const preparedMedia =
386
+ await prepareWAMessageMedia(
387
+ mediaInput, {
388
+ upload: socket.waUploadToServer,
389
+ });
390
+ productImageMessage = preparedMedia
391
+ .imageMessage;
392
+ }
393
+
394
+ messageContent.header = {
395
+ title: title || product.title ||
396
+ "Product",
397
+ hasMediaAttachment: hasMediaAttachment,
398
+ productMessage: {
399
+ product: {
400
+ productImage: productImageMessage,
401
+ productId: product.productId ||
402
+ "",
403
+ title: product.title || "",
404
+ description: product
405
+ .description || "",
406
+ currencyCode: product
407
+ .currencyCode || "USD",
408
+ priceAmount1000: parseInt(
409
+ product.priceAmount1000
410
+ ) || 0,
411
+ retailerId: product
412
+ .retailerId || "",
413
+ url: product.url || "",
414
+ productImageCount: product
415
+ .productImageCount || 1,
416
+ },
417
+ businessOwnerJid: businessOwnerJid ||
418
+ product.businessOwnerJid || socket
419
+ .user.id,
420
+ },
421
+ };
422
+ } else if (title) {
423
+ messageContent.header = {
424
+ title: title,
425
+ hasMediaAttachment: false,
426
+ };
427
+ }
428
+
429
+ const hasMedia = !!(image || video || document ||
430
+ location || product);
431
+ const bodyText = hasMedia ? caption : text ||
432
+ caption;
433
+
434
+ if (bodyText) {
435
+ messageContent.body = {
436
+ text: bodyText
437
+ };
438
+ }
439
+
440
+ if (footer) {
441
+ messageContent.footer = {
442
+ text: footer
443
+ };
444
+ }
445
+
446
+ messageContent.nativeFlowMessage = {
447
+ buttons: interactiveButtons,
448
+ };
449
+
450
+ const payload = proto.Message.InteractiveMessage
451
+ .create(messageContent);
452
+
453
+ const msg = generateWAMessageFromContent(
454
+ jid, {
455
+ viewOnceMessage: {
456
+ message: {
457
+ interactiveMessage: payload,
458
+ },
459
+ },
460
+ }, {
461
+ userJid: socket.user.id,
462
+ quoted: options?.quoted || null,
463
+ }
464
+ );
465
+ const isGroup = jid.endsWith("@g.us");
466
+ const additionalNodes = [{
467
+ tag: "biz",
468
+ attrs: {},
469
+ content: [{
470
+ tag: "interactive",
471
+ attrs: {
472
+ type: "native_flow",
473
+ v: "1",
474
+ },
475
+ content: [{
476
+ tag: "native_flow",
477
+ attrs: {
478
+ v: "9",
479
+ name: "mixed",
480
+ },
481
+ }, ],
482
+ }, ],
483
+ }, ];
484
+
485
+ if (!isGroup) {
486
+ additionalNodes.push({
487
+ tag: "bot",
488
+ attrs: {
489
+ biz_bot: "1"
490
+ },
491
+ });
492
+ }
493
+
494
+ await socket.relayMessage(jid, msg.message, {
495
+ messageId: msg.key.id,
496
+ additionalNodes,
497
+ });
498
+
499
+ return msg;
500
+ },
501
+ sendAlbum: {
502
+ async value(jid, items = [], options = {}) {
503
+ if (!this.user?.id) {
504
+ throw new Error("User not authenticated");
505
+ }
506
+
507
+ const messageSecret = new Uint8Array(32);
508
+ crypto.getRandomValues(messageSecret);
509
+
510
+ const messageContent = {
511
+ messageContextInfo: { messageSecret },
512
+ albumMessage: {
513
+ expectedImageCount: items.filter((a) =>
514
+ a?.image).length,
515
+ expectedVideoCount: items.filter((a) =>
516
+ a?.video).length,
517
+ },
518
+ };
519
+
520
+ const generationOptions = {
521
+ userJid: this.user.id,
522
+ upload: this.waUploadToServer,
523
+ quoted: options?.quoted || null,
524
+ ephemeralExpiration: options?.quoted
525
+ ?.expiration ?? 0,
526
+ };
527
+
528
+ const album = generateWAMessageFromContent(jid,
529
+ messageContent, generationOptions);
530
+
531
+ await this.relayMessage(album.key.remoteJid, album
532
+ .message, {
533
+ messageId: album.key.id,
534
+ });
535
+
536
+ await Promise.all(
537
+ items.map(async (content) => {
538
+ const mediaSecret =
539
+ new Uint8Array(32);
540
+ crypto.getRandomValues(
541
+ mediaSecret);
542
+
543
+ const mediaMsg =
544
+ await generateWAMessage(
545
+ album.key.remoteJid,
546
+ content, {
547
+ upload: this
548
+ .waUploadToServer,
549
+ ephemeralExpiration: options
550
+ ?.quoted
551
+ ?.expiration ??
552
+ 0,
553
+ });
554
+
555
+ mediaMsg.message
556
+ .messageContextInfo = {
557
+ messageSecret: mediaSecret,
558
+ messageAssociation: {
559
+ associationType: 1,
560
+ parentMessageKey: album
561
+ .key,
562
+ },
563
+ };
564
+
565
+ return this.relayMessage(
566
+ mediaMsg.key.remoteJid,
567
+ mediaMsg.message, {
568
+ messageId: mediaMsg
569
+ .key.id,
570
+ });
571
+ })
572
+ );
573
+
574
+ return album;
575
+ }
576
+ },
577
+
578
+ sendOrder: async (jid, orderData, options = {}) => {
579
+ if (!socket.user?.id) {
580
+ throw new Error("User not authenticated");
581
+ }
582
+
583
+ let thumbnail = null;
584
+ if (orderData.thumbnail) {
585
+ if (Buffer.isBuffer(orderData.thumbnail)) {
586
+ thumbnail = orderData.thumbnail;
587
+ } else if (typeof orderData.thumbnail === "string") {
588
+ try {
589
+ if (orderData.thumbnail.startsWith("http")) {
590
+ const response = await fetch(orderData.thumbnail);
591
+ const arrayBuffer = await response.arrayBuffer();
592
+ thumbnail = Buffer.from(arrayBuffer);
593
+ } else {
594
+ thumbnail = Buffer.from(orderData.thumbnail, "base64");
595
+ }
596
+ } catch (e) {
597
+ socket.logger?.warn(
598
+ {
599
+ err: e.message
600
+ },
601
+ "Failed to fetch/convert thumbnail"
602
+ );
603
+ thumbnail = null;
604
+ }
605
+ }
606
+ }
607
+
608
+ const orderMessage = proto.Message.OrderMessage.fromObject({
609
+ orderId: orderData.orderId || generateMessageID(),
610
+ thumbnail: thumbnail,
611
+ itemCount: orderData.itemCount || 1,
612
+ status: orderData.status || proto.Message.OrderMessage.OrderStatus.INQUIRY,
613
+ surface: orderData.surface || proto.Message.OrderMessage.OrderSurface.CATALOG,
614
+ message: orderData.message || "",
615
+ orderTitle: orderData.orderTitle || "Order",
616
+ sellerJid: orderData.sellerJid || socket.user.id,
617
+ token: orderData.token || "",
618
+ totalAmount1000: orderData.totalAmount1000 || 0,
619
+ totalCurrencyCode: orderData.totalCurrencyCode || "IDR",
620
+ contextInfo: {
621
+ ...(options.contextInfo || {}),
622
+ ...(options.mentions ?
623
+ {
624
+ mentionedJid: options.mentions,
625
+ } :
626
+ {}),
627
+ },
628
+ });
629
+
630
+ const msg = proto.Message.create({
631
+ orderMessage,
632
+ });
633
+
634
+ const message = generateWAMessageFromContent(jid, msg, {
635
+ userJid: socket.user.id,
636
+ timestamp: options.timestamp || new Date(),
637
+ quoted: options.quoted || null,
638
+ ephemeralExpiration: options.ephemeralExpiration || 0,
639
+ messageId: options.messageId || null,
640
+ });
641
+
642
+ return await socket.relayMessage(message.key.remoteJid, message.message, {
643
+ messageId: message.key.id,
644
+ });
645
+ },
646
+
647
+ getPNFromLid: async (m, lidInput) => {
648
+ if (!lidInput) throw new Error("Misssing input");
649
+ try {
650
+ let chat = m.chat
651
+ if (chat.endsWith('@g.us')) {
652
+
653
+ const metadata = await socket.groupMetadata(chat);
654
+ if (!metadata || !metadata.participants) return lidInput;
655
+
656
+ const found = metadata.participants.find(entry => entry.id === lidInput);
657
+ return found ? found.phoneNumber : lidInput;
658
+ } else {
659
+ const { remoteJid, remoteJidAlt } = m.key
660
+ if (remoteJid === lidInput && remoteJidAlt.endsWith("@s.whatsapp.net")) {
661
+ return remoteJidAlt
662
+ }
663
+ }
664
+ } catch (e) {
665
+ console.error('Gagal ambil group metadata:', e.message);
666
+ return lidInput;
667
+ }
668
+ },
669
+
670
+ getLidFromPN: async (m, jidInput) => {
671
+ if (!jidInput) throw new Error("Misssing jid input");
672
+ try {
673
+ let chat = m.chat;
674
+ if (chat.endsWith('@g.us')) {
675
+ const metadata = await socket.groupMetadata(chat);
676
+ if (!metadata || !metadata.participants) return jidInput;
677
+
678
+ const found = metadata.participants.find(entry => entry.phoneNumber === jidInput);
679
+ return found ? found.id : jidInput;
680
+ } else {
681
+ const { remoteJid, remoteJidAlt } = m.key
682
+ if (!remoteJid || !remoteJidAlt) return null
683
+ if (remoteJidAlt === jidInput && remoteJid.endsWith("@lid")) {
684
+ return remoteJid
685
+ }
686
+ }
687
+ } catch (e) {
688
+ console.error('Gagal ambil group metadata:', e.message);
689
+ return jidInput;
690
+ }
691
+ },
692
+ });
693
+ };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Custom SQLite Auth Store untuk Baileys
3
+ * by Ryuu
4
+ */
5
+
6
+ //import Database from "bun:sqlite";
7
+ //jika menggunakan bun runtime
8
+ import Database from "better-sqlite3";
9
+ /**
10
+ * Membuat atau mengambil auth state dari SQLite
11
+ * @param {string} dbPath - Lokasi file SQLite (contoh: "./auth.db")
12
+ */
13
+ export async function useSQLiteAuthState(dbPath = "./auth.db", baileys) {
14
+ const { proto, BufferJSON, initAuthCreds } = baileys;
15
+ const db = new Database(dbPath);
16
+ db.pragma("journal_mode = WAL");
17
+
18
+ db.prepare(`
19
+ CREATE TABLE IF NOT EXISTS baileys_state (
20
+ key TEXT PRIMARY KEY,
21
+ value BLOB
22
+ )
23
+ `).run();
24
+
25
+ const load = (key) => {
26
+ const row = db.prepare("SELECT value FROM baileys_state WHERE key = ?").get(key);
27
+ if (!row) return null;
28
+ try {
29
+ return JSON.parse(row.value.toString(), BufferJSON.reviver);
30
+ } catch {
31
+ return null;
32
+ }
33
+ };
34
+
35
+ const save = (key, data) => {
36
+ const json = JSON.stringify(data, BufferJSON.replacer);
37
+ const buf = Buffer.from(json, "utf8");
38
+ db.prepare("REPLACE INTO baileys_state (key, value) VALUES (?, ?)").run(key, buf);
39
+ };
40
+
41
+ const creds = load("creds") || initAuthCreds();
42
+
43
+ const keys = {};
44
+ const categories = [
45
+ "pre-key",
46
+ "session",
47
+ "sender-key",
48
+ "app-state-sync-key",
49
+ "app-state-sync-version"
50
+ ];
51
+
52
+ for (const category of categories) {
53
+ keys[category] = {};
54
+ const rows = db
55
+ .prepare("SELECT key, value FROM baileys_state WHERE key LIKE ?")
56
+ .all(`${category}:%`);
57
+ for (const row of rows) {
58
+ try {
59
+ keys[category][row.key.slice(category.length + 1)] = JSON.parse(row.value.toString());
60
+ } catch {}
61
+ }
62
+ }
63
+
64
+ async function saveCreds() {
65
+ save("creds", creds);
66
+ }
67
+
68
+ const set = (category, id, value) => {
69
+ const key = `${category}:${id}`;
70
+ save(key, value);
71
+ };
72
+
73
+ const get = (category, id) => {
74
+ const key = `${category}:${id}`;
75
+ return load(key);
76
+ };
77
+
78
+ const del = (category, id) => {
79
+ const key = `${category}:${id}`;
80
+ db.prepare("DELETE FROM baileys_state WHERE key = ?").run(key);
81
+ };
82
+
83
+ return {
84
+ state: {
85
+ creds,
86
+ keys: {
87
+ get: async (type, ids) => {
88
+ const data = {};
89
+ for (const id of ids) {
90
+ const value = load(`${type}:${id}`);
91
+ if (value) data[id] = value;
92
+ }
93
+ return data;
94
+ },
95
+ set: async (data) => {
96
+ for (const category in data) {
97
+ for (const id in data[category]) {
98
+ const value = data[category][id];
99
+ save(`${category}:${id}`, value);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ },
105
+ saveCreds: async () => save("creds", creds)
106
+ };
107
+ setInterval(async () => {
108
+ await save("creds", creds);
109
+ }, 30_000);
110
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@ryuu-reinzz/haruka-lib",
3
+ "version": "1.0.0",
4
+ "description": "Library extra for bot WhatsApp",
5
+ "main": "main/index.js",
6
+ "type": "module",
7
+ "author": "RyuuReinzz",
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "whatsapp-bot",
11
+ "baileys",
12
+ "haruka",
13
+ "nodejs",
14
+ "ryuu-kun",
15
+ ":v"
16
+ ],
17
+ "dependencies": {
18
+ "better-sqlite3": "^12.5.0",
19
+ "node-fetch": "^2.6.1",
20
+ "wa-sticker-formatter": "^4.4.4"
21
+ }
22
+ }