# Apa Yang Baru di Mataram

Mataram adalah pengembangan lanjutan dari Baileys dengan berbagai fitur tambahan, perbaikan, dan dokumentasi lengkap.

---

##  Autentikasi

### Custom Pairing Code
Pairing code bisa kamu tentukan sendiri, maksimal 8 karakter.

```ts
const code = await sock.requestPairingCode('6281234567890', 'MATARAM')
// Pairing code: MATARAM
// Tinggal ketik di WhatsApp > Perangkat Tertaut
```

Kode kustom ini memudahkan pengguna bot yang kesulitan mengetik kode acak panjang.

### Browser Compatible
`Browsers.mataram()` sekarang otomatis pakai platform `Mac OS` agar tidak ditolak WhatsApp.

```ts
Browsers.mataram('App')   // → ['Mac OS', 'Mataram/App', '14.4.1'] ✅
Browsers.macOS('Chrome')   // → ['Mac OS', 'Chrome', '14.4.1'] ✅
Browsers.windows('Edge')   // → ['Windows', 'Edge', '10.0.22631'] ✅
```

> ⚠ WhatsApp hanya menerima platform asli: `Mac OS`, `Windows`, `Ubuntu`.

---

##  Newsletter / Channel

Dukungan penuh untuk WhatsApp Channel/Newsletter.

### Follow & Unfollow Channel
```ts
await sock.newsletterFollow('120363427213718147@newsletter')
await sock.newsletterUnfollow('120363427213718147@newsletter')
```

### Membuat Channel
```ts
const channel = await sock.newsletterCreate('Nama Channel', 'Deskripsi')
console.log('ID:', channel.id)
```

### Melihat Metadata Channel
```ts
const meta = await sock.newsletterMetadata('invite', '0029VbC9SyFKWEKuKjaQxV21')
// atau via JID:
const meta2 = await sock.newsletterMetadata('jid', '120363...@newsletter')

console.log('Nama:', meta.name)
console.log('Subscribers:', meta.subscribers)
console.log('Deskripsi:', meta.description)
```

### Update Channel
```ts
await sock.newsletterUpdateName('120363...@newsletter', 'Nama Baru')
await sock.newsletterUpdateDescription('120363...@newsletter', 'Deskripsi baru')

await sock.newsletterUpdatePicture('120363...@newsletter', { url: './foto.jpg' })
await sock.newsletterRemovePicture('120363...@newsletter')
```

### Subscribe Live Updates
Mendapatkan notifikasi实时 dari channel.

```ts
const result = await sock.subscribeNewsletterUpdates('120363...@newsletter')
console.log('Duration:', result?.duration)
```

### Mute / Unmute Channel
```ts
await sock.newsletterMute('120363...@newsletter')
await sock.newsletterUnmute('120363...@newsletter')
```

### React ke Post Channel
```ts
await sock.newsletterReactMessage('120363...@newsletter', 'message_server_id', '')
// Untuk hapus reaction (param ketiga dikosongkan):
await sock.newsletterReactMessage('120363...@newsletter', 'message_server_id')
```

### Jumlah Subscriber & Admin
```ts
const { subscribers } = await sock.newsletterSubscribers('120363...@newsletter')
console.log('Subscribers:', subscribers)

const adminCount = await sock.newsletterAdminCount('120363...@newsletter')
console.log('Admin:', adminCount)
```

### Ganti Owner & Demote Admin
```ts
await sock.newsletterChangeOwner('120363...@newsletter', '628xxx@s.whatsapp.net')
await sock.newsletterDemote('120363...@newsletter', '628xxx@s.whatsapp.net')
```

### Fetch Postingan Channel
```ts
const posts = await sock.newsletterFetchMessages(
  '120363...@newsletter',
  10, // jumlah pesan
  0,  // since (timestamp)
  0,  // after (timestamp)
)
```

### Hapus Channel
```ts
await sock.newsletterDelete('120363...@newsletter')
```

---

##  Groups

### Request Join
Lihat, setujui, atau tolak permintaan masuk grup.

```ts
// Lihat daftar permintaan
const requests = await sock.groupRequestParticipantsList('123@g.us')

// Setujui
await sock.groupRequestParticipantsUpdate('123@g.us', ['628xxx'], 'approve')

// Tolak
await sock.groupRequestParticipantsUpdate('123@g.us', ['628xxx'], 'reject')
```

