Spaces:
Sleeping
Sleeping
| import os | |
| import time | |
| import base64 | |
| import httpx | |
| import asyncio | |
| import uvicorn | |
| from fastapi import FastAPI | |
| from pydantic import BaseModel | |
| from playwright.async_api import async_playwright | |
| from contextlib import asynccontextmanager | |
| # --- Configuration --- | |
| IMGBB_API_KEY = os.getenv("IMGBB_API_KEY", "5765c99f7a977515ba320a689330e508") | |
| SAVE_FOLDER = "/tmp/downloaded_images" | |
| os.makedirs(SAVE_FOLDER, exist_ok=True) | |
| class GeminiBot: | |
| def __init__(self): | |
| self.playwright = None | |
| self.context = None | |
| self.page = None | |
| self.history = {"last_text": "", "last_img_id": ""} | |
| async def start(self): | |
| print("Starting Playwright...") | |
| self.playwright = await async_playwright().start() | |
| # HF Spaces కోసం Chromium + Headless వాడాలి | |
| self.context = await self.playwright.chromium.launch_persistent_context( | |
| user_data_dir="./user_data", # Local folder for session | |
| channel='chromium', # msedge కాదు, chromium వాడాలి | |
| headless=True, # True అయి ఉండాలి (Server కోసం) | |
| accept_downloads=True, | |
| args=[ | |
| "--disable-blink-features=AutomationControlled", | |
| "--no-sandbox", | |
| "--disable-setuid-sandbox", | |
| "--disable-dev-shm-usage" | |
| ] | |
| ) | |
| self.page = self.context.pages[0] | |
| print("Navigating to Gemini...") | |
| await self.page.goto("https://gemini.google.com") | |
| try: | |
| # లాగిన్ అయ్యాక మాత్రమే ఈ ఎలిమెంట్ కనిపిస్తుంది | |
| await self.page.wait_for_selector("div[contenteditable='true']", timeout=60000) | |
| print("--- Gemini Bot is ready (Logged In) ---") | |
| except Exception as e: | |
| print(f"Error: Login failed or selector not found. {e}") | |
| await self.page.screenshot(path="debug_error.png") | |
| # లాగిన్ ఫెయిల్ అయితే కూడా app run avvali kabatti raise cheyyakunda continue cheyyachu | |
| # కానీ chat work avvakapovachu. | |
| async def upload_to_imgbb(self, image_path): | |
| if not IMGBB_API_KEY: | |
| return None | |
| try: | |
| if not image_path or not os.path.exists(image_path): | |
| return None | |
| with open(image_path, "rb") as file: | |
| img_base64 = base64.b64encode(file.read()).decode('utf-8') | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post( | |
| "https://api.imgbb.com/1/upload", | |
| data={"key": IMGBB_API_KEY, "image": img_base64} | |
| ) | |
| if response.status_code == 200: | |
| return response.json()['data']['url'] | |
| else: | |
| return None | |
| except Exception as e: | |
| print(f"--- [ERROR] Upload: {e} ---") | |
| return None | |
| async def download_image_expert(self): | |
| try: | |
| all_imgs = self.page.locator("img[src^='blob:']") | |
| count = await all_imgs.count() | |
| if count > 0: | |
| target_img = all_imgs.last | |
| img_src = await target_img.get_attribute("src") | |
| if img_src == self.history["last_img_id"]: | |
| return None | |
| box = await target_img.bounding_box() | |
| if box: | |
| await self.page.mouse.move(box['x'] + (box['width'] / 2), box['y'] + (box['height'] / 2)) | |
| await asyncio.sleep(2) | |
| download_btn = self.page.locator("button:has(mat-icon[fonticon='download'])").last | |
| if await download_btn.is_visible(): | |
| async with self.page.expect_download(timeout=30000) as download_info: | |
| try: | |
| await download_btn.click(force=True) | |
| except: | |
| await self.page.evaluate("el => el.click()", await download_btn.element_handle()) | |
| downloaded_file = await download_info.value | |
| file_name = f"gemini_{int(time.time())}.png" | |
| save_path = os.path.join(SAVE_FOLDER, file_name) | |
| await downloaded_file.save_as(save_path) | |
| self.history["last_img_id"] = img_src | |
| return save_path | |
| except Exception as e: | |
| print(f"--- [ERROR] Download: {e} ---") | |
| return None | |
| async def process_chat(self, message: str, file_paths: list = None): | |
| try: | |
| # File Upload Logic (Optional - needs adjustment for server paths) | |
| if file_paths: | |
| print("File upload requested but not fully implemented for server paths yet.") | |
| # 1. Message Send | |
| chat_box = self.page.locator("div[contenteditable='true']").first | |
| await chat_box.fill(message) | |
| await self.page.keyboard.press("Enter") | |
| # 2. Wait for Response | |
| stop_btn = self.page.locator("mat-icon[fonticon='stop']") | |
| await self.page.wait_for_selector("message-content", timeout=60000) | |
| while await stop_btn.is_visible(): | |
| await asyncio.sleep(0.5) | |
| await asyncio.sleep(2) | |
| # 3. Get Text | |
| current_text = await self.page.evaluate(""" | |
| () => { | |
| const msgs = document.querySelectorAll('message-content'); | |
| if (msgs.length > 0) return msgs[msgs.length - 1].innerText; | |
| return ''; | |
| } | |
| """) | |
| # 4. Handle Image | |
| img_path = await self.download_image_expert() | |
| img_url = await self.upload_to_imgbb(img_path) if img_path else None | |
| return {"text": current_text, "image_url": img_url} | |
| except Exception as e: | |
| return {"text": f"Error: {str(e)}", "image_url": None} | |
| bot = GeminiBot() | |
| async def lifespan(app: FastAPI): | |
| await bot.start() | |
| yield | |
| if bot.context: | |
| await bot.context.close() | |
| if bot.playwright: | |
| await bot.playwright.stop() | |
| app = FastAPI(lifespan=lifespan) | |
| class ChatRequest(BaseModel): | |
| message: str | |
| files: list = [] | |
| def health_check(): | |
| return {"status": "online", "message": "Gemini API is running"} | |
| async def chat_api(request: ChatRequest): | |
| result = await bot.process_chat(request.message, request.files) | |
| return {"status": "success", "data": result} | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |