VoltarProjeto Pessoal
SaaS PlatformAffiliate Marketing

AutoMark Platform

Plataforma SaaS multi-tenant de distribuição inteligente de ofertas afiliadas. Conecta marketplaces (Shopee, ML, Amazon) a canais (WhatsApp, Telegram) com automações anti-spam, scoring inteligente e deduplicação.

Multi
Marketplace
Multi
Channel
48h
Dedupe Target
0
Spam/Ban

! O Problema

Afiliados enfrentam três problemas críticos na distribuição de ofertas:

  • Distribuição manual não escala:

    Postar links manualmente em grupos gera repetição, spam e ban. Tempo gasto vs conversão não compensa.

  • Marketplaces são fragmentados:

    Shopee, Mercado Livre, Amazon têm APIs diferentes, formatos diferentes, regras de afiliado diferentes.

  • WhatsApp pune comportamento robótico:

    Sem controle de cadência, dedupe e humanização: shadow ban, bloqueio, queda de engajamento.

🎯 Objetivo

Criar uma plataforma que pareça humana, poste o que converte, no lugar certo, na hora certa, sem spam.

🏗️ Arquitetura Multi-Tenant

A arquitetura segue princípios rígidos que permitem evolução sem breaking changes:

Marketplace nunca hardcoded

Shopee é o primeiro conector, mas ML, Amazon, AliExpress são plug-and-play.

Canal nunca hardcoded

WhatsApp é o primeiro, mas Telegram, Discord, Instagram seguem a mesma interface.

Automação gera Post, não envia

Separação de concerns: Automação decide O QUE, Dispatcher decide COMO.

Dedupe obrigatório

Nenhum envio acontece sem passar pelo dedupe. Zero spam garantido.

Modelo de Dados

┌─────────────────┐      ┌──────────────────┐      ┌─────────────────┐
│     Tenants     │      │   Marketplaces   │      │    Channels     │
│  (multi-tenant) │      │ (Shopee, ML...)  │      │ (WhatsApp, TG)  │
└────────┬────────┘      └────────┬─────────┘      └────────┬────────┘
         │                        │                         │
         │ 1:N                    │ 1:N                     │ 1:N
         ▼                        ▼                         ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    tenant_marketplace_connections                    │
│                    tenant_channel_connections                        │
│  (credenciais, affiliate_tag, config, status)                       │
└────────┬────────────────────────────────────────────────────────────┘
         │
         │ 1:N
         ▼
┌─────────────────┐      ┌──────────────────┐      ┌─────────────────┐
│   Automations   │─────▶│ AutomationRules  │      │AutomationTargets│
│ (search/ranking)│      │ (keywords, price)│      │ (groups, limits)│
└────────┬────────┘      └──────────────────┘      └────────┬────────┘
         │                                                   │
         │ 1:N                                              │
         ▼                                                   │
┌─────────────────┐      ┌──────────────────┐               │
│     Offers      │─────▶│      Posts       │◀──────────────┘
│ (normalized)    │      │ (content ready)  │
└─────────────────┘      └────────┬─────────┘
                                  │
                                  │ 1:N
                                  ▼
                         ┌──────────────────┐
                         │  PostDispatches  │
                         │ (sent/failed/...)│
                         └──────────────────┘

Papéis e Permissões

SuperAdmin (Plataforma)

  • • Cria tenants
  • • Define quais marketplaces existem
  • • Controla feature flags
  • • Observa saúde global

Tenant Admin (Cliente)

  • • Conecta canais (WhatsApp, Telegram)
  • • Conecta afiliados (Shopee, ML)
  • • Cria automações
  • • Vê histórico e métricas

🔌 Provider Pattern

Cada marketplace implementa uma interface comum. O provider não sabe o que é tenant — tudo vem via tenant_connection. Isso permite adicionar novos marketplaces sem tocar no core.

