Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
|
@@ -1,120 +1,107 @@
|
|
| 1 |
# main.py
|
| 2 |
from fastapi import FastAPI, Request, UploadFile
|
| 3 |
-
from typing import Any, Dict
|
| 4 |
from datetime import datetime
|
| 5 |
-
import json
|
|
|
|
| 6 |
|
| 7 |
app = FastAPI()
|
| 8 |
|
|
|
|
|
|
|
| 9 |
def print_human_friendly(payload: Dict[str, Any]) -> None:
|
| 10 |
-
"""
|
| 11 |
-
ูุทุจุน ุงูุฑูููุณุช ุจุดูู ู
ูุฑูุก ููููุฒุฑ + ุฅูู
ูุฌูุฒ
|
| 12 |
-
"""
|
| 13 |
-
# ุชุฑุชูุจ ุงูุนุฑุถ
|
| 14 |
order = [
|
| 15 |
-
("client",
|
| 16 |
-
("
|
| 17 |
-
("
|
| 18 |
-
("
|
| 19 |
-
("
|
| 20 |
-
("
|
| 21 |
-
("
|
| 22 |
-
("
|
| 23 |
-
("content_type", "๐ฆ Content Type"),
|
| 24 |
-
("fbp", "๐
ต fbp"),
|
| 25 |
-
("fbc", "๐
ต fbc"),
|
| 26 |
-
("ttclid", "๐ธ๏ธ ttclid"),
|
| 27 |
-
("ttp", "๐ช ttp"),
|
| 28 |
-
("scid", "๐ช scid"),
|
| 29 |
-
("x_email", "๐ง x_email (hashed)"),
|
| 30 |
-
("x_phone", "๐ฑ x_phone (hashed)"),
|
| 31 |
-
("x_fn", "๐ง First Name"),
|
| 32 |
-
("x_ln", "๐ง Last Name"),
|
| 33 |
-
("timestamp", "โฑ๏ธ Timestamp"),
|
| 34 |
-
("client_ua", "๐ฅ๏ธ Client UA"),
|
| 35 |
]
|
| 36 |
-
|
| 37 |
print("\n" + "-"*18 + " ๐ Human-friendly view " + "-"*18)
|
| 38 |
-
|
| 39 |
-
# ุฃุทุจุน ุงูุญููู ุงูุฃุณุงุณูุฉ
|
| 40 |
label_pad = 20
|
| 41 |
for key, label in order:
|
| 42 |
-
if key in payload and payload.get(key) not in (None, ""):
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
# ุฃุทุจุน ุงูุนูุงุตุฑ (contents) ุจุดูู ู
ูุณู
|
| 47 |
-
contents = payload.get("contents")
|
| 48 |
if isinstance(contents, list):
|
| 49 |
print("\n๐งบ Contents:")
|
| 50 |
for idx, item in enumerate(contents, start=1):
|
| 51 |
if isinstance(item, dict):
|
| 52 |
-
cid
|
| 53 |
-
|
| 54 |
-
price = item.get("price", "-")
|
| 55 |
-
qty = item.get("quantity", "-")
|
| 56 |
print(f" #{idx} โข ID: {cid} | Name: {name} | Price: {price} | Qty: {qty}")
|
| 57 |
else:
|
| 58 |
print(f" #{idx} โข {item}")
|
| 59 |
elif contents is not None:
|
| 60 |
-
# ูู ุญุงูุฉ ุฌุงุช ุณุชุฑูุฌ JSON ุฃู ููุฑู
ุงุช ู
ุฎุชูู
|
| 61 |
print("\n๐งบ Contents (raw):")
|
| 62 |
try:
|
| 63 |
-
parsed = contents if isinstance(contents,
|
| 64 |
print(json.dumps(parsed, ensure_ascii=False, indent=2))
|
| 65 |
except Exception:
|
| 66 |
print(str(contents))
|
| 67 |
-
|
| 68 |
print("-"*60 + "\n")
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
@app.post("/echo")
|
| 72 |
async def echo(request: Request) -> Dict[str, Any]:
|
| 73 |
body_bytes = await request.body()
|
| 74 |
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 75 |
|
| 76 |
-
# ููุฏุฑ ูุงุถุญ + ุงูููุช
|
| 77 |
print("\n" + "="*30 + f" ๐ NEW REQUEST ({now}) " + "="*30)
|
| 78 |
|
| 79 |
-
#
|
| 80 |
-
print("\n๐ Request body:")
|
| 81 |
raw_text = body_bytes.decode("utf-8", errors="ignore")
|
|
|
|
| 82 |
print(raw_text)
|
| 83 |
print("="*98)
|
| 84 |
|
| 85 |
-
#
|
| 86 |
parsed: Any
|
| 87 |
try:
|
| 88 |
parsed = await request.json()
|
| 89 |
except Exception:
|
| 90 |
try:
|
| 91 |
-
|
| 92 |
-
parsed = {}
|
| 93 |
-
for k, v in form.items():
|
| 94 |
-
if isinstance(v, UploadFile):
|
| 95 |
-
parsed[k] = {"filename": v.filename, "content_type": v.content_type}
|
| 96 |
-
else:
|
| 97 |
-
parsed[k] = v
|
| 98 |
except Exception:
|
| 99 |
-
|
| 100 |
-
try:
|
| 101 |
-
parsed = json.loads(raw_text)
|
| 102 |
-
except Exception:
|
| 103 |
-
parsed = raw_text
|
| 104 |
|
| 105 |
-
# โ
ุงูุทุจุงุนุฉ ุงูุฅุถุงููุฉ โุงููุฏูุฉ ููุนููโ (ูุง ุชุณุชุจุฏู ุทุจุงุนุฉ ุงูู JSON)
|
| 106 |
if isinstance(parsed, dict):
|
| 107 |
print_human_friendly(parsed)
|
| 108 |
else:
|
| 109 |
-
print("\n(โน๏ธ Human-friendly view skipped: payload is not
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
return {
|
| 112 |
-
"received": parsed,
|
|
|
|
|
|
|
| 113 |
"info": {
|
| 114 |
"method": request.method,
|
| 115 |
"url": str(request.url),
|
| 116 |
"headers": dict(request.headers),
|
| 117 |
},
|
| 118 |
}
|
| 119 |
-
|
| 120 |
-
# ุชุดุบูู: uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
|
|
|
| 1 |
# main.py
|
| 2 |
from fastapi import FastAPI, Request, UploadFile
|
| 3 |
+
from typing import Any, Dict, Tuple
|
| 4 |
from datetime import datetime
|
| 5 |
+
import json, os, asyncio
|
| 6 |
+
import httpx # pip install httpx
|
| 7 |
|
| 8 |
app = FastAPI()
|
| 9 |
|
| 10 |
+
GAS_WEBAPP_URL = os.getenv("GAS_WEBAPP_URL")
|
| 11 |
+
|
| 12 |
def print_human_friendly(payload: Dict[str, Any]) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
order = [
|
| 14 |
+
("client","๐ค Client"),("source","๐ Source"),("event_name","๐ฏ Event Name"),
|
| 15 |
+
("event_id","๐งพ Event ID"),("value","๐ฐ Value"),("currency","๐ฑ Currency"),
|
| 16 |
+
("transaction_id","๐ณ Transaction ID"),("content_id","๐ Content ID"),
|
| 17 |
+
("content_type","๐ฆ Content Type"),("fbp","๐
ต fbp"),("fbc","๐
ต fbc"),
|
| 18 |
+
("ttclid","๐ธ๏ธ ttclid"),("ttp","๐ช ttp"),("scid","๐ช scid"),
|
| 19 |
+
("x_email","๐ง x_email (hashed)"),("x_phone","๐ฑ x_phone (hashed)"),
|
| 20 |
+
("x_fn","๐ง First Name"),("x_ln","๐ง Last Name"),
|
| 21 |
+
("timestamp","โฑ๏ธ Timestamp"),("client_ua","๐ฅ๏ธ Client UA"),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
]
|
|
|
|
| 23 |
print("\n" + "-"*18 + " ๐ Human-friendly view " + "-"*18)
|
|
|
|
|
|
|
| 24 |
label_pad = 20
|
| 25 |
for key, label in order:
|
| 26 |
+
if isinstance(payload, dict) and key in payload and payload.get(key) not in (None, ""):
|
| 27 |
+
print(f"{label:<{label_pad}}: {payload.get(key)}")
|
| 28 |
+
contents = payload.get("contents") if isinstance(payload, dict) else None
|
|
|
|
|
|
|
|
|
|
| 29 |
if isinstance(contents, list):
|
| 30 |
print("\n๐งบ Contents:")
|
| 31 |
for idx, item in enumerate(contents, start=1):
|
| 32 |
if isinstance(item, dict):
|
| 33 |
+
cid=item.get("content_id","-"); name=item.get("content_name","-")
|
| 34 |
+
price=item.get("price","-"); qty=item.get("quantity","-")
|
|
|
|
|
|
|
| 35 |
print(f" #{idx} โข ID: {cid} | Name: {name} | Price: {price} | Qty: {qty}")
|
| 36 |
else:
|
| 37 |
print(f" #{idx} โข {item}")
|
| 38 |
elif contents is not None:
|
|
|
|
| 39 |
print("\n๐งบ Contents (raw):")
|
| 40 |
try:
|
| 41 |
+
parsed = contents if isinstance(contents,(dict,list)) else json.loads(str(contents))
|
| 42 |
print(json.dumps(parsed, ensure_ascii=False, indent=2))
|
| 43 |
except Exception:
|
| 44 |
print(str(contents))
|
|
|
|
| 45 |
print("-"*60 + "\n")
|
| 46 |
|
| 47 |
+
async def post_to_gas_raw(raw_body: str, content_type: str) -> Tuple[int, str]:
|
| 48 |
+
""" ูุฑุณู ุงูุจูุฏู ูู
ุง ูู (RAW) ุฅูู GAS ุจููุณ ููุน ุงูู
ุญุชูู """
|
| 49 |
+
if not GAS_WEBAPP_URL or GAS_WEBAPP_URL.startswith("YOUR_"):
|
| 50 |
+
return (0, "GAS_WEBAPP_URL is not configured")
|
| 51 |
+
headers = {"Content-Type": content_type or "application/json"}
|
| 52 |
+
timeout = httpx.Timeout(10.0, connect=5.0)
|
| 53 |
+
async with httpx.AsyncClient(timeout=timeout) as client:
|
| 54 |
+
for attempt in range(1, 3):
|
| 55 |
+
try:
|
| 56 |
+
r = await client.post(GAS_WEBAPP_URL, content=raw_body.encode("utf-8"), headers=headers)
|
| 57 |
+
return (r.status_code, r.text[:5000])
|
| 58 |
+
except Exception as e:
|
| 59 |
+
if attempt == 2:
|
| 60 |
+
return (0, f"Error posting to GAS: {e}")
|
| 61 |
+
await asyncio.sleep(0.5)
|
| 62 |
|
| 63 |
@app.post("/echo")
|
| 64 |
async def echo(request: Request) -> Dict[str, Any]:
|
| 65 |
body_bytes = await request.body()
|
| 66 |
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 67 |
|
|
|
|
| 68 |
print("\n" + "="*30 + f" ๐ NEW REQUEST ({now}) " + "="*30)
|
| 69 |
|
| 70 |
+
# ุงุทุจุน ุงูุจูุฏู ุงูุฎุงู
ูู
ุง ูู
|
|
|
|
| 71 |
raw_text = body_bytes.decode("utf-8", errors="ignore")
|
| 72 |
+
print("\n๐ Request body:")
|
| 73 |
print(raw_text)
|
| 74 |
print("="*98)
|
| 75 |
|
| 76 |
+
# ุญุงูู ูุทุจุนู ุจุดูู ูุฏูู ููุท ูู JSON object (ููุนุฑุถ ููุทุ ุจุฏูู ุชุนุฏูู ุงูุจูุงูุงุช)
|
| 77 |
parsed: Any
|
| 78 |
try:
|
| 79 |
parsed = await request.json()
|
| 80 |
except Exception:
|
| 81 |
try:
|
| 82 |
+
parsed = json.loads(raw_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
except Exception:
|
| 84 |
+
parsed = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
|
|
|
| 86 |
if isinstance(parsed, dict):
|
| 87 |
print_human_friendly(parsed)
|
| 88 |
else:
|
| 89 |
+
print("\n(โน๏ธ Human-friendly view skipped: payload is not a JSON object)\n")
|
| 90 |
+
|
| 91 |
+
# ๐ ุงุจุนุช ููู GAS ูู
ุง ูู (RAW) ุจููุณ content-type ุงููู ุฌู
|
| 92 |
+
ct = request.headers.get("content-type", "application/json")
|
| 93 |
+
gas_status, gas_body = await post_to_gas_raw(raw_text, ct)
|
| 94 |
+
print(f"๐ค GAS post status: {gas_status}")
|
| 95 |
+
if gas_body:
|
| 96 |
+
print(f"๐ GAS response: {gas_body[:1000]}")
|
| 97 |
|
| 98 |
return {
|
| 99 |
+
"received": parsed if isinstance(parsed, dict) else raw_text,
|
| 100 |
+
"forwarded_to_gas": True,
|
| 101 |
+
"gas_status": gas_status,
|
| 102 |
"info": {
|
| 103 |
"method": request.method,
|
| 104 |
"url": str(request.url),
|
| 105 |
"headers": dict(request.headers),
|
| 106 |
},
|
| 107 |
}
|
|
|
|
|
|