### Join Approval Mode
Atur mode persetujuan anggota baru.

```ts
await sock.groupJoinApprovalMode('123@g.us', 'request_required')
```

### Invite V4 (GroupInviteMessage)
Gabung grup menggunakan pesan undangan.

```ts
await sock.groupAcceptInviteV4('123@g.us', groupInviteMessage)
await sock.groupRevokeInviteV4('123@g.us')
```

### Cek Info Undangan
```ts
const info = await sock.groupGetInviteInfo('INVITE_CODE')
console.log('Nama grup:', info.subject)
console.log('Anggota:', info.participants?.length)
```

### Fetch Disappearing Duration
```ts
const duration = await sock.fetchDisappearingDuration('123@g.us')
console.log('Disappearing mode:', duration, 'detik')
```

---

##  Communities

Dukungan penuh untuk WhatsApp Communities.

### Membuat Community
```ts
const community = await sock.communityCreate('Nama Komunitas', 'Deskripsi singkat')
```

### Melihat Metadata Community
```ts
const meta = await sock.communityMetadata('123@g.us')
console.log('Nama:', meta.subject, 'Grup:', meta.groups?.length)
```

### Membuat Grup di Dalam Community
```ts
await sock.communityCreateGroup(
  'Nama Grup Baru',
  ['628xxx@s.whatsapp.net'],
  'parent_community@g.us'
)
```

### Menautkan / Melepas Grup
```ts
await sock.communityLinkGroup('community@g.us', 'sub_group@g.us')
await sock.communityUnlinkGroup('community@g.us', 'sub_group@g.us')
```

### Manajemen Anggota
```ts
// Tambah anggota
await sock.communityParticipantsUpdate('123@g.us', ['628xxx'], 'add')

// Hapus anggota
await sock.communityParticipantsUpdate('123@g.us', ['628xxx'], 'remove')

// Daftar permintaan bergabung
const members = await sock.communityRequestParticipantsList('123@g.us')

// Setujui / tolak
await sock.communityRequestParticipantsUpdate('123@g.us', ['628xxx'], 'approve')
await sock.communityRequestParticipantsUpdate('123@g.us', ['628xxx'], 'reject')
```

### Pengaturan Community
```ts
// Hanya admin bisa kirim pesan
await sock.communitySettingUpdate('123@g.us', 'announcement')

// Semua anggota bisa kirim
await sock.communitySettingUpdate('123@g.us', 'not_announcement')

// Mode tambah anggota
await sock.communityMemberAddMode('123@g.us', 'admin_add')

// Mode persetujuan
await sock.communityJoinApprovalMode('123@g.us', 'request_required')

// Pesan sementara
await sock.communityToggleEphemeral('123@g.us', 86400)
```

### Undangan Community
```ts
// Dapatkan kode undangan
const code = await sock.communityInviteCode('123@g.us')

// Reset kode
await sock.communityRevokeInvite('123@g.us')

// Gabung via kode
await sock.communityAcceptInvite('INVITE_CODE')

// Cek info undangan
const info = await sock.communityGetInviteInfo('INVITE_CODE')
```

### Update Community
```ts
await sock.communityUpdateSubject('123@g.us', 'Nama Baru')
await sock.communityUpdateDescription('123@g.us', 'Deskripsi baru')
```

### Keluar dari Community
```ts
await sock.communityLeave('123@g.us')
```

---

##  Business

### Business Profile
Ambil dan perbarui profil bisnis.

```ts
// Ambil profil
const profile = await sock.getBusinessProfile('628xxx@s.whatsapp.net')
console.log('Deskripsi:', profile.description)
console.log('Kategori:', profile.category)
console.log('Email:', profile.email)
console.log('Website:', profile.website)

// Update profil
await sock.updateBussinesProfile({
  address: 'Jakarta, Indonesia',
  email: 'hello@example.com',
  description: 'Bot WhatsApp bisnis',
  websites: ['https://example.com'],
})
```

### Product Catalog
Manajemen produk untuk bisnis.

