Spaces:
Sleeping
Sleeping
| from pydantic import BaseModel | |
| import httpx | |
| import json | |
| from config import TELEGRAM_URL | |
| from ai_service import get_ai_response | |
| from database import db_manager | |
| TELEGRAM_IP = "149.154.167.220" | |
| MAX_TELEGRAM_MESSAGE_LENGTH = 4096 | |
| def _sanitize_telegram_text(text: str) -> str: | |
| if text is None: | |
| return "" | |
| normalized = str(text).replace("\r\n", "\n").replace("\r", "\n") | |
| # Remove control/surrogate chars that Telegram can reject. | |
| cleaned = "".join( | |
| ch | |
| for ch in normalized | |
| if (ch in ("\n", "\t") or ord(ch) >= 32) and not (0xD800 <= ord(ch) <= 0xDFFF) | |
| ) | |
| return cleaned.strip() | |
| def _split_telegram_message(text: str, max_length: int = MAX_TELEGRAM_MESSAGE_LENGTH): | |
| if len(text) <= max_length: | |
| return [text] | |
| parts = [] | |
| remaining = text | |
| while len(remaining) > max_length: | |
| split_at = remaining.rfind("\n", 0, max_length) | |
| if split_at == -1: | |
| split_at = remaining.rfind(" ", 0, max_length) | |
| if split_at == -1: | |
| split_at = max_length | |
| part = remaining[:split_at].strip() | |
| if not part: | |
| part = remaining[:max_length] | |
| split_at = max_length | |
| parts.append(part) | |
| remaining = remaining[split_at:].strip() | |
| if remaining: | |
| parts.append(remaining) | |
| return parts | |
| class ChatInfo(BaseModel): | |
| id: int | |
| username: str = None | |
| first_name: str = None | |
| last_name: str = None | |
| class Message(BaseModel): | |
| chat: ChatInfo | |
| text: str = "" | |
| class WebhookData(BaseModel): | |
| message: Message = None | |
| async def telegram_webhook(data: WebhookData): | |
| try: | |
| if not data.message or not data.message.text: | |
| return {"status": "ok"} | |
| telegram_id = data.message.chat.id | |
| user_text = data.message.text | |
| username = data.message.chat.username | |
| first_name = data.message.chat.first_name | |
| print(f"--- Processing message from {first_name} ---") | |
| if db_manager: | |
| db_manager.create_or_update_user(telegram_id, username, first_name, data.message.chat.last_name) | |
| ai_answer = await get_ai_response(user_text, telegram_id) | |
| if TELEGRAM_URL: | |
| try: | |
| from config import TELEGRAM_TOKEN | |
| async with httpx.AsyncClient(timeout=40.0, verify=False, follow_redirects=True) as client: | |
| prepared_text = _sanitize_telegram_text( | |
| ai_answer or "Sorry, I couldn't generate a response. Please try again." | |
| ) | |
| if not prepared_text: | |
| prepared_text = "Sorry, I couldn't generate a response. Please try again." | |
| message_parts = _split_telegram_message(prepared_text, MAX_TELEGRAM_MESSAGE_LENGTH) | |
| for idx, part in enumerate(message_parts, start=1): | |
| payload = { | |
| "chat_id": telegram_id, | |
| "text": part, | |
| } | |
| print( | |
| f"--- Sending Telegram message part {idx}/{len(message_parts)} " | |
| f"(chars={len(part)}) ---" | |
| ) | |
| try: | |
| # Use form data for maximal compatibility with Telegram endpoint parsers. | |
| response = await client.post(TELEGRAM_URL, data=payload) | |
| except Exception: | |
| print(f"--- DNS Failed. Forcing Direct IP Routing to {TELEGRAM_IP} ---") | |
| forced_ip_url = f"https://{TELEGRAM_IP}/bot{TELEGRAM_TOKEN}/sendMessage" | |
| json_body = json.dumps(payload, ensure_ascii=False).encode("utf-8") | |
| headers = { | |
| "Host": "api.telegram.org", | |
| "Content-Type": "application/json; charset=utf-8", | |
| "Content-Length": str(len(json_body)), | |
| } | |
| response = await client.post( | |
| forced_ip_url, | |
| content=json_body, | |
| headers=headers, | |
| ) | |
| if response.status_code != 200: | |
| print(f"--- Telegram payload rejected (part {idx}) ---") | |
| print(f"--- Payload: {payload} ---") | |
| print( | |
| f"--- Telegram Rejected Request: " | |
| f"{response.status_code} - {response.text} ---" | |
| ) | |
| break | |
| else: | |
| print("--- Success: All Telegram message parts delivered ---") | |
| except Exception as send_error: | |
| print(f"--- Emergency: Network Blockage Detected: {str(send_error)} ---") | |
| return {"status": "ok"} | |
| except Exception as e: | |
| print(f"Error in webhook: {str(e)}") | |
| return {"status": "error", "message": str(e)} | |