Files
rdv/pyapi/main.py
2026-01-01 20:11:36 +02:00

372 lines
12 KiB
Python

import logging
import time
from typing import Dict, List, Optional
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse, Response
from pydantic import BaseModel
from scrapling.fetchers import StealthySession
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
app = FastAPI()
logger.info("FastAPI Proxy Server initialized")
CONSTANTS = {
"SESSION_KEY_NAME": "SID",
"SESSION_EXPIRE_TIME_MS": 6 * 60 * 60 * 1000,
"POST_SESSION_KEY": "postsession",
"LAST_FETCHED_KEY": "LAST_FETCHED",
"SCRAP_API_URL": "https://gamebooking24.com/lottery-api",
"SCRAP_API_SESSION_KEY": "SRAJWT",
"SCRAP_API_BASE_HEADERS": {
"Host": "gamebooking24.com",
"Sec-Ch-Ua": '"Not/A)Brand";v="8", "Chromium";v="126"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Fetch-Site": "cross-site",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Dest": "image",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Access-Control-Allow-Origin": "*",
"Accept": "application/json, text/plain, */*",
"Origin": "https://gamebooking24.com",
"Referer": "https://gamebooking24.com/",
"Priority": "u=1, i",
},
}
# Global StealthySession instance - will be initialized on startup
stealthy_session: Optional[StealthySession] = None
@app.on_event("startup")
async def startup_event():
"""Initialize the StealthySession when the app starts"""
global stealthy_session
logger.info("Initializing StealthySession...")
stealthy_session = StealthySession(
headless=True,
solve_cloudflare=True,
max_pages=10, # Allow up to 10 concurrent requests
google_search=False, # Skip Google search simulation for faster startup
)
logger.info("StealthySession initialized successfully")
@app.on_event("shutdown")
async def shutdown_event():
"""Close the StealthySession when the app shuts down"""
global stealthy_session
if stealthy_session:
logger.info("Closing StealthySession...")
await stealthy_session.close()
logger.info("StealthySession closed successfully")
# Middleware for logging all requests
@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
# Log incoming request
logger.info(f"{request.method} {request.url.path}")
if request.query_params:
logger.debug(f" Query params: {dict(request.query_params)}")
# Process request
response = await call_next(request)
# Log response
duration = (time.time() - start_time) * 1000
logger.info(
f"{request.method} {request.url.path} [{response.status_code}] ({duration:.2f}ms)"
)
return response
def build_headers(
authorization: Optional[str] = None, extra_headers: Optional[Dict[str, str]] = None
) -> Dict[str, str]:
"""Build headers dict for requests"""
headers = CONSTANTS["SCRAP_API_BASE_HEADERS"].copy()
if authorization:
headers["Authorization"] = authorization
if extra_headers:
headers.update(extra_headers)
return headers
# Pydantic models for request bodies
class LoginPayload(BaseModel):
userId: str
password: str
verifyToken: str
code: str
userType: int
class DealerListPayload(BaseModel):
page: int
pageSize: int
parentDistributor: int
class DistributorListPayload(BaseModel):
page: int
pageSize: int
parentDistributor: int
class BookListPayload(BaseModel):
userType: int
userIds: List[int]
drawId: int
startDate: str
endDate: str
beAdmin: bool
containImported: bool
keyword: str
class AddMultiplePayload(BaseModel):
dealerId: int
drawId: int
closeTime: str
date: str
changedBalance: int
insertData: str
class DeleteMultiplePayload(BaseModel):
bookIds: List[str]
closeTime: str
dealerId: int
drawId: int
@app.get("/ping")
def ping():
logger.info("Ping request received")
return {"status": "pong"}
@app.get("/v1/user/get-balance")
async def get_balance(userId: int, authorization: str):
logger.info(f"[GET /v1/user/get-balance] userId={userId}")
try:
headers = build_headers(authorization=authorization)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/user/get-balance",
params={"userId": userId},
headers=headers,
)
logger.info(f"[GET /v1/user/get-balance] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[GET /v1/user/get-balance] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/v1/auth/login")
async def login(payload: LoginPayload):
logger.info(f"[POST /v1/auth/login] - payload={payload.model_dump()}")
try:
headers = build_headers(extra_headers={"Content-Type": "application/json"})
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/auth/login",
method="POST",
data=payload.model_dump(),
headers=headers,
)
logger.info(f"[POST /v1/auth/login] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[POST /v1/auth/login] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/verify/image")
async def get_captcha(uuid: str):
logger.info(f"[GET /verify/image] uuid={uuid}")
try:
headers = build_headers(
extra_headers={
"Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
}
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/verify/image",
params={"uuid": uuid},
headers=headers,
)
if page.status == 403:
logger.error(
"[GET /verify/image] 403 Forbidden - Cloudflare blocked the request"
)
logger.error(
f"[GET /verify/image] Response headers: {dict(page.response.headers)}"
)
logger.error(f"[GET /verify/image] Response text: {page.response.text}")
logger.info(
f"[GET /verify/image] Response: {page.status}, size={len(page.response.content)} bytes"
)
return Response(
content=page.response.content,
media_type="image/png",
status_code=page.status,
)
except Exception as e:
logger.error(f"[GET /verify/image] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/v1/user/dealer-list")
async def dealer_list(payload: DealerListPayload, authorization: str):
logger.info(
f"[POST /v1/user/dealer-list] parentDistributor={payload.parentDistributor}, page={payload.page}, pageSize={payload.pageSize}"
)
try:
headers = build_headers(
authorization=authorization,
extra_headers={"Content-Type": "application/json"},
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/user/dealer-list",
method="POST",
data=payload.model_dump(),
headers=headers,
)
logger.info(f"[POST /v1/user/dealer-list] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[POST /v1/user/dealer-list] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/v1/user/distributor-list")
async def distributor_list(payload: DistributorListPayload, authorization: str):
logger.info(
f"[POST /v1/user/distributor-list] parentDistributor={payload.parentDistributor}, page={payload.page}, pageSize={payload.pageSize}"
)
try:
headers = build_headers(
authorization=authorization,
extra_headers={"Content-Type": "application/json"},
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/user/distributor-list",
method="POST",
data=payload.model_dump(),
headers=headers,
)
logger.info(f"[POST /v1/user/distributor-list] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[POST /v1/user/distributor-list] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/v1/draw/list-my")
async def list_draws(userId: int, authorization: str):
logger.info(f"[GET /v1/draw/list-my] userId={userId}")
try:
headers = build_headers(
authorization=authorization,
extra_headers={"Content-Type": "application/json"},
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/draw/list-my",
params={"userId": userId},
headers=headers,
)
logger.info(f"[GET /v1/draw/list-my] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[GET /v1/draw/list-my] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/v1/book/list2")
async def book_list(payload: BookListPayload, authorization: str):
logger.info(
f"[POST /v1/book/list2] drawId={payload.drawId}, userIds={len(payload.userIds)}, date={payload.startDate}"
)
try:
headers = build_headers(
authorization=authorization,
extra_headers={"Content-Type": "application/json"},
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/book/list2",
method="POST",
data=payload.model_dump(),
headers=headers,
)
logger.info(f"[POST /v1/book/list2] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[POST /v1/book/list2] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/v1/book/add-multiple")
async def add_multiple(payload: AddMultiplePayload, authorization: str):
entries_count = len(payload.insertData.split(";")) if payload.insertData else 0
logger.info(
f"[POST /v1/book/add-multiple] dealerId={payload.dealerId}, drawId={payload.drawId}, entries={entries_count}, balance={payload.changedBalance}"
)
try:
headers = build_headers(
authorization=authorization,
extra_headers={"Content-Type": "application/json;charset=UTF-8"},
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/book/add-multiple",
method="POST",
data=payload.model_dump(),
headers=headers,
)
logger.info(f"[POST /v1/book/add-multiple] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[POST /v1/book/add-multiple] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/v1/book/delete-multiple")
async def delete_multiple(payload: DeleteMultiplePayload, authorization: str):
logger.info(
f"[POST /v1/book/delete-multiple] dealerId={payload.dealerId}, drawId={payload.drawId}, bookIds={len(payload.bookIds)}"
)
try:
headers = build_headers(
authorization=authorization,
extra_headers={"Content-Type": "application/json;charset=UTF-8"},
)
page = stealthy_session.fetch(
f"{CONSTANTS['SCRAP_API_URL']}/v1/book/delete-multiple",
method="POST",
data=payload.model_dump(),
headers=headers,
)
logger.info(f"[POST /v1/book/delete-multiple] Response: {page.status}")
return JSONResponse(content=page.response.json(), status_code=page.status)
except Exception as e:
logger.error(f"[POST /v1/book/delete-multiple] Error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))