```ts
// Lihat katalog
const catalog = await sock.getCatalog('628xxx@s.whatsapp.net')
console.log('Produk:', catalog.products?.length)

// Buat produk baru
const product = await sock.productCreate({
  name: 'Produk Baru',
  price: '50000',
  description: 'Deskripsi produk',
  images: [{ url: './product.jpg' }],
})

// Update produk
await sock.productUpdate(product.id, {
  name: 'Nama Baru',
  price: '60000',
})

// Hapus produk
await sock.productDelete(product.id)
```

### Collections
```ts
const collections = await sock.getCollections('628xxx@s.whatsapp.net')
```

### Order Management
```ts
const orders = await sock.getOrderDetails('order_id', '628xxx@s.whatsapp.net')
```

### Cover Photo
```ts
await sock.updateCoverPhoto({ url: './cover.jpg' })
await sock.removeCoverPhoto()
```

---

##  Labels

Manajemen label untuk chat dan pesan.

```ts
// Buat label
await sock.addLabel({ name: 'Penting', color: 1 })
// color: 0-20 (warna berbeda)

// Label ke chat
await sock.addChatLabel('628xxx@s.whatsapp.net', 'label_id')
await sock.removeChatLabel('628xxx@s.whatsapp.net', 'label_id')

// Label ke pesan
await sock.addMessageLabel('628xxx@s.whatsapp.net', 'message_id', 'label_id')
await sock.removeMessageLabel('628xxx@s.whatsapp.net', 'message_id', 'label_id')
```

---

##  Privacy & Calls

### Update Call Privacy
Mengatur siapa yang bisa menghubungi lewat panggilan.

```ts
await sock.updateCallPrivacy('all')
// 'all' | 'contacts' | 'none'
```

### Update Link Preview Privacy
Mematikan/menyalakan link preview.

```ts
await sock.updateDisableLinkPreviewsPrivacy(true) // matikan
await sock.updateDisableLinkPreviewsPrivacy(false) // nyalakan
```

### Default Disappearing Mode
Mengatur mode pesan sementara default.

```ts
await sock.updateDefaultDisappearingMode(86400)
// 0 = off
// 86400 = 24 jam
// 604800 = 7 hari
// 7776000 = 90 hari
```

### Privacy Settings Lainnya
Semua pengaturan privasi yang tersedia:

```ts
await sock.updateLastSeenPrivacy('all')        // 'all' | 'contacts' | 'none'
await sock.updateOnlinePrivacy('all')           // 'all' | 'match_last_seen'
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('contacts')
await sock.updateReadReceiptsPrivacy('all')     // 'all' | 'none'
await sock.updateGroupsAddPrivacy('contacts')
```

### Block / Unblock
```ts
await sock.updateBlockStatus('628xxx@s.whatsapp.net', 'block')
await sock.updateBlockStatus('628xxx@s.whatsapp.net', 'unblock')
const blocked = await sock.fetchBlocklist()
```

### Reject Panggilan
```ts
sock.ev.on('call', async (calls) => {
  for (const call of calls) {
    if (!call.isGroup) {
      await sock.rejectCall(call.id, call.from)
    }
  }
})
```

---

##  Chat Management

### Star Message
```ts
// Via chatModify
await sock.chatModify({ star: { messages: [{ id: msgId, fromMe: true }], star: true } }, jid)

// Via standalone function
await sock.star(jid, [{ id: msgId, fromMe: true }], true)
```

### Archive / Mute / Delete Chat
```ts
await sock.chatModify({ archive: true }, jid)
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid) // 8 jam
await sock.chatModify({ mute: null }, jid) // unmute
await sock.chatModify({ delete: true }, jid)
```

### Read / Unread
```ts
await sock.readMessages([messageKey])
await sock.chatModify({ markRead: false, lastMessages: [lastMsg] }, jid)
```

### Disappearing Messages
```ts
await sock.sendMessage(jid, { disappearingMessagesInChat: 86400 })
await sock.sendMessage(jid, { text: 'rahasia' }, { ephemeralExpiration: 86400 })
```

---

##  Fetch Data

### Cek Nomor WhatsApp
```ts
const [result] = await sock.onWhatsApp('6281234567890')
console.log('Terdaftar:', result.exists)
console.log('JID:', result.jid)
```

### Fetch Message History
Ambil riwayat pesan lama dari server.

```ts
const oldest = await getOldestMessageFromStore(jid)
await sock.fetchMessageHistory(50, oldest.key, oldest.messageTimestamp)
// Hasil akan masuk lewat event 'messaging-history.set'
```

