WhatsApp Bot Python: Guia Completo 2025

📚 Série: Automação WhatsApp → Automação WhatsApp | Evolution API | Chatbot WhatsApp | API WhatsApp

O que é WhatsApp Bot Python?

WhatsApp bot Python é aplicação web (Flask/FastAPI) que integra Evolution API ou Twilio para receber mensagens WhatsApp via webhook, processar com lógica customizada (ChatGPT, banco dados, APIs externas) e responder automaticamente. Bot Python permite: atendimento 24/7, qualificação leads, consulta estoque, geração boletos, agendamento consultas, integração CRM/ERP = tudo programado sob medida vs plataformas no-code limitadas. Desenvolvimento completo (webhook + lógica + deploy) leva 4-8h vs 4 semanas com linguagens compiladas.

Diferença: Python permite IA avançada (ML models), integrações ilimitadas (qualquer API) e lógica negócio complexa impossível em plataformas drag-and-drop.


Arquitetura Bot Python WhatsApp

Stack Completo

WhatsApp → Evolution API → Webhook → Flask Server → Lógica Python → Response

                                    PostgreSQL
                                    ChatGPT API
                                    CRM APIs
                                    Business Logic

Componentes:

  1. Evolution API (WhatsApp gateway)

    • Gerencia conexões WhatsApp
    • Envia/recebe mensagens
    • Webhook notificações
  2. Flask Server (aplicação Python)

    • Recebe webhooks
    • Processa mensagens
    • Envia respostas
  3. PostgreSQL (banco dados)

    • Histórico conversas
    • Estado conversação
    • Clientes/produtos
  4. ChatGPT API (IA conversacional)

    • Entender intenção
    • Gerar respostas naturais
    • Qualificar leads
  5. APIs Externas (integrações)

    • CRM (Pipedrive)
    • Pagamentos (Stripe)
    • Email (SendGrid)

Tutorial: Bot WhatsApp Python (Código Completo)

Passo 1: Setup Ambiente

# Criar projeto
mkdir whatsapp-bot-python
cd whatsapp-bot-python

# Virtual environment
python3 -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate  # Windows

# Instalar dependências
pip install flask requests python-dotenv openai psycopg2-binary

requirements.txt:

flask==3.0.0
requests==2.31.0
python-dotenv==1.0.0
openai==1.6.1
psycopg2-binary==2.9.9
gunicorn==21.2.0

Passo 2: Estrutura Projeto

whatsapp-bot/
├── .env
├── app.py (servidor Flask)
├── config.py (configurações)
├── database.py (conexão PostgreSQL)
├── whatsapp.py (funções Evolution API)
├── chatgpt.py (integração OpenAI)
├── handlers.py (lógica atendimento)
└── requirements.txt

Passo 3: Configurações (.env)

# Evolution API
EVOLUTION_API_URL=https://evolution.seudominio.com
EVOLUTION_API_KEY=sua-chave-secreta-aqui
EVOLUTION_INSTANCE=cliente-atendimento

# OpenAI
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx

# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/whatsapp_bot

# Flask
FLASK_SECRET_KEY=sua-chave-secreta-flask
FLASK_PORT=5000

Passo 4: Código Bot (app.py)

from flask import Flask, request, jsonify
from dotenv import load_dotenv
import os
import logging

from handlers import processar_mensagem
from database import init_database

load_dotenv()

app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET_KEY')

# Configurar logs
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Inicializar banco
init_database()

@app.route('/webhook/whatsapp', methods=['POST'])
def webhook_whatsapp():
    """
    Recebe mensagens WhatsApp da Evolution API
    """
    try:
        data = request.get_json()

        # Validar estrutura
        if not data or 'data' not in data:
            return jsonify({'error': 'Invalid payload'}), 400

        # Extrair dados mensagem
        event = data.get('event')
        message_data = data.get('data', {})

        # Filtrar apenas mensagens recebidas (não enviadas)
        if event != 'messages.upsert':
            return jsonify({'status': 'ignored'}), 200

        # Ignorar mensagens do próprio bot
        if message_data.get('key', {}).get('fromMe'):
            return jsonify({'status': 'own message'}), 200

        # Processar mensagem
        processar_mensagem(message_data)

        return jsonify({'status': 'success'}), 200

    except Exception as e:
        logger.error(f'Erro webhook: {str(e)}')
        return jsonify({'error': str(e)}), 500

@app.route('/health', methods=['GET'])
def health_check():
    """
    Health check endpoint
    """
    return jsonify({
        'status': 'healthy',
        'service': 'WhatsApp Bot Python'
    }), 200

if __name__ == '__main__':
    port = int(os.getenv('FLASK_PORT', 5000))
    app.run(host='0.0.0.0', port=port, debug=False)

Passo 5: Integração Evolution API (whatsapp.py)

import requests
import os
import logging

logger = logging.getLogger(__name__)

EVOLUTION_URL = os.getenv('EVOLUTION_API_URL')
API_KEY = os.getenv('EVOLUTION_API_KEY')
INSTANCE = os.getenv('EVOLUTION_INSTANCE')

def enviar_mensagem_texto(numero, texto):
    """
    Envia mensagem texto para WhatsApp

    Args:
        numero: Número formato 5511999887766
        texto: Mensagem texto

    Returns:
        bool: True se enviou com sucesso
    """
    try:
        url = f'{EVOLUTION_URL}/message/sendText/{INSTANCE}'

        headers = {
            'Content-Type': 'application/json',
            'apikey': API_KEY
        }

        payload = {
            'number': numero,
            'textMessage': {
                'text': texto
            }
        }

        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()

        logger.info(f'Mensagem enviada para {numero}')
        return True

    except Exception as e:
        logger.error(f'Erro enviar mensagem: {str(e)}')
        return False

def enviar_mensagem_imagem(numero, url_imagem, caption=''):
    """
    Envia imagem WhatsApp
    """
    try:
        url = f'{EVOLUTION_URL}/message/sendMedia/{INSTANCE}'

        headers = {
            'Content-Type': 'application/json',
            'apikey': API_KEY
        }

        payload = {
            'number': numero,
            'mediaMessage': {
                'mediatype': 'image',
                'media': url_imagem,
                'caption': caption
            }
        }

        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()

        return True

    except Exception as e:
        logger.error(f'Erro enviar imagem: {str(e)}')
        return False

def buscar_contato(numero):
    """
    Busca informações contato WhatsApp
    """
    try:
        url = f'{EVOLUTION_URL}/chat/findContact/{INSTANCE}'

        headers = {'apikey': API_KEY}

        params = {'number': numero}

        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()

        data = response.json()
        return data.get('name', 'Cliente')

    except Exception as e:
        logger.error(f'Erro buscar contato: {str(e)}')
        return 'Cliente'

Passo 6: Integração ChatGPT (chatgpt.py)

from openai import OpenAI
import os
import logging

logger = logging.getLogger(__name__)

client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

SYSTEM_PROMPT = """
Você é assistente virtual WhatsApp da empresa TechStore.

Informações importantes:
- Horário funcionamento: Seg-Sex 8h-18h, Sáb 9h-13h
- Produtos: Notebooks, celulares, acessórios
- Entrega: Todo Brasil (prazo 5-10 dias úteis)
- Pagamento: PIX, cartão crédito, boleto

Suas regras:
1. Seja breve (máximo 2-3 frases por mensagem)
2. Use emojis com moderação 😊
3. Sempre pergunte se resolveu dúvida
4. Se cliente quiser falar com vendedor, diga que vai transferir
5. NUNCA invente preços ou informações técnicas (diga "vou consultar")

Exemplos:
Cliente: "Quanto custa iPhone 15?"
Você: "Deixa eu consultar preço atualizado pra você! Um momento 😊"

Cliente: "Vocês entregam em São Paulo?"
Você: "Sim! Entregamos em todo Brasil. Prazo pra SP: 3-5 dias úteis 📦"
"""

def gerar_resposta(mensagem_usuario, historico_contexto=[]):
    """
    Gera resposta usando ChatGPT

    Args:
        mensagem_usuario: Mensagem atual cliente
        historico_contexto: Lista mensagens anteriores [{'role': 'user', 'content': '...'}]

    Returns:
        str: Resposta gerada
    """
    try:
        # Montar histórico conversa
        messages = [{'role': 'system', 'content': SYSTEM_PROMPT}]
        messages.extend(historico_contexto[-5:])  # Últimas 5 mensagens contexto
        messages.append({'role': 'user', 'content': mensagem_usuario})

        # Chamar API
        response = client.chat.completions.create(
            model='gpt-4o-mini',
            messages=messages,
            temperature=0.7,
            max_tokens=150
        )

        resposta = response.choices[0].message.content
        logger.info(f'ChatGPT respondeu: {resposta[:50]}...')

        return resposta

    except Exception as e:
        logger.error(f'Erro ChatGPT: {str(e)}')
        return "Desculpe, tive um problema técnico. Pode repetir? 😅"

def detectar_intencao(mensagem):
    """
    Detecta intenção mensagem (compra, suporte, info, etc)
    """
    try:
        response = client.chat.completions.create(
            model='gpt-4o-mini',
            messages=[
                {
                    'role': 'system',
                    'content': '''
Detecte intenção da mensagem:
- preco: Cliente quer saber preço produto
- estoque: Cliente quer saber disponibilidade
- compra: Cliente quer comprar/fechar pedido
- suporte: Cliente tem problema/reclamação
- info: Cliente quer informações gerais (horário, entrega)
- outros: Não se encaixa acima

Responda APENAS com palavra-chave (preco/estoque/compra/suporte/info/outros)
                    '''
                },
                {'role': 'user', 'content': mensagem}
            ],
            temperature=0.3,
            max_tokens=10
        )

        intencao = response.choices[0].message.content.strip().lower()
        return intencao

    except Exception as e:
        logger.error(f'Erro detectar intenção: {str(e)}')
        return 'outros'

Passo 7: Lógica Atendimento (handlers.py)

import logging
from whatsapp import enviar_mensagem_texto, buscar_contato
from chatgpt import gerar_resposta, detectar_intencao
from database import (
    salvar_mensagem,
    buscar_historico,
    criar_ou_atualizar_contato
)

logger = logging.getLogger(__name__)

def processar_mensagem(message_data):
    """
    Processa mensagem recebida WhatsApp
    """
    try:
        # Extrair dados
        numero = message_data.get('key', {}).get('remoteJid', '').replace('@s.whatsapp.net', '')
        mensagem_texto = message_data.get('message', {}).get('conversation', '')

        if not mensagem_texto:
            # Tentar extrair de outros tipos mensagem
            mensagem_texto = (
                message_data.get('message', {}).get('extendedTextMessage', {}).get('text', '') or
                message_data.get('message', {}).get('imageMessage', {}).get('caption', '')
            )

        if not numero or not mensagem_texto:
            logger.warning('Mensagem sem número ou texto')
            return

        logger.info(f'Mensagem de {numero}: {mensagem_texto}')

        # Buscar nome contato
        nome = buscar_contato(numero)

        # Salvar mensagem banco
        salvar_mensagem(numero, mensagem_texto, 'inbound')

        # Criar/atualizar contato
        criar_ou_atualizar_contato(numero, nome)

        # Detectar intenção
        intencao = detectar_intencao(mensagem_texto)
        logger.info(f'Intenção detectada: {intencao}')

        # Roteamento por intenção
        if intencao == 'preco':
            resposta = handle_preco(mensagem_texto)
        elif intencao == 'estoque':
            resposta = handle_estoque(mensagem_texto)
        elif intencao == 'compra':
            resposta = handle_compra(numero, mensagem_texto)
        elif intencao == 'suporte':
            resposta = handle_suporte(numero, mensagem_texto)
        else:
            # ChatGPT genérico
            historico = buscar_historico(numero, limit=5)
            resposta = gerar_resposta(mensagem_texto, historico)

        # Enviar resposta
        enviar_mensagem_texto(numero, resposta)

        # Salvar resposta banco
        salvar_mensagem(numero, resposta, 'outbound')

    except Exception as e:
        logger.error(f'Erro processar mensagem: {str(e)}')
        enviar_mensagem_texto(numero, 'Desculpe, tive um problema. Tente novamente em instantes! 😅')

