Skip to main content
Glama

Uazapi WhatsApp MCP Server

by pabloweyne
workflow-chatbot-v3-grupos.json22.6 kB
{ "name": "UAZAPI WhatsApp Chatbot v3 - Com Filtro de Grupos", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "uazapi-webhook", "responseMode": "responseNode", "options": {} }, "id": "webhook-trigger", "name": "Webhook UAZAPI", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [240, 300] }, { "parameters": { "jsCode": "// Extrair dados e detectar tipo de chat\nconst body = $input.first().json.body || $input.first().json;\n\n// Identificar remetente e chat\nconst remoteJid = body.key?.remoteJid || body.remoteJid || '';\nconst participant = body.key?.participant || body.participant || '';\nconst pushName = body.pushName || '';\n\n// Detectar se é grupo\nconst isGroup = remoteJid.includes('@g.us');\n\n// Número do remetente real\nconst senderPhone = isGroup ? participant.split('@')[0] : remoteJid.split('@')[0];\n\n// Mensagem\nconst message = body.message?.conversation || \n body.message?.extendedTextMessage?.text || \n body.message?.imageMessage?.caption ||\n '';\n\n// Detectar se foi mencionado (@)\nconst mentionedJids = body.message?.extendedTextMessage?.contextInfo?.mentionedJid || [];\nconst isMentioned = message.includes('@') || mentionedJids.length > 0;\n\n// Detectar menção ao bot (precisa configurar o número do bot)\nconst botNumber = $env.BOT_PHONE_NUMBER || 'SEU_NUMERO_BOT';\nconst isBotMentioned = mentionedJids.some(jid => jid.includes(botNumber)) || \n message.toLowerCase().includes('@bot') ||\n message.toLowerCase().includes('@assistente');\n\nreturn {\n json: {\n // Dados originais\n rawBody: body,\n \n // Dados processados\n chatId: remoteJid,\n senderPhone: senderPhone,\n senderName: pushName,\n message: message,\n messageId: body.key?.id || '',\n timestamp: Date.now(),\n \n // Flags de tipo\n isGroup: isGroup,\n isMentioned: isMentioned,\n isBotMentioned: isBotMentioned,\n \n // Para resposta\n replyTo: isGroup ? remoteJid : senderPhone,\n \n // Redis keys\n redisKeyUser: `uazapi:chat:${senderPhone}`,\n redisKeyConfig: 'uazapi:config:grupos'\n }\n};" }, "id": "extract-data", "name": "Extrair e Classificar Mensagem", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [460, 300] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "phone-check", "leftValue": "={{ $json.senderPhone }}", "rightValue": "SEU_NUMERO_AQUI", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "filter-authorized", "name": "Verificar Numero Autorizado", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [680, 300] }, { "parameters": { "operation": "get", "key": "={{ $json.redisKeyConfig }}" }, "id": "redis-get-config", "name": "Redis Buscar Config Grupos", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [900, 240], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "jsCode": "// Verificar se deve processar a mensagem\nconst msgData = $('Extrair e Classificar Mensagem').first().json;\nlet config = {\n grupos_habilitados: false,\n grupos_whitelist: [],\n responder_apenas_mencao: true\n};\n\n// Carregar config do Redis\ntry {\n const redisData = $input.first().json;\n if (redisData && typeof redisData === 'string') {\n config = { ...config, ...JSON.parse(redisData) };\n } else if (redisData && redisData.value) {\n config = { ...config, ...JSON.parse(redisData.value) };\n }\n} catch (e) {\n // Config padrão\n}\n\nlet shouldProcess = true;\nlet skipReason = '';\n\n// Se for grupo\nif (msgData.isGroup) {\n // Grupos desabilitados = ignora tudo\n if (!config.grupos_habilitados) {\n shouldProcess = false;\n skipReason = 'grupos_desabilitados';\n }\n // Grupos habilitados mas só com menção\n else if (config.responder_apenas_mencao && !msgData.isBotMentioned) {\n shouldProcess = false;\n skipReason = 'sem_mencao';\n }\n // Verificar whitelist (se não estiver vazia)\n else if (config.grupos_whitelist.length > 0) {\n const isWhitelisted = config.grupos_whitelist.some(g => \n msgData.chatId.includes(g.id) || g.nome?.toLowerCase() === msgData.chatId.toLowerCase()\n );\n if (!isWhitelisted) {\n shouldProcess = false;\n skipReason = 'grupo_nao_whitelist';\n }\n }\n}\n\n// Ignorar mensagens vazias\nif (!msgData.message || msgData.message.trim() === '') {\n shouldProcess = false;\n skipReason = 'mensagem_vazia';\n}\n\nreturn {\n json: {\n ...msgData,\n config: config,\n shouldProcess: shouldProcess,\n skipReason: skipReason\n }\n};" }, "id": "check-group-filter", "name": "Verificar Filtro de Grupos", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1120, 240] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "should-process", "leftValue": "={{ $json.shouldProcess }}", "rightValue": "={{true}}", "operator": { "type": "boolean", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "filter-should-process", "name": "Deve Processar?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [1340, 240] }, { "parameters": { "operation": "get", "key": "={{ $json.redisKeyUser }}" }, "id": "redis-get-context", "name": "Redis Buscar Contexto Usuario", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [1560, 180], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "jsCode": "// Preparar contexto para o GPT\nconst msgData = $('Verificar Filtro de Grupos').first().json;\nlet context = {\n historico: [],\n grupos_frequentes: [],\n contatos_frequentes: [],\n ultima_acao: null,\n preferencias: {}\n};\n\ntry {\n const redisData = $input.first().json;\n if (redisData && typeof redisData === 'string') {\n context = JSON.parse(redisData);\n } else if (redisData && redisData.value) {\n context = JSON.parse(redisData.value);\n }\n} catch (e) {}\n\n// Limitar histórico\nif (context.historico && context.historico.length > 10) {\n context.historico = context.historico.slice(-10);\n}\n\n// Montar prompt de contexto\nlet contextPrompt = '';\n\nif (context.historico && context.historico.length > 0) {\n contextPrompt += '\\n\\n## HISTÓRICO RECENTE\\n';\n context.historico.forEach(msg => {\n contextPrompt += `${msg.role === 'user' ? 'Usuário' : 'Assistente'}: ${msg.content}\\n`;\n });\n}\n\nif (context.grupos_frequentes && context.grupos_frequentes.length > 0) {\n contextPrompt += '\\n\\n## GRUPOS DO USUÁRIO\\n';\n context.grupos_frequentes.forEach(g => {\n contextPrompt += `- ${g.nome} (ID: ${g.id})\\n`;\n });\n}\n\nif (context.contatos_frequentes && context.contatos_frequentes.length > 0) {\n contextPrompt += '\\n\\n## CONTATOS FREQUENTES\\n';\n context.contatos_frequentes.forEach(c => {\n contextPrompt += `- ${c.nome}: ${c.numero}\\n`;\n });\n}\n\n// Info do chat atual\nlet chatInfo = '';\nif (msgData.isGroup) {\n chatInfo = `\\n\\n## CONTEXTO ATUAL\\nMensagem recebida em GRUPO: ${msgData.chatId}\\nRemetente: ${msgData.senderName} (${msgData.senderPhone})`;\n} else {\n chatInfo = `\\n\\n## CONTEXTO ATUAL\\nMensagem recebida em chat PRIVADO`;\n}\n\n// Config de grupos atual\nlet configInfo = `\\n\\n## CONFIGURAÇÃO ATUAL DE GRUPOS\\n- Grupos habilitados: ${msgData.config.grupos_habilitados ? 'SIM' : 'NÃO'}\\n- Responder apenas com @: ${msgData.config.responder_apenas_mencao ? 'SIM' : 'NÃO'}\\n- Whitelist: ${msgData.config.grupos_whitelist.length} grupos`;\n\nreturn {\n json: {\n ...msgData,\n context: context,\n contextPrompt: contextPrompt + chatInfo + configInfo\n }\n};" }, "id": "prepare-context", "name": "Preparar Contexto", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1780, 180] }, { "parameters": { "model": "gpt-4o", "messages": { "values": [ { "role": "system", "content": "=Você é um assistente de gerenciamento do WhatsApp via UAZAPI. Você tem MEMÓRIA e pode responder em grupos quando mencionado.\n\n## AÇÕES DISPONÍVEIS\n\n### Mensagens\n- enviar_texto: Enviar mensagem de texto\n- enviar_imagem: Enviar imagem com URL\n- enviar_documento: Enviar documento/arquivo\n\n### Grupos\n- criar_grupo: Criar novo grupo\n- adicionar_participante: Adicionar pessoa a grupo\n- remover_participante: Remover pessoa de grupo\n- listar_grupos: Listar todos os grupos\n\n### Contatos\n- listar_contatos: Listar contatos salvos\n- verificar_numero: Verificar se número tem WhatsApp\n\n### Configuração de Grupos (IMPORTANTE!)\n- habilitar_grupos: Ativa respostas em grupos\n- desabilitar_grupos: Desativa respostas em grupos\n- adicionar_grupo_whitelist: Adiciona grupo à lista de permitidos\n- remover_grupo_whitelist: Remove grupo da whitelist\n- listar_whitelist: Mostra grupos na whitelist\n- config_mencao: Ativa/desativa responder apenas com @\n\n{{ $json.contextPrompt }}\n\n## FORMATO DE RESPOSTA\n\n```json\n{\n \"acao\": \"nome_da_acao\",\n \"parametros\": { ... },\n \"mensagem_usuario\": \"Resposta amigável\",\n \"config_update\": {\n \"grupos_habilitados\": true/false,\n \"responder_apenas_mencao\": true/false,\n \"adicionar_whitelist\": { \"nome\": \"...\", \"id\": \"...\" },\n \"remover_whitelist\": \"id_do_grupo\"\n },\n \"memoria\": { ... }\n}\n```\n\n## COMANDOS DE CONFIG (exemplos que usuário pode pedir)\n- \"habilitar grupos\" → config_update.grupos_habilitados = true\n- \"desabilitar grupos\" → config_update.grupos_habilitados = false\n- \"responder só com @\" → config_update.responder_apenas_mencao = true\n- \"adicionar este grupo\" → adicionar_whitelist com ID atual\n- \"remover este grupo\" → remover_whitelist\n\n## REGRAS\n- Se estiver em grupo, a resposta vai para o GRUPO\n- Se em chat privado, vai para o usuário\n- Números com DDI (ex: 5511999999999)\n- Seja conciso em grupos" }, { "role": "user", "content": "={{ $json.message }}" } ] }, "options": { "temperature": 0.3, "maxTokens": 1500 } }, "id": "openai-process", "name": "OpenAI Processar", "type": "@n8n/n8n-nodes-langchain.openAi", "typeVersion": 1.6, "position": [2000, 180], "credentials": { "openAiApi": { "id": "OPENAI_CREDENTIAL_ID", "name": "OpenAI API" } } }, { "parameters": { "jsCode": "// Parse resposta e atualizar contexto/config\nconst gptResponse = $input.first().json.message.content;\nconst prevData = $('Preparar Contexto').first().json;\nlet parsed;\n\ntry {\n const jsonMatch = gptResponse.match(/```json\\s*([\\s\\S]*?)```/) || gptResponse.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const jsonStr = jsonMatch[1] || jsonMatch[0];\n parsed = JSON.parse(jsonStr);\n } else {\n parsed = { acao: 'responder', parametros: {}, mensagem_usuario: gptResponse };\n }\n} catch (e) {\n parsed = { acao: 'responder', parametros: {}, mensagem_usuario: gptResponse };\n}\n\n// Atualizar contexto do usuário\nlet context = prevData.context || { historico: [], grupos_frequentes: [], contatos_frequentes: [], ultima_acao: null };\n\ncontext.historico.push({ role: 'user', content: prevData.message, timestamp: prevData.timestamp });\ncontext.historico.push({ role: 'assistant', content: parsed.mensagem_usuario, timestamp: Date.now() });\n\nif (context.historico.length > 20) {\n context.historico = context.historico.slice(-20);\n}\n\n// Atualizar config de grupos se necessário\nlet config = prevData.config;\nif (parsed.config_update) {\n if (typeof parsed.config_update.grupos_habilitados === 'boolean') {\n config.grupos_habilitados = parsed.config_update.grupos_habilitados;\n }\n if (typeof parsed.config_update.responder_apenas_mencao === 'boolean') {\n config.responder_apenas_mencao = parsed.config_update.responder_apenas_mencao;\n }\n if (parsed.config_update.adicionar_whitelist) {\n const novoGrupo = parsed.config_update.adicionar_whitelist;\n // Se não tem ID, usar o chat atual se for grupo\n if (!novoGrupo.id && prevData.isGroup) {\n novoGrupo.id = prevData.chatId;\n novoGrupo.nome = novoGrupo.nome || prevData.chatId;\n }\n if (novoGrupo.id && !config.grupos_whitelist.find(g => g.id === novoGrupo.id)) {\n config.grupos_whitelist.push(novoGrupo);\n }\n }\n if (parsed.config_update.remover_whitelist) {\n const idRemover = parsed.config_update.remover_whitelist === 'atual' ? prevData.chatId : parsed.config_update.remover_whitelist;\n config.grupos_whitelist = config.grupos_whitelist.filter(g => g.id !== idRemover);\n }\n}\n\nreturn {\n json: {\n ...parsed,\n replyTo: prevData.replyTo,\n isGroup: prevData.isGroup,\n senderPhone: prevData.senderPhone,\n redisKeyUser: prevData.redisKeyUser,\n redisKeyConfig: prevData.redisKeyConfig,\n contextToSave: JSON.stringify(context),\n configToSave: JSON.stringify(config),\n configChanged: !!parsed.config_update\n }\n};" }, "id": "parse-response", "name": "Parse e Atualizar", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [2220, 180] }, { "parameters": { "operation": "set", "key": "={{ $json.redisKeyUser }}", "value": "={{ $json.contextToSave }}", "expire": true, "ttl": 604800 }, "id": "redis-save-user", "name": "Redis Salvar Contexto", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [2440, 120], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "operation": "set", "key": "={{ $json.redisKeyConfig }}", "value": "={{ $json.configToSave }}", "expire": false }, "id": "redis-save-config", "name": "Redis Salvar Config", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [2440, 240], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": false }, "conditions": [{ "leftValue": "={{ $json.acao }}", "rightValue": "enviar_texto", "operator": { "type": "string", "operation": "equals" } }] }, "renameOutput": true, "outputLabel": "Enviar Texto" }, { "conditions": { "options": { "caseSensitive": false }, "conditions": [{ "leftValue": "={{ $json.acao }}", "rightValue": "criar_grupo", "operator": { "type": "string", "operation": "equals" } }] }, "renameOutput": true, "outputLabel": "Criar Grupo" }, { "conditions": { "options": { "caseSensitive": false }, "conditions": [{ "leftValue": "={{ $json.acao }}", "rightValue": "listar_grupos", "operator": { "type": "string", "operation": "equals" } }] }, "renameOutput": true, "outputLabel": "Listar Grupos" } ], "fallbackOutput": { "renameOutput": true, "outputLabel": "Resposta Simples" } }, "options": {} }, "id": "router-action", "name": "Router de Ações", "type": "n8n-nodes-base.switch", "typeVersion": 3.2, "position": [2660, 180] }, { "parameters": { "method": "POST", "url": "=https://api.uazapi.com/instances/{{ $env.UAZAPI_INSTANCE_ID }}/messages/send/text", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"phone\": \"{{ $json.parametros.numero }}\",\n \"message\": \"{{ $json.parametros.mensagem }}\"\n}", "options": {} }, "id": "uazapi-send-text", "name": "UAZAPI Enviar Texto", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2900, 60], "credentials": { "httpHeaderAuth": { "id": "UAZAPI_CREDENTIAL_ID", "name": "UAZAPI Auth" } } }, { "parameters": { "method": "POST", "url": "=https://api.uazapi.com/instances/{{ $env.UAZAPI_INSTANCE_ID }}/groups/create", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"name\": \"{{ $json.parametros.nome }}\",\n \"participants\": {{ JSON.stringify($json.parametros.participantes || []) }}\n}", "options": {} }, "id": "uazapi-create-group", "name": "UAZAPI Criar Grupo", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2900, 180], "credentials": { "httpHeaderAuth": { "id": "UAZAPI_CREDENTIAL_ID", "name": "UAZAPI Auth" } } }, { "parameters": { "method": "GET", "url": "=https://api.uazapi.com/instances/{{ $env.UAZAPI_INSTANCE_ID }}/groups", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "options": {} }, "id": "uazapi-list-groups", "name": "UAZAPI Listar Grupos", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [2900, 300], "credentials": { "httpHeaderAuth": { "id": "UAZAPI_CREDENTIAL_ID", "name": "UAZAPI Auth" } } }, { "parameters": { "method": "POST", "url": "=https://api.uazapi.com/instances/{{ $env.UAZAPI_INSTANCE_ID }}/messages/send/text", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"phone\": \"{{ $('Parse e Atualizar').first().json.replyTo }}\",\n \"message\": \"{{ $('Parse e Atualizar').first().json.mensagem_usuario }}\"\n}", "options": {} }, "id": "send-response", "name": "Enviar Resposta", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [3120, 180], "credentials": { "httpHeaderAuth": { "id": "UAZAPI_CREDENTIAL_ID", "name": "UAZAPI Auth" } } }, { "parameters": { "respondWith": "json", "responseBody": "={\"status\": \"ok\"}", "options": {} }, "id": "respond-webhook-ok", "name": "Responder OK", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [3340, 180] }, { "parameters": { "respondWith": "json", "responseBody": "={\"status\": \"skipped\", \"reason\": \"{{ $json.skipReason }}\"}", "options": {} }, "id": "respond-skipped", "name": "Responder Ignorado", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [1560, 340] }, { "parameters": { "respondWith": "json", "responseBody": "={\"status\": \"unauthorized\"}", "options": { "responseCode": 403 } }, "id": "respond-unauthorized", "name": "Nao Autorizado", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [900, 400] } ], "connections": { "Webhook UAZAPI": { "main": [[{ "node": "Extrair e Classificar Mensagem", "type": "main", "index": 0 }]] }, "Extrair e Classificar Mensagem": { "main": [[{ "node": "Verificar Numero Autorizado", "type": "main", "index": 0 }]] }, "Verificar Numero Autorizado": { "main": [ [{ "node": "Redis Buscar Config Grupos", "type": "main", "index": 0 }], [{ "node": "Nao Autorizado", "type": "main", "index": 0 }] ] }, "Redis Buscar Config Grupos": { "main": [[{ "node": "Verificar Filtro de Grupos", "type": "main", "index": 0 }]] }, "Verificar Filtro de Grupos": { "main": [[{ "node": "Deve Processar?", "type": "main", "index": 0 }]] }, "Deve Processar?": { "main": [ [{ "node": "Redis Buscar Contexto Usuario", "type": "main", "index": 0 }], [{ "node": "Responder Ignorado", "type": "main", "index": 0 }] ] }, "Redis Buscar Contexto Usuario": { "main": [[{ "node": "Preparar Contexto", "type": "main", "index": 0 }]] }, "Preparar Contexto": { "main": [[{ "node": "OpenAI Processar", "type": "main", "index": 0 }]] }, "OpenAI Processar": { "main": [[{ "node": "Parse e Atualizar", "type": "main", "index": 0 }]] }, "Parse e Atualizar": { "main": [[{ "node": "Redis Salvar Contexto", "type": "main", "index": 0 }, { "node": "Redis Salvar Config", "type": "main", "index": 0 }]] }, "Redis Salvar Contexto": { "main": [[{ "node": "Router de Ações", "type": "main", "index": 0 }]] }, "Redis Salvar Config": { "main": [[]] }, "Router de Ações": { "main": [ [{ "node": "UAZAPI Enviar Texto", "type": "main", "index": 0 }], [{ "node": "UAZAPI Criar Grupo", "type": "main", "index": 0 }], [{ "node": "UAZAPI Listar Grupos", "type": "main", "index": 0 }], [{ "node": "Enviar Resposta", "type": "main", "index": 0 }] ] }, "UAZAPI Enviar Texto": { "main": [[{ "node": "Enviar Resposta", "type": "main", "index": 0 }]] }, "UAZAPI Criar Grupo": { "main": [[{ "node": "Enviar Resposta", "type": "main", "index": 0 }]] }, "UAZAPI Listar Grupos": { "main": [[{ "node": "Enviar Resposta", "type": "main", "index": 0 }]] }, "Enviar Resposta": { "main": [[{ "node": "Responder OK", "type": "main", "index": 0 }]] } }, "settings": { "executionOrder": "v1" } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pabloweyne/uazapi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server