Implantação da geração de mensagem de forma livre e mudança na engine de mensagem

This commit is contained in:
2026-08-24 17:32:16 -03:00
parent 94bbe7556f
commit 88f19dd8f5
77 changed files with 4650 additions and 25 deletions

View File

@@ -11,9 +11,15 @@ class ConfiguracaoNotificacao < ApplicationRecord
self.table_name = 'configuracao_notificacoes'
atributo_cifrado :smtp_password, :twilio_auth_token
atributo_cifrado :smtp_password, :twilio_auth_token, :baileys_token
AUTENTICACOES = %w[plain login cram_md5].freeze
# Quem entrega o WhatsApp. `baileys` é a ponte por QR code (container
# `whatsapp`), gratuita e ilimitada, porém NÃO OFICIAL — a Meta pode banir o
# número. `twilio` é o canal oficial pago que existia antes.
PROVEDORES_WHATSAPP = %w[baileys twilio].freeze
BAILEYS_URL_PADRAO = 'http://whatsapp:3001'.freeze
EMAIL_RE = /\A[^@\s]+@[^@\s]+\z/
REMETENTE_PADRAO = 'noreply@gade.com.br'
@@ -28,9 +34,16 @@ class ConfiguracaoNotificacao < ApplicationRecord
validates :email_admin, format: { with: EMAIL_RE }, allow_blank: true
validate :senha_smtp_presente, if: :smtp_ativo?
validates :twilio_account_sid, presence: true, if: :whatsapp_ativo?
validates :twilio_from, presence: true, if: :whatsapp_ativo?
validate :token_twilio_presente, if: :whatsapp_ativo?
validates :whatsapp_provedor, inclusion: { in: PROVEDORES_WHATSAPP }
validates :whatsapp_intervalo_segundos,
numericality: { only_integer: true, greater_than_or_equal_to: 0,
less_than_or_equal_to: 300 }
# As credenciais do Twilio só são exigidas quando ele É o provedor escolhido.
validates :twilio_account_sid, presence: true, if: :twilio_exigido?
validates :twilio_from, presence: true, if: :twilio_exigido?
validate :token_twilio_presente, if: :twilio_exigido?
validate :baileys_configuravel, if: :baileys_exigido?
# ── Singleton ───────────────────────────────────────────────
def self.instancia
@@ -102,6 +115,32 @@ class ConfiguracaoNotificacao < ApplicationRecord
email_notificacoes_ativo?
end
# ── WhatsApp: escolha de provedor ───────────────────────────
def baileys? = whatsapp_provedor.to_s == 'baileys'
def twilio? = whatsapp_provedor.to_s == 'twilio'
def twilio_exigido? = whatsapp_ativo? && twilio?
def baileys_exigido? = whatsapp_ativo? && baileys?
# URL e token da ponte: banco > .env, mesma hierarquia do resto da tela.
def baileys_url_efetiva
baileys_url.presence || ENV['BAILEYS_URL'].presence || BAILEYS_URL_PADRAO
end
def baileys_token_efetivo
baileys_token.presence || ENV['WHATSAPP_TOKEN'].presence
end
def baileys_pronto?
whatsapp_ativo? && baileys? && baileys_token_efetivo.present?
end
# Segundos entre uma mensagem e a seguinte. Disparo em rajada para dezenas de
# contatos é a forma mais rápida de o número ser banido no canal não oficial.
def intervalo_envio
whatsapp_intervalo_segundos.to_i.clamp(0, 300)
end
# ── WhatsApp / Twilio ───────────────────────────────────────
def whatsapp_pronto?
whatsapp_ativo? && twilio_account_sid.present? &&
@@ -109,7 +148,9 @@ class ConfiguracaoNotificacao < ApplicationRecord
end
def whatsapp_habilitado?
whatsapp_ativo? && credenciais_whatsapp.present?
return false unless whatsapp_ativo?
baileys? ? baileys_pronto? : credenciais_whatsapp.present?
end
# Tudo-ou-nada: nunca mistura o SID do banco com o token do .env (a conta
@@ -162,12 +203,22 @@ class ConfiguracaoNotificacao < ApplicationRecord
self.remetente_nome = remetente_nome.to_s.strip.presence
self.email_admin = email_admin.to_s.strip.downcase.presence
self.twilio_account_sid = twilio_account_sid.to_s.strip.presence
self.baileys_url = baileys_url.to_s.strip.chomp('/').presence
self.whatsapp_provedor = whatsapp_provedor.presence || 'baileys'
# Mantém o que o admin digitou se não der para normalizar — a validação de
# presença acusa, e ele vê o próprio valor de volta no campo para corrigir.
self.twilio_from = self.class.normalizar_telefone(twilio_from) || twilio_from.presence
self.whatsapp_admin = self.class.normalizar_telefone(whatsapp_admin) || whatsapp_admin.presence
end
def baileys_configuravel
return if baileys_token_efetivo.present?
errors.add(:baileys_token,
'é obrigatório: defina WHATSAPP_TOKEN no .env (o mesmo do container `whatsapp`) ' \
'ou preencha aqui.')
end
def senha_smtp_presente
return if smtp_password.present?