yo / main.py
Bhatiasab's picture
Update main.py
058bcbd verified
Raw
History Blame Contribute Delete
7.85 kB
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'
# Extracted Peer ID from your web link (-1003794439741)
# Telethon requires the -100 prefix dropped for entity lookups via ID integer
TARGET_GROUP_ID = -1003794439741
# Dynamically bind to Hugging Face's required routing port
PORT = int(os.environ.get("PORT", 7860))
state = {"my_id": None, "group_entity": None}
# Pointing explicitly to the absolute path in the HF container
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}")
# Group must be in the account's dialog history for this to find it instantly
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:
# Determine the latest message ID in the group before tracking
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)
# Poll up to 50 iterations for group environments due to latency differences
for i in range(50):
await asyncio.sleep(0.5)
try:
# Fetch more messages (limit=5) because group chatter can pass quickly
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:
# Target messages that appeared AFTER our request, and are NOT sent by us
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
# Validate that this payload matches our tracked query sequence
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")