import os import re import asyncio import uvicorn from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from telethon import TelegramClient from contextlib import asynccontextmanager # --- CONFIG --- API_ID = 21934109 API_HASH = 'e7e8c554b9ff88d180983996c33bdf27' BOT_USERNAME = '@TruecallerInfoLookupBot' # Dynamically bind to Hugging Face's required routing port (defaults to 7860) PORT = int(os.environ.get("PORT", 7860)) # Cache profile ID to prevent hitting rate limits state = {"my_id": None} # --- INITIALIZE THE CLEAN CLIENT --- # We point directly to the standard connection layer since Hugging Face supports it natively client = TelegramClient('/code/lookup_session', API_ID, API_HASH) def parse_to_json(text: str) -> dict: """Parses the bot text into a structured dictionary.""" e_match = re.search(r'Eyecon.*?Name:\s*`?(.*?)`?(?:\n|$)', text, re.IGNORECASE | re.DOTALL) c_match = re.search(r'CALLAPP.*?Name:\s*`?(.*?)`?(?:\n|$)', text, re.IGNORECASE | re.DOTALL) name1 = e_match.group(1).strip() if e_match else None name2 = c_match.group(1).strip() if c_match else None data = {} if name1: data["name1"] = name1 if name2: data["name2"] = name2 if not name1 and not name2: fallback = re.search(r'Name:\s*`?(.*?)`?(?:\n|$)', text, re.IGNORECASE) if fallback: data["name1"] = fallback.group(1).strip() carrier_match = re.search(r'Carrier:\s*`?(.*?)`?(?:\n|$)', text, re.IGNORECASE) location_match = re.search(r'Location:\s*`?(.*?)`?(?:\n|$)', text, re.IGNORECASE) data["carrier"] = carrier_match.group(1).strip() if carrier_match else "N/A" data["location"] = location_match.group(1).strip() if location_match else "N/A" data["full_text"] = text return data # --- NATIVE FASTAPI ASYNC LIFECYCLE --- @asynccontextmanager async def lifespan(app: FastAPI): session_path = "/code/lookup_session.session" print(f"[STARTUP] Looking for session file at: {session_path}") try: print("[STARTUP] Connecting directly to Telegram Network...") await client.connect() except Exception as e: print(f"\n[CRITICAL] Connection failed: {e}\n") raise e if not await client.is_user_authorized(): print("!!! NOT LOGGED IN. Please verify your 'lookup_session.session' file !!!") else: print("Telethon Connected and Authorized.") me = await client.get_me() state["my_id"] = me.id yield print("[SHUTDOWN] Disconnecting Telegram Client...") await client.disconnect() app = FastAPI(lifespan=lifespan) # --- CORS CONFIGURATION --- app.add_middleware( CORSMiddleware, allow_origins=["https://crm.gudmed.in"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # --- HEALTH CHECK ROUTE (Keeps Hugging Face Routing Live) --- @app.get("/") async def root_index(): return {"status": "healthy", "service": "Telegram Truecaller Polling API"} # --- PRIMARY LOOKUP ENDPOINT --- @app.get("/lookup") async def lookup(phone: str): # Take last 10 digits clean_phone = str(phone)[-10:].strip() if not state["my_id"]: raise HTTPException(status_code=503, detail="Telegram client not authorized or fully connected.") try: print(f"[API] Sending search payload for target: +91{clean_phone}") # 1. Send the number to the bot await client.send_message(BOT_USERNAME, f'+91{clean_phone}') # 2. Polling logic (Checks every 0.5s for 40 attempts -> 20 seconds total) for i in range(40): await asyncio.sleep(0.5) # Fetch latest 3 messages to bypass ads or temporary indicators messages = await client.get_messages(BOT_USERNAME, limit=3) for m in messages: # Confirm message came from the bot, not your own account if m.sender_id != state["my_id"]: # Ignore working status frames if not m.text or any(x in m.text for x in ["Searching", "Please wait", "Typing"]): continue # Target confirmation check if clean_phone in m.text: print(f"[API] Target found on polling loop step {i}! Processing...") return { "status": "success", "phone": clean_phone, "data": parse_to_json(m.text) } raise HTTPException(status_code=504, detail="Timeout: Bot didn't respond with correct number in time.") except HTTPException: raise except Exception as e: print(f"Lookup Error: {e}") raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=PORT, loop="asyncio")