
Como Usar o whatsmeow-node com Typebot
O Typebot é um construtor de chatbots open-source com editor visual de fluxos. Normalmente é conectado ao WhatsApp através da Evolution API (que usa Baileys). Você pode substituir toda essa camada pelo whatsmeow-node para uma conexão mais leve e estável.
Como Funciona
WhatsApp User
↕
whatsmeow-node Bridge (Express)
↕ Typebot API
Typebot Flow Engine
Em vez de WhatsApp, Evolution API (Baileys), Typebot, você usa WhatsApp, whatsmeow-node, Typebot diretamente. Menos peças móveis, menos memória, mais estabilidade.
Pré-requisitos
- Uma sessão pareada do whatsmeow-node (Como Parear)
- Uma instância do Typebot (self-hosted ou cloud) com um fluxo publicado
- Express:
npm install express
Passo 1: Obter o ID do Typebot
- Abra seu fluxo do Typebot no editor
- Clique em Share e anote o ID do typebot na URL ou nas configurações de embed
- A URL base da API do Typebot é
https://typebot.io(cloud) ou a URL do seu self-hosted
Passo 2: Construir a Ponte
A ponte gerencia as conversas — ela inicia uma sessão do Typebot por usuário do WhatsApp e repassa as mensagens nos dois sentidos:
import { createClient } from "@whatsmeow-node/whatsmeow-node";
const client = createClient({ store: "session.db" });
const TYPEBOT_URL = process.env.TYPEBOT_URL ?? "https://typebot.io";
const TYPEBOT_ID = process.env.TYPEBOT_ID!;
// Track active Typebot sessions per WhatsApp user
const sessions = new Map<string, string>(); // JID → sessionId
// --- Start or continue a Typebot conversation ---
async function handleMessage(jid: string, text: string): Promise<string[]> {
const sessionId = sessions.get(jid);
let response;
if (!sessionId) {
// Start a new Typebot session
response = await fetch(`${TYPEBOT_URL}/api/v1/typebots/${TYPEBOT_ID}/startChat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: { type: "text", text } }),
}).then((r) => r.json());
if (response.sessionId) {
sessions.set(jid, response.sessionId);
}
} else {
// Continue existing session
response = await fetch(`${TYPEBOT_URL}/api/v1/sessions/${sessionId}/continueChat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: { type: "text", text } }),
}).then((r) => r.json());
}
// Extract text messages from Typebot response
return extractMessages(response);
}
// --- Parse Typebot response blocks into text messages ---
function extractMessages(response: Record<string, unknown>): string[] {
const messages: string[] = [];
const msgs = (response.messages as Array<{ type: string; content?: { richText?: Array<{ children: Array<{ text?: string }> }> } }>) ?? [];
for (const msg of msgs) {
if (msg.type === "text" && msg.content?.richText) {
const text = msg.content.richText
.map((block) => block.children.map((c) => c.text ?? "").join(""))
.join("\n");
if (text.trim()) messages.push(text);
}
}
return messages;
}
// --- Listen for WhatsApp messages ---
client.on("message", async ({ info, message }) => {
if (info.isFromMe || info.isGroup) return;
const text =
(message.conversation as string) ??
(message.extendedTextMessage as { text?: string } | undefined)?.text;
if (!text) return;
await client.sendChatPresence(info.chat, "composing");
try {
const replies = await handleMessage(info.sender, text);
for (const reply of replies) {
await client.sendMessage(info.chat, { conversation: reply });
// Small delay between multiple messages
if (replies.length > 1) {
await new Promise((r) => setTimeout(r, 1000));
}
}
} catch (err) {
console.error("Typebot error:", err);
await client.sendMessage(info.chat, {
conversation: "Sorry, I'm having trouble right now. Try again in a moment.",
});
}
});
async function main() {
const { jid } = await client.init();
if (!jid) {
console.error("Not paired!");
process.exit(1);
}
await client.connect();
await client.sendPresence("available");
console.log("Typebot bridge is online!");
process.on("SIGINT", async () => {
await client.sendPresence("unavailable");
await client.disconnect();
client.close();
process.exit(0);
});
}
main().catch(console.error);
Tratando Tipos de Input
Fluxos do Typebot podem solicitar diferentes tipos de input. Trate-os verificando o bloco input:
function extractMessages(response: Record<string, unknown>): string[] {
const messages: string[] = [];
const msgs = (response.messages as Array<Record<string, unknown>>) ?? [];
for (const msg of msgs) {
if (msg.type === "text" && (msg.content as Record<string, unknown>)?.richText) {
const richText = (msg.content as Record<string, unknown>).richText as Array<{ children: Array<{ text?: string }> }>;
const text = richText
.map((block) => block.children.map((c) => c.text ?? "").join(""))
.join("\n");
if (text.trim()) messages.push(text);
}
}
// If the flow expects input, prompt the user
const input = response.input as Record<string, unknown> | undefined;
if (input) {
const inputType = input.type as string;
if (inputType === "choice input") {
const items = (input.items as Array<{ content: string }>) ?? [];
const options = items.map((item, i) => `${i + 1}. ${item.content}`).join("\n");
messages.push(options);
}
}
return messages;
}
Resetando Conversas
Adicione um comando !reset para reiniciar o fluxo do Typebot:
client.on("message", async ({ info, message }) => {
if (info.isFromMe || info.isGroup) return;
const text = /* ... extract text ... */;
if (!text) return;
if (text.toLowerCase() === "!reset") {
sessions.delete(info.sender);
await client.sendMessage(info.chat, {
conversation: "Conversation reset! Send a message to start over.",
});
return;
}
// ... handle message as before
});
Evolution API vs whatsmeow-node
Se você está usando a Evolution API com Typebot atualmente, veja o que muda:
| Evolution API | ponte whatsmeow-node | |
|---|---|---|
| Backend WhatsApp | Baileys | whatsmeow (Go) |
| Memória | ~50-100 MB | ~10-20 MB |
| Arquitetura | Serviço separado | Embutido na ponte |
| Setup | Docker + config | npm install + 50 linhas |
| Estabilidade | Atualizações de fork do Baileys | Upstream estável |
Erros Comuns
Sessões do Typebot podem expirar. Se o continueChat retornar um erro, delete a sessão do map e inicie uma nova.
Fluxos do Typebot podem enviar múltiplas mensagens em sequência. Adicione um pequeno delay (1 segundo) entre as mensagens para evitar rate limiting do WhatsApp.
O Typebot suporta imagens, vídeos e botões nos fluxos. A ponte básica acima só trata texto. Expanda extractMessages() para tratar blocos de mídia e enviá-los via uploadMedia() + sendRawMessage().







