template
This commit is contained in:
parent
a7e6ed2e4b
commit
84544e1153
1 changed files with 83 additions and 34 deletions
|
|
@ -20,7 +20,6 @@ from .whatsapp_onboarding import (
|
||||||
_load_phones,
|
_load_phones,
|
||||||
_load_waba,
|
_load_waba,
|
||||||
_owned_account,
|
_owned_account,
|
||||||
_phone_ids_from_debug,
|
|
||||||
_quality_label,
|
_quality_label,
|
||||||
_token_app_id,
|
_token_app_id,
|
||||||
_waba_ids_from_debug,
|
_waba_ids_from_debug,
|
||||||
|
|
@ -527,30 +526,62 @@ def _wrong_template_node(exc: HTTPException) -> bool:
|
||||||
return "message_templates" in detail and "nonexisting field" in detail
|
return "message_templates" in detail and "nonexisting field" in detail
|
||||||
|
|
||||||
|
|
||||||
async def _correct_waba_id(db: Session, account: WhatsAppBusinessAccount, token: str) -> bool:
|
def _add_id(found: list[str], value: Any) -> None:
|
||||||
"""Replace a stored phone-number ID with the WABA ID from the embedded-signup token."""
|
text = str(value or "").strip()
|
||||||
|
if text and text not in found:
|
||||||
|
found.append(text)
|
||||||
|
|
||||||
|
|
||||||
|
async def _discover_waba_ids(db: Session, token: str, business_id: str | None) -> list[str]:
|
||||||
|
"""WABA IDs this token can manage. Debug target_ids are often empty on embedded signup."""
|
||||||
|
found: list[str] = []
|
||||||
try:
|
try:
|
||||||
debug = await _debug_token(db, token)
|
debug = await _debug_token(db, token)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
return False
|
debug = {}
|
||||||
wabas = _waba_ids_from_debug(debug)
|
for waba in _waba_ids_from_debug(debug):
|
||||||
phone_ids = set(_phone_ids_from_debug(debug))
|
_add_id(found, waba)
|
||||||
|
lookups: list[tuple[str, dict[str, str]]] = [
|
||||||
|
("me/assigned_whatsapp_business_accounts", {"fields": "id,name", "limit": "50"}),
|
||||||
|
]
|
||||||
|
if business_id:
|
||||||
|
for edge in ("owned_whatsapp_business_accounts", "client_whatsapp_business_accounts"):
|
||||||
|
lookups.append((f"{business_id}/{edge}", {"fields": "id,name", "limit": "50"}))
|
||||||
|
for path, params in lookups:
|
||||||
|
try:
|
||||||
|
data = await _graph("GET", path, token=token, params=params)
|
||||||
|
except HTTPException:
|
||||||
|
continue
|
||||||
|
rows = data.get("data") if isinstance(data.get("data"), list) else []
|
||||||
|
for row in rows:
|
||||||
|
if isinstance(row, dict):
|
||||||
|
_add_id(found, row.get("id"))
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
async def _waba_owning_phone(token: str, wabas: list[str], phone_id: str) -> str | None:
|
||||||
|
for waba in wabas:
|
||||||
|
if waba == phone_id:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
rows = await _load_phones(token, waba)
|
||||||
|
except HTTPException:
|
||||||
|
continue
|
||||||
|
if phone_id in {str(row.get("id")) for row in rows if row.get("id")}:
|
||||||
|
return waba
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def _correct_waba_id(db: Session, account: WhatsAppBusinessAccount, token: str, *, allow_only_waba: bool) -> str:
|
||||||
|
"""Point a phone-number ID at its WABA. Returns updated, covered, or no."""
|
||||||
stored = str(account.waba_id)
|
stored = str(account.waba_id)
|
||||||
if not wabas or stored in wabas:
|
business_id = account.connection.meta_business_id if account.connection else None
|
||||||
return False
|
wabas = [waba for waba in await _discover_waba_ids(db, token, business_id) if waba != stored]
|
||||||
real = wabas[0] if len(wabas) == 1 else None
|
real = await _waba_owning_phone(token, wabas, stored)
|
||||||
if real is None:
|
if real is None and allow_only_waba and len(wabas) == 1:
|
||||||
for waba in wabas:
|
real = wabas[0]
|
||||||
try:
|
if not real:
|
||||||
rows = await _load_phones(token, waba)
|
return "no"
|
||||||
except HTTPException:
|
|
||||||
continue
|
|
||||||
ids = {str(row.get("id")) for row in rows if row.get("id")}
|
|
||||||
if stored in ids or phone_ids.intersection(ids):
|
|
||||||
real = waba
|
|
||||||
break
|
|
||||||
if not real or real == stored:
|
|
||||||
return False
|
|
||||||
taken = db.scalar(
|
taken = db.scalar(
|
||||||
select(WhatsAppBusinessAccount.id).where(
|
select(WhatsAppBusinessAccount.id).where(
|
||||||
WhatsAppBusinessAccount.waba_id == real,
|
WhatsAppBusinessAccount.waba_id == real,
|
||||||
|
|
@ -558,7 +589,10 @@ async def _correct_waba_id(db: Session, account: WhatsAppBusinessAccount, token:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if taken is not None:
|
if taken is not None:
|
||||||
return False
|
owner = db.get(WhatsAppBusinessAccount, taken)
|
||||||
|
if owner is not None and owner.user_id == account.user_id:
|
||||||
|
return "covered"
|
||||||
|
return "no"
|
||||||
account.waba_id = real
|
account.waba_id = real
|
||||||
try:
|
try:
|
||||||
info = await _load_waba(token, real)
|
info = await _load_waba(token, real)
|
||||||
|
|
@ -575,7 +609,7 @@ async def _correct_waba_id(db: Session, account: WhatsAppBusinessAccount, token:
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
pass
|
pass
|
||||||
db.flush()
|
db.flush()
|
||||||
return True
|
return "updated"
|
||||||
|
|
||||||
|
|
||||||
async def sync_account_templates(db: Session, account: WhatsAppBusinessAccount) -> str | None:
|
async def sync_account_templates(db: Session, account: WhatsAppBusinessAccount) -> str | None:
|
||||||
|
|
@ -584,22 +618,37 @@ async def sync_account_templates(db: Session, account: WhatsAppBusinessAccount)
|
||||||
if account.status == "disconnected":
|
if account.status == "disconnected":
|
||||||
return None
|
return None
|
||||||
return "Reconnect this WhatsApp account to refresh its templates."
|
return "Reconnect this WhatsApp account to refresh its templates."
|
||||||
|
own_token = _stored_token(account.connection)
|
||||||
last_error: str | None = None
|
last_error: str | None = None
|
||||||
for index, token in enumerate(tokens):
|
for index, token in enumerate(tokens):
|
||||||
try:
|
try:
|
||||||
try:
|
seen = await _pull_templates(db, account, token)
|
||||||
seen = await _pull_templates(db, account, token)
|
|
||||||
except HTTPException as exc:
|
|
||||||
db.rollback()
|
|
||||||
if not _wrong_template_node(exc) or not await _correct_waba_id(db, account, token):
|
|
||||||
raise
|
|
||||||
seen = await _pull_templates(db, account, token)
|
|
||||||
except HTTPException as exc:
|
except HTTPException as exc:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
last_error = str(exc.detail)
|
if not _wrong_template_node(exc):
|
||||||
if _retry_with_another_token(exc) and index < len(tokens) - 1:
|
last_error = str(exc.detail)
|
||||||
continue
|
if _retry_with_another_token(exc) and index < len(tokens) - 1:
|
||||||
return last_error
|
continue
|
||||||
|
return last_error
|
||||||
|
try:
|
||||||
|
outcome = await _correct_waba_id(db, account, token, allow_only_waba=token == own_token)
|
||||||
|
except HTTPException:
|
||||||
|
outcome = "no"
|
||||||
|
if outcome == "covered":
|
||||||
|
return None
|
||||||
|
if outcome != "updated":
|
||||||
|
last_error = str(exc.detail)
|
||||||
|
if index < len(tokens) - 1:
|
||||||
|
continue
|
||||||
|
return last_error
|
||||||
|
try:
|
||||||
|
seen = await _pull_templates(db, account, token)
|
||||||
|
except HTTPException as retry_exc:
|
||||||
|
db.rollback()
|
||||||
|
last_error = str(retry_exc.detail)
|
||||||
|
if index < len(tokens) - 1:
|
||||||
|
continue
|
||||||
|
return last_error
|
||||||
stored = db.scalars(
|
stored = db.scalars(
|
||||||
select(WhatsAppMessageTemplate).where(WhatsAppMessageTemplate.waba_id == account.waba_id)
|
select(WhatsAppMessageTemplate).where(WhatsAppMessageTemplate.waba_id == account.waba_id)
|
||||||
).all()
|
).all()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue