Update app.py
Browse files
app.py
CHANGED
|
@@ -55,51 +55,71 @@ class MedicalLabBot:
|
|
| 55 |
self.file_cache = {}
|
| 56 |
self.application = None
|
| 57 |
self.is_initialized = False
|
|
|
|
| 58 |
self.load_all_materials()
|
| 59 |
|
| 60 |
async def initialize_application(self):
|
| 61 |
-
"""تهيئة تطبيق التليجرام بشكل غير متزامن مع محاولات إعادة"""
|
| 62 |
try:
|
| 63 |
-
if
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
except Exception as e:
|
| 101 |
logger.error(f"❌ خطأ فادح في تهيئة التطبيق: {e}")
|
| 102 |
self.is_initialized = False
|
|
|
|
| 103 |
return False
|
| 104 |
|
| 105 |
async def setup_handlers(self):
|
|
@@ -765,33 +785,11 @@ bot = MedicalLabBot()
|
|
| 765 |
# ========== دوال FastAPI ==========
|
| 766 |
@app.on_event("startup")
|
| 767 |
async def on_startup():
|
| 768 |
-
"""
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
if not success:
|
| 774 |
-
logger.error("❌ فشل في تهيئة التطبيق الأساسي بعد عدة محاولات")
|
| 775 |
-
# التطبيق سيعمل، لكن is_initialized ستكون False
|
| 776 |
-
return
|
| 777 |
-
|
| 778 |
-
if SPACE_URL:
|
| 779 |
-
webhook_url = f"{SPACE_URL.rstrip('/')}/telegram"
|
| 780 |
-
logger.info(f"ℹ️ جاري إعداد الويب هوك على: {webhook_url}")
|
| 781 |
-
await bot.application.bot.set_webhook(
|
| 782 |
-
url=webhook_url,
|
| 783 |
-
allowed_updates=Update.ALL_TYPES,
|
| 784 |
-
drop_pending_updates=True
|
| 785 |
-
)
|
| 786 |
-
logger.info(f"✅ Webhook set to: {webhook_url}")
|
| 787 |
-
else:
|
| 788 |
-
logger.warning("⚠️ SPACE_URL not set, using polling instead (if run locally)")
|
| 789 |
-
|
| 790 |
-
except Exception as e:
|
| 791 |
-
logger.error(f"❌ Error during startup (likely setting webhook): {e}")
|
| 792 |
-
# إذا فشل الويب هوك، التطبيق لا يزال "مهيأ" من ناحية الكود
|
| 793 |
-
# لكنه لن يستقبل تحديثات
|
| 794 |
-
pass
|
| 795 |
|
| 796 |
@app.get("/", response_class=HTMLResponse)
|
| 797 |
async def root():
|
|
@@ -799,6 +797,16 @@ async def root():
|
|
| 799 |
materials_count = len(bot.available_materials)
|
| 800 |
total_files = sum(len(material['files']) for material in bot.available_materials.values())
|
| 801 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
return f"""
|
| 803 |
<html>
|
| 804 |
<head>
|
|
@@ -806,7 +814,7 @@ async def root():
|
|
| 806 |
<style>
|
| 807 |
body {{ font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }}
|
| 808 |
.container {{ max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 15px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }}
|
| 809 |
-
.status {{ padding: 20px; background: #f0f8ff; border-radius: 10px; border-left: 5px solid
|
| 810 |
h1 {{ color: #2c3e50; text-align: center; }}
|
| 811 |
ul {{ line-height: 1.8; }}
|
| 812 |
li {{ margin: 10px 0; }}
|
|
@@ -816,8 +824,7 @@ async def root():
|
|
| 816 |
<div class="container">
|
| 817 |
<h1>🏥 بوت المختبرات الطبية الذكي</h1>
|
| 818 |
<div class="status">
|
| 819 |
-
<h2
|
| 820 |
-
<p><strong>الحالة:</strong> {'✅ نشط ومهيأ' if bot.is_initialized else '❌ غير مهيأ (فشل الاتصال بـ Telegram)'}</p>
|
| 821 |
<p><strong>المواد المحملة:</strong> {materials_count} مادة</p>
|
| 822 |
<p><strong>إجمالي الملفات:</strong> {total_files} ملف</p>
|
| 823 |
</div>
|
|
@@ -843,8 +850,8 @@ async def handle_telegram_update(request: Request):
|
|
| 843 |
try:
|
| 844 |
# التحقق من أن التطبيق مهيأ
|
| 845 |
if not bot.is_initialized or not bot.application:
|
| 846 |
-
logger.error("❌ التطبيق غير مهيأ، لا يمكن معالجة
|
| 847 |
-
raise HTTPException(status_code=503, detail="Application not initialized")
|
| 848 |
|
| 849 |
update_data = await request.json()
|
| 850 |
update = Update.de_json(update_data, bot.application.bot)
|
|
@@ -863,10 +870,18 @@ async def health_check():
|
|
| 863 |
materials_count = len(bot.available_materials)
|
| 864 |
total_files = sum(len(material['files']) for material in bot.available_materials.values())
|
| 865 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 866 |
return {
|
| 867 |
-
"status":
|
| 868 |
"service": "medical-lab-bot",
|
| 869 |
-
"
|
| 870 |
"materials_loaded": materials_count,
|
| 871 |
"total_files": total_files,
|
| 872 |
"timestamp": datetime.now().isoformat()
|
|
|
|
| 55 |
self.file_cache = {}
|
| 56 |
self.application = None
|
| 57 |
self.is_initialized = False
|
| 58 |
+
self.initialization_status = "pending" # تتبع حالة التهيئة
|
| 59 |
self.load_all_materials()
|
| 60 |
|
| 61 |
async def initialize_application(self):
|
| 62 |
+
"""تهيئة تطبيق التليجرام بشكل غير متزامن مع محاولات إعادة (للتشغيل في الخلفية)"""
|
| 63 |
try:
|
| 64 |
+
if self.is_initialized:
|
| 65 |
+
return True
|
| 66 |
+
|
| 67 |
+
logger.info("🔄 جاري تهيئة تطبيق التليجرام...")
|
| 68 |
+
self.application = (
|
| 69 |
+
Application.builder()
|
| 70 |
+
.token(TELEGRAM_BOT_TOKEN)
|
| 71 |
+
.build()
|
| 72 |
+
)
|
| 73 |
+
await self.setup_handlers()
|
| 74 |
+
|
| 75 |
+
# ========== !! الحــل !! ==========
|
| 76 |
+
# زيادة المحاولات والتأخير بشكل كبير للتعامل مع بطء شبكة HFS
|
| 77 |
+
max_retries = 5 # زيادة إلى 5 محاولات
|
| 78 |
+
retry_delay = 10 # زيادة إلى 10 ثواني
|
| 79 |
+
|
| 80 |
+
for attempt in range(max_retries):
|
| 81 |
+
try:
|
| 82 |
+
logger.info(f"🚀 محاولة تهيئة الاتصال بـ Telegram (محاولة {attempt + 1}/{max_retries})...")
|
| 83 |
+
# 1. الاتصال بـ Telegram (getMe)
|
| 84 |
+
await self.application.initialize()
|
| 85 |
+
logger.info("✅ تم تهيئة الاتصال بـ Telegram بنجاح.")
|
| 86 |
+
|
| 87 |
+
# 2. ضبط الـ Webhook (فقط بعد نجاح الاتصال)
|
| 88 |
+
if SPACE_URL:
|
| 89 |
+
webhook_url = f"{SPACE_URL.rstrip('/')}/telegram"
|
| 90 |
+
logger.info(f"ℹ️ جاري إعداد الويب هوك على: {webhook_url}")
|
| 91 |
+
await self.application.bot.set_webhook(
|
| 92 |
+
url=webhook_url,
|
| 93 |
+
allowed_updates=Update.ALL_TYPES,
|
| 94 |
+
drop_pending_updates=True
|
| 95 |
+
)
|
| 96 |
+
logger.info(f"✅ Webhook set to: {webhook_url}")
|
| 97 |
+
else:
|
| 98 |
+
logger.warning("⚠️ SPACE_URL not set. Webhook cannot be set.")
|
| 99 |
+
|
| 100 |
+
# 3. ضبط الحالة على "ناجح"
|
| 101 |
+
self.is_initialized = True
|
| 102 |
+
self.initialization_status = "success"
|
| 103 |
+
logger.info("✅✅✅ التطبيق جاهز لاستقبال الطلبات.")
|
| 104 |
+
return True # نجح، اخرج من الدالة
|
| 105 |
+
|
| 106 |
+
except Exception as e:
|
| 107 |
+
logger.warning(f"⚠️ فشلت ��حاولة التهيئة {attempt + 1}: {e}")
|
| 108 |
+
if attempt < max_retries - 1:
|
| 109 |
+
logger.info(f"⏳ الانتظار {retry_delay} ثواني قبل إعادة المحاولة...")
|
| 110 |
+
await asyncio.sleep(retry_delay)
|
| 111 |
+
else:
|
| 112 |
+
logger.error(f"❌ فشل تهيئة التطبيق نهائياً بعد {max_retries} محاولات.")
|
| 113 |
+
|
| 114 |
+
# فشل بعد كل المحاولات
|
| 115 |
+
self.is_initialized = False
|
| 116 |
+
self.initialization_status = "failed"
|
| 117 |
+
return False
|
| 118 |
|
| 119 |
except Exception as e:
|
| 120 |
logger.error(f"❌ خطأ فادح في تهيئة التطبيق: {e}")
|
| 121 |
self.is_initialized = False
|
| 122 |
+
self.initialization_status = "failed"
|
| 123 |
return False
|
| 124 |
|
| 125 |
async def setup_handlers(self):
|
|
|
|
| 785 |
# ========== دوال FastAPI ==========
|
| 786 |
@app.on_event("startup")
|
| 787 |
async def on_startup():
|
| 788 |
+
"""بدء تشغيل التطبيق وبدء تهيئة البوت في الخلفية"""
|
| 789 |
+
logger.info("🚀 بدء تشغيل FastAPI... بدء مهمة تهيئة البوت في الخلفية.")
|
| 790 |
+
# لا ننتظر (no await) - سيبدأ الخادم فوراً، وستعمل هذه المهمة في الخلفية
|
| 791 |
+
asyncio.create_task(bot.initialize_application())
|
| 792 |
+
logger.info("✅ خادم FastAPI يعمل. التهيئة (الاتصال بـ Telegram) جارية في الخلفية.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
|
| 794 |
@app.get("/", response_class=HTMLResponse)
|
| 795 |
async def root():
|
|
|
|
| 797 |
materials_count = len(bot.available_materials)
|
| 798 |
total_files = sum(len(material['files']) for material in bot.available_materials.values())
|
| 799 |
|
| 800 |
+
# تحديد رسالة الحالة بناءً على حالة التهيئة
|
| 801 |
+
status_message = "⏳ جاري التهيئة (الاتصال بـ Telegram)..."
|
| 802 |
+
status_color = "#ffc107" # أصفر
|
| 803 |
+
if bot.initialization_status == "success":
|
| 804 |
+
status_message = "✅ نشط ومهيأ"
|
| 805 |
+
status_color = "#28a745" # أخضر
|
| 806 |
+
elif bot.initialization_status == "failed":
|
| 807 |
+
status_message = "❌ فشل التهيئة (خطأ في الاتصال بـ Telegram)"
|
| 808 |
+
status_color = "#dc3545" # أحمر
|
| 809 |
+
|
| 810 |
return f"""
|
| 811 |
<html>
|
| 812 |
<head>
|
|
|
|
| 814 |
<style>
|
| 815 |
body {{ font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }}
|
| 816 |
.container {{ max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 15px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }}
|
| 817 |
+
.status {{ padding: 20px; background: #f0f8ff; border-radius: 10px; border-left: 5px solid {status_color}; }}
|
| 818 |
h1 {{ color: #2c3e50; text-align: center; }}
|
| 819 |
ul {{ line-height: 1.8; }}
|
| 820 |
li {{ margin: 10px 0; }}
|
|
|
|
| 824 |
<div class="container">
|
| 825 |
<h1>🏥 بوت المختبرات الطبية الذكي</h1>
|
| 826 |
<div class="status">
|
| 827 |
+
<h2>حالة البوت: {status_message}</h2>
|
|
|
|
| 828 |
<p><strong>المواد المحملة:</strong> {materials_count} مادة</p>
|
| 829 |
<p><strong>إجمالي الملفات:</strong> {total_files} ملف</p>
|
| 830 |
</div>
|
|
|
|
| 850 |
try:
|
| 851 |
# التحقق من أن التطبيق مهيأ
|
| 852 |
if not bot.is_initialized or not bot.application:
|
| 853 |
+
logger.error("❌ التطبيق غير مهيأ، لا يمكن معالجة التحديث (ربما لا يزال قيد التهيئة).")
|
| 854 |
+
raise HTTPException(status_code=503, detail="Application not initialized or still initializing")
|
| 855 |
|
| 856 |
update_data = await request.json()
|
| 857 |
update = Update.de_json(update_data, bot.application.bot)
|
|
|
|
| 870 |
materials_count = len(bot.available_materials)
|
| 871 |
total_files = sum(len(material['files']) for material in bot.available_materials.values())
|
| 872 |
|
| 873 |
+
status = "unhealthy"
|
| 874 |
+
if bot.initialization_status == "pending":
|
| 875 |
+
status = "initializing"
|
| 876 |
+
elif bot.initialization_status == "success":
|
| 877 |
+
status = "healthy"
|
| 878 |
+
elif bot.initialization_status == "failed":
|
| 879 |
+
status = "unhealthy_failed_init"
|
| 880 |
+
|
| 881 |
return {
|
| 882 |
+
"status": status,
|
| 883 |
"service": "medical-lab-bot",
|
| 884 |
+
"initialization_status": bot.initialization_status,
|
| 885 |
"materials_loaded": materials_count,
|
| 886 |
"total_files": total_files,
|
| 887 |
"timestamp": datetime.now().isoformat()
|