Riy777 commited on
Commit
448e22b
·
verified ·
1 Parent(s): c05fb24

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -62
app.py CHANGED
@@ -4,6 +4,7 @@ import asyncio
4
  import re
5
  from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
6
  from telegram.ext import Application, CommandHandler, CallbackQueryHandler, MessageHandler, filters, ContextTypes, ConversationHandler
 
7
  from huggingface_hub import HfApi, hf_hub_download, list_repo_files
8
  from openai import OpenAI
9
  import pickle
@@ -25,6 +26,7 @@ logger = logging.getLogger(__name__)
25
  # التوكنات - سيتم تعيينها في متغيرات البيئة
26
  TELEGRAM_BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN')
27
  NVAPI_API_KEY = os.environ.get('NVAPI_API_KEY')
 
28
 
29
  # تكوين عميل NVIDIA
30
  nvidia_client = OpenAI(
@@ -42,7 +44,7 @@ class MedicalLabBot:
42
  def __init__(self):
43
  self.conversation_memory = {}
44
  self.available_materials = {}
45
- self.file_cache = {} # تخزين مؤقت للمحتوى المستخرج
46
  self.load_all_materials()
47
 
48
  def load_all_materials(self):
@@ -55,11 +57,10 @@ class MedicalLabBot:
55
 
56
  for file_path in all_files:
57
  try:
58
- # استخراج اسم المادة من المسار
59
  path_parts = file_path.split('/')
60
 
61
  if len(path_parts) >= 2:
62
- subject = path_parts[0] # اسم المجلد (المادة)
63
  file_name = path_parts[-1]
64
 
65
  if subject not in materials:
@@ -68,13 +69,11 @@ class MedicalLabBot:
68
  'file_details': {}
69
  }
70
 
71
- # استخراج معلومات الملف
72
  file_info = self.extract_file_info(file_name, file_path)
73
  materials[subject]['files'].append(file_info)
74
  materials[subject]['file_details'][file_name] = file_info
75
 
76
  else:
77
- # ملفات في المجلد الرئيسي
78
  if 'general' not in materials:
79
  materials['general'] = {
80
  'files': [],
@@ -90,9 +89,7 @@ class MedicalLabBot:
90
 
91
  self.available_materials = materials
92
  logger.info(f"تم تحميل {len(materials)} مادة بنجاح")
93
- for subject, data in materials.items():
94
- logger.info(f"المادة: {subject} - عدد الملفات: {len(data['files'])}")
95
-
96
  except Exception as e:
97
  logger.error(f"خطأ في تحميل المواد: {e}")
98
  self.available_materials = {'Biochemistry': {'files': [], 'file_details': {}}}
@@ -107,12 +104,10 @@ class MedicalLabBot:
107
  'type': 'unknown'
108
  }
109
 
110
- # البحث عن رقم المحاضرة في اسم الملف
111
  numbers = re.findall(r'\d+', file_name)
112
  if numbers:
113
  file_info['lecture_number'] = int(numbers[0])
114
 
115
- # تحديد نوع الملف
116
  file_lower = file_name.lower()
117
  if any(term in file_lower for term in ['lecture', 'محاضرة', 'lec', 'week']):
118
  file_info['type'] = 'lecture'
@@ -128,13 +123,11 @@ class MedicalLabBot:
128
  async def download_and_extract_content(self, file_path):
129
  """تحميل الملف واستخراج محتواه"""
130
  try:
131
- # التحقق من التخزين المؤقت
132
  if file_path in self.file_cache:
133
  return self.file_cache[file_path]
134
 
135
  logger.info(f"جاري تحميل الملف: {file_path}")
136
 
137
- # تحميل الملف من Hugging Face
138
  local_path = hf_hub_download(
139
  repo_id=REPO_ID,
140
  filename=file_path,
@@ -154,7 +147,6 @@ class MedicalLabBot:
154
  else:
155
  content = f"نوع الملف {file_ext} غير مدعوم حالياً للقراءة المباشرة"
156
 
157
- # التخزين المؤقت
158
  self.file_cache[file_path] = content
159
  return content
160
 
@@ -163,11 +155,10 @@ class MedicalLabBot:
163
  return f"❌ تعذر تحميل محتوى الملف: {str(e)}"
164
 
165
  async def extract_pdf_content(self, pdf_path):
166
- """استخراج النص من ملف PDF مع التعامل مع الصور"""
167
  try:
168
  text_content = ""
169
 
170
- # الطريقة 1: استخدام PyPDF2 للنص الأساسي
171
  try:
172
  with open(pdf_path, 'rb') as file:
173
  pdf_reader = PyPDF2.PdfReader(file)
@@ -178,7 +169,6 @@ class MedicalLabBot:
178
  except Exception as e:
179
  logger.warning(f"PyPDF2 failed: {e}")
180
 
181
- # إذا كان النص قليلاً، نستخدم PyMuPDF الذي يعمل أفضل مع الملفات المعقدة
182
  if len(text_content.strip()) < 100:
183
  try:
184
  doc = fitz.open(pdf_path)
@@ -190,7 +180,6 @@ class MedicalLabBot:
190
  except Exception as e:
191
  logger.warning(f"PyMuPDF failed: {e}")
192
 
193
- # إذا لم يتم استخراج نص كافي، نعتبر أن الملف يحتوي على صور بشكل أساسي
194
  if len(text_content.strip()) < 50:
195
  text_content = "📄 هذا الملف يحتوي بشكل رئيسي على صور أو رسومات. " \
196
  "يرجى طلب شرح محتوى معين أو طرح أسئلة محددة عن الملف."
@@ -204,7 +193,6 @@ class MedicalLabBot:
204
  async def extract_doc_content(self, doc_path):
205
  """استخراج النص من ملف Word"""
206
  try:
207
- # هذه وظيفة مبسطة - في الإنتاج الحقيقي تحتاج python-docx
208
  content = "📝 محتوى ملف Word: "
209
  content += "هذا النموذج يدعم قراءة ملفات Word بشكل أساسي. "
210
  content += "للاستفادة الكاملة، يرجى تحويل الملف إلى PDF أو طلب شرح محتوى معين."
@@ -255,7 +243,6 @@ class MedicalLabBot:
255
  📚 **المواد المتاحة حالياً:**
256
  """
257
 
258
- # عرض المواد المتاحة
259
  for subject in self.available_materials.keys():
260
  file_count = len(self.available_materials[subject]['files'])
261
  welcome_text += f"\n• {subject} ({file_count} ملف)"
@@ -303,7 +290,6 @@ class MedicalLabBot:
303
  memory = self.get_user_memory(user_id)
304
  memory['last_subject'] = subject
305
 
306
- # عرض خيارات للمادة المحددة
307
  subject_files = self.available_materials[subject]['files']
308
  subject_name = subject.replace('_', ' ').title()
309
 
@@ -382,7 +368,7 @@ class MedicalLabBot:
382
  return "❌ لا توجد ملفات متاحة لهذه المادة."
383
 
384
  files_text = "📁 **الملفات المتاحة:**\n\n"
385
- for i, file_info in enumerate(files[:15], 1): # عرض أول 15 ملف فقط
386
  file_name = file_info['name']
387
  lecture_num = file_info['lecture_number']
388
  file_type = file_info['type']
@@ -456,7 +442,6 @@ class MedicalLabBot:
456
  async def explain_lecture(self, lecture_request, subject, user_id):
457
  """شرح محاضرة محددة"""
458
  try:
459
- # البحث عن الملف المطلوب
460
  target_file_info = await self.find_requested_file(lecture_request, subject)
461
 
462
  if not target_file_info:
@@ -466,13 +451,8 @@ class MedicalLabBot:
466
  file_path = target_file_info['path']
467
  file_name = target_file_info['name']
468
 
469
- # إعلام المستخدم بأننا نحميل المحتوى
470
- loading_msg = f"⏳ جاري تحميل وتحليل الملف: {file_name} ..."
471
-
472
- # تحميل واستخراج محتوى الملف
473
  file_content = await self.download_and_extract_content(file_path)
474
 
475
- # استخدام الذكاء الاصطناعي لشرح المحتوى
476
  prompt = f"""
477
  قم بشرح المحتوى التالي من ملف تعليمي في مجال المختبرات الطبية:
478
 
@@ -509,7 +489,6 @@ class MedicalLabBot:
509
  if not files:
510
  return None
511
 
512
- # البحث برقم المحاضرة
513
  numbers = re.findall(r'\d+', request)
514
  if numbers:
515
  requested_num = int(numbers[0])
@@ -517,22 +496,19 @@ class MedicalLabBot:
517
  if file_info['lecture_number'] == requested_num:
518
  return file_info
519
 
520
- # البحث بكلمات مفتاحية
521
  request_lower = request.lower()
522
  for file_info in files:
523
  file_name_lower = file_info['name'].lower()
524
  if any(term in file_name_lower for term in request_lower.split()):
525
  return file_info
526
 
527
- # إذا لم يتم العثور، نعيد أول ملف
528
  return files[0]
529
 
530
  async def explain_concept(self, concept, subject, user_id):
531
  """شرح مفهوم معين باستخدام محتوى الملفات"""
532
  try:
533
- # جمع عينات من محتوى الملفات لفهم السياق
534
  context_content = ""
535
- sample_files = self.available_materials[subject]['files'][:3] # عينات من أول 3 ملفات
536
 
537
  for file_info in sample_files:
538
  file_content = await self.download_and_extract_content(file_info['path'])
@@ -585,7 +561,6 @@ class MedicalLabBot:
585
  async def generate_questions_for_subject(self, subject, user_id):
586
  """توليد أسئلة عن المادة باستخدام محتوى حقيقي"""
587
  try:
588
- # جمع محتوى من عدة ملفات للسياق
589
  context_content = ""
590
  for file_info in self.available_materials[subject]['files'][:2]:
591
  file_content = await self.download_and_extract_content(file_info['path'])
@@ -780,34 +755,45 @@ class MedicalLabBot:
780
  )
781
  return SELECTING_ACTION
782
 
783
- def main():
784
- """الدالة الرئيسية لتشغيل البوت"""
785
- bot = MedicalLabBot()
786
-
787
- application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
788
-
789
- conv_handler = ConversationHandler(
790
- entry_points=[CommandHandler('start', bot.start)],
791
- states={
792
- SELECTING_SUBJECT: [
793
- CallbackQueryHandler(bot.handle_subject_selection, pattern='^subject_|general_help|refresh_materials$')
794
- ],
795
- SELECTING_ACTION: [
796
- CallbackQueryHandler(bot.handle_action_selection),
797
- CallbackQueryHandler(bot.handle_subject_selection, pattern='^subject_')
798
- ],
799
- WAITING_FOR_QUESTION: [
800
- MessageHandler(filters.TEXT & ~filters.COMMAND, bot.handle_message)
801
- ]
802
- },
803
- fallbacks=[CommandHandler('start', bot.start)]
804
- )
805
-
806
- application.add_handler(conv_handler)
807
- application.add_handler(CallbackQueryHandler(bot.handle_callback_query, pattern='^more_questions|change_subject|back_to_actions$'))
808
-
809
- logger.info("Starting Medical Lab Bot...")
810
- application.run_polling()
 
 
 
 
 
 
 
 
 
811
 
812
  if __name__ == '__main__':
813
- main()
 
 
 
4
  import re
5
  from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
6
  from telegram.ext import Application, CommandHandler, CallbackQueryHandler, MessageHandler, filters, ContextTypes, ConversationHandler
7
+ from telegram.request import HTTPXRequest
8
  from huggingface_hub import HfApi, hf_hub_download, list_repo_files
9
  from openai import OpenAI
10
  import pickle
 
26
  # التوكنات - سيتم تعيينها في متغيرات البيئة
27
  TELEGRAM_BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN')
28
  NVAPI_API_KEY = os.environ.get('NVAPI_API_KEY')
29
+ WEBHOOK_URL = os.environ.get('WEBHOOK_URL', '') # سيعينه Hugging Face تلقائياً
30
 
31
  # تكوين عميل NVIDIA
32
  nvidia_client = OpenAI(
 
44
  def __init__(self):
45
  self.conversation_memory = {}
46
  self.available_materials = {}
47
+ self.file_cache = {}
48
  self.load_all_materials()
49
 
50
  def load_all_materials(self):
 
57
 
58
  for file_path in all_files:
59
  try:
 
60
  path_parts = file_path.split('/')
61
 
62
  if len(path_parts) >= 2:
63
+ subject = path_parts[0]
64
  file_name = path_parts[-1]
65
 
66
  if subject not in materials:
 
69
  'file_details': {}
70
  }
71
 
 
72
  file_info = self.extract_file_info(file_name, file_path)
73
  materials[subject]['files'].append(file_info)
74
  materials[subject]['file_details'][file_name] = file_info
75
 
76
  else:
 
77
  if 'general' not in materials:
78
  materials['general'] = {
79
  'files': [],
 
89
 
90
  self.available_materials = materials
91
  logger.info(f"تم تحميل {len(materials)} مادة بنجاح")
92
+
 
 
93
  except Exception as e:
94
  logger.error(f"خطأ في تحميل المواد: {e}")
95
  self.available_materials = {'Biochemistry': {'files': [], 'file_details': {}}}
 
104
  'type': 'unknown'
105
  }
106
 
 
107
  numbers = re.findall(r'\d+', file_name)
108
  if numbers:
109
  file_info['lecture_number'] = int(numbers[0])
110
 
 
111
  file_lower = file_name.lower()
112
  if any(term in file_lower for term in ['lecture', 'محاضرة', 'lec', 'week']):
113
  file_info['type'] = 'lecture'
 
123
  async def download_and_extract_content(self, file_path):
124
  """تحميل الملف واستخراج محتواه"""
125
  try:
 
126
  if file_path in self.file_cache:
127
  return self.file_cache[file_path]
128
 
129
  logger.info(f"جاري تحميل الملف: {file_path}")
130
 
 
131
  local_path = hf_hub_download(
132
  repo_id=REPO_ID,
133
  filename=file_path,
 
147
  else:
148
  content = f"نوع الملف {file_ext} غير مدعوم حالياً للقراءة المباشرة"
149
 
 
150
  self.file_cache[file_path] = content
151
  return content
152
 
 
155
  return f"❌ تعذر تحميل محتوى الملف: {str(e)}"
156
 
157
  async def extract_pdf_content(self, pdf_path):
158
+ """استخراج النص من ملف PDF"""
159
  try:
160
  text_content = ""
161
 
 
162
  try:
163
  with open(pdf_path, 'rb') as file:
164
  pdf_reader = PyPDF2.PdfReader(file)
 
169
  except Exception as e:
170
  logger.warning(f"PyPDF2 failed: {e}")
171
 
 
172
  if len(text_content.strip()) < 100:
173
  try:
174
  doc = fitz.open(pdf_path)
 
180
  except Exception as e:
181
  logger.warning(f"PyMuPDF failed: {e}")
182
 
 
183
  if len(text_content.strip()) < 50:
184
  text_content = "📄 هذا الملف يحتوي بشكل رئيسي على صور أو رسومات. " \
185
  "يرجى طلب شرح محتوى معين أو طرح أسئلة محددة عن الملف."
 
193
  async def extract_doc_content(self, doc_path):
194
  """استخراج النص من ملف Word"""
195
  try:
 
196
  content = "📝 محتوى ملف Word: "
197
  content += "هذا النموذج يدعم قراءة ملفات Word بشكل أساسي. "
198
  content += "للاستفادة الكاملة، يرجى تحويل الملف إلى PDF أو طلب شرح محتوى معين."
 
243
  📚 **المواد المتاحة حالياً:**
244
  """
245
 
 
246
  for subject in self.available_materials.keys():
247
  file_count = len(self.available_materials[subject]['files'])
248
  welcome_text += f"\n• {subject} ({file_count} ملف)"
 
290
  memory = self.get_user_memory(user_id)
291
  memory['last_subject'] = subject
292
 
 
293
  subject_files = self.available_materials[subject]['files']
294
  subject_name = subject.replace('_', ' ').title()
295
 
 
368
  return "❌ لا توجد ملفات متاحة لهذه المادة."
369
 
370
  files_text = "📁 **الملفات المتاحة:**\n\n"
371
+ for i, file_info in enumerate(files[:15], 1):
372
  file_name = file_info['name']
373
  lecture_num = file_info['lecture_number']
374
  file_type = file_info['type']
 
442
  async def explain_lecture(self, lecture_request, subject, user_id):
443
  """شرح محاضرة محددة"""
444
  try:
 
445
  target_file_info = await self.find_requested_file(lecture_request, subject)
446
 
447
  if not target_file_info:
 
451
  file_path = target_file_info['path']
452
  file_name = target_file_info['name']
453
 
 
 
 
 
454
  file_content = await self.download_and_extract_content(file_path)
455
 
 
456
  prompt = f"""
457
  قم بشرح المحتوى التالي من ملف تعليمي في مجال المختبرات الطبية:
458
 
 
489
  if not files:
490
  return None
491
 
 
492
  numbers = re.findall(r'\d+', request)
493
  if numbers:
494
  requested_num = int(numbers[0])
 
496
  if file_info['lecture_number'] == requested_num:
497
  return file_info
498
 
 
499
  request_lower = request.lower()
500
  for file_info in files:
501
  file_name_lower = file_info['name'].lower()
502
  if any(term in file_name_lower for term in request_lower.split()):
503
  return file_info
504
 
 
505
  return files[0]
506
 
507
  async def explain_concept(self, concept, subject, user_id):
508
  """شرح مفهوم معين باستخدام محتوى الملفات"""
509
  try:
 
510
  context_content = ""
511
+ sample_files = self.available_materials[subject]['files'][:3]
512
 
513
  for file_info in sample_files:
514
  file_content = await self.download_and_extract_content(file_info['path'])
 
561
  async def generate_questions_for_subject(self, subject, user_id):
562
  """توليد أسئلة عن المادة باستخدام محتوى حقيقي"""
563
  try:
 
564
  context_content = ""
565
  for file_info in self.available_materials[subject]['files'][:2]:
566
  file_content = await self.download_and_extract_content(file_info['path'])
 
755
  )
756
  return SELECTING_ACTION
757
 
758
+ # إنشاء كائن البوت
759
+ bot = MedicalLabBot()
760
+
761
+ # إنشاء تطبيق التليجرام مع تكوين Webhook
762
+ application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
763
+
764
+ # إضافة handlers
765
+ conv_handler = ConversationHandler(
766
+ entry_points=[CommandHandler('start', bot.start)],
767
+ states={
768
+ SELECTING_SUBJECT: [
769
+ CallbackQueryHandler(bot.handle_subject_selection, pattern='^subject_|general_help|refresh_materials$')
770
+ ],
771
+ SELECTING_ACTION: [
772
+ CallbackQueryHandler(bot.handle_action_selection),
773
+ CallbackQueryHandler(bot.handle_subject_selection, pattern='^subject_')
774
+ ],
775
+ WAITING_FOR_QUESTION: [
776
+ MessageHandler(filters.TEXT & ~filters.COMMAND, bot.handle_message)
777
+ ]
778
+ },
779
+ fallbacks=[CommandHandler('start', bot.start)]
780
+ )
781
+
782
+ application.add_handler(conv_handler)
783
+ application.add_handler(CallbackQueryHandler(bot.handle_callback_query, pattern='^more_questions|change_subject|back_to_actions$'))
784
+
785
+ async def setup_webhook():
786
+ """إعداد Webhook للبوت"""
787
+ if WEBHOOK_URL:
788
+ await application.bot.set_webhook(
789
+ url=f"{WEBHOOK_URL}/telegram",
790
+ allowed_updates=Update.ALL_TYPES
791
+ )
792
+ logger.info(f"Webhook set to: {WEBHOOK_URL}/telegram")
793
+ else:
794
+ logger.warning("WEBHOOK_URL not set, using polling instead")
795
 
796
  if __name__ == '__main__':
797
+ # عند التشغيل المحلي، استخدام Polling
798
+ logger.info("Starting bot in polling mode...")
799
+ application.run_polling()