# <div align='center'>wailib</div>

<div align='center'>

[![npm version](https://img.shields.io/npm/v/wailib.svg)](https://www.npmjs.com/package/wailib)
[![License](https://img.shields.io/badge/license-GPL%203-blue.svg)](LICENSE)

</div>

<div align="center">
  <strong>wailib</strong> — biblioteca WhatsApp Web API para bots Node.js.
  Fork do noxleyss com normalizadores de JID/LID portados de @boruto_vk7/baileys.
  Mantida por <a href="https://github.com/walkzin57">walkzin57</a>.
</div>

## Destaques do fork

- **Correção real de @lid** — cache bidirecional LID <-> numero de telefone alimentado pelo proprio protocolo (`sharedLidPhoneCache`). O `lidToJid` so converte quando existe o par conhecido; nunca inventa numero falso (JIDs desconhecidos permanecem `@lid`).
- **Metadata de grupos semeia o cache** — cada `groupMetadata` registra os pares `jid`/`lid`/`phone_number` de todos os participantes automaticamente.
- **Sem banner no terminal** — `require('wailib')` e silencioso.
- **Sem auto-follow de canais** — removida a rotina que seguia canais automaticamente apos conectar.

---

## Aviso

Este projeto **nao tem afiliacao, apoio ou ligacao oficial** com o WhatsApp ou suas subsidiarias. Site oficial: [whatsapp.com](https://whatsapp.com).

O usuario e totalmente responsavel pelo uso da biblioteca. Evite spam e automacao excessiva que violem os Termos de Servico do WhatsApp.

---

## Instalacao

```bash
npm install wailib
```

Importacao:

```javascript
// CommonJS
const { default: makeWASocket } = require("wailib")

// ES6 Module
import makeWASocket from "wailib"
```

---

## Inicio rapido

Exemplo basico (com QR Code):

```javascript
const { default: makeWASocket, DisconnectReason, useMultiFileAuthState } = require('wailib')
const { Boom } = require('@hapi/boom')

async function connectToWhatsApp() {
    // salva credenciais na pasta 'auth_info_wailib'
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_wailib')

    const sock = makeWASocket({
        auth: state,
        printQRInTerminal: true,
        browser: ['wailib', 'Desktop', '3.0']
    })

    // eventos de conexao
    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect } = update
        if (connection === 'close') {
            const shouldReconnect = (lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut
            console.log('Conexao fechada, reconectar:', shouldReconnect)
            if (shouldReconnect) connectToWhatsApp()
        } else if (connection === 'open') {
            console.log('Conectado ao WhatsApp!')
        }
    })

    // mensagens recebidas
    sock.ev.on('messages.upsert', async ({ messages }) => {
        for (const m of messages) {
            if (!m.message) continue
            console.log('Nova mensagem:', JSON.stringify(m, undefined, 2))

            // resposta automatica
            await sock.sendMessage(m.key.remoteJid, {
                text: 'Ola! Sou um bot usando wailib'
            })
        }
    })

    // salva credenciais quando atualizarem
    sock.ev.on('creds.update', saveCreds)
}

connectToWhatsApp()
```

---

## Conexao

### QR Code

```javascript
const { default: makeWASocket, Browsers } = require("wailib")

const sock = makeWASocket({
    browser: Browsers.ubuntu('My App'),
    printQRInTerminal: true
})
```

Apos iniciar, escaneie o QR com o WhatsApp do celular.

### Pairing Code

Permite conectar sem escanear QR. O numero deve ir sem `+`, `()` ou `-`, com codigo do pais.

```javascript
const { default: makeWASocket } = require("wailib")

const sock = makeWASocket({ printQRInTerminal: false })

if (!sock.authState.creds.registered) {
    const number = '5511999998888' // seu numero
    const code = await sock.requestPairingCode(number)
    console.log('Codigo:', code)
}
```

Com chave personalizada (8 caracteres, padrao da wailib: `WALKZINN`):

```javascript
if (!sock.authState.creds.registered) {
    const code = await sock.requestPairingCode(number, 'WALKZINN')
    console.log('Codigo:', code)
}
```

### Historico completo

```javascript
const sock = makeWASocket({
    browser: Browsers.macOS('Desktop'),
    syncFullHistory: true
})
```

---

## Configuracoes recomendadas

### Cache de metadata de grupos

```javascript
const { default: makeWASocket } = require("wailib")
const NodeCache = require('node-cache')

const groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false })

const sock = makeWASocket({
    cachedGroupMetadata: async (jid) => groupCache.get(jid)
})

sock.ev.on('groups.update', async ([event]) => {
    const metadata = await sock.groupMetadata(event.id)
    groupCache.set(event.id, metadata)
})

sock.ev.on('group-participants.update', async (event) => {
    const metadata = await sock.groupMetadata(event.id)
    groupCache.set(event.id, metadata)
})
```

### Retry de envio e decrypt de voto em enquete

```javascript
const sock = makeWASocket({
    getMessage: async (key) => await getMessageFromStore(key)
})
```

### Receber notificacoes no app

```javascript
const sock = makeWASocket({
    markOnlineOnConnect: false
})
```

---

## Salvar autenticacao

```javascript
const { default: makeWASocket, useMultiFileAuthState } = require("wailib")

async function connect() {
    const { state, saveCreds } = await useMultiFileAuthState('auth_info_wailib')
    const sock = makeWASocket({ auth: state })
    sock.ev.on('creds.update', saveCreds)
}

connect()
```

Importante: para producao, prefira banco SQL/NoSQL no lugar da pasta.

---

## Data Store

A wailib nao inclui armazenamento padrao, mas fornece uma implementacao in-memory:

```javascript
const { default: makeWASocket, makeInMemoryStore } = require("wailib")

const store = makeInMemoryStore({})
store.readFromFile('./wailib_store.json')

setInterval(() => {
    store.writeToFile('./wailib_store.json')
}, 10_000)

const sock = makeWASocket({})
store.bind(sock.ev)

sock.ev.on('chats.upsert', () => {
    console.log('chats', store.chats.all())
})

sock.ev.on('contacts.upsert', () => {
    console.log('contatos', Object.values(store.contacts))
})
```

---

## IDs do WhatsApp

O `id` (tambem chamado `jid`) identifica a pessoa ou grupo:

- Pessoa: `[codigo do pais][numero]@s.whatsapp.net` — ex.: `5511999998888@s.whatsapp.net`
- Grupo: `123456789-123345@g.us`
- Lista de transmissao: `[timestamp]@broadcast`
- Status/stories: `status@broadcast`
- LID (identificador anonimo): `123456789@lid` — a wailib converte para o numero real quando o par e conhecido via `sharedLidPhoneCache`.

---

## Utilitarios

- `getContentType` — retorna o tipo de conteudo da mensagem
- `getDevice` — retorna o dispositivo de origem da mensagem
- `makeCacheableSignalKeyStore` — acelera o store de chaves de auth
- `downloadContentFromMessage` — baixa conteudo de uma mensagem
- `sharedLidPhoneCache` — cache LID <-> numero (`set`, `get`, `getPhoneForLid`, `getLidForPhone`)
- `lidToJid` / `jidNormalizedUser` — normalizacao de JIDs com suporte a LID

---

## Enviar mensagens

```javascript
sock.sendMessage(jid, content, options)
```

### Texto

```javascript
await sock.sendMessage(jid, { text: 'ola mundo' })
```

### Citando mensagem

```javascript
await sock.sendMessage(jid, { text: 'ola mundo' }, { quoted: message })
```

### Mencionando usuario

```javascript
await sock.sendMessage(jid, {
    text: '@5511999998888',
    mentions: ['5511999998888@s.whatsapp.net']
})
```

### Encaminhar

```javascript
const msg = getMessageFromStore() // implemente voce
await sock.sendMessage(jid, { forward: msg })
```

### Localizacao

```javascript
await sock.sendMessage(jid, {
    location: {
        degreesLatitude: 24.121231,
        degreesLongitude: 55.1121221
    }
})
```

### Contato (vCard)

```javascript
const vcard = 'BEGIN:VCARD\n'
            + 'VERSION:3.0\n'
            + 'FN:Fulano Silva\n'
            + 'ORG:Empresa;\n'
            + 'TEL;type=CELL;type=VOICE;waid=5511999998888:+55 11 99999 8888\n'
            + 'END:VCARD'

await sock.sendMessage(jid, {
    contacts: { displayName: 'Fulano', contacts: [{ vcard }] }
})
```

### Reacao

```javascript
await sock.sendMessage(jid, {
    react: {
        text: '\u{1F496}', // vazio remove a reacao
        key: message.key
    }
})
```

### Fixar mensagem

```javascript
await sock.sendMessage(jid, {
    pin: {
        type: 1, // 0 para desafixar
        time: 86400, // 24h em segundos
        key: message.key
    }
})
```

### Enquete

```javascript
await sock.sendMessage(jid, {
    poll: {
        name: 'Minha enquete',
        values: ['Opcao 1', 'Opcao 2', 'Opcao 3'],
        selectableCount: 1,
        toAnnouncementGroup: false
    }
})
```

### Previa de link

Instale `link-preview-js` para previas automaticas:

```bash
npm install link-preview-js
```

### Midia

Em mensagens de midia use `{ stream: Stream }`, `{ url }` ou Buffer direto.

```javascript
// imagem com legenda
await sock.sendMessage(jid, { image: { url: './media/img.png' }, caption: 'ola mundo' })

// video (ptv: true = video nota)
await sock.sendMessage(jid, { video: { url: './media/video.mp4' }, caption: 'ola mundo', ptv: false })

// gif
await sock.sendMessage(jid, { video: fs.readFileSync('./media/gif.mp4'), caption: 'ola mundo', gifPlayback: true })

// audio
await sock.sendMessage(jid, { audio: { url: './media/audio.mp3' }, mimetype: 'audio/mp4' })

// visualizacao unica
await sock.sendMessage(jid, { image: { url: './media/img.png' }, viewOnce: true, caption: 'ola mundo' })
```

---

## Editar e apagar

```javascript
// apagar para todos
const msg = await sock.sendMessage(jid, { text: 'ola mundo' })
await sock.sendMessage(jid, { delete: msg.key })

// editar
await sock.sendMessage(jid, { text: 'texto atualizado', edit: msg.key })
```

---

## Baixar midia

```javascript
import { createWriteStream } from 'fs'
import { downloadMediaMessage, getContentType } from 'wailib'

sock.ev.on('messages.upsert', async ({ messages: [m] }) => {
    if (!m.message) return
    const messageType = getContentType(m)

    if (messageType === 'imageMessage') {
        const stream = await downloadMediaMessage(
            m,
            'stream',
            {},
            { logger, reuploadRequest: sock.updateMediaMessage }
        )
        stream.pipe(createWriteStream('./download.jpeg'))
    }
})
```

---

## Chamadas

```javascript
await sock.rejectCall(callId, callFrom)
```

---

## Leitura e presenca

```javascript
// marcar como lida (aceita varias chaves)
await sock.readMessages([key])

// presenca
await sock.sendPresenceUpdate('available', jid)
await sock.sendPresenceUpdate('composing', jid)
```

---

## Modificar chats

```javascript
// arquivar
await sock.chatModify({ archive: true, lastMessages: [lastMsgInChat] }, jid)

// silenciar 8h / reativar
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
await sock.chatModify({ mute: null }, jid)

// marcar nao lida
await sock.chatModify({ markRead: false, lastMessages: [lastMsgInChat] }, jid)

// limpar mensagens para mim
await sock.chatModify({
    clear: { messages: [{ id: 'ID_DA_MSG', fromMe: true, timestamp: '1654823909' }] }
}, jid)

// apagar chat
await sock.chatModify({
    delete: true,
    lastMessages: [{ key: lastMsgInChat.key, messageTimestamp: lastMsgInChat.messageTimestamp }]
}, jid)

// fixar / desafixar
await sock.chatModify({ pin: true }, jid)
await sock.chatModify({ pin: false }, jid)

// favoritar mensagem / remover favorito
await sock.chatModify({
    star: { messages: [{ id: 'messageID', fromMe: true }], star: true }
}, jid)
```

### Mensagens temporarias

```javascript
// ativar no chat (7 dias padrao)
await sock.sendMessage(jid, { disappearingMessagesInChat: 604800 })

// enviar como temporaria
await sock.sendMessage(jid, { text: 'oi' }, { ephemeralExpiration: 604800 })

// desativar
await sock.sendMessage(jid, { disappearingMessagesInChat: false })
```

---

## Consultas de usuario

```javascript
// verificar se o numero existe no WhatsApp
const [result] = await sock.onWhatsApp(jid)
if (result.exists) console.log(`${jid} existe, jid: ${result.jid}`)

// historico de conversa (max 50 por consulta)
const msg = await getOldestMessageInChat(jid)
await sock.fetchMessageHistory(50, msg.key, msg.messageTimestamp)

// status/bio
const status = await sock.fetchStatus(jid)

// foto de perfil ('image' = alta resolucao)
const ppUrl = await sock.profilePictureUrl(jid)
const ppUrlHigh = await sock.profilePictureUrl(jid, 'image')

// perfil comercial
const profile = await sock.getBusinessProfile(jid)

// presenca (online/digitando)
sock.ev.on('presence.update', console.log)
await sock.presenceSubscribe(jid)
```

---

## Perfil

```javascript
await sock.updateProfileStatus('Ola Mundo!')
await sock.updateProfileName('Meu Nome')
await sock.updateProfilePicture(jid, { url: './nova-foto.jpeg' })
await sock.removeProfilePicture(jid)
```

---

## Grupos

```javascript
// criar grupo
const group = await sock.groupCreate('Meu Grupo', ['5511...@s.whatsapp.net'])

// adicionar/remover/promover/rebaixar
await sock.groupParticipantsUpdate(jid, ['5511...@s.whatsapp.net'], 'add')
// 'remove' | 'promote' | 'demote'

// nome e descricao
await sock.groupUpdateSubject(jid, 'Novo Nome!')
await sock.groupUpdateDescription(jid, 'Nova Descricao!')

// configuracoes
await sock.groupSettingUpdate(jid, 'announcement')     // so admins falam
await sock.groupSettingUpdate(jid, 'not_announcement') // todos falam
await sock.groupSettingUpdate(jid, 'locked')           // so admins mudam config
await sock.groupSettingUpdate(jid, 'unlocked')

// sair
await sock.groupLeave(jid)

// convites
const code = await sock.groupInviteCode(jid)
const novo = await sock.groupRevokeInvite(jid)
await sock.groupAcceptInvite(code)
const info = await sock.groupGetInviteInfo(code)

// metadata (semeia o cache LID <-> numero dos participantes)
const metadata = await sock.groupMetadata(jid)
const all = await sock.groupFetchAllParticipating()

// mensagens temporarias do grupo
await sock.groupToggleEphemeral(jid, 86400)

// quem pode adicionar membros
await sock.groupMemberAddMode(jid, 'all_member_add') // ou 'admin_add'
```

> Nota: os participantes retornados em `metadata.participants` incluem `id` (jid original), `jid` (numero real quando disponivel) e `lid`.

---

## Privacidade

```javascript
await sock.updateBlockStatus(jid, 'block')     // ou 'unblock'
const privacySettings = await sock.fetchPrivacySettings(true)
const blocklist = await sock.fetchBlocklist()

await sock.updateLastSeenPrivacy('contacts')
await sock.updateOnlinePrivacy('all')
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('none')
await sock.updateReadReceiptsPrivacy('all')
await sock.updateGroupsAddPrivacy('contacts')
await sock.updateDefaultDisappearingMode(86400)
```

---

## Status e listas de transmissao

```javascript
await sock.sendMessage(
    jid,
    { image: { url }, caption },
    { backgroundColor, font, statusJidList, broadcast: true }
)

const bList = await sock.getBroadcastListInfo('1234@broadcast')
console.log(`lista: ${bList.name}, destinatarios: ${bList.recipients}`)
```

---

## Funcoes avancadas

### Log de debug

```javascript
const { P } = require('wailib')

const sock = makeWASocket({ logger: P({ level: 'debug' }) })
```

### Eventos brutos do websocket

```javascript
sock.ws.on('CB:edge_routing', (node) => {})
sock.ws.on('CB:edge_routing,id:abcd', (node) => {})
sock.ws.on('CB:edge_routing,id:abcd,routing_info', (node) => {})
```

---

## Dicas

- Use cache de metadata de grupos para melhor desempenho.
- Implemente reconexao automatica para estabilidade.
- Em producao, use banco de dados (MongoDB, PostgreSQL) em vez de store em memoria.
- Envolva chamadas do socket em try/catch.
- Respeite os limites do WhatsApp para evitar ban.
- Nunca compartilhe credenciais de autenticacao; use variaveis de ambiente.

---

## Licenca

Distribuido sob a licenca GPL-3.0. Veja o arquivo LICENSE.

---

<div align="center">
  <strong>wailib</strong><br>
  WhatsApp API com correcao de @lid para @pn
</div>
