Update app.py
Browse files
app.py
CHANGED
|
@@ -20,9 +20,10 @@ import uvicorn
|
|
| 20 |
import random
|
| 21 |
import docx # لإضافة دعم .docx
|
| 22 |
|
| 23 |
-
# ========== (الحل) إضافة المكتبات
|
| 24 |
import httpx
|
| 25 |
-
import
|
|
|
|
| 26 |
# ==========================================================
|
| 27 |
|
| 28 |
# ========== تكوين السجلات ==========
|
|
@@ -49,16 +50,58 @@ REPO_ID = "Riy777/Study"
|
|
| 49 |
# ========== حالات المحادثة ==========
|
| 50 |
SELECTING_SUBJECT, SELECTING_ACTION, WAITING_FOR_QUESTION = range(3)
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
# ========== تطبيق FastAPI ==========
|
| 53 |
-
# تعديل: استخدام lifespan بدلاً من on_event
|
| 54 |
@asyncio.contextmanager
|
| 55 |
async def lifespan(app: FastAPI):
|
| 56 |
-
# الكود الذي كان في on_startup
|
| 57 |
logger.info("🚀 بدء تشغيل FastAPI... بدء مهمة تهيئة البوت في الخلفية.")
|
| 58 |
asyncio.create_task(bot.initialize_application())
|
| 59 |
logger.info("✅ خادم FastAPI يعمل. التهيئة (الاتصال بـ Telegram) جارية في الخلفية.")
|
| 60 |
yield
|
| 61 |
-
# كود إيقاف التشغيل (on_shutdown) (إن وجد)
|
| 62 |
logger.info("🛑 إيقاف تشغيل خادم FastAPI.")
|
| 63 |
|
| 64 |
app = FastAPI(title="Medical Lab Bot", version="1.0.0", lifespan=lifespan)
|
|
@@ -83,12 +126,9 @@ class MedicalLabBot:
|
|
| 83 |
logger.info("🔄 جاري تهيئة تطبيق التليجرام...")
|
| 84 |
|
| 85 |
# ========== !! الحل الجذري (DNS) !! ==========
|
| 86 |
-
logger.info("🔧 إعداد عميل HTTP مخصص مع
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
)
|
| 90 |
-
# نستخدم httpx.AsyncClient مع الناقل المخصص
|
| 91 |
-
custom_client = httpx.AsyncClient(transport=transport)
|
| 92 |
# ============================================
|
| 93 |
|
| 94 |
self.application = (
|
|
@@ -99,8 +139,8 @@ class MedicalLabBot:
|
|
| 99 |
)
|
| 100 |
await self.setup_handlers()
|
| 101 |
|
| 102 |
-
max_retries = 3
|
| 103 |
-
retry_delay = 5
|
| 104 |
|
| 105 |
for attempt in range(max_retries):
|
| 106 |
try:
|
|
@@ -545,7 +585,7 @@ class MedicalLabBot:
|
|
| 545 |
keyboard.append([InlineKeyboardButton(display_name, callback_data=f"subject_{subject}")])
|
| 546 |
|
| 547 |
keyboard.append([InlineKeyboardButton("🔄 تحديث قائمة المواد", callback_data="refresh_materials")])
|
| 548 |
-
keyboard.append([InlineKeyboardButton("❓ مساعدة",
|
| 549 |
return keyboard
|
| 550 |
|
| 551 |
async def handle_subject_selection(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
@@ -809,15 +849,6 @@ bot = MedicalLabBot()
|
|
| 809 |
|
| 810 |
# ========== دوال FastAPI ==========
|
| 811 |
|
| 812 |
-
# تم نقل @app.on_event("startup") إلى تعريف "lifespan"
|
| 813 |
-
# @app.on_event("startup")
|
| 814 |
-
# async def on_startup():
|
| 815 |
-
# """بدء تشغيل التطبيق وبدء تهيئة البوت في الخلفية"""
|
| 816 |
-
# logger.info("🚀 بدء تشغيل FastAPI... بدء مهمة تهيئة البوت في الخلفية.")
|
| 817 |
-
# # لا ننتظر (no await) - سيبدأ الخادم فوراً، وستعمل هذه المهمة في الخلفية
|
| 818 |
-
# asyncio.create_task(bot.initialize_application())
|
| 819 |
-
# logger.info("✅ خادم FastAPI يعمل. التهيئة (الاتصال بـ Telegram) جارية في الخلفية.")
|
| 820 |
-
|
| 821 |
@app.get("/", response_class=HTMLResponse)
|
| 822 |
async def root():
|
| 823 |
"""الصفحة الرئيسية"""
|
|
|
|
| 20 |
import random
|
| 21 |
import docx # لإضافة دعم .docx
|
| 22 |
|
| 23 |
+
# ========== (الحل) إضافة المكتبات الصحيحة لحل مشكلة DNS ==========
|
| 24 |
import httpx
|
| 25 |
+
import dns.asyncresolver
|
| 26 |
+
from httpx import AsyncClient, AsyncHTTPTransport, Request, Response
|
| 27 |
# ==========================================================
|
| 28 |
|
| 29 |
# ========== تكوين السجلات ==========
|
|
|
|
| 50 |
# ========== حالات المحادثة ==========
|
| 51 |
SELECTING_SUBJECT, SELECTING_ACTION, WAITING_FOR_QUESTION = range(3)
|
| 52 |
|
| 53 |
+
|
| 54 |
+
# ========== (الحل) فئة الناقل المخصص للـ DNS ==========
|
| 55 |
+
class CustomDNSTransport(AsyncHTTPTransport):
|
| 56 |
+
def __init__(self, *args, **kwargs):
|
| 57 |
+
super().__init__(*args, **kwargs)
|
| 58 |
+
self.resolver = dns.asyncresolver.Resolver()
|
| 59 |
+
self.resolver.nameservers = ['1.1.1.1', '8.8.8.8']
|
| 60 |
+
logger.info("🔧 CustomDNSTransport initialized with 1.1.1.1 and 8.8.8.8")
|
| 61 |
+
|
| 62 |
+
async def handle_async_request(self, request: Request) -> Response:
|
| 63 |
+
try:
|
| 64 |
+
host = request.url.host
|
| 65 |
+
|
| 66 |
+
# التحقق إذا كان Host هو أصلاً IP
|
| 67 |
+
is_ip = True
|
| 68 |
+
try:
|
| 69 |
+
parts = host.split('.')
|
| 70 |
+
if len(parts) != 4 or not all(p.isdigit() for p in parts):
|
| 71 |
+
is_ip = False
|
| 72 |
+
except Exception:
|
| 73 |
+
is_ip = False
|
| 74 |
+
|
| 75 |
+
if is_ip:
|
| 76 |
+
logger.info(f"Host {host} is already an IP. Skipping resolution.")
|
| 77 |
+
else:
|
| 78 |
+
logger.info(f"🔧 Resolving host: {host}")
|
| 79 |
+
result = await self.resolver.resolve(host, 'A')
|
| 80 |
+
ip = result[0].address
|
| 81 |
+
logger.info(f"✅ Resolved {host} to {ip}")
|
| 82 |
+
|
| 83 |
+
# تعيين SNI (مهم جداً لـ SSL/HTTPS)
|
| 84 |
+
request.extensions["sni_hostname"] = host
|
| 85 |
+
# تحديث الـ URL لاستخدام الـ IP
|
| 86 |
+
request.url = request.url.copy_with(host=ip)
|
| 87 |
+
|
| 88 |
+
except Exception as e:
|
| 89 |
+
logger.error(f"❌ DNS resolution failed for {host}: {e}. Trying request with original host...")
|
| 90 |
+
# إذا فشل الحل، اسمح للطلب بالاستمرار مع اسم المضيف الأصلي
|
| 91 |
+
pass
|
| 92 |
+
|
| 93 |
+
# استدعاء المعالج الأصلي (الأب) لإكمال الطلب
|
| 94 |
+
return await super().handle_async_request(request)
|
| 95 |
+
# ==========================================================
|
| 96 |
+
|
| 97 |
+
|
| 98 |
# ========== تطبيق FastAPI ==========
|
|
|
|
| 99 |
@asyncio.contextmanager
|
| 100 |
async def lifespan(app: FastAPI):
|
|
|
|
| 101 |
logger.info("🚀 بدء تشغيل FastAPI... بدء مهمة تهيئة البوت في الخلفية.")
|
| 102 |
asyncio.create_task(bot.initialize_application())
|
| 103 |
logger.info("✅ خادم FastAPI يعمل. التهيئة (الاتصال بـ Telegram) جارية في الخلفية.")
|
| 104 |
yield
|
|
|
|
| 105 |
logger.info("🛑 إيقاف تشغيل خادم FastAPI.")
|
| 106 |
|
| 107 |
app = FastAPI(title="Medical Lab Bot", version="1.0.0", lifespan=lifespan)
|
|
|
|
| 126 |
logger.info("🔄 جاري تهيئة تطبيق التليجرام...")
|
| 127 |
|
| 128 |
# ========== !! الحل الجذري (DNS) !! ==========
|
| 129 |
+
logger.info("🔧 إعداد عميل HTTP مخصص مع CustomDNSTransport...")
|
| 130 |
+
custom_transport = CustomDNSTransport()
|
| 131 |
+
custom_client = httpx.AsyncClient(transport=custom_transport)
|
|
|
|
|
|
|
|
|
|
| 132 |
# ============================================
|
| 133 |
|
| 134 |
self.application = (
|
|
|
|
| 139 |
)
|
| 140 |
await self.setup_handlers()
|
| 141 |
|
| 142 |
+
max_retries = 3
|
| 143 |
+
retry_delay = 5
|
| 144 |
|
| 145 |
for attempt in range(max_retries):
|
| 146 |
try:
|
|
|
|
| 585 |
keyboard.append([InlineKeyboardButton(display_name, callback_data=f"subject_{subject}")])
|
| 586 |
|
| 587 |
keyboard.append([InlineKeyboardButton("🔄 تحديث قائمة المواد", callback_data="refresh_materials")])
|
| 588 |
+
keyboard.append([InlineKeyboardButton("❓ مساعدة", callback_data="general_help")])
|
| 589 |
return keyboard
|
| 590 |
|
| 591 |
async def handle_subject_selection(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|
|
|
| 849 |
|
| 850 |
# ========== دوال FastAPI ==========
|
| 851 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 852 |
@app.get("/", response_class=HTMLResponse)
|
| 853 |
async def root():
|
| 854 |
"""الصفحة الرئيسية"""
|