providers/marketplace/base.py
class MarketplaceProvider:
    """Interface obrigatória para todo marketplace."""
    
    def search(
        self, 
        *, 
        rules: SearchRules, 
        limit: int,
        tenant_connection: TenantMarketplaceConnection,
    ) -> list[OfferCandidate]:
        """Busca ofertas no marketplace."""
        ...
    
    def build_affiliate_url(
        self, 
        *, 
        product_url: str, 
        tenant_connection: TenantMarketplaceConnection,
    ) -> str:
        """Gera URL com tag de afiliado do tenant."""
        ...
    
    def validate_connection(
        self, 
        tenant_connection: TenantMarketplaceConnection,
    ) -> ValidationResult:
        """Valida credenciais (app_id, secret, affiliate_tag)."""
        ...

Shopee Provider

providers/marketplace/shopee.py
class ShopeeProvider(MarketplaceProvider):
    PROVIDER_KEY = "shopee"
    GRAPHQL_ENDPOINT = "https://open-api.affiliate.shopee.com.br/graphql"
    
    def search(self, *, rules, limit, tenant_connection):
        credentials = self._get_credentials(tenant_connection)
        
        # GraphQL query com filtros
        query = self._build_search_query(
            keyword=rules.query,
            category=rules.category,
            min_price=rules.min_price,
            max_price=rules.max_price,
            min_discount=rules.min_discount_percent,
        )
        
        # Assinatura HMAC para autenticação
        signature = self._sign_request(query, credentials)
        
        response = httpx.post(
            self.GRAPHQL_ENDPOINT,
            json={"query": query},
            headers={"Authorization": f"SHA256 {signature}"},
            timeout=20.0,
        )
        
        return self._parse_offers(response.json())
    
    def _build_dedupe_key(self, item) -> str:
        """Identidade única: shopee:{shop_id}:{item_id}"""
        return f"shopee:{item['shopId']}:{item['itemId']}"

✓ Dedupe Key

O dedupe_key é a identidade real do produto: shopee:123:456. Nunca usar título para dedupe — variações de texto causariam spam.

Offer Scoring

Nem toda oferta vale a pena postar. O sistema de scoring ranqueia ofertas por potencial de conversão, penalizando produtos suspeitos.

Fórmula de Score
# Score Components (0-10 each)
discount_score = min(discount_percent, 80) / 80 * 10
rating_score = (rating / 5) * 10
commission_score = min(commission_pct, 10) / 10 * 10
sales_score = min(sold_count, 1000) / 1000 * 10

# Weighted Sum
score = (discount_score * 0.40) +
        (rating_score * 0.25) +
        (commission_score * 0.15) +
        (sales_score * 0.20) +
        recency_bonus

# Recency Bonus
< 6h:  +1.5
< 24h: +1.0
< 72h: +0.5
> 72h: +0.0

# Penalties (subtraídas do score)
fake_promo:      discount > 70% AND rating < 4.1 → -2.0
no_reviews:      rating is None or 0 → -1.0
too_cheap:       price < R$5.00 → -0.5
high_commission: commission > 20% → -1.0

Por que esses pesos?

  • 40%Desconto: Principal driver de clique. Ofertas com desconto alto convertem mais.
  • 25%Rating: Confiança do produto. Rating baixo = devolução = comissão cancelada.
  • 20%Vendas: Prova social. Produto muito vendido tem validação de mercado.
  • 15%Comissão: Retorno para o afiliado. Mas comissão alta demais é red flag.

🔄 Deduplicação Anti-Spam

O sistema de dedupe opera em dois escopos: por target (grupo específico) e por channel (todos os grupos do canal). Isso previne spam tanto vertical quanto horizontal.

Target Scope (48h)

Mesmo produto não pode ser enviado para o mesmo grupo em 48h. Evita repetição percebida pelos membros.

Channel Scope (2-5min)

Gap mínimo entre qualquer envio para qualquer grupo. Evita burst que parece bot.

