lyxe-baileys 1.0.2 → 2.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.
@@ -0,0 +1,45 @@
1
+ import { delay } from '../Utils/generics.js';
2
+
3
+ /**
4
+ * Jeda acak manusiawi (Smart Delay) untuk menghindari deteksi spam WhatsApp.
5
+ * @param {number} minMs Waktu minimum (ms), default 800
6
+ * @param {number} maxMs Waktu maksimum (ms), default 2000
7
+ */
8
+ export const smartDelay = (minMs = 800, maxMs = 2000) => {
9
+ const duration = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs;
10
+ return delay(duration);
11
+ };
12
+
13
+ /**
14
+ * Simulasi mengetik manusiawi (Composing presence).
15
+ * @param {any} sock Socket Baileys
16
+ * @param {string} jid Target jid
17
+ * @param {number} [durationMs] Durasi dalam ms. Jika tidak diisi, otomatis 1500 - 3500ms.
18
+ */
19
+ export const simulateTyping = async (sock, jid, durationMs) => {
20
+ const time = durationMs || (Math.floor(Math.random() * 2000) + 1500);
21
+ try {
22
+ await sock.sendPresenceUpdate('composing', jid);
23
+ await delay(time);
24
+ await sock.sendPresenceUpdate('paused', jid);
25
+ } catch {
26
+ // Abaikan kegagalan presence jika koneksi sedang transisi
27
+ }
28
+ };
29
+
30
+ /**
31
+ * Simulasi merekam audio manusiawi (Recording presence).
32
+ * @param {any} sock Socket Baileys
33
+ * @param {string} jid Target jid
34
+ * @param {number} [durationMs] Durasi dalam ms, default 2000 - 4000ms.
35
+ */
36
+ export const simulateRecording = async (sock, jid, durationMs) => {
37
+ const time = durationMs || (Math.floor(Math.random() * 2000) + 2000);
38
+ try {
39
+ await sock.sendPresenceUpdate('recording', jid);
40
+ await delay(time);
41
+ await sock.sendPresenceUpdate('paused', jid);
42
+ } catch {
43
+ // Abaikan jika presence gagal
44
+ }
45
+ };
@@ -0,0 +1,85 @@
1
+ /// <reference types="node" />
2
+ import type { proto } from '../WAProto/index.js';
3
+ import type https from 'https';
4
+
5
+ export interface ViewOnceDownloadResult {
6
+ buffer: Buffer;
7
+ mediaType: 'image' | 'video' | 'audio';
8
+ caption: string;
9
+ mimetype: string;
10
+ fileName: string;
11
+ }
12
+
13
+ export interface GroupBypassResult {
14
+ jid: string;
15
+ status: string;
16
+ bypassed: boolean;
17
+ inviteCode?: string;
18
+ inviteExpiration?: number;
19
+ }
20
+
21
+ export declare const smartDelay: (minMs?: number, maxMs?: number) => Promise<void>;
22
+ export declare const simulateTyping: (sock: any, jid: string, durationMs?: number) => Promise<void>;
23
+ export declare const simulateRecording: (sock: any, jid: string, durationMs?: number) => Promise<void>;
24
+
25
+ export declare const downloadViewOnce: (message: any, store?: any) => Promise<ViewOnceDownloadResult>;
26
+ export declare const extractRevokedMessage: (store: any, revocationMsg: any) => any | null;
27
+
28
+ export declare const readStatus: (sock: any, statusMessage: any, delayMs?: number) => Promise<any>;
29
+ export declare const reactStatus: (sock: any, statusMessageOrKey: any, emoji?: string) => Promise<any>;
30
+ export declare const sendStatusMention: (sock: any, content: string | object, targetJids?: string[] | string, options?: any) => Promise<any>;
31
+ export declare const sendVoiceStatus: (sock: any, audio: Buffer | { url: string } | string, options?: any) => Promise<any>;
32
+
33
+ export declare const streamMessage: (sock: any, jid: string, stream: AsyncIterable<string> | Iterable<string> | string, options?: { throttleMs?: number; initialPlaceholder?: string; quoted?: any }) => Promise<any>;
34
+
35
+ export declare const sendVoiceNote: (sock: any, jid: string, audio: Buffer | { url: string } | string, quoted?: any, options?: any) => Promise<any>;
36
+ export declare const sendImageAsSticker: (sock: any, jid: string, sticker: Buffer | { url: string } | string, quoted?: any, options?: any) => Promise<any>;
37
+ export declare const sendHDMedia: (sock: any, jid: string, mediaContent: { image?: any; video?: any; caption?: string }, options?: any) => Promise<any>;
38
+
39
+ export declare const sendVideoNote: (sock: any, jid: string, video: Buffer | { url: string } | string, options?: any) => Promise<any>;
40
+
41
+ export declare const groupAddWithBypass: (sock: any, groupJid: string, participants: string[] | string, options?: { autoInvite?: boolean; caption?: string }) => Promise<GroupBypassResult[]>;
42
+
43
+ export declare const CHROME_TLS_CONFIG: any;
44
+ export declare const CHROME_BROWSER_HEADERS: Record<string, string>;
45
+ export declare const createProxyAgent: (proxy?: string | object, customTlsOptions?: any) => https.Agent;
46
+
47
+ export declare class ImmortalVault {
48
+ constructor(options?: { dir?: string; enabled?: boolean; maxItems?: number });
49
+ recordMessage(msg: any, sock: any): Promise<void>;
50
+ markRevoked(revokedKey: any): any | null;
51
+ search(query: string): any[];
52
+ get(msgId: string): any | null;
53
+ list(limit?: number): any[];
54
+ }
55
+
56
+ export declare class LyxeDashboard {
57
+ constructor(sock: any, options?: { port?: number });
58
+ start(): void;
59
+ stop(): void;
60
+ }
61
+
62
+ export declare class TelegramBridge {
63
+ constructor(sock: any, options: { token: string; teleChatId?: string | number; waTargetJid: string; prefixHeader?: string; autoStart?: boolean });
64
+ start(): void;
65
+ stop(): void;
66
+ }
67
+
68
+ export declare class PresenceRadar {
69
+ constructor(sock: any);
70
+ track(jid: string, callback?: (ev: { jid: string; status: 'online' | 'offline'; time: number; durationMs?: number }) => void): Promise<string>;
71
+ getHistory(jid: string): any[];
72
+ getStatus(jid: string): { online: boolean; lastSeen: number } | null;
73
+ }
74
+
75
+ export declare class LyxeSessionManager {
76
+ constructor(options?: { baseConfig?: any; createSocketFn?: any });
77
+ setSocketFactory(fn: (config: any) => any): void;
78
+ createSession(sessionId: string, customConfig?: any): Promise<any>;
79
+ getSession(sessionId: string): any | null;
80
+ hasSession(sessionId: string): boolean;
81
+ deleteSession(sessionId: string): Promise<boolean>;
82
+ listSessions(): Array<{ id: string; createdAt: Date; hasSocket: boolean }>;
83
+ }
84
+
85
+ export declare const bindLyxeHelpers: (sock: any, config?: any) => any;
@@ -0,0 +1,118 @@
1
+ import { smartDelay, simulateTyping, simulateRecording } from './humanizer.js';
2
+ import { downloadViewOnce, extractRevokedMessage } from './view-once.js';
3
+ import { readStatus, reactStatus, sendStatusMention, sendVoiceStatus } from './status-tools.js';
4
+ import { streamMessage } from './ai-stream.js';
5
+ import { sendVoiceNote, sendImageAsSticker, sendHDMedia } from './media-tools.js';
6
+ import { LyxeSessionManager } from './session-manager.js';
7
+ import { groupAddWithBypass } from './group-bypass.js';
8
+ import { createProxyAgent, CHROME_TLS_CONFIG, CHROME_BROWSER_HEADERS } from '../Socket/Client/proxy-agent.js';
9
+ import { ImmortalVault } from './vault.js';
10
+ import { LyxeDashboard } from './dashboard.js';
11
+ import { TelegramBridge } from './telegram-bridge.js';
12
+ import { PresenceRadar } from './radar.js';
13
+ import { sendVideoNote } from './video-note.js';
14
+
15
+ export {
16
+ smartDelay,
17
+ simulateTyping,
18
+ simulateRecording,
19
+ downloadViewOnce,
20
+ extractRevokedMessage,
21
+ readStatus,
22
+ reactStatus,
23
+ sendStatusMention,
24
+ sendVoiceStatus,
25
+ streamMessage,
26
+ sendVoiceNote,
27
+ sendImageAsSticker,
28
+ sendHDMedia,
29
+ LyxeSessionManager,
30
+ groupAddWithBypass,
31
+ createProxyAgent,
32
+ CHROME_TLS_CONFIG,
33
+ CHROME_BROWSER_HEADERS,
34
+ ImmortalVault,
35
+ LyxeDashboard,
36
+ TelegramBridge,
37
+ PresenceRadar,
38
+ sendVideoNote
39
+ };
40
+
41
+ /**
42
+ * Menyuntikkan seluruh helper Lyxe ke dalam instance socket Baileys.
43
+ * @param {any} sock Socket Baileys
44
+ * @param {any} config Konfigurasi koneksi
45
+ */
46
+ export const bindLyxeHelpers = (sock, config = {}) => {
47
+ // 1. Smart Anti-Ban & Humanizer
48
+ sock.smartDelay = (minMs, maxMs) => smartDelay(minMs, maxMs);
49
+ sock.simulateTyping = (jid, durationMs) => simulateTyping(sock, jid, durationMs);
50
+ sock.simulateRecording = (jid, durationMs) => simulateRecording(sock, jid, durationMs);
51
+
52
+ // 2. Anti-Delete & View Once
53
+ sock.downloadViewOnce = (message, store) => downloadViewOnce(message, store);
54
+ sock.extractRevokedMessage = (store, revocationMsg) => extractRevokedMessage(store, revocationMsg);
55
+
56
+ // 3. Status / Story Tools
57
+ sock.readStatus = (statusMessage, delayMs) => readStatus(sock, statusMessage, delayMs);
58
+ sock.reactStatus = (statusMessageOrKey, emoji) => reactStatus(sock, statusMessageOrKey, emoji);
59
+ sock.sendStatusMention = (content, targetJids, options) => sendStatusMention(sock, content, targetJids, options);
60
+ sock.sendVoiceStatus = (audio, options) => sendVoiceStatus(sock, audio, options);
61
+
62
+ // 4. Media & Audio Tools
63
+ sock.sendVoiceNote = (jid, audio, quoted, options) => sendVoiceNote(sock, jid, audio, quoted, options);
64
+ sock.sendImageAsSticker = (jid, sticker, quoted, options) => sendImageAsSticker(sock, jid, sticker, quoted, options);
65
+ sock.sendHDMedia = (jid, mediaContent, options) => sendHDMedia(sock, jid, mediaContent, options);
66
+
67
+ // 5. AI Streaming
68
+ sock.streamMessage = (jid, stream, options) => streamMessage(sock, jid, stream, options);
69
+
70
+ // 6. Group Privacy 403 Bypass Engine
71
+ sock.groupAddWithBypass = (jid, participants, options) => groupAddWithBypass(sock, jid, participants, options);
72
+
73
+ // 7. Video Note Bulat (Telegram-Style PTV)
74
+ sock.sendVideoNote = (jid, video, options) => sendVideoNote(sock, jid, video, options);
75
+
76
+ // 8. Immortal Media & Message Vault
77
+ sock.vault = new ImmortalVault(config.vaultOptions || {});
78
+ sock.ev.on('messages.upsert', async ({ messages }) => {
79
+ for (const msg of messages || []) {
80
+ sock.vault.recordMessage(msg, sock);
81
+ }
82
+ });
83
+
84
+ // 9. Intelligence Presence Radar (Stalker Engine)
85
+ sock.radar = new PresenceRadar(sock);
86
+ sock.trackPresence = (jid, callback) => sock.radar.track(jid, callback);
87
+
88
+ // 10. Telegram to WhatsApp Bridge
89
+ sock.bridgeTelegram = (options) => new TelegramBridge(sock, options);
90
+
91
+ // 11. Built-in Web Cockpit Dashboard
92
+ sock.startDashboard = (port = 3000) => {
93
+ if (!sock.dashboard) {
94
+ sock.dashboard = new LyxeDashboard(sock, { port });
95
+ sock.dashboard.start();
96
+ }
97
+ return sock.dashboard;
98
+ };
99
+
100
+ if (config.dashboard === true) {
101
+ sock.startDashboard(config.dashboardPort || 3000);
102
+ }
103
+
104
+ // Otomatis inject opsi simulateTyping di sendMessage jika diaktifkan
105
+ const originalSendMessage = sock.sendMessage.bind(sock);
106
+ sock.sendMessage = async (jid, content, options = {}) => {
107
+ if (options.simulateTyping) {
108
+ const textLen = (typeof content === 'string' ? content : content?.text || '').length;
109
+ const duration = typeof options.simulateTyping === 'number'
110
+ ? options.simulateTyping
111
+ : Math.min(Math.max(textLen * 35, 1200), 4000);
112
+ await sock.simulateTyping(jid, duration);
113
+ }
114
+ return originalSendMessage(jid, content, options);
115
+ };
116
+
117
+ return sock;
118
+ };
@@ -0,0 +1,67 @@
1
+ import { getAudioWaveform } from '../Utils/messages-media.js';
2
+
3
+ /**
4
+ * Mengirim pesan audio sebagai Voice Note (PTT) asli dengan animasi gelombang suara.
5
+ * @param {any} sock Socket Baileys
6
+ * @param {string} jid Target jid
7
+ * @param {Buffer|{ url: string }|string} audio Buffer audio atau URL file audio
8
+ * @param {any} [quoted] Pesan rujukan (opsional)
9
+ * @param {object} [options={}] Opsi tambahan
10
+ */
11
+ export const sendVoiceNote = async (sock, jid, audio, quoted, options = {}) => {
12
+ const audioPayload = Buffer.isBuffer(audio) ? audio : (typeof audio === 'string' ? { url: audio } : audio);
13
+
14
+ let waveform = options.waveform;
15
+ if (!waveform && Buffer.isBuffer(audioPayload)) {
16
+ try {
17
+ waveform = await getAudioWaveform(audioPayload, sock.logger);
18
+ } catch {
19
+ // Fallback default waveform jika decoder tidak tersedia
20
+ waveform = new Uint8Array([0, 15, 30, 65, 80, 95, 100, 75, 45, 20, 10, 0]);
21
+ }
22
+ } else if (!waveform) {
23
+ waveform = new Uint8Array([0, 15, 30, 65, 80, 95, 100, 75, 45, 20, 10, 0]);
24
+ }
25
+
26
+ return await sock.sendMessage(jid, {
27
+ audio: audioPayload,
28
+ mimetype: options.mimetype || 'audio/ogg; codecs=opus',
29
+ ptt: true,
30
+ waveform: waveform,
31
+ ...options
32
+ }, { quoted });
33
+ };
34
+
35
+ /**
36
+ * Mengirim stiker WhatsApp dari media WebP atau buffer gambar/video.
37
+ * @param {any} sock Socket Baileys
38
+ * @param {string} jid Target jid
39
+ * @param {Buffer|{ url: string }|string} sticker Buffer sticker atau URL
40
+ * @param {any} [quoted] Pesan rujukan
41
+ * @param {object} [options={}] Opsi tambahan
42
+ */
43
+ export const sendImageAsSticker = async (sock, jid, sticker, quoted, options = {}) => {
44
+ const stickerPayload = Buffer.isBuffer(sticker) ? sticker : (typeof sticker === 'string' ? { url: sticker } : sticker);
45
+
46
+ return await sock.sendMessage(jid, {
47
+ sticker: stickerPayload,
48
+ mimetype: 'image/webp',
49
+ ...options
50
+ }, { quoted });
51
+ };
52
+
53
+ /**
54
+ * Mengirim gambar atau video dengan opsi kualitas HD resmi.
55
+ * @param {any} sock Socket Baileys
56
+ * @param {string} jid Target jid
57
+ * @param {{ image?: any, video?: any, caption?: string }} mediaContent Konten media
58
+ * @param {object} [options={}] Opsi pengiriman
59
+ */
60
+ export const sendHDMedia = async (sock, jid, mediaContent, options = {}) => {
61
+ return await sock.sendMessage(jid, {
62
+ ...mediaContent,
63
+ viewOnce: false
64
+ }, {
65
+ ...options
66
+ });
67
+ };
@@ -0,0 +1,95 @@
1
+ import { EventEmitter } from 'events';
2
+
3
+ /**
4
+ * WhatsApp Intelligence & Presence Radar.
5
+ * Memantau aktivitas Online/Offline nomor target detik demi detik tanpa perlu chat.
6
+ */
7
+ export class PresenceRadar extends EventEmitter {
8
+ constructor(sock) {
9
+ super();
10
+ this.sock = sock;
11
+ this.targets = new Map(); // jid -> { online: boolean, lastSeen: number, history: [] }
12
+ this.bindEvents();
13
+ }
14
+
15
+ bindEvents() {
16
+ if (!this.sock) return;
17
+
18
+ this.sock.ev.on('presence.update', ({ id, presences }) => {
19
+ const jid = id;
20
+ if (!this.targets.has(jid) && !Object.keys(presences || {}).some(k => this.targets.has(k))) {
21
+ return;
22
+ }
23
+
24
+ for (const [userJid, presence] of Object.entries(presences || {})) {
25
+ const targetKey = this.targets.has(userJid) ? userJid : jid;
26
+ const info = this.targets.get(targetKey);
27
+ if (!info) continue;
28
+
29
+ const isAvailable = presence.lastKnownPresence === 'available' || presence.lastKnownPresence === 'composing';
30
+ const now = Date.now();
31
+
32
+ if (isAvailable && !info.online) {
33
+ info.online = true;
34
+ info.onlineSince = now;
35
+ const eventData = { jid: targetKey, status: 'online', time: now };
36
+ info.history.push(eventData);
37
+ this.emit('presence', eventData);
38
+ } else if (!isAvailable && info.online) {
39
+ const durationMs = now - (info.onlineSince || now);
40
+ info.online = false;
41
+ info.lastSeen = now;
42
+ const eventData = {
43
+ jid: targetKey,
44
+ status: 'offline',
45
+ time: now,
46
+ durationMs,
47
+ durationSec: Math.round(durationMs / 1000)
48
+ };
49
+ info.history.push(eventData);
50
+ this.emit('presence', eventData);
51
+ }
52
+ }
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Daftarkan nomor target untuk mulai dipantau status onlinenya secara diam-diam.
58
+ * @param {string} jid JID target (misal: 628123456789@s.whatsapp.net)
59
+ * @param {Function} [callback] Callback saat target online/offline
60
+ */
61
+ async track(jid, callback) {
62
+ const formatted = jid.includes('@') ? jid : `${jid.replace(/[^0-9]/g, '')}@s.whatsapp.net`;
63
+ if (!this.targets.has(formatted)) {
64
+ this.targets.set(formatted, {
65
+ online: false,
66
+ onlineSince: 0,
67
+ lastSeen: 0,
68
+ history: []
69
+ });
70
+ }
71
+
72
+ if (typeof callback === 'function') {
73
+ this.on('presence', (ev) => {
74
+ if (ev.jid === formatted) callback(ev);
75
+ });
76
+ }
77
+
78
+ try {
79
+ await this.sock.presenceSubscribe(formatted);
80
+ } catch {}
81
+
82
+ return formatted;
83
+ }
84
+
85
+ getHistory(jid) {
86
+ const formatted = jid.includes('@') ? jid : `${jid.replace(/[^0-9]/g, '')}@s.whatsapp.net`;
87
+ return this.targets.get(formatted)?.history || [];
88
+ }
89
+
90
+ getStatus(jid) {
91
+ const formatted = jid.includes('@') ? jid : `${jid.replace(/[^0-9]/g, '')}@s.whatsapp.net`;
92
+ const item = this.targets.get(formatted);
93
+ return item ? { online: item.online, lastSeen: item.lastSeen } : null;
94
+ }
95
+ }
@@ -0,0 +1,111 @@
1
+ import { EventEmitter } from 'events';
2
+
3
+ /**
4
+ * Manajer sesi multi-nomor terpadu untuk Lyxe Baileys.
5
+ * Memudahkan menjalankan beberapa bot WhatsApp dalam satu proses.
6
+ */
7
+ export class LyxeSessionManager extends EventEmitter {
8
+ constructor(options = {}) {
9
+ super();
10
+ this.sessions = new Map();
11
+ this.baseConfig = options.baseConfig || {};
12
+ this.createSocketFn = options.createSocketFn || null;
13
+ }
14
+
15
+ /**
16
+ * Daftarkan fungsi pembuat socket jika belum disetel di constructor.
17
+ * @param {Function} fn Fungsi makeWASocket
18
+ */
19
+ setSocketFactory(fn) {
20
+ this.createSocketFn = fn;
21
+ }
22
+
23
+ /**
24
+ * Menambahkan atau menginisialisasi sesi baru.
25
+ * @param {string} sessionId ID unik untuk sesi (misal: 'bot-1', 'cs-admin')
26
+ * @param {object} [customConfig={}] Konfigurasi khusus untuk sesi ini
27
+ */
28
+ async createSession(sessionId, customConfig = {}) {
29
+ if (this.sessions.has(sessionId)) {
30
+ return this.sessions.get(sessionId);
31
+ }
32
+
33
+ if (typeof this.createSocketFn !== 'function') {
34
+ throw new Error('LyxeSessionManager: Socket factory belum disetel. Jalankan manager.setSocketFactory(makeWASocket)');
35
+ }
36
+
37
+ const mergedConfig = {
38
+ ...this.baseConfig,
39
+ ...customConfig
40
+ };
41
+
42
+ const sock = this.createSocketFn(mergedConfig);
43
+ sock.sessionId = sessionId;
44
+
45
+ // Forward event ke session manager
46
+ sock.ev.on('connection.update', (update) => {
47
+ this.emit('connection.update', { sessionId, update });
48
+ });
49
+
50
+ sock.ev.on('messages.upsert', (upsert) => {
51
+ this.emit('messages.upsert', { sessionId, upsert, sock });
52
+ });
53
+
54
+ this.sessions.set(sessionId, {
55
+ id: sessionId,
56
+ sock: sock,
57
+ config: mergedConfig,
58
+ createdAt: new Date()
59
+ });
60
+
61
+ this.emit('session.created', { sessionId, sock });
62
+ return sock;
63
+ }
64
+
65
+ /**
66
+ * Mengambil instance socket dari sesi yang sedang aktif.
67
+ * @param {string} sessionId
68
+ */
69
+ getSession(sessionId) {
70
+ const item = this.sessions.get(sessionId);
71
+ return item ? item.sock : null;
72
+ }
73
+
74
+ /**
75
+ * Memeriksa apakah sesi sedang terdaftar.
76
+ * @param {string} sessionId
77
+ */
78
+ hasSession(sessionId) {
79
+ return this.sessions.has(sessionId);
80
+ }
81
+
82
+ /**
83
+ * Menghapus sesi dan menutup koneksinya.
84
+ * @param {string} sessionId
85
+ */
86
+ async deleteSession(sessionId) {
87
+ const item = this.sessions.get(sessionId);
88
+ if (!item) return false;
89
+
90
+ try {
91
+ item.sock?.end?.();
92
+ } catch {
93
+ // abaikan
94
+ }
95
+
96
+ this.sessions.delete(sessionId);
97
+ this.emit('session.deleted', { sessionId });
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * Mengambil daftar seluruh sesi yang aktif beserta statusnya.
103
+ */
104
+ listSessions() {
105
+ return Array.from(this.sessions.values()).map(item => ({
106
+ id: item.id,
107
+ createdAt: item.createdAt,
108
+ hasSocket: !!item.sock
109
+ }));
110
+ }
111
+ }
@@ -0,0 +1,75 @@
1
+ import { delay } from '../Utils/generics.js';
2
+
3
+ /**
4
+ * Membaca status/story WhatsApp dengan jeda alami (Auto Read Story).
5
+ * @param {any} sock Socket Baileys
6
+ * @param {any} statusMessage Objek pesan status atau key status
7
+ * @param {number} [delayMs=1500] Jeda sebelum menandai dibaca (ms)
8
+ */
9
+ export const readStatus = async (sock, statusMessage, delayMs = 1500) => {
10
+ if (delayMs > 0) {
11
+ await delay(delayMs);
12
+ }
13
+ const key = statusMessage?.key || statusMessage;
14
+ if (!key) throw new Error('readStatus: key status tidak valid');
15
+ return await sock.readMessages([key]);
16
+ };
17
+
18
+ /**
19
+ * Memberi reaksi emoji ke status WhatsApp pengguna.
20
+ * @param {any} sock Socket Baileys
21
+ * @param {any} statusMessageOrKey Pesan status atau key status
22
+ * @param {string} [emoji='❤️'] Emoji reaksi
23
+ */
24
+ export const reactStatus = async (sock, statusMessageOrKey, emoji = '❤️') => {
25
+ const key = statusMessageOrKey?.key || statusMessageOrKey;
26
+ if (!key) throw new Error('reactStatus: key status tidak valid');
27
+
28
+ const participant = key.participant || key.remoteJid;
29
+ return await sock.sendMessage('status@broadcast', {
30
+ react: {
31
+ text: emoji,
32
+ key: key
33
+ }
34
+ }, {
35
+ statusJidList: [participant]
36
+ });
37
+ };
38
+
39
+ /**
40
+ * Membuat status WhatsApp baru dengan men-tag/mention nomor tertentu.
41
+ * @param {any} sock Socket Baileys
42
+ * @param {string|object} content Teks status atau objek pesan media
43
+ * @param {string[]|string} targetJids Daftar JID kontak yang di-mention
44
+ * @param {object} [options={}] Opsi tambahan sendMessage
45
+ */
46
+ export const sendStatusMention = async (sock, content, targetJids = [], options = {}) => {
47
+ const msg = typeof content === 'string' ? { text: content } : content;
48
+ const jidList = Array.isArray(targetJids) ? targetJids : [targetJids];
49
+
50
+ return await sock.sendMessage('status@broadcast', msg, {
51
+ statusJidList: jidList,
52
+ ...options
53
+ });
54
+ };
55
+
56
+ /**
57
+ * Mengirim status rekaman suara (Voice Note Status) ke status WhatsApp.
58
+ * @param {any} sock Socket Baileys
59
+ * @param {Buffer|{ url: string }} audio Buffer audio atau URL
60
+ * @param {object} [options={}] Opsi (backgroundColor, statusJidList, waveform)
61
+ */
62
+ export const sendVoiceStatus = async (sock, audio, options = {}) => {
63
+ const audioPayload = Buffer.isBuffer(audio) ? audio : (typeof audio === 'string' ? { url: audio } : audio);
64
+
65
+ return await sock.sendMessage('status@broadcast', {
66
+ audio: audioPayload,
67
+ mimetype: 'audio/mp4',
68
+ ptt: true,
69
+ backgroundColor: options.backgroundColor || '#7F5AF0',
70
+ waveform: options.waveform || new Uint8Array([0, 10, 25, 45, 70, 90, 100, 80, 50, 30, 10, 0]),
71
+ ...options
72
+ }, {
73
+ statusJidList: options.statusJidList || []
74
+ });
75
+ };