Unignore site/ (was blocked by mkdocs /site rule), add compose/Docker/uv tooling, and split deploys so push to main goes to beta while prod stays manual.
44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
"""Meta Graph API connector (Facebook Page + Instagram Business)."""
|
|
|
|
import logging
|
|
|
|
import requests
|
|
from django.conf import settings
|
|
|
|
from social.crypto import decrypt_tokens
|
|
import json
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MetaConnector:
|
|
platform = "meta"
|
|
GRAPH = "https://graph.facebook.com/v21.0"
|
|
|
|
def publish(self, post, target) -> str:
|
|
tokens = json.loads(decrypt_tokens(target.account.encrypted_tokens) or "{}")
|
|
access_token = tokens.get("access_token")
|
|
page_id = target.account.external_id or tokens.get("page_id")
|
|
if not access_token or not page_id:
|
|
raise RuntimeError("Meta account missing access_token/page_id")
|
|
|
|
if target.platform == "instagram":
|
|
# IG content publishing is a multi-step Graph flow; stub container create.
|
|
raise NotImplementedError(
|
|
"Instagram publish requires IG business account wiring — complete OAuth first"
|
|
)
|
|
|
|
response = requests.post(
|
|
f"{self.GRAPH}/{page_id}/feed",
|
|
data={"message": post.body, "access_token": access_token},
|
|
timeout=30,
|
|
)
|
|
response.raise_for_status()
|
|
return str(response.json().get("id") or "")
|
|
|
|
def refresh_token(self, account) -> None:
|
|
# Long-lived token exchange when META_APP_ID/SECRET are set.
|
|
if not settings.META_APP_ID or not settings.META_APP_SECRET:
|
|
return
|
|
logger.info("Meta token refresh not yet implemented for account %s", account.pk)
|