Skip to content

Arquitetura Multi-Plataforma de Mensageria

📋 Visão Geral

Esta refatoração transforma o bot monolítico em uma arquitetura extensível e modular que permite adicionar novas plataformas de mensageria (WhatsApp, Telegram, Discord, etc.) sem modificar o código core.

🏗️ Arquitetura

Componentes Principais

messaging/
├── __init__.py              # Exports públicos
├── base.py                  # Interface abstrata MessagingAdapter
├── factory.py               # Factory para criar adaptadores
├── processor.py             # Processador central de conversas
└── adapters/
    ├── __init__.py
    ├── slack_adapter.py     # Implementação Slack
    ├── whatsapp_adapter.py  # Implementação WhatsApp (exemplo)
    └── telegram_adapter.py  # Implementação Telegram (exemplo)

unified_bot.py               # Bot unificado multi-plataforma
slack_bot.py                 # [LEGACY] Bot antigo (manter por compatibilidade)

Padrões Utilizados

  1. Strategy Pattern: Cada plataforma é uma estratégia diferente
  2. Factory Pattern: Criação centralizada de adaptadores
  3. Adapter Pattern: Interface uniforme para plataformas heterogêneas
  4. Single Responsibility: Separação clara de responsabilidades

🔌 Componentes

1. MessagingAdapter (Interface Abstrata)

Define o contrato que todos os adaptadores devem implementar:

class MessagingAdapter(ABC):
    async def setup() -> None
    async def parse_webhook_request(request) -> Optional[IncomingMessage]
    async def send_message(message: OutgoingMessage) -> bool
    async def send_typing_indicator(channel_id, thread_id) -> None
    async def update_message(message_id, new_text, channel_id) -> bool
    async def delete_message(message_id, channel_id) -> bool
    def format_text(text: str) -> str

    @property
    def platform_name() -> str

    @property
    def webhook_path() -> str

2. Modelos de Dados

IncomingMessage

@dataclass
class IncomingMessage:
    platform: str           # slack, whatsapp, telegram
    user_id: str
    channel_id: str
    thread_id: str
    text: str
    message_type: MessageType
    is_direct_message: bool
    metadata: Dict[str, Any]

OutgoingMessage

@dataclass
class OutgoingMessage:
    text: str
    channel_id: str
    thread_id: Optional[str]
    message_type: MessageType
    metadata: Dict[str, Any]

3. AdapterFactory

Gerencia criação e registro de adaptadores:

factory = AdapterFactory()

# Auto-detecção via env vars
factory.register_from_env()

# Ou manual
factory.register_from_config("slack", {
    "bot_token": "...",
    "signing_secret": "..."
})

# Buscar adapter
slack = factory.get_adapter("slack")
all_adapters = factory.get_all_adapters()

4. ConversationProcessor

Processa conversas de forma agnóstica à plataforma:

processor = ConversationProcessor(runner)

# Processa mensagem de qualquer plataforma
await processor.process_message(
    message=incoming_message,
    adapter=slack_adapter
)

5. Unified Bot (unified_bot.py)

Bot unificado com webhook único que roteia para o adapter correto:

@app.post("/webhook/{path:path}")
async def unified_webhook(path: str, request: Request):
    # Encontra adapter baseado no path
    adapter = factory.get_adapter_by_webhook_path(f"/{path}")

    # Parse mensagem
    message = await adapter.parse_webhook_request(request)

    # Processa
    await processor.process_message(message, adapter)

🚀 Como Usar

Configuração via Variáveis de Ambiente

# Plataformas ativas (auto-detectado se não especificado)
MESSAGING_PLATFORMS=slack,telegram,whatsapp

# Slack
SLACK_BOT_TOKEN=xoxb-...
SLACK_SIGNING_SECRET=...

# Telegram
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...

# WhatsApp (via Twilio)
WHATSAPP_ACCOUNT_SID=AC...
WHATSAPP_AUTH_TOKEN=...
WHATSAPP_NUMBER=+5511999999999

# Backends (session e memory)
SESSION_BACKEND=redis
MEMORY_BACKEND=cloudsql

Executar o Bot Unificado

python unified_bot.py

Webhooks

Configure os webhooks nas plataformas:

  • Slack: https://seu-dominio.com/webhook/slack/events
  • Telegram: https://seu-dominio.com/webhook/telegram/webhook
  • WhatsApp: https://seu-dominio.com/webhook/whatsapp/webhook

➕ Adicionando Nova Plataforma

Exemplo: Discord

  1. Criar adapter em messaging/adapters/discord_adapter.py:
from ..base import MessagingAdapter, IncomingMessage, OutgoingMessage

