Pular para o conteúdo principal

Como Criar um Bot de WhatsApp com Node.js Como Criar um Bot de WhatsApp com Node.js

Como Criar um Bot de WhatsApp com Node.js

whatsmeow-node permite criar um bot de WhatsApp totalmente funcional em menos de 60 linhas de TypeScript. O bot se conecta como um dispositivo vinculado (como o WhatsApp Web), recebe mensagens em tempo real e pode responder com texto, mídia ou mensagens estruturadas.

Pré-requisitos

  • Node.js 18+ e npm
  • Uma conta WhatsApp para vincular como dispositivo
  • whatsmeow-node instalado (Guia de instalação)
  • Uma sessão pareada — siga o guia Como Parear o WhatsApp primeiro, ou o bot vai parear na primeira execução

Passo 1: Criar o Client

import { createClient } from "@whatsmeow-node/whatsmeow-node";

const client = createClient({ store: "session.db" });

client.on("error", (err) => {
console.error("Error:", err);
});

A opção store indica ao whatsmeow-node onde persistir a sessão. Passe um caminho de arquivo para SQLite (bom para desenvolvimento) ou uma string de conexão PostgreSQL para produção.

Passo 2: Tratar Mensagens Recebidas

client.on("message", async ({ info, message }) => {
// Skip your own messages to avoid echo loops
if (info.isFromMe) return;

const text =
(message.conversation as string) ??
(message.extendedTextMessage as { text?: string } | undefined)?.text;

if (!text) return;

console.log(`${info.pushName}: ${text}`);
});

Cada mensagem recebida dispara o evento "message" com dois objetos:

  • info — metadados (JID do remetente, JID do chat, timestamp, se é um grupo, etc.)
  • message — o conteúdo da mensagem protobuf
aviso

Sempre verifique info.isFromMe e ignore suas próprias mensagens. Sem essa verificação, seu bot vai responder às próprias mensagens em um loop infinito.

Passo 3: Responder Mensagens

// Simple text reply
await client.sendMessage(info.chat, { conversation: "Hello!" });

// Reply with a quote (shows the original message)
await client.sendRawMessage(info.chat, {
extendedTextMessage: {
text: "I got your message!",
contextInfo: {
stanzaId: info.id,
participant: info.sender,
quotedMessage: { conversation: text },
},
},
});

sendMessage é a forma mais simples de enviar texto. Para respostas que citam a mensagem original, use sendRawMessage com contextInfo.

Passo 4: Adicionar Comandos

Um padrão comum é rotear mensagens que começam com ! para handlers de comandos:

client.on("message", async ({ info, message }) => {
if (info.isFromMe) return;

const text =
(message.conversation as string) ??
(message.extendedTextMessage as { text?: string } | undefined)?.text;
if (!text) return;

// Mark as read
await client.markRead([info.id], info.chat, info.sender);

// Show typing indicator
await client.sendChatPresence(info.chat, "composing");

const command = text.toLowerCase().trim();

if (command === "!ping") {
await client.sendMessage(info.chat, { conversation: "pong" });
return;
}

if (command === "!help") {
await client.sendMessage(info.chat, {
conversation: "Commands: !ping, !help, !whoami",
});
return;
}

if (command === "!whoami") {
await client.sendMessage(info.chat, {
conversation: `You are ${info.pushName}\nJID: ${info.sender}`,
});
return;
}

// Echo everything else
await client.sendMessage(info.chat, { conversation: text });
});

Passo 5: Tratar Erros e Reconexão

// Session was permanently revoked — must re-pair
client.on("logged_out", ({ reason }) => {
console.error(`Logged out: ${reason}`);
client.close();
process.exit(1);
});

// Informational — whatsmeow handles reconnection automatically
client.on("disconnected", () => {
console.log("Disconnected, waiting for auto-reconnect...");
});
informação

Você não precisa de lógica manual de reconexão. A biblioteca whatsmeow subjacente reconecta automaticamente. O evento disconnected é apenas informativo.

Passo 6: Encerramento Gracioso

process.on("SIGINT", async () => {
console.log("Shutting down...");
await client.sendPresence("unavailable");
await client.disconnect();
client.close();
process.exit(0);
});

Definir a presença como "unavailable" antes de desconectar permite que seus contatos vejam que você ficou offline imediatamente, em vez de esperar pelo timeout.

Exemplo Completo

import { createClient } from "@whatsmeow-node/whatsmeow-node";
import qrcode from "qrcode-terminal";

const client = createClient({ store: "session.db" });

client.on("error", (err) => console.error("Error:", err));
client.on("logged_out", ({ reason }) => {
console.error(`Logged out: ${reason}`);
client.close();
process.exit(1);
});

client.on("message", async ({ info, message }) => {
if (info.isFromMe) return;

const text =
(message.conversation as string) ??
(message.extendedTextMessage as { text?: string } | undefined)?.text;
if (!text) return;

await client.markRead([info.id], info.chat, info.sender);
await client.sendChatPresence(info.chat, "composing");

const command = text.toLowerCase().trim();

if (command === "!ping") {
await client.sendMessage(info.chat, { conversation: "pong" });
} else if (command === "!help") {
await client.sendMessage(info.chat, {
conversation: "Commands: !ping, !help, !whoami",
});
} else if (command === "!whoami") {
await client.sendMessage(info.chat, {
conversation: `You are ${info.pushName}\nJID: ${info.sender}`,
});
} else {
// Echo
await client.sendRawMessage(info.chat, {
extendedTextMessage: {
text: text,
contextInfo: {
stanzaId: info.id,
participant: info.sender,
quotedMessage: { conversation: text },
},
},
});
}
});

async function main() {
const { jid } = await client.init();
if (!jid) {
console.log("Not paired — scan the QR code:");
client.on("qr", ({ code }) => qrcode.generate(code, { small: true }));
await client.getQRChannel();
}

await client.connect();
await client.sendPresence("available");
console.log("Bot is online! Commands: !ping, !help, !whoami");

process.on("SIGINT", async () => {
await client.sendPresence("unavailable");
await client.disconnect();
client.close();
process.exit(0);
});
}

main().catch(console.error);

Erros Comuns

Loops de eco

Sempre verifique info.isFromMe antes de processar uma mensagem. Sem isso, o bot responde às próprias respostas para sempre.

Mensagens de grupo

Em grupos, info.chat é o JID do grupo e info.sender é o indivíduo que enviou a mensagem. Responda para info.chat para enviar ao grupo, não para info.sender.

Rate limiting

O WhatsApp limita a taxa de envio de mensagens. Se o seu bot enviar muitas mensagens muito rapidamente, você pode ser bloqueado temporariamente. Veja Rate Limiting para detalhes.

Próximos Passos