sauruspro-bail 5.0.2-alpha.spr.test โ†’ 5.0.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ๐Ÿฆ– SAURUSPRO BAILEYS
4
4
 
5
- **Custom WhatsApp Web API Library โ€” SPR, Newsletter Status, Rich Messages & more**
5
+ **WhatsApp Web API untuk Node.js, lengkap dengan Rich Message, SPR, dan Newsletter Status**
6
6
 
7
7
  <img src="https://cdn.saurus.qzz.io/image/EGYoX.png" width="140" alt="Sauruspro">
8
8
 
@@ -11,35 +11,41 @@
11
11
  ![node](https://img.shields.io/badge/Node.js-20%2B-22c55e?style=for-the-badge)
12
12
  ![license](https://img.shields.io/badge/License-MIT-3b82f6?style=for-the-badge)
13
13
 
14
- **Owner: Saurus / Dinonic** ยท **Version: 5.0.2-alpha.spr.test**
15
-
16
14
  </div>
17
15
 
18
16
  ---
19
17
 
20
- ## ๐Ÿ“ฆ Package Info
18
+ ## ๐Ÿ“– Apa itu Baileys?
21
19
 
22
- | | |
20
+ **Baileys** adalah library JavaScript/TypeScript yang berbicara langsung dengan server WhatsApp lewat **WebSocket**, sama seperti WhatsApp Web, tetapi **tanpa browser** dan tanpa Selenium/Puppeteer. Dengan Baileys kamu bisa membuat bot atau aplikasi yang:
21
+
22
+ - login sebagai *linked device* (perangkat tertaut) lewat **QR** atau **pairing code**,
23
+ - mengirim dan menerima pesan (teks, gambar, video, stiker, poll, dll),
24
+ - mengelola grup, komunitas, channel (newsletter), dan profil,
25
+ - berjalan ringan karena hanya memakai protokol multi-device, bukan emulasi browser.
26
+
27
+ **Sauruspro Baileys (`sauruspro-bail`)** adalah turunan Baileys dengan fitur tambahan:
28
+
29
+ | Fitur | Isi |
23
30
  |---|---|
24
- | Package | `sauruspro-bail` |
25
- | Version | `5.0.2-alpha.spr.test` |
26
- | Owner | Saurus / Dinonic |
27
- | Module type | ESM (`"type": "module"`) |
28
- | Runtime | Node.js >= 20 |
29
- | Telegram | [@lordsaurus](https://t.me/lordsaurus) ยท [@lordsaurus](https://t.me/lordsaurus) |
31
+ | ๐Ÿ’ฌ **Rich Message** | `AIRich`, Carousel, Button, Native Flow, A2UI |
32
+ | ๐ŸŽฏ **SPR (Selective Relay)** | Kirim pesan hanya ke device tertentu, pesan khusus per orang |
33
+ | ๐Ÿ“ข **Newsletter Status** | Kirim, react, hapus, dan ambil status Channel |
34
+ | ๐Ÿชช **Greeting Card `/buss`** | Kartu sapaan untuk WhatsApp Business |
35
+ | ๐Ÿงฉ **Plugin system** | `sock.use(plugin)` dan fitur bawaan bisa dimatikan |
36
+ | ๐Ÿ‘ค **Presence & Profile API** | `sock.presence.*` dan `sock.profile.*` yang lebih rapi |
37
+ | โšก **Native crypto (opsional)** | X25519 dan XEdDSA lebih cepat jika `@zapo-js/native` terpasang |
30
38
 
31
- ## โœจ Features
39
+ > Beberapa fitur diadaptasi dari [zapo-js](https://github.com/vinikjkkj/zapo) (MIT).
32
40
 
33
- | Feature | Description |
41
+ ## ๐Ÿ“ฆ Info Paket
42
+
43
+ | | |
34
44
  |---|---|
35
- | ๐Ÿ” Pairing Code & QR | Login via pairing code or QR |
36
- | ๐Ÿ’ฌ Rich Messages | MessageBuilder: `AIRich`, `Carousel`, `Button`, `ButtonV2`, `Toolkit` |
37
- | ๐ŸŽฏ **SPR** | Selective Relay: kirim pesan hanya ke device/JID tertentu |
38
- | ๐Ÿ“ข **Newsletter Status** | Kirim, react, revoke & ambil status Channel (newsletter) |
39
- | ๐Ÿ”— LID & PN | Kompatibel LID dan nomor telepon |
40
- | ๐Ÿ‘ค Username | Fetch username |
41
- | ๐Ÿ‘ฅ Groups & Communities | Manajemen grup dan komunitas |
42
- | ๐Ÿ—‚๏ธ Auth | Multi-file & SQLite auth state |
45
+ | Nama | `sauruspro-bail` |
46
+ | Modul | ESM (`"type": "module"`), bisa dipakai dari CommonJS (lihat di bawah) |
47
+ | Node.js | >= 20 |
48
+ | Telegram | [@lordsaurus](https://t.me/lordsaurus) |
43
49
 
44
50
  ## ๐Ÿš€ Install
45
51
 
@@ -47,44 +53,214 @@
47
53
  npm install sauruspro-bail
48
54
  ```
49
55
 
50
- > Paket ini **ESM**. Gunakan `import`, atau `await import()` dari CommonJS.
56
+ ---
57
+
58
+ ## ๐Ÿ”Œ Menghubungkan ke WhatsApp (bot)
59
+
60
+ Ada dua cara login: **QR code** atau **pairing code** (kode 8 karakter yang dimasukkan di HP). Sesi disimpan di folder, jadi login hanya perlu sekali.
61
+
62
+ ### Contoh lengkap (pairing code + reconnect)
63
+
64
+ ```js
65
+ import makeWASocket, {
66
+ useMultiFileAuthState,
67
+ DisconnectReason,
68
+ Browsers
69
+ } from 'sauruspro-bail'
70
+
71
+ const NOMOR = '628123456789' // format internasional, tanpa +, spasi, atau strip
72
+
73
+ async function start() {
74
+ const { state, saveCreds } = await useMultiFileAuthState('session')
75
+
76
+ const sock = makeWASocket({
77
+ auth: state,
78
+ browser: Browsers.ubuntu('Chrome') // dianjurkan untuk pairing code
79
+ })
80
+
81
+ // simpan kredensial setiap kali berubah
82
+ sock.ev.on('creds.update', saveCreds)
83
+
84
+ // minta pairing code hanya jika belum login
85
+ if (!sock.authState.creds.registered) {
86
+ const code = await sock.requestPairingCode(NOMOR)
87
+ console.log('Pairing code:', code.match(/.{1,4}/g).join('-'))
88
+ // HP: WhatsApp > Perangkat tertaut > Tautkan perangkat > Tautkan dengan nomor telepon
89
+ }
90
+
91
+ // status koneksi + reconnect otomatis
92
+ sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
93
+ if (connection === 'open') console.log('โœ… Bot terhubung')
94
+ if (connection === 'close') {
95
+ const status = lastDisconnect?.error?.output?.statusCode
96
+ if (status === DisconnectReason.loggedOut) {
97
+ console.log('Logout. Hapus folder "session" lalu jalankan ulang.')
98
+ } else {
99
+ start() // koneksi putus, sambungkan lagi
100
+ }
101
+ }
102
+ })
103
+
104
+ // contoh membalas pesan
105
+ sock.ev.on('messages.upsert', async ({ messages }) => {
106
+ const msg = messages[0]
107
+ if (!msg?.message || msg.key.fromMe) return
108
+ const teks = msg.message.conversation || msg.message.extendedTextMessage?.text
109
+ if (teks === 'ping') {
110
+ await sock.sendMessage(msg.key.remoteJid, { text: 'pong' }, { quoted: msg })
111
+ }
112
+ })
113
+ }
114
+
115
+ start()
116
+ ```
117
+
118
+ ### Catatan pairing code
119
+
120
+ - Setiap pemanggilan `requestPairingCode` menghasilkan **kode acak baru**. Kamu juga bisa memakai kode sendiri: `requestPairingCode(NOMOR, 'ABCD1234')` (8 karakter, `A-Z` dan `0-9`).
121
+ - Nomor otomatis dibersihkan, jadi `+62 812-3456-789` tetap diterima.
122
+ - Jika sesi sudah login (`registered`), pairing ditolak. Hapus folder sesi untuk login ulang.
123
+ - Kode berlaku singkat. Kalau kedaluwarsa, panggil `requestPairingCode` lagi.
124
+
125
+ ### Login dengan QR
51
126
 
52
- ## โšก Quick Start
127
+ Dengarkan event `connection.update` dan tampilkan `qr` (misalnya dengan paket `qrcode-terminal`):
53
128
 
54
129
  ```js
55
- import makeWASocket, { useMultiFileAuthState } from 'sauruspro-bail'
130
+ sock.ev.on('connection.update', ({ qr }) => {
131
+ if (qr) console.log('Scan QR ini di WhatsApp:', qr)
132
+ })
133
+ ```
134
+
135
+ ### Menyimpan sesi di SQLite (opsional)
136
+
137
+ ```js
138
+ import { useSqliteAuthState } from 'sauruspro-bail'
139
+ const { state, saveCreds } = await useSqliteAuthState('session.db')
140
+ ```
141
+
142
+ ---
56
143
 
57
- const { state, saveCreds } = await useMultiFileAuthState('session')
58
- const sock = makeWASocket({ auth: state })
144
+ ## ๐Ÿ“œ Memakai dari CommonJS (`require`)
59
145
 
60
- sock.ev.on('creds.update', saveCreds)
146
+ Paket ini ESM. Dari file `.js`/`.cjs` biasa, ada dua cara:
61
147
 
62
- // Pairing code
63
- if (!sock.authState.creds.registered) {
64
- const code = await sock.requestPairingCode('628xxxxxxxxxx')
65
- console.log('Pairing code:', code)
148
+ **1. `import()` dinamis (semua versi Node 20+, paling aman)**
149
+
150
+ ```js
151
+ // index.cjs
152
+ async function main() {
153
+ const { default: makeWASocket, useMultiFileAuthState } = await import('sauruspro-bail')
154
+
155
+ const { state, saveCreds } = await useMultiFileAuthState('session')
156
+ const sock = makeWASocket({ auth: state })
157
+ sock.ev.on('creds.update', saveCreds)
66
158
  }
159
+ main()
160
+ ```
161
+
162
+ **2. `require()` langsung (Node 22.12+ atau Node 20.19+)**
163
+
164
+ ```js
165
+ const baileys = require('sauruspro-bail')
166
+ const makeWASocket = baileys.default
167
+ const { useMultiFileAuthState } = baileys
168
+ ```
169
+
170
+ Pada Node versi lama, `require('sauruspro-bail')` akan error `ERR_REQUIRE_ESM`. Pakai cara pertama.
171
+
172
+ Mau ubah proyekmu jadi ESM saja? Tambahkan `"type": "module"` di `package.json` proyekmu lalu pakai `import`.
173
+
174
+ ---
175
+
176
+ ## ๐Ÿ’ฌ Rich Message (AIRich)
177
+
178
+ Rich Message adalah balasan bergaya Meta AI: teks, kode, tabel, gambar, dan lainnya dalam satu pesan. Dibuat dengan builder `sock.AIRich`.
179
+
180
+ ```js
181
+ const rich = new sock.AIRich(sock)
182
+
183
+ rich
184
+ .addText('Halo! Ini **Rich Message** dari Sauruspro.')
185
+ .addCode('javascript', "console.log('halo dunia')")
186
+ .addTable([
187
+ ['Fitur', 'Status'],
188
+ ['SPR', 'Aktif'],
189
+ ['Newsletter Status', 'Aktif']
190
+ ])
191
+
192
+ await rich.send(jid)
193
+ ```
194
+
195
+ Cara singkat tanpa builder:
196
+
197
+ ```js
198
+ import { sendAIRichMessage } from 'sauruspro-bail'
199
+
200
+ await sendAIRichMessage(sock, jid, { text: 'Halo dari Rich Message!' })
201
+ ```
202
+
203
+ Builder lain yang tersedia: `sock.Carousel`, `sock.Button`, `sock.ButtonV2`, `sock.Toolkit`. Pesan `interactiveMessage`, `buttonsMessage`, `flowMessage`, dan `carouselMessage` juga bisa dikirim lewat `sock.sendMessage`.
204
+
205
+ > Rich Message memakai fitur WhatsApp yang bisa berubah. Tampilannya bergantung pada versi aplikasi penerima.
206
+
207
+ ---
208
+
209
+ ## ๐ŸŽฏ SPR (Selective Relay)
210
+
211
+ SPR mengontrol **siapa membaca apa** dalam satu pesan grup. Tiap device penerima masuk salah satu peran:
212
+
213
+ | Peran | Yang diterima |
214
+ |---|---|
215
+ | `public` | pesan umum |
216
+ | `personal` | pesan khusus miliknya saja |
217
+ | `hidden` | tidak menerima apa-apa |
218
+
219
+ ```js
220
+ // Sembunyikan pesan dari 1 orang
221
+ await sock.sPR(groupJid, { conversation: 'rahasia' }, {
222
+ mode: 'exclude', // 'include' = hanya target yang menerima
223
+ target: ['6281234567890@s.whatsapp.net']
224
+ })
225
+
226
+ // Pesan umum untuk semua, pesan lain khusus untuk A
227
+ await sock.sPR(groupJid, { conversation: 'info umum' }, {
228
+ personal: { '6281234567890@s.whatsapp.net': { conversation: 'info khusus kamu' } }
229
+ })
230
+
231
+ // Anggota lain lihat publicText, targetJid lihat privateText + foto profilnya
232
+ await sock.sPRPrivate(groupJid, targetJid, {
233
+ publicText: 'Pesan ini memiliki isi khusus untuk beberapa penerima.',
234
+ privateText: 'rahasia buat kamu'
235
+ })
236
+
237
+ // Tiap anggota menerima pesan sendiri, {name} menjadi @mention orang itu
238
+ await sock.sPRBroadcast(groupJid, { template: 'Halo {name}, ada pesan untukmu.', limit: 100 })
67
239
  ```
68
240
 
241
+ Opsi lengkap dan penjelasan lebih dalam: **[docs/SPR.md](docs/SPR.md)**
242
+
243
+ ---
244
+
69
245
  ## ๐Ÿ“ข Newsletter Status
70
246
 
71
- Kirim status ke Channel (newsletter) yang kamu admin-i.
247
+ Kirim status ke **Channel (newsletter)** yang kamu admin-i. JID channel berakhiran `@newsletter`.
72
248
 
73
249
  ```js
74
250
  const jid = '120363xxxxxxxxxxxxx@newsletter'
75
251
 
76
252
  // Teks
77
253
  const sent = await sock.newsletter.sendStatus(jid, { text: 'Halo Channel!' })
78
- console.log(sent.newsletterStatusServerId)
254
+ console.log(sent.newsletterStatusServerId) // dipakai untuk react / revoke
79
255
 
80
- // Gambar
81
- await sock.sendNewsletterStatus(jid, {
256
+ // Gambar atau video
257
+ await sock.newsletter.sendStatus(jid, {
82
258
  image: { url: './foto.jpg' },
83
259
  caption: 'Update terbaru'
84
260
  })
85
261
 
86
- // React ke status (butuh serverId dari status)
87
- await sock.newsletter.reactToStatus(jid, serverId, '๐Ÿ”ฅ')
262
+ // React ke status
263
+ await sock.newsletter.reactToStatus(jid, sent.newsletterStatusServerId, '๐Ÿ”ฅ')
88
264
 
89
265
  // Hapus status
90
266
  await sock.newsletter.revokeStatus(jid, statusId)
@@ -93,27 +269,36 @@ await sock.newsletter.revokeStatus(jid, statusId)
93
269
  const list = await sock.newsletter.fetchStatuses(jid, { count: 20 })
94
270
  ```
95
271
 
96
- Dokumentasi lengkap: **[docs/NEWSLETTER-STATUS.md](docs/NEWSLETTER-STATUS.md)**
272
+ Semua fungsi juga tersedia langsung di socket: `sock.sendNewsletterStatus(...)`, `sock.sendNewsletterStatusReaction(...)`, dan seterusnya.
97
273
 
98
- ## ๐ŸŽฏ SPR (Selective Relay)
274
+ Dokumentasi jenis konten, opsi, dan error: lihat tipe di `lib/Utils/newsletter-status.d.ts`.
275
+
276
+ ---
277
+
278
+ ## ๐Ÿงฉ Fitur lain
99
279
 
100
280
  ```js
101
- await sock.sPR(jid, { conversation: 'Halo!' })
102
- // alias: sock.relayMessageSelective(jid, message, options)
281
+ // Greeting Card /buss (hanya terlihat di WhatsApp Business)
282
+ await sock.sendGreetingCard(jid, {
283
+ text: 'wow', title: 'Judul Kartu', greeting: 'Hi Business',
284
+ imageUrl: 'https://.../img.jpg', contactUrl: 'https://wa.me/NOMOR'
285
+ })
103
286
 
104
- // pesan privat per orang + foto profil
105
- await sock.sPRPrivate(groupJid, targetJid, { publicText: 'Ada pesan', privateText: 'rahasia' })
106
- await sock.sPRBroadcast(groupJid, { template: 'Halo {name}!' })
107
- ```
287
+ // Presence & profile
288
+ await sock.presence.withTyping(jid, () => sock.sendMessage(jid, { text: 'halo' }))
289
+ await sock.profile.setStatus('Bot aktif')
108
290
 
109
- Dokumentasi lengkap: **[docs/SPR.md](docs/SPR.md)**
291
+ // Plugin
292
+ import { definePlugin } from 'sauruspro-bail'
293
+ sock.use(definePlugin({ id: 'halo', setup: ({ extend }) => extend({ halo: () => 'hai' }) }))
294
+ // matikan fitur bawaan: makeWASocket({ disablePlugins: ['greeting-card'] })
295
+ ```
110
296
 
111
297
  ## ๐Ÿ“š Dokumentasi
112
298
 
113
299
  | File | Isi |
114
300
  |---|---|
115
- | [docs/NEWSLETTER-STATUS.md](docs/NEWSLETTER-STATUS.md) | API newsletter status, opsi, error |
116
- | [docs/SPR.md](docs/SPR.md) | Selective Relay |
301
+ | [docs/SPR.md](docs/SPR.md) | Selective Relay lengkap |
117
302
  | [docs/CHANGELOG.md](docs/CHANGELOG.md) | Riwayat versi |
118
303
 
119
304
  ## โš ๏ธ Disclaimer
package/docs/CHANGELOG.md CHANGED
@@ -1,39 +1,61 @@
1
1
  # Changelog
2
2
 
3
- ## 5.0.2-alpha.spr.test
3
+ ## 5.0.3
4
+ ### Fixed
5
+ - **Pair code**: `requestPairingCode` diperbaiki.
6
+ - Nomor dibersihkan otomatis (`+62 812-3456-789` menjadi `628123456789`) dan divalidasi 7-15 digit.
7
+ - Menunggu socket terbuka dulu sebelum mengirim permintaan (tidak lagi error "Connection Closed" kalau dipanggil terlalu cepat).
8
+ - Setiap request membuat kode acak baru **dan** keypair ephemeral baru (sebelumnya keypair lama dipakai ulang).
9
+ - Kode custom dinormalkan ke huruf besar dan divalidasi `A-Z0-9` 8 karakter.
10
+ - Sesi yang sudah `registered` ditolak dengan pesan jelas.
11
+ - Jika pengiriman gagal, state pairing (kode, keypair, `me`) dikembalikan seperti semula.
12
+
4
13
  ### Changed
5
- - `lib/Utils/dinonic-spr.js` ditulis ulang dari nol (engine baru, sender key sekali pakai). API `sPR` / `selectiveRelay` tetap kompatibel.
14
+ - Versi di banner `lib/index.js` kini dibaca dari `package.json` (export `packageVersion`). Banner bisa dimatikan dengan env `SAURUS_NO_BANNER=1`.
15
+ - Info fitur di banner diperbarui.
16
+ - README baru: pengertian Baileys, koneksi (QR dan pairing code), pemakaian dari CommonJS, Rich Message, SPR, dan Newsletter Status.
6
17
 
7
- ### Added (diadaptasi dari plugin zapo-js, MIT)
8
- - Opsi `personal` pada `sPR`: pesan khusus per orang.
9
- - `sPRPrivate`, `sPRBroadcast` (`{name}` + mention + foto profil), `getSPRTargets`, `loadSPRAvatars`.
10
- - Batas target 100 + hitung overflow, cache foto, budget/timeout, konkurensi.
18
+ ## 5.0.2
19
+ ### Added (dari zapo-js, MIT)
20
+ - **Greeting Card `/buss`**: `sock.sendGreetingCard(jid, { text, title, greeting, imageUrl, contactUrl, thumbnail?, sourceApp?, ref?, mentions?, adReply? }, sendOptions?)` dan `sock.buildGreetingCard(opts)`.
21
+ Pesan `extendedTextMessage` + `contextInfo.externalAdReply`; `sourceId`, `ctwaClid`, `ctaPayload` diacak tiap kirim. Kartu hanya tampil di WhatsApp Business; user biasa hanya melihat teks.
22
+ ```js
23
+ await sock.sendGreetingCard(jid, {
24
+ text: 'wow', title: 'Megumin Dominada BUSI', greeting: 'Hi Business',
25
+ imageUrl: 'https://.../img.jpg', contactUrl: 'https://wa.me/NOMOR'
26
+ }, { quoted: null })
27
+ ```
28
+ - **Group History Bundle** (`sock.GroupHistory`): `encodeGroupHistoryBundle`, `decodeGroupHistoryBundle`, `streamGroupHistoryBundle`.
29
+ - **Meta AI Bot helpers** (`sock.MetaAIBot`): `BOT_KNOWN_JIDS`, `genBotMsgSecret`, `deriveBotChunkKey`, `decryptBotChunk`, `attachBotMetadata`, `attachBotThread`, `extractInvokedBotJid`, `buildBotInvokeProtoCopy`.
30
+ - **Plugin system**: `sock.use(plugin)`, `sock.removePlugin(id)`, `sock.hasPlugin(id)`, `sock.listPlugins()`, helper `definePlugin({ id, exposeAs?, setup(ctx), dispose? })`. Opsi socket `plugins: [...]` dan `disablePlugins: [...]`.
31
+ Fitur bawaan berupa plugin: `spr`, `greeting-card`, `group-history`, `meta-ai-bot`, `presence`, `profile`.
32
+ - **Presence & Profile API**: `sock.presence` (`setOnline`, `setOffline`, `typing`, `recording`, `paused`, `subscribe`, `typeFor`, `withTyping`) dan `sock.profile` (`getPicture`, `setPicture`, `removePicture`, `getStatus`, `setStatus`, `setName`, `username.*`).
33
+ - **Akselerator kripto native opsional**: jika `@zapo-js/native` terpasang, X25519 dan XEdDSA memakai native (NAPI lalu WASM), jika tidak tetap JS. Env `SAURUS_NATIVE_BACKEND=auto|napi|wasm|js|none`.
34
+ - **SPR**: opsi `personal` (pesan khusus per orang), `sPRPrivate`, `sPRBroadcast` (`{name}` + mention + foto profil), `getSPRTargets`, `loadSPRAvatars`, batas target 100 dengan hitungan overflow, cache foto, budget/timeout, konkurensi (diadaptasi dari plugin zapo-js).
35
+
36
+ ### Fixed
37
+ - Kode pairing tanpa custom kini acak tiap request (sebelumnya selalu sama).
38
+
39
+ ### Changed
40
+ - `lib/Utils/dinonic-spr.js` ditulis ulang (sender key sekali pakai). API `sPR` / `selectiveRelay` tetap kompatibel.
11
41
  - Reporting token memakai util resmi, tctoken memakai pengecekan expired.
12
42
 
13
43
  ## 5.0.1
14
- **Owner: Saurus / Dinonic**
15
-
16
- ### Added (fitur tambahan, by Saurus)
44
+ ### Added
17
45
  - **A2UI / Bloks widget**: `a2uiText`, `a2uiButton`, `a2uiCard`, `a2uiTabs`, `a2uiModal`, dll, plus `sendA2UI`, `sendBloksWidget`.
18
- - **DinonicBuilder** (`sock.DinonicBuilder`): Button, ButtonV2, Carousel, AIRich, Toolkit.
19
- - **Extras / AIRich lengkap**, **HTML App** (`sendHtmlApp`, `sendHtmlDocument`), **Meta AI sections**, **Bot Signature**, **Native Flow**.
46
+ - **Builder** (`sock.DinonicBuilder`): Button, ButtonV2, Carousel, AIRich, Toolkit.
47
+ - **AIRich lengkap**, **HTML App** (`sendHtmlApp`, `sendHtmlDocument`), **Meta AI sections**, **Bot Signature**, **Native Flow**.
20
48
  - **Modern Messages**: komentar, event invite, pertanyaan/jawaban, poll add option, scheduled call, undangan newsletter, status mention/reaction/audience.
21
- - **Payment Messages** (payment reminder, split payment), **Scheduled Message** (conditional reveal), **Status Stickers** (musik, link, lokasi, pesan, channel).
22
- - **Dinonic handler** (`Socket/dinonic-handler.js`): Interactive/Carousel, Buttons, Flow, Group Story di `sendMessage`, plus `sock.sendStatusWhatsApp` dan `sock.getName`.
49
+ - **Payment Messages** (payment reminder, split payment), **Scheduled Message**, **Status Stickers** (musik, link, lokasi, pesan, channel).
50
+ - Handler `Socket/dinonic-handler.js`: Interactive/Carousel, Buttons, Flow, Group Story di `sendMessage`, plus `sock.sendStatusWhatsApp` dan `sock.getName`.
23
51
  - Opsi socket: `rejectCalls`, `callRejectMessage`, `autoReadStatus`, `autoReadMessages`.
24
- - `baileys-event-stream`, file `.d.ts` untuk modul baru.
52
+ - `baileys-event-stream` dan file `.d.ts` untuk modul baru.
25
53
 
26
54
  ### Changed
27
- - Semua nama `dinolith` โ†’ `dinonic` (termasuk file `dinonic-spr.js`). Branding fitur tambahan diganti menjadi Saurus / Dinonic.
28
55
  - **Newsletter Status** diganti dengan implementasi penuh (alias `prepareNewsletterStatusContent` dan `makeNewsletterStatusAttribution` tetap ada).
29
- - Pesan `interactiveMessage`, `buttonsMessage`, `flowMessage`, `carouselMessage`/`cards` sekarang lewat handler Dinonic.
30
-
31
- ### Tidak dibawa
32
- - Telegram adapter dan bridge, auto-follow channel (sudah ada di `Socket/newsletter.js`), registrasi mobile, mobile client, `phonenumber-mcc` (dasar Baileys lama).
56
+ - Pesan `interactiveMessage`, `buttonsMessage`, `flowMessage`, `carouselMessage`/`cards` sekarang lewat handler baru.
33
57
 
34
58
  ## 5.0.0
35
- **Owner: Saurus / Dinonic**
36
-
37
59
  ### Added
38
60
  - **Newsletter Status**: `sendNewsletterStatus`, `sendNewsletterStatusReaction`, `revokeNewsletterStatus`, `getNewsletterStatuses`, `getNewsletterStatusUpdates`, plus `sock.newsletter.{sendStatus, reactToStatus, revokeStatus, fetchStatuses, fetchStatusUpdates}`.
39
61
  - File tipe `newsletter-status.d.ts` dan `dinonic-spr.d.ts`.
@@ -42,12 +64,4 @@
42
64
 
43
65
  ### Fixed
44
66
  - `dinonic-spr.js` mengimpor `encodeSignedDeviceIdentity` dari `generics.js` (tidak ada), sehingga seluruh paket gagal load. Sekarang dari `validate-connection.js`.
45
- - SPR: disamakan penuh dengan SPR diezyxhao-baileys 1.4.0 (logika identik, hanya CJS โ†’ ESM), termasuk fallback `conn.user` saat `authState.creds.me` kosong.
46
- - `package.json`: `"type": "module"` ditambahkan (kode ESM), duplikat `lru-cache` di devDependencies dihapus.
47
-
48
- ### Changed
49
- - Versi `3.0.0` โ†’ `5.0.0`, author/owner `Saurus / Dinonic`.
50
- - README ditulis ulang, banner menampilkan versi & owner.
51
-
52
- ### Notes
53
- - Newsletter status diport dari diezyxhao-baileys 1.4.0 ke ESM; tidak mengubah `sendMessage`.
67
+ - SPR: fallback `conn.user` saat `authState.creds.me` kosong.
package/docs/SPR.md CHANGED
@@ -85,4 +85,4 @@ Foto profil diambil konkuren (10 sekaligus) dengan budget 10 dtk dan batas keras
85
85
 
86
86
  - Penerima `personal` hanya melihat pesan personalnya; pesan umum tidak dikirim ke mereka.
87
87
  - Fitur privat/broadcast diadaptasi dari plugin zapo-js (MIT).
88
- - Perubahan v5.0.2-alpha.spr.test: engine ditulis ulang (kode baru), tambah `personal`, `sPRPrivate`, `sPRBroadcast`, `getSPRTargets`, `loadSPRAvatars`. API `sPR`/`selectiveRelay` lama tetap kompatibel.
88
+ - Perubahan v5.0.2: engine ditulis ulang (kode baru), tambah `personal`, `sPRPrivate`, `sPRBroadcast`, `getSPRTargets`, `loadSPRAvatars`. API `sPR`/`selectiveRelay` lama tetap kompatibel.
@@ -6,6 +6,11 @@ import { makePrivacySocket } from './privacy.js'
6
6
  import { makeGraphQLSocket } from './graphql.js'
7
7
  import { makeMessageBuilderSocket } from './message-builder.js'
8
8
  import { sPR, sPRPrivate, sPRBroadcast, getSPRTargets } from '../Utils/dinonic-spr.js'
9
+ import { sendGreetingCard, buildGreetingCardContent } from '../Utils/greeting-card.js'
10
+ import * as GroupHistory from '../Utils/group-history.js'
11
+ import * as MetaAIBot from '../Utils/meta-ai-bot.js'
12
+ import { definePlugin, makePluginHost } from '../Utils/plugins.js'
13
+ import { makePresence, makeProfile } from '../Utils/presence-profile.js'
9
14
  import * as DinonicBuilder from '../Utils/MessageBuilder.js'
10
15
  import {
11
16
  makeNewsletterStatusSender,
@@ -36,12 +41,38 @@ const makeWASocket = (config) => {
36
41
  // Builder by Saurus (A2UI, AIRich, Button, ButtonV2, Carousel, Toolkit)
37
42
  clientWithBuilder.DinonicBuilder = DinonicBuilder;
38
43
 
39
- // SPR (Selective Relay)
40
- clientWithBuilder.sPR = (jid, message, options) => sPR(clientWithBuilder, jid, message, options);
41
- clientWithBuilder.relayMessageSelective = clientWithBuilder.sPR;
42
- clientWithBuilder.sPRPrivate = (groupJid, targetJid, options) => sPRPrivate(clientWithBuilder, groupJid, targetJid, options);
43
- clientWithBuilder.sPRBroadcast = (groupJid, options) => sPRBroadcast(clientWithBuilder, groupJid, options);
44
- clientWithBuilder.getSPRTargets = (groupJid, options) => getSPRTargets(clientWithBuilder, groupJid, options);
44
+ // Plugin system (sock.use) + fitur bawaan sebagai plugin (bisa dimatikan lewat config.disablePlugins)
45
+ const host = makePluginHost(clientWithBuilder, newConfig);
46
+ clientWithBuilder.use = host.use;
47
+ clientWithBuilder.removePlugin = host.removePlugin;
48
+ clientWithBuilder.hasPlugin = host.hasPlugin;
49
+ clientWithBuilder.listPlugins = host.listPlugins;
50
+ const sock = clientWithBuilder;
51
+ const builtinPlugins = [
52
+ definePlugin({
53
+ id: 'spr',
54
+ setup: ({ extend }) => extend({
55
+ sPR: (jid, message, options) => sPR(sock, jid, message, options),
56
+ relayMessageSelective: (jid, message, options) => sPR(sock, jid, message, options),
57
+ sPRPrivate: (groupJid, targetJid, options) => sPRPrivate(sock, groupJid, targetJid, options),
58
+ sPRBroadcast: (groupJid, options) => sPRBroadcast(sock, groupJid, options),
59
+ getSPRTargets: (groupJid, options) => getSPRTargets(sock, groupJid, options)
60
+ })
61
+ }),
62
+ definePlugin({
63
+ id: 'greeting-card',
64
+ setup: ({ extend }) => extend({
65
+ sendGreetingCard: (jid, opts, sendOptions) => sendGreetingCard(sock, jid, opts, sendOptions),
66
+ buildGreetingCard: buildGreetingCardContent
67
+ })
68
+ }),
69
+ definePlugin({ id: 'group-history', setup: ({ extend }) => extend({ GroupHistory }) }),
70
+ definePlugin({ id: 'meta-ai-bot', setup: ({ extend }) => extend({ MetaAIBot }) }),
71
+ definePlugin({ id: 'presence', exposeAs: 'presence', setup: ({ sock: s }) => makePresence(s) }),
72
+ definePlugin({ id: 'profile', exposeAs: 'profile', setup: ({ sock: s }) => makeProfile(s) })
73
+ ];
74
+ for (const plugin of builtinPlugins) host.use(plugin);
75
+ for (const plugin of newConfig.plugins ?? []) host.use(plugin);
45
76
 
46
77
  // Newsletter Status (Channel Status)
47
78
  if (!clientWithBuilder.sendNewsletterStatus) {
@@ -735,62 +735,92 @@ export const makeSocket = (config) => {
735
735
  void end(new Boom(msg || 'Intentional Logout', { statusCode: DisconnectReason.loggedOut }));
736
736
  };
737
737
  const requestPairingCode = async (phoneNumber, customPairingCode) => {
738
- const pairingCode = customPairingCode ?? 'VINXSYNC';
739
- if (customPairingCode && customPairingCode?.length !== 8) {
740
- throw new Error('Custom pairing code must be exactly 8 chars');
738
+ if (authState.creds.registered) {
739
+ throw new Boom('Sesi sudah terdaftar (registered). Hapus folder sesi untuk pairing ulang.', { statusCode: 400 });
741
740
  }
742
- authState.creds.pairingCode = pairingCode;
741
+ // Nomor harus angka saja dengan kode negara (tanpa +, spasi, atau strip).
742
+ const digits = String(phoneNumber ?? '').replace(/\D/g, '');
743
+ if (digits.length < 7 || digits.length > 15) {
744
+ throw new Boom('Nomor tidak valid. Gunakan format internasional tanpa +, contoh: 628123456789', { statusCode: 400 });
745
+ }
746
+ let customCode;
747
+ if (customPairingCode) {
748
+ customCode = String(customPairingCode).toUpperCase();
749
+ if (!/^[A-Z0-9]{8}$/.test(customCode)) {
750
+ throw new Error('Custom pairing code must be exactly 8 chars (A-Z, 0-9)');
751
+ }
752
+ }
753
+ // Pastikan koneksi sudah terbuka sebelum mengirim permintaan pairing.
754
+ await waitForSocketOpen();
755
+ const previous = {
756
+ pairingCode: authState.creds.pairingCode,
757
+ pairingEphemeralKeyPair: authState.creds.pairingEphemeralKeyPair,
758
+ me: authState.creds.me
759
+ };
760
+ // Kode acak baru + keypair ephemeral baru di setiap request (tidak memakai ulang yang lama).
761
+ authState.creds.pairingCode = customCode ?? bytesToCrockford(randomBytes(5));
762
+ authState.creds.pairingEphemeralKeyPair = Curve.generateKeyPair();
743
763
  authState.creds.me = {
744
- id: jidEncode(phoneNumber, 's.whatsapp.net'),
764
+ id: jidEncode(digits, 's.whatsapp.net'),
745
765
  name: '~'
746
766
  };
747
767
  ev.emit('creds.update', authState.creds);
748
- await sendNode({
749
- tag: 'iq',
750
- attrs: {
751
- to: S_WHATSAPP_NET,
752
- type: 'set',
753
- id: generateMessageTag(),
754
- xmlns: 'md'
755
- },
756
- content: [
757
- {
758
- tag: 'link_code_companion_reg',
759
- attrs: {
760
- jid: authState.creds.me.id,
761
- stage: 'companion_hello',
762
- should_show_push_notification: 'true'
763
- },
764
- content: [
765
- {
766
- tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
767
- attrs: {},
768
- content: await generatePairingKey()
769
- },
770
- {
771
- tag: 'companion_server_auth_key_pub',
772
- attrs: {},
773
- content: authState.creds.noiseKey.public
774
- },
775
- {
776
- tag: 'companion_platform_id',
777
- attrs: {},
778
- content: getCompanionPlatformId(browser)
779
- },
780
- {
781
- tag: 'companion_platform_display',
782
- attrs: {},
783
- content: `${browser[1]} (${browser[0]})`
768
+ try {
769
+ await sendNode({
770
+ tag: 'iq',
771
+ attrs: {
772
+ to: S_WHATSAPP_NET,
773
+ type: 'set',
774
+ id: generateMessageTag(),
775
+ xmlns: 'md'
776
+ },
777
+ content: [
778
+ {
779
+ tag: 'link_code_companion_reg',
780
+ attrs: {
781
+ jid: authState.creds.me.id,
782
+ stage: 'companion_hello',
783
+ should_show_push_notification: 'true'
784
784
  },
785
- {
786
- tag: 'link_code_pairing_nonce',
787
- attrs: {},
788
- content: '0'
789
- }
790
- ]
791
- }
792
- ]
793
- });
785
+ content: [
786
+ {
787
+ tag: 'link_code_pairing_wrapped_companion_ephemeral_pub',
788
+ attrs: {},
789
+ content: await generatePairingKey()
790
+ },
791
+ {
792
+ tag: 'companion_server_auth_key_pub',
793
+ attrs: {},
794
+ content: authState.creds.noiseKey.public
795
+ },
796
+ {
797
+ tag: 'companion_platform_id',
798
+ attrs: {},
799
+ content: getCompanionPlatformId(browser)
800
+ },
801
+ {
802
+ tag: 'companion_platform_display',
803
+ attrs: {},
804
+ content: `${browser[1]} (${browser[0]})`
805
+ },
806
+ {
807
+ tag: 'link_code_pairing_nonce',
808
+ attrs: {},
809
+ content: '0'
810
+ }
811
+ ]
812
+ }
813
+ ]
814
+ });
815
+ }
816
+ catch (error) {
817
+ // Gagal kirim: kembalikan state lama supaya tidak tertinggal kode/keypair setengah jadi.
818
+ authState.creds.pairingCode = previous.pairingCode;
819
+ authState.creds.pairingEphemeralKeyPair = previous.pairingEphemeralKeyPair;
820
+ authState.creds.me = previous.me;
821
+ ev.emit('creds.update', authState.creds);
822
+ throw error;
823
+ }
794
824
  return authState.creds.pairingCode;
795
825
  };
796
826
  async function generatePairingKey() {