### Fetch Profile Picture
```ts
const lowRes = await sock.profilePictureUrl(jid)
const highRes = await sock.profilePictureUrl(jid, 'image')
```

### Fetch Status
```ts
const status = await sock.fetchStatus(jid)
```

### Fetch Privacy Settings
```ts
const settings = await sock.fetchPrivacySettings(true)
```

### Fetch New Chat Message Cap
```ts
const cap = await sock.fetchNewChatMessageCap()
console.log('Kuota:', cap.quota, 'Pemakaian:', cap.usage)
```

---

##  Mengirim Pesan

Semua jenis pesan dikirim lewat satu fungsi: `sock.sendMessage(jid, content, options)`.

### Text
```ts
await sock.sendMessage('628xxx@s.whatsapp.net', { text: 'Halo!' })
```

### Image
```ts
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/foto.jpg' },
  caption: 'Foto keren',
})
```

### Video
```ts
await sock.sendMessage(jid, {
  video: { url: './video.mp4' },
  caption: 'Video',
  ptv: false, // true = video note lingkaran
})
```

### Audio
```ts
await sock.sendMessage(jid, {
  audio: { url: './audio.ogg' },
  mimetype: 'audio/ogg; codecs=opus',
  ptt: true, // true = voice note
})
```

### Document
```ts
await sock.sendMessage(jid, {
  document: { url: './file.pdf' },
  fileName: 'dokumen.pdf',
  mimetype: 'application/pdf',
})
```

### Sticker
```ts
await sock.sendMessage(jid, { sticker: { url: './sticker.webp' } })
```

### Reply / Quote
```ts
await sock.sendMessage(jid, { text: 'Reply' }, { quoted: originalMessage })
```

### Reaction
```ts
await sock.sendMessage(jid, { react: { text: '', key: messageKey } })
```

### Mention
```ts
await sock.sendMessage(jid, {
  text: 'Halo @628xxx',
  mentions: ['628xxx@s.whatsapp.net'],
})
```

### Poll
```ts
await sock.sendMessage(jid, {
  poll: {
    name: 'Pertanyaan?',
    values: ['Opsi A', 'Opsi B', 'Opsi C'],
    selectableCount: 1,
  },
})
```

### Location
```ts
await sock.sendMessage(jid, {
  location: { degreesLatitude: -6.2, degreesLongitude: 106.8 },
})
```

### Contact
```ts
await sock.sendMessage(jid, {
  contacts: {
    displayName: 'Nama',
    contacts: [{ vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:Nama\nTEL;waid=628xxx\nEND:VCARD' }],
  },
})
```

### Forward
```ts
await sock.sendMessage(jid, { forward: originalMessage })
```

### View Once
```ts
await sock.sendMessage(jid, {
  image: { url: './foto.jpg' },
  viewOnce: true,
})
```

### Edit & Delete
```ts
await sock.sendMessage(jid, { text: 'Teks baru', edit: messageKey })
await sock.sendMessage(jid, { delete: messageKey })
```

### Link Preview
```ts
const sock = makeWASocket({ generateHighQualityLinkPreview: true })
await sock.sendMessage(jid, { text: 'https://github.com/IbraDecode/Mataram' })
// Preview otomatis digenerate
```

---

##  Media

### Download Media
```ts
import { downloadContentFromMessage } from '@mataram/wa'

const stream = await downloadContentFromMessage(media, 'image')
const chunks = []
for await (const chunk of stream) chunks.push(chunk)
const buffer = Buffer.concat(chunks)
```

### Download via Utility
```ts
import { downloadMediaMessage } from '@mataram/wa'

const buffer = await downloadMediaMessage(message, 'buffer', {}, {
  reuploadRequest: sock.updateMediaMessage,
})
```

### Re-upload Media Kedaluwarsa
```ts
await sock.updateMediaMessage(msg)
```

### Thumbnail
Thumbnail digenerate otomatis untuk image & video (butuh `jimp` atau `sharp`).

---

##  Profile Management

```ts
// Nama
await sock.updateProfileName('Nama Baru')

// Status
await sock.updateProfileStatus('Sedang sibuk')

// Foto profil
await sock.updateProfilePicture(jid, { url: './foto.jpg' })
await sock.removeProfilePicture(jid)

// Fetch
const pp = await sock.profilePictureUrl(jid, 'image')
const status = await sock.fetchStatus(jid)
```

