Kaveh commited on
Commit
e19df82
·
unverified ·
1 Parent(s): 1d05bbb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -373
app.py CHANGED
@@ -10,457 +10,203 @@ import nltk
10
  from nltk.tokenize import sent_tokenize
11
  import torch
12
 
13
- # تنظیم cache directory
14
  cache_dir = '/tmp/transformers_cache'
15
  os.environ['TRANSFORMERS_CACHE'] = cache_dir
16
  os.environ['HF_HOME'] = cache_dir
17
  os.makedirs(cache_dir, exist_ok=True)
18
 
19
- # دانلود nltk data
20
  try:
21
  nltk.download('punkt', download_dir='./nltk_data', quiet=True)
22
  nltk.data.path.append('./nltk_data')
23
  except:
24
  pass
25
 
26
- # تنظیم logging
27
- logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
 
 
 
28
  logger = logging.getLogger(__name__)
29
 
30
- # حافظه موقتی پیام‌ها برای هر گروه
31
- message_storage = {}
32
- MAX_MESSAGES_PER_CHAT = 1000
33
-
34
- # مدل فارسی
35
  MODEL_NAME = "nafisehNik/mt5-persian-summary"
36
  model = None
37
  tokenizer = None
38
 
 
 
39
  class MessageStore:
40
- """کلاس برای ذخیره و مدیریت پیام‌ها"""
41
-
42
  def __init__(self):
43
  self.messages = {}
44
-
45
- def add_message(self, chat_id: int, user_id: int, username: str, text: str, timestamp: datetime):
46
- """اضافه کردن پیام جدید"""
47
  if chat_id not in self.messages:
48
  self.messages[chat_id] = []
49
-
50
- # حفظ حداکثر تعداد پیام
51
  if len(self.messages[chat_id]) >= MAX_MESSAGES_PER_CHAT:
52
- self.messages[chat_id] = self.messages[chat_id][-MAX_MESSAGES_PER_CHAT//2:]
53
-
54
  self.messages[chat_id].append({
55
- 'user_id': user_id,
56
- 'username': username,
57
- 'text': text,
58
- 'timestamp': timestamp
59
  })
60
-
61
- def get_messages(self, chat_id: int, count: int = 50, hours_back: int = None):
62
- """دریافت پیام‌ها براساس تعداد یا زمان"""
63
  if chat_id not in self.messages:
64
  return []
65
-
66
  messages = self.messages[chat_id]
67
-
68
- # فیلتر بر اساس زمان
69
  if hours_back:
70
- cutoff_time = datetime.now() - timedelta(hours=hours_back)
71
- messages = [msg for msg in messages if msg['timestamp'] >= cutoff_time]
72
-
73
- # برگرداندن آخرین پیام‌ها
74
  return messages[-count:] if count else messages
75
 
76
- # ایجاد نمونه از مخزن پیام‌ها
77
  message_store = MessageStore()
78
 
79
  def load_persian_model():
80
- """بارگیری مدل فارسی"""
81
  try:
82
  logger.info(f"Loading Persian model: {MODEL_NAME}")
83
-
84
- tokenizer = AutoTokenizer.from_pretrained(
85
- MODEL_NAME,
86
- cache_dir=cache_dir,
87
- local_files_only=False
88
- )
89
-
90
  model = AutoModelForSeq2SeqLM.from_pretrained(
91
  MODEL_NAME,
92
  cache_dir=cache_dir,
93
- local_files_only=False,
94
- torch_dtype=torch.float32,
95
- low_cpu_mem_usage=True,
96
  )
97
-
98
  model.eval()
99
- logger.info("Persian model loaded successfully")
100
- return model, tokenizer
101
-
102
  except Exception as e:
103
  logger.error(f"Error loading Persian model: {e}")
104
- return None, None
105
 
106
  def preprocess_persian_text(text):
107
- """پیش‌پردازش پیشرفته متن فارسی"""
108
- # حذف کاراکترهای اضافی و تمیز کردن
109
- text = re.sub(r'\s+', ' ', text) # چندین فاصله -> یک فاصله
110
- text = re.sub(r'\n+', '\n', text) # چندین خط جدید -> یک خط
111
-
112
- # حذف timestamp و نام‌های کاربری تلگرام
113
- text = re.sub(r'\d{2}:\d{2}', '', text) # زمان
114
- text = re.sub(r'@\w+', '', text) # منشن‌ها
115
-
116
- # حذف لینک‌ها
117
- text = re.sub(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', '', text)
118
-
119
- # حذف ایموجی‌ها
120
- text = re.sub(r'[^\w\s\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]', ' ', text)
121
-
122
  return text.strip()
123
 
124
  def chunk_text_smart(text, max_length=300):
125
- """تقسیم هوشمند متن با در نظر گیری زبان فارسی"""
126
  try:
127
  sentences = sent_tokenize(text)
128
  except:
129
- # روش جایگزین برای جمله‌بندی فارسی
130
- sentences = re.split(r'[.!?؟۔]+', text)
131
-
132
  chunks = []
133
- current_chunk = ""
134
-
135
  for sentence in sentences:
136
- sentence = sentence.strip()
137
- if not sentence:
138
- continue
139
-
140
- if len(current_chunk + sentence) < max_length:
141
- current_chunk += sentence + " "
142
  else:
143
- if current_chunk:
144
- chunks.append(current_chunk.strip())
145
- current_chunk = sentence + " " if len(sentence) < max_length else sentence[:max_length]
146
-
147
- if current_chunk:
148
- chunks.append(current_chunk.strip())
149
-
150
  return chunks
151
 
152
  def summarize_messages(messages_data):
153
- """خلاصه‌سازی پیام‌های گروه با مدل فارسی"""
154
  global model, tokenizer
155
-
156
  if not model or not tokenizer:
157
  return "❌ مدل خلاصه‌سازی در دسترس نیست"
158
-
159
  if not messages_data:
160
  return "❌ پیامی برای خلاصه‌سازی یافت نشد"
161
-
162
  try:
163
- # ترکیب پیام‌ها
164
- combined_text = ""
165
  for msg in messages_data:
166
- user_prefix = f"{msg['username'] or 'کاربر'}: " if msg['username'] else ""
167
- combined_text += f"{user_prefix}{msg['text']}\n"
168
-
169
- # پیش‌پردازش
170
- combined_text = preprocess_persian_text(combined_text)
171
-
172
- if len(combined_text) < 100:
173
  return "❌ متن برای خلاصه‌سازی بسیار کوتاه است"
174
-
175
- # تقسیم به بخش‌های کوچک
176
- chunks = chunk_text_smart(combined_text, max_length=400)
177
  summaries = []
178
-
179
- for i, chunk in enumerate(chunks[:2]): # حداکثر 2 بخش
180
- try:
181
- inputs = tokenizer.encode(
182
- f"خلاصه: {chunk}",
183
- return_tensors="pt",
184
- max_length=512,
185
- truncation=True
186
- )
187
-
188
- summary_ids = model.generate(
189
- inputs,
190
- max_length=100,
191
- min_length=30,
192
- length_penalty=1.2,
193
- num_beams=3,
194
- early_stopping=True,
195
- no_repeat_ngram_size=3
196
- )
197
-
198
- summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
199
-
200
- # پاک کردن prefix
201
- if summary.startswith("خلاصه:"):
202
- summary = summary[5:].strip()
203
-
204
- summaries.append(summary)
205
-
206
- except Exception as e:
207
- logger.error(f"Error summarizing chunk {i}: {e}")
208
- continue
209
-
210
  if not summaries:
211
- return "❌ خطا در فرآیند خلاصه‌سازی"
212
-
213
- # ترکیب نهایی
214
- final_summary = "\n\n".join(summaries)
215
-
216
- # اضافه کردن اطلاعات آماری
217
- stats = f"\n\n📊 آمار: {len(messages_data)} پیام، {len(combined_text)} کاراکتر"
218
-
219
- return f"📝 خلاصه گفتگو:\n\n{final_summary}{stats}"
220
-
221
  except Exception as e:
222
  logger.error(f"Summarization error: {e}")
223
- return f"❌ خطا در خلاصه‌سازی: {str(e)}"
224
 
225
  def parse_summary_request(text):
226
- """تجزیه درخواست خلاصه‌سازی"""
227
  text = text.lower()
228
-
229
- # پیدا کردن تعداد پیام
230
- count_patterns = [
231
- r'(\d+)\s*پیام',
232
- r'(\d+)\s*تا',
233
- r'آخرین\s*(\d+)',
234
- ]
235
-
236
- message_count = 50 # پیش‌فرض
237
-
238
- for pattern in count_patterns:
239
- match = re.search(pattern, text)
240
- if match:
241
- message_count = min(int(match.group(1)), 200) # حداکثر 200
242
- break
243
-
244
- # پیدا کردن بازه زمانی
245
- time_patterns = [
246
- r'(\d+)\s*ساعت',
247
- r'(\d+)\s*روز',
248
- ]
249
-
250
- hours_back = None
251
-
252
- for pattern in time_patterns:
253
- match = re.search(pattern, text)
254
- if match:
255
- if 'روز' in pattern:
256
- hours_back = int(match.group(1)) * 24
257
- else:
258
- hours_back = int(match.group(1))
259
- hours_back = min(hours_back, 72) # حداکثر 3 روز
260
- break
261
-
262
- return message_count, hours_back
263
 
264
  async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
265
- """شروع ربات"""
266
- welcome_msg = f"""
267
- 🤖 سلام! من ربات خلاصه‌ساز گروه هستم.
268
-
269
- 📋 برای استفاده از من:
270
- - من را با @{context.bot.username} تگ کنید
271
- - بعد عبارت "خلاصه" یا "خلاصه کن" بنویسید
272
-
273
- 🔹 مثال‌ها:
274
- • @{context.bot.username} خلاصه کن
275
- • @{context.bot.username} خلاصه 100 پیام آخر
276
- • @{context.bot.username} خلاصه 2 ساعت اخیر
277
-
278
- ⚙️ دستورات:
279
- /help - راهنمای کامل
280
- /stats - آمار گروه
281
- /model - اطلاعات مدل فعلی
282
-
283
- 🔸 توجه: من فقط وقتی تگ شوم کار می‌کنم!
284
- """
285
-
286
- await update.message.reply_text(welcome_msg)
287
-
288
- async def model_info(update: Update, context: ContextTypes.DEFAULT_TYPE):
289
- """نمایش اطلاعات مدل فعلی"""
290
- info_text = f"""
291
- 🤖 اطلاعات مدل فعلی:
292
-
293
- 📦 نام مدل: {MODEL_NAME}
294
- 🌐 پشتیبانی زبان: ✅ فارسی
295
- 💾 وضعیت: فعال و آماده
296
- """
297
- await update.message.reply_text(info_text)
298
-
299
- async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
300
- """راهنمای کامل"""
301
- help_text = f"""
302
- 🤖 راهنمای ربات خلاصه‌ساز
303
-
304
- 📝 نحوه استفاده:
305
- 1. من را با @{context.bot.username} تگ کنید
306
- 2. کلمه "خلاصه" یا "خلاصه کن" اضافه کنید
307
- 3. اختیاری: تعداد پیام یا بازه زمانی مشخص کنید
308
-
309
- 🔹 مثال‌های مختلف:
310
- • @{context.bot.username} خلاصه کن
311
- • @{context.bot.username} خلاصه 50 پیام
312
- • @{context.bot.username} خلاصه 3 ساعت اخیر
313
-
314
- ⚡ ویژگی‌ها:
315
- • پردازش تا 200 پیام
316
- • بازه زمانی تا 3 روز
317
- • پشتیبانی از متن فارسی
318
- • تطبیق خودکار با بهترین مدل موجود
319
-
320
- 🔧 دستورات:
321
- /start - شروع
322
- /help - راهنما
323
- /stats - آمار گروه
324
- /model - اطلاعات مدل
325
-
326
- 🔸 نکته: من فقط در گروه‌ها و وقتی تگ شوم کار می‌کنم!
327
- """
328
- await update.message.reply_text(help_text)
329
-
330
- async def stats_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
331
- """نمایش آمار گروه"""
332
- chat_id = update.effective_chat.id
333
-
334
- if chat_id not in message_store.messages:
335
- await update.message.reply_text("📊 هنوز پیامی ذخیره نشده")
336
- return
337
-
338
- messages = message_store.messages[chat_id]
339
- total_messages = len(messages)
340
-
341
- # شمارش پیام‌ها در 24 ساعت اخیر
342
- day_ago = datetime.now() - timedelta(hours=24)
343
- recent_messages = len([m for m in messages if m['timestamp'] >= day_ago])
344
-
345
- # کاربران فعال
346
- users = {}
347
- for msg in messages:
348
- username = msg['username'] or 'کاربر ناشناس'
349
- users[username] = users.get(username, 0) + 1
350
-
351
- top_users = sorted(users.items(), key=lambda x: x[1], reverse=True)[:5]
352
-
353
- stats_text = f"""
354
- 📊 آمار گروه:
355
-
356
- 📈 کل پ��ام‌های ذخیره شده: {total_messages}
357
- 🕐 پیام‌های 24 ساعت اخیر: {recent_messages}
358
-
359
- 👥 کاربران فعال:
360
- """
361
-
362
- for username, count in top_users:
363
- stats_text += f"• {username}: {count} پیام\n"
364
-
365
- await update.message.reply_text(stats_text)
366
 
367
  async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
368
- """ذخیره پیام‌ها و پردازش درخواست‌های خلاصه"""
369
-
370
- # اگر پیام خصوصی باشد، نادیده بگیر
371
- if update.effective_chat.type == 'private':
372
- return
373
-
374
- chat_id = update.effective_chat.id
375
- user_id = update.effective_user.id
376
- username = update.effective_user.username
377
- message_text = update.message.text
378
- timestamp = datetime.now()
379
-
380
- # ذخیره پیام (همیشه)
381
- message_store.add_message(chat_id, user_id, username, message_text, timestamp)
382
-
383
- # بررسی اینکه آیا ربات تگ شده یا نه
384
- if not context.bot.username:
385
- return
386
-
387
- bot_mention = f"@{context.bot.username.lower()}"
388
- message_lower = message_text.lower()
389
-
390
- # اگر ربات تگ نشده، کاری نکن
391
- if bot_mention not in message_lower:
392
  return
393
-
394
- # بررسی درخواست خلاصه
395
- summary_keywords = ['خلاصه', 'خلاصه کن', 'summarize', 'خلاصه بده']
396
-
397
- if not any(keyword in message_lower for keyword in summary_keywords):
398
- return
399
-
400
- # ارسال پیام "در حال پردازش"
401
- processing_msg = await update.message.reply_text("⏳ در حال جمع‌آوری و خلاصه‌سازی پیام‌ها...")
402
-
403
- try:
404
- # تجزیه درخواست
405
- message_count, hours_back = parse_summary_request(message_text)
406
-
407
- # دریافت پیام‌ها
408
- messages_data = message_store.get_messages(chat_id, message_count, hours_back)
409
-
410
- if not messages_data:
411
- await processing_msg.edit_text("❌ پیامی برای خلاصه‌سازی یافت نشد")
412
- return
413
-
414
- # خلاصه‌سازی
415
- summary = summarize_messages(messages_data)
416
-
417
- # ارسال نتیجه
418
- await processing_msg.edit_text(summary)
419
-
420
- except Exception as e:
421
- logger.error(f"Error in handle_message: {e}")
422
- await processing_msg.edit_text(f"❌ خطا در پردازش: {str(e)}")
423
 
424
- async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
425
- """مدیریت خطاها"""
426
- logger.error(f"Update {update} caused error {context.error}")
 
 
427
 
428
- def main():
429
- """تابع اصلی"""
430
- global model, tokenizer
431
-
432
- # دریافت توکن
433
- BOT_TOKEN = os.environ.get('BOT_TOKEN')
434
- if not BOT_TOKEN:
435
- logger.error("BOT_TOKEN not found!")
436
- return
437
-
438
- # بارگیری مدل فارسی
439
- logger.info("Loading Persian model...")
440
- model, tokenizer = load_persian_model()
441
-
442
- if not model:
443
- logger.error("Failed to load any model!")
444
- return
445
-
446
- # ساخت اپلیکیشن
447
- app = ApplicationBuilder().token(BOT_TOKEN).build()
448
-
449
- # اضافه کردن handlers
450
  app.add_handler(CommandHandler("start", start))
451
- app.add_handler(CommandHandler("help", help_command))
452
- app.add_handler(CommandHandler("stats", stats_command))
453
- app.add_handler(CommandHandler("model", model_info))
454
-
455
- # Handler برای تمام پیام‌ها (ذخیره + پردازش)
456
  app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
457
-
458
- # Error handler
459
- app.add_error_handler(error_handler)
460
-
461
- # شروع
462
- logger.info(f"Bot started with Persian model: {MODEL_NAME}")
463
- app.run_polling(drop_pending_updates=True)
464
-
465
- if __name__ == '__main__':
466
- main()
 
10
  from nltk.tokenize import sent_tokenize
11
  import torch
12
 
13
+ # تنظیم مسیر cache برای Transformers
14
  cache_dir = '/tmp/transformers_cache'
15
  os.environ['TRANSFORMERS_CACHE'] = cache_dir
16
  os.environ['HF_HOME'] = cache_dir
17
  os.makedirs(cache_dir, exist_ok=True)
18
 
19
+ # تنظیم مسیر nltk
20
  try:
21
  nltk.download('punkt', download_dir='./nltk_data', quiet=True)
22
  nltk.data.path.append('./nltk_data')
23
  except:
24
  pass
25
 
26
+ # تنظیمات لاگ
27
+ logging.basicConfig(
28
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
29
+ level=logging.INFO
30
+ )
31
  logger = logging.getLogger(__name__)
32
 
33
+ # اطلاعات مدل
 
 
 
 
34
  MODEL_NAME = "nafisehNik/mt5-persian-summary"
35
  model = None
36
  tokenizer = None
37
 
38
+ # ذخیره پیام‌ها برای هر چت
39
+ MAX_MESSAGES_PER_CHAT = 1000
40
  class MessageStore:
 
 
41
  def __init__(self):
42
  self.messages = {}
43
+
44
+ def add_message(self, chat_id, user_id, username, text, timestamp):
 
45
  if chat_id not in self.messages:
46
  self.messages[chat_id] = []
47
+
 
48
  if len(self.messages[chat_id]) >= MAX_MESSAGES_PER_CHAT:
49
+ self.messages[chat_id] = self.messages[chat_id][-MAX_MESSAGES_PER_CHAT // 2:]
50
+
51
  self.messages[chat_id].append({
52
+ "user_id": user_id,
53
+ "username": username,
54
+ "text": text,
55
+ "timestamp": timestamp
56
  })
57
+
58
+ def get_messages(self, chat_id, count=50, hours_back=None):
 
59
  if chat_id not in self.messages:
60
  return []
61
+
62
  messages = self.messages[chat_id]
63
+
 
64
  if hours_back:
65
+ cutoff = datetime.now() - timedelta(hours=hours_back)
66
+ messages = [m for m in messages if m["timestamp"] >= cutoff]
67
+
 
68
  return messages[-count:] if count else messages
69
 
 
70
  message_store = MessageStore()
71
 
72
  def load_persian_model():
73
+ global model, tokenizer
74
  try:
75
  logger.info(f"Loading Persian model: {MODEL_NAME}")
76
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, cache_dir=cache_dir)
 
 
 
 
 
 
77
  model = AutoModelForSeq2SeqLM.from_pretrained(
78
  MODEL_NAME,
79
  cache_dir=cache_dir,
80
+ torch_dtype=torch.float32
 
 
81
  )
 
82
  model.eval()
83
+ logger.info("Model loaded successfully")
 
 
84
  except Exception as e:
85
  logger.error(f"Error loading Persian model: {e}")
86
+ model, tokenizer = None, None
87
 
88
  def preprocess_persian_text(text):
89
+ text = re.sub(r'\s+', ' ', text)
90
+ text = re.sub(r'\n+', '\n', text)
91
+ text = re.sub(r'\d{2}:\d{2}', '', text)
92
+ text = re.sub(r'@\w+', '', text)
93
+ text = re.sub(r'http\S+', '', text)
94
+ text = re.sub(r'[^\w\s\u0600-\u06FF]', ' ', text)
 
 
 
 
 
 
 
 
 
95
  return text.strip()
96
 
97
  def chunk_text_smart(text, max_length=300):
 
98
  try:
99
  sentences = sent_tokenize(text)
100
  except:
101
+ sentences = re.split(r'[.!?؟]+', text)
102
+
 
103
  chunks = []
104
+ current = ""
 
105
  for sentence in sentences:
106
+ if len(current + sentence) < max_length:
107
+ current += sentence + " "
 
 
 
 
108
  else:
109
+ if current:
110
+ chunks.append(current.strip())
111
+ current = sentence + " "
112
+ if current:
113
+ chunks.append(current.strip())
 
 
114
  return chunks
115
 
116
  def summarize_messages(messages_data):
 
117
  global model, tokenizer
 
118
  if not model or not tokenizer:
119
  return "❌ مدل خلاصه‌سازی در دسترس نیست"
 
120
  if not messages_data:
121
  return "❌ پیامی برای خلاصه‌سازی یافت نشد"
122
+
123
  try:
124
+ text = ""
 
125
  for msg in messages_data:
126
+ username = msg['username'] or "کاربر"
127
+ text += f"{username}: {msg['text']}\n"
128
+
129
+ text = preprocess_persian_text(text)
130
+ if len(text) < 100:
 
 
131
  return "❌ متن برای خلاصه‌سازی بسیار کوتاه است"
132
+
133
+ chunks = chunk_text_smart(text, max_length=400)
 
134
  summaries = []
135
+
136
+ for chunk in chunks[:2]:
137
+ inputs = tokenizer.encode(f"خلاصه: {chunk}", return_tensors="pt", max_length=512, truncation=True)
138
+ output = model.generate(
139
+ inputs,
140
+ max_length=100,
141
+ min_length=30,
142
+ length_penalty=1.2,
143
+ num_beams=3,
144
+ early_stopping=True,
145
+ no_repeat_ngram_size=3
146
+ )
147
+ summary = tokenizer.decode(output[0], skip_special_tokens=True)
148
+ summaries.append(summary.replace("خلاصه:", "").strip())
149
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  if not summaries:
151
+ return "❌ خطا در خلاصه‌سازی"
152
+
153
+ stats = f"\n\n📊 آمار: {len(messages_data)} پیام، {len(text)} کاراکتر"
154
+ return f"📝 خلاصه گفتگو:\n\n" + "\n\n".join(summaries) + stats
155
+
 
 
 
 
 
156
  except Exception as e:
157
  logger.error(f"Summarization error: {e}")
158
+ return "❌ خطا در خلاصه‌سازی"
159
 
160
  def parse_summary_request(text):
 
161
  text = text.lower()
162
+ count = 50
163
+ hours = None
164
+
165
+ match = re.search(r'(\d+)\s*(پیام|تا|عدد)', text)
166
+ if match:
167
+ count = min(int(match.group(1)), 200)
168
+
169
+ match = re.search(r'(\d+)\s*(ساعت|روز)', text)
170
+ if match:
171
+ hours = int(match.group(1))
172
+ if "روز" in match.group(2):
173
+ hours *= 24
174
+ hours = min(hours, 72)
175
+
176
+ return count, hours
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
  async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
179
+ await update.message.reply_text("🤖 سلام! برای خلاصه‌سازی، عبارت «خلاصه» به همراه تعداد پیام یا مدت زمان را بفرست.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
182
+ message = update.message
183
+ if not message or not message.text:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
+ chat_id = message.chat_id
187
+ user_id = message.from_user.id
188
+ username = message.from_user.username
189
+ text = message.text.strip()
190
+ timestamp = message.date or datetime.utcnow()
191
 
192
+ message_store.add_message(chat_id, user_id, username, text, timestamp)
193
+
194
+ if "خلاصه" in text:
195
+ count, hours = parse_summary_request(text)
196
+ msgs = message_store.get_messages(chat_id, count, hours)
197
+ summary = summarize_messages(msgs)
198
+ await update.message.reply_text(summary)
199
+
200
+ if __name__ == "__main__":
201
+ load_persian_model()
202
+ TOKEN = os.getenv("BOT_TOKEN") # یا مستقیم وارد کن: 'your_token_here'
203
+
204
+ if not TOKEN:
205
+ raise ValueError("❌ توکن تلگرام تعریف نشده.")
206
+
207
+ app = ApplicationBuilder().token(TOKEN).build()
 
 
 
 
 
 
208
  app.add_handler(CommandHandler("start", start))
 
 
 
 
 
209
  app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
210
+
211
+ logger.info("Starting bot...")
212
+ app.run_polling()