Files changed (1) hide show
  1. app.py +42 -44
app.py CHANGED
@@ -1,4 +1,3 @@
1
- import os
2
  import asyncio
3
  import uvicorn
4
  from fastapi import FastAPI
@@ -7,6 +6,10 @@ from playwright.async_api import async_playwright
7
  from contextlib import asynccontextmanager
8
 
9
  # --- Configuration ---
 
 
 
 
10
  class GeminiBot:
11
  def __init__(self):
12
  self.playwright = None
@@ -14,50 +17,49 @@ class GeminiBot:
14
 
15
  async def start(self):
16
  self.playwright = await async_playwright().start()
17
- # Launch browser with flags to help avoid detection
18
  self.browser = await self.playwright.chromium.launch(
19
- headless=True,
20
  args=["--no-sandbox", "--disable-blink-features=AutomationControlled"]
21
  )
22
- print("--- Gemini Bot (Stateless) Ready ---")
23
 
24
  async def process_chat(self, message: str):
25
- # Create context with a realistic User Agent
26
- context = await self.browser.new_context(
27
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
28
- )
29
- page = await context.new_page()
30
-
31
- try:
32
- # BLOCK unnecessary assets to speed up loading
33
- await page.route("**/*", lambda route: route.abort() if route.request.resource_type in
34
- ["image", "font", "media", "stylesheet"] else route.continue_())
35
-
36
- # Navigate and wait for network idle to ensure JS loads
37
- await page.goto("https://gemini.google.com", wait_until="networkidle")
38
-
39
- # Interact with the chat box
40
- chat_box = page.locator("div[contenteditable='true']")
41
- await chat_box.wait_for(timeout=20000)
42
- await chat_box.fill(message)
43
- await page.keyboard.press("Enter")
44
 
45
- # Wait for the response container
46
- await page.wait_for_selector("message-content", timeout=45000)
47
-
48
- # Extract Text
49
- current_text = await page.evaluate("""() => {
50
- const msgs = document.querySelectorAll('message-content');
51
- return msgs.length > 0 ? msgs[msgs.length - 1].innerText : "Error: No message content found";
52
- }""")
53
-
54
- return {"text": current_text}
55
-
56
- except Exception as e:
57
- return {"text": f"Error: {str(e)}"}
58
- finally:
59
- # ALWAYS wipe the session
60
- await context.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  async def stop(self):
63
  if self.browser: await self.browser.close()
@@ -76,13 +78,9 @@ app = FastAPI(lifespan=lifespan)
76
  class ChatRequest(BaseModel):
77
  message: str
78
 
79
- @app.get("/")
80
- def health_check():
81
- return {"status": "online", "message": "Gemini API is running"}
82
-
83
  @app.post("/chat")
84
  async def chat_api(request: ChatRequest):
85
- # This now handles the Pydantic model correctly, resolving 422 errors
86
  return await bot.process_chat(request.message)
87
 
88
  if __name__ == "__main__":
 
 
1
  import asyncio
2
  import uvicorn
3
  from fastapi import FastAPI
 
6
  from contextlib import asynccontextmanager
7
 
8
  # --- Configuration ---
9
+ # Limit simultaneous browser instances to prevent memory crashes
10
+ MAX_CONCURRENT_REQUESTS = 3
11
+ semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
12
+
13
  class GeminiBot:
14
  def __init__(self):
15
  self.playwright = None
 
17
 
18
  async def start(self):
19
  self.playwright = await async_playwright().start()
 
20
  self.browser = await self.playwright.chromium.launch(
21
+ headless=True,
22
  args=["--no-sandbox", "--disable-blink-features=AutomationControlled"]
23
  )
24
+ print("--- Gemini Bot (Concurrent Stateless Mode) Ready ---")
25
 
26
  async def process_chat(self, message: str):
27
+ # The semaphore ensures only N requests run at once
28
+ async with semaphore:
29
+ context = await self.browser.new_context(
30
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
31
+ )
32
+ page = await context.new_page()
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ try:
35
+ # Block heavy assets for speed
36
+ await page.route("**/*", lambda route: route.abort() if route.request.resource_type in
37
+ ["image", "font", "media", "stylesheet"] else route.continue_())
38
+
39
+ await page.goto("https://gemini.google.com", wait_until="networkidle")
40
+
41
+ # Logic: If not logged in, this will wait until timeout
42
+ chat_box = page.locator("div[contenteditable='true']")
43
+ await chat_box.wait_for(timeout=30000)
44
+ await chat_box.fill(message)
45
+ await page.keyboard.press("Enter")
46
+
47
+ # Wait for the response container
48
+ await page.wait_for_selector("message-content", timeout=45000)
49
+
50
+ # Extract the last message
51
+ current_text = await page.evaluate("""() => {
52
+ const msgs = document.querySelectorAll('message-content');
53
+ return msgs.length > 0 ? msgs[msgs.length - 1].innerText : "No response";
54
+ }""")
55
+
56
+ return {"text": current_text}
57
+
58
+ except Exception as e:
59
+ return {"text": f"Error: {str(e)}"}
60
+ finally:
61
+ # Session is wiped here, regardless of success or failure
62
+ await context.close()
63
 
64
  async def stop(self):
65
  if self.browser: await self.browser.close()
 
78
  class ChatRequest(BaseModel):
79
  message: str
80
 
 
 
 
 
81
  @app.post("/chat")
82
  async def chat_api(request: ChatRequest):
83
+ # This call is non-blocking to other concurrent requests
84
  return await bot.process_chat(request.message)
85
 
86
  if __name__ == "__main__":