88 lines
3.2 KiB
Python
88 lines
3.2 KiB
Python
import os, requests
|
|
from typing import Any, Dict, List, Tuple, Optional
|
|
from utils.boxapi_log import log_boxapi_response
|
|
|
|
BOXAPI_USERNAME = os.environ.get("BOXAPI_USERNAME", "")
|
|
BOXAPI_PASSWORD = os.environ.get("BOXAPI_PASSWORD", "")
|
|
# Your .env can override to /get_media; default matches your cURL (/get_media_by_username)
|
|
BOXAPI_ENDPOINT = os.environ.get(
|
|
"BOXAPI_ENDPOINT",
|
|
"https://boxapi.ir/api/instagram/user/get_media_by_username"
|
|
)
|
|
|
|
class BoxAPIError(Exception):
|
|
pass
|
|
|
|
def get_media(username: str, count: int = 10) -> Tuple[List[Dict[str, Any]], str]:
|
|
"""
|
|
Calls BoxAPI and returns (items, log_path).
|
|
"""
|
|
if not BOXAPI_USERNAME or not BOXAPI_PASSWORD:
|
|
raise BoxAPIError("Missing BOXAPI_USERNAME/BOXAPI_PASSWORD in environment.")
|
|
|
|
payload = {"username": username, "count": count}
|
|
resp = None
|
|
resp_json: Optional[Dict[str, Any]] = None
|
|
resp_text: Optional[str] = None
|
|
status_code: Optional[int] = None
|
|
|
|
try:
|
|
resp = requests.post(
|
|
BOXAPI_ENDPOINT,
|
|
json=payload,
|
|
auth=(BOXAPI_USERNAME, BOXAPI_PASSWORD),
|
|
timeout=60,
|
|
)
|
|
status_code = resp.status_code
|
|
resp_text = resp.text
|
|
# Try to parse JSON; if it fails, we'll log text and raise
|
|
try:
|
|
resp_json = resp.json()
|
|
except Exception:
|
|
log_path = log_boxapi_response(
|
|
username, payload, status_code, response_text=resp_text,
|
|
note="Non-JSON response"
|
|
)
|
|
raise BoxAPIError(f"BoxAPI non-JSON response. See log: {log_path}")
|
|
|
|
# Expected shape: status -> response -> status_code + body.items
|
|
top_status = resp_json.get("status")
|
|
inner = resp_json.get("response") or {}
|
|
inner_code = inner.get("status_code")
|
|
body = inner.get("body") or {}
|
|
items = body.get("items") or []
|
|
|
|
log_path = log_boxapi_response(
|
|
username, payload, inner_code, response_json=resp_json, note="OK"
|
|
)
|
|
|
|
if top_status != "done" or inner_code != 200 or not isinstance(items, list):
|
|
# Include a concise snapshot of keys to help debugging
|
|
snapshot = {
|
|
"top_status": top_status,
|
|
"inner_code": inner_code,
|
|
"has_body": isinstance(body, dict),
|
|
"items_type": str(type(items)),
|
|
"items_len": len(items) if isinstance(items, list) else None,
|
|
}
|
|
raise BoxAPIError(f"Unexpected response format from BoxAPI ({snapshot}). See log: {log_path}")
|
|
|
|
return items, log_path
|
|
|
|
except BoxAPIError:
|
|
raise
|
|
except Exception as e:
|
|
log_path = log_boxapi_response(
|
|
username, payload, status_code, response_json=resp_json, response_text=resp_text,
|
|
note=f"HTTP/parse error: {e}"
|
|
)
|
|
raise BoxAPIError(f"BoxAPI request failed ({e}). See log: {log_path}")
|
|
|
|
# Legacy function for backward compatibility
|
|
def fetch_boxapi_posts(username: str, count: int = 30) -> List[Dict]:
|
|
try:
|
|
items, _ = get_media(username, count)
|
|
return items
|
|
except BoxAPIError as e:
|
|
print(f"❌ Failed to fetch posts from BoxAPI: {e}")
|
|
return [] |