def handle_preco(mensagem):
    """
    Cliente perguntou preço produto
    """
    # Aqui você consultaria banco dados/API produtos
    # Exemplo simplificado:

    if 'iphone' in mensagem.lower():
        return '''iPhone 15 Pro 256GB: R$ 7.899
iPhone 15 128GB: R$ 5.999

Qual te interessa? 😊'''

    return "Deixa eu consultar preço atualizado! Qual produto exato você quer? 😊"

def handle_estoque(mensagem):
    """
    Cliente perguntou estoque
    """
    return "Temos disponível em estoque! Entrega expressa 3-5 dias. Quer fechar pedido? 📦"

def handle_compra(numero, mensagem):
    """
    Cliente quer comprar
    """
    # Aqui você criaria pedido no sistema
    # Integraria com Stripe/Mercado Pago

    return f'''Ótimo! Vou gerar link pagamento.

Como prefere pagar?
💳 Cartão crédito
🔵 PIX (5% desconto)
📄 Boleto (vence 3 dias)

Responda número da opção!'''

def handle_suporte(numero, mensagem):
    """
    Cliente tem problema
    """
    # Criar ticket Zendesk
    # Notificar atendente humano

    return f'''Entendo sua situação 😔

Já chamei nosso time suporte.
Alguém responde em até 10 minutos.

Pode detalhar problema?'''

Passo 8: Banco Dados (database.py)

import psycopg2
from psycopg2.extras import RealDictCursor
import os
import logging
from datetime import datetime

logger = logging.getLogger(__name__)

DATABASE_URL = os.getenv('DATABASE_URL')

def get_connection():
    """Retorna conexão PostgreSQL"""
    return psycopg2.connect(DATABASE_URL, cursor_factory=RealDictCursor)

