# app/services/notificacao/teste_whatsapp.rb # # Dispara um WhatsApp de teste para o número administrativo, pelo provedor # escolhido na tela (QR/Baileys ou Twilio). # # No Twilio, traduz o código de erro da API para uma instrução acionável em # português — os erros de Sandbox (opt-in) são de longe os mais comuns e o # código cru ("63003") não diz nada para quem está configurando. module Notificacao class TesteWhatsapp MENSAGENS = { 20003 => 'Account SID ou Auth Token inválidos: o Twilio recusou a autenticação.', 20404 => 'Account SID não encontrado no Twilio. Confira se copiou o SID certo (começa com "AC").', 21211 => 'Número de destino inválido. Informe com DDD, ex.: 11 92005-1157.', 21606 => 'O número remetente não é um número Twilio habilitado para envio.', 21608 => 'Conta trial do Twilio: só é possível enviar para números verificados no console da Twilio.', 21610 => 'Este número pediu descadastro (enviou STOP). Ele precisa enviar START para voltar a receber.', 63003 => 'O Twilio não encontrou o destinatário nesse canal. No Sandbox do WhatsApp o número ' \ 'precisa enviar "join " para o número do sandbox ANTES de conseguir receber.', 63007 => 'O número remetente não está habilitado como canal de WhatsApp. Use exatamente o ' \ 'número do Sandbox (ou do Sender aprovado) do Twilio.', 63015 => 'No Sandbox só dá para enviar a números que fizeram o opt-in ("join ").', 63016 => 'Passou da janela de 24h desde a última mensagem do destinatário — nesse caso o Twilio ' \ 'só aceita template aprovado. Peça para ele mandar qualquer mensagem e tente de novo.', 63018 => 'Limite de mensagens do Twilio atingido. Aguarde e tente novamente.', 63038 => 'Limite diário de mensagens da conta Twilio atingido.' }.freeze def initialize(config = ConfiguracaoNotificacao.instancia, destino: nil) @config = config @numero = ConfiguracaoNotificacao.normalizar_telefone(destino.presence || config.whatsapp_admin) @destino = @numero && ConfiguracaoNotificacao.canal(@numero) end def executar if @destino.blank? return Resultado.falha('Preencha o "WhatsApp do administrador" com DDD ' \ '(ex.: 11 92005-1157) antes de testar.') end @config.baileys? ? via_qr : via_twilio end private # Modo QR: quem responde é a ponte Baileys (container `whatsapp`). Os erros # dela já vêm traduzidos pelo ClienteWhatsapp. def via_qr resposta = ClienteWhatsapp.padrao.enviar(para: @numero, texto: corpo) return Resultado.falha(resposta.erro.to_s) unless resposta.ok? Resultado.sucesso("WhatsApp de teste enviado para #{@numero} pela ponte QR.") end def via_twilio credenciais = @config.credenciais_whatsapp if credenciais.blank? return Resultado.falha('Marque "Ativar WhatsApp" e preencha Account SID, Auth Token e o ' \ 'número remetente do Twilio.') end if credenciais[:from].blank? return Resultado.falha('O número remetente do Twilio está vazio ou em formato inválido.') end msg = ClienteTwilio .montar(credenciais[:sid], credenciais[:token]) .messages.create(from: credenciais[:from], to: @destino, body: corpo) Resultado.sucesso("WhatsApp de teste aceito pelo Twilio para " \ "#{@destino.sub('whatsapp:', '')} (SID #{msg.sid}, status \"#{msg.status}\"). " \ 'Atenção: "queued"/"accepted" significa que a Twilio recebeu — ' \ 'a entrega final ainda pode falhar depois.') rescue Twilio::REST::RestError => e Resultado.falha(MENSAGENS[e.code] || "O Twilio recusou o envio (código #{e.code}).", "[#{e.code}] #{e.message}") rescue Twilio::REST::TwilioError => e Resultado.falha('Não foi possível falar com a API do Twilio (rede ou timeout). ' \ 'Verifique a saída de internet do servidor.', e.message) rescue StandardError => e Rails.logger.error("[TesteWhatsapp] #{e.class}: #{e.message}") Resultado.falha("Falha inesperada ao enviar (#{e.class}).", e.message) end def corpo "✅ Teste do Reem Notas\n" \ "Se você recebeu esta mensagem, o envio de WhatsApp está funcionando.\n" \ "Enviado em #{Time.current.strftime('%d/%m/%Y às %H:%M')}." end end end