Skip to main content
Glama

Uazapi WhatsApp MCP Server

by pabloweyne
workflow-vcp-forward.json10.5 kB
{ "name": "VCP Forward - Reencaminhamento Automatico", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "vcp-forward", "responseMode": "responseNode", "options": {} }, "id": "webhook", "name": "Webhook Forward", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [200, 400], "webhookId": "vcp-forward-id" }, { "parameters": { "jsCode": "const body = $input.first().json.body || $input.first().json;\n\n// Extrair dados\nconst remoteJid = body.key?.remoteJid || '';\nconst isGroup = remoteJid.includes('@g.us');\nconst groupName = body.groupMetadata?.subject || '';\nconst pushName = body.pushName || 'Usuario';\n\n// Extrair mensagem\nlet message = '';\nif (body.message?.conversation) message = body.message.conversation;\nelse if (body.message?.extendedTextMessage?.text) message = body.message.extendedTextMessage.text;\nelse if (body.message?.imageMessage?.caption) message = body.message.imageMessage.caption;\n\n// Detectar imagem\nlet hasImage = false;\nlet imageMessage = null;\nif (body.message?.imageMessage) {\n hasImage = true;\n imageMessage = body.message.imageMessage;\n}\n\n// Criar hash da mensagem para deduplicacao\nconst msgHash = require('crypto').createHash('md5').update(message + groupName).digest('hex').substring(0, 16);\n\nreturn {\n json: {\n remoteJid,\n isGroup,\n groupName,\n pushName,\n message,\n hasImage,\n imageMessage,\n msgHash,\n timestamp: new Date().toISOString(),\n raw: body\n }\n};" }, "id": "extrair", "name": "Extrair Dados", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [420, 400] }, { "parameters": { "conditions": { "options": { "leftValue": "", "typeValidation": "loose" }, "conditions": [ { "leftValue": "={{ $json.isGroup }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } }, { "leftValue": "={{ $json.message }}", "rightValue": "", "operator": { "type": "string", "operation": "notEmpty" } } ], "combinator": "and" } }, "id": "is-group", "name": "E Grupo com Msg?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [640, 400] }, { "parameters": { "operation": "get", "key": "uazapi:vcp:forward:config" }, "id": "redis-config", "name": "Config Forward", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [860, 340], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "operation": "get", "key": "uazapi:vcp:forward:sent" }, "id": "redis-sent", "name": "Mensagens Enviadas", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [860, 460], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "jsCode": "const extract = $('Extrair Dados').first().json;\nconst configRaw = $('Config Forward').first().json?.value || '{}';\nconst sentRaw = $('Mensagens Enviadas').first().json?.value || '{}';\n\nlet config = {\n enabled: false,\n routes: [],\n keywords: [],\n blacklist_words: [],\n min_delay_seconds: 5,\n max_delay_seconds: 15,\n daily_limit_per_route: 50,\n dedupe_hours: 24\n};\n\nlet sent = {\n hashes: {},\n counts: {},\n last_sent: {}\n};\n\ntry { config = { ...config, ...JSON.parse(configRaw) }; } catch(e) {}\ntry { sent = { ...sent, ...JSON.parse(sentRaw) }; } catch(e) {}\n\n// Verificar se esta habilitado\nif (!config.enabled) return [];\n\n// Encontrar rota para este grupo de origem\nconst route = config.routes.find(r => \n r.enabled && \n r.source_groups.some(g => \n extract.groupName.toLowerCase().includes(g.toLowerCase())\n )\n);\n\nif (!route) return [];\n\n// Verificar blacklist words\nconst msgLower = extract.message.toLowerCase();\nif (config.blacklist_words.some(w => msgLower.includes(w.toLowerCase()))) {\n return [];\n}\n\n// Verificar keywords (se configurado)\nif (route.keywords?.length > 0) {\n const hasKeyword = route.keywords.some(k => msgLower.includes(k.toLowerCase()));\n if (!hasKeyword) return [];\n}\n\n// Verificar deduplicacao\nconst now = Date.now();\nconst dedupeMs = config.dedupe_hours * 60 * 60 * 1000;\nif (sent.hashes[extract.msgHash] && (now - sent.hashes[extract.msgHash]) < dedupeMs) {\n return []; // Mensagem duplicada\n}\n\n// Verificar limite diario por rota\nconst today = new Date().toISOString().split('T')[0];\nconst routeCountKey = `${route.id}_${today}`;\nconst currentCount = sent.counts[routeCountKey] || 0;\n\nif (currentCount >= config.daily_limit_per_route) {\n return []; // Limite diario atingido\n}\n\n// Verificar delay minimo desde ultima mensagem\nconst lastSentTime = sent.last_sent[route.id] || 0;\nconst minDelayMs = config.min_delay_seconds * 1000;\nif ((now - lastSentTime) < minDelayMs) {\n return []; // Muito rapido, ignorar\n}\n\n// Preparar dados para envio\nconst randomDelay = Math.floor(Math.random() * (config.max_delay_seconds - config.min_delay_seconds + 1)) + config.min_delay_seconds;\n\nreturn {\n json: {\n extract,\n route,\n config,\n sent,\n randomDelay,\n today,\n routeCountKey,\n currentCount,\n shouldForward: true\n }\n};" }, "id": "verificar", "name": "Verificar Regras", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1080, 400] }, { "parameters": { "time": "={{ $json.randomDelay }}", "unit": "seconds" }, "id": "wait", "name": "Delay Anti-Spam", "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [1300, 400] }, { "parameters": { "jsCode": "const data = $('Verificar Regras').first().json;\nconst route = data.route;\nconst extract = data.extract;\n\n// Formatar mensagem\nlet forwardMsg = '';\n\nif (route.format === 'with_source') {\n forwardMsg = `📢 De: ${extract.groupName}\\n\\n${extract.message}`;\n} else if (route.format === 'with_credit') {\n forwardMsg = `${extract.message}\\n\\n— Via ${extract.groupName}`;\n} else {\n forwardMsg = extract.message;\n}\n\n// Criar lista de destinos\nconst destinations = route.destination_groups.map(dest => ({\n phone: dest.id,\n name: dest.name,\n message: forwardMsg\n}));\n\nreturn destinations.map(d => ({ json: d }));" }, "id": "preparar-envio", "name": "Preparar Destinos", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1520, 400] }, { "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": "={ \"phone\": \"{{ $json.phone }}\", \"message\": \"{{ $json.message }}\" }" }, "id": "enviar", "name": "Enviar UAZAPI", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1740, 400], "credentials": { "httpHeaderAuth": { "id": "UAZAPI_CREDENTIAL_ID", "name": "UAZAPI Auth" } } }, { "parameters": { "jsCode": "const data = $('Verificar Regras').first().json;\nconst sent = data.sent;\nconst extract = data.extract;\nconst route = data.route;\nconst now = Date.now();\n\n// Atualizar hash para deduplicacao\nsent.hashes[extract.msgHash] = now;\n\n// Limpar hashes antigos (mais de 48h)\nconst maxAge = 48 * 60 * 60 * 1000;\nfor (const [hash, time] of Object.entries(sent.hashes)) {\n if ((now - time) > maxAge) delete sent.hashes[hash];\n}\n\n// Atualizar contador diario\nconst today = new Date().toISOString().split('T')[0];\nconst routeCountKey = `${route.id}_${today}`;\nsent.counts[routeCountKey] = (sent.counts[routeCountKey] || 0) + route.destination_groups.length;\n\n// Limpar contadores antigos\nfor (const key of Object.keys(sent.counts)) {\n if (!key.includes(today)) delete sent.counts[key];\n}\n\n// Atualizar last_sent\nsent.last_sent[route.id] = now;\n\nreturn { json: { sent: JSON.stringify(sent) } };" }, "id": "atualizar-sent", "name": "Atualizar Contadores", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1960, 400] }, { "parameters": { "operation": "set", "key": "uazapi:vcp:forward:sent", "value": "={{ $json.sent }}", "expire": true, "ttl": 172800 }, "id": "save-sent", "name": "Salvar Contadores", "type": "n8n-nodes-base.redis", "typeVersion": 1, "position": [2180, 400], "credentials": { "redis": { "id": "REDIS_CREDENTIAL_ID", "name": "Redis" } } }, { "parameters": { "respondWith": "text", "responseBody": "OK" }, "id": "webhook-response", "name": "Responder Webhook", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [2400, 400] } ], "connections": { "Webhook Forward": { "main": [[{ "node": "Extrair Dados", "type": "main", "index": 0 }]] }, "Extrair Dados": { "main": [[{ "node": "E Grupo com Msg?", "type": "main", "index": 0 }]] }, "E Grupo com Msg?": { "main": [[{ "node": "Config Forward", "type": "main", "index": 0 }, { "node": "Mensagens Enviadas", "type": "main", "index": 0 }], [{ "node": "Responder Webhook", "type": "main", "index": 0 }]] }, "Config Forward": { "main": [[{ "node": "Verificar Regras", "type": "main", "index": 0 }]] }, "Mensagens Enviadas": { "main": [[{ "node": "Verificar Regras", "type": "main", "index": 0 }]] }, "Verificar Regras": { "main": [[{ "node": "Delay Anti-Spam", "type": "main", "index": 0 }]] }, "Delay Anti-Spam": { "main": [[{ "node": "Preparar Destinos", "type": "main", "index": 0 }]] }, "Preparar Destinos": { "main": [[{ "node": "Enviar UAZAPI", "type": "main", "index": 0 }]] }, "Enviar UAZAPI": { "main": [[{ "node": "Atualizar Contadores", "type": "main", "index": 0 }]] }, "Atualizar Contadores": { "main": [[{ "node": "Salvar Contadores", "type": "main", "index": 0 }]] }, "Salvar Contadores": { "main": [[{ "node": "Responder Webhook", "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