# 🌿 riyoo

<p align="center">
   WhatsApp Web API library — fork dari <a href="https://github.com/WhiskeySockets/Baileys">Baileys</a> dengan perbaikan LID/PN JID handling, dukungan pesan interaktif, album, newsletter, dan berbagai tipe pesan tambahan.
   <br><br>
   <img src="https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&labelColor=green&logoColor=white&style=for-the-badge"/>
   <img src="https://img.shields.io/badge/ESM-only-yellow?logo=javascript&logoColor=black&style=for-the-badge"/>
   <img src="https://img.shields.io/badge/license-MIT-blue?style=for-the-badge"/>
</p>

---

### ✨ Fitur Utama

- 🔧 Fix LID/PN JID handling untuk grup (addressing mode `lid` & `s.whatsapp.net/@g.us`)
- 🚫 Tanpa obfuscation — kode mudah dibaca dan diaudit
- 🚫 Tanpa auto-follow channel (newsletter)
- 📦 Includes `makeInMemoryStore` dengan adaptasi ESM minimal
- 🖼️ Support multi image processing backend: `sharp`, `@napi-rs/image`, atau `jimp`
- 📦 FFmpeg menggunakan `spawn` (lebih aman dari `exec`)

---

### 🛠️ Perbaikan Internal

- 🔑 **Fix variable shadowing `isLidUser`** di `getUSyncDevices` — mencegah konflik antara variabel lokal dan fungsi import
- 📍 **Fix server encoding untuk `@lid` JID** — memastikan JID yang diminta sebagai LID tetap `@lid`, bukan berubah jadi `@s.whatsapp.net`
- 👥 **Fix participant list di grup** — null-check eksplisit, normalisasi JID via `jidNormalizedUser`, dan cross-field validation antara `phoneNumber` dan `lid`
- 🖼️ **Fix media upload ke newsletter**
- 🔒 Fix `updateBlockStatus` implementation

---

### 📨 Tipe Pesan yang Didukung

- 🖼️ Album (Image & Video)
- 👥 Group Status Message
- 👉🏻 Interactive Message (buttons, list, native flow, template, carousel)
- 🎞️ Status Mention Message
- 📦 Sticker Pack Message
- ✨ Rich Response Message
- 🧾 Message with Code Block
- 🌏 Message with Inline Entities
- 📋 Message with Table
- 💳 Payment-related Message (request, invite, order, invoice)

---

### 📋 Daftar Isi