def init_database():
    """
    Cria tabelas se não existirem
    """
    try:
        conn = get_connection()
        cursor = conn.cursor()

        # Tabela contatos
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS contatos (
                id SERIAL PRIMARY KEY,
                numero VARCHAR(20) UNIQUE NOT NULL,
                nome VARCHAR(255),
                primeiro_contato TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                ultimo_contato TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')

        # Tabela mensagens
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS mensagens (
                id SERIAL PRIMARY KEY,
                numero VARCHAR(20) NOT NULL,
                mensagem TEXT NOT NULL,
                direcao VARCHAR(10) NOT NULL,
                timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (numero) REFERENCES contatos(numero)
            )
        ''')

        conn.commit()
        cursor.close()
        conn.close()

        logger.info('Banco dados inicializado')

    except Exception as e:
        logger.error(f'Erro init database: {str(e)}')

def criar_ou_atualizar_contato(numero, nome):
    """
    Cria novo contato ou atualiza último contato
    """
    try:
        conn = get_connection()
        cursor = conn.cursor()

        cursor.execute('''
            INSERT INTO contatos (numero, nome, ultimo_contato)
            VALUES (%s, %s, CURRENT_TIMESTAMP)
            ON CONFLICT (numero)
            DO UPDATE SET ultimo_contato = CURRENT_TIMESTAMP, nome = EXCLUDED.nome
        ''', (numero, nome))

        conn.commit()
        cursor.close()
        conn.close()

    except Exception as e:
        logger.error(f'Erro criar contato: {str(e)}')

def salvar_mensagem(numero, mensagem, direcao):
    """
    Salva mensagem banco

    Args:
        direcao: 'inbound' ou 'outbound'
    """
    try:
        conn = get_connection()
        cursor = conn.cursor()

        cursor.execute('''
            INSERT INTO mensagens (numero, mensagem, direcao)
            VALUES (%s, %s, %s)
        ''', (numero, mensagem, direcao))

        conn.commit()
        cursor.close()
        conn.close()

    except Exception as e:
        logger.error(f'Erro salvar mensagem: {str(e)}')

def buscar_historico(numero, limit=10):
    """
    Busca histórico conversas (para contexto ChatGPT)

    Returns:
        list: [{'role': 'user', 'content': '...'}, ...]
    """
    try:
        conn = get_connection()
        cursor = conn.cursor()

        cursor.execute('''
            SELECT mensagem, direcao
            FROM mensagens
            WHERE numero = %s
            ORDER BY timestamp DESC
            LIMIT %s
        ''', (numero, limit))

        mensagens = cursor.fetchall()
        cursor.close()
        conn.close()

        # Formatar para ChatGPT
        historico = []
        for msg in reversed(mensagens):
            role = 'user' if msg['direcao'] == 'inbound' else 'assistant'
            historico.append({'role': role, 'content': msg['mensagem']})

        return historico

    except Exception as e:
        logger.error(f'Erro buscar histórico: {str(e)}')
        return []

Passo 9: Deploy VPS (Ubuntu)

# SSH no servidor
ssh root@seu-servidor.com

# Instalar Python e dependências
apt update
apt install python3 python3-pip python3-venv postgresql nginx -y

# Clonar projeto (ou upload via SCP)
cd /root
git clone https://github.com/seu-usuario/whatsapp-bot.git
cd whatsapp-bot

# Criar virtual environment
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Configurar .env
nano .env
# [COLAR CONFIGURAÇÕES]

# Setup PostgreSQL
sudo -u postgres psql
CREATE DATABASE whatsapp_bot;
CREATE USER bot_user WITH PASSWORD 'senha123';
GRANT ALL PRIVILEGES ON DATABASE whatsapp_bot TO bot_user;
\q

# Testar aplicação
python app.py
# Acessar: http://IP:5000/health

# Setup Gunicorn (produção)
gunicorn --bind 0.0.0.0:5000 --workers 2 app:app

# PM2 (process manager)
npm install -g pm2
pm2 start "gunicorn --bind 0.0.0.0:5000 --workers 2 app:app" --name whatsapp-bot
pm2 save
pm2 startup

# Nginx reverse proxy
nano /etc/nginx/sites-available/whatsapp-bot

server {
    listen 80;
    server_name bot.seudominio.com;

    location / {
        proxy_pass http://localhost:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

ln -s /etc/nginx/sites-available/whatsapp-bot /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx

# SSL (Certbot)
apt install certbot python3-certbot-nginx -y
certbot --nginx -d bot.seudominio.com

Passo 10: Configurar Webhook Evolution API

curl -X POST 'https://evolution.seudominio.com/webhook/set/cliente-atendimento' \
-H 'Content-Type: application/json' \
-H 'apikey: SUA-CHAVE' \
-d '{
  "url": "https://bot.seudominio.com/webhook/whatsapp",
  "webhook_by_events": false,
  "events": ["messages.upsert"]
}'

Testar: Enviar mensagem WhatsApp → Bot deve responder automaticamente ✅


Features Avançadas

1. Rate Limiting (Evitar Spam)

from functools import wraps
from time import time

# Cache último contato por número
ultimos_contatos = {}

def rate_limit(seconds=3):
    """
    Impede múltiplas respostas em X segundos
    """
    def decorator(func):
        @wraps(func)
        def wrapper(numero, *args, **kwargs):
            agora = time()
            ultimo = ultimos_contatos.get(numero, 0)

            if agora - ultimo < seconds:
                logger.warning(f'Rate limit {numero}')
                return

            ultimos_contatos[numero] = agora
            return func(numero, *args, **kwargs)
        return wrapper
    return decorator

@rate_limit(seconds=5)
def processar_mensagem_safe(numero, mensagem):
    # Lógica bot
    pass

2. Typing Indicator

def enviar_com_typing(numero, texto, delay=2):
    """
    Simula digitando antes de enviar
    """
    import time

    # Ativar "digitando..."
    requests.post(
        f'{EVOLUTION_URL}/chat/sendPresence/{INSTANCE}',
        headers={'apikey': API_KEY},
        json={'number': numero, 'presence': 'composing'}
    )

    time.sleep(delay)

    # Enviar mensagem
    enviar_mensagem_texto(numero, texto)

    # Desativar "digitando"
    requests.post(
        f'{EVOLUTION_URL}/chat/sendPresence/{INSTANCE}',
        headers={'apikey': API_KEY},
        json={'number': numero, 'presence': 'available'}
    )

3. Botões Interativos

def enviar_mensagem_botoes(numero, texto, botoes):
    """
    Envia mensagem com botões clicáveis

    Args:
        botoes: [{'id': '1', 'text': 'Sim'}, {'id': '2', 'text': 'Não'}]
    """
    url = f'{EVOLUTION_URL}/message/sendButtons/{INSTANCE}'

    payload = {
        'number': numero,
        'buttonMessage': {
            'text': texto,
            'buttons': [
                {'buttonId': b['id'], 'buttonText': {'displayText': b['text']}}
                for b in botoes
            ]
        }
    }

    requests.post(url, json=payload, headers={'apikey': API_KEY})

# Uso
enviar_mensagem_botoes(
    '5511999887766',
    'Quer continuar atendimento?',
    [
        {'id': 'sim', 'text': '✅ Sim'},
        {'id': 'nao', 'text': '❌ Não'}
    ]
)

Casos Reais ROI

Caso 1: E-commerce - Bot qualificação leads (ROI 800%)

Código bot:

def qualificar_lead(numero, mensagem):
    """
    Qualifica lead e-commerce
    """
    # ChatGPT analisa mensagem
    score = calcular_score_ia(mensagem)

    if score >= 70:
        # Lead quente → Notificar vendedor
        enviar_alerta_vendedor(numero)
        return "Ótimo! Já chamei vendedor. Responde em 2 min! 😊"
    else:
        # Lead frio → Nutrir
        return "Legal! Enviei catálogo por email. Dúvidas? Responda aqui! 📧"

Resultado:

  • 500 leads/mês qualificados automaticamente
  • Vendedores focam só quentes (score 70+)
  • Conversão: 6% → 18% (+200%)
  • ROI: R$ 120k vendas extras vs R$ 15k custo

Caso 2: Clínica - Agendamento automático (95% automação)

Bot agendamento:

def agendar_consulta(numero, data_hora, especialidade):
    """
    Integra Google Calendar API
    """
    # Criar evento
    evento = calendar.events().insert(
        calendarId='clinica@gmail.com',
        body={
            'summary': f'Consulta {especialidade}',
            'start': {'dateTime': data_hora},
            'end': {'dateTime': calcular_fim(data_hora, 30)},  # 30 min
            'attendees': [{'email': buscar_email(numero)}]
        }
    ).execute()

    # Confirmar WhatsApp
    enviar_mensagem_texto(numero, f'''
✅ Agendado!

📅 {formatar_data(data_hora)}
👨‍⚕️ {especialidade}
📍 Rua X, 123

Lembrete: Envio 24h antes!
    ''')

Resultado: 95% agendamentos automáticos (atendente só confirma casos especiais)


Próximos passos

  1. Evolution API Tutorial - Setup API completo
  2. Chatbot WhatsApp IA - IA conversacional
  3. N8N WhatsApp - Alternativa no-code
  4. Flask Python Tutorial - Fundamentos Flask
  5. Docker Tutorial - Containerizar bot
  6. API OpenAI Python - Integração ChatGPT
  7. Automação Python - Automações avançadas

Precisa desenvolver bot WhatsApp Python customizado? A Agência Café Online já criou 40+ bots (ROI médio 600%). Consultoria grátis.


Sobre o autor: Felipe Zanoni desenvolve bots WhatsApp Python há 4 anos, com 2M+ mensagens processadas/mês e 99.9% uptime em produção.