nah gonna redo it again
This commit is contained in:
257
pyapi/main.py
257
pyapi/main.py
@@ -1,11 +1,16 @@
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from typing import Dict, List, Optional
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import aiohttp
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.responses import JSONResponse, Response
|
||||
from pydantic import BaseModel
|
||||
from scrapling.fetchers import StealthySession
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
@@ -19,6 +24,10 @@ app = FastAPI()
|
||||
|
||||
logger.info("FastAPI Proxy Server initialized")
|
||||
|
||||
SCRAPERAPI_API_KEY = os.getenv("SCRAPERAPI_API_KEY")
|
||||
if not SCRAPERAPI_API_KEY:
|
||||
raise ValueError("SCRAPERAPI_API_KEY is not set")
|
||||
|
||||
|
||||
CONSTANTS = {
|
||||
"SESSION_KEY_NAME": "SID",
|
||||
@@ -27,6 +36,7 @@ CONSTANTS = {
|
||||
"LAST_FETCHED_KEY": "LAST_FETCHED",
|
||||
"SCRAP_API_URL": "https://gamebooking24.com/lottery-api",
|
||||
"SCRAP_API_SESSION_KEY": "SRAJWT",
|
||||
"SCRAPERAPI_BASE_URL": "http://api.scraperapi.com",
|
||||
"SCRAP_API_BASE_HEADERS": {
|
||||
"Host": "gamebooking24.com",
|
||||
"Sec-Ch-Ua": '"Not/A)Brand";v="8", "Chromium";v="126"',
|
||||
@@ -44,33 +54,6 @@ CONSTANTS = {
|
||||
},
|
||||
}
|
||||
|
||||
# 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")
|
||||
@@ -109,6 +92,110 @@ def build_headers(
|
||||
return headers
|
||||
|
||||
|
||||
async def make_get_request(
|
||||
url: str, params: Optional[Dict] = None, headers: Optional[Dict] = None
|
||||
):
|
||||
"""Make a GET request using ScraperAPI"""
|
||||
if SCRAPERAPI_API_KEY == "<TODO: get and put the key in here>":
|
||||
raise HTTPException(status_code=500, detail="ScraperAPI API key not configured")
|
||||
|
||||
# Build the ScraperAPI request params
|
||||
scraperapi_params = {
|
||||
"api_key": SCRAPERAPI_API_KEY,
|
||||
"url": url,
|
||||
"render": "true",
|
||||
}
|
||||
|
||||
# Add query params to the target URL if provided
|
||||
if params:
|
||||
url_with_params = f"{url}?{urlencode(params)}"
|
||||
scraperapi_params["url"] = url_with_params
|
||||
|
||||
# Make the request to ScraperAPI using aiohttp
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
CONSTANTS["SCRAPERAPI_BASE_URL"],
|
||||
params=scraperapi_params,
|
||||
headers=headers,
|
||||
timeout=aiohttp.ClientTimeout(total=60),
|
||||
) as response:
|
||||
# Create a simple response-like object
|
||||
class AsyncResponse:
|
||||
def __init__(self, aiohttp_response):
|
||||
self._response = aiohttp_response
|
||||
self.status_code = aiohttp_response.status
|
||||
self.headers = aiohttp_response.headers
|
||||
self._text = None
|
||||
self._json = None
|
||||
self._content = None
|
||||
|
||||
async def text(self):
|
||||
if self._text is None:
|
||||
self._text = await self._response.text()
|
||||
return self._text
|
||||
|
||||
async def json(self):
|
||||
if self._json is None:
|
||||
self._json = await self._response.json()
|
||||
return self._json
|
||||
|
||||
async def content(self):
|
||||
if self._content is None:
|
||||
self._content = await self._response.read()
|
||||
return self._content
|
||||
|
||||
return AsyncResponse(response)
|
||||
|
||||
|
||||
async def make_post_request(url: str, data: dict, headers: Optional[Dict] = None):
|
||||
"""Make a POST request using ScraperAPI"""
|
||||
if SCRAPERAPI_API_KEY == "<TODO: get and put the key in here>":
|
||||
raise HTTPException(status_code=500, detail="ScraperAPI API key not configured")
|
||||
|
||||
# Build the ScraperAPI request params
|
||||
scraperapi_params = {
|
||||
"api_key": SCRAPERAPI_API_KEY,
|
||||
"url": url,
|
||||
"render": "true",
|
||||
}
|
||||
|
||||
# Make the POST request to ScraperAPI using aiohttp
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
CONSTANTS["SCRAPERAPI_BASE_URL"],
|
||||
params=scraperapi_params,
|
||||
json=data, # Use json= for JSON payloads (sets Content-Type automatically)
|
||||
headers=headers,
|
||||
timeout=aiohttp.ClientTimeout(total=60),
|
||||
) as response:
|
||||
# Create a simple response-like object
|
||||
class AsyncResponse:
|
||||
def __init__(self, aiohttp_response):
|
||||
self._response = aiohttp_response
|
||||
self.status_code = aiohttp_response.status
|
||||
self.headers = aiohttp_response.headers
|
||||
self._text = None
|
||||
self._json = None
|
||||
self._content = None
|
||||
|
||||
async def text(self):
|
||||
if self._text is None:
|
||||
self._text = await self._response.text()
|
||||
return self._text
|
||||
|
||||
async def json(self):
|
||||
if self._json is None:
|
||||
self._json = await self._response.json()
|
||||
return self._json
|
||||
|
||||
async def content(self):
|
||||
if self._content is None:
|
||||
self._content = await self._response.read()
|
||||
return self._content
|
||||
|
||||
return AsyncResponse(response)
|
||||
|
||||
|
||||
# Pydantic models for request bodies
|
||||
class LoginPayload(BaseModel):
|
||||
userId: str
|
||||
@@ -168,13 +255,15 @@ 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(
|
||||
response = await make_get_request(
|
||||
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)
|
||||
logger.info(f"[GET /v1/user/get-balance] Response: {response.status_code}")
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[GET /v1/user/get-balance] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -185,14 +274,37 @@ 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(
|
||||
response = await make_post_request(
|
||||
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)
|
||||
logger.info(f"[POST /v1/auth/login] Response: {response.status_code}")
|
||||
|
||||
# Handle non-JSON responses (e.g., 403 HTML pages)
|
||||
if response.status_code == 403:
|
||||
response_text = await response.text()
|
||||
logger.error(
|
||||
f"[POST /v1/auth/login] 403 Forbidden - Response: {response_text[:500]}"
|
||||
)
|
||||
raise HTTPException(status_code=403, detail="Request blocked")
|
||||
|
||||
# Try to parse as JSON
|
||||
try:
|
||||
response_json = await response.json()
|
||||
except Exception as json_error:
|
||||
response_text = await response.text()
|
||||
logger.error(
|
||||
f"[POST /v1/auth/login] Failed to parse JSON response: {json_error}"
|
||||
)
|
||||
logger.error(f"[POST /v1/auth/login] Response text: {response_text[:500]}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"Invalid JSON response: {str(json_error)}"
|
||||
)
|
||||
|
||||
return JSONResponse(content=response_json, status_code=response.status_code)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[POST /v1/auth/login] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -207,28 +319,28 @@ async def get_captcha(uuid: str):
|
||||
"Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
|
||||
}
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_get_request(
|
||||
f"{CONSTANTS['SCRAP_API_URL']}/verify/image",
|
||||
params={"uuid": uuid},
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if page.status == 403:
|
||||
if response.status_code == 403:
|
||||
logger.error("[GET /verify/image] 403 Forbidden - Request blocked")
|
||||
logger.error(
|
||||
"[GET /verify/image] 403 Forbidden - Cloudflare blocked the request"
|
||||
f"[GET /verify/image] Response headers: {dict(response.headers)}"
|
||||
)
|
||||
logger.error(
|
||||
f"[GET /verify/image] Response headers: {dict(page.response.headers)}"
|
||||
)
|
||||
logger.error(f"[GET /verify/image] Response text: {page.response.text}")
|
||||
response_text = await response.text()
|
||||
logger.error(f"[GET /verify/image] Response text: {response_text[:500]}")
|
||||
|
||||
content = await response.content()
|
||||
logger.info(
|
||||
f"[GET /verify/image] Response: {page.status}, size={len(page.response.content)} bytes"
|
||||
f"[GET /verify/image] Response: {response.status_code}, size={len(content)} bytes"
|
||||
)
|
||||
return Response(
|
||||
content=page.response.content,
|
||||
content=content,
|
||||
media_type="image/png",
|
||||
status_code=page.status,
|
||||
status_code=response.status_code,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[GET /verify/image] Error: {str(e)}")
|
||||
@@ -245,14 +357,15 @@ async def dealer_list(payload: DealerListPayload, authorization: str):
|
||||
authorization=authorization,
|
||||
extra_headers={"Content-Type": "application/json"},
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_post_request(
|
||||
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)
|
||||
logger.info(f"[POST /v1/user/dealer-list] Response: {response.status_code}")
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[POST /v1/user/dealer-list] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -268,14 +381,17 @@ async def distributor_list(payload: DistributorListPayload, authorization: str):
|
||||
authorization=authorization,
|
||||
extra_headers={"Content-Type": "application/json"},
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_post_request(
|
||||
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)
|
||||
logger.info(
|
||||
f"[POST /v1/user/distributor-list] Response: {response.status_code}"
|
||||
)
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[POST /v1/user/distributor-list] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -289,13 +405,15 @@ async def list_draws(userId: int, authorization: str):
|
||||
authorization=authorization,
|
||||
extra_headers={"Content-Type": "application/json"},
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_get_request(
|
||||
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)
|
||||
logger.info(f"[GET /v1/draw/list-my] Response: {response.status_code}")
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[GET /v1/draw/list-my] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -311,14 +429,15 @@ async def book_list(payload: BookListPayload, authorization: str):
|
||||
authorization=authorization,
|
||||
extra_headers={"Content-Type": "application/json"},
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_post_request(
|
||||
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)
|
||||
logger.info(f"[POST /v1/book/list2] Response: {response.status_code}")
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[POST /v1/book/list2] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -335,14 +454,15 @@ async def add_multiple(payload: AddMultiplePayload, authorization: str):
|
||||
authorization=authorization,
|
||||
extra_headers={"Content-Type": "application/json;charset=UTF-8"},
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_post_request(
|
||||
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)
|
||||
logger.info(f"[POST /v1/book/add-multiple] Response: {response.status_code}")
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[POST /v1/book/add-multiple] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -358,14 +478,15 @@ async def delete_multiple(payload: DeleteMultiplePayload, authorization: str):
|
||||
authorization=authorization,
|
||||
extra_headers={"Content-Type": "application/json;charset=UTF-8"},
|
||||
)
|
||||
page = stealthy_session.fetch(
|
||||
response = await make_post_request(
|
||||
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)
|
||||
logger.info(f"[POST /v1/book/delete-multiple] Response: {response.status_code}")
|
||||
return JSONResponse(
|
||||
content=await response.json(), status_code=response.status_code
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[POST /v1/book/delete-multiple] Error: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
Reference in New Issue
Block a user