- [📥 Instalasi](#-instalasi)
- [🌐 Koneksi ke WhatsApp](#-koneksi-ke-whatsapp)
- [🗄️ Data Store](#️-data-store)
- [🪪 Penjelasan WhatsApp ID](#-penjelasan-whatsapp-id)
- [✉️ Mengirim Pesan](#️-mengirim-pesan)
- [📁 Mengirim Media](#-mengirim-media)
- [👉🏻 Pesan Interaktif](#-pesan-interaktif)
- [💳 Pesan Pembayaran](#-pesan-pembayaran)
- [👁️ Opsi Pesan Lainnya](#️-opsi-pesan-lainnya)
- [♻️ Modifikasi Pesan](#️-modifikasi-pesan)
- [🧰 Konten Tambahan](#-konten-tambahan)
- [📣 Credits](#-credits)

---

### 📥 Instalasi

```bash
# Via NPM
npm i baileys-yorkv2

# Via GitHub
npm i github:baileys-yorkv2
```

```json
// package.json
"dependencies": {
   "baileys-yorkv2": "latest"
}
```

#### 🧩 Import (ESM & CJS)

```javascript
// ESM
import { makeWASocket } from 'baileys-yorkv2'

// CJS
const { makeWASocket } = require('baileys-yorkv2')
```

---

### 🌐 Koneksi ke WhatsApp

```javascript
import { makeWASocket, delay, DisconnectReason, useMultiFileAuthState } from 'baileys-yorkv2'
import { Boom } from '@hapi/boom'
import pino from 'pino'

const myPhoneNumber = '6288888888888'
const logger = pino({ level: 'silent' })

const connectToWhatsApp = async () => {
   const { state, saveCreds } = await useMultiFileAuthState('session')

   const sock = makeWASocket({
      logger,
      auth: state
   })

   sock.ev.on('creds.update', saveCreds)

   sock.ev.on('connection.update', async (update) => {
      const { connection, lastDisconnect } = update
      if (connection === 'connecting' && !sock.authState.creds.registered) {
         await delay(1500)
         const code = await sock.requestPairingCode(myPhoneNumber)
         console.log('🔗 Pairing code:', code)
      }
      else if (connection === 'close') {
         const shouldReconnect = new Boom(lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut
         if (shouldReconnect) connectToWhatsApp()
      }
      else if (connection === 'open') {
         console.log('✅ Terhubung ke WhatsApp')
      }
   })

   sock.ev.on('messages.upsert', async ({ messages }) => {
      for (const message of messages) {
         if (!message.message) continue
         await sock.sendMessage(message.key.remoteJid, { text: '👋🏻 Halo!' })
      }
   })
}

connectToWhatsApp()
```

#### 🔐 Auth State

> [!NOTE]
> Bisa juga pakai `useSingleFileAuthState` atau `useSqliteAuthState` sebagai alternatif dari `useMultiFileAuthState`.

---

### 🗄️ Data Store

> [!CAUTION]
> Sangat disarankan membangun data store sendiri — menyimpan seluruh riwayat chat di memori bisa menyebabkan penggunaan RAM yang berlebihan.

```javascript
import { makeWASocket, makeInMemoryStore, useMultiFileAuthState } from 'baileys-yorkv2'
import pino from 'pino'

const logger = pino({ level: 'silent' })
const storePath = './store.json'

const connectToWhatsApp = async () => {
   const { state, saveCreds } = await useMultiFileAuthState('session')

   const sock = makeWASocket({ logger, auth: state })
   const store = makeInMemoryStore({ logger, socket: sock })

   store.bind(sock.ev)
   store.readFromFile(storePath)
   setInterval(() => store.writeToFile(storePath), 180_000)

   sock.ev.on('creds.update', saveCreds)
}

connectToWhatsApp()
```

---

### 🪪 Penjelasan WhatsApp ID

`jid` adalah ID WhatsApp pengguna atau grup.

- **Pengguna PN**: `6281111111111@s.whatsapp.net`
- **Pengguna LID**: `43411111111111@lid`
- **Grup**: `123456789-123345@g.us`
- **Meta AI**: `11111111111@bot`
- **Broadcast list**: `[timestamp]@broadcast`
- **Stories**: `status@broadcast`
- **Newsletter**: `1231111111111@newsletter`

> [!NOTE]
> Nomor telepon harus menggunakan format internasional tanpa `+`, `()`, atau `-`.

---

### ✉️ Mengirim Pesan

#### 🔠 Teks

```javascript
// Teks biasa
sock.sendMessage(jid, { text: '👋🏻 Halo' }, { quoted: message })

// Teks dengan link preview
sock.sendMessage(jid, {
   text: 'https://github.com — cek ini!',
   linkPreview: {
      'matched-text': 'https://github.com',
      title: 'GitHub',
      description: 'Where the world builds software',
      previewType: 0
   }
})
```

#### 🔔 Mention

```javascript
// Mention spesifik
sock.sendMessage(jid, {
   text: '👋🏻 Halo @628123456789',
   mentions: ['628123456789@s.whatsapp.net']
}, { quoted: message })

// Mention semua
sock.sendMessage(jid, {
   text: '👋🏻 Halo @all',
   mentionAll: true
}, { quoted: message })
```

#### 😁 Reaksi

```javascript
sock.sendMessage(jid, {
   react: { key: message.key, text: '✨' }
})
```

#### 📌 Pin Pesan

```javascript
sock.sendMessage(jid, {
   pin: message.key,
   time: 86400, // detik: 86400 (1h), 604800 (7h), 2592000 (30h)
   type: 1 // 2 untuk unpin
})
```

#### 🔖 Keep Chat

> [!NOTE]
> Hanya untuk chat dengan disappearing messages aktif.

```javascript
sock.sendMessage(jid, { keep: message.key, type: 1 }) // type: 2 untuk remove
```

#### ➡️ Forward

```javascript
sock.sendMessage(jid, { forward: message, force: true })
```

#### 👤 Kontak

```javascript
const vcard = 'BEGIN:VCARD\nVERSION:3.0\nFN:Nama Kontak\nTEL;type=CELL;waid=628123456789:+62 8123 456 789\nEND:VCARD'

sock.sendMessage(jid, {
   contacts: {
      displayName: 'Nama Kontak',
      contacts: [{ vcard }]
   }
}, { quoted: message })
```

#### 📍 Lokasi

```javascript
sock.sendMessage(jid, {
   location: {
      degreesLatitude: -6.2,
      degreesLongitude: 106.8,
      name: '📍 Jakarta'
   }
}, { quoted: message })
```

#### 🗓️ Event

```javascript
sock.sendMessage(jid, {
   event: {
      name: '🎉 Acara Seru',
      description: 'Deskripsi acara di sini.',
      startDate: new Date(Date.now() + 3_600_000),
      endDate: new Date(Date.now() + 28_800_000),
      location: { name: 'Jakarta', degreesLatitude: -6.2, degreesLongitude: 106.8 }
   }
}, { quoted: message })
```

#### 📊 Polling

```javascript
sock.sendMessage(jid, {
   poll: {
      name: '🔥 Pilih satu!',
      values: ['Ya', 'Tidak'],
      selectableCount: 1,
      endDate: new Date(Date.now() + 28_800_000)
   }
}, { quoted: message })
```

---

### 📁 Mengirim Media

> [!NOTE]
> Media bisa berupa `Buffer`, `{ stream: Readable }`, atau `{ url: string }` (path lokal atau URL HTTP/HTTPS).

#### 🖼️ Gambar

```javascript
sock.sendMessage(jid, {
   image: { url: './path/to/image.jpg' },
   caption: '🔥 Keren!'
}, { quoted: message })
```

#### 🎥 Video

```javascript
sock.sendMessage(jid, {
   video: { url: './path/to/video.mp4' },
   gifPlayback: false,
   caption: '🎬 Video keren!'
}, { quoted: message })
```

#### 📃 Stiker

```javascript
sock.sendMessage(jid, {
   sticker: { url: './path/to/sticker.webp' }
}, { quoted: message })
```

#### 💽 Audio

```javascript
sock.sendMessage(jid, {
   audio: { url: './path/to/audio.mp3' },
   ptt: false // true = Voice Note
}, { quoted: message })
```

#### 🗂️ Dokumen

```javascript
sock.sendMessage(jid, {
   document: { url: './path/to/file.pdf' },
   mimetype: 'application/pdf',
   caption: '📄 Dokumen saya'
}, { quoted: message })
```

#### 🖼️ Album (Gambar & Video)

```javascript
sock.sendMessage(jid, {
   album: [
      { image: { url: './image1.jpg' }, caption: 'Foto 1' },
      { video: { url: './video1.mp4' }, caption: 'Video 1' },
      { image: { url: './image2.jpg' }, caption: 'Foto 2' }
   ]
}, { quoted: message })
```

#### 📦 Sticker Pack

> [!IMPORTANT]
> Jika `sharp` atau `@napi-rs/image` tidak terpasang, `cover` dan `stickers` harus sudah dalam format WebP.

```javascript
sock.sendMessage(jid, {
   cover: { url: './cover.webp' },
   stickers: [
      { data: { url: './sticker1.webp' } },
      { data: { url: './sticker2.webp' } }
   ],
   name: '📦 Sticker Pack Ku',
   publisher: '🌟 Nama Kamu'
}, { quoted: message })
```

---

### 👉🏻 Pesan Interaktif

#### 🔘 Tombol (Buttons)

```javascript
sock.sendMessage(jid, {
   text: '👆🏻 Pilih opsi:',
   footer: 'riyoo',
   buttons: [
      { text: '👋🏻 Halo', id: '#Halo' },
      { text: '❓ Bantuan', id: '#Bantuan' }
   ]
}, { quoted: message })
```

#### 📋 List

> [!NOTE]
> Hanya berfungsi di chat pribadi (`@s.whatsapp.net`).

```javascript
sock.sendMessage(jid, {
   text: '📋 Menu',
   footer: 'riyoo',
   buttonText: '📋 Pilih',
   sections: [{
      title: '🚀 Menu Utama',
      rows: [
         { title: '✨ Fitur A', description: '', rowId: '#FiturA' },
         { title: '🔍 Cari', description: '', rowId: '#Cari' }
      ]
   }]
}, { quoted: message })
```

#### 🗄️ Interactive (Native Flow)

```javascript
sock.sendMessage(jid, {
   image: { url: './image.jpg' },
   caption: '🗄️ Menu Interaktif',
   footer: 'riyoo',
   nativeFlow: [
      { text: '👋🏻 Sapa', id: '#Sapa', icon: 'review' },
      { text: '🌐 Website', url: 'https://github.com', useWebview: true },
      { text: '📋 Salin', copy: 'teks yang disalin' },
      { text: '📞 Hubungi', call: '628123456789' }
   ]
}, { quoted: message })
```

#### 🫙 Template

```javascript
sock.sendMessage(jid, {
   title: '👋🏻 Halo',
   image: { url: './image.jpg' },
   caption: '🫙 Template',
   footer: 'riyoo',
   templateButtons: [
      { text: '👉🏻 Tap', id: '#Tap' },
      { text: '🌐 Buka', url: 'https://github.com' },
      { text: '📞 Telepon', call: '628123456789' }
   ]
}, { quoted: message })
```

---

### 💳 Pesan Pembayaran

```javascript
// Undang pembayaran
sock.sendMessage(jid, { paymentInviteServiceType: 3 })

// Request pembayaran
sock.sendMessage(jid, {
   text: '💳 Request Pembayaran',
   requestPaymentFrom: '628123456789@s.whatsapp.net'
})
```

---

### 👁️ Opsi Pesan Lainnya

#### 🤖 Ikon AI

> [!NOTE]
> Hanya berfungsi di chat pribadi (`@s.whatsapp.net`).

```javascript
sock.sendMessage(jid, {
   image: { url: './image.jpg' },
   caption: '🤖 Pesan AI',
   ai: true
})
```

#### 📑 Spoiler

```javascript
sock.sendMessage(jid, {
   image: { url: './image.jpg' },
   caption: '❔ Spoiler nih',
   spoiler: true
})
```

#### 👁️ View Once

```javascript
sock.sendMessage(jid, {
   image: { url: './image.jpg' },
   caption: '👁️ Lihat sekali saja',
   viewOnce: true
})
```

#### 🕒 Ephemeral

```javascript
sock.sendMessage(jid, {
   image: { url: './image.jpg' },
   caption: '🕒 Ephemeral',
   ephemeral: true
})
```

#### 📰 External Ad Reply

```javascript
sock.sendMessage(jid, {
   text: '📰 External Ad',
   externalAdReply: {
      title: '📝 Info Menarik',
      body: 'Cek ini!',
      thumbnail: fs.readFileSync('./thumb.jpg'),
      largeThumbnail: false,
      url: 'https://github.com'
   }
}, { quoted: message })
```

#### 🧩 Raw

```javascript
sock.sendMessage(jid, {
   extendedTextMessage: {
      text: '📃 Dibuat manual dari struktur proto WhatsApp',
      contextInfo: {
         externalAdReply: {
            title: 'riyoo',
            thumbnail: fs.readFileSync('./thumb.jpg'),
            sourceApp: 'whatsapp',
            showAdAttribution: true,
            mediaType: 1
         }
      }
   },
   raw: true
}, { quoted: message })
```

---

### ♻️ Modifikasi Pesan

#### 🗑️ Hapus Pesan

```javascript
sock.sendMessage(jid, { delete: message.key })
```

#### ✏️ Edit Pesan

```javascript
// Edit teks
sock.sendMessage(jid, { text: '✨ Sudah diedit!', edit: message.key })

// Edit caption media
sock.sendMessage(jid, { caption: '✨ Caption baru', edit: message.key })
```

---

### 🧰 Konten Tambahan

#### 🏷️ Cari User ID (JID | PN / LID)

```javascript
// Via PN
const ids = await sock.findUserId('6281111111111@s.whatsapp.net')
console.log('🏷️ User ID:', ids)
// Output: { phoneNumber: '6281111111111@s.whatsapp.net', lid: '43411111111111@lid' }

// Via LID
const ids = await sock.findUserId('43411111111111@lid')
console.log('🏷️ User ID:', ids)
```

#### 🔑 Pairing Code Kustom

```javascript
const code = await sock.requestPairingCode('6281111111111', 'CUSTOMKODE')
console.log('🔗 Pairing code:', code)
```

#### 🖼️ Image Processing

```javascript
import { getImageProcessingLibrary } from 'riyoo'
import { readFile } from 'fs/promises'

const lib = await getImageProcessingLibrary()
const width = 512

if (lib.sharp?.default) {
   const output = await lib.sharp.default('./image.jpg').resize(width).jpeg({ quality: 80 }).toBuffer()
} else if (lib.image?.Transformer) {
   const buf = await readFile('./image.jpg')
   const output = await new lib.image.Transformer(buf).resize(width, undefined, 0).jpeg(50)
} else if (lib.jimp?.Jimp) {
   const img = await lib.jimp.Jimp.read('./image.jpg')
   const output = await img.resize({ w: width }).getBuffer('image/jpeg', { quality: 50 })
}
```

#### 📣 Manajemen Newsletter

```javascript
sock.newsletterCreate('Nama Channel', 'Deskripsi channel')
sock.newsletterFollow('1231111111111@newsletter')
sock.newsletterUnfollow('1231111111111@newsletter')
sock.newsletterMute('1231111111111@newsletter')
sock.newsletterUnmute('1231111111111@newsletter')
sock.newsletterDelete('1231111111111@newsletter')
```

#### 👥 Manajemen Grup

```javascript
// Buat grup
const group = await sock.groupCreate('Nama Grup', ['628123456789@s.whatsapp.net'])

// Info grup
const metadata = await sock.groupMetadata(jid)

// Update peserta
sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'add')    // tambah
sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'remove') // keluarkan
sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'promote') // jadikan admin
sock.groupParticipantsUpdate(jid, ['628123456789@s.whatsapp.net'], 'demote')  // cabut admin

// Pengaturan grup
sock.groupSettingUpdate(jid, 'announcement')    // hanya admin yang bisa chat
sock.groupSettingUpdate(jid, 'not_announcement') // semua bisa chat
sock.groupToggleEphemeral(jid, 86400) // aktifkan pesan hilang (86400 = 1 hari)
sock.groupToggleEphemeral(jid, 0)     // nonaktifkan pesan hilang
sock.groupLeave(jid)
```

#### 👤 Manajemen Profil

```javascript
sock.updateProfileName('Nama Saya')
sock.updateProfileStatus('Sedang sibuk')
sock.updateProfilePicture(jid, { url: './photo.jpg' })
sock.removeProfilePicture(jid)
sock.updateBlockStatus(jid, 'block')
sock.updateBlockStatus(jid, 'unblock')
sock.readMessages([message.key])
```

#### 🔐 Manajemen Privasi

```javascript
sock.updateLastSeenPrivacy('all')       // semua bisa lihat last seen
sock.updateLastSeenPrivacy('nobody')    // tidak ada yang bisa lihat
sock.updateOnlinePrivacy('all')
sock.updateProfilePicturePrivacy('contacts')
sock.updateReadReceiptsPrivacy('all')
sock.updateReadReceiptsPrivacy('none')
sock.updateGroupsAddPrivacy('contacts')
sock.updateCallPrivacy('everyone')
sock.updateDefaultDisappearingMode(86400)
```

#### 📡 Events

```javascript
sock.ev.on('connection.update', (update) => {})
sock.ev.on('creds.update', (update) => {})
sock.ev.on('messages.upsert', (update) => {})
sock.ev.on('messages.update', (update) => {})
sock.ev.on('messages.reaction', (update) => {})
sock.ev.on('message-receipt.update', (update) => {})
sock.ev.on('chats.upsert', (update) => {})
sock.ev.on('chats.update', (update) => {})
sock.ev.on('contacts.upsert', (update) => {})
sock.ev.on('contacts.update', (update) => {})
sock.ev.on('groups.upsert', (update) => {})
sock.ev.on('groups.update', (update) => {})
sock.ev.on('group-participants.update', (update) => {})
sock.ev.on('group.join-request', (update) => {})
sock.ev.on('presence.update', (update) => {})
sock.ev.on('lid-mapping.update', (update) => {})
sock.ev.on('blocklist.set', (update) => {})
sock.ev.on('blocklist.update', (update) => {})
sock.ev.on('call', (update) => {})
sock.ev.on('newsletter.reaction', (update) => {})
sock.ev.on('newsletter.view', (update) => {})
sock.ev.on('settings.update', (update) => {})
```

---

### 📦 Fork Base

Project ini berbasis [Baileys (GitHub)](https://github.com/WhiskeySockets/Baileys) dan merupakan fork dari [@itsliaaa/baileys](https://github.com/itsliaaa/baileys).

### 📣 Credits

Menggunakan definisi Protocol Buffer yang dikelola oleh [WPP Connect](https://github.com/wppconnect-team) via [`wa-proto`](https://github.com/wppconnect-team/wa-proto).

Kredit penuh diberikan kepada para maintainer dan kontributor asli Baileys:
- [purpshell](https://github.com/purpshell)
- [jlucaso1](https://github.com/jlucaso1)
- [adiwajshing](https://github.com/adiwajshing)

Fork ini didasarkan pada peningkatan dan modifikasi oleh [Lia Wynn](https://github.com/itsliaaa).

Terima kasih kepada [itsreimau](https://github.com/itsreimau) untuk perbaikan implementasi `updateBlockStatus`.

> [!CAUTION]
> ⚠️ **Modifikasi, penghapusan, atau salah representasi kredit ini sangat dilarang. Setiap redistribusi atau fork wajib mempertahankan bagian ini dalam bentuk aslinya tanpa pengecualian.**
