Riy777 commited on
Commit
d60f4cf
·
verified ·
1 Parent(s): d8e8d49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -78
app.py CHANGED
@@ -28,7 +28,7 @@ logger = logging.getLogger(__name__)
28
  # ========== التوكنات ومتغيرات البيئة ==========
29
  TELEGRAM_BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN')
30
  NVAPI_API_KEY = os.environ.get('NVAPI_API_KEY')
31
- WEBHOOK_URL = os.environ.get('SPACE_URL', '')
32
 
33
  # ========== تكوين عميل NVIDIA ==========
34
  nvidia_client = OpenAI(
@@ -58,39 +58,82 @@ class MedicalLabBot:
58
  """تهيئة تطبيق التليجرام"""
59
  try:
60
  if not self.application:
61
- # استخدام تكوين مبسط لتجنب مشكلة proxies
62
- self.application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
 
 
 
 
63
  self.setup_handlers()
64
  logger.info("✅ تم تهيئة تطبيق التليجرام بنجاح")
65
  except Exception as e:
66
  logger.error(f"❌ خطأ في تهيئة التطبيق: {e}")
67
- # محاولة بديلة
68
- self.application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
69
- self.setup_handlers()
70
 
71
  def setup_handlers(self):
72
  """إعداد معالجات التليجرام"""
 
73
  conv_handler = ConversationHandler(
74
  entry_points=[CommandHandler('start', self.start)],
75
  states={
76
  SELECTING_SUBJECT: [
77
- CallbackQueryHandler(self.handle_subject_selection, pattern='^subject_|general_help|refresh_materials$')
78
  ],
79
  SELECTING_ACTION: [
80
- CallbackQueryHandler(self.handle_action_selection),
81
- CallbackQueryHandler(self.handle_subject_selection, pattern='^subject_')
82
  ],
83
  WAITING_FOR_QUESTION: [
84
- MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message)
 
85
  ]
86
  },
87
- fallbacks=[CommandHandler('start', self.start)]
 
 
 
 
 
 
88
  )
89
 
 
90
  self.application.add_handler(conv_handler)
91
- self.application.add_handler(CallbackQueryHandler(self.handle_callback_query, pattern='^more_questions|change_subject|back_to_actions$'))
 
 
 
 
92
  logger.info("✅ تم إعداد معالجات التليجرام")
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  def load_all_materials(self):
95
  """تحميل جميع المواد والملفات من Hugging Face"""
96
  try:
@@ -132,10 +175,10 @@ class MedicalLabBot:
132
  continue
133
 
134
  self.available_materials = materials
135
- logger.info(f"تم تحميل {len(materials)} مادة بنجاح")
136
 
137
  except Exception as e:
138
- logger.error(f"خطأ في تحميل المواد: {e}")
139
  self.available_materials = {'Biochemistry': {'files': [], 'file_details': {}}}
140
 
141
  def extract_file_info(self, file_name, file_path):
@@ -170,7 +213,7 @@ class MedicalLabBot:
170
  if file_path in self.file_cache:
171
  return self.file_cache[file_path]
172
 
173
- logger.info(f"جاري تحميل الملف: {file_path}")
174
 
175
  local_path = hf_hub_download(
176
  repo_id=REPO_ID,
@@ -195,7 +238,7 @@ class MedicalLabBot:
195
  return content
196
 
197
  except Exception as e:
198
- logger.error(f"خطأ في تحميل الملف {file_path}: {e}")
199
  return f"❌ تعذر تحميل محتوى الملف: {str(e)}"
200
 
201
  async def extract_pdf_content(self, pdf_path):
@@ -231,7 +274,7 @@ class MedicalLabBot:
231
  return text_content
232
 
233
  except Exception as e:
234
- logger.error(f"خطأ في استخراج محتوى PDF: {e}")
235
  return f"❌ حدث خطأ في قراءة ملف PDF: {str(e)}"
236
 
237
  async def extract_doc_content(self, doc_path):
@@ -242,7 +285,7 @@ class MedicalLabBot:
242
  content += "للاستفادة الكاملة، يرجى تحويل الملف إلى PDF أو طلب شرح محتوى معين."
243
  return content
244
  except Exception as e:
245
- logger.error(f"خطأ في استخراج محتوى Word: {e}")
246
  return f"❌ حدث خطأ في قراءة ملف Word: {str(e)}"
247
 
248
  def get_user_memory(self, user_id):
@@ -296,7 +339,11 @@ class MedicalLabBot:
296
  keyboard = self.create_subjects_keyboard()
297
  reply_markup = InlineKeyboardMarkup(keyboard)
298
 
299
- await update.message.reply_text(welcome_text, reply_markup=reply_markup)
 
 
 
 
300
  return SELECTING_SUBJECT
301
 
302
  def create_subjects_keyboard(self):
@@ -366,10 +413,7 @@ class MedicalLabBot:
366
  subject = context.user_data.get('current_subject', 'general')
367
 
368
  if action == "main_menu":
369
- keyboard = self.create_subjects_keyboard()
370
- reply_markup = InlineKeyboardMarkup(keyboard)
371
- await query.edit_message_text("اختر المادة:", reply_markup=reply_markup)
372
- return SELECTING_SUBJECT
373
 
374
  elif action == "browse_files":
375
  return await self.browse_available_files(query, context)
@@ -479,7 +523,7 @@ class MedicalLabBot:
479
  return SELECTING_ACTION
480
 
481
  except Exception as e:
482
- logger.error(f"Error processing message: {e}")
483
  await update.message.reply_text("❌ حدث خطأ أثناء معالجة طلبك. يرجى المحاولة مرة أخرى.")
484
  return SELECTING_ACTION
485
 
@@ -523,7 +567,7 @@ class MedicalLabBot:
523
  return f"**📖 شرح {file_name}**\n\n{response}"
524
 
525
  except Exception as e:
526
- logger.error(f"Error explaining lecture: {e}")
527
  return "❌ عذراً، حدث خطأ في شرح المحاضرة. يرجى المحاولة مرة أخرى."
528
 
529
  async def find_requested_file(self, request, subject):
@@ -582,7 +626,7 @@ class MedicalLabBot:
582
  return response
583
 
584
  except Exception as e:
585
- logger.error(f"Error explaining concept: {e}")
586
  return await self.fallback_explain_concept(concept, subject, user_id)
587
 
588
  async def fallback_explain_concept(self, concept, subject, user_id):
@@ -629,7 +673,7 @@ class MedicalLabBot:
629
  return f"**❓ أسئلة {subject}**\n\n{response}"
630
 
631
  except Exception as e:
632
- logger.error(f"Error generating questions: {e}")
633
  return await self.fallback_generate_questions(subject, user_id)
634
 
635
  async def fallback_generate_questions(self, subject, user_id):
@@ -716,12 +760,12 @@ class MedicalLabBot:
716
 
717
  reasoning = getattr(completion.choices[0].message, "reasoning_content", None)
718
  if reasoning:
719
- logger.info(f"Reasoning content for user {user_id}: {reasoning}")
720
 
721
  return completion.choices[0].message.content
722
 
723
  except Exception as e:
724
- logger.error(f"Error calling NVIDIA AI: {e}")
725
  return "❌ عذراً، حدث خطأ في الاتصال بالذكاء الاصطناعي. يرجى المحاولة مرة أخرى لاحقاً."
726
 
727
  async def handle_general_help(self, query, context):
@@ -761,43 +805,6 @@ class MedicalLabBot:
761
 
762
  await query.edit_message_text(help_text, reply_markup=reply_markup)
763
  return SELECTING_SUBJECT
764
-
765
- async def handle_callback_query(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
766
- """معالجة استعلامات الـ Callback"""
767
- query = update.callback_query
768
- await query.answer()
769
-
770
- if query.data == "more_questions":
771
- await query.edit_message_text("💬 اكتب سؤالك أو طلبك:")
772
- context.user_data['waiting_for'] = 'general'
773
- return WAITING_FOR_QUESTION
774
-
775
- elif query.data == "change_subject":
776
- keyboard = self.create_subjects_keyboard()
777
- reply_markup = InlineKeyboardMarkup(keyboard)
778
- await query.edit_message_text("اختر المادة:", reply_markup=reply_markup)
779
- return SELECTING_SUBJECT
780
-
781
- elif query.data == "back_to_actions":
782
- subject = context.user_data.get('current_subject')
783
- subject_name = subject.replace('_', ' ').title() if subject else "المادة"
784
-
785
- keyboard = [
786
- [InlineKeyboardButton("📖 شرح محاضرة محددة", callback_data="explain_lecture")],
787
- [InlineKeyboardButton("🔍 استعراض جميع الملفات", callback_data="browse_files")],
788
- [InlineKeyboardButton("❓ أسئلة عن المادة", callback_data="generate_questions")],
789
- [InlineKeyboardButton("📝 ملخص المادة", callback_data="summarize_content")],
790
- [InlineKeyboardButton("🧪 تفسير مفهوم", callback_data="explain_concept")],
791
- [InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data="main_menu")]
792
- ]
793
- reply_markup = InlineKeyboardMarkup(keyboard)
794
-
795
- await query.edit_message_text(
796
- f"📚 **{subject_name}**\n\nاختر الخدمة التي تريدها:",
797
- reply_markup=reply_markup,
798
- parse_mode='Markdown'
799
- )
800
- return SELECTING_ACTION
801
 
802
  # ========== إنشاء كائن البوت ==========
803
  bot = MedicalLabBot()
@@ -810,14 +817,15 @@ async def on_startup():
810
  # تهيئة التطبيق
811
  bot.initialize_application()
812
 
813
- if WEBHOOK_URL:
 
814
  await bot.application.bot.set_webhook(
815
- url=f"{WEBHOOK_URL}/telegram",
816
  allowed_updates=Update.ALL_TYPES
817
  )
818
- logger.info(f"✅ Webhook set to: {WEBHOOK_URL}/telegram")
819
  else:
820
- logger.warning("⚠️ WEBHOOK_URL not set, using polling instead")
821
 
822
  except Exception as e:
823
  logger.error(f"❌ Error during startup: {e}")
@@ -830,9 +838,12 @@ async def root():
830
  <head>
831
  <title>Medical Lab Bot</title>
832
  <style>
833
- body { font-family: Arial, sans-serif; margin: 40px; }
834
- .container { max-width: 800px; margin: 0 auto; }
835
- .status { padding: 20px; background: #f0f8ff; border-radius: 10px; }
 
 
 
836
  </style>
837
  </head>
838
  <body>
@@ -841,17 +852,21 @@ async def root():
841
  <div class="status">
842
  <h2>✅ البوت يعمل بنجاح</h2>
843
  <p>البوت متصل بـ Telegram وجاهز لاستقبال الرسائل.</p>
844
- <p>استخدم الرابط التالي للتواصل مع البوت:</p>
845
- <p><strong>https://t.me/your_bot_username</strong></p>
846
  </div>
847
- <h3>المميزات:</h3>
848
  <ul>
849
  <li>📚 شرح المواد الدراسية من ملفات PDF وWord</li>
850
  <li>❓ توليد أسئلة متنوعة للمراجعة</li>
851
  <li>📖 تلخيص المحتوى الدراسي</li>
852
  <li>🧪 تفسير المفاهيم العلمية</li>
853
- <li>🎯 اختبار الفهم</li>
 
854
  </ul>
 
 
 
855
  </div>
856
  </body>
857
  </html>
@@ -872,9 +887,19 @@ async def handle_telegram_update(request: Request):
872
  @app.get("/health")
873
  async def health_check():
874
  """فحص صحة الخدمة"""
875
- return {"status": "healthy", "service": "medical-lab-bot"}
 
 
 
 
 
 
 
 
 
876
 
877
  # ========== التشغيل الرئيسي ==========
878
  if __name__ == "__main__":
879
  port = int(os.environ.get("PORT", 7860))
 
880
  uvicorn.run(app, host="0.0.0.0", port=port)
 
28
  # ========== التوكنات ومتغيرات البيئة ==========
29
  TELEGRAM_BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN')
30
  NVAPI_API_KEY = os.environ.get('NVAPI_API_KEY')
31
+ SPACE_URL = os.environ.get('SPACE_URL', '')
32
 
33
  # ========== تكوين عميل NVIDIA ==========
34
  nvidia_client = OpenAI(
 
58
  """تهيئة تطبيق التليجرام"""
59
  try:
60
  if not self.application:
61
+ # استخدام تكوين مبسط مع إعدادات محددة
62
+ self.application = (
63
+ Application.builder()
64
+ .token(TELEGRAM_BOT_TOKEN)
65
+ .build()
66
+ )
67
  self.setup_handlers()
68
  logger.info("✅ تم تهيئة تطبيق التليجرام بنجاح")
69
  except Exception as e:
70
  logger.error(f"❌ خطأ في تهيئة التطبيق: {e}")
71
+ raise
 
 
72
 
73
  def setup_handlers(self):
74
  """إعداد معالجات التليجرام"""
75
+ # إعداد Conversation Handler مع إعدادات محسنة
76
  conv_handler = ConversationHandler(
77
  entry_points=[CommandHandler('start', self.start)],
78
  states={
79
  SELECTING_SUBJECT: [
80
+ CallbackQueryHandler(self.handle_subject_selection, pattern='^(subject_|general_help|refresh_materials)')
81
  ],
82
  SELECTING_ACTION: [
83
+ CallbackQueryHandler(self.handle_action_selection, pattern='^(explain_lecture|browse_files|generate_questions|summarize_content|explain_concept|main_menu)$')
 
84
  ],
85
  WAITING_FOR_QUESTION: [
86
+ MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message),
87
+ CallbackQueryHandler(self.handle_back_actions, pattern='^back_to_actions$')
88
  ]
89
  },
90
+ fallbacks=[
91
+ CommandHandler('start', self.start),
92
+ CallbackQueryHandler(self.handle_main_menu, pattern='^main_menu$')
93
+ ],
94
+ name="medical_lab_conversation",
95
+ persistent=False,
96
+ per_message=False # هذا الإعداد يقلل التحذيرات
97
  )
98
 
99
+ # إضافة الـ handlers
100
  self.application.add_handler(conv_handler)
101
+
102
+ # إضافة handlers منفصلة للاستعلامات العامة
103
+ self.application.add_handler(CallbackQueryHandler(self.handle_more_questions, pattern='^more_questions$'))
104
+ self.application.add_handler(CallbackQueryHandler(self.handle_change_subject, pattern='^change_subject$'))
105
+
106
  logger.info("✅ تم إعداد معالجات التليجرام")
107
 
108
+ async def handle_back_actions(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
109
+ """معالجة العودة إلى القائمة"""
110
+ query = update.callback_query
111
+ await query.answer()
112
+ return await self.handle_action_selection(update, context)
113
+
114
+ async def handle_main_menu(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
115
+ """معالجة العودة للقائمة الرئيسية"""
116
+ query = update.callback_query
117
+ await query.answer()
118
+ return await self.start(update, context)
119
+
120
+ async def handle_more_questions(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
121
+ """معالجة طلب المزيد من الأسئلة"""
122
+ query = update.callback_query
123
+ await query.answer()
124
+ await query.edit_message_text("💬 اكتب سؤالك أو طلبك:")
125
+ context.user_data['waiting_for'] = 'general'
126
+ return WAITING_FOR_QUESTION
127
+
128
+ async def handle_change_subject(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
129
+ """معالجة تغيير المادة"""
130
+ query = update.callback_query
131
+ await query.answer()
132
+ keyboard = self.create_subjects_keyboard()
133
+ reply_markup = InlineKeyboardMarkup(keyboard)
134
+ await query.edit_message_text("اختر المادة:", reply_markup=reply_markup)
135
+ return SELECTING_SUBJECT
136
+
137
  def load_all_materials(self):
138
  """تحميل جميع المواد والملفات من Hugging Face"""
139
  try:
 
175
  continue
176
 
177
  self.available_materials = materials
178
+ logger.info(f"تم تحميل {len(materials)} مادة بنجاح")
179
 
180
  except Exception as e:
181
+ logger.error(f"خطأ في تحميل المواد: {e}")
182
  self.available_materials = {'Biochemistry': {'files': [], 'file_details': {}}}
183
 
184
  def extract_file_info(self, file_name, file_path):
 
213
  if file_path in self.file_cache:
214
  return self.file_cache[file_path]
215
 
216
+ logger.info(f"📥 جاري تحميل الملف: {file_path}")
217
 
218
  local_path = hf_hub_download(
219
  repo_id=REPO_ID,
 
238
  return content
239
 
240
  except Exception as e:
241
+ logger.error(f"خطأ في تحميل الملف {file_path}: {e}")
242
  return f"❌ تعذر تحميل محتوى الملف: {str(e)}"
243
 
244
  async def extract_pdf_content(self, pdf_path):
 
274
  return text_content
275
 
276
  except Exception as e:
277
+ logger.error(f"خطأ في استخراج محتوى PDF: {e}")
278
  return f"❌ حدث خطأ في قراءة ملف PDF: {str(e)}"
279
 
280
  async def extract_doc_content(self, doc_path):
 
285
  content += "للاستفادة الكاملة، يرجى تحويل الملف إلى PDF أو طلب شرح محتوى معين."
286
  return content
287
  except Exception as e:
288
+ logger.error(f"خطأ في استخراج محتوى Word: {e}")
289
  return f"❌ حدث خطأ في قراءة ملف Word: {str(e)}"
290
 
291
  def get_user_memory(self, user_id):
 
339
  keyboard = self.create_subjects_keyboard()
340
  reply_markup = InlineKeyboardMarkup(keyboard)
341
 
342
+ if update.callback_query:
343
+ await update.callback_query.edit_message_text(welcome_text, reply_markup=reply_markup)
344
+ else:
345
+ await update.message.reply_text(welcome_text, reply_markup=reply_markup)
346
+
347
  return SELECTING_SUBJECT
348
 
349
  def create_subjects_keyboard(self):
 
413
  subject = context.user_data.get('current_subject', 'general')
414
 
415
  if action == "main_menu":
416
+ return await self.start(update, context)
 
 
 
417
 
418
  elif action == "browse_files":
419
  return await self.browse_available_files(query, context)
 
523
  return SELECTING_ACTION
524
 
525
  except Exception as e:
526
+ logger.error(f"Error processing message: {e}")
527
  await update.message.reply_text("❌ حدث خطأ أثناء معالجة طلبك. يرجى المحاولة مرة أخرى.")
528
  return SELECTING_ACTION
529
 
 
567
  return f"**📖 شرح {file_name}**\n\n{response}"
568
 
569
  except Exception as e:
570
+ logger.error(f"Error explaining lecture: {e}")
571
  return "❌ عذراً، حدث خطأ في شرح المحاضرة. يرجى المحاولة مرة أخرى."
572
 
573
  async def find_requested_file(self, request, subject):
 
626
  return response
627
 
628
  except Exception as e:
629
+ logger.error(f"Error explaining concept: {e}")
630
  return await self.fallback_explain_concept(concept, subject, user_id)
631
 
632
  async def fallback_explain_concept(self, concept, subject, user_id):
 
673
  return f"**❓ أسئلة {subject}**\n\n{response}"
674
 
675
  except Exception as e:
676
+ logger.error(f"Error generating questions: {e}")
677
  return await self.fallback_generate_questions(subject, user_id)
678
 
679
  async def fallback_generate_questions(self, subject, user_id):
 
760
 
761
  reasoning = getattr(completion.choices[0].message, "reasoning_content", None)
762
  if reasoning:
763
+ logger.info(f"🤔 Reasoning content for user {user_id}: {reasoning}")
764
 
765
  return completion.choices[0].message.content
766
 
767
  except Exception as e:
768
+ logger.error(f"Error calling NVIDIA AI: {e}")
769
  return "❌ عذراً، حدث خطأ في الاتصال بالذكاء الاصطناعي. يرجى المحاولة مرة أخرى لاحقاً."
770
 
771
  async def handle_general_help(self, query, context):
 
805
 
806
  await query.edit_message_text(help_text, reply_markup=reply_markup)
807
  return SELECTING_SUBJECT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808
 
809
  # ========== إنشاء كائن البوت ==========
810
  bot = MedicalLabBot()
 
817
  # تهيئة التطبيق
818
  bot.initialize_application()
819
 
820
+ if SPACE_URL:
821
+ webhook_url = f"{SPACE_URL.rstrip('/')}/telegram"
822
  await bot.application.bot.set_webhook(
823
+ url=webhook_url,
824
  allowed_updates=Update.ALL_TYPES
825
  )
826
+ logger.info(f"✅ Webhook set to: {webhook_url}")
827
  else:
828
+ logger.warning("⚠️ SPACE_URL not set, using polling instead")
829
 
830
  except Exception as e:
831
  logger.error(f"❌ Error during startup: {e}")
 
838
  <head>
839
  <title>Medical Lab Bot</title>
840
  <style>
841
+ body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
842
+ .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); }
843
+ .status { padding: 20px; background: #f0f8ff; border-radius: 10px; border-left: 5px solid #007bff; }
844
+ h1 { color: #2c3e50; text-align: center; }
845
+ ul { line-height: 1.8; }
846
+ li { margin: 10px 0; }
847
  </style>
848
  </head>
849
  <body>
 
852
  <div class="status">
853
  <h2>✅ البوت يعمل بنجاح</h2>
854
  <p>البوت متصل بـ Telegram وجاهز لاستقبال الرسائل.</p>
855
+ <p><strong>الحالة:</strong> نشط</p>
856
+ <p><strong>المواد المحملة:</strong> """ + str(len(bot.available_materials)) + """ مادة</p>
857
  </div>
858
+ <h3>🎯 المميزات:</h3>
859
  <ul>
860
  <li>📚 شرح المواد الدراسية من ملفات PDF وWord</li>
861
  <li>❓ توليد أسئلة متنوعة للمراجعة</li>
862
  <li>📖 تلخيص المحتوى الدراسي</li>
863
  <li>🧪 تفسير المفاهيم العلمية</li>
864
+ <li>🎯 اختبار الفهم والاستيعاب</li>
865
+ <li>💾 ذاكرة محادثة لكل مستخدم</li>
866
  </ul>
867
+ <div class="status" style="margin-top: 20px;">
868
+ <p><strong>ℹ️ معلومات:</strong> هذا البوت يستخدم الذكاء الاصطناعي من NVIDIA وملفات Hugging Face</p>
869
+ </div>
870
  </div>
871
  </body>
872
  </html>
 
887
  @app.get("/health")
888
  async def health_check():
889
  """فحص صحة الخدمة"""
890
+ materials_count = len(bot.available_materials)
891
+ total_files = sum(len(material['files']) for material in bot.available_materials.values())
892
+
893
+ return {
894
+ "status": "healthy",
895
+ "service": "medical-lab-bot",
896
+ "materials_loaded": materials_count,
897
+ "total_files": total_files,
898
+ "timestamp": datetime.now().isoformat()
899
+ }
900
 
901
  # ========== التشغيل الرئيسي ==========
902
  if __name__ == "__main__":
903
  port = int(os.environ.get("PORT", 7860))
904
+ logger.info(f"🚀 Starting Medical Lab Bot on port {port}")
905
  uvicorn.run(app, host="0.0.0.0", port=port)