feat(whatsapp): per-tenant WhatsApp configuration
- Refactor whatsapp_service.py to accept bridge_url parameter - whatsapp_bp.py: remove hardcoded tenant_id=11, use g.tenant_id - whatsapp_bp.py: webhook now accepts ?tenant_id param with fallback - config_bp.py: add GET/PUT /config/whatsapp endpoints - Each tenant can now have its own Baileys bridge URL and settings
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""WhatsApp service via Baileys Bridge (self-hosted, free).
|
||||
|
||||
Simple REST bridge at localhost:21465 that wraps WhatsApp Web via Baileys.
|
||||
Simple REST bridge that wraps WhatsApp Web via Baileys.
|
||||
Supports per-tenant configuration via bridge_url parameter.
|
||||
"""
|
||||
|
||||
import requests
|
||||
@@ -9,47 +10,56 @@ from config import WHATSAPP_BRIDGE_URL
|
||||
HEADERS = {'Content-Type': 'application/json'}
|
||||
|
||||
|
||||
def get_status():
|
||||
def _get_url(bridge_url=None):
|
||||
return bridge_url or WHATSAPP_BRIDGE_URL
|
||||
|
||||
|
||||
def get_status(bridge_url=None):
|
||||
url = _get_url(bridge_url)
|
||||
try:
|
||||
return requests.get(f'{WHATSAPP_BRIDGE_URL}/status', timeout=5).json()
|
||||
return requests.get(f'{url}/status', timeout=5).json()
|
||||
except Exception as e:
|
||||
return {'state': 'error', 'error': str(e)}
|
||||
|
||||
|
||||
def get_qr():
|
||||
def get_qr(bridge_url=None):
|
||||
url = _get_url(bridge_url)
|
||||
try:
|
||||
return requests.get(f'{WHATSAPP_BRIDGE_URL}/qr', timeout=5).json()
|
||||
return requests.get(f'{url}/qr', timeout=5).json()
|
||||
except Exception as e:
|
||||
return {'state': 'error', 'error': str(e)}
|
||||
|
||||
|
||||
def connect():
|
||||
def connect(bridge_url=None):
|
||||
url = _get_url(bridge_url)
|
||||
try:
|
||||
return requests.post(f'{WHATSAPP_BRIDGE_URL}/connect', headers=HEADERS, timeout=5).json()
|
||||
return requests.post(f'{url}/connect', headers=HEADERS, timeout=5).json()
|
||||
except Exception as e:
|
||||
return {'state': 'error', 'error': str(e)}
|
||||
|
||||
|
||||
def send_message(phone, text):
|
||||
def send_message(phone, text, bridge_url=None):
|
||||
url = _get_url(bridge_url)
|
||||
try:
|
||||
return requests.post(f'{WHATSAPP_BRIDGE_URL}/send', headers=HEADERS, json={'phone': phone, 'message': text}, timeout=15).json()
|
||||
return requests.post(f'{url}/send', headers=HEADERS, json={'phone': phone, 'message': text}, timeout=15).json()
|
||||
except Exception as e:
|
||||
return {'error': str(e)}
|
||||
|
||||
|
||||
def send_quote(phone, quote_data):
|
||||
def send_quote(phone, quote_data, bridge_url=None):
|
||||
lines = [f"*Cotizacion #{quote_data.get('id', '')}*", ""]
|
||||
for item in quote_data.get('items', []):
|
||||
lines.append(f"- {item.get('quantity', 1)}x {item.get('name', '')} ${item.get('subtotal', 0):,.2f}")
|
||||
lines.append(f"\nSubtotal: ${quote_data.get('subtotal', 0):,.2f}")
|
||||
lines.append(f"IVA: ${quote_data.get('tax_total', 0):,.2f}")
|
||||
lines.append(f"*Total: ${quote_data.get('total', 0):,.2f}*")
|
||||
return send_message(phone, "\n".join(lines))
|
||||
return send_message(phone, "\n".join(lines), bridge_url=bridge_url)
|
||||
|
||||
|
||||
def logout():
|
||||
def logout(bridge_url=None):
|
||||
url = _get_url(bridge_url)
|
||||
try:
|
||||
return requests.post(f'{WHATSAPP_BRIDGE_URL}/logout', headers=HEADERS, timeout=5).json()
|
||||
return requests.post(f'{url}/logout', headers=HEADERS, timeout=5).json()
|
||||
except Exception as e:
|
||||
return {'error': str(e)}
|
||||
|
||||
@@ -72,6 +82,7 @@ def process_incoming(webhook_data):
|
||||
media_base64 — base64 string if media, else None
|
||||
media_mimetype — e.g. 'image/jpeg', 'audio/ogg'
|
||||
is_voice_note — True for WhatsApp voice notes (audioMessage ptt)
|
||||
push_name — display name from WhatsApp
|
||||
"""
|
||||
data = webhook_data.get('data', {})
|
||||
key = data.get('key', {})
|
||||
|
||||
Reference in New Issue
Block a user