mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 07:19:35 +00:00
4 WhatsApp providers with automatic fallback: 1. Green API (green-api.com) — free dev tier, simplest setup 2. Ultramsg (ultramsg.com) — existing integration, cleaned 3. Fonnte (fonnte.com) — ultra-cheap alternative 4. Meta Cloud API (official) — most reliable, needs verification send_whatsapp_smart() tries each configured provider in order until one succeeds. No hardcoded credentials (removed leaked Ultramsg token from outreach_engine.py). New endpoints: - GET /os/whatsapp-providers — check which are configured - POST /os/test-send — test send via smart chain Full OS /os/process-and-act now uses smart multi-provider instead of Ultramsg-only. Env vars per provider: - GREEN_API_INSTANCE_ID + GREEN_API_TOKEN - ULTRAMSG_INSTANCE_ID + ULTRAMSG_TOKEN - FONNTE_TOKEN - WHATSAPP_API_TOKEN + WHATSAPP_PHONE_NUMBER_ID https://claude.ai/code/session_01W1rJthWDkasijTdXCfxVHs
184 lines
6.5 KiB
Python
184 lines
6.5 KiB
Python
"""Full OS API — unified deal lifecycle orchestration.
|
|
|
|
Single endpoint processes any event (inbound message, reply, booking,
|
|
payment) and returns: next stage, actions to take, response message,
|
|
and whether human approval is needed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from fastapi import APIRouter
|
|
from pydantic import BaseModel
|
|
|
|
router = APIRouter(prefix="/os", tags=["Full OS"])
|
|
|
|
|
|
class ProcessEventRequest(BaseModel):
|
|
lead_id: str = ""
|
|
phone: str = ""
|
|
email: str = ""
|
|
company: str = ""
|
|
sector: str = ""
|
|
source: str = "whatsapp_inbound"
|
|
message: str = ""
|
|
current_stage: str = "new_lead"
|
|
event_type: str = "inbound_message"
|
|
|
|
|
|
class BulkProcessRequest(BaseModel):
|
|
events: List[ProcessEventRequest]
|
|
|
|
|
|
@router.post("/process")
|
|
async def process_event(req: ProcessEventRequest) -> Dict[str, Any]:
|
|
"""Process a single event through the deal lifecycle state machine.
|
|
|
|
Returns: new_stage, actions, response_message_ar, human_approval_required.
|
|
If auto_send_allowed=True, the response can be sent automatically.
|
|
If human_approval_required=True, create a draft for Sami to review.
|
|
"""
|
|
from app.services.full_os_orchestrator import orchestrator, OrchestratorEvent
|
|
|
|
event = OrchestratorEvent(
|
|
lead_id=req.lead_id,
|
|
phone=req.phone,
|
|
email=req.email,
|
|
company=req.company,
|
|
sector=req.sector,
|
|
source=req.source,
|
|
message=req.message,
|
|
current_stage=req.current_stage,
|
|
event_type=req.event_type,
|
|
)
|
|
return orchestrator.process_event(event)
|
|
|
|
|
|
@router.post("/process-and-act")
|
|
async def process_and_act(req: ProcessEventRequest) -> Dict[str, Any]:
|
|
"""Process event AND execute the first safe action.
|
|
|
|
If auto_send_allowed: sends WhatsApp response via Ultramsg.
|
|
If human_approval_required: creates a draft for review.
|
|
Always logs the activity.
|
|
"""
|
|
from app.services.full_os_orchestrator import orchestrator, OrchestratorEvent
|
|
|
|
event = OrchestratorEvent(
|
|
lead_id=req.lead_id,
|
|
phone=req.phone,
|
|
email=req.email,
|
|
company=req.company,
|
|
sector=req.sector,
|
|
source=req.source,
|
|
message=req.message,
|
|
current_stage=req.current_stage,
|
|
event_type=req.event_type,
|
|
)
|
|
result = orchestrator.process_event(event)
|
|
|
|
execution = {"action_taken": "none", "send_result": None, "draft_created": False}
|
|
|
|
if result.get("auto_send_allowed") and result.get("response_message_ar") and req.phone:
|
|
if "send_whatsapp" in result.get("actions", []):
|
|
try:
|
|
from app.services.whatsapp_multi_provider import send_whatsapp_smart
|
|
send_result = await send_whatsapp_smart(req.phone, result["response_message_ar"])
|
|
execution = {
|
|
"action_taken": "whatsapp_sent",
|
|
"send_result": send_result,
|
|
"draft_created": False,
|
|
}
|
|
except Exception as exc:
|
|
execution = {
|
|
"action_taken": "whatsapp_failed",
|
|
"error": str(exc)[:200],
|
|
"draft_created": False,
|
|
}
|
|
|
|
elif result.get("human_approval_required") and result.get("response_message_ar"):
|
|
try:
|
|
from app.models.outreach_draft import OutreachDraft
|
|
from app.database import async_session
|
|
async with async_session() as session:
|
|
draft = OutreachDraft(
|
|
batch_id=f"os_{result['lead_id']}",
|
|
company=req.company,
|
|
contact_phone=req.phone,
|
|
contact_email=req.email,
|
|
channel="whatsapp" if req.phone else "email",
|
|
subject=f"[{result['new_stage']}] {req.company}",
|
|
body=result["response_message_ar"],
|
|
sector=req.sector,
|
|
status="draft",
|
|
approval_required=True,
|
|
source="full_os_orchestrator",
|
|
)
|
|
session.add(draft)
|
|
await session.commit()
|
|
execution = {
|
|
"action_taken": "draft_created",
|
|
"draft_id": str(draft.id),
|
|
"draft_created": True,
|
|
}
|
|
except Exception:
|
|
execution = {"action_taken": "draft_failed", "draft_created": False}
|
|
|
|
return {**result, "execution": execution}
|
|
|
|
|
|
@router.post("/bulk-process")
|
|
async def bulk_process(req: BulkProcessRequest) -> Dict[str, Any]:
|
|
"""Process multiple events at once (for batch imports)."""
|
|
from app.services.full_os_orchestrator import orchestrator, OrchestratorEvent
|
|
|
|
results = []
|
|
for event_req in req.events:
|
|
event = OrchestratorEvent(
|
|
lead_id=event_req.lead_id,
|
|
phone=event_req.phone,
|
|
email=event_req.email,
|
|
company=event_req.company,
|
|
sector=event_req.sector,
|
|
source=event_req.source,
|
|
message=event_req.message,
|
|
current_stage=event_req.current_stage,
|
|
event_type=event_req.event_type,
|
|
)
|
|
results.append(orchestrator.process_event(event))
|
|
|
|
return {
|
|
"processed": len(results),
|
|
"results": results,
|
|
}
|
|
|
|
|
|
@router.get("/whatsapp-providers")
|
|
async def whatsapp_provider_status() -> Dict[str, Any]:
|
|
"""Check which WhatsApp providers are configured."""
|
|
from app.services.whatsapp_multi_provider import check_providers
|
|
return await check_providers()
|
|
|
|
|
|
@router.post("/test-send")
|
|
async def test_whatsapp_send(phone: str, message: str = "اختبار Dealix — النظام شغّال 🚀") -> Dict[str, Any]:
|
|
"""Test WhatsApp send via all configured providers."""
|
|
from app.services.whatsapp_multi_provider import send_whatsapp_smart
|
|
return await send_whatsapp_smart(phone, message)
|
|
|
|
|
|
@router.get("/stages")
|
|
async def list_stages() -> Dict[str, Any]:
|
|
"""List all deal lifecycle stages with their possible transitions."""
|
|
from app.services.full_os_orchestrator import STAGE_TRANSITIONS, STAGE_AUTO_ACTIONS, STAGE_MESSAGES_AR
|
|
|
|
stages = {}
|
|
for stage, transitions in STAGE_TRANSITIONS.items():
|
|
stages[stage.value] = {
|
|
"transitions": {k: v.value for k, v in transitions.items()},
|
|
"auto_actions": [a.value for a in STAGE_AUTO_ACTIONS.get(stage, [])],
|
|
"message_template": STAGE_MESSAGES_AR.get(stage, ""),
|
|
}
|
|
return {"stages": stages, "total": len(stages)}
|