feat: complete session — catalog, marketplace, WhatsApp, peer-to-peer, install scripts
Major features: - Pixel-Perfect glassmorphism design (landing + POS + public catalog) - OEM/Local catalog toggle with Nexpart taxonomy (14 groups, 108 subgroups, 558 part types) - Marketplace B2B Phase 1 (bodegas, POs, status machine, WA+email notifications) - Peer-to-peer inventory (multi-instance, LAN discovery) - WhatsApp: photo→Vision AI, voice→Whisper, conversational quotations - Smart unified search (VIN/plate/part_number/keyword auto-detect) - Shop Supplies tab (vehicle-independent parts) - Chatbot AI fallback chain (5 models) + response cache - CSV inventory import tool + setup_instance.sh installer - Tablet-responsive CSS + sidebar toggle - Filters, export CSV, employee edit, business data save - Quotation system (WA→POS) with auto-print on confirmation - Live stats on landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,12 +55,63 @@ def logout():
|
||||
|
||||
|
||||
def process_incoming(webhook_data):
|
||||
"""Extract a normalized dict from a Baileys webhook payload.
|
||||
|
||||
Supports text messages, image messages, audio (voice notes), and video.
|
||||
Media content comes pre-downloaded as base64 from the bridge so Python
|
||||
doesn't have to re-authenticate with WhatsApp servers.
|
||||
|
||||
Returns:
|
||||
dict with keys:
|
||||
phone — numeric phone (no JID suffix)
|
||||
jid — full remote JID (may be @s.whatsapp.net or @lid)
|
||||
text — text content (plain text or media caption)
|
||||
from_me — bool, True if we sent the message
|
||||
message_id — WhatsApp message ID
|
||||
media_kind — 'text' | 'image' | 'audio' | 'video'
|
||||
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)
|
||||
"""
|
||||
data = webhook_data.get('data', {})
|
||||
key = data.get('key', {})
|
||||
message = data.get('message', {})
|
||||
|
||||
# remoteJid can be phone@s.whatsapp.net or LID@lid
|
||||
remote_jid = key.get('remoteJid', '')
|
||||
phone = remote_jid.replace('@s.whatsapp.net', '').replace('@lid', '')
|
||||
|
||||
# The bridge now classifies and passes these extra fields. Fall back to
|
||||
# the old parsing if they're missing (older bridge version).
|
||||
media_kind = data.get('media_kind', 'text')
|
||||
media_base64 = data.get('media_base64')
|
||||
media_mimetype = data.get('media_mimetype')
|
||||
media_caption = data.get('media_caption') or ''
|
||||
is_voice_note = bool(data.get('media_ptt'))
|
||||
push_name = data.get('push_name') or ''
|
||||
|
||||
# Text content:
|
||||
# - For 'text' messages → conversation or extendedTextMessage
|
||||
# - For 'image'/'video' → the caption (may be empty)
|
||||
# - For 'audio' → empty (filled in later by Whisper transcription)
|
||||
if media_kind == 'text':
|
||||
text = (
|
||||
message.get('conversation', '')
|
||||
or message.get('extendedTextMessage', {}).get('text', '')
|
||||
or ''
|
||||
)
|
||||
else:
|
||||
text = media_caption
|
||||
|
||||
return {
|
||||
'phone': key.get('remoteJid', '').replace('@s.whatsapp.net', ''),
|
||||
'text': message.get('conversation', '') or message.get('extendedTextMessage', {}).get('text', ''),
|
||||
'phone': phone,
|
||||
'jid': remote_jid,
|
||||
'text': text,
|
||||
'from_me': key.get('fromMe', False),
|
||||
'message_id': key.get('id', ''),
|
||||
'media_kind': media_kind,
|
||||
'media_base64': media_base64,
|
||||
'media_mimetype': media_mimetype,
|
||||
'is_voice_note': is_voice_note,
|
||||
'push_name': push_name,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user