---

## ⚙ Socket Config Options

| Opsi | Default | Deskripsi |
|---|---|---|
| `auth` |  | **(wajib)** State autentikasi |
| `browser` | `['Mac OS','Chrome','14.4.1']` | Identitas browser |
| `version` | latest WA | Versi protokol |
| `waWebSocketUrl` | `wss://web.whatsapp.com/ws/chat` | URL WebSocket |
| `connectTimeoutMs` | `20000` | Timeout koneksi |
| `keepAliveIntervalMs` | `30000` | Interval ping |
| `defaultQueryTimeoutMs` | `60000` | Timeout query |
| `markOnlineOnConnect` | `true` | Online pas connect |
| `syncFullHistory` | `true` | Sinkron history lengkap |
| `fireInitQueries` | `true` | Init query otomatis |
| `retryRequestDelayMs` | `250` | Delay retry |
| `maxMsgRetryCount` | `5` | Maksimal retry |
| `emitOwnEvents` | `true` | Event untuk aksi sendiri |
| `logger` | pino | Instance logger |
| `agent` |  | Proxy untuk WS |
| `fetchAgent` |  | Proxy untuk fetch |
| `msgRetryCounterCache` |  | Cache retry |
| `getMessage` |  | Lookup pesan untuk retry |
| `cachedGroupMetadata` |  | Cache metadata grup |
| `generateHighQualityLinkPreview` | `false` | Generate link preview |

---

##  Events

Daftar semua event yang bisa di-listen:

### connection.update
```ts
sock.ev.on('connection.update', ({ connection, lastDisconnect, qr }) => {
  // connection: 'connecting' | 'open' | 'close'
  // lastDisconnect: error info
  // qr: QR code (base64)
})
```

### messages.upsert
```ts
sock.ev.on('messages.upsert', ({ messages, type }) => {
  // type: 'notify' = realtime, 'append' = history
  for (const msg of messages) {
    console.log(msg.message?.conversation)
  }
})
```

### messages.update & messages.reaction
```ts
sock.ev.on('messages.update', (updates) => {
  for (const { key, update } of updates) {
    if (update.pollUpdates) {
      // Ada vote poll baru
    }
  }
})

sock.ev.on('messages.reaction', ({ key, reaction }) => {
  console.log(reaction.text, 'dari', key.participant)
})
```

### Event Lainnya

| Event | Deskripsi |
|---|---|
| `creds.update` | Credentials berubah |
| `messages.delete` | Pesan dihapus |
| `message-receipt.update` | Status terkirim/dibaca |
| `chats.upsert` | Chat baru |
| `chats.update` | Chat berubah |
| `chats.delete` | Chat dihapus |
| `contacts.upsert` | Kontak baru |
| `contacts.update` | Kontak berubah |
| `presence.update` | Status online/typing |
| `groups.upsert` | Grup baru |
| `groups.update` | Grup berubah |
| `group-participants.update` | Anggota grup berubah |
| `messaging-history.set` | History sync |
| `messaging-history.status` | Status sync |

### Batch Processing
Proses banyak event sekaligus:

```ts
sock.ev.process(async (events) => {
  if (events['connection.update']) { /* handle */ }
  if (events['messages.upsert']) { /* handle */ }
})
```

---

##  Events & Error Handling

### Disconnect Reasons
```ts
import { DisconnectReason } from '@mataram/wa'

sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
  if (connection === 'close') {
    const code = lastDisconnect?.error?.output?.statusCode
    switch (code) {
      case DisconnectReason.loggedOut:      // 401 - session expired
      case DisconnectReason.connectionLost: // 408 - koneksi putus
      case DisconnectReason.connectionClosed: // 428 - koneksi ditutup
      case DisconnectReason.restartRequired: // 515 - restart normal
      case DisconnectReason.badSession:     // 500 - session rusak
      case DisconnectReason.timedOut:       // 408 - timeout
      case DisconnectReason.forbidden:      // 403 - diblokir WA
    }
  }
})
```

### Low-Level WebSocket
```ts
sock.ws.on('CB:edge_routing', (node) => {
  console.log('Raw node:', node.tag)
})

sock.ws.on('CB:notification,type:server_sync', (node) => {
  // Filter by tag + attribute
})
```

