token
This commit is contained in:
parent
9dbda69db1
commit
a2b076e9a1
6 changed files with 122 additions and 8 deletions
|
|
@ -283,6 +283,47 @@ async def _subscribe(token: str, waba_id: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def _customer_callback() -> dict[str, str] | None:
|
||||
"""Public address where a customer's own Meta app should deliver messages."""
|
||||
url = settings.whatsapp_webhook_url
|
||||
verify = settings.whatsapp_verify_token.strip()
|
||||
if not url.startswith("https://") or not verify or len(url) > 200:
|
||||
return None
|
||||
return {"override_callback_uri": url, "verify_token": verify}
|
||||
|
||||
|
||||
async def _token_app_id(token: str) -> str | None:
|
||||
try:
|
||||
data = await _graph("GET", "app", token=token, params={"fields": "id"})
|
||||
except HTTPException:
|
||||
return None
|
||||
app_id = str(data.get("id") or "").strip()
|
||||
return app_id or None
|
||||
|
||||
|
||||
async def _attach_customer_webhook(token: str, waba_id: str, phone_ids: list[str]) -> bool:
|
||||
"""Send this account's messages to the workspace, using the customer's token."""
|
||||
callback = _customer_callback()
|
||||
if callback is None:
|
||||
return False
|
||||
try:
|
||||
await _graph("POST", f"{waba_id}/subscribed_apps", token=token, json_body=callback)
|
||||
except HTTPException:
|
||||
logger.warning("WhatsApp webhook override failed for waba %s", waba_id[-4:])
|
||||
return False
|
||||
for phone_id in phone_ids:
|
||||
try:
|
||||
await _graph(
|
||||
"POST",
|
||||
phone_id,
|
||||
token=token,
|
||||
json_body={"webhook_configuration": callback},
|
||||
)
|
||||
except HTTPException:
|
||||
logger.info("Phone webhook override failed for %s", phone_id[-4:])
|
||||
return True
|
||||
|
||||
|
||||
async def _template_count(token: str, waba_id: str) -> tuple[int, bool]:
|
||||
try:
|
||||
data = await _graph(
|
||||
|
|
@ -732,7 +773,12 @@ async def _save_token_account(
|
|||
if previous_connection_id and previous_connection_id != connection.id:
|
||||
previous = db.get(WhatsAppMetaConnection, previous_connection_id)
|
||||
if previous is not None and previous.user_id == user.id:
|
||||
if not connection.meta_app_secret and previous.meta_app_secret:
|
||||
connection.meta_app_secret = previous.meta_app_secret
|
||||
if not connection.meta_app_id and previous.meta_app_id:
|
||||
connection.meta_app_id = previous.meta_app_id
|
||||
previous.access_token = None
|
||||
previous.meta_app_secret = None
|
||||
account.name = name
|
||||
account.last_error = None
|
||||
account.management_status = "active"
|
||||
|
|
@ -740,14 +786,35 @@ async def _save_token_account(
|
|||
phones = await _phones_for_token(token, waba_id, phone_id)
|
||||
if phones:
|
||||
_apply_phones(db, user, account, phones, phone_id)
|
||||
subscribed = await _subscribe(token, waba_id)
|
||||
app_id = await _token_app_id(token)
|
||||
if app_id:
|
||||
connection.meta_app_id = app_id
|
||||
platform_app = settings.meta_app_id.strip()
|
||||
same_app = bool(app_id and platform_app and app_id == platform_app)
|
||||
phone_ids = [phone.phone_number_id for phone in account.phones]
|
||||
if same_app or connection.meta_app_secret:
|
||||
receiving = await _attach_customer_webhook(token, waba_id, phone_ids)
|
||||
if not receiving and same_app:
|
||||
receiving = await _subscribe(token, waba_id)
|
||||
else:
|
||||
await _subscribe(token, waba_id)
|
||||
receiving = False
|
||||
template_count, templates_ok = await _template_count(token, waba_id)
|
||||
account.template_count = template_count
|
||||
account.templates_status = "active" if templates_ok else "unavailable"
|
||||
account.webhooks_status = "active" if subscribed else "unavailable"
|
||||
account.webhooks_status = "active" if receiving else "unavailable"
|
||||
if account.phones:
|
||||
account.status = "connected"
|
||||
account.messaging_status = "active"
|
||||
if receiving:
|
||||
account.last_error = None
|
||||
elif same_app:
|
||||
account.last_error = "Sending is on. Meta did not accept the delivery address for replies."
|
||||
else:
|
||||
account.last_error = (
|
||||
"Sending uses this token. Save it again with the App Secret from the Meta app that created the token "
|
||||
"so replies arrive in this workspace."
|
||||
)
|
||||
_audit(db, user, "token_connected", "WhatsApp token saved for this account", connection_id=connection.id, waba_id=waba_id)
|
||||
else:
|
||||
account.status = "incomplete"
|
||||
|
|
@ -763,6 +830,7 @@ async def connect_with_token(
|
|||
access_token: str,
|
||||
waba_id: str,
|
||||
phone_number_id: str | None = None,
|
||||
app_secret: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Store a customer-supplied WhatsApp token. Does not use Embedded Signup."""
|
||||
token = _clean_token(access_token)
|
||||
|
|
@ -780,12 +848,19 @@ async def connect_with_token(
|
|||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Phone number ID should be the number Meta shows for that phone.",
|
||||
)
|
||||
secret = "".join(str(app_secret or "").split())
|
||||
if secret and len(secret) < 8:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="App secret should be the value from the Meta app that created this token.",
|
||||
)
|
||||
|
||||
connection = WhatsAppMetaConnection(
|
||||
user_id=user.id,
|
||||
status="pending",
|
||||
granted_scopes="manual",
|
||||
access_token=token,
|
||||
meta_app_secret=secret or None,
|
||||
)
|
||||
db.add(connection)
|
||||
db.flush()
|
||||
|
|
@ -802,7 +877,7 @@ async def connect_with_token(
|
|||
db.refresh(account)
|
||||
if account.status == "connected":
|
||||
overall = "connected"
|
||||
message = "This WhatsApp account is saved for the workspace."
|
||||
message = account.last_error or "This WhatsApp account is saved. Messages send and arrive in this workspace."
|
||||
elif account.status == "incomplete":
|
||||
overall = "incomplete"
|
||||
message = account.last_error or "The token is saved. Add the phone number ID to finish."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue