import os import json import asyncio import httpx from fastapi import FastAPI, Request, Response from fastapi.responses import JSONResponse from contextlib import asynccontextmanager import gradio as gr import uvicorn # ================== التكوينات ================== TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") WORKER_URL = os.getenv("WORKER_URL", "https://your-worker.workers.dev") if not TOKEN: raise ValueError("❌ TELEGRAM_BOT_TOKEN غير موجود") # الرابط الرئيسي لإرسال الطلبات عبر الوكيل الخاص بك BASE_API_URL = f"{WORKER_URL}/bot{TOKEN}" # ================== وظائف الاتصال بتيليجرام ================== async def send_telegram_request(method: str, payload: dict): """إرسال الطلبات مباشرة إلى واجهة تيليجرام البرمجية عبر الوكيل""" try: async with httpx.AsyncClient(timeout=30.0) as client: url = f"{BASE_API_URL}/{method}" response = await client.post(url, json=payload) response.raise_for_status() return response.json() except Exception as e: print(f"❌ خطأ أثناء إرسال الطلب [{method}]: {e}") return None def get_main_keyboard(): """بناء لوحة الأزرار الشفافة بتنسيق ومصفوفات JSON مخصصة""" return { "inline_keyboard": [ [ {"text": "📚 زر 1 - معلومات", "callback_data": "btn_1"}, {"text": "💡 زر 2 - نصائح", "callback_data": "btn_2"}, ], [ {"text": "🎯 زر 3 - عروض", "callback_data": "btn_3"}, ] ] } RESPONSES = { "btn_1": "📚 هذا رد الزر الأول: معلومات عامة عن البوت.", "btn_2": "💡 هذا رد الزر الثاني: نصائح مفيدة لتجاوز الحظر.", "btn_3": "🎯 هذا رد الزر الثالث: عروض حصرية للمستخدمين." } # ================== إدارة دورة حياة التطبيق (Lifespan) ================== @asynccontextmanager async def lifespan(app: FastAPI): """يتم تشغيل هذا الجزء عند إقلاع الحاوية لربط الـ Webhook فقط دون تهيئة البوت""" space_host = os.getenv("SPACE_HOST") if space_host: # تأخير بسيط لضمان جاهزية شبكة الحاوية للاتصال الخارجي await asyncio.sleep(2) webhook_url = f"https://{space_host}/webhook" print(f"🔄 جاري ضبط الـ Webhook على الرابط: {webhook_url}") # إرسال طلب ربط الـ Webhook async with httpx.AsyncClient(timeout=20.0) as client: try: url = f"{BASE_API_URL}/setWebhook" # أضفنا drop_pending_updates لتجاهل الرسائل القديمة المتراكمة أثناء توقف البوت resp = await client.post(url, json={"url": webhook_url, "drop_pending_updates": True}) if resp.status_code == 200: print(f"✅ تم ضبط الـ Webhook بنجاح: {resp.text}") else: print(f"⚠️ فشل ضبط Webhook (خطأ HTTP {resp.status_code}): {resp.text}") except Exception as e: # استخدام repr(e) لطباعة التفاصيل الدقيقة للخطأ إذا حدث print(f"⚠️ تحذير: تعذر ضبط الـ Webhook: {repr(e)}") yield # ================== إنشاء تطبيق FastAPI ================== app = FastAPI(title="Telegram Bot with Gradio Dashboard", lifespan=lifespan) @app.post("/webhook") async def webhook(request: Request): """استقبال التحديثات مباشرة من تيليجرام ومعالجتها فورياً كـ JSON""" try: data = await request.json() # 1. معالجة الرسائل النصية العادية (مثل أمر /start) if "message" in data: message = data["message"] chat_id = message["chat"]["id"] text = message.get("text", "") if text == "/start": welcome_text = "👋 مرحباً بك في البوت!\n\nاختر أحد الأزرار أدناه:" payload = { "chat_id": chat_id, "text": welcome_text, "parse_mode": "HTML", "reply_markup": get_main_keyboard() } # تشغيل المهمة بالخلفية لسرعة استجابة السيرفر الـ Webhook asyncio.create_task(send_telegram_request("sendMessage", payload)) # 2. معالجة النقرات على الأزرار الشفافة (Callback Queries) elif "callback_query" in data: callback_query = data["callback_query"] callback_id = callback_query["id"] chat_id = callback_query["message"]["chat"]["id"] callback_data = callback_query.get("data", "") # إخطار تيليجرام باستلام النقرة لإيقاف مؤشر التحميل على الزر asyncio.create_task(send_telegram_request("answerCallbackQuery", {"callback_query_id": callback_id})) # جلب النص المقابل للزر وإرساله response_text = RESPONSES.get(callback_data, "⚠️ زر غير معروف") payload = { "chat_id": chat_id, "text": response_text, "parse_mode": "HTML", "reply_markup": get_main_keyboard() } asyncio.create_task(send_telegram_request("sendMessage", payload)) return Response(status_code=200) except Exception as e: print(f"❌ خطأ داخلي في معالجة الـ Webhook: {e}") return JSONResponse(status_code=500, content={"error": str(e)}) @app.get("/") @app.get("/health") async def health(): return {"status": "healthy", "bot": "running", "proxy_url": WORKER_URL} # ================== إنشاء واجهة Gradio ================== def create_gradio_interface(): with gr.Blocks(title="🤖 Telegram Bot Dashboard") as demo: gr.Markdown("# 🤖 Telegram Bot Dashboard") gr.Markdown(f"**الوكيل المستخدم:** `{WORKER_URL}`") gr.Markdown("**حالة النظام:** 🟢 يعمل بثبات وبدون تهيئة معقدة") return demo gradio_app = create_gradio_interface() # دمج واجهة وبوابات Gradio مع FastAPI في المسار الفرعي /gradio app = gr.mount_gradio_app(app, gradio_app, path="/gradio") # ================== تشغيل خادم التطبيق ================== #if __name__ == "__main__": # print("🚀 تشغيل السيرفر الرئيسي على المنفذ 7860...") # uvicorn.run(app, host="0.0.0.0", port=7860)