Pular para o conteúdo principal

Como Enviar Mensagens no WhatsApp com TypeScript Como Enviar Mensagens no WhatsApp com TypeScript

Como Enviar Mensagens no WhatsApp com TypeScript

O whatsmeow-node oferece métodos async tipados para cada tipo de mensagem do WhatsApp — texto, respostas, menções, mídia, enquetes e reações. Este guia cobre cada um deles com exemplos em TypeScript.

Pré-requisitos

Configurar o Client

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

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

async function main() {
const { jid: myJid } = await client.init();
if (!myJid) {
console.error("Not paired — run the pairing flow first");
process.exit(1);
}
await client.connect();
await client.waitForConnection();

const recipient = "5512345678@s.whatsapp.net";

// ... send messages here
}

main().catch(console.error);

Enviar uma Mensagem de Texto

A forma mais simples — passe uma string conversation:

const resp = await client.sendMessage(recipient, {
conversation: "Hello from TypeScript!",
});

console.log(`Sent with ID: ${resp.id}`);

sendMessage é o método de alto nível. Ele recebe um JID e um objeto MessageContent, e retorna um SendResponse com o id e o timestamp da mensagem.

Responder com Citação

Para mostrar a mensagem original em um balão de citação, use sendRawMessage com contextInfo:

// Assume we received a message and have its info
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.sendRawMessage(info.chat, {
extendedTextMessage: {
text: `You said: "${text}"`,
contextInfo: {
stanzaId: info.id,
participant: info.sender,
quotedMessage: { conversation: text },
},
},
});
});

Mencionar Usuários com @

Inclua os JIDs em mentionedJid e use @<número> no texto:

await client.sendRawMessage(groupJid, {
extendedTextMessage: {
text: `Hey @${memberJid.split("@")[0]}, check this out!`,
contextInfo: {
mentionedJid: [memberJid],
},
},
});

Para mencionar todos em um grupo:

const group = await client.getGroupInfo(groupJid);
const jids = group.participants.map((p) => p.jid);
const mentions = jids.map((j) => `@${j.split("@")[0]}`).join(" ");

await client.sendRawMessage(groupJid, {
extendedTextMessage: {
text: `Attention: ${mentions}`,
contextInfo: { mentionedJid: jids },
},
});

Enviar Mídia

Faça o upload primeiro, depois envie os metadados com uma raw message:

// Upload an image
const media = await client.uploadMedia("/path/to/photo.jpg", "image");

await client.sendRawMessage(recipient, {
imageMessage: {
URL: media.URL,
directPath: media.directPath,
mediaKey: media.mediaKey,
fileEncSHA256: media.fileEncSHA256,
fileSHA256: media.fileSHA256,
fileLength: String(media.fileLength),
mimetype: "image/jpeg",
caption: "Check this out!",
},
});

Outros tipos de mídia seguem o mesmo padrão — videoMessage, audioMessage, documentMessage, stickerMessage.

Casing dos campos proto

Os campos da resposta do upload usam o casing exato do protobuf: URL, fileSHA256, fileEncSHA256não url, fileSha256. O casing errado falha silenciosamente.

Enviar uma Enquete

const resp = await client.sendPollCreation(
groupJid,
"Where should we eat?", // question
["Pizza", "Sushi", "Tacos"], // options
1, // max selectable
);

Reagir a uma Mensagem

// Add a reaction
await client.sendReaction(chat, senderJid, messageId, "🔥");

// Remove a reaction (empty string)
await client.sendReaction(chat, senderJid, messageId, "");

Editar uma Mensagem Enviada

const sent = await client.sendMessage(recipient, {
conversation: "Hello!",
});

// Edit it — only works on your own messages
await client.editMessage(recipient, sent.id, {
conversation: "Hello! (edited)",
});

Apagar uma Mensagem

// Revoke for everyone — only your own messages, within the time limit
await client.revokeMessage(chat, myJid, sent.id);

Marcar como Lida

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

Exemplo Completo

Um bot que ecoa mensagens de texto como respostas com citação:

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("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");

await client.sendRawMessage(info.chat, {
extendedTextMessage: {
text: `Echo: ${text}`,
contextInfo: {
stanzaId: info.id,
participant: info.sender,
quotedMessage: { conversation: text },
},
},
});
});

async function main() {
const { jid } = await client.init();
if (!jid) {
client.on("qr", ({ code }) => qrcode.generate(code, { small: true }));
await client.getQRChannel();
}
await client.connect();
console.log("Listening for messages...");

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

main().catch(console.error);

Erros Comuns

sendMessage vs sendRawMessage

sendMessage é para texto simples. Para qualquer coisa estruturada (citações, menções, mídia), use sendRawMessage com o formato protobuf completo.

Mensagens de grupo vão para info.chat

Em grupos, sempre envie para info.chat (o JID do grupo), não para info.sender (o indivíduo). Enviar para info.sender inicia uma conversa privada.

Rate limiting

O WhatsApp limita a taxa de envio. Espaçe as mensagens, especialmente em grupos. Veja Rate Limiting para detalhes.