### Custom Query (IQ)
```ts
const result = await sock.query({
  tag: 'iq',
  attrs: { to: '@s.whatsapp.net', type: 'get', xmlns: 'w:profile:picture' },
  content: [{ tag: 'picture', attrs: { type: 'image', query: 'url' } }],
})
```

---

##  Utility Functions

### WA Version Check
```ts
import { fetchLatestVersion } from '@mataram/wa'

const { version, isLatest } = await fetchLatestVersion()
console.log('WA Version:', version.join('.'))
```

### Signal Repository
Akses ke enkripsi/dekripsi Signal Protocol.

```ts
// Dekripsi pesan grup
const groupMsg = await sock.signalRepository.decryptGroupMessage({
  group: '123@g.us',
  authorJid: '628xxx@s.whatsapp.net',
  msg: ciphertext,
})

// Dekripsi pesan personal
const personalMsg = await sock.signalRepository.decryptMessage({
  jid: '628xxx@s.whatsapp.net',
  type: 'pkmsg',
  ciphertext: buffer,
})
```

### USync Query
Mencari informasi kontak, perangkat, status, dll.

```ts
import { USyncQuery, USyncUser } from '@mataram/wa'

const query = new USyncQuery()
query.addUser(new USyncUser('628xxx@s.whatsapp.net'))
query.addProtocol(USyncQuery.createContactProtocol())
query.addProtocol(USyncQuery.createStatusProtocol())
query.addProtocol(USyncQuery.createDeviceProtocol())
query.addProtocol(USyncQuery.createDisappearingModeProtocol())
query.addProtocol(USyncQuery.createUsernameProtocol())

const result = await sock.executeUSyncQuery(query)
```

### Wait for Connection
```ts
await sock.waitForConnectionUpdate((update) => update.connection === 'open')
```

### Logout & Disconnect
```ts
await sock.logout() // Logout dari WhatsApp
sock.end(new Error('manual')) // Tutup socket
```

### BufferJSON
Untuk serialisasi Buffer ke JSON.

```ts
import { BufferJSON } from '@mataram/wa'

const json = JSON.stringify(data, BufferJSON.replacer)
const parsed = JSON.parse(json, BufferJSON.reviver)
```

### Get Content Type & Device
```ts
import { getContentType, getDevice } from '@mataram/wa'

const type = getContentType(message)  // 'conversation' | 'imageMessage' | etc.
const device = getDevice(message)      // 'android' | 'ios' | 'web' | etc.
```

### makeCacheableSignalKeyStore
Mempercepat akses key store dengan cache.

```ts
const sock = makeWASocket({
  auth: { creds: state.creds, keys: makeCacheableSignalKeyStore(state.keys) },
})
```

---

##  Keamanan & Kualitas

### Obfuscated Code
Kode JavaScript di-obfuscate sebelum publish ke npm. Melindungi logic dari reverse engineering.

### TypeScript Strict
Full type definitions dengan `strict`, `strictNullChecks`, `noUncheckedIndexedAccess`.

### Error Handling (Boom)
Semua error protokol pake `@hapi/boom` dengan HTTP-style status codes.

```ts
import { Boom } from '@hapi/boom'

sock.ev.on('connection.update', ({ lastDisconnect }) => {
  const err = lastDisconnect?.error
  if (err instanceof Boom) {
    const code = err.output.statusCode
    // 401 = loggedOut, 408 = timeout, 428 = connectionClosed, 515 = restartRequired
  }
})
```

---

##  Install

```bash
npm install @mataram/wa
```

```ts
import makeWASocket from '@mataram/wa'
```

##  Dokumentasi Lengkap

github.com/IbraDecode/Mataram

| Topik | File |
|---|---|
| Semua fitur | docs/apa-yang-baru.md |
| Autentikasi | docs/autentikasi.md |
| Events | docs/events.md |
| Konfigurasi | docs/config.md |
| Kirim Pesan | docs/mengirim-pesan.md |
| Grup | docs/grup.md |
| Newsletter | docs/newsletter.md |
| Media | docs/media.md |
| Utilities | docs/utilities.md |
| Troubleshooting | docs/troubleshooting.md |

##  Community

**WhatsApp Channel:** https://whatsapp.com/channel/0029VbC9SyFKWEKuKjaQxV21