class DiscordAdapter(MessagingAdapter):
    def __init__(self, config):
        super().__init__(config)
        self.bot = None

    def _validate_config(self):
        if "bot_token" not in self.config:
            raise ValueError("Discord bot_token required")

    async def setup(self):
        # Inicializa cliente Discord
        self.bot = discord.Client(token=self.config["bot_token"])
        await self.bot.start()

    async def parse_webhook_request(self, request):
        # Parse Discord webhook/gateway event
        data = await request.json()
        # ... converter para IncomingMessage
        return IncomingMessage(...)

    async def send_message(self, message):
        # Enviar via Discord API
        channel = await self.bot.fetch_channel(message.channel_id)
        await channel.send(message.text)
        return True

    # ... implementar outros métodos abstratos

    @property
    def platform_name(self):
        return "discord"

    @property
    def webhook_path(self):
        return "/discord/webhook"
  1. Registrar no factory em messaging/factory.py:
from .adapters.discord_adapter import DiscordAdapter

class AdapterFactory:
    ADAPTER_CLASSES = {
        "slack": SlackAdapter,
        "whatsapp": WhatsAppAdapter,
        "telegram": TelegramAdapter,
        "discord": DiscordAdapter,  # ← Adicionar aqui
    }

    def _get_platform_config(self, platform):
        # ... código existente ...

        elif platform == "discord":
            token = os.getenv("DISCORD_BOT_TOKEN")
            if token:
                return {"bot_token": token}
  1. Configurar env vars:
MESSAGING_PLATFORMS=slack,discord
DISCORD_BOT_TOKEN=...
  1. Pronto! O bot já suporta Discord 🎉

🔄 Migração do Código Antigo

Opção 1: Substituir Completamente

Renomeie o arquivo antigo e use o novo:

mv slack_bot.py slack_bot.old.py
cp unified_bot.py slack_bot.py

Opção 2: Manter Ambos

Rode em portas diferentes:

# Bot legado (apenas Slack)
PORT=8080 python slack_bot.py

# Bot unificado (todas plataformas)
PORT=8081 python unified_bot.py

Opção 3: Migração Gradual

  1. Mantenha slack_bot.py em produção
  2. Teste unified_bot.py em staging
  3. Quando estável, redirecione webhook do Slack
  4. Desative slack_bot.py

📊 Comparação

Aspecto Antes (slack_bot.py) Depois (unified_bot.py)
Plataformas Apenas Slack Múltiplas (extensível)
Webhooks 1 por plataforma 1 webhook unificado
Código duplicado Muito Zero
Adicionar plataforma Reescrever bot inteiro Criar 1 adapter (50-100 linhas)
Manutenção Difícil (código entrelaçado) Fácil (separação clara)
Testes Difícil Fácil (mock adapters)

🧪 Testes

Testar Adapter Isoladamente

from messaging.adapters import SlackAdapter

adapter = SlackAdapter({
    "bot_token": "...",
    "signing_secret": "..."
})

await adapter.setup()

# Simular mensagem
message = OutgoingMessage(
    text="Hello",
    channel_id="C123",
)

success = await adapter.send_message(message)

Testar Processor

processor = ConversationProcessor(runner)

incoming = IncomingMessage(
    platform="slack",
    user_id="U123",
    channel_id="C123",
    thread_id="123.456",
    text="Olá",
)

await processor.process_message(incoming, slack_adapter)

🔧 Troubleshooting

Problema: Adapter não encontrado

Causa: Plataforma não registrada ou env vars incorretas

Solução:

# Verificar env vars
echo $MESSAGING_PLATFORMS
echo $SLACK_BOT_TOKEN

# Verificar logs
# Deve aparecer: "✅ Plataformas registradas: slack, telegram"

Problema: Webhook retorna 404

Causa: Path do webhook incorreto

Solução: - Verificar webhook_path no adapter - Garantir que começa com / correto - Exemplo: /slack/events (não slack/events)

Problema: Mensagens duplicadas

Causa: Múltiplos bots rodando ou retry não filtrado

Solução: - Verificar filtro de retries no parse_webhook_request - Garantir apenas 1 instância do bot rodando

📈 Próximos Passos

  1. ✅ Refatoração completa
  2. ⬜ Adicionar testes unitários
  3. ⬜ Adicionar rate limiting
  4. ⬜ Implementar métricas (Prometheus)
  5. ⬜ Adicionar suporte a attachments (imagens, arquivos)
  6. ⬜ Implementar rich messages (buttons, cards)
  7. ⬜ Cache de respostas

📚 Recursos

🤝 Contribuindo

Para adicionar nova plataforma:

  1. Crie adapter em messaging/adapters/
  2. Implemente interface MessagingAdapter
  3. Registre no AdapterFactory.ADAPTER_CLASSES
  4. Adicione env vars no _get_platform_config
  5. Documente no README
  6. Adicione testes

Desenvolvido com ❤️ para extensibilidade e manutenibilidade