services/dedupe_service.py
class DedupeService:
    def is_blocked(
        self,
        *,
        tenant_id: UUID,
        channel_id: UUID,
        dedupe_key: str,  # ex: "shopee:123:456"
        target_ref: UUID | None,
        scope: DedupeScope,
    ) -> bool:
        """
        Check if offer is blocked by cooldown.
        
        Returns True if still in cooldown, False if can send.
        """
        record = self.db.query(DedupeRecord).filter(
            DedupeRecord.tenant_id == tenant_id,
            DedupeRecord.dedupe_key == dedupe_key,
            DedupeRecord.scope == scope,
            DedupeRecord.cooldown_until > now,  # Still cooling
        ).first()
        
        return record is not None
    
    def mark_sent(self, *, dedupe_key, scope, post_id):
        """Mark offer as sent, starting cooldown."""
        cooldown = (
            timedelta(hours=48) if scope == DedupeScope.target
            else timedelta(minutes=5)
        )
        
        record = DedupeRecord(
            dedupe_key=dedupe_key,
            cooldown_until=now + cooldown,
            last_post_id=post_id,
        )
        self.db.add(record)

✓ Cleanup Automático

Records expirados são deletados periodicamente via cleanup_expired(). A tabela não cresce indefinidamente.

🛡️ Anti-Ban (WhatsApp)

WhatsApp detecta bots por padrões de envio. O sistema implementa múltiplas camadas de humanização para evitar banimento:

  • 🖊️
    Typing Indicator:

    Antes de enviar, simula "digitando..." por 2-5 segundos (Evolution API).

  • 🎲
    Jitter Humano:

    Delay aleatório entre mensagens. Nunca intervalos exatos.

  • 🚦
    Rate Limit por Conta:

    Semáforo Redis limita msgs/minuto por número. Múltiplas contas = mais throughput.

  • Circuit Breaker:

    Erro 429 ou 503 → pausa automática de 30min para aquela conta.

  • 🌙
    Quiet Hours:

    Configurável por target: não envia entre 23h-8h (ou custom).

⚙️ Sistema de Automações

Automações são o coração do sistema. Definem O QUE buscar, ONDE enviar, e QUANDO executar.

Automation Pipeline
# Pipeline de Execução (workers/tasks.py)

1. tick_automations()
   └─ Busca automations habilitadas no intervalo

2. Para cada automation:
   ├─ Valida janela de horário (start_time/end_time)
   ├─ Carrega conexões ativas do tenant (marketplace + channel)
   │
   ├─ Busca ofertas via provider.search()
   │   └─ Aplica rules (keywords, price, discount)
   │
   ├─ Normaliza ofertas + gera dedupe_key
   │
   ├─ Aplica scoring + ranking
   │   └─ priority_mode: max_discount | min_price | score
   │
   └─ Para cada target:
       ├─ Check dedupe (target scope)
       ├─ Check rate limit (channel scope)
       ├─ Check quiet hours
       │
       ├─ typing() → delay → send()
       │
       ├─ Cria Post + PostDispatch
       └─ Marca dedupe

Tipos de Automação

search

Busca ativa no marketplace. Executa a cada intervalo configurado.

feed

Usa ofertas já no banco (ingestão externa). Aplica ranking e distribui.

monitor

Monitora preço de produtos específicos. Alerta quando desconto atinge threshold.

AutomationTarget (Limites)

Configuração por Grupo
automation_targets:
  - target_ref: "uuid-do-grupo"
    max_posts_per_day: 10      # Máximo de posts/dia neste grupo
    min_gap_minutes: 30        # Mínimo entre posts (além do dedupe)
    quiet_hours_start: "23:00"
    quiet_hours_end: "08:00"

🛠️ Stack Técnico

Backend

Python 3.12FastAPISQLAlchemy 2.0CeleryPostgreSQLRedisAlembicstructlog

Frontend

Vue.js 3TypeScriptViteTailwindCSS

Integrações

Shopee Affiliate APIEvolution APIWhatsApp

Infra

DockerDocker Compose

📊 Resultados

0
Bans de conta WhatsApp
100%
Dedupe coverage (zero spam)
Plug
& Play para novos marketplaces
Multi
Tenant desde o dia 1

Explore Outros Projetos

Veja outros sistemas que construí

Case Study: AutoMark Platform — Affiliate Marketing Automation