system-prompts-and-models-o.../salesflow-saas/backend/app/services/outbound_governance.py
2026-04-04 18:04:21 +03:00

79 lines
2.6 KiB
Python

"""
حوكمة الإرسال: عند تفعيلها في tenant.settings["governance"] لا يُرسل واتساب آلياً
بل يُنشأ طلب موافقة ويُسجَّل حدث نطاق.
"""
from __future__ import annotations
from typing import Any, Dict, Optional
from uuid import UUID
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.integrations.whatsapp import send_whatsapp_message
from app.models.operations import ApprovalRequest
from app.models.tenant import Tenant
from app.models.user import User
from app.services.operations_hub import emit_domain_event
def _governance_whatsapp_approval_required(settings: Optional[dict]) -> bool:
if not isinstance(settings, dict):
return False
gov = settings.get("governance") or {}
return bool(gov.get("whatsapp_outbound_requires_approval"))
async def send_whatsapp_with_governance(
db: AsyncSession,
*,
tenant_id: UUID,
phone: str,
message: str,
lead_id: UUID,
) -> Dict[str, Any]:
t_result = await db.execute(select(Tenant).where(Tenant.id == tenant_id))
tenant = t_result.scalar_one_or_none()
settings = tenant.settings if tenant and isinstance(tenant.settings, dict) else {}
if not _governance_whatsapp_approval_required(settings):
out = await send_whatsapp_message(phone, message)
return {"sent": True, "result": out}
u_result = await db.execute(
select(User)
.where(User.tenant_id == tenant_id, User.role.in_(["owner", "admin"]))
.order_by(User.created_at.asc())
.limit(1)
)
actor = u_result.scalar_one_or_none()
if not actor:
u_result = await db.execute(
select(User).where(User.tenant_id == tenant_id).order_by(User.created_at.asc()).limit(1)
)
actor = u_result.scalar_one_or_none()
if not actor:
out = await send_whatsapp_message(phone, message)
return {"sent": True, "result": out, "note": "no user for approval queue; sent anyway"}
row = ApprovalRequest(
tenant_id=tenant_id,
channel="whatsapp",
resource_type="lead",
resource_id=lead_id,
payload={"phone": phone, "message_preview": (message or "")[:2000], "kind": "ai_inbound_reply"},
status="pending",
requested_by_id=actor.id,
)
db.add(row)
await db.flush()
await emit_domain_event(
db,
tenant_id=tenant_id,
event_type="whatsapp.outbound.deferred_for_approval",
payload={"approval_id": str(row.id), "lead_id": str(lead_id)},
source="webhook",
)
return {"sent": False, "pending_approval": True, "approval_id": str(row.id)}