mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
feat: Add WhatsApp AI Brain — central intelligence for Dealix number
WhatsApp Brain (4 files, ~1,200 lines): whatsapp_brain.py (350 lines): - Central router: identify caller → detect intent → route → respond - 5 modes: SALES, SUPPORT, MARKETER, DEALS, GENERAL - Connected to DB: queries leads, users, affiliates by phone - Arabic/English language detection - 11 intent types with keyword matching - Conversation history (last 50 messages per caller) - Contextual responses using caller profile data whatsapp_knowledge.py (250 lines): - 6 features with Arabic descriptions + selling points - 3 pricing plans with Arabic feature lists - 8 objection responses (Arabic + English) - 3 competitor battlecards (Zoho, Salesforce, HubSpot) - 10 FAQ + 5 Marketer FAQ - FAQ search by keyword matching comparison_engine.py (200 lines): - 5 competitors × 12 dimensions scoring (0-10) - Chart data for radar/bar charts (frontend-ready) - Feature comparison matrix (8 features × 5 competitors) - "Why Dealix Wins" lists (Arabic + English) - Per-competitor comparison summaries whatsapp_webhook.py (120 lines): - POST /webhooks/whatsapp/incoming — Meta + Twilio format parsing - GET /webhooks/whatsapp/verify — Meta challenge verification - POST /webhooks/whatsapp/status — Delivery/read receipts https://claude.ai/code/session_01LsnvBa7HwF5hs99VZbgLGj
This commit is contained in:
parent
2ad8cd02bd
commit
738a7b5bf2
135
salesflow-saas/backend/app/api/v1/whatsapp_webhook.py
Normal file
135
salesflow-saas/backend/app/api/v1/whatsapp_webhook.py
Normal file
@ -0,0 +1,135 @@
|
||||
"""
|
||||
WhatsApp Webhook — Dealix AI Revenue OS
|
||||
Handles incoming WhatsApp messages, verification, and delivery status.
|
||||
"""
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Request, Depends, HTTPException
|
||||
from fastapi.responses import PlainTextResponse
|
||||
|
||||
from app.database import get_db
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/webhooks/whatsapp", tags=["WhatsApp Webhook"])
|
||||
|
||||
|
||||
@router.post("/incoming")
|
||||
async def handle_incoming(request: Request, db=Depends(get_db)):
|
||||
"""Handle incoming WhatsApp messages from Meta Cloud API or Twilio."""
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail="Invalid JSON body")
|
||||
|
||||
phone = ""
|
||||
message = ""
|
||||
|
||||
# Meta Cloud API format
|
||||
if "entry" in body:
|
||||
try:
|
||||
entry = body["entry"][0]
|
||||
changes = entry.get("changes", [{}])[0]
|
||||
value = changes.get("value", {})
|
||||
messages = value.get("messages", [])
|
||||
if messages:
|
||||
msg = messages[0]
|
||||
phone = msg.get("from", "")
|
||||
if msg.get("type") == "text":
|
||||
message = msg.get("text", {}).get("body", "")
|
||||
elif msg.get("type") == "interactive":
|
||||
interactive = msg.get("interactive", {})
|
||||
if "button_reply" in interactive:
|
||||
message = interactive["button_reply"].get("title", "")
|
||||
elif "list_reply" in interactive:
|
||||
message = interactive["list_reply"].get("title", "")
|
||||
else:
|
||||
message = f"[{msg.get('type', 'unknown')} message]"
|
||||
except (IndexError, KeyError) as e:
|
||||
logger.warning(f"Failed to parse Meta webhook: {e}")
|
||||
return {"status": "ok"}
|
||||
|
||||
# Twilio format
|
||||
elif "From" in body or "from" in body:
|
||||
phone = body.get("From", body.get("from", "")).replace("whatsapp:", "")
|
||||
message = body.get("Body", body.get("body", ""))
|
||||
|
||||
if not phone or not message:
|
||||
logger.debug("Webhook received but no actionable message")
|
||||
return {"status": "ok"}
|
||||
|
||||
# Process through WhatsApp Brain
|
||||
from app.services.whatsapp_brain import whatsapp_brain
|
||||
|
||||
try:
|
||||
response = await whatsapp_brain.handle_incoming(phone, message, db)
|
||||
except Exception as e:
|
||||
logger.error(f"WhatsApp brain error for {phone}: {e}")
|
||||
response = "عذراً، حدث خطأ. حاول مرة أخرى أو تواصل مع support@dealix.sa"
|
||||
|
||||
# Send response via WhatsApp API
|
||||
try:
|
||||
from app.integrations.whatsapp import send_whatsapp_message
|
||||
await send_whatsapp_message(phone, response)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send WhatsApp response to {phone}: {e}")
|
||||
|
||||
logger.info(f"[WhatsApp] {phone}: '{message[:50]}...' → response sent")
|
||||
return {"status": "ok", "phone": phone, "response_length": len(response)}
|
||||
|
||||
|
||||
@router.get("/verify")
|
||||
async def verify_webhook(request: Request):
|
||||
"""Meta webhook verification challenge."""
|
||||
params = request.query_params
|
||||
mode = params.get("hub.mode")
|
||||
token = params.get("hub.verify_token")
|
||||
challenge = params.get("hub.challenge")
|
||||
|
||||
import os
|
||||
verify_token = os.environ.get("WHATSAPP_VERIFY_TOKEN", "dealix-whatsapp-verify-2026")
|
||||
|
||||
if mode == "subscribe" and token == verify_token:
|
||||
logger.info("WhatsApp webhook verified successfully")
|
||||
return PlainTextResponse(content=challenge or "", status_code=200)
|
||||
|
||||
logger.warning(f"WhatsApp webhook verification failed: mode={mode}, token={token}")
|
||||
raise HTTPException(status_code=403, detail="Verification failed")
|
||||
|
||||
|
||||
@router.post("/status")
|
||||
async def delivery_status(request: Request):
|
||||
"""Handle delivery/read status updates from WhatsApp."""
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
return {"status": "ok"}
|
||||
|
||||
# Meta format
|
||||
if "entry" in body:
|
||||
try:
|
||||
entry = body["entry"][0]
|
||||
changes = entry.get("changes", [{}])[0]
|
||||
value = changes.get("value", {})
|
||||
statuses = value.get("statuses", [])
|
||||
|
||||
for status in statuses:
|
||||
recipient = status.get("recipient_id", "")
|
||||
status_type = status.get("status", "") # sent, delivered, read, failed
|
||||
timestamp = status.get("timestamp", "")
|
||||
logger.debug(
|
||||
f"[WhatsApp Status] {recipient}: {status_type} at {timestamp}"
|
||||
)
|
||||
|
||||
# Update message status in database if needed
|
||||
if status_type == "failed":
|
||||
errors = status.get("errors", [])
|
||||
error_msg = errors[0].get("title", "Unknown") if errors else "Unknown"
|
||||
logger.error(
|
||||
f"[WhatsApp] Message to {recipient} FAILED: {error_msg}"
|
||||
)
|
||||
except (IndexError, KeyError) as e:
|
||||
logger.warning(f"Failed to parse status webhook: {e}")
|
||||
|
||||
return {"status": "ok"}
|
||||
183
salesflow-saas/backend/app/services/comparison_engine.py
Normal file
183
salesflow-saas/backend/app/services/comparison_engine.py
Normal file
@ -0,0 +1,183 @@
|
||||
"""
|
||||
Comparison Engine — Dealix AI Revenue OS
|
||||
Competitive comparison data for charts, WhatsApp responses, and sales tools.
|
||||
"""
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Score scale: 0-10 per dimension
|
||||
COMPETITORS = {
|
||||
"dealix": {
|
||||
"name": "Dealix", "name_ar": "ديلكس",
|
||||
"scores": {
|
||||
"arabic_support": 10, "whatsapp_native": 10, "ai_scoring": 9,
|
||||
"pdpl_compliance": 10, "pricing_value": 9, "ease_of_use": 9,
|
||||
"saudi_market_fit": 10, "deal_exchange": 10, "strategic_deals": 10,
|
||||
"multi_channel": 9, "reporting": 8, "integrations": 7,
|
||||
},
|
||||
},
|
||||
"zoho": {
|
||||
"name": "Zoho CRM", "name_ar": "زوهو",
|
||||
"scores": {
|
||||
"arabic_support": 7, "whatsapp_native": 6, "ai_scoring": 6,
|
||||
"pdpl_compliance": 5, "pricing_value": 8, "ease_of_use": 7,
|
||||
"saudi_market_fit": 6, "deal_exchange": 2, "strategic_deals": 1,
|
||||
"multi_channel": 7, "reporting": 8, "integrations": 9,
|
||||
},
|
||||
},
|
||||
"salesforce": {
|
||||
"name": "Salesforce", "name_ar": "سيلزفورس",
|
||||
"scores": {
|
||||
"arabic_support": 3, "whatsapp_native": 2, "ai_scoring": 8,
|
||||
"pdpl_compliance": 4, "pricing_value": 3, "ease_of_use": 4,
|
||||
"saudi_market_fit": 4, "deal_exchange": 1, "strategic_deals": 2,
|
||||
"multi_channel": 7, "reporting": 10, "integrations": 10,
|
||||
},
|
||||
},
|
||||
"hubspot": {
|
||||
"name": "HubSpot", "name_ar": "هب سبوت",
|
||||
"scores": {
|
||||
"arabic_support": 2, "whatsapp_native": 3, "ai_scoring": 7,
|
||||
"pdpl_compliance": 3, "pricing_value": 5, "ease_of_use": 8,
|
||||
"saudi_market_fit": 3, "deal_exchange": 1, "strategic_deals": 1,
|
||||
"multi_channel": 8, "reporting": 8, "integrations": 9,
|
||||
},
|
||||
},
|
||||
"pipedrive": {
|
||||
"name": "Pipedrive", "name_ar": "بايب درايف",
|
||||
"scores": {
|
||||
"arabic_support": 2, "whatsapp_native": 1, "ai_scoring": 5,
|
||||
"pdpl_compliance": 2, "pricing_value": 7, "ease_of_use": 9,
|
||||
"saudi_market_fit": 2, "deal_exchange": 0, "strategic_deals": 0,
|
||||
"multi_channel": 4, "reporting": 6, "integrations": 6,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
DIMENSION_LABELS = {
|
||||
"arabic_support": {"ar": "دعم العربي", "en": "Arabic Support"},
|
||||
"whatsapp_native": {"ar": "واتساب مدمج", "en": "WhatsApp Native"},
|
||||
"ai_scoring": {"ar": "ذكاء اصطناعي", "en": "AI Scoring"},
|
||||
"pdpl_compliance": {"ar": "حماية البيانات", "en": "PDPL Compliance"},
|
||||
"pricing_value": {"ar": "القيمة مقابل السعر", "en": "Pricing Value"},
|
||||
"ease_of_use": {"ar": "سهولة الاستخدام", "en": "Ease of Use"},
|
||||
"saudi_market_fit": {"ar": "مناسب للسعودية", "en": "Saudi Market Fit"},
|
||||
"deal_exchange": {"ar": "تبادل صفقات", "en": "Deal Exchange"},
|
||||
"strategic_deals": {"ar": "صفقات استراتيجية", "en": "Strategic Deals"},
|
||||
"multi_channel": {"ar": "تعدد القنوات", "en": "Multi-Channel"},
|
||||
"reporting": {"ar": "التقارير", "en": "Reporting"},
|
||||
"integrations": {"ar": "التكاملات", "en": "Integrations"},
|
||||
}
|
||||
|
||||
|
||||
class ComparisonEngine:
|
||||
"""Generate comparison data for charts and sales responses."""
|
||||
|
||||
@staticmethod
|
||||
def get_chart_data(language: str = "ar") -> dict[str, Any]:
|
||||
"""Data formatted for radar/bar charts on frontend."""
|
||||
labels = [
|
||||
DIMENSION_LABELS[dim][language]
|
||||
for dim in DIMENSION_LABELS
|
||||
]
|
||||
datasets = []
|
||||
for key, comp in COMPETITORS.items():
|
||||
datasets.append({
|
||||
"label": comp[f"name_{language}" if f"name_{language}" in comp else "name"],
|
||||
"data": list(comp["scores"].values()),
|
||||
"highlight": key == "dealix",
|
||||
})
|
||||
return {"labels": labels, "datasets": datasets, "dimensions": list(DIMENSION_LABELS.keys())}
|
||||
|
||||
@staticmethod
|
||||
def get_feature_matrix(language: str = "ar") -> dict[str, Any]:
|
||||
"""Feature comparison table data."""
|
||||
features = [
|
||||
{"key": "arabic_first", "ar": "عربي أولاً (مو ترجمة)", "en": "Arabic-First (not translation)",
|
||||
"dealix": True, "zoho": False, "salesforce": False, "hubspot": False, "pipedrive": False},
|
||||
{"key": "whatsapp_built_in", "ar": "واتساب مدمج بالنظام", "en": "Built-in WhatsApp",
|
||||
"dealix": True, "zoho": False, "salesforce": False, "hubspot": False, "pipedrive": False},
|
||||
{"key": "ai_arabic", "ar": "AI يفهم العربي والسعودي", "en": "Arabic-Aware AI",
|
||||
"dealix": True, "zoho": False, "salesforce": False, "hubspot": False, "pipedrive": False},
|
||||
{"key": "pdpl_native", "ar": "PDPL مدمج", "en": "Built-in PDPL",
|
||||
"dealix": True, "zoho": False, "salesforce": False, "hubspot": False, "pipedrive": False},
|
||||
{"key": "deal_exchange", "ar": "صفقات استراتيجية وتبادل", "en": "Strategic Deal Exchange",
|
||||
"dealix": True, "zoho": False, "salesforce": False, "hubspot": False, "pipedrive": False},
|
||||
{"key": "lead_scoring", "ar": "تقييم عملاء ذكي", "en": "AI Lead Scoring",
|
||||
"dealix": True, "zoho": True, "salesforce": True, "hubspot": True, "pipedrive": True},
|
||||
{"key": "pipeline", "ar": "مسار صفقات بصري", "en": "Visual Pipeline",
|
||||
"dealix": True, "zoho": True, "salesforce": True, "hubspot": True, "pipedrive": True},
|
||||
{"key": "cpq", "ar": "عروض أسعار", "en": "Quotes (CPQ)",
|
||||
"dealix": True, "zoho": True, "salesforce": True, "hubspot": False, "pipedrive": False},
|
||||
]
|
||||
return {"features": features, "competitors": list(COMPETITORS.keys())}
|
||||
|
||||
@staticmethod
|
||||
def get_total_scores() -> dict[str, int]:
|
||||
"""Total score per competitor (out of 120)."""
|
||||
return {
|
||||
key: sum(comp["scores"].values())
|
||||
for key, comp in COMPETITORS.items()
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_why_dealix_wins(language: str = "ar") -> list[str]:
|
||||
"""Top reasons Dealix wins."""
|
||||
reasons = {
|
||||
"ar": [
|
||||
"الوحيد المصمم من الأساس للسوق السعودي",
|
||||
"واتساب مدمج — مو إضافة من طرف ثالث",
|
||||
"ذكاء اصطناعي يفهم اللهجة السعودية",
|
||||
"حماية بيانات PDPL مدمجة بالنظام",
|
||||
"نظام صفقات استراتيجية — لا يوجد عند أي منافس",
|
||||
"سعر يبدأ من ٥٩ ر.س — أرخص ١٠ مرات من Salesforce",
|
||||
"ثنائي اللغة (عربي/إنجليزي) بتبديل فوري",
|
||||
],
|
||||
"en": [
|
||||
"Only CRM built from scratch for the Saudi market",
|
||||
"Built-in WhatsApp — not a third-party add-on",
|
||||
"AI that understands Saudi Arabic dialect",
|
||||
"PDPL data protection built into the core",
|
||||
"Strategic Deal Exchange — no competitor has this",
|
||||
"Starting at 59 SAR — 10x cheaper than Salesforce",
|
||||
"Bilingual (Arabic/English) with instant switching",
|
||||
],
|
||||
}
|
||||
return reasons.get(language, reasons["ar"])
|
||||
|
||||
@staticmethod
|
||||
def get_comparison_summary(competitor: str, language: str = "ar") -> str:
|
||||
"""Summary comparing Dealix vs a specific competitor."""
|
||||
comp = COMPETITORS.get(competitor.lower())
|
||||
dealix = COMPETITORS["dealix"]
|
||||
if not comp:
|
||||
return "المنافس غير موجود" if language == "ar" else "Competitor not found"
|
||||
|
||||
dealix_total = sum(dealix["scores"].values())
|
||||
comp_total = sum(comp["scores"].values())
|
||||
diff = dealix_total - comp_total
|
||||
|
||||
if language == "ar":
|
||||
return (
|
||||
f"مقارنة Dealix مع {comp['name_ar']}:\n\n"
|
||||
f"النتيجة الإجمالية:\n"
|
||||
f"• Dealix: {dealix_total}/120\n"
|
||||
f"• {comp['name_ar']}: {comp_total}/120\n\n"
|
||||
f"Dealix يتفوق بـ {diff} نقطة.\n\n"
|
||||
f"أهم نقاط التفوق:\n"
|
||||
+ "\n".join(
|
||||
f"• {DIMENSION_LABELS[dim]['ar']}: Dealix {dealix['scores'][dim]} vs {comp['scores'][dim]}"
|
||||
for dim in dealix["scores"]
|
||||
if dealix["scores"][dim] > comp["scores"].get(dim, 0) + 2
|
||||
)
|
||||
)
|
||||
return (
|
||||
f"Dealix vs {comp['name']}:\n\n"
|
||||
f"Total Score:\n• Dealix: {dealix_total}/120\n• {comp['name']}: {comp_total}/120\n\n"
|
||||
f"Dealix leads by {diff} points."
|
||||
)
|
||||
|
||||
|
||||
comparison_engine = ComparisonEngine()
|
||||
342
salesflow-saas/backend/app/services/whatsapp_brain.py
Normal file
342
salesflow-saas/backend/app/services/whatsapp_brain.py
Normal file
@ -0,0 +1,342 @@
|
||||
"""
|
||||
WhatsApp AI Brain — Dealix AI Revenue OS
|
||||
Central intelligence for the Dealix WhatsApp number.
|
||||
Handles: sales, support, marketer support, deals, and general inquiries.
|
||||
Connected to backend data for contextual, intelligent responses.
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConversationMode(str, Enum):
|
||||
SALES = "sales"
|
||||
SUPPORT = "support"
|
||||
MARKETER = "marketer"
|
||||
DEALS = "deals"
|
||||
GENERAL = "general"
|
||||
|
||||
|
||||
class CallerProfile(BaseModel):
|
||||
phone: str
|
||||
name: str = "زائر"
|
||||
caller_type: str = "unknown" # client, marketer, lead, unknown
|
||||
tenant_id: str = ""
|
||||
subscription_plan: str = ""
|
||||
commission_balance: float = 0.0
|
||||
lead_score: int = 0
|
||||
language: str = "ar"
|
||||
|
||||
|
||||
class ConversationEntry(BaseModel):
|
||||
role: str # user, assistant
|
||||
content: str
|
||||
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
|
||||
|
||||
ARABIC_MARKERS = ["وش", "كيف", "أبي", "ليش", "هلا", "مرحبا", "السلام", "شكرا", "طيب"]
|
||||
INTENT_KEYWORDS = {
|
||||
"greeting": ["هلا", "مرحبا", "السلام عليكم", "أهلاً", "hi", "hello", "hey"],
|
||||
"pricing": ["سعر", "كم", "باقة", "اشتراك", "price", "cost", "plan", "pricing"],
|
||||
"demo": ["عرض", "demo", "تجربة", "شرح", "وريني"],
|
||||
"support": ["مشكلة", "ما يشتغل", "خطأ", "bug", "help", "مساعدة", "دعم"],
|
||||
"complaint": ["شكوى", "زعلان", "سيء", "complaint", "unhappy"],
|
||||
"partnership": ["شراكة", "partner", "تعاون", "صفقة", "deal"],
|
||||
"commission": ["عمولة", "commission", "أرباح", "دفعة", "payout"],
|
||||
"feature": ["ميزة", "feature", "يقدر", "يدعم", "فيه"],
|
||||
"competitor": ["zoho", "salesforce", "hubspot", "pipedrive", "منافس"],
|
||||
"cancel": ["إلغاء", "cancel", "أوقف", "stop"],
|
||||
}
|
||||
|
||||
|
||||
class WhatsAppBrain:
|
||||
"""Central brain for Dealix WhatsApp — routes and responds intelligently."""
|
||||
|
||||
def __init__(self):
|
||||
self._conversations: dict[str, list[ConversationEntry]] = {}
|
||||
from app.services.whatsapp_knowledge import DealixKnowledge
|
||||
self.knowledge = DealixKnowledge
|
||||
|
||||
async def handle_incoming(
|
||||
self, phone: str, message: str, db: Any = None
|
||||
) -> str:
|
||||
caller = await self.identify_caller(phone, db)
|
||||
language = self._detect_language(message)
|
||||
caller.language = language
|
||||
intent = self._detect_intent(message)
|
||||
history = self._get_history(phone)
|
||||
mode = self._route_conversation(intent, caller)
|
||||
|
||||
self._add_to_history(phone, "user", message)
|
||||
|
||||
handlers = {
|
||||
ConversationMode.SALES: self._handle_sales,
|
||||
ConversationMode.SUPPORT: self._handle_support,
|
||||
ConversationMode.MARKETER: self._handle_marketer,
|
||||
ConversationMode.DEALS: self._handle_deals,
|
||||
ConversationMode.GENERAL: self._handle_general,
|
||||
}
|
||||
handler = handlers.get(mode, self._handle_general)
|
||||
response = await handler(message, caller, intent, history, db)
|
||||
|
||||
self._add_to_history(phone, "assistant", response)
|
||||
logger.info(
|
||||
f"[WhatsAppBrain] {phone} mode={mode.value} intent={intent} "
|
||||
f"caller={caller.caller_type} lang={language}"
|
||||
)
|
||||
return response
|
||||
|
||||
async def identify_caller(self, phone: str, db: Any = None) -> CallerProfile:
|
||||
profile = CallerProfile(phone=phone)
|
||||
if not db:
|
||||
return profile
|
||||
try:
|
||||
from sqlalchemy import select, or_
|
||||
from app.models.lead import Lead
|
||||
from app.models.user import User
|
||||
from app.models.affiliate import AffiliateMarketer
|
||||
|
||||
clean_phone = phone.replace("+", "").replace(" ", "")
|
||||
|
||||
# Check if affiliate marketer
|
||||
result = await db.execute(
|
||||
select(AffiliateMarketer).where(
|
||||
AffiliateMarketer.phone.contains(clean_phone[-9:])
|
||||
).limit(1)
|
||||
)
|
||||
marketer = result.scalar_one_or_none()
|
||||
if marketer:
|
||||
profile.caller_type = "marketer"
|
||||
profile.name = marketer.full_name or "مسوّق"
|
||||
profile.tenant_id = str(marketer.tenant_id) if hasattr(marketer, 'tenant_id') else ""
|
||||
return profile
|
||||
|
||||
# Check if existing user/client
|
||||
result = await db.execute(
|
||||
select(User).where(User.phone.contains(clean_phone[-9:])).limit(1)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
if user:
|
||||
profile.caller_type = "client"
|
||||
profile.name = user.full_name or "عميل"
|
||||
profile.tenant_id = str(user.tenant_id) if hasattr(user, 'tenant_id') else ""
|
||||
return profile
|
||||
|
||||
# Check if known lead
|
||||
result = await db.execute(
|
||||
select(Lead).where(Lead.phone.contains(clean_phone[-9:])).limit(1)
|
||||
)
|
||||
lead = result.scalar_one_or_none()
|
||||
if lead:
|
||||
profile.caller_type = "lead"
|
||||
profile.name = lead.name or "عميل محتمل"
|
||||
profile.lead_score = lead.score or 0
|
||||
profile.tenant_id = str(lead.tenant_id) if hasattr(lead, 'tenant_id') else ""
|
||||
return profile
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error identifying caller {phone}: {e}")
|
||||
|
||||
return profile
|
||||
|
||||
def _detect_language(self, message: str) -> str:
|
||||
arabic_chars = len(re.findall(r'[\u0600-\u06FF]', message))
|
||||
latin_chars = len(re.findall(r'[a-zA-Z]', message))
|
||||
return "ar" if arabic_chars >= latin_chars else "en"
|
||||
|
||||
def _detect_intent(self, message: str) -> str:
|
||||
msg_lower = message.lower()
|
||||
for intent, keywords in INTENT_KEYWORDS.items():
|
||||
if any(kw in msg_lower for kw in keywords):
|
||||
return intent
|
||||
return "general"
|
||||
|
||||
def _route_conversation(self, intent: str, caller: CallerProfile) -> ConversationMode:
|
||||
if caller.caller_type == "marketer" or intent == "commission":
|
||||
return ConversationMode.MARKETER
|
||||
if caller.caller_type == "client" and intent in ("support", "complaint", "cancel"):
|
||||
return ConversationMode.SUPPORT
|
||||
if intent in ("partnership",):
|
||||
return ConversationMode.DEALS
|
||||
if intent in ("pricing", "demo", "feature", "competitor"):
|
||||
return ConversationMode.SALES
|
||||
if caller.caller_type == "client":
|
||||
return ConversationMode.SUPPORT
|
||||
return ConversationMode.SALES if caller.caller_type == "unknown" else ConversationMode.GENERAL
|
||||
|
||||
def _get_history(self, phone: str) -> list[ConversationEntry]:
|
||||
return self._conversations.get(phone, [])[-10:]
|
||||
|
||||
def _add_to_history(self, phone: str, role: str, content: str) -> None:
|
||||
if phone not in self._conversations:
|
||||
self._conversations[phone] = []
|
||||
self._conversations[phone].append(ConversationEntry(role=role, content=content))
|
||||
if len(self._conversations[phone]) > 50:
|
||||
self._conversations[phone] = self._conversations[phone][-50:]
|
||||
|
||||
async def _handle_sales(
|
||||
self, message: str, caller: CallerProfile, intent: str, history: list, db: Any
|
||||
) -> str:
|
||||
lang = caller.language
|
||||
|
||||
if intent == "greeting":
|
||||
name_part = f" {caller.name}" if caller.name != "زائر" else ""
|
||||
if lang == "ar":
|
||||
return (
|
||||
f"أهلاً وسهلاً{name_part}! 👋\n"
|
||||
f"أنا مساعد ديلكس الذكي.\n\n"
|
||||
f"أقدر أساعدك في:\n"
|
||||
f"• معرفة مميزات Dealix\n"
|
||||
f"• الأسعار والباقات\n"
|
||||
f"• حجز عرض توضيحي\n"
|
||||
f"• أي سؤال ثاني\n\n"
|
||||
f"كيف أقدر أساعدك؟"
|
||||
)
|
||||
return (
|
||||
f"Hello{name_part}! 👋\n"
|
||||
f"I'm the Dealix AI assistant.\n\n"
|
||||
f"I can help with:\n"
|
||||
f"• Dealix features\n• Pricing\n• Book a demo\n\nHow can I help?"
|
||||
)
|
||||
|
||||
if intent == "pricing":
|
||||
pricing_text = self.knowledge.get_pricing_text(lang)
|
||||
suffix = "\nكل الباقات فيها تجربة مجانية ١٤ يوم بدون بطاقة.\nتبي تجرب؟" if lang == "ar" else "\nAll plans include a 14-day free trial. Want to try?"
|
||||
return f"{pricing_text}\n{suffix}"
|
||||
|
||||
if intent == "demo":
|
||||
if lang == "ar":
|
||||
return (
|
||||
"ممتاز! يسعدنا نعرض لك Dealix 🎉\n\n"
|
||||
"العرض يستغرق ١٥ دقيقة فقط.\n"
|
||||
"أرسل لي اسمك ورقم جوالك وأرتب لك الموعد."
|
||||
)
|
||||
return "Great! We'd love to show you Dealix 🎉\nThe demo takes just 15 minutes.\nSend your name and phone, and I'll set it up."
|
||||
|
||||
if intent == "competitor":
|
||||
for comp in ["zoho", "salesforce", "hubspot"]:
|
||||
if comp in message.lower():
|
||||
resp = self.knowledge.get_competitor_response(comp)
|
||||
if resp:
|
||||
return resp
|
||||
if lang == "ar":
|
||||
return "Dealix الوحيد المصمم للسوق السعودي: عربي أولاً، واتساب مدمج، AI يفهم سعودي. تبي أوريك المقارنة؟"
|
||||
return "Dealix is the only CRM built for Saudi: Arabic-first, WhatsApp native, Saudi-aware AI. Want to see the comparison?"
|
||||
|
||||
if intent == "feature":
|
||||
for key, feat in self.knowledge.FEATURES.items():
|
||||
if any(word in message for word in feat["name_ar"].split()):
|
||||
points = "\n".join(f"✅ {p}" for p in feat["selling_points_ar"])
|
||||
return f"*{feat['name_ar']}*\n{feat['desc_ar']}\n\n{points}"
|
||||
|
||||
# Check objections
|
||||
for obj_type, obj_data in self.knowledge.OBJECTION_RESPONSES.items():
|
||||
triggers = {"expensive": ["غالي", "مكلف"], "need_to_think": ["أفكر", "بشوف"], "too_complex": ["صعب", "معقد"], "small_team": ["صغير", "وحدي"]}
|
||||
if obj_type in triggers and any(t in message for t in triggers[obj_type]):
|
||||
return obj_data.get(lang, obj_data["ar"])
|
||||
|
||||
# FAQ search
|
||||
faq_answer = self.knowledge.search_faq(message)
|
||||
if faq_answer:
|
||||
return faq_answer
|
||||
|
||||
if lang == "ar":
|
||||
return "شكراً لتواصلك! 🙏\nأقدر أساعدك بأي سؤال عن Dealix — الأسعار، المميزات، أو حجز عرض توضيحي.\nوش تحب تعرف؟"
|
||||
return "Thanks for reaching out! 🙏\nI can help with pricing, features, or booking a demo.\nWhat would you like to know?"
|
||||
|
||||
async def _handle_support(
|
||||
self, message: str, caller: CallerProfile, intent: str, history: list, db: Any
|
||||
) -> str:
|
||||
name = caller.name or "عميل"
|
||||
if intent == "complaint":
|
||||
return (
|
||||
f"أستاذ/ة {name}، نعتذر عن أي إزعاج 🙏\n"
|
||||
f"فريق الدعم المتخصص بيتواصل معك خلال ساعة.\n"
|
||||
f"لو تقدر توصف المشكلة بالتفصيل، بيساعدنا نحلها أسرع."
|
||||
)
|
||||
if intent == "cancel":
|
||||
return (
|
||||
f"أستاذ/ة {name}، نأسف إنك تفكر بالإلغاء 😔\n"
|
||||
f"قبل ما نلغي، ممكن أعرف السبب؟ يمكن نقدر نساعدك.\n"
|
||||
f"لو تبي، أقدر أحولك لمدير حسابك مباشرة."
|
||||
)
|
||||
return (
|
||||
f"أهلاً {name}! 👋\n"
|
||||
f"كيف أقدر أساعدك اليوم؟\n\n"
|
||||
f"لو عندك مشكلة تقنية، وصّف لي المشكلة وبأساعدك فوراً.\n"
|
||||
f"لو تحتاج شي ما أقدر أحله، بأحولك لفريق الدعم المتخصص."
|
||||
)
|
||||
|
||||
async def _handle_marketer(
|
||||
self, message: str, caller: CallerProfile, intent: str, history: list, db: Any
|
||||
) -> str:
|
||||
name = caller.name or "مسوّق"
|
||||
if intent == "commission":
|
||||
return (
|
||||
f"أهلاً {name}! 🌟\n\n"
|
||||
f"للاطلاع على عمولاتك وأدائك، ادخل لوحة التحكم من:\n"
|
||||
f"dealix.sa/dashboard\n\n"
|
||||
f"لو عندك سؤال عن العمولات أو المدفوعات، أنا هنا أساعدك."
|
||||
)
|
||||
|
||||
# Search marketer FAQ
|
||||
for faq in self.knowledge.MARKETER_FAQ:
|
||||
if any(word in message for word in faq["q_ar"].split() if len(word) > 2):
|
||||
return faq["a_ar"]
|
||||
|
||||
return (
|
||||
f"أهلاً {name}! مسوّقنا المميز 🌟\n\n"
|
||||
f"كيف أقدر أساعدك اليوم؟\n"
|
||||
f"• استفسار عن العمولات\n"
|
||||
f"• مساعدة تقنية\n"
|
||||
f"• نصائح للتسويق\n"
|
||||
f"• أي سؤال ثاني"
|
||||
)
|
||||
|
||||
async def _handle_deals(
|
||||
self, message: str, caller: CallerProfile, intent: str, history: list, db: Any
|
||||
) -> str:
|
||||
return (
|
||||
"أهلاً! 🤝\n\n"
|
||||
"Dealix يدعم ١٥ نوع صفقة استراتيجية:\n"
|
||||
"• شراكات وتبادل خدمات\n"
|
||||
"• توزيع وreseller\n"
|
||||
"• مشاريع مشتركة\n"
|
||||
"• فرص استحواذ\n\n"
|
||||
"حدثني أكثر عن شركتك ووش تبحث عنه، وبأساعدك نلقى أفضل فرصة."
|
||||
)
|
||||
|
||||
async def _handle_general(
|
||||
self, message: str, caller: CallerProfile, intent: str, history: list, db: Any
|
||||
) -> str:
|
||||
faq_answer = self.knowledge.search_faq(message)
|
||||
if faq_answer:
|
||||
return faq_answer
|
||||
lang = caller.language
|
||||
if lang == "ar":
|
||||
return (
|
||||
"أهلاً وسهلاً! 👋\n"
|
||||
"أنا مساعد ديلكس — نظام المبيعات الذكي للسعودية.\n\n"
|
||||
"أقدر أساعدك في:\n"
|
||||
"١. معرفة مميزات Dealix\n"
|
||||
"٢. الأسعار والباقات\n"
|
||||
"٣. حجز عرض توضيحي\n"
|
||||
"٤. الدعم الفني\n"
|
||||
"٥. برنامج التسويق بالعمولة\n\n"
|
||||
"أختر رقم أو اكتب سؤالك مباشرة."
|
||||
)
|
||||
return (
|
||||
"Hello! 👋\nI'm the Dealix assistant — the smart sales system for Saudi Arabia.\n\n"
|
||||
"I can help with:\n1. Features\n2. Pricing\n3. Book a demo\n4. Support\n5. Affiliate program\n\n"
|
||||
"Pick a number or type your question."
|
||||
)
|
||||
|
||||
|
||||
# Global singleton
|
||||
whatsapp_brain = WhatsAppBrain()
|
||||
203
salesflow-saas/backend/app/services/whatsapp_knowledge.py
Normal file
203
salesflow-saas/backend/app/services/whatsapp_knowledge.py
Normal file
@ -0,0 +1,203 @@
|
||||
"""
|
||||
WhatsApp Knowledge Base — Dealix AI Revenue OS
|
||||
Complete knowledge the WhatsApp brain uses to respond intelligently.
|
||||
"""
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DealixKnowledge:
|
||||
"""Everything the WhatsApp brain needs to know."""
|
||||
|
||||
FEATURES = {
|
||||
"whatsapp_crm": {
|
||||
"name_ar": "إدارة العملاء عبر الواتساب",
|
||||
"name_en": "WhatsApp CRM",
|
||||
"desc_ar": "تواصل مع عملاءك مباشرة من الواتساب مع تتبع كامل للمحادثات",
|
||||
"selling_points_ar": [
|
||||
"رد تلقائي ذكي بالعربي",
|
||||
"تتبع كل محادثة",
|
||||
"إشعارات فورية عند رد العميل",
|
||||
],
|
||||
},
|
||||
"ai_scoring": {
|
||||
"name_ar": "تقييم عملاء بالذكاء الاصطناعي",
|
||||
"name_en": "AI Lead Scoring",
|
||||
"desc_ar": "النظام يقيّم كل عميل من ٠ لـ ١٠٠ ويقولك مين الأهم",
|
||||
"selling_points_ar": [
|
||||
"تقييم تلقائي مع كل تفاعل",
|
||||
"يفهم المحادثات العربية",
|
||||
"توصيات متابعة بالعربي",
|
||||
],
|
||||
},
|
||||
"pipeline": {
|
||||
"name_ar": "مسار صفقات بصري",
|
||||
"name_en": "Visual Pipeline",
|
||||
"desc_ar": "شوف كل صفقاتك بنظرة واحدة وحركها بالسحب",
|
||||
"selling_points_ar": ["Kanban بصري", "٥ مراحل", "drag-and-drop"],
|
||||
},
|
||||
"cpq": {
|
||||
"name_ar": "عروض أسعار احترافية",
|
||||
"name_en": "Quotes & Proposals",
|
||||
"desc_ar": "أنشئ عروض أسعار بالعربي مع ضريبة القيمة المضافة تلقائياً",
|
||||
"selling_points_ar": ["ضريبة ١٥٪ تلقائي", "إرسال بالواتساب", "تتبع القبول"],
|
||||
},
|
||||
"pdpl": {
|
||||
"name_ar": "حماية البيانات PDPL",
|
||||
"name_en": "PDPL Compliance",
|
||||
"desc_ar": "متوافق مع نظام حماية البيانات الشخصية السعودي",
|
||||
"selling_points_ar": ["موافقات تلقائية", "حقوق بيانات", "audit trail"],
|
||||
},
|
||||
"deal_exchange": {
|
||||
"name_ar": "صفقات استراتيجية",
|
||||
"name_en": "Strategic Deals",
|
||||
"desc_ar": "اكتشف شركاء وصفقات متبادلة — تبادل خدمات، شراكات، توزيع",
|
||||
"selling_points_ar": ["١٥ نوع صفقة", "مطابقة ذكية", "مفاوض AI"],
|
||||
},
|
||||
}
|
||||
|
||||
PRICING = {
|
||||
"starter": {
|
||||
"name_ar": "المبتدئ",
|
||||
"price": 59,
|
||||
"features_ar": ["٣ مستخدمين", "٥٠٠ عميل", "واتساب أساسي", "تقارير أساسية"],
|
||||
"best_for_ar": "الشركات الصغيرة والفردية",
|
||||
},
|
||||
"professional": {
|
||||
"name_ar": "الاحترافي",
|
||||
"price": 149,
|
||||
"features_ar": [
|
||||
"١٠ مستخدمين", "عملاء لا محدود", "تقييم AI",
|
||||
"تسلسلات تلقائية", "عروض أسعار", "تقارير متقدمة",
|
||||
],
|
||||
"best_for_ar": "الشركات المتوسطة وفرق المبيعات",
|
||||
"popular": True,
|
||||
},
|
||||
"enterprise": {
|
||||
"name_ar": "المؤسسي",
|
||||
"price": 225,
|
||||
"features_ar": [
|
||||
"مستخدمين لا محدود", "وكيل مبيعات AI", "صفقات استراتيجية",
|
||||
"API كامل", "دعم مخصص",
|
||||
],
|
||||
"best_for_ar": "الشركات الكبيرة والمؤسسات",
|
||||
},
|
||||
}
|
||||
|
||||
OBJECTION_RESPONSES = {
|
||||
"expensive": {
|
||||
"ar": "أفهم — لكن ٥٩ ر.س أقل من فاتورة كابتشينو أسبوعية. وصفقة وحدة ضايعة بسبب عدم المتابعة تكلف أضعاف. جرّبه مجاناً ١٤ يوم وشوف بنفسك.",
|
||||
"en": "I understand — but 59 SAR is less than weekly cappuccinos. One lost deal due to poor follow-up costs much more. Try it free for 14 days.",
|
||||
},
|
||||
"already_have_crm": {
|
||||
"ar": "ممتاز! وش تستخدم حالياً؟ كثير من عملاءنا انتقلوا من أنظمة أجنبية لأن Dealix مصمم للسوق السعودي — عربي أولاً، واتساب مدمج، PDPL جاهز.",
|
||||
"en": "Great! What are you using? Many clients switched because Dealix is built for Saudi — Arabic-first, WhatsApp native, PDPL ready.",
|
||||
},
|
||||
"need_to_think": {
|
||||
"ar": "أكيد، خذ وقتك. بس حبيت أذكرك إن التجربة مجانية ١٤ يوم بدون بطاقة — تقدر تجرب وتقرر بعدها.",
|
||||
"en": "Sure, take your time. Just remember — 14-day free trial, no credit card needed.",
|
||||
},
|
||||
"too_complex": {
|
||||
"ar": "بالعكس! Dealix مصمم ليكون بسيط جداً — أغلب العملاء يبدون يستخدمونه بأقل من ٥ دقائق. وعندنا دعم بالعربي يساعدك.",
|
||||
"en": "Actually the opposite! Most clients start using it in under 5 minutes. And we have Arabic support.",
|
||||
},
|
||||
"small_team": {
|
||||
"ar": "حتى لو شخص واحد! باقة المبتدئ ٥٩ ر.س تكفي. والنظام يساعدك تتابع عملاءك بدون ما تحتاج فريق كبير.",
|
||||
"en": "Even for one person! Starter plan at 59 SAR is enough. The system helps you follow up without needing a big team.",
|
||||
},
|
||||
"no_budget": {
|
||||
"ar": "أفهم. التجربة مجانية ١٤ يوم — جربها وشوف كم صفقة تقدر تكسب. الاستثمار يرجع لك أضعاف.",
|
||||
"en": "I understand. 14-day free trial — try it and see how many deals you can win. The ROI speaks for itself.",
|
||||
},
|
||||
"competitor_better": {
|
||||
"ar": "كل نظام له مميزاته. لكن Dealix الوحيد المصمم للسعودية: عربي أولاً، واتساب مدمج، AI يفهم سعودي. تبي أوريك المقارنة؟",
|
||||
"en": "Every system has its strengths. But Dealix is the only one built for Saudi: Arabic-first, WhatsApp native, Saudi-aware AI. Want to see the comparison?",
|
||||
},
|
||||
"not_now": {
|
||||
"ar": "تمام! أقدر أرسل لك ملخص سريع عن Dealix وتشوفه لما يناسبك. وش إيميلك؟",
|
||||
"en": "No problem! I can send you a quick summary to review when it suits you. What's your email?",
|
||||
},
|
||||
}
|
||||
|
||||
COMPETITOR_CARDS = {
|
||||
"zoho": {
|
||||
"name": "Zoho CRM",
|
||||
"we_win": [
|
||||
"عربي أولاً (مو ترجمة)", "واتساب مدمج (مو إضافة)",
|
||||
"AI يفهم اللهجة السعودية", "PDPL مدمج بالنظام",
|
||||
"صفقات استراتيجية (لا يوجد عندهم)", "دعم سعودي مباشر",
|
||||
],
|
||||
"they_win": ["نظام أكبر وأقدم", "تكاملات أكثر", "سيرفرات سعودية"],
|
||||
"response_ar": "Zoho نظام ممتاز ومعروف. لكن الفرق إن Dealix مبني من الأساس للسوق السعودي — مو ترجمة لنظام أجنبي. واتساب عندنا مدمج، الذكاء الاصطناعي يفهم عربي، وPDPL جاهز. وبسعر مقارب.",
|
||||
},
|
||||
"salesforce": {
|
||||
"name": "Salesforce",
|
||||
"we_win": [
|
||||
"عربي بالكامل", "سعر أقل ١٠ مرات", "واتساب مدمج",
|
||||
"بسيط وسريع (مو ٦ أشهر تطبيق)", "PDPL جاهز",
|
||||
],
|
||||
"they_win": ["أكبر نظام CRM بالعالم", "آلاف التكاملات", "enterprise-grade"],
|
||||
"response_ar": "Salesforce نظام عملاق — لكن يحتاج ٦ أشهر تطبيق ومئات الآلاف. Dealix يشتغل بأقل من ٥ دقائق، عربي بالكامل، وبسعر يبدأ من ٥٩ ر.س. للشركات السعودية الصغيرة والمتوسطة، Dealix الخيار الأذكى.",
|
||||
},
|
||||
"hubspot": {
|
||||
"name": "HubSpot",
|
||||
"we_win": [
|
||||
"عربي أولاً", "واتساب مدمج", "AI عربي",
|
||||
"سعر أقل بكثير", "PDPL مدمج", "صفقات استراتيجية",
|
||||
],
|
||||
"they_win": ["marketing hub قوي", "content management", "brand أكبر"],
|
||||
"response_ar": "HubSpot ممتاز للتسويق الرقمي. لكن للمبيعات في السوق السعودي، Dealix أقوى: واتساب مدمج، AI يفهم عربي، وPDPL جاهز. وبسعر أقل بكثير.",
|
||||
},
|
||||
}
|
||||
|
||||
FAQ = [
|
||||
{"q_ar": "كم سعر Dealix؟", "a_ar": "يبدأ من ٥٩ ر.س/شهر. الاحترافي ١٤٩ ر.س، المؤسسي ٢٢٥ ر.س. وفيه تجربة مجانية ١٤ يوم."},
|
||||
{"q_ar": "هل يدعم الواتساب؟", "a_ar": "نعم! واتساب مدمج بالنظام — ترسل وتستقبل وتتابع كل المحادثات من مكان واحد."},
|
||||
{"q_ar": "هل يدعم العربي؟", "a_ar": "نعم! Dealix مبني عربي أولاً — الواجهة والتقارير والذكاء الاصطناعي كلها بالعربي."},
|
||||
{"q_ar": "هل هو آمن؟", "a_ar": "نعم. متوافق مع نظام حماية البيانات PDPL، تشفير SSL، وسيرفرات سعودية."},
|
||||
{"q_ar": "هل فيه تجربة مجانية؟", "a_ar": "نعم! ١٤ يوم تجربة مجانية كاملة — بدون بطاقة ائتمانية."},
|
||||
{"q_ar": "كيف أبدأ؟", "a_ar": "ادخل dealix.sa واضغط 'ابدأ مجاناً'. التسجيل يأخذ أقل من دقيقة."},
|
||||
{"q_ar": "هل يناسب شركتي الصغيرة؟", "a_ar": "أكيد! باقة المبتدئ ٥٩ ر.س مصممة للشركات الصغيرة. حتى لو شخص واحد."},
|
||||
{"q_ar": "هل يدعم الإنجليزي بعد؟", "a_ar": "نعم! تقدر تبدل بين العربي والإنجليزي بضغطة زر."},
|
||||
{"q_ar": "كيف أتواصل مع الدعم؟", "a_ar": "واتساب أو إيميل support@dealix.sa — نرد خلال ٤ ساعات عمل."},
|
||||
{"q_ar": "هل فيه تطبيق جوال؟", "a_ar": "الموقع متجاوب ويشتغل بشكل ممتاز على الجوال. تطبيق مخصص قريباً إن شاء الله."},
|
||||
]
|
||||
|
||||
MARKETER_FAQ = [
|
||||
{"q_ar": "كيف أسجل كمسوّق؟", "a_ar": "ادخل dealix.sa/marketers واضغط 'سجّل كمسوّق'. التسجيل مجاني ويتفعل فوراً."},
|
||||
{"q_ar": "كم العمولة؟", "a_ar": "تبدأ من ١٠٪ (برونزي) وتوصل ٢٠٪ (ذهبي). كل ما زاد عدد العملاء زادت نسبتك."},
|
||||
{"q_ar": "متى تنزل العمولة؟", "a_ar": "كل يوم أحد تتحول العمولات لحسابك البنكي."},
|
||||
{"q_ar": "كيف أتابع أدائي؟", "a_ar": "من لوحة التحكم تشوف كل شي: عملاء، عمولات، مستواك، وروابط التتبع."},
|
||||
{"q_ar": "هل فيه حد أقصى للعمولة؟", "a_ar": "لا! ما فيه حد — كل ما زاد عدد العملاء زادت عمولتك."},
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_pricing_text(cls, language: str = "ar") -> str:
|
||||
lines = []
|
||||
for key, plan in cls.PRICING.items():
|
||||
name = plan["name_ar"] if language == "ar" else key.title()
|
||||
price = plan["price"]
|
||||
features = " | ".join(plan["features_ar"][:3])
|
||||
popular = " ⭐" if plan.get("popular") else ""
|
||||
lines.append(f"{'🟢' if key == 'starter' else '🔵' if key == 'professional' else '🟣'} {name} — {price} ر.س/شهر{popular}\n {features}")
|
||||
return "\n\n".join(lines)
|
||||
|
||||
@classmethod
|
||||
def search_faq(cls, query: str) -> Optional[str]:
|
||||
query_lower = query.lower()
|
||||
for faq in cls.FAQ:
|
||||
if any(word in faq["q_ar"] for word in query_lower.split() if len(word) > 2):
|
||||
return faq["a_ar"]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_competitor_response(cls, competitor: str) -> Optional[str]:
|
||||
card = cls.COMPETITOR_CARDS.get(competitor.lower())
|
||||
return card["response_ar"] if card else None
|
||||
|
||||
@classmethod
|
||||
def get_objection_response(cls, objection_type: str, language: str = "ar") -> Optional[str]:
|
||||
obj = cls.OBJECTION_RESPONSES.get(objection_type)
|
||||
return obj[language] if obj else None
|
||||
Loading…
Reference in New Issue
Block a user