This commit is contained in:
parent
8726c931fa
commit
8c46d091ff
49 changed files with 1607 additions and 712 deletions
|
|
@ -8,6 +8,7 @@ addressed by another tenant.
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
import secrets
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
|
@ -101,11 +102,13 @@ def _quality_label(value: str | None) -> str | None:
|
|||
|
||||
|
||||
def _graph_error_message(data: Any, fallback: str) -> str:
|
||||
message = fallback
|
||||
if isinstance(data, dict):
|
||||
error = data.get("error")
|
||||
if isinstance(error, dict) and error.get("message"):
|
||||
return str(error["message"])[:500]
|
||||
return fallback
|
||||
message = str(error["message"])
|
||||
message = re.sub(r"(access_token|token|secret)=([^&\s]+)", r"\1=redacted", message, flags=re.I)
|
||||
return message[:500]
|
||||
|
||||
|
||||
async def _graph(
|
||||
|
|
@ -607,6 +610,78 @@ async def complete_signup(db: Session, user: UserAccount, payload: dict[str, Any
|
|||
return {"status": overall, "message": message, "accounts": [serialize_account(account) for account in accounts]}
|
||||
|
||||
|
||||
_META_ID = re.compile(r"^\d{6,32}$")
|
||||
|
||||
|
||||
def _clean_token(value: str) -> str:
|
||||
token = "".join(str(value or "").split())
|
||||
if token.lower().startswith("bearer"):
|
||||
token = token[6:]
|
||||
return token
|
||||
|
||||
|
||||
async def connect_with_token(
|
||||
db: Session,
|
||||
user: UserAccount,
|
||||
access_token: str,
|
||||
waba_id: str,
|
||||
phone_number_id: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Store a customer-supplied WhatsApp token and provision that account.
|
||||
|
||||
This is the non-embedded alternative to Embedded Signup. The token is
|
||||
kept on the connection row and is not returned to the browser.
|
||||
"""
|
||||
token = _clean_token(access_token)
|
||||
account_id = "".join(str(waba_id or "").split())
|
||||
phone_id = "".join(str(phone_number_id or "").split()) or None
|
||||
if len(token) < 20:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Paste the access token from Meta.")
|
||||
if not _META_ID.match(account_id):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Enter the WhatsApp Business account ID from Meta. It is a number.",
|
||||
)
|
||||
if phone_id and not _META_ID.match(phone_id):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Phone number ID should be the number Meta shows for that phone.",
|
||||
)
|
||||
|
||||
connection = WhatsAppMetaConnection(
|
||||
user_id=user.id,
|
||||
status="pending",
|
||||
granted_scopes="manual",
|
||||
access_token=token,
|
||||
)
|
||||
db.add(connection)
|
||||
db.flush()
|
||||
_audit(
|
||||
db,
|
||||
user,
|
||||
"token_saved",
|
||||
"Customer saved a WhatsApp access token",
|
||||
connection_id=connection.id,
|
||||
waba_id=account_id,
|
||||
)
|
||||
account = await _provision_waba(db, user, connection, token, account_id, phone_id)
|
||||
db.commit()
|
||||
db.refresh(account)
|
||||
if account.status == "connected":
|
||||
overall = "connected"
|
||||
message = "WhatsApp Business is connected."
|
||||
elif account.status == "incomplete":
|
||||
overall = "incomplete"
|
||||
message = account.last_error or "The account was saved, but the phone number still needs verification."
|
||||
elif account.status == "expired":
|
||||
overall = "expired"
|
||||
message = account.last_error or "Meta did not accept this access token."
|
||||
else:
|
||||
overall = "failed"
|
||||
message = account.last_error or "Urmi could not finish WhatsApp setup."
|
||||
return {"status": overall, "message": message, "accounts": [serialize_account(account)]}
|
||||
|
||||
|
||||
async def refresh_account(db: Session, user: UserAccount, account_id: Any) -> dict[str, Any]:
|
||||
account = _owned_account(db, user, account_id)
|
||||
connection = account.connection
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue