Opera8 commited on
Commit
084a731
·
verified ·
1 Parent(s): 69a88d8

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +208 -209
main.py CHANGED
@@ -21,39 +21,46 @@ app = Flask(__name__)
21
 
22
  @app.route('/')
23
  def home():
24
- return "ربات یکپارچه آلفا (نسخه پرو + دانلود فایل ۱۰۰٪ تضمینی + چت چندرسانه‌ای + پرامپت‌نویس هوشمند + ساخت پادکست) روشن است! 🚀"
25
 
26
  def run_flask():
27
  app.run(host="0.0.0.0", port=7860)
28
 
29
 
30
- # --- ساختار کیبورد پایین صفحه (Chat Keypad) ---
31
  MAIN_KEYPAD_DICT = {
32
  "rows":[
33
- # ردیف اول (فقط ۱ دکمه)
34
  {
35
  "buttons":[
 
36
  {"id": "chat_btn", "type": "Simple", "button_text": "چت با هوش مصنوعی 🤖"}
37
  ]
38
  },
39
- # ردیف دوم (۲ دکمه)
40
  {
41
  "buttons":[
42
- {"id": "img_btn", "type": "Simple", "button_text": "ساخت تصاویر🎨"},
43
- {"id": "tts_btn", "type": "Simple", "button_text": "تبدیل متن به صدا🗣️"}
44
  ]
45
  },
46
- # ردیف سوم (۲ دکمه)
47
  {
48
  "buttons":[
49
  {"id": "podcast_btn", "type": "Simple", "button_text": "ساخت پادکست 🎙️"},
50
- {"id": "file_btn", "type": "Simple", "button_text": "تحلیل فایل 📁"}
51
  ]
52
  },
53
- # ردیف چهارم (۲ دکمه)
54
  {
55
  "buttons":[
56
  {"id": "stt_btn", "type": "Simple", "button_text": "فایل صوتی به متن 📝"},
 
 
 
 
 
 
57
  {"id": "cancel_btn", "type": "Simple", "button_text": "برگشت♻️"}
58
  ]
59
  }
@@ -62,7 +69,7 @@ MAIN_KEYPAD_DICT = {
62
  }
63
 
64
 
65
- # --- تابع ارسال منحصراً برای کیبورد پایین صفحه (اصلاح شده برای جلوگیری از اسپم) ---
66
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
67
  try:
68
  if not use_keyboard:
@@ -77,7 +84,6 @@ async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
77
  return await client._make_request("sendMessage", payload)
78
  except Exception:
79
  try:
80
- # اگر حالت کیبورد دار در سطح API خطا داد، فقط یکبار به صورت ساده پیام ارسال شود
81
  return await client.send_message(chat_id, text)
82
  except Exception:
83
  return None
@@ -85,7 +91,7 @@ async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
85
 
86
  # --- 🚨 تابع هوشمند دانلود فایل 🚨 ---
87
  async def helper_download_file(client, msg_obj):
88
- errors =[]
89
 
90
  file_id = None
91
  file_obj = None
@@ -139,10 +145,10 @@ async def helper_download_file(client, msg_obj):
139
 
140
  # --- تنظیمات کلیدها ---
141
  GEMINI_KEYS_STR = os.environ.get("GEMINI_API_KEYS", "")
142
- GEMINI_KEYS =[k.strip() for k in GEMINI_KEYS_STR.split(",") if k.strip()]
143
 
144
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
145
- HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
146
 
147
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
148
 
@@ -150,7 +156,7 @@ bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
150
  # --- 🚨 تابع اختصاصی آپلود فایل به روبیکا 🚨 ---
151
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
152
  abs_path = os.path.abspath(file_name)
153
- error_logs =[]
154
 
155
  api_file_type = "Image" if file_type in ["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else "File"
156
 
@@ -211,7 +217,7 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
211
  return "\n".join(error_logs)
212
 
213
 
214
- WORKER_URLS =[
215
  "https://hamed744-ttspro.hf.space/generate",
216
  "https://hamed744-ttspro2.hf.space/generate",
217
  "https://hamed744-ttspro3.hf.space/generate",
@@ -237,21 +243,68 @@ SPEAKERS = {
237
  }
238
 
239
  user_states = {}
240
-
241
- # --- متغیرهای کَش برای جلوگیری از پیام تکراری و اسپم شدن ---
242
  processed_message_ids = set()
243
  user_last_request_time = {}
244
 
245
 
246
- # --- ۱. پردازش چت متنی و چندرسانه‌ای ---
247
- async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
248
- if not GEMINI_KEYS:
249
- await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشده‌اند.", False)
250
  return
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
253
- history = user_states[chat_id].get("history",[])
254
- new_parts =[]
255
 
256
  if prompt: new_parts.append({"text": prompt})
257
  elif file_bytes: new_parts.append({"text": "لطفاً این فایل را به دقت بررسی کن."})
@@ -259,16 +312,7 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
259
  if file_bytes and file_name:
260
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
261
  mime_type, _ = mimetypes.guess_type(file_name)
262
- if not mime_type:
263
- if file_name.endswith(('.jpg', '.jpeg')): mime_type = "image/jpeg"
264
- elif file_name.endswith('.png'): mime_type = "image/png"
265
- elif file_name.endswith('.pdf'): mime_type = "application/pdf"
266
- elif file_name.endswith('.mp4'): mime_type = "video/mp4"
267
- elif file_name.endswith('.mp3'): mime_type = "audio/mp3"
268
- elif file_name.endswith(('.ogg', '.oga')): mime_type = "audio/ogg"
269
- elif file_name.endswith('.wav'): mime_type = "audio/wav"
270
- else: mime_type = "image/jpeg"
271
-
272
  new_parts.append({"inlineData": {"mimeType": mime_type, "data": base64_data}})
273
 
274
  if history and history[-1]["role"] == "user": history[-1]["parts"].extend(new_parts)
@@ -300,22 +344,21 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
300
  if proc_msg:
301
  msg_id = getattr(proc_msg, 'message_id', None)
302
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
303
- if msg_id: await client.delete_messages(chat_id,[msg_id])
304
  except Exception: pass
305
 
306
  if not final_answer:
307
  if history and history[-1]["role"] == "user": history.pop()
308
- await send_with_keyboard(client, chat_id, "❌ متأسفانه پاسخی دریافت نشد. (شاید سایز فایل بیش از حد مجاز بوده است)", False)
309
  return
310
 
311
- history.append({"role": "model", "parts":[{"text": final_answer}]})
312
  user_states[chat_id]["history"] = history
313
 
314
  try:
315
  max_len = 1000
316
- chunks =[]
317
  temp_text = final_answer
318
-
319
  while len(temp_text) > max_len:
320
  split_idx = temp_text.rfind('\n', 0, max_len)
321
  if split_idx == -1: split_idx = temp_text.rfind(' ', 0, max_len)
@@ -334,29 +377,18 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
334
  except Exception:
335
  await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
336
 
337
-
338
- # --- ۲. پردازش ساخت عکس ---
339
  async def process_image(client, chat_id, prompt):
340
- if not HF_TOKENS:
341
- await send_with_keyboard(client, chat_id, "❌ توکن‌های هاگینگ فیس تنظیم نشده‌اند.", False)
342
- return
343
-
344
  proc_msg = await send_with_keyboard(client, chat_id, "✨ در حال ترجمه و بهینه‌سازی پرامپت شما توسط جیمینای...\n(تبدیل به پرامپت حرفه‌ای)", False)
345
  enhanced_prompt = prompt
346
  if GEMINI_KEYS:
347
  keys_to_try_gemini = GEMINI_KEYS.copy()
348
  random.shuffle(keys_to_try_gemini)
349
- gemini_sys_prompt = (
350
- "You are an expert AI image generation prompt engineer. "
351
- "Translate the following user input to English, and enhance it with high-quality, "
352
- "highly detailed, 4k resolution, cinematic lighting, and visually striking descriptive keywords. "
353
- "Return ONLY the final English prompt string."
354
- f"\nUser input: {prompt}"
355
- )
356
  async with aiohttp.ClientSession() as session:
357
  for key in keys_to_try_gemini:
358
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
359
- payload = {"contents":[{"parts":[{"text": gemini_sys_prompt}]}], "generationConfig": {"temperature": 0.7}}
360
  try:
361
  async with session.post(url, json=payload, timeout=20) as response:
362
  if response.status == 200:
@@ -369,7 +401,7 @@ async def process_image(client, chat_id, prompt):
369
  if proc_msg:
370
  msg_id = getattr(proc_msg, 'message_id', None)
371
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
372
- if msg_id: await client.delete_messages(chat_id,[msg_id])
373
  except Exception: pass
374
 
375
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
@@ -393,12 +425,10 @@ async def process_image(client, chat_id, prompt):
393
  if proc_msg:
394
  msg_id = getattr(proc_msg, 'message_id', None)
395
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
396
- if msg_id: await client.delete_messages(chat_id,[msg_id])
397
  except Exception: pass
398
 
399
- if not generated_image:
400
- await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
401
- return
402
 
403
  try:
404
  file_name = f"image_{random.randint(1000, 999999)}.jpg"
@@ -407,14 +437,10 @@ async def process_image(client, chat_id, prompt):
407
  await asyncio.sleep(1)
408
  caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n✨ ایده اولیه: {prompt}"
409
  upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
410
- if upload_result is not True:
411
- await send_with_keyboard(client, chat_id, f"❌ خطا در آپلود روبیکا:\n`{str(upload_result)[:800]}`", True)
412
  if os.path.exists(file_name): os.remove(file_name)
413
- except Exception as e:
414
- await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس:\n{str(e)[:150]}", True)
415
 
416
-
417
- # --- ۳. پردازش ساخت صدا ---
418
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
419
  try:
420
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
@@ -442,7 +468,7 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
442
  if proc_msg:
443
  msg_id = getattr(proc_msg, 'message_id', None)
444
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
445
- if msg_id: await client.delete_messages(chat_id,[msg_id])
446
  except Exception: pass
447
 
448
  if audio_bytes:
@@ -455,57 +481,40 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
455
  else: await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
456
  except Exception: traceback.print_exc()
457
 
458
- # --- بخش جدید: پردازش و ساخت پادکست (اتصال به سرور HF) ---
459
  async def process_podcast(client, chat_id, prompt):
460
- proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست توسط هوش مصنوعی...\n(لطفاً صبور باشید)", False)
461
-
462
- # آماده‌سازی دیتای گویندگان پیش‌فرض (همانند مرورگر)
463
- available_speakers =[]
464
  for num_key, (spk_name, spk_id) in SPEAKERS.items():
465
  gender = "male" if "مرد" in spk_name else "female"
466
  available_speakers.append({"id": spk_id, "name": spk_name.split(' (')[0], "gender": gender})
467
 
468
  url_create = "https://ezmarynoori-podgen.hf.space/api/create-full-podcast"
469
- payload_create = {
470
- "prompt": prompt,
471
- "available_speakers": available_speakers
472
- }
473
 
474
  async with aiohttp.ClientSession() as session:
475
  try:
476
- # 1. درخواست نگارش سناریو به اسپیس ساخت پادکست
477
  async with session.post(url_create, json=payload_create, timeout=60) as resp:
478
- if resp.status != 202:
479
- await send_with_keyboard(client, chat_id, f"❌ خطا در سرور پادکست: وضعیت {resp.status}", True)
480
- return
481
- data = await resp.json()
482
- task_id = data.get("task_id")
483
- except Exception as e:
484
- await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور پادکست:\n{str(e)}", True)
485
- return
486
 
487
- # 2. بررسی وضعیت (Polling) تا زمان آماده شدن سناریو
488
  url_status = f"https://ezmarynoori-podgen.hf.space/api/podcast-status/{task_id}"
489
  script_data = None
490
- for _ in range(40): # تا 120 ثانیه صبر میکند
491
  await asyncio.sleep(3)
492
  try:
493
  async with session.get(url_status) as resp:
494
  if resp.status == 200:
495
  status_data = await resp.json()
496
  if status_data.get("status") == "completed":
497
- script_data = status_data.get("data", {}).get("script",[])
498
  break
499
  elif status_data.get("status") == "failed":
500
- await send_with_keyboard(client, chat_id, "❌ متأسفانه هوش مصنوعی نتوانست برای این موضوع سناریو بنویسد.", True)
501
- return
502
  except Exception: pass
503
 
504
- if not script_data:
505
- await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای نوشتن سناریو به پایان رسید.", True)
506
- return
507
 
508
- # آپدیت پیام به کاربر
509
  try:
510
  if proc_msg:
511
  msg_id = getattr(proc_msg, 'message_id', None)
@@ -513,42 +522,29 @@ async def process_podcast(client, chat_id, prompt):
513
  if msg_id: await client.delete_messages(chat_id, [msg_id])
514
  except: pass
515
 
516
- proc_msg = await send_with_keyboard(client, chat_id, f"🎙 سناریو نوشته شد! در حال ریکورد و میکس دیالوگ‌های گویندگان (شامل {len(script_data)} نوبت صحبت)...\nاین مرحله زمان‌بر است...", False)
517
 
518
- # 3. دریافت فایل صوتی هر دیالوگ و میکس آن‌ها
519
  combined_audio = AudioSegment.empty()
520
  url_generate = "https://ezmarynoori-podgen.hf.space/api/generate"
521
 
522
  for index, turn in enumerate(script_data):
523
- payload_gen = {
524
- "text": turn["dialogue"],
525
- "speaker": turn["speaker_id"],
526
- "temperature": 0.9
527
- }
528
-
529
  chunk_audio_bytes = None
530
- for attempt in range(3): # ۳ بار تلاش برای هر بخش
531
  try:
532
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
533
  if resp.status == 200:
534
  chunk_audio_bytes = await resp.read()
535
  break
536
- except Exception:
537
- await asyncio.sleep(2)
538
 
539
- if not chunk_audio_bytes:
540
- await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} از سرور. عملیات متوقف شد.", True)
541
- return
542
 
543
  try:
544
- # تبدیل بایت به AudioSegment با استفاده از pydub
545
  audio_segment = AudioSegment.from_file(io.BytesIO(chunk_audio_bytes))
546
  combined_audio += audio_segment
547
- except Exception as e:
548
- await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
549
- return
550
 
551
- # 4. خروجی گرفتن و ارسال به کاربر
552
  file_name = f"final_podcast_{random.randint(1000, 999999)}.mp3"
553
  combined_audio.export(file_name, format="mp3")
554
 
@@ -559,21 +555,14 @@ async def process_podcast(client, chat_id, prompt):
559
  if msg_id: await client.delete_messages(chat_id, [msg_id])
560
  except: pass
561
 
562
- caption = f"🎧 پادکست شما با موفقیت میکس و ساخته شد!\n\n💡 موضوع شما: {prompt}"
563
  upload_result = await helper_upload_file(client, chat_id, file_name, "Voice", caption)
564
-
565
- if upload_result is not True:
566
- err_msg = str(upload_result)[:800]
567
- await send_with_keyboard(client, chat_id, f"❌ پادکست با موفقیت در سرور ساخته شد اما روبیکا خطای آپلود داد.\n\n`{err_msg}`", True)
568
-
569
- if os.path.exists(file_name):
570
- os.remove(file_name)
571
-
572
 
573
- # --- ۴. پردازش تبدیل فایل صوتی/تصویری به متن ---
574
  async def process_stt(client, chat_id, audio_bytes, file_name):
575
- if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای جیمینای تنظیم نشده‌اند.", False)
576
- proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال گوش دادن و پیاده‌سازی متن...", False)
577
  base64_data = base64.b64encode(audio_bytes).decode('utf-8')
578
  mime_type, _ = mimetypes.guess_type(file_name)
579
  if not mime_type: mime_type = "audio/ogg"
@@ -581,12 +570,12 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
581
  keys_to_try = GEMINI_KEYS.copy()
582
  random.shuffle(keys_to_try)
583
  transcribed_text = None
584
- prompt = "لطفاً این فایل صوتی/تصویری را با دقت کامل گوش بده و صحبت‌های داخل آن را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
585
 
586
  async with aiohttp.ClientSession() as session:
587
  for key in keys_to_try:
588
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
589
- payload = {"contents":[{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.2}}
590
  try:
591
  async with session.post(url, json=payload, timeout=60) as response:
592
  if response.status == 200:
@@ -599,16 +588,14 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
599
  if proc_msg:
600
  msg_id = getattr(proc_msg, 'message_id', None)
601
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
602
- if msg_id: await client.delete_messages(chat_id,[msg_id])
603
  except Exception: pass
604
 
605
- if transcribed_text: await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
606
- else: await send_with_keyboard(client, chat_id, f"❌ تبدیل فایل به متن ناموفق بود.", True)
607
 
608
-
609
- # --- ۵. پردازش تحلیل فایل مستقل ---
610
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
611
- if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید جیمینای تنظیم نیست.", False)
612
  proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
613
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
614
  mime_type, _ = mimetypes.guess_type(file_name)
@@ -621,7 +608,7 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
621
  async with aiohttp.ClientSession() as session:
622
  for key in keys_to_try:
623
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
624
- payload = {"contents":[{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.6}}
625
  try:
626
  async with session.post(url, json=payload, timeout=45) as response:
627
  if response.status == 200:
@@ -634,10 +621,10 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
634
  if proc_msg:
635
  msg_id = getattr(proc_msg, 'message_id', None)
636
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
637
- if msg_id: await client.delete_messages(chat_id,[msg_id])
638
  except Exception: pass
639
 
640
- if final_answer: await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
641
  else: await send_with_keyboard(client, chat_id, "❌ پاسخی دریافت نشد.", True)
642
 
643
 
@@ -651,39 +638,26 @@ else:
651
  async def main_handler(client, update):
652
  try:
653
  current_time = time.time()
654
-
655
- # --- 🛡 سیستم ضد اسپم و حذف پیام‌های تکراری 🛡 ---
656
  msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
657
  msg_id = getattr(update, "message_id", None)
658
  if not msg_id and msg_obj:
659
- if isinstance(msg_obj, dict):
660
- msg_id = msg_obj.get("message_id")
661
- else:
662
- msg_id = getattr(msg_obj, "message_id", None)
663
 
664
- # جلوگیری از پردازش یک آیدی پیام برای دفعات متعدد
665
  if msg_id:
666
- if msg_id in processed_message_ids:
667
- return
668
  processed_message_ids.add(msg_id)
669
- # پاکسازی کش برای جلوگیری از پر شدن رم
670
- if len(processed_message_ids) > 5000:
671
- processed_message_ids.clear()
672
 
673
  chat_id = getattr(update, "chat_id", None) or getattr(update, "author_guid", None) or getattr(update, "object_guid", None)
674
  if not chat_id: return
675
 
676
- # سیستم Rate Limit برای کاربران (جلوگیری از پاسخ به پیام‌های جمع شده در بک‌لاگ ربات)
677
  last_req_time = user_last_request_time.get(chat_id, 0)
678
- if current_time - last_req_time < 1.0: # اگر کاربر در کمتر از ۱ ثانیه پیام جدید داده، آن را نادیده بگیر
679
- return
680
  user_last_request_time[chat_id] = current_time
681
- # ----------------------------------------------------
682
 
683
  user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
684
  user_text_str = str(user_text).strip() if user_text else ""
685
 
686
- # تشخیص فایل
687
  is_file = False
688
  file_name = "unknown_file.jpg"
689
 
@@ -692,7 +666,7 @@ else:
692
  file_attr = getattr(msg_obj, attr, None)
693
  if file_attr:
694
  is_file = True
695
- if hasattr(file_attr, 'file_name'): file_name = file_attr.file_name
696
  break
697
 
698
  if not is_file and hasattr(msg_obj, 'to_dict'):
@@ -701,62 +675,87 @@ else:
701
  if attr in msg_dict:
702
  is_file = True
703
  file_attr = msg_dict.get(attr, {})
704
- if isinstance(file_attr, dict) and 'file_name' in file_attr: file_name = file_attr['file_name']
705
  break
706
 
707
  if chat_id not in user_states:
708
- user_states[chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None}
709
 
710
  if user_text_str in ["/start", "سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
711
- user_states[chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None}
712
- await send_with_keyboard(client, chat_id, "سلام! به ربات هوشمند آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
  return
714
 
715
  if user_text_str in ["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]:
716
- user_states[chat_id] = {"mode": "chat", "text": "", "history":[]}
717
- await send_with_keyboard(client, chat_id, "💬 شما وارد بخش **چت با هوش مصنوعی** شدید.\n\nهر سوالی دارید بفرستید تا جواب بدم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
718
  return
719
 
720
  if user_text_str in ["/image", "🎨 عکس", "ساخت تصاویر🎨"]:
721
- user_states[chat_id] = {"mode": "image_waiting_for_text", "text": "", "history":[]}
722
- await send_with_keyboard(client, chat_id, "🎨 شما وارد بخش **ساخت عکس پیشرفته** شدید.\n\nمتن خود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
 
 
 
 
 
 
723
  return
724
 
725
  if user_text_str in ["/tts", "🎙️ صدا", "تبدیل متن به صدا🗣️"]:
726
- user_states[chat_id] = {"mode": "tts_waiting_for_text", "text": "", "history":[]}
727
- await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تبدیل متن به صدا** شدید.\n\nلطفاً متنی که می‌خواهید به صدا تبدیل شود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
728
  return
729
 
730
  if user_text_str in ["/podcast", "📻 پادکست", "ساخت پادکست 🎙️"]:
731
- user_states[chat_id] = {"mode": "podcast_waiting_for_topic", "text": "", "history":[]}
732
- await send_with_keyboard(client, chat_id, "📻 شما وارد بخش **ساخت پادکست** شدید.\n\nلطفاً موضوع پادکست خود را بفرستید.\nمثال: درباره تاریخچه پیدایش قهوه با ۳ گوینده یک پادکست جذاب بساز\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
733
  return
734
 
735
  if user_text_str in ["/file", "تحلیل فایل 📁"]:
736
- user_states[chat_id] = {"mode": "file_waiting_for_file", "text": "", "history":[], "file_bytes": None, "file_name": None}
737
- await send_with_keyboard(client, chat_id, "📁 شما وارد بخش **تحلیل فایل اختصاصی** شدید.\n\nلطفاً فایل خود را ارسال کنید:\n(برای خروج دکمه «برگشت���️» را بزنید)", True)
738
  return
739
 
740
  if user_text_str in ["/stt", "فایل صوتی به متن 📝"]:
741
- user_states[chat_id] = {"mode": "stt_waiting_for_audio", "text": "", "history":[]}
742
- await send_with_keyboard(client, chat_id, "📝 شما وارد بخش **تبدیل صدا به متن** شدید.\n\nلطفاً فایل خود (ویس، آهنگ، ویدیو و...) را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
743
  return
744
 
745
  current_mode = user_states[chat_id].get("mode")
746
 
747
  if current_mode is None:
748
  if is_file: pass
749
- elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True)
750
  return
751
 
752
- # --- مسیریابی ---
753
  elif current_mode == "chat":
754
  if is_file:
755
  await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
756
  try:
757
  file_bytes = await helper_download_file(client, msg_obj)
758
  asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=file_bytes, file_name=file_name))
759
- except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
760
  elif user_text_str: asyncio.create_task(process_gemini(client, chat_id, user_text_str))
761
  return
762
 
@@ -764,65 +763,68 @@ else:
764
  if user_text_str: asyncio.create_task(process_image(client, chat_id, user_text_str))
765
  return
766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  elif current_mode == "tts_waiting_for_text":
768
  if user_text_str:
769
- if len(user_text_str) > 2500:
770
- await send_with_keyboard(client, chat_id, "⚠️ لطفاً متنی کوتاه‌تر از 2500 کاراکتر بفرستید.", False)
771
- return
772
  user_states[chat_id]["text"] = user_text_str
773
  user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
774
- speakers_menu = """✅ متن شما ذخیره شد.
775
- لطفاً **شماره** گوینده مورد نظر خود را بفرستید:
776
- 1. شهاب | 2. آوا | 3. نوید
777
- 4. آرمان | 5. مهسا | 6. دانا
778
- 7. سامان | 8. آرش | 9. شبنم
779
- 10. سحر | 11. مریم | 12. بهرام
780
- 13. نیکان| 14. فرناز | 15. سارا
781
- 16. مانی | 17. آرتین | 18. دلنواز
782
- 19. روژان | 20. امید | 21. بردیا
783
- 22. ترانه | 23. نیکو | 24. هستی
784
- 25. کامیار| 26. کیانوش| 27. پویا
785
- 28. مهتاب | 29. سام | 30. لیدا"""
786
- await send_with_keyboard(client, chat_id, speakers_menu, False)
787
  return
788
 
789
  elif current_mode == "tts_waiting_for_speaker":
790
  if user_text_str.isdigit() and user_text_str in SPEAKERS:
791
- speaker_name, speaker_id = SPEAKERS[user_text_str]
792
- saved_text = user_states[chat_id]["text"]
793
  user_states[chat_id]["mode"] = "tts_waiting_for_text"
794
- user_states[chat_id]["text"] = ""
795
- asyncio.create_task(process_tts(client, chat_id, saved_text, speaker_id, speaker_name))
796
- else:
797
- await send_with_keyboard(client, chat_id, "❌ شماره نامعتبر است! لطفاً فقط یک عدد بین 1 تا 30 بفرستید.", False)
798
  return
799
 
800
  elif current_mode == "podcast_waiting_for_topic":
801
- if user_text_str:
802
- asyncio.create_task(process_podcast(client, chat_id, user_text_str))
803
- else:
804
- await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع پادکست خود را به صورت متنی بفرستید.", False)
805
  return
806
 
807
  elif current_mode == "stt_waiting_for_audio":
808
- if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل ارسال کنید.", False)
809
- await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
810
  try:
811
  audio_bytes = await helper_download_file(client, msg_obj)
812
  asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name))
813
- except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دانلود فایل!\n{str(dl_err)}", False)
814
  return
815
 
816
  elif current_mode == "file_waiting_for_file":
817
- if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک فایل ارسال کنید.", False)
818
- await send_with_keyboard(client, chat_id, "📥 در حال دریافت فایل...", False)
819
  try:
820
  file_bytes = await helper_download_file(client, msg_obj)
821
  user_states[chat_id]["file_bytes"] = file_bytes
822
  user_states[chat_id]["file_name"] = file_name
823
  user_states[chat_id]["mode"] = "file_waiting_for_prompt"
824
- await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بگویید **چگونه تحلیل شود؟**", False)
825
- except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
826
  return
827
 
828
  elif current_mode == "file_waiting_for_prompt":
@@ -830,17 +832,14 @@ else:
830
  saved_bytes = user_states[chat_id].get("file_bytes")
831
  saved_name = user_states[chat_id].get("file_name", "file.jpeg")
832
  user_states[chat_id]["mode"] = "file_waiting_for_file"
833
- user_states[chat_id]["file_bytes"] = None
834
- user_states[chat_id]["file_name"] = None
835
  asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str))
836
- else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
837
  return
838
 
839
  except Exception: traceback.print_exc()
840
 
841
  if __name__ == "__main__":
842
  threading.Thread(target=run_flask, daemon=True).start()
843
-
844
  if bot_token:
845
- print("ربات یکپارچه آلفا روشن شد...")
846
  bot.run()
 
21
 
22
  @app.route('/')
23
  def home():
24
+ return "ربات یکپارچه آلفا (نسخه پرو + دانلود فایل ۱۰۰٪ تضمینی + چت چندرسانه‌ای + پرامپت‌نویس + ساخت پادکست + ویرایش عکس) روشن است! 🚀"
25
 
26
  def run_flask():
27
  app.run(host="0.0.0.0", port=7860)
28
 
29
 
30
+ # --- ساختار کیبورد آپدیت شده (اضافه شدن ویرایش تصویر و حساب کاربری) ---
31
  MAIN_KEYPAD_DICT = {
32
  "rows":[
33
+ # ردیف اول
34
  {
35
  "buttons":[
36
+ {"id": "account_btn", "type": "Simple", "button_text": "حساب کاربری 👤"},
37
  {"id": "chat_btn", "type": "Simple", "button_text": "چت با هوش مصنوعی 🤖"}
38
  ]
39
  },
40
+ # ردیف دوم
41
  {
42
  "buttons":[
43
+ {"id": "edit_img_btn", "type": "Simple", "button_text": "ویرایش تصاویر 🪄"},
44
+ {"id": "img_btn", "type": "Simple", "button_text": "ساخت تصاویر🎨"}
45
  ]
46
  },
47
+ # ردیف سوم
48
  {
49
  "buttons":[
50
  {"id": "podcast_btn", "type": "Simple", "button_text": "ساخت پادکست 🎙️"},
51
+ {"id": "tts_btn", "type": "Simple", "button_text": "تبدیل متن به صدا🗣️"}
52
  ]
53
  },
54
+ # ردیف چهارم
55
  {
56
  "buttons":[
57
  {"id": "stt_btn", "type": "Simple", "button_text": "فایل صوتی به متن 📝"},
58
+ {"id": "file_btn", "type": "Simple", "button_text": "تحلیل فایل 📁"}
59
+ ]
60
+ },
61
+ # ردیف پنجم
62
+ {
63
+ "buttons":[
64
  {"id": "cancel_btn", "type": "Simple", "button_text": "برگشت♻️"}
65
  ]
66
  }
 
69
  }
70
 
71
 
72
+ # --- تابع ارسال منحصراً برای کیبورد پایین صفحه ---
73
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
74
  try:
75
  if not use_keyboard:
 
84
  return await client._make_request("sendMessage", payload)
85
  except Exception:
86
  try:
 
87
  return await client.send_message(chat_id, text)
88
  except Exception:
89
  return None
 
91
 
92
  # --- 🚨 تابع هوشمند دانلود فایل 🚨 ---
93
  async def helper_download_file(client, msg_obj):
94
+ errors = []
95
 
96
  file_id = None
97
  file_obj = None
 
145
 
146
  # --- تنظیمات کلیدها ---
147
  GEMINI_KEYS_STR = os.environ.get("GEMINI_API_KEYS", "")
148
+ GEMINI_KEYS = [k.strip() for k in GEMINI_KEYS_STR.split(",") if k.strip()]
149
 
150
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
151
+ HF_TOKENS = [k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
152
 
153
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
154
 
 
156
  # --- 🚨 تابع اختصاصی آپلود فایل به روبیکا 🚨 ---
157
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
158
  abs_path = os.path.abspath(file_name)
159
+ error_logs = []
160
 
161
  api_file_type = "Image" if file_type in ["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else "File"
162
 
 
217
  return "\n".join(error_logs)
218
 
219
 
220
+ WORKER_URLS = [
221
  "https://hamed744-ttspro.hf.space/generate",
222
  "https://hamed744-ttspro2.hf.space/generate",
223
  "https://hamed744-ttspro3.hf.space/generate",
 
243
  }
244
 
245
  user_states = {}
 
 
246
  processed_message_ids = set()
247
  user_last_request_time = {}
248
 
249
 
250
+ # --- بخش جدید: پردازش ویرایش عکس با FLUX.2-dev ---
251
+ async def process_image_edit(client, chat_id, image_bytes, prompt):
252
+ if not HF_TOKENS:
253
+ await send_with_keyboard(client, chat_id, "❌ توکن‌های هاگینگ فیس تنظیم نشده‌اند.", False)
254
  return
255
 
256
+ proc_msg = await send_with_keyboard(client, chat_id, "🪄 در حال پردازش و اعمال جادوی FLUX.2 روی عکس شما...\n(این فرآیند ممکن است کمی طول بکشد)", False)
257
+
258
+ keys_to_try = HF_TOKENS.copy()
259
+ random.shuffle(keys_to_try)
260
+ generated_image = None
261
+ last_error_log = "سرور پاسخ نداد."
262
+
263
+ for token in keys_to_try:
264
+ try:
265
+ hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
266
+ # استفاده از متد image_to_image با ارسال دیتای بایت عکس و پرامپت متنی کاربر
267
+ generated_image = await hf_client.image_to_image(
268
+ image_bytes,
269
+ prompt=prompt,
270
+ model="black-forest-labs/FLUX.2-dev"
271
+ )
272
+ break
273
+ except Exception as e:
274
+ last_error_log = str(e)
275
+ continue
276
+
277
+ try:
278
+ if proc_msg:
279
+ msg_id = getattr(proc_msg, 'message_id', None)
280
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
281
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
282
+ except Exception: pass
283
+
284
+ if not generated_image:
285
+ await send_with_keyboard(client, chat_id, f"❌ متأسفانه ویرایش عکس انجام نشد.\n\n⚠️ علت:\n{last_error_log[:200]}", True)
286
+ return
287
+
288
+ try:
289
+ file_name = f"edited_flux_{random.randint(1000, 999999)}.jpg"
290
+ rgb_im = generated_image.convert('RGB')
291
+ rgb_im.save(file_name, format="JPEG", quality=100)
292
+ await asyncio.sleep(1)
293
+ caption_text = f"🪄 ویرایش عکس با هوش مصنوعی Flux.2 انجام شد!\n\n✨ تغییرات خواسته شده: {prompt}"
294
+ upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
295
+ if upload_result is not True:
296
+ await send_with_keyboard(client, chat_id, f"❌ عکس ساخته شد اما خطا در ارسال به روبیکا رخ داد:\n`{str(upload_result)[:800]}`", True)
297
+ if os.path.exists(file_name): os.remove(file_name)
298
+ except Exception as e:
299
+ await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
300
+
301
+
302
+ # --- پردازش‌های قبلی (چت، عکس، صدا، پادکست، استخراج متن و تحلیل) بدون تغییر ---
303
+ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
304
+ if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشده‌اند.", False)
305
  proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
306
+ history = user_states[chat_id].get("history", [])
307
+ new_parts = []
308
 
309
  if prompt: new_parts.append({"text": prompt})
310
  elif file_bytes: new_parts.append({"text": "لطفاً این فایل را به دقت بررسی کن."})
 
312
  if file_bytes and file_name:
313
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
314
  mime_type, _ = mimetypes.guess_type(file_name)
315
+ if not mime_type: mime_type = "image/jpeg"
 
 
 
 
 
 
 
 
 
316
  new_parts.append({"inlineData": {"mimeType": mime_type, "data": base64_data}})
317
 
318
  if history and history[-1]["role"] == "user": history[-1]["parts"].extend(new_parts)
 
344
  if proc_msg:
345
  msg_id = getattr(proc_msg, 'message_id', None)
346
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
347
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
348
  except Exception: pass
349
 
350
  if not final_answer:
351
  if history and history[-1]["role"] == "user": history.pop()
352
+ await send_with_keyboard(client, chat_id, "❌ متأسفانه پاسخی دریافت نشد.", False)
353
  return
354
 
355
+ history.append({"role": "model", "parts": [{"text": final_answer}]})
356
  user_states[chat_id]["history"] = history
357
 
358
  try:
359
  max_len = 1000
360
+ chunks = []
361
  temp_text = final_answer
 
362
  while len(temp_text) > max_len:
363
  split_idx = temp_text.rfind('\n', 0, max_len)
364
  if split_idx == -1: split_idx = temp_text.rfind(' ', 0, max_len)
 
377
  except Exception:
378
  await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
379
 
 
 
380
  async def process_image(client, chat_id, prompt):
381
+ if not HF_TOKENS: return await send_with_keyboard(client, chat_id, "❌ توکن‌ها تنظیم نشده‌اند.", False)
 
 
 
382
  proc_msg = await send_with_keyboard(client, chat_id, "✨ در حال ترجمه و بهینه‌سازی پرامپت شما توسط جیمینای...\n(تبدیل به پرامپت حرفه‌ای)", False)
383
  enhanced_prompt = prompt
384
  if GEMINI_KEYS:
385
  keys_to_try_gemini = GEMINI_KEYS.copy()
386
  random.shuffle(keys_to_try_gemini)
387
+ gemini_sys_prompt = f"You are an expert AI image generation prompt engineer. Translate the following user input to English, and enhance it with high-quality, highly detailed, 4k resolution, cinematic lighting keywords. Return ONLY the final English prompt string.\nUser input: {prompt}"
 
 
 
 
 
 
388
  async with aiohttp.ClientSession() as session:
389
  for key in keys_to_try_gemini:
390
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
391
+ payload = {"contents": [{"parts": [{"text": gemini_sys_prompt}]}], "generationConfig": {"temperature": 0.7}}
392
  try:
393
  async with session.post(url, json=payload, timeout=20) as response:
394
  if response.status == 200:
 
401
  if proc_msg:
402
  msg_id = getattr(proc_msg, 'message_id', None)
403
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
404
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
405
  except Exception: pass
406
 
407
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
 
425
  if proc_msg:
426
  msg_id = getattr(proc_msg, 'message_id', None)
427
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
428
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
429
  except Exception: pass
430
 
431
+ if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
 
 
432
 
433
  try:
434
  file_name = f"image_{random.randint(1000, 999999)}.jpg"
 
437
  await asyncio.sleep(1)
438
  caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n✨ ایده اولیه: {prompt}"
439
  upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
440
+ if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ خطا در آپلود:\n`{str(upload_result)[:800]}`", True)
 
441
  if os.path.exists(file_name): os.remove(file_name)
442
+ except Exception as e: await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس:\n{str(e)[:150]}", True)
 
443
 
 
 
444
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
445
  try:
446
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
 
468
  if proc_msg:
469
  msg_id = getattr(proc_msg, 'message_id', None)
470
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
471
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
472
  except Exception: pass
473
 
474
  if audio_bytes:
 
481
  else: await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
482
  except Exception: traceback.print_exc()
483
 
 
484
  async def process_podcast(client, chat_id, prompt):
485
+ proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست...\n(لطفاً صبور باشید)", False)
486
+ available_speakers = []
 
 
487
  for num_key, (spk_name, spk_id) in SPEAKERS.items():
488
  gender = "male" if "مرد" in spk_name else "female"
489
  available_speakers.append({"id": spk_id, "name": spk_name.split(' (')[0], "gender": gender})
490
 
491
  url_create = "https://ezmarynoori-podgen.hf.space/api/create-full-podcast"
492
+ payload_create = {"prompt": prompt, "available_speakers": available_speakers}
 
 
 
493
 
494
  async with aiohttp.ClientSession() as session:
495
  try:
 
496
  async with session.post(url_create, json=payload_create, timeout=60) as resp:
497
+ if resp.status != 202: return await send_with_keyboard(client, chat_id, f"❌ خطا: وضعیت {resp.status}", True)
498
+ task_id = (await resp.json()).get("task_id")
499
+ except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا اتصال:\n{str(e)}", True)
 
 
 
 
 
500
 
 
501
  url_status = f"https://ezmarynoori-podgen.hf.space/api/podcast-status/{task_id}"
502
  script_data = None
503
+ for _ in range(40):
504
  await asyncio.sleep(3)
505
  try:
506
  async with session.get(url_status) as resp:
507
  if resp.status == 200:
508
  status_data = await resp.json()
509
  if status_data.get("status") == "completed":
510
+ script_data = status_data.get("data", {}).get("script", [])
511
  break
512
  elif status_data.get("status") == "failed":
513
+ return await send_with_keyboard(client, chat_id, "❌ هوش مصنوعی نتوانست سناریو بنویسد.", True)
 
514
  except Exception: pass
515
 
516
+ if not script_data: return await send_with_keyboard(client, chat_id, "❌ پایان زمان انتظار برای نوشتن سناریو.", True)
 
 
517
 
 
518
  try:
519
  if proc_msg:
520
  msg_id = getattr(proc_msg, 'message_id', None)
 
522
  if msg_id: await client.delete_messages(chat_id, [msg_id])
523
  except: pass
524
 
525
+ proc_msg = await send_with_keyboard(client, chat_id, f"🎙 سناریو نوشته شد! در حال میکس (شامل {len(script_data)} نوبت صحبت)...\nزمان‌بر است...", False)
526
 
 
527
  combined_audio = AudioSegment.empty()
528
  url_generate = "https://ezmarynoori-podgen.hf.space/api/generate"
529
 
530
  for index, turn in enumerate(script_data):
531
+ payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
 
 
 
 
 
532
  chunk_audio_bytes = None
533
+ for attempt in range(3):
534
  try:
535
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
536
  if resp.status == 200:
537
  chunk_audio_bytes = await resp.read()
538
  break
539
+ except Exception: await asyncio.sleep(2)
 
540
 
541
+ if not chunk_audio_bytes: return await send_with_keyboard(client, chat_id, f"❌ خطا در بخش {index+1}", True)
 
 
542
 
543
  try:
 
544
  audio_segment = AudioSegment.from_file(io.BytesIO(chunk_audio_bytes))
545
  combined_audio += audio_segment
546
+ except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا میکس:\n{str(e)}", True)
 
 
547
 
 
548
  file_name = f"final_podcast_{random.randint(1000, 999999)}.mp3"
549
  combined_audio.export(file_name, format="mp3")
550
 
 
555
  if msg_id: await client.delete_messages(chat_id, [msg_id])
556
  except: pass
557
 
558
+ caption = f"🎧 پادکست شما میکس شد!\n\n💡 موضوع: {prompt}"
559
  upload_result = await helper_upload_file(client, chat_id, file_name, "Voice", caption)
560
+ if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ خطای آپلود روبیکا.\n\n`{str(upload_result)[:800]}`", True)
561
+ if os.path.exists(file_name): os.remove(file_name)
 
 
 
 
 
 
562
 
 
563
  async def process_stt(client, chat_id, audio_bytes, file_name):
564
+ if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید تنظیم نشده", False)
565
+ proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال پیاده‌سازی متن...", False)
566
  base64_data = base64.b64encode(audio_bytes).decode('utf-8')
567
  mime_type, _ = mimetypes.guess_type(file_name)
568
  if not mime_type: mime_type = "audio/ogg"
 
570
  keys_to_try = GEMINI_KEYS.copy()
571
  random.shuffle(keys_to_try)
572
  transcribed_text = None
573
+ prompt = "این فایل صوتی/تصویری را دقیق گوش بده و صحبت‌ها را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
574
 
575
  async with aiohttp.ClientSession() as session:
576
  for key in keys_to_try:
577
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
578
+ payload = {"contents": [{"parts": [{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.2}}
579
  try:
580
  async with session.post(url, json=payload, timeout=60) as response:
581
  if response.status == 200:
 
588
  if proc_msg:
589
  msg_id = getattr(proc_msg, 'message_id', None)
590
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
591
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
592
  except Exception: pass
593
 
594
+ if transcribed_text: await send_with_keyboard(client, chat_id, f"📝 **متن:**\n\n{transcribed_text}", True)
595
+ else: await send_with_keyboard(client, chat_id, f"❌ تبدیل ناموفق", True)
596
 
 
 
597
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
598
+ if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید تنظیم نیست.", False)
599
  proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
600
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
601
  mime_type, _ = mimetypes.guess_type(file_name)
 
608
  async with aiohttp.ClientSession() as session:
609
  for key in keys_to_try:
610
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
611
+ payload = {"contents": [{"parts": [{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.6}}
612
  try:
613
  async with session.post(url, json=payload, timeout=45) as response:
614
  if response.status == 200:
 
621
  if proc_msg:
622
  msg_id = getattr(proc_msg, 'message_id', None)
623
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
624
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
625
  except Exception: pass
626
 
627
+ if final_answer: await send_with_keyboard(client, chat_id, f"💡 **تحلیل:**\n\n{final_answer}", True)
628
  else: await send_with_keyboard(client, chat_id, "❌ پاسخی دریافت نشد.", True)
629
 
630
 
 
638
  async def main_handler(client, update):
639
  try:
640
  current_time = time.time()
 
 
641
  msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
642
  msg_id = getattr(update, "message_id", None)
643
  if not msg_id and msg_obj:
644
+ msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None)
 
 
 
645
 
 
646
  if msg_id:
647
+ if msg_id in processed_message_ids: return
 
648
  processed_message_ids.add(msg_id)
649
+ if len(processed_message_ids) > 5000: processed_message_ids.clear()
 
 
650
 
651
  chat_id = getattr(update, "chat_id", None) or getattr(update, "author_guid", None) or getattr(update, "object_guid", None)
652
  if not chat_id: return
653
 
 
654
  last_req_time = user_last_request_time.get(chat_id, 0)
655
+ if current_time - last_req_time < 1.0: return
 
656
  user_last_request_time[chat_id] = current_time
 
657
 
658
  user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
659
  user_text_str = str(user_text).strip() if user_text else ""
660
 
 
661
  is_file = False
662
  file_name = "unknown_file.jpg"
663
 
 
666
  file_attr = getattr(msg_obj, attr, None)
667
  if file_attr:
668
  is_file = True
669
+ file_name = getattr(file_attr, 'file_name', file_name)
670
  break
671
 
672
  if not is_file and hasattr(msg_obj, 'to_dict'):
 
675
  if attr in msg_dict:
676
  is_file = True
677
  file_attr = msg_dict.get(attr, {})
678
+ file_name = file_attr.get('file_name', file_name) if isinstance(file_attr, dict) else file_name
679
  break
680
 
681
  if chat_id not in user_states:
682
+ user_states[chat_id] = {"mode": None, "text": "", "history": [], "file_bytes": None, "file_name": None}
683
 
684
  if user_text_str in ["/start", "سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
685
+ user_states[chat_id] = {"mode": None, "text": "", "history": [], "file_bytes": None, "file_name": None}
686
+ await send_with_keyboard(client, chat_id, "سلام! به ربات هوشمند آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، یکی از بخش‌های زیر را انتخاب کنید:", True)
687
+ return
688
+
689
+ # --- بخش جدید: حساب کاربری ---
690
+ if user_text_str in ["/account", "حساب کاربری 👤"]:
691
+ # متن پیشنهادی برای محدودیت‌های نسخه رایگان
692
+ account_profile = f"""👤 **اطلاعات حساب کاربری**
693
+
694
+ 🔹 **نام:** حامد نوری
695
+ 🔹 **شناسه یکتا:** `{chat_id}`
696
+ 🔹 **وضعیت اشتراک:** 🥉 نسخه رایگان (آزمایشی)
697
+
698
+ 📊 **سهمیه روزانه شما در نسخه رایگان:**
699
+ - 💬 چت هوشمند: ۳۰ پیام
700
+ - 🎨 ساخت عکس: ۵ تصویر
701
+ - 🪄 ویرایش عکس پیشرفته: ۳ تصویر
702
+ - 🎙 ساخت پادکست: ۱ برنامه
703
+ - 🗣 تبدیل متن به صدا: ۵ فایل
704
+
705
+ *نکته: در آینده با ارتقا به نسخه آلفا پرو می‌توانید تمام این محدودیت‌ها را بردارید.* 🚀"""
706
+ await send_with_keyboard(client, chat_id, account_profile, True)
707
  return
708
 
709
  if user_text_str in ["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]:
710
+ user_states[chat_id] = {"mode": "chat", "text": "", "history": []}
711
+ await send_with_keyboard(client, chat_id, "💬 وارد بخش **چت هوشمند** شدید.\nسوالی بپرسید تا جواب بدهم:", True)
712
  return
713
 
714
  if user_text_str in ["/image", "🎨 عکس", "ساخت تصاویر🎨"]:
715
+ user_states[chat_id] = {"mode": "image_waiting_for_text", "text": "", "history": []}
716
+ await send_with_keyboard(client, chat_id, "🎨 وارد بخش **ساخت عکس** شدید.\nمتن خود را بفرستید:", True)
717
+ return
718
+
719
+ # --- بخش جدید: ویرایش عکس ---
720
+ if user_text_str in ["/edit_image", "ویرایش تصاویر 🪄"]:
721
+ user_states[chat_id] = {"mode": "image_edit_waiting_for_image", "text": "", "history": [], "file_bytes": None, "file_name": None}
722
+ await send_with_keyboard(client, chat_id, "🪄 به بخش **ویرایش عکس (Flux.2)** خوش آمدید.\n\nلطفاً ابتدا عکسی که می‌خواهید ویرایش کنید را بفرستید:", True)
723
  return
724
 
725
  if user_text_str in ["/tts", "🎙️ صدا", "تبدیل متن به صدا🗣️"]:
726
+ user_states[chat_id] = {"mode": "tts_waiting_for_text", "text": "", "history": []}
727
+ await send_with_keyboard(client, chat_id, "🎙️ وارد بخش **تبدیل متن به صدا** شدید.\nمتن خود را بفرستید:", True)
728
  return
729
 
730
  if user_text_str in ["/podcast", "📻 پادکست", "ساخت پادکست 🎙️"]:
731
+ user_states[chat_id] = {"mode": "podcast_waiting_for_topic", "text": "", "history": []}
732
+ await send_with_keyboard(client, chat_id, "📻 وارد بخش **ساخت پادکست** شدید.\nموضوع پادکست را بفرستید:", True)
733
  return
734
 
735
  if user_text_str in ["/file", "تحلیل فایل 📁"]:
736
+ user_states[chat_id] = {"mode": "file_waiting_for_file", "text": "", "history": [], "file_bytes": None, "file_name": None}
737
+ await send_with_keyboard(client, chat_id, "📁 وارد بخش **تحلیل فایل** شدید.\nفایل خود را ارسال کنید:", True)
738
  return
739
 
740
  if user_text_str in ["/stt", "فایل صوتی به متن 📝"]:
741
+ user_states[chat_id] = {"mode": "stt_waiting_for_audio", "text": "", "history": []}
742
+ await send_with_keyboard(client, chat_id, "📝 وارد بخش **تبدیل صدا به متن** شدید.\nفایل خود را بفرستید:", True)
743
  return
744
 
745
  current_mode = user_states[chat_id].get("mode")
746
 
747
  if current_mode is None:
748
  if is_file: pass
749
+ elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک بخش را از کیبورد پایین انتخاب کنید:", True)
750
  return
751
 
 
752
  elif current_mode == "chat":
753
  if is_file:
754
  await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
755
  try:
756
  file_bytes = await helper_download_file(client, msg_obj)
757
  asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=file_bytes, file_name=file_name))
758
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا!\n{str(dl_err)}", False)
759
  elif user_text_str: asyncio.create_task(process_gemini(client, chat_id, user_text_str))
760
  return
761
 
 
763
  if user_text_str: asyncio.create_task(process_image(client, chat_id, user_text_str))
764
  return
765
 
766
+ # --- مسیریابی بخش ویرایش تصویر ---
767
+ elif current_mode == "image_edit_waiting_for_image":
768
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک عکس ارسال کنید.", False)
769
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود عکس...", False)
770
+ try:
771
+ file_bytes = await helper_download_file(client, msg_obj)
772
+ user_states[chat_id]["file_bytes"] = file_bytes
773
+ user_states[chat_id]["mode"] = "image_edit_waiting_for_prompt"
774
+ await send_with_keyboard(client, chat_id, "✅ عکس با موفقیت دریافت شد.\n\nحالا دستور خود را به صورت متنی تایپ کنید.\nمثال: یک کلاه قرمز روی سر این گربه بگذار.", False)
775
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
776
+ return
777
+
778
+ elif current_mode == "image_edit_waiting_for_prompt":
779
+ if user_text_str:
780
+ saved_bytes = user_states[chat_id].get("file_bytes")
781
+ user_states[chat_id]["mode"] = None
782
+ user_states[chat_id]["file_bytes"] = None
783
+ asyncio.create_task(process_image_edit(client, chat_id, saved_bytes, user_text_str))
784
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
785
+ return
786
+
787
  elif current_mode == "tts_waiting_for_text":
788
  if user_text_str:
789
+ if len(user_text_str) > 2500: return await send_with_keyboard(client, chat_id, "⚠️ متن خیلی طولانی است.", False)
 
 
790
  user_states[chat_id]["text"] = user_text_str
791
  user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
792
+ await send_with_keyboard(client, chat_id, "✅ متن ذخیره شد.\nلطفاً **شماره** گوینده (بین 1 تا 30) را بفرستید:", False)
 
 
 
 
 
 
 
 
 
 
 
 
793
  return
794
 
795
  elif current_mode == "tts_waiting_for_speaker":
796
  if user_text_str.isdigit() and user_text_str in SPEAKERS:
797
+ spk_name, spk_id = SPEAKERS[user_text_str]
798
+ txt = user_states[chat_id]["text"]
799
  user_states[chat_id]["mode"] = "tts_waiting_for_text"
800
+ asyncio.create_task(process_tts(client, chat_id, txt, spk_id, spk_name))
801
+ else: await send_with_keyboard(client, chat_id, "❌ شماره نامعتبر!", False)
 
 
802
  return
803
 
804
  elif current_mode == "podcast_waiting_for_topic":
805
+ if user_text_str: asyncio.create_task(process_podcast(client, chat_id, user_text_str))
806
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع را متنی بفرستید.", False)
 
 
807
  return
808
 
809
  elif current_mode == "stt_waiting_for_audio":
810
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل بفرستید.", False)
811
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود...", False)
812
  try:
813
  audio_bytes = await helper_download_file(client, msg_obj)
814
  asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name))
815
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا!\n{str(dl_err)}", False)
816
  return
817
 
818
  elif current_mode == "file_waiting_for_file":
819
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ فایل بفرستید.", False)
820
+ await send_with_keyboard(client, chat_id, "📥 دریافت فایل...", False)
821
  try:
822
  file_bytes = await helper_download_file(client, msg_obj)
823
  user_states[chat_id]["file_bytes"] = file_bytes
824
  user_states[chat_id]["file_name"] = file_name
825
  user_states[chat_id]["mode"] = "file_waiting_for_prompt"
826
+ await send_with_keyboard(client, chat_id, "✅ دریافت شد.\nچگونه تحلیل شود؟", False)
827
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا!\n{str(dl_err)}", False)
828
  return
829
 
830
  elif current_mode == "file_waiting_for_prompt":
 
832
  saved_bytes = user_states[chat_id].get("file_bytes")
833
  saved_name = user_states[chat_id].get("file_name", "file.jpeg")
834
  user_states[chat_id]["mode"] = "file_waiting_for_file"
 
 
835
  asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str))
836
+ else: await send_with_keyboard(client, chat_id, "⚠️ متن بفرستید.", False)
837
  return
838
 
839
  except Exception: traceback.print_exc()
840
 
841
  if __name__ == "__main__":
842
  threading.Thread(target=run_flask, daemon=True).start()
 
843
  if bot_token:
844
+ print("ربات آلفا روشن شد...")
845
  bot.run()