Spaces:
Running
Running
| # main.py | |
| from fastapi import FastAPI, Request, UploadFile | |
| from typing import Any, Dict, Tuple | |
| from datetime import datetime | |
| import json, os, asyncio | |
| import httpx # pip install httpx | |
| app = FastAPI() | |
| GAS_WEBAPP_URL = os.getenv("GAS_WEBAPP_URL") | |
| def print_human_friendly(payload: Dict[str, Any]) -> None: | |
| order = [ | |
| ("client","๐ค Client"),("source","๐ Source"),("event_name","๐ฏ Event Name"), | |
| ("event_id","๐งพ Event ID"),("value","๐ฐ Value"),("currency","๐ฑ Currency"), | |
| ("transaction_id","๐ณ Transaction ID"),("content_id","๐ Content ID"), | |
| ("content_type","๐ฆ Content Type"),("fbp","๐ ต fbp"),("fbc","๐ ต fbc"), | |
| ("ttclid","๐ธ๏ธ ttclid"),("ttp","๐ช ttp"),("scid","๐ช scid"), | |
| ("x_email","๐ง x_email (hashed)"),("x_phone","๐ฑ x_phone (hashed)"), | |
| ("x_fn","๐ง First Name"),("x_ln","๐ง Last Name"), | |
| ("timestamp","โฑ๏ธ Timestamp"),("client_ua","๐ฅ๏ธ Client UA"), | |
| ("user_info","๐ค User Info"), ("cart_data","๐ Cart Data"), | |
| ] | |
| print("\n" + "-"*18 + " ๐ Human-friendly view " + "-"*18) | |
| label_pad = 20 | |
| for key, label in order: | |
| if isinstance(payload, dict) and key in payload and payload.get(key) not in (None, ""): | |
| print(f"{label:<{label_pad}}: {payload.get(key)}") | |
| contents = payload.get("contents") if isinstance(payload, dict) else None | |
| if isinstance(contents, list): | |
| print("\n๐งบ Contents:") | |
| for idx, item in enumerate(contents, start=1): | |
| if isinstance(item, dict): | |
| cid = item.get("content_id","-") | |
| name = item.get("content_name","-") | |
| price = item.get("price","-") | |
| qty = item.get("quantity","-") | |
| print(f" #{idx} โข ID: {cid} | Name: {name} | Price: {price} | Qty: {qty}") | |
| else: | |
| print(f" #{idx} โข {item}") | |
| elif contents is not None: | |
| print("\n๐งบ Contents (raw):") | |
| try: | |
| parsed = contents if isinstance(contents,(dict,list)) else json.loads(str(contents)) | |
| print(json.dumps(parsed, ensure_ascii=False, indent=2)) | |
| except Exception: | |
| print(str(contents)) | |
| # ๐ค User Info | |
| ui = payload.get("user_info") if isinstance(payload, dict) else None | |
| if ui is not None: | |
| print("\n๐ค User Info:") | |
| try: | |
| print(json.dumps(ui, ensure_ascii=False, indent=2)) | |
| except Exception: | |
| print(str(ui)) | |
| # ๐ Cart Data | |
| cd = payload.get("cart_data") if isinstance(payload, dict) else None | |
| if cd is not None: | |
| print("\n๐ Cart Data:") | |
| try: | |
| print(json.dumps(cd, ensure_ascii=False, indent=2)) | |
| except Exception: | |
| print(str(cd)) | |
| print("-"*60 + "\n") | |
| async def post_to_gas_raw(raw_body: str, content_type: str) -> Tuple[int, str]: | |
| if not GAS_WEBAPP_URL or GAS_WEBAPP_URL.startswith("YOUR_"): | |
| return (0, "GAS_WEBAPP_URL is not configured") | |
| headers = {"Content-Type": content_type or "application/json"} | |
| timeout = httpx.Timeout(10.0, connect=5.0) | |
| async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client: | |
| for attempt in range(1, 3): | |
| try: | |
| r = await client.post(GAS_WEBAPP_URL, content=raw_body.encode("utf-8"), headers=headers) | |
| # ูู ูุงู 302/303 ูุงุชุจุนุช ุงูุชุญูููุ ููุจูู status 200 ูู ุงูุขุฎุฑ | |
| # ุจุณ ูู ูุณู 302 ูุฃู ุณุจุจุ ุงุนุชุจุฑู ูุฌุงุญ ูุณุฌูู Location | |
| if r.status_code in (301, 302, 303, 307, 308): | |
| loc = r.headers.get("Location", "") | |
| return (r.status_code, f"Redirected to: {loc}") | |
| return (r.status_code, r.text[:5000]) | |
| except Exception as e: | |
| if attempt == 2: | |
| return (0, f"Error posting to GAS: {e}") | |
| await asyncio.sleep(0.5) | |
| async def ping(): | |
| return {"message": "pong"} | |
| async def echo(request: Request) -> Dict[str, Any]: | |
| body_bytes = await request.body() | |
| now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| print("\n" + "="*30 + f" ๐ NEW REQUEST ({now}) " + "="*30) | |
| # ุงุทุจุน ุงูุจูุฏู ุงูุฎุงู ูู ุง ูู | |
| raw_text = body_bytes.decode("utf-8", errors="ignore") | |
| # print("\n๐ Request body:") | |
| # print(raw_text) | |
| # print("="*98) | |
| # ุญุงูู ูุทุจุนู ุจุดูู ูุฏูู ููุท ูู JSON object (ููุนุฑุถ ููุทุ ุจุฏูู ุชุนุฏูู ุงูุจูุงูุงุช) | |
| parsed: Any | |
| try: | |
| parsed = await request.json() | |
| except Exception: | |
| try: | |
| parsed = json.loads(raw_text) | |
| except Exception: | |
| parsed = None | |
| if isinstance(parsed, dict): | |
| print_human_friendly(parsed) | |
| else: | |
| print("\n(โน๏ธ Human-friendly view skipped: payload is not a JSON object)\n") | |
| # ๐ ุงุจุนุช ููู GAS ูู ุง ูู (RAW) ุจููุณ content-type ุงููู ุฌู | |
| ct = request.headers.get("content-type", "application/json") | |
| gas_status, gas_body = await post_to_gas_raw(raw_text, ct) | |
| print(f"๐ค GAS post status: {gas_status}") | |
| # if gas_body: | |
| # print(f"๐ GAS response: {gas_body[:1000]}") | |
| return { | |
| "received": parsed if isinstance(parsed, dict) else raw_text, | |
| "forwarded_to_gas": True, | |
| "gas_status": gas_status, | |
| "info": { | |
| "method": request.method, | |
| "url": str(request.url), | |
| "headers": dict(request.headers), | |
| }, | |
| } | |