Spaces:
Runtime error
Runtime error
| # utils/telegram_utils.py | |
| import os | |
| import json | |
| import httpx # Sử dụng httpx để gửi request bất đồng bộ | |
| # Lấy URL của Cloudflare Worker từ biến môi trường (Secrets) | |
| WORKER_URL = os.environ.get("WORKER_URL") | |
| async def send_telegram_message(chat_id, text, reply_markup=None): | |
| """Gửi tin nhắn văn bản qua Cloudflare Worker.""" | |
| if not WORKER_URL: | |
| print("ERROR: WORKER_URL is not set!") | |
| return False | |
| url = f"{WORKER_URL}" | |
| # Dùng Markdown để định dạng nếu Worker hỗ trợ | |
| payload = {'action': 'sendMessage', 'chat_id': str(chat_id), 'text': text, 'parse_mode': 'Markdown'} | |
| if reply_markup: | |
| # Đảm bảo reply_markup là JSON string nếu nó là dict | |
| payload['reply_markup'] = json.dumps(reply_markup) if isinstance(reply_markup, dict) else reply_markup | |
| try: | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post(url, json=payload, timeout=60) | |
| response.raise_for_status() # Báo lỗi nếu request thất bại (status code >= 400) | |
| # print(f"Sent message to {chat_id} via worker: {response.json()}") # Tùy chọn: log lại | |
| return True | |
| except httpx.RequestError as e: | |
| print(f"ERROR sending message via worker (RequestError): {e}") | |
| except httpx.HTTPStatusError as e: | |
| print(f"ERROR sending message via worker (HTTPStatusError {e.response.status_code}): {e.response.text}") | |
| except Exception as e: | |
| print(f"ERROR sending message via worker (General): {e}") | |
| return False | |
| async def send_telegram_video(chat_id, video_path, caption): | |
| """Gửi file video qua Cloudflare Worker.""" | |
| if not WORKER_URL: | |
| print("ERROR: WORKER_URL is not set!") | |
| return False | |
| url = f"{WORKER_URL}" | |
| if not os.path.exists(video_path): | |
| print(f"ERROR: Video file not found: {video_path}") | |
| # Gửi thông báo lỗi về Telegram nếu file không tồn tại | |
| await send_telegram_message(chat_id, f"❌ Lỗi Server: Không tìm thấy file `{os.path.basename(video_path)}` để gửi.") | |
| return False | |
| # Kiểm tra kích thước file (Tùy chọn, Telegram Bot API thường giới hạn 50MB) | |
| file_size_mb = os.path.getsize(video_path) / (1024 * 1024) | |
| if file_size_mb > 49.0: # Giới hạn an toàn < 50MB | |
| print(f"WARNING: File {video_path} ({file_size_mb:.1f}MB) might be too large for Telegram Bot API.") | |
| await send_telegram_message(chat_id, f"⚠️ File `{os.path.basename(video_path)}` ({file_size_mb:.1f}MB) có thể quá lớn để gửi qua bot.") | |
| # Bạn có thể chọn trả về False ở đây nếu muốn chặn gửi file lớn | |
| # return False | |
| # Khi gửi file, payload thường được gửi dưới dạng 'data' (form data), không phải 'json' | |
| data_payload = { | |
| 'action': 'sendVideo', | |
| 'chat_id': str(chat_id), | |
| 'caption': caption | |
| } | |
| try: | |
| with open(video_path, 'rb') as video_file: | |
| # files={'tên_trường_file': (tên_file, đối_tượng_file, kiểu_mime)} | |
| files_payload = { 'video': (os.path.basename(video_path), video_file, 'video/mp4') } | |
| async with httpx.AsyncClient() as client: | |
| # Dùng data=data_payload và files=files_payload | |
| response = await client.post(url, data=data_payload, files=files_payload, timeout=600) # Tăng timeout | |
| response.raise_for_status() | |
| # print(f"Sent video to {chat_id} via worker: {response.json()}") # Tùy chọn: log lại | |
| return True | |
| except httpx.RequestError as e: | |
| print(f"ERROR sending video via worker (RequestError): {e}") | |
| await send_telegram_message(chat_id, f"❌ Lỗi mạng khi gửi file `{os.path.basename(video_path)}`.") | |
| except httpx.HTTPStatusError as e: | |
| print(f"ERROR sending video via worker (HTTPStatusError {e.response.status_code}): {e.response.text}") | |
| await send_telegram_message(chat_id, f"❌ Lỗi từ server khi gửi file `{os.path.basename(video_path)}`: {e.response.status_code}") | |
| except Exception as e: | |
| print(f"ERROR sending video via worker (General): {e}") | |
| await send_telegram_message(chat_id, f"❌ Lỗi không xác định khi gửi file `{os.path.basename(video_path)}`.") | |
| return False | |
| async def answer_telegram_callback_query(callback_query_id, text="OK"): | |
| """Trả lời một callback query qua Worker.""" | |
| if not WORKER_URL: print("ERROR: WORKER_URL not set!"); return | |
| url = f"{WORKER_URL}" | |
| payload = {'action': 'answerCallbackQuery', 'callback_query_id': callback_query_id, 'text': text} | |
| try: | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post(url, json=payload, timeout=30) | |
| response.raise_for_status() | |
| except Exception as e: | |
| print(f"ERROR answering callback query {callback_query_id} via worker: {e}") | |
| async def edit_telegram_message_text(chat_id, message_id, text, reply_markup=None): | |
| """Sửa nội dung tin nhắn văn bản qua Worker.""" | |
| if not WORKER_URL: print("ERROR: WORKER_URL not set!"); return | |
| url = f"{WORKER_URL}" | |
| payload = {'action': 'editMessageText', 'chat_id': str(chat_id), 'message_id': message_id, 'text': text, 'parse_mode': 'Markdown'} | |
| if reply_markup: | |
| payload['reply_markup'] = json.dumps(reply_markup) if isinstance(reply_markup, dict) else reply_markup | |
| try: | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post(url, json=payload, timeout=60) | |
| response.raise_for_status() | |
| except Exception as e: | |
| print(f"ERROR editing message {message_id} in chat {chat_id} via worker: {e}") | |
| # (Tùy chọn) Thêm các hàm khác nếu cần, ví dụ: delete_telegram_message, edit_telegram_reply_markup | |
| # async def delete_telegram_message(chat_id, message_id): ... |