| 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 |
|
|
| |
| API_ID = 21934109 |
| API_HASH = 'e7e8c554b9ff88d180983996c33bdf27' |
|
|
| |
| |
| TARGET_GROUP_ID = -1003794439741 |
|
|
| |
| PORT = int(os.environ.get("PORT", 7860)) |
| state = {"my_id": None, "group_entity": None} |
|
|
| |
| client = TelegramClient('/code/num_lookup_session', API_ID, API_HASH) |
|
|
| def clean_value(val: str, to_title: bool = False) -> str: |
| if not val: |
| return "N/A" |
| cleaned = val.strip().strip('`').strip() |
| if cleaned.upper() in ["NA", "N/A", "NONE", ""]: |
| return "N/A" |
| return cleaned.title() if to_title else cleaned |
|
|
| def parse_multiple_records(text: str) -> list: |
| record_blocks = re.split(r'⭐\s*RECORD\s*#\d+', text, flags=re.IGNORECASE) |
| parsed_records = [] |
| |
| for block in record_blocks[1:]: |
| if not block.strip(): |
| continue |
| |
| name_match = re.search(r'Name:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| father_match = re.search(r'Father\'s Name:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| address_match = re.search(r'Address:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| circle_match = re.search(r'Circle:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| alternate_match = re.search(r'Alternate:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| id_match = re.search(r'\bID:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| email_match = re.search(r'Email:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| truecaller_match = re.search(r'Truecaller:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
| mobile_match = re.search(r'Mobile:\s*(.*?)(?:\n|$)', block, re.IGNORECASE) |
|
|
| parsed_records.append({ |
| "mobile": clean_value(mobile_match.group(1) if mobile_match else None), |
| "name": clean_value(name_match.group(1) if name_match else None, to_title=True), |
| "father_name": clean_value(father_match.group(1) if father_match else None, to_title=True), |
| "address": clean_value(address_match.group(1) if address_match else None, to_title=True), |
| "circle": clean_value(circle_match.group(1) if circle_match else None), |
| "alternate": clean_value(alternate_match.group(1) if alternate_match else None), |
| "id": clean_value(id_match.group(1) if id_match else None), |
| "email": clean_value(email_match.group(1) if email_match else None), |
| "truecaller": clean_value(truecaller_match.group(1) if truecaller_match else None, to_title=True) |
| }) |
| |
| return parsed_records |
|
|
| async def ensure_connected(): |
| if not client.is_connected(): |
| print("[CONNECTION] Client disconnected. Connecting now...") |
| await client.connect() |
| try: |
| await client.get_me() |
| except Exception: |
| print("[CONNECTION] Session link broken. Reconnecting session layer...") |
| await client.disconnect() |
| await client.connect() |
|
|
| @asynccontextmanager |
| async def lifespan(app: FastAPI): |
| print("[STARTUP] Connecting directly to Telegram Network...") |
| await client.connect() |
|
|
| if not await client.is_user_authorized(): |
| print("!!! NOT LOGGED IN. Please verify your session file !!!") |
| else: |
| print("Telethon Connected and Authorized.") |
| me = await client.get_me() |
| state["my_id"] = me.id |
| |
| try: |
| print(f"[STARTUP] Fetching and caching entity for Group ID: {TARGET_GROUP_ID}") |
| |
| state["group_entity"] = await client.get_input_entity(TARGET_GROUP_ID) |
| print("[STARTUP] Group entity caching completed successfully.") |
| except Exception as e: |
| print(f"[CRITICAL STARTUP ERROR] Could not cache Group Entity: {e}") |
| |
| yield |
| print("[SHUTDOWN] Disconnecting Telegram Client...") |
| await client.disconnect() |
|
|
| app = FastAPI(lifespan=lifespan) |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["https://crm.gudmed.in"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| @app.get("/") |
| async def root_index(): |
| return {"status": "healthy", "service": "Telegram Truecaller Group Polling API"} |
|
|
| @app.get("/lookup") |
| async def lookup(phone: str): |
| clean_phone = str(phone)[-10:].strip() |
| await ensure_connected() |
| |
| if not state["my_id"] or not state["group_entity"]: |
| raise HTTPException(status_code=503, detail="Telegram client components are uninitialized.") |
| |
| try: |
| |
| try: |
| last_messages = await client.get_messages(state["group_entity"], limit=1) |
| start_msg_id = last_messages[0].id if last_messages else 0 |
| except Exception: |
| await ensure_connected() |
| last_messages = await client.get_messages(state["group_entity"], limit=1) |
| start_msg_id = last_messages[0].id if last_messages else 0 |
|
|
| command_payload = f"/num {clean_phone}" |
| print(f"[API] Dispatching search request to Group: {command_payload}") |
| await client.send_message(state["group_entity"], command_payload) |
| |
| |
| for i in range(50): |
| await asyncio.sleep(0.5) |
| |
| try: |
| |
| messages = await client.get_messages(state["group_entity"], limit=5) |
| except Exception as loop_err: |
| print(f"[API WARNING] Polling read failed on step {i}: {loop_err}. Reconnecting...") |
| await ensure_connected() |
| continue |
| |
| for m in messages: |
| |
| if m.id > start_msg_id and m.sender_id != state["my_id"]: |
| if not m.text or any(x in m.text for x in ["Fetching", "Please wait", "Searching", "Typing", "/num"]): |
| continue |
| |
| |
| query_match = re.search(r'Query:\s*`?(\d+)`?', m.text, re.IGNORECASE) |
| if query_match and query_match.group(1).endswith(clean_phone): |
| print(f"[API] Targeted response packet found in group on iteration step {i}!") |
| total_records_match = re.search(r'Total Records:\s*(\d+)', m.text, re.IGNORECASE) |
| total_count = int(total_records_match.group(1)) if total_records_match else 0 |
| |
| return { |
| "status": "success", |
| "phone": clean_phone, |
| "total_records": total_count, |
| "records": parse_multiple_records(m.text), |
| "full_text": m.text |
| } |
|
|
| raise HTTPException(status_code=504, detail="Timeout: Target bot in group didn't answer with query verification.") |
| |
| 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") |