Opera8 commited on
Commit
4832843
·
verified ·
1 Parent(s): fa37ed4

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +1037 -210
main.py CHANGED
@@ -610,163 +610,122 @@ HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
610
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
611
 
612
  # ==============================================================================
613
- # 🟢 پارت 11: آپلودر قدرتمند و تابع ارسال فایل ضد خطا (نسخه نهایی ضد بمب اتم 🚀)
614
  # ==============================================================================
 
 
615
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
616
  abs_path = os.path.abspath(file_name)
617
- error_logs = []
618
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619
  api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else file_type
 
 
620
 
621
- # روبیکا فرمت‌های خاصی را قبول میکند.
622
- if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'):
623
- api_file_type = "Music"
624
- if api_file_type == "File" and abs_path.lower().endswith('.mp3'):
625
- api_file_type = "Music"
626
-
627
- # --- فاز اول (فوق سریع): استفاده از متدهای اختصاصی rubpy ---
628
- for attempt in range(10):
629
  try:
630
  sent_success = False
631
 
632
- # تلاش برای ارسال عکس
633
  if api_file_type == "Image" and hasattr(client, "send_photo"):
634
- try:
635
- await client.send_photo(chat_id, photo=abs_path, caption=caption)
636
- sent_success = True
637
- except TypeError:
638
- await client.send_photo(chat_id, abs_path, caption=caption)
639
- sent_success = True
640
-
641
- # تلاش برای ارسال ویس
642
  elif api_file_type == "Voice" and hasattr(client, "send_voice"):
643
- try:
644
- await client.send_voice(chat_id, abs_path, caption=caption)
645
- sent_success = True
646
- except TypeError:
647
- await client.send_voice(chat_id, file=abs_path, caption=caption)
648
- sent_success = True
649
-
650
- # تلاش برای ارسال موزیک
651
  elif api_file_type == "Music" and hasattr(client, "send_music"):
652
- try:
653
- await client.send_music(chat_id, abs_path, caption=caption)
654
- sent_success = True
655
- except TypeError:
656
- await client.send_music(chat_id, music=abs_path, caption=caption)
657
- sent_success = True
658
-
659
- # *** سیستم ضد بمب اتم فاز 1 ***
660
- # اگر هیچکدام از متدهای بالا وجود نداشت یا به هر دلیلی ارسال نشد، حتما به عنوان فایل (سند) بفرست تا معطل نشود
661
  if not sent_success:
662
  if hasattr(client, "send_document"):
663
- try:
664
- await client.send_document(chat_id, document=abs_path, caption=caption)
665
- sent_success = True
666
- except TypeError:
667
- await client.send_document(chat_id, abs_path, caption=caption)
668
- sent_success = True
669
  elif hasattr(client, "send_file"):
670
- try:
671
- await client.send_file(chat_id, file=abs_path)
672
- except TypeError:
673
- await client.send_file(chat_id, abs_path)
674
- if caption:
675
- await send_with_keyboard(client, chat_id, caption, True)
676
  sent_success = True
677
-
678
  if sent_success:
679
  return True
680
-
681
  except Exception as e:
682
- err_msg = str(e)[:150]
683
- error_logs.append(f"Rubpy Send Error: {err_msg}")
684
-
685
- if "502" in err_msg or "timeout" in err_msg.lower() or "time" in err_msg.lower():
686
- await asyncio.sleep(4)
687
- else:
688
- await asyncio.sleep(2)
689
 
690
- # --- فاز دوم (سنگر آخر): آپلود دستی (Raw HTTP) در صورت مسدودی کامل روب‌پای ---
691
- for attempt in range(15):
692
- try:
693
- url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
694
- url_send = f"https://botapi.rubika.ir/v3/{bot_token}/sendFile"
695
-
696
- async with aiohttp.ClientSession() as session:
697
- async with session.post(url_request, json={"type": api_file_type}, timeout=30) as resp:
698
- if resp.status != 200:
699
- error_logs.append(f"Request HTTP {resp.status}")
700
- await asyncio.sleep(2)
701
- continue
702
-
703
- req_data = await resp.json()
704
- if req_data.get("status") == "OK":
705
- upload_url = req_data.get("data", {}).get("upload_url")
706
- if upload_url:
707
- with open(abs_path, "rb") as f:
708
- form = aiohttp.FormData()
709
- form.add_field('file', f, filename=os.path.basename(abs_path))
710
- async with session.post(upload_url, data=form, timeout=300) as up_resp:
711
- if up_resp.status != 200:
712
- error_logs.append(f"Upload HTTP {up_resp.status}")
713
- if up_resp.status in[502, 503, 500]:
714
- await asyncio.sleep(3)
715
- else:
716
- await asyncio.sleep(1.5)
717
- continue
718
-
719
- try:
720
- up_data = await up_resp.json()
721
- except Exception as json_err:
722
- error_logs.append(f"JSON Decode Error: {json_err}")
723
- await asyncio.sleep(2)
724
- continue
725
-
726
- final_file_id = None
727
-
728
- # *** سیستم ضد بمب اتم فاز 2 ***
729
- # دور زدن ارور روبیکا: اگر روبیکا به فرمت فایل گیر داد، سریعاً نوع را به File تغییر بده تا بدون بررسی فرمت آپلود کند!
730
- if up_data.get("status") == "INVALID_INPUT" and "format" in str(up_data).lower():
731
- api_file_type = "File"
732
- error_logs.append(f"Format Blocked! Downgrading type to File.")
733
- await asyncio.sleep(1)
734
- continue
735
-
736
- if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
737
- if "data" in up_data and isinstance(up_data["data"], dict):
738
- final_file_id = up_data["data"].get("file_id") or up_data["data"].get("id")
739
- elif "file_id" in up_data:
740
- final_file_id = up_data.get("file_id")
741
-
742
- if final_file_id:
743
- send_payload = {
744
- "chat_id": str(chat_id),
745
- "file_id": str(final_file_id),
746
- "text": caption,
747
- "chat_keypad_type": "New",
748
- "chat_keypad": MAIN_KEYPAD_DICT
749
- }
750
- async with session.post(url_send, json=send_payload, timeout=30) as send_resp:
751
- if send_resp.status != 200:
752
- error_logs.append(f"Send HTTP {send_resp.status}")
753
- await asyncio.sleep(2)
754
- continue
755
-
756
- s_data = await send_resp.json()
757
- if s_data.get("status") == "OK": return True
758
- else: error_logs.append(f"SendFile API Error: {s_data}")
759
- else:
760
- if up_data.get("status") == "INVALID_INPUT" and api_file_type in ["Voice", "Music"]:
761
- api_file_type = "File" # دور زدن گیر دادن روبیکا
762
- error_logs.append(f"Upload API Error: {up_data}")
763
- else: error_logs.append(f"Missing upload_url in Response: {req_data}")
764
- else: error_logs.append(f"Request API Error: {req_data}")
765
- except Exception as e:
766
- error_logs.append(f"Raw HTTP Error: {str(e)[:100]}")
767
- await asyncio.sleep(2.5)
768
 
769
- return "\n".join(error_logs[-6:])
770
 
771
  # ==============================================================================
772
  # 🟢 پارت 12: توابع تغییر صدا و لیست گویندگان
@@ -801,20 +760,33 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
801
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {job_type_name})", False)
802
 
803
  async with aiohttp.ClientSession() as session:
804
- form = aiohttp.FormData()
805
- form.add_field('source_audio', src_bytes, filename='src.wav', content_type='audio/wav')
806
- form.add_field('ref_audio', ref_bytes, filename='ref.wav', content_type='audio/wav')
807
 
808
- try:
809
- async with session.post(f"{VC_BASE_URL}/upload", data=form, timeout=43200) as resp:
810
- if resp.status != 200:
811
- return await send_with_keyboard(client, chat_id, f"❌ خطا در ارسال فایل‌ها (کد {resp.status})", True)
812
- data = await resp.json()
813
- job_id = data.get("job_id")
814
- total_chunks = data.get("total_chunks", 1)
815
- chunks = data.get("chunks",[])
816
- except Exception as e:
817
- return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور:\n{str(e)[:100]}", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
818
 
819
  await send_with_keyboard(client, chat_id, "✅ فایل‌ها ارسال شد. در حال پردازش و تغییر صدا...\n(لطفا چند دقیقه صبور باشید)", False)
820
 
@@ -829,8 +801,10 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
829
  if c_data.get("status") == "completed":
830
  final_filename = c_data.get("filename")
831
  break
832
- elif c_data.get("status") in ["failed", "error"]:
833
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش صدا.", True)
 
 
834
  except Exception:
835
  pass
836
 
@@ -838,15 +812,24 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
838
  return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True)
839
 
840
  download_url = f"{VC_BASE_URL}/download/{final_filename}"
841
- await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال دانلود نتیجه...", False)
842
- try:
843
- async with session.get(download_url, timeout=43200) as d_resp:
844
- if d_resp.status == 200:
845
- result_bytes = await d_resp.read()
846
- else:
847
- return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True)
848
- except Exception:
849
- return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی.", True)
 
 
 
 
 
 
 
 
 
850
 
851
  file_name_mp3 = f"vc_standard_{uuid.uuid4().hex[:6]}.mp3"
852
  await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes)
@@ -864,7 +847,7 @@ async def process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, job_typ
864
  user_credits_db[str_chat_id][credit_type] -= 1
865
  save_db(user_credits_db)
866
  else:
867
- await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال در روبیکا فراهم نشد.", True)
868
 
869
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
870
 
@@ -875,26 +858,37 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo
875
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {model_name})", False)
876
 
877
  async with aiohttp.ClientSession() as session:
878
- form = aiohttp.FormData()
879
- form.add_field('audio_file', src_bytes, filename='input.wav', content_type='audio/wav')
880
- form.add_field('model_url', model_url)
881
- form.add_field('pitch', str(pitch))
882
- form.add_field('algo', 'rmvpe+')
883
- form.add_field('index_inf', '0.75')
884
- form.add_field('res_filter', '3')
885
- form.add_field('env_ratio', '0.25')
886
- form.add_field('protect', '0.33')
887
- form.add_field('denoise', 'false')
888
- form.add_field('reverb', 'false')
889
 
890
- try:
891
- async with session.post(f"{LEGACY_BASE_URL}/upload", data=form, timeout=43200) as resp:
892
- if resp.status != 200:
893
- return await send_with_keyboard(client, chat_id, f"❌ خطا در ارسال فایل‌ها (کد {resp.status})", True)
894
- data = await resp.json()
895
- job_id = data.get("job_id")
896
- except Exception as e:
897
- return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور:\n{str(e)[:100]}", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898
 
899
  await send_with_keyboard(client, chat_id, "✅ فایل ارسال شد. در حال پردازش و تغییر صدای شما...\n(لطفا چند دقیقه صبور باشید)", False)
900
 
@@ -910,6 +904,8 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo
910
  break
911
  elif c_data.get("status") in["failed", "error", "not_found"]:
912
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش.", True)
 
 
913
  except Exception:
914
  pass
915
 
@@ -917,15 +913,24 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo
917
  return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True)
918
 
919
  download_url = f"{LEGACY_BASE_URL}/download/{final_filename}"
920
- await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال دانلود نتیجه...", False)
921
- try:
922
- async with session.get(download_url, timeout=43200) as d_resp:
923
- if d_resp.status == 200:
924
- result_bytes = await d_resp.read()
925
- else:
926
- return await send_with_keyboard(client, chat_id, "❌ خطا در دریافت فایل نهایی از سرور.", True)
927
- except Exception:
928
- return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی.", True)
 
 
 
 
 
 
 
 
 
929
 
930
  file_name_mp3 = f"vc_legacy_{uuid.uuid4().hex[:6]}.mp3"
931
  await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes)
@@ -939,11 +944,12 @@ async def process_legacy_vc_job(client, chat_id, src_bytes, model_url, pitch, mo
939
  await asyncio.sleep(4)
940
 
941
  if upload_result is True:
942
- if not creds.get("is_premium"):
 
943
  user_credits_db[str_chat_id]["voice_conv"] -= 1
944
  save_db(user_credits_db)
945
  else:
946
- await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال در روبیکا فراهم نشد.", True)
947
 
948
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
949
 
@@ -1428,7 +1434,7 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
1428
  except Exception: traceback.print_exc()
1429
 
1430
  # ==============================================================================
1431
- # 🟢 پارت 17: سیستم ساخت پادکست
1432
  # ==============================================================================
1433
  async def process_podcast(client, chat_id, prompt):
1434
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
@@ -1446,11 +1452,23 @@ async def process_podcast(client, chat_id, prompt):
1446
  payload_create = {"prompt": prompt, "available_speakers": available_speakers}
1447
 
1448
  async with aiohttp.ClientSession() as session:
1449
- try:
1450
- async with session.post(url_create, json=payload_create, timeout=60) as resp:
1451
- if resp.status != 202: return await send_with_keyboard(client, chat_id, f"❌ خطا در سرور پادکست: وضعیت {resp.status}", True)
1452
- task_id = (await resp.json()).get("task_id")
1453
- except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور پادکست:\n{str(e)}", True)
 
 
 
 
 
 
 
 
 
 
 
 
1454
 
1455
  url_status = f"https://opera8-podgen.hf.space/api/podcast-status/{task_id}"
1456
  script_data = None
@@ -1465,6 +1483,8 @@ async def process_podcast(client, chat_id, prompt):
1465
  break
1466
  elif status_data.get("status") == "failed":
1467
  return await send_with_keyboard(client, chat_id, "❌ متأسفانه هوش مصنوعی نتوانست برای این موضوع سناریو بنویسد.", True)
 
 
1468
  except Exception: pass
1469
 
1470
  if not script_data: return await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای نوشتن سناریو به پایان رسید.", True)
@@ -1485,16 +1505,21 @@ async def process_podcast(client, chat_id, prompt):
1485
  payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1486
  chunk_audio_bytes = None
1487
 
1488
- for attempt in range(10):
 
1489
  try:
1490
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
1491
  if resp.status == 200:
1492
  chunk_audio_bytes = await resp.read()
1493
  break
 
 
 
 
1494
  except Exception: await asyncio.sleep(2)
1495
 
1496
  if not chunk_audio_bytes:
1497
- return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} از سرور. عملیات متوقف شد.", True)
1498
 
1499
  try:
1500
  combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
@@ -1848,36 +1873,838 @@ async def process_create_file(client, chat_id, topic):
1848
  await send_with_keyboard(client, chat_id, "❌ متأسفانه پس از تلاش‌های مکرر، سرور قادر به ساخت و ارسال هیچ‌یک از فایل‌ها نبود و اعتباری از شما کسر نشد.", True)
1849
 
1850
  # ==============================================================================
1851
- # 🔴 پارت 20: نسخه پاکسازی اضطراری (مخصوص تخلیه صف در دیتابیس جدید v3)
1852
  # ==============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1853
 
1854
  if not bot_token:
1855
- print("خطا: توکن وارد نشده!")
1856
  else:
1857
  bot = BotClient(bot_token)
1858
 
1859
  @bot.on_update(filters.private)
1860
  async def main_handler(client, update):
 
 
1861
  try:
1862
- # فقط استخراج آیدی پیام
 
 
 
 
 
 
 
1863
  msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
 
 
 
 
 
 
 
 
 
 
 
1864
  msg_id = getattr(update, "message_id", None)
1865
  if not msg_id and msg_obj:
1866
  msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None)
1867
 
1868
- if not msg_id: return
1869
- str_msg_id = str(msg_id).strip()
1870
-
1871
- # ثبت در دیتابیس v3 بدون فرستادن هیچ پاسخی
1872
- if not is_message_processed(str_msg_id):
 
 
 
 
 
 
 
1873
  mark_message_processed(str_msg_id)
1874
- print(f"🗑️ [V3-CLEARING] پیام {str_msg_id} ثبت شد.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1875
 
1876
- except Exception:
1877
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1878
 
1879
  if __name__ == "__main__":
1880
  threading.Thread(target=run_flask, daemon=True).start()
 
1881
  if bot_token:
1882
- print("⚠️ وضعیت: در حال تخلیه صف پیام‌ها در دیتابیس v3... لطفاً تا توقف لاگ‌ها صبر کنید.")
 
 
 
 
 
 
 
 
1883
  bot.run()
 
610
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
611
 
612
  # ==============================================================================
613
+ # 🟢 پارت 11: آپلودر دوگانه هوشمند (آپلود در بله + بکاپ خودکار در روبیکا)
614
  # ==============================================================================
615
+ BALE_BOT_TOKEN = os.environ.get("BALE_BOT_TOKEN", "").strip()
616
+
617
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
618
  abs_path = os.path.abspath(file_name)
619
+ error_logs =[]
620
 
621
+ # ==========================================
622
+ # فاز 1: تلاش برای آپلود در بله (ساخت لینک)
623
+ # ==========================================
624
+ if BALE_BOT_TOKEN:
625
+ bale_chat_id = BALE_BOT_TOKEN.split(":")[0]
626
+ for attempt in range(3): # ۳ بار تلاش در سرور بله
627
+ try:
628
+ bale_url = f"https://tapi.bale.ai/bot{BALE_BOT_TOKEN}/sendDocument"
629
+ with open(abs_path, "rb") as f:
630
+ form = aiohttp.FormData()
631
+ form.add_field('chat_id', bale_chat_id)
632
+ form.add_field('document', f, filename=os.path.basename(abs_path))
633
+
634
+ async with aiohttp.ClientSession() as session:
635
+ async with session.post(bale_url, data=form, timeout=120) as resp:
636
+ if resp.status == 200:
637
+ bale_data = await resp.json()
638
+ if bale_data.get("ok"):
639
+ try:
640
+ file_id = bale_data['result']['document']['file_id']
641
+ except KeyError:
642
+ if 'audio' in bale_data['result']: file_id = bale_data['result']['audio']['file_id']
643
+ elif 'video' in bale_data['result']: file_id = bale_data['result']['video']['file_id']
644
+ elif 'voice' in bale_data['result']: file_id = bale_data['result']['voice']['file_id']
645
+ elif 'photo' in bale_data['result']: file_id = bale_data['result']['photo'][-1]['file_id']
646
+ else:
647
+ error_logs.append("فرمت در بله شناخته نشد.")
648
+ continue
649
+
650
+ download_url = f"https://tapi.bale.ai/file/bot{BALE_BOT_TOKEN}/{file_id}"
651
+ final_text = f"""{caption}\n\n━━━━━━━━━━━━━━━━━━━\n🌐 لینک دانلود فایل شما:\n\n{download_url}\n\n━━━━━━━━━━━━━━━━━━━\n⚠️ **راهنمای دانلود:**\nلطفاً لینک بالا را کپی کرده و در **مرورگر گوشی خود** باز کنید.\n*(مستقیماً کلیک نکنید)*"""
652
+
653
+ send_res = await send_with_keyboard(client, chat_id, final_text, True)
654
+ if send_res: return True
655
+ else:
656
+ error_logs.append(f"Bale API Err: {bale_data.get('description')}")
657
+ else:
658
+ error_logs.append(f"Bale HTTP Error: {resp.status}")
659
+ if resp.status in[500, 502, 503]: await asyncio.sleep(2)
660
+ except Exception as e:
661
+ error_logs.append(f"Bale Err: {str(e)[:100]}")
662
+ await asyncio.sleep(2)
663
+
664
+ # ==========================================
665
+ # فاز 2: سیستم نجات! (آپلود مستقیم در روبیکا)
666
+ # (اگر بله قطع بود، فایل مستقیما تو روبیکا ارسال میشه)
667
+ # ==========================================
668
  api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else file_type
669
+ if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'): api_file_type = "Music"
670
+ if api_file_type == "File" and abs_path.lower().endswith('.mp3'): api_file_type = "Music"
671
 
672
+ fallback_caption = f"{caption}\n\n⚠️ (به دلیل اختلال سرورهای بله، فایل موقتاً به صورت مستقیم در روبیکا ارسال شد)"
673
+
674
+ for attempt in range(5):
 
 
 
 
 
675
  try:
676
  sent_success = False
677
 
 
678
  if api_file_type == "Image" and hasattr(client, "send_photo"):
679
+ try: await client.send_photo(chat_id, photo=abs_path, caption=fallback_caption); sent_success = True
680
+ except: await client.send_photo(chat_id, abs_path, caption=fallback_caption); sent_success = True
 
 
 
 
 
 
681
  elif api_file_type == "Voice" and hasattr(client, "send_voice"):
682
+ try: await client.send_voice(chat_id, abs_path, caption=fallback_caption); sent_success = True
683
+ except: await client.send_voice(chat_id, file=abs_path, caption=fallback_caption); sent_success = True
 
 
 
 
 
 
684
  elif api_file_type == "Music" and hasattr(client, "send_music"):
685
+ try: await client.send_music(chat_id, abs_path, caption=fallback_caption); sent_success = True
686
+ except: await client.send_music(chat_id, music=abs_path, caption=fallback_caption); sent_success = True
687
+
 
 
 
 
 
 
688
  if not sent_success:
689
  if hasattr(client, "send_document"):
690
+ try: await client.send_document(chat_id, document=abs_path, caption=fallback_caption); sent_success = True
691
+ except: await client.send_document(chat_id, abs_path, caption=fallback_caption); sent_success = True
 
 
 
 
692
  elif hasattr(client, "send_file"):
693
+ try: await client.send_file(chat_id, file=abs_path)
694
+ except: await client.send_file(chat_id, abs_path)
695
+ if fallback_caption: await send_with_keyboard(client, chat_id, fallback_caption, True)
 
 
 
696
  sent_success = True
697
+
698
  if sent_success:
699
  return True
700
+
701
  except Exception as e:
702
+ error_logs.append(f"Rubpy Send Err: {str(e)[:100]}")
703
+ await asyncio.sleep(3)
 
 
 
 
 
704
 
705
+ return "\n".join(error_logs[-5:])
706
+
707
+ # ==============================================================================
708
+ # 🟢 پارت 12: توابع تغییر صدا و لیست گویندگان
709
+ # ==============================================================================
710
+ # ==================================================================
711
+ # لیست‌های اولیه ربات
712
+ # ==================================================================
713
+ WORKER_URLS =["https://opera8-ttspro.hf.space/generate"]
714
+
715
+ SPEAKERS = {
716
+ "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
717
+ "4": ("آرمان (مرد)", "Zubenelgenubi"), "5": ("مهسا (زن)", "Vindemiatrix"), "6": ("دانا (مرد)", "Rasalgethi"),
718
+ "7": ("سامان (مرد)", "Sadachbia"), "8": ("آرش (مرد)", "Sadaltager"), "9": ("شبنم (زن)", "Sulafat"),
719
+ "10": ("سحر (زن)", "Laomedeia"), "11": ("مریم (زن)", "Achernar"), "12": ("بهرام (مرد)", "Alnilam"),
720
+ "13": ("نیکان (مرد)", "Schedar"), "14": ("فرناز (زن)", "Gacrux"), "15": ("سارا (زن)", "Pulcherrima"),
721
+ "16": ("مانی (مرد)", "Umbriel"), "17": ("آرتین (مرد)", "Algieba"), "18": ("دلنواز (زن)", "Despina"),
722
+ "19": ("روژان (زن)", "Erinome"), "20": ("امید (مرد)", "Algenib"), "21": ("بردیا (مرد)", "Orus"),
723
+ "22": ("ترانه (زن)", "Aoede"), "23": ("نیکو (زن)", "Callirrhoe"), "24": ("هستی (زن)", "Autonoe"),
724
+ "25": ("کامیار (مرد)", "Enceladus"), "26": ("کیانوش (مرد)", "Iapetus"), "27": ("پویا (مرد)", "Puck"),
725
+ "28": ("مهتاب (زن)", "Kore"), "29": ("سام (مرد)", "Fenrir"), "30": ("لیدا (زن)", "Leda")
726
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
727
 
728
+ user_states = {}
729
 
730
  # ==============================================================================
731
  # 🟢 پارت 12: توابع تغییر صدا و لیست گویندگان
 
760
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {job_type_name})", False)
761
 
762
  async with aiohttp.ClientSession() as session:
763
+ job_id = None
764
+ total_chunks = 1
765
+ chunks =[]
766
 
767
+ # ♻️ سیستم تلاش مجدد پنهان (دور زدن ارور 429)
768
+ for attempt in range(8):
769
+ form = aiohttp.FormData()
770
+ form.add_field('source_audio', src_bytes, filename='src.wav', content_type='audio/wav')
771
+ form.add_field('ref_audio', ref_bytes, filename='ref.wav', content_type='audio/wav')
772
+
773
+ try:
774
+ async with session.post(f"{VC_BASE_URL}/upload", data=form, timeout=43200) as resp:
775
+ if resp.status == 200:
776
+ data = await resp.json()
777
+ job_id = data.get("job_id")
778
+ total_chunks = data.get("total_chunks", 1)
779
+ chunks = data.get("chunks",[])
780
+ break
781
+ elif resp.status == 429:
782
+ await asyncio.sleep(4 + attempt * 2) # تاخیر تصاعدی تا سرور خلوت شود
783
+ else:
784
+ await asyncio.sleep(3)
785
+ except Exception as e:
786
+ await asyncio.sleep(3)
787
+
788
+ if not job_id:
789
+ return await send_with_keyboard(client, chat_id, "❌ سرور پردازش صدا به دلیل ترافیک بالا پاسخگو نیست (خطای 429). لطفاً چند دقیقه دیگر امتحان کنید.", True)
790
 
791
  await send_with_keyboard(client, chat_id, "✅ فایل‌ها ارسال شد. در حال پردازش و تغییر صدا...\n(لطفا چند دقیقه صبور باشید)", False)
792
 
 
801
  if c_data.get("status") == "completed":
802
  final_filename = c_data.get("filename")
803
  break
804
+ elif c_data.get("status") in["failed", "error"]:
805
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش صدا.", True)
806
+ elif c_resp.status == 429:
807
+ await asyncio.sleep(5)
808
  except Exception:
809
  pass
810
 
 
812
  return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True)
813
 
814
  download_url = f"{VC_BASE_URL}/download/{final_filename}"
815
+ await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال آماده‌سازی لینک دانلود...", False)
816
+
817
+ result_bytes = None
818
+ for attempt in range(5):
819
+ try:
820
+ async with session.get(download_url, timeout=43200) as d_resp:
821
+ if d_resp.status == 200:
822
+ result_bytes = await d_resp.read()
823
+ break
824
+ elif d_resp.status == 429:
825
+ await asyncio.sleep(4 + attempt * 2)
826
+ else:
827
+ await asyncio.sleep(3)
828
+ except Exception:
829
+ await asyncio.sleep(3)
830
+
831
+ if not result_bytes:
832
+ return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی از سرور اصلی.", True)
833
 
834
  file_name_mp3 = f"vc_standard_{uuid.uuid4().hex[:6]}.mp3"
835
  await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes)
 
847
  user_credits_db[str_chat_id][credit_type] -= 1
848
  save_db(user_credits_db)
849
  else:
850
+ await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال فراهم نشد.", True)
851
 
852
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
853
 
 
858
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال آماده‌سازی فایل‌ها...\n(مدل: {model_name})", False)
859
 
860
  async with aiohttp.ClientSession() as session:
861
+ job_id = None
 
 
 
 
 
 
 
 
 
 
862
 
863
+ # ♻️ سیستم تلاش مجدد پنهان (دور زدن ارور 429)
864
+ for attempt in range(8):
865
+ form = aiohttp.FormData()
866
+ form.add_field('audio_file', src_bytes, filename='input.wav', content_type='audio/wav')
867
+ form.add_field('model_url', model_url)
868
+ form.add_field('pitch', str(pitch))
869
+ form.add_field('algo', 'rmvpe+')
870
+ form.add_field('index_inf', '0.75')
871
+ form.add_field('res_filter', '3')
872
+ form.add_field('env_ratio', '0.25')
873
+ form.add_field('protect', '0.33')
874
+ form.add_field('denoise', 'false')
875
+ form.add_field('reverb', 'false')
876
+
877
+ try:
878
+ async with session.post(f"{LEGACY_BASE_URL}/upload", data=form, timeout=43200) as resp:
879
+ if resp.status == 200:
880
+ data = await resp.json()
881
+ job_id = data.get("job_id")
882
+ break
883
+ elif resp.status == 429:
884
+ await asyncio.sleep(4 + attempt * 2)
885
+ else:
886
+ await asyncio.sleep(3)
887
+ except Exception as e:
888
+ await asyncio.sleep(3)
889
+
890
+ if not job_id:
891
+ return await send_with_keyboard(client, chat_id, "❌ سرور پردازش صدا به دلیل ترافیک بالا پاسخگو نیست (خطای 429). لطفاً چند دقیقه دیگر امتحان کنید.", True)
892
 
893
  await send_with_keyboard(client, chat_id, "✅ فایل ارسال شد. در حال پردازش و تغییر صدای شما...\n(لطفا چند دقیقه صبور باشید)", False)
894
 
 
904
  break
905
  elif c_data.get("status") in["failed", "error", "not_found"]:
906
  return await send_with_keyboard(client, chat_id, "❌ خطای سرور در حین پردازش.", True)
907
+ elif c_resp.status == 429:
908
+ await asyncio.sleep(5)
909
  except Exception:
910
  pass
911
 
 
913
  return await send_with_keyboard(client, chat_id, "❌ پردازش بیش از حد طول کشید و متوقف شد.", True)
914
 
915
  download_url = f"{LEGACY_BASE_URL}/download/{final_filename}"
916
+ await send_with_keyboard(client, chat_id, "📥 پردازش تمام شد! در حال آماده‌سازی لینک دانلود...", False)
917
+
918
+ result_bytes = None
919
+ for attempt in range(5):
920
+ try:
921
+ async with session.get(download_url, timeout=43200) as d_resp:
922
+ if d_resp.status == 200:
923
+ result_bytes = await d_resp.read()
924
+ break
925
+ elif d_resp.status == 429:
926
+ await asyncio.sleep(4 + attempt * 2)
927
+ else:
928
+ await asyncio.sleep(3)
929
+ except Exception:
930
+ await asyncio.sleep(3)
931
+
932
+ if not result_bytes:
933
+ return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال هنگام دانلود خروجی از سرور.", True)
934
 
935
  file_name_mp3 = f"vc_legacy_{uuid.uuid4().hex[:6]}.mp3"
936
  await asyncio.to_thread(sync_write_file, file_name_mp3, result_bytes)
 
944
  await asyncio.sleep(4)
945
 
946
  if upload_result is True:
947
+ # اگر مدل باب اسفنجی نبود، از اعتبار کسر کن
948
+ if not creds.get("is_premium") and model_name != "باب اسفنجی":
949
  user_credits_db[str_chat_id]["voice_conv"] -= 1
950
  save_db(user_credits_db)
951
  else:
952
+ await send_with_keyboard(client, chat_id, "❌ فایل پردازش شد اما امکان ارسال لینک فراهم نشد.", True)
953
 
954
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
955
 
 
1434
  except Exception: traceback.print_exc()
1435
 
1436
  # ==============================================================================
1437
+ # 🟢 پارت 17: سیستم ساخت پادکست (ضد قطعی و مقاوم در برابر 429)
1438
  # ==============================================================================
1439
  async def process_podcast(client, chat_id, prompt):
1440
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
 
1452
  payload_create = {"prompt": prompt, "available_speakers": available_speakers}
1453
 
1454
  async with aiohttp.ClientSession() as session:
1455
+ task_id = None
1456
+ # ♻️ تلاش مجدد برای جلوگیری از شکست در صورت شلوغی سرور
1457
+ for attempt in range(8):
1458
+ try:
1459
+ async with session.post(url_create, json=payload_create, timeout=60) as resp:
1460
+ if resp.status == 202:
1461
+ task_id = (await resp.json()).get("task_id")
1462
+ break
1463
+ elif resp.status == 429:
1464
+ await asyncio.sleep(4 + attempt * 2)
1465
+ else:
1466
+ await asyncio.sleep(3)
1467
+ except Exception as e:
1468
+ await asyncio.sleep(3)
1469
+
1470
+ if not task_id:
1471
+ return await send_with_keyboard(client, chat_id, "❌ ترافیک سرور پادکست بالا است (خطای 429). لطفاً چند دقیقه دیگر امتحان کنید.", True)
1472
 
1473
  url_status = f"https://opera8-podgen.hf.space/api/podcast-status/{task_id}"
1474
  script_data = None
 
1483
  break
1484
  elif status_data.get("status") == "failed":
1485
  return await send_with_keyboard(client, chat_id, "❌ متأسفانه هوش مصنوعی نتوانست برای این موضوع سناریو بنویسد.", True)
1486
+ elif resp.status == 429:
1487
+ await asyncio.sleep(5)
1488
  except Exception: pass
1489
 
1490
  if not script_data: return await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای نوشتن سناریو به پایان رسید.", True)
 
1505
  payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1506
  chunk_audio_bytes = None
1507
 
1508
+ # ♻️ حلقه ضد قطعی برای ریکورد تک تک دیالوگ ها
1509
+ for attempt in range(15):
1510
  try:
1511
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
1512
  if resp.status == 200:
1513
  chunk_audio_bytes = await resp.read()
1514
  break
1515
+ elif resp.status == 429:
1516
+ await asyncio.sleep(4 + attempt * 2)
1517
+ else:
1518
+ await asyncio.sleep(2)
1519
  except Exception: await asyncio.sleep(2)
1520
 
1521
  if not chunk_audio_bytes:
1522
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} به دلیل شلوغی سرور. عملیات متوقف شد.", True)
1523
 
1524
  try:
1525
  combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
 
1873
  await send_with_keyboard(client, chat_id, "❌ متأسفانه پس از تلاش‌های مکرر، سرور قادر به ساخت و ارسال هیچ‌یک از فایل‌ها نبود و اعتباری از شما کسر نشد.", True)
1874
 
1875
  # ==============================================================================
1876
+ # 🟢 پارت 20: بدنه اصلی ربات (Handler)، دستورات مدیریتی، حلقه اصلی و استارت
1877
  # ==============================================================================
1878
+ # ==================================================================
1879
+ # حلقه و مدیریت اصلی ربات
1880
+ # ==================================================================
1881
+
1882
+ # 🧹 سیستم پاکسازی هوشمند رم (Garbage Collector) مخصوص هزاران کاربر
1883
+ async def ram_garbage_collector():
1884
+ """این تابع در پس‌زمینه می‌چرخد و فایل‌های رها شده در حافظه را پاک می‌کند تا رم سرور پر نشود"""
1885
+ while True:
1886
+ await asyncio.sleep(1800) # هر ۳۰ دقیقه یکبار بررسی می‌��ند
1887
+ now = time.time()
1888
+ try:
1889
+ for uid in list(user_states.keys()):
1890
+ # اگر کاربر بیش از ۱ ساعت (۳۶۰۰ ثانیه) با ربات کار نکرده بود
1891
+ if now - user_states[uid].get("last_time", 0) > 3600:
1892
+ # پاک کردن بایت‌های فایل‌های سنگین از رم سرور
1893
+ if user_states[uid].get("file_bytes") is not None:
1894
+ user_states[uid]["file_bytes"] = None
1895
+ if user_states[uid].get("ref_bytes") is not None:
1896
+ user_states[uid]["ref_bytes"] = None
1897
+ # خالی کردن تاریخچه چت برای آزادسازی رم (نگه داشتن ۵ پیام آخر)
1898
+ if len(user_states[uid].get("history",[])) > 5:
1899
+ user_states[uid]["history"] = user_states[uid]["history"][-5:]
1900
+ except Exception as e:
1901
+ pass
1902
 
1903
  if not bot_token:
1904
+ print("خطا: توکن ربات روبیکا وارد نشده است!")
1905
  else:
1906
  bot = BotClient(bot_token)
1907
 
1908
  @bot.on_update(filters.private)
1909
  async def main_handler(client, update):
1910
+ global BOT_GUID
1911
+
1912
  try:
1913
+ if not BOT_GUID:
1914
+ try:
1915
+ me_info = await client.get_me()
1916
+ if me_info and hasattr(me_info, 'user'):
1917
+ BOT_GUID = getattr(me_info.user, 'user_guid', None)
1918
+ except Exception:
1919
+ pass
1920
+
1921
  msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
1922
+
1923
+ author_id = getattr(update, 'author_guid', None)
1924
+ if not author_id and msg_obj:
1925
+ author_id = msg_obj.get('author_object_guid') if isinstance(msg_obj, dict) else getattr(msg_obj, 'author_object_guid', None)
1926
+
1927
+ if BOT_GUID and author_id == BOT_GUID:
1928
+ return
1929
+
1930
+ chat_id = getattr(update, 'object_guid', None) or getattr(update, 'author_guid', None) or getattr(update, "chat_id", None)
1931
+ if not chat_id: return
1932
+
1933
  msg_id = getattr(update, "message_id", None)
1934
  if not msg_id and msg_obj:
1935
  msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None)
1936
 
1937
+ str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
1938
+ creds = get_user_credits(str_chat_id)
1939
+
1940
+ # ===================================================================
1941
+ # 🛑 سیستم صد در صد قطعی ضد تکرار پیام با استفاده از دیتابیس ابری
1942
+ # ===================================================================
1943
+ if msg_id:
1944
+ str_msg_id = str(msg_id).strip()
1945
+ # 1. اگر پیام قبلاً در دیتابیس ثبت شده بود، ربات بی‌درنگ کار را متوقف می‌کند
1946
+ if is_message_processed(str_msg_id):
1947
+ return
1948
+ # 2. اگر پیام جدید بود، در همان لحظه در دیتابیس ذخیره می‌شود تا دیگر هرگز خوانده نشود
1949
  mark_message_processed(str_msg_id)
1950
+ # ===================================================================
1951
+
1952
+ user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
1953
+ user_text_str = str(user_text).strip() if user_text else ""
1954
+ user_text_lower = user_text_str.lower()
1955
+
1956
+ if is_user_spamming(str_chat_id):
1957
+ return
1958
+
1959
+ # ===================================================================
1960
+ # 🟢 ثبت آخرین زمان فعالیت کاربر برای سیستم پاکسازی رم
1961
+ # ===================================================================
1962
+ if str_chat_id not in user_states:
1963
+ user_states[str_chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None, "last_time": time.time()}
1964
+ else:
1965
+ user_states[str_chat_id]["last_time"] = time.time() # آپدیت زمان فعالیت
1966
+
1967
+ if user_text_lower.startswith(f"{ADMIN_CODE} pro=") or user_text_lower.startswith(f"{ADMIN_CODE}pro="):
1968
+ parts = user_text_str.split("=", 1)
1969
+ if len(parts) >= 2:
1970
+ target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
1971
+ if target_id:
1972
+ if target_id not in user_credits_db or not isinstance(user_credits_db[target_id], dict):
1973
+ user_credits_db[target_id] = {
1974
+ "is_premium": False,
1975
+ "expire_date": None,
1976
+ "last_reset": "",
1977
+ "chat": 10,
1978
+ "image": 5,
1979
+ "edit_image": 1,
1980
+ "podcast": 2,
1981
+ "tts": 5,
1982
+ "file": 1,
1983
+ "stt": 5,
1984
+ "voice_conv": 3,
1985
+ "voice_clone": 1,
1986
+ "last_msg_id": 0,
1987
+ "has_joined": True,
1988
+ "invited_count": 0,
1989
+ "used_referral": False,
1990
+ "referral_code": ""
1991
+ }
1992
+
1993
+ user_credits_db[target_id]["is_premium"] = True
1994
+ expire_time = datetime.datetime.now() + datetime.timedelta(days=30)
1995
+ user_credits_db[target_id]["expire_date"] = expire_time.isoformat()
1996
+
1997
+ user_credits_db[target_id]["chat"] = 999999
1998
+ user_credits_db[target_id]["image"] = 20
1999
+ user_credits_db[target_id]["edit_image"] = 10
2000
+ user_credits_db[target_id]["podcast"] = 999999
2001
+ user_credits_db[target_id]["tts"] = 999999
2002
+ user_credits_db[target_id]["file"] = 999999
2003
+ user_credits_db[target_id]["stt"] = 999999
2004
+ user_credits_db[target_id]["voice_conv"] = 999999
2005
+ user_credits_db[target_id]["voice_clone"] = 999999
2006
+
2007
+ save_db(user_credits_db)
2008
+ await send_with_keyboard(client, chat_id, f"✅ حساب کاربر `{target_id}` به مدت ۳۰ روز شارژ شد و به پرو ارتقا یافت.", False)
2009
+ try:
2010
+ await send_with_keyboard(client, target_id, "🎉 **کاربر گرامی، تبریک!**\n\nحساب شما با موفقیت توسط پشتیبانی به **🌟 نسخه پرو (ویژه)** ارتقا یافت.\nهم‌اکنون بسته‌های نامحدود و طلایی یک‌ماهه شما فعال گردید.\n\nجهت مشاهده جزئیات روی دکمه «حساب کاربری 👤» کلیک کنید.", True)
2011
+ except Exception: pass
2012
+ return
2013
+
2014
+ if user_text_lower.startswith(f"{ADMIN_CODE} free=") or user_text_lower.startswith(f"{ADMIN_CODE}free="):
2015
+ parts = user_text_str.split("=", 1)
2016
+ if len(parts) >= 2:
2017
+ target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
2018
+ if target_id:
2019
+ if target_id in user_credits_db and isinstance(user_credits_db[target_id], dict):
2020
+ user_credits_db[target_id]["is_premium"] = False
2021
+ user_credits_db[target_id]["expire_date"] = None
2022
+ user_credits_db[target_id]["last_reset"] = ""
2023
+ save_db(user_credits_db)
2024
+ await send_with_keyboard(client, chat_id, f"✅ اشتراک کاربر `{target_id}` لغو شد و به رایگان تبدیل گشت.", False)
2025
+ try:
2026
+ await send_with_keyboard(client, target_id, "⚠️ کاربر گرامی، اشتراک ویژه شما به پایان رسید و حساب شما به نسخه رایگان (آزمایشی) تغییر یافت.", True)
2027
+ except Exception: pass
2028
+ return
2029
+
2030
+ if user_text_lower.startswith(f"{ADMIN_CODE} =") or user_text_lower.startswith(f"{ADMIN_CODE}="):
2031
+ parts = user_text_str.split("=", 1)
2032
+ if len(parts) >= 2:
2033
+ target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
2034
+ if target_id:
2035
+ if target_id in user_credits_db and isinstance(user_credits_db[target_id], dict):
2036
+ t_creds = user_credits_db[target_id]
2037
+ is_prem = t_creds.get("is_premium", False)
2038
+
2039
+ if is_prem:
2040
+ expire_txt = "نامشخص"
2041
+ days_left = 0
2042
+ if t_creds.get("expire_date"):
2043
+ exp_d = datetime.datetime.fromisoformat(t_creds["expire_date"])
2044
+ now = datetime.datetime.now()
2045
+ diff = exp_d - now
2046
+ days_left = diff.days if diff.days >= 0 else 0
2047
+
2048
+ jy, jm, jd = gregorian_to_jalali(exp_d.year, exp_d.month, exp_d.day)
2049
+ expire_txt = f"{jy:04d}/{jm:02d}/{jd:02d}"
2050
+ status_text = f"🌟 نسخه پرو (ویژه)\n📅 انقضا: {expire_txt}\n⏳ باقیمانده: {days_left} روز"
2051
+ else:
2052
+ status_text = "🥉 نسخه رایگان (آزمایشی)\n⏳ وضعیت: سهمیه روزانه محدود"
2053
+
2054
+ chat_rem = "نامحدود ∞" if is_prem else t_creds.get('chat', 0)
2055
+ vc_rem = "نامحدود ∞" if is_prem else t_creds.get('voice_conv', 0)
2056
+ clone_rem = "نامحدود ∞" if is_prem else t_creds.get('voice_clone', 0)
2057
+ podcast_rem = "نامحدود ∞" if is_prem else t_creds.get('podcast', 0)
2058
+ tts_rem = "نامحدود ∞" if is_prem else t_creds.get('tts', 0)
2059
+ file_rem = "نامحدود ∞" if is_prem else t_creds.get('file', 0)
2060
+ stt_rem = "نامحدود ∞" if is_prem else t_creds.get('stt', 0)
2061
+ image_rem = t_creds.get('image', 0)
2062
+ edit_image_rem = t_creds.get('edit_image', 0)
2063
+ invs = t_creds.get('invited_count', 0)
2064
+ joined_status = "بله ✅" if t_creds.get('has_joined') else "خیر ❌"
2065
+
2066
+ info_msg = f"""🔍 **اطلاعات اختصاصی کاربر:**
2067
+ 👤 شناسه: `{target_id}`
2068
+ عضو کانال: {joined_status}
2069
+
2070
+ 🔹 **وضعیت اشتراک:**
2071
+ {status_text}
2072
+ 🎁 **تعداد افراد دعوت شده:** {invs} نفر
2073
+
2074
+ 📊 **سهمیه باقی‌مانده فعلی:**
2075
+ 💬 چت: {chat_rem}
2076
+ 🎨 تولید عکس: {image_rem}
2077
+ 🪄 ویرایش عکس: {edit_image_rem}
2078
+ 🎙️ تغییر صدا: {vc_rem}
2079
+ 👤 کلون کردن صدا: {clone_rem}
2080
+ 🎙 پادکست: {podcast_rem}
2081
+ 🗣 متن به صدا: {tts_rem}
2082
+ 📁 تحلیل فایل: {file_rem}
2083
+ 📝 صدا به متن: {stt_rem}"""
2084
+ await send_with_keyboard(client, chat_id, info_msg, False)
2085
+ else:
2086
+ await send_with_keyboard(client, chat_id, f"❌ کاربری با شناسه `{target_id}` در دیتابیس ربات یافت نشد.", False)
2087
+ return
2088
+
2089
+ is_file = False
2090
+ file_name = f"unknown_file_{uuid.uuid4().hex[:6]}.jpg"
2091
 
2092
+ if msg_obj:
2093
+ for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
2094
+ file_attr = getattr(msg_obj, attr, None)
2095
+ if file_attr:
2096
+ is_file = True
2097
+ file_name = getattr(file_attr, 'file_name', file_name)
2098
+ break
2099
+
2100
+ if not is_file and hasattr(msg_obj, 'to_dict'):
2101
+ msg_dict = msg_obj.to_dict()
2102
+ for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
2103
+ if attr in msg_dict:
2104
+ is_file = True
2105
+ file_attr = msg_dict.get(attr, {})
2106
+ file_name = file_attr.get('file_name', file_name) if isinstance(file_attr, dict) else file_name
2107
+ break
2108
+
2109
+ if user_text_str == "✅ عضو شدم":
2110
+ if not creds.get("has_joined", False):
2111
+ user_credits_db[str_chat_id]["has_joined"] = True
2112
+ save_db(user_credits_db)
2113
+ await send_with_keyboard(client, chat_id, "🎉 **عضویت شما با موفقیت تایید شد! خیلی خوش آمدید.**\n\nحالا می‌توانید از تمامی امکانات ربات استفاده کنید.\nلطفاً یکی از گزینه‌های منو را انتخاب کنید:", True)
2114
+ return
2115
+ else:
2116
+ await send_with_keyboard(client, chat_id, "✅ شما قبلاً عضو شده‌اید! لطفا از منوی اصلی استفاده کنید.", True)
2117
+ return
2118
+
2119
+ if not creds.get("has_joined", False):
2120
+ join_msg = "👋 **سلام کاربر گرامی!**\n\nجهت استفاده از خدمات ربات هوش مصنوعی آلفا و مطلع شدن از آخرین آپدیت‌ها، لطفا ابتدا در کانال رسمی ما عضو شوید:\n\n📢 **آیدی کانال:** @aialpha\n\n👇 پس از عضویت در کانال، بر روی دکمه **«✅ عضو شدم»** کلیک کنید تا ربات برای شما فعال شود."
2121
+ try:
2122
+ payload = {"chat_id": chat_id, "text": join_msg, "chat_keypad_type": "New", "chat_keypad": JOIN_KEYPAD_DICT}
2123
+ await client._make_request("sendMessage", payload)
2124
+ except Exception:
2125
+ await client.send_message(chat_id, join_msg)
2126
+ return
2127
+
2128
+ if user_text_lower.startswith("/start"):
2129
+ user_states[str_chat_id]["mode"] = None
2130
+ user_states[str_chat_id]["file_bytes"] = None
2131
+ await send_with_keyboard(client, chat_id, "سلام! به ربات هوش مصنوعی آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True)
2132
+ return
2133
+
2134
+ if user_text_str in["سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
2135
+ user_states[str_chat_id]["mode"] = None
2136
+ user_states[str_chat_id]["file_bytes"] = None
2137
+ await send_with_keyboard(client, chat_id, "سلام! به ربات هوش مصنوعی آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True)
2138
+ return
2139
+
2140
+ if user_text_str in["/account", "حساب کاربری 👤"]:
2141
+ is_prem = creds.get("is_premium", False)
2142
+
2143
+ if is_prem:
2144
+ expire_txt = "نامشخص"
2145
+ days_left = 0
2146
+ if creds.get("expire_date"):
2147
+ exp_d = datetime.datetime.fromisoformat(creds["expire_date"])
2148
+ now = datetime.datetime.now()
2149
+ diff = exp_d - now
2150
+ days_left = diff.days if diff.days >= 0 else 0
2151
+
2152
+ jy, jm, jd = gregorian_to_jalali(exp_d.year, exp_d.month, exp_d.day)
2153
+ expire_txt = f"{jy:04d}/{jm:02d}/{jd:02d} (ساعت {exp_d.hour:02d}:{exp_d.minute:02d})"
2154
+
2155
+ status_text = "🌟 نسخه پرو (ویژه)"
2156
+ expire_info = f"\n📅 **تاریخ انقضا:** {expire_txt}\n⏳ **زمان باقیمانده:** {days_left} روز"
2157
+ daily_note = "*نکته: سهمیه پردازشی شما مختص همین دوره یک‌ماهه می‌باشد.*"
2158
+ else:
2159
+ status_text = "🥉 نسخه رایگان (آزمایشی)"
2160
+ expire_info = ""
2161
+ daily_note = "*نکته: سهمیه شما هر روز ساعت ۰۰:۰۰ بامداد به صورت خودکار مجدداً شارژ می‌گردد.*"
2162
+
2163
+ chat_rem = "نامحدود ∞" if is_prem else creds['chat']
2164
+ vc_rem = "نامحدود ∞" if is_prem else creds.get('voice_conv', 0)
2165
+ clone_rem = "نامحدود ∞" if is_prem else creds.get('voice_clone', 0)
2166
+ podcast_rem = "نامحدود ∞" if is_prem else creds['podcast']
2167
+ tts_rem = "نامحدود ∞" if is_prem else creds['tts']
2168
+ file_rem = "نامحدود ∞" if is_prem else creds['file']
2169
+ stt_rem = "نامحدود ∞" if is_prem else creds['stt']
2170
+ image_rem = creds['image']
2171
+ edit_image_rem = creds['edit_image']
2172
+ invited_count = creds.get('invited_count', 0)
2173
+ my_code = get_or_create_referral_code(str_chat_id)
2174
+
2175
+ account_profile = f"""👤 **اطلاعات حساب کاربری شما**
2176
+
2177
+ 🔹 **شناسه یکتا:** `{chat_id}`
2178
+ 🎫 **کد هدیه شما:** `{my_code}`
2179
+ 🔹 **وضعیت اشتراک:** {status_text}{expire_info}
2180
+ 🎁 **تعداد افراد دعوت شده:** {invited_count} نفر
2181
+
2182
+ 📊 **سهمیه باقی‌مانده شما:**
2183
+ - 💬 چت هوشمند: {chat_rem}
2184
+ - 🎨 تولید تصویر: {image_rem} عدد
2185
+ - 🪄 ویرایش تصویر: {edit_image_rem} عدد
2186
+ - 🎙️ تغییر صدا: {vc_rem}
2187
+ - 👤 کلون کردن صدا: {clone_rem}
2188
+ - 🎙 ساخت پادکست: {podcast_rem}
2189
+ - 🗣 تبدیل متن به صدا: {tts_rem}
2190
+ - 📁 تحلیل فایل و سند: {file_rem}
2191
+ - 📝 تبدیل صدا به متن: {stt_rem}
2192
+
2193
+ {daily_note}"""
2194
+ await send_with_keyboard(client, chat_id, account_profile, True)
2195
+ return
2196
+
2197
+ if user_text_str in["/invite", "دعوت دوستان 🎁"]:
2198
+ invites = creds.get("invited_count", 0)
2199
+ remains = 10 - (invites % 10)
2200
+ my_code = get_or_create_referral_code(str_chat_id)
2201
+
2202
+ invite_text = f"""🎁 **سیستم دعوت دوستان (دو سر سود)**
2203
+
2204
+ با دعوت دوستان خود به ربات آلفا، هم شما و هم دوستتان هدیه می‌گیرید!
2205
+ به دوست خود بگویید پس از ورود به ربات، دکمه **«ثبت کد هدیه 🎫»** را بزند و کد زیر را وارد کند.
2206
+ ✨ **سود دوست شما:** در همان لحظه 10 تبدیل رایگان متن به صدا دریافت می‌کند.
2207
+ ✨ **سود شما:** به آمار دعوت‌هایتان اضافه می‌شود و به ازای هر **10 نفر**، **3 روز اشتراک پرو نامحدود** می‌گیرید.
2208
+
2209
+ 📊 **آمار شما:**
2210
+ - تعداد دعوت‌های موفق: {invites} نفر
2211
+ - دعوت‌های باقی‌مانده تا جایزه بعدی: {remains} نفر
2212
+
2213
+ کد هدیه اختصاصی شما:
2214
+ `{my_code}`
2215
+
2216
+ (متن زیر را کپی کرده و برای دوستانتان بفرستید 👇)"""
2217
+ await send_with_keyboard(client, chat_id, invite_text, True)
2218
+
2219
+ forward_text = f"""🤖 **ربات هوش مصنوعی آلفا پرو**
2220
+
2221
+ ✨ با این ربات می‌تونی کارهای زیر رو به راحتی انجام بدی:
2222
+ 💬 چت با پیشرفته‌ترین هوش مصنوعی
2223
+ 🎨 ساخت و ویرایش حرفه‌ای عکس
2224
+ 🎙️ تغییر صدا و کلون کردن صدا
2225
+ 🎙 ساخت پادکست اختصاصی
2226
+ 🗣 تبدیل متن به صدا (30 گوینده مختلف)
2227
+ 📝 تبدیل صدا و ویدیو به متن
2228
+ 📁 تحلیل فایل‌ها و ساخت مقاله
2229
+
2230
+ 👇 اول وارد ربات زیر شو:
2231
+ @aialphabot
2232
+
2233
+ سپس دکمه **«ثبت کد هدیه 🎫»** را بزن و کد 8 رقمی زیر رو وارد کن تا همون اول **10 تا تبدیل صدا هدیه بگیری**:
2234
+ `{my_code}`"""
2235
+ await send_with_keyboard(client, chat_id, forward_text, False)
2236
+ return
2237
+
2238
+ if user_text_str in["/referral", "ثبت کد هدیه 🎫"]:
2239
+ if creds.get("used_referral", False):
2240
+ await send_with_keyboard(client, chat_id, "❌ شما قبلاً کد هدیه یک نفر را ثبت کرده‌اید و فقط یک‌بار مجاز به استفاده از این امکان هستید.", True)
2241
+ return
2242
+ user_states[str_chat_id]["mode"] = "waiting_for_referral_code"
2243
+ msg = "🎫 **ثبت کد هدیه**\n\nکد هدیه 8 رقمی (اعداد) که از دوست خود دریافت کرده‌اید را اینجا وارد کنید تا در همان لحظه **10 سهمیه تبدیل رایگان متن به صدا** هدیه بگیرید!\n\n(برای انصراف دکمه «برگشت♻️» را بزنید)"
2244
+ await send_with_keyboard(client, chat_id, msg, True)
2245
+ return
2246
+
2247
+ if user_text_str in["/buy", "خرید اشتراک 💎"]:
2248
+ buy_text = f"""💎 **خرید اشتراک ویژه آلفا پرو (یک ماهه)**
2249
+
2250
+ با تهیه اشتراک ویژه، محدودیت‌ها را کنار بزنید و از نهایت قدرت هوش مصنوعی لذت ببرید! 🚀
2251
+
2252
+ ━━━━━━━━━━━━━━━━━━━
2253
+ 🎁 **بسته طلایی یک‌ماهه شامل:**
2254
+ 🤖 چت با هوش مصنوعی: نامحدود ∞
2255
+ 🎙️ تغییر صدا و کلون کردن صدا: نامحدود ∞
2256
+ 🗣 تبدیل متن به صدا (۳۰ گوینده): نامحدود ∞
2257
+ 🎙 ساخت پادکست: نامحدود ∞
2258
+ 📁 تحلیل فایل و سند: نامحدود ∞
2259
+ 📝 تبدیل فایل صوتی به متن: نامحدود ∞
2260
+ 🪄 ویرایش تصویر: ۱۰ عدد
2261
+ 🎨 تولید تصویر: ۲۰ عدد
2262
+ ━━━━━━━━━━━━━━━━━━━
2263
+
2264
+ 💳 **هزینه اشتراک یک ماهه:** 250 هزار تومان
2265
+
2266
+ 💳 **شماره کارت جهت واریز:**
2267
+ ➖➖➖➖➖➖➖➖
2268
+ `6219861411958035`
2269
+ ➖➖➖➖➖➖➖➖
2270
+ 👤 **به نام:** کوهی
2271
+
2272
+ ✅ **نحوه فعال‌سازی:**
2273
+ پس از واریز مبلغ، لطفاً رسید پرداختی را به همراه **شناسه یکتای خود** (که در پایین آمده) به آیدی پشتیبانی زیر ارسال کنید تا اشتراک شما فعال گردد:
2274
+
2275
+ 🔑 **شناسه یکتای شما:**
2276
+ `{chat_id}`
2277
+
2278
+ 👨‍💻 **ارتباط با پشتیبانی:**
2279
+ 🆔 @aialpha_admin"""
2280
+ await send_with_keyboard(client, chat_id, buy_text, True)
2281
+ return
2282
+
2283
+ if user_text_str in["/transfer", "انتقال اکانت از برنامه به ربات"]:
2284
+ transfer_text = f"""🔄 **انتقال اکانت از برنامه به ربات**
2285
+
2286
+ کاربر گرامی، در صورتی که داخل برنامه «هوش مصنوعی آلفا» پیش‌تر اشتراک تهیه کرده‌اید، نیازی به خرید مجدد اشتراک داخل ربات نیست! 🎉
2287
+
2288
+ کافیست **شناسه یکتای** ربات روبیکای خود را کپی کرده و برای پشتیبانی ما در برنامه هوش مصنوعی ��لفا ارسال کنید تا اکانت اشتراکی شما به سرعت از برنامه به ربات روبیکا انتقال داده شود.
2289
+
2290
+ 🔑 **شناسه یکتای ربات شما:**
2291
+ `{chat_id}`
2292
+
2293
+ 👨‍💻 **دقت کنید شناسه ربات رو به پشتیبانی داخل خود برنامه هوش مصنوعی آلفا ارسال کنید.**"""
2294
+ await send_with_keyboard(client, chat_id, transfer_text, True)
2295
+ return
2296
+
2297
+ if user_text_str in["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]:
2298
+ user_states[str_chat_id]["mode"] = "chat"
2299
+ user_states[str_chat_id]["history"] =[]
2300
+ await send_with_keyboard(client, chat_id, "💬 شما وارد بخش **چت با هوش مصنوعی** شدید.\n\nهر سوالی دارید بفرستید تا جواب بدم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2301
+ return
2302
+
2303
+ if user_text_str in["/image", "🎨 عکس", "ساخت تصاویر🎨"]:
2304
+ user_states[str_chat_id]["mode"] = "image_waiting_for_text"
2305
+ await send_with_keyboard(client, chat_id, "🎨 شما وارد بخش **ساخت عکس پیشرفته** شدید.\n\nمتن (ایده) خود را به صورت کامل ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2306
+ return
2307
+
2308
+ if user_text_str in["/edit_image", "ویرایش تصاویر 🪄"]:
2309
+ user_states[str_chat_id]["mode"] = "image_edit_waiting_for_image"
2310
+ user_states[str_chat_id]["file_bytes"] = None
2311
+ await send_with_keyboard(client, chat_id, "🪄 به بخش **ویرایش عکس (Flux.2)** خوش آمدید.\n\nلطفاً ابتدا عکسی که می‌خواهید ویرایش کنید را بفرستید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2312
+ return
2313
+
2314
+ if user_text_str in["/tts", "🎙️ صدا", "تبدیل متن به صدا🗣️"]:
2315
+ user_states[str_chat_id]["mode"] = "tts_waiting_for_text"
2316
+ await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تبدیل متن به صدا** شدید.\n\nلطفاً متنی که می‌خواهید به صدا تبدیل شود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2317
+ return
2318
+
2319
+ if user_text_str in["/podcast", "📻 پادکست", "ساخت پادکست 🎙️"]:
2320
+ user_states[str_chat_id]["mode"] = "podcast_waiting_for_topic"
2321
+ await send_with_keyboard(client, chat_id, "📻 شما وارد بخش **ساخت پادکست** شدید.\n\nلطفاً موضوع پادکست خود را بفرستید.\nمثال: درباره تاریخچه پیدایش قهوه با ۳ گوینده یک پادکست جذاب بساز . همچنین این قسمت متصل به مدل زبانی است و درخواست هارو قبل از ساخت درک میکنه. میتوانید مقاله کامل یک سایت بفرستید با تبلیغات یا هرچی، هوش مصنوعی متن مقاله رو استخراج و پادکست براتون میسازه . در توضیحات امکان مشخص کردن تعداد گوینده به همراه اسم شون نیز از سمت شما امکان پذیر است.\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2322
+ return
2323
+
2324
+ # ===============================================
2325
+ # بخش‌های تغییر صدا و کلون کردن صدا
2326
+ # ===============================================
2327
+ if user_text_str in["/vc", "تغییر صدا 🎙️"]:
2328
+ user_states[str_chat_id]["mode"] = "vc_waiting_for_voice"
2329
+ user_states[str_chat_id]["file_bytes"] = None
2330
+ await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تغییر صدا** شدید.\n\nلطفاً صدای خود (ویس) را بفرستید تا آن را به صدای شخصیت‌های معروف تبدیل کنم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2331
+ return
2332
+
2333
+ if user_text_str in["/clone", "کلون کردن صدا 👤"]:
2334
+ user_states[str_chat_id]["mode"] = "clone_waiting_for_src"
2335
+ user_states[str_chat_id]["file_bytes"] = None
2336
+ user_states[str_chat_id]["ref_bytes"] = None
2337
+ clone_text = """👤 **شما وارد بخش کلون کردن صدای اختصاصی شدید.**
2338
+
2339
+ ✨ **این بخش چه کاری انجام می‌دهد؟**
2340
+ با این ویژگی بی‌نظیر، شما می‌توانید صدای خود (یا هر فرد دیگری) را دقیقاً شبیه‌سازی کنید! کافیست یک نمونه از صدای خودتان صحبت کنید و یک نمونه از صدای شخص مورد نظر (الگو) را به هوش مصنوعی بدهید. ربات لحن و کلمات شما را گرفته و دقیقاً با جنس صدای شخص الگو بازسازی می‌کند.
2341
+
2342
+ 📌 **راهنمای استفاده:**
2343
+ شما در این مرحله به **2 فایل صوتی** نیاز دارید:
2344
+ 1️⃣ **صدای ورودی (شما):** صدایی که می‌خواهید متن و لحن آن خوانده شود.
2345
+ 2️⃣ **صدای الگو (هدف):** صدایی که می‌خواهید خروجی نهایی شبیه به آن شود (بدون نویز و موزیک پس‌زمینه، بین ۳ تا ۱۰ ثانیه بهترین نتیجه را می‌دهد).
2346
+
2347
+ 👇 **مرحله اول:**
2348
+ لطفاً ابتدا **صدای خودتان (ورودی)** را به صورت ویس یا فایل صوتی ارسال کنید:
2349
+
2350
+ *(برای خروج دکمه «برگشت♻️» را بزنید)*"""
2351
+ await send_with_keyboard(client, chat_id, clone_text, True)
2352
+ return
2353
+ # ===============================================
2354
+
2355
+ if user_text_str in["/file", "تحلیل فایل 📁"]:
2356
+ user_states[str_chat_id]["mode"] = "file_waiting_for_file"
2357
+ user_states[str_chat_id]["file_bytes"] = None
2358
+ await send_with_keyboard(client, chat_id, "📁 شما وارد بخش **تحلیل فایل اختصاصی** شدید.\n\nلطفاً فایل خود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2359
+ return
2360
+
2361
+ if user_text_str in["/stt", "فایل صوتی به متن 📝"]:
2362
+ user_states[str_chat_id]["mode"] = "stt_waiting_for_audio"
2363
+ await send_with_keyboard(client, chat_id, "📝 شما وارد بخش **تبدیل صدا به متن** شدید.\n\nلطفاً فایل خود (ویس، آهنگ، ویدیو و...) را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2364
+ return
2365
+
2366
+ if user_text_str in["/create_file", "ساخت فایل 📄"]:
2367
+ user_states[str_chat_id]["mode"] = "create_file_waiting_for_topic"
2368
+ await send_with_keyboard(client, chat_id, "📄 شما وارد بخش **ساخت فایل** شدید.\n\nلطفاً موضوع مقاله‌ای که می‌خواهید را کامل بفرستید.\nمثال: نحوه مدیریت زمان\n\nهوش مصنوعی یک مقاله کامل و طولانی نوشته و در نهایت فایل PDF و Word آن را به شما تحویل می‌دهد.\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
2369
+ return
2370
+
2371
+ current_mode = user_states[str_chat_id].get("mode")
2372
+
2373
+ if current_mode is None:
2374
+ if is_file: pass
2375
+ elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True)
2376
+ return
2377
+
2378
+ elif current_mode == "waiting_for_referral_code":
2379
+ if user_text_str:
2380
+ normalized_code = to_english_digits(user_text_str).strip()
2381
+
2382
+ if not normalized_code.isdigit() or len(normalized_code) != 8:
2383
+ await send_with_keyboard(client, chat_id, "❌ کد وارد شده نامعتبر است. لطفاً یک کد 8 رقمی صحیح بفرستید.", False)
2384
+ return
2385
+
2386
+ inviter_id = find_user_by_referral_code(normalized_code)
2387
+
2388
+ if not inviter_id:
2389
+ await send_with_keyboard(client, chat_id, "❌ کد هدیه یافت نشد. لطفاً کد را با دقت بررسی کنید.", False)
2390
+ return
2391
+
2392
+ if str(inviter_id) == str(str_chat_id):
2393
+ await send_with_keyboard(client, chat_id, "❌ شما نمی‌توانید کد خودتان را ثبت کنید!", False)
2394
+ return
2395
+
2396
+ user_states[str_chat_id]["mode"] = None
2397
+
2398
+ user_credits_db[str_chat_id]["used_referral"] = True
2399
+ user_credits_db[str_chat_id]["tts"] = user_credits_db[str_chat_id].get("tts", 0) + 10
2400
+
2401
+ user_credits_db[inviter_id]["invited_count"] = user_credits_db[inviter_id].get("invited_count", 0) + 1
2402
+ current_invites = user_credits_db[inviter_id]["invited_count"]
2403
+
2404
+ if current_invites > 0 and current_invites % 10 == 0:
2405
+ inviter_data = user_credits_db[inviter_id]
2406
+ inviter_data["is_premium"] = True
2407
+ now = datetime.datetime.now()
2408
+ if inviter_data.get("expire_date"):
2409
+ try:
2410
+ current_exp = datetime.datetime.fromisoformat(inviter_data["expire_date"])
2411
+ if current_exp > now:
2412
+ new_exp = current_exp + datetime.timedelta(days=3)
2413
+ else:
2414
+ new_exp = now + datetime.timedelta(days=3)
2415
+ except:
2416
+ new_exp = now + datetime.timedelta(days=3)
2417
+ else:
2418
+ new_exp = now + datetime.timedelta(days=3)
2419
+
2420
+ inviter_data["expire_date"] = new_exp.isoformat()
2421
+ inviter_data["chat"] = 999999
2422
+ inviter_data["image"] = 20
2423
+ inviter_data["edit_image"] = 10
2424
+ inviter_data["podcast"] = 999999
2425
+ inviter_data["tts"] = 999999
2426
+ inviter_data["file"] = 999999
2427
+ inviter_data["stt"] = 999999
2428
+
2429
+ save_db(user_credits_db)
2430
+ try:
2431
+ msg_text = f"🎉 **تبریک ویژه!**\nیک دوست کد هدیه شما را ثبت کرد.\nتعداد کل دعوت‌های شما: {current_invites} نفر\n\n🎁 **هدیه شما:** ۳ روز اشتراک پرو نامحدود به حساب شما افزوده شد!"
2432
+ asyncio.create_task(send_with_keyboard(client, inviter_id, msg_text, True))
2433
+ except: pass
2434
+ else:
2435
+ save_db(user_credits_db)
2436
+ remains = 10 - (current_invites % 10)
2437
+ try:
2438
+ msg_text = f"🎉 **تبریک!**\nیک دوست کد هدیه شما را ثبت کرد.\nتعداد کل دعوت‌های شما: {current_invites} نفر\n\n⏳ با دعوت {remains} نفر دیگر، ۳ روز اشتراک رایگان دریافت می‌کنید!"
2439
+ asyncio.create_task(send_with_keyboard(client, inviter_id, msg_text, True))
2440
+ except: pass
2441
+
2442
+ await send_with_keyboard(client, chat_id, "✅ کد هدیه با موفقیت ثبت شد!\n🎁 **10 سهمیه تبدیل رایگان متن به صدا** به حساب شما اضافه گردید.", True)
2443
+ return
2444
+
2445
+ elif current_mode == "chat":
2446
+ if is_file:
2447
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
2448
+ try:
2449
+ file_bytes = await helper_download_file(client, msg_obj)
2450
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2451
+ user_states[str_chat_id]["file_name"] = file_name
2452
+ user_states[str_chat_id]["mode"] = "chat_waiting_for_prompt"
2453
+ await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بفرستید و بگویید **چه کاری با این فایل انجام دهم؟**\n(مثلاً: این تصویر را توصیف کن، یا متن این سند را خلاصه کن)", False)
2454
+ except Exception as dl_err:
2455
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2456
+ elif user_text_str:
2457
+ asyncio.create_task(process_gemini(client, chat_id, user_text_str))
2458
+ return
2459
+
2460
+ elif current_mode == "chat_waiting_for_prompt":
2461
+ if user_text_str:
2462
+ saved_bytes = user_states[str_chat_id].get("file_bytes")
2463
+ saved_name = user_states[str_chat_id].get("file_name", "file.jpeg")
2464
+ user_states[str_chat_id]["mode"] = "chat"
2465
+ user_states[str_chat_id]["file_bytes"] = None
2466
+ asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=saved_bytes, file_name=saved_name))
2467
+ else:
2468
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
2469
+ return
2470
+
2471
+ elif current_mode == "image_waiting_for_text":
2472
+ if user_text_str:
2473
+ user_states[str_chat_id]["text"] = user_text_str
2474
+ user_states[str_chat_id]["mode"] = "image_waiting_for_size"
2475
+
2476
+ size_menu = """🖼 **لطفاً ابعاد تصویر خود را انتخاب کنید:**
2477
+
2478
+ 1️⃣ `1:1` (مربع) - مناسب پروفایل ⬛
2479
+ 2️⃣ `9:16` (عمودی) - مناسب استوری و پس‌زمینه گوشی 📱
2480
+ 3️⃣ `16:9` (افقی) - مناسب دسکتاپ و ویدیو 🖥️
2481
+ 4️⃣ `4:3` (استاندارد) - مناسب چاپ و پست 📸
2482
+
2483
+ (لطفاً فقط عدد 1 تا 4 را بفرستید)"""
2484
+ await send_with_keyboard(client, chat_id, size_menu, False)
2485
+ else:
2486
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً ایده عکس خود را به صورت متنی بفرستید.", False)
2487
+ return
2488
+
2489
+ elif current_mode == "image_waiting_for_size":
2490
+ normalized_choice = to_english_digits(user_text_str).strip()
2491
+ if normalized_choice in["1", "2", "3", "4"]:
2492
+ saved_text = user_states[str_chat_id].get("text", "")
2493
+ user_states[str_chat_id]["mode"] = None
2494
+ asyncio.create_task(process_image(client, chat_id, saved_text, normalized_choice))
2495
+ else:
2496
+ await send_with_keyboard(client, chat_id, "❌ عدد وارد شده نامعتبر است! لطفاً فقط یکی از اعداد 1 تا 4 را بفرستید.", False)
2497
+ return
2498
+
2499
+ elif current_mode == "image_edit_waiting_for_image":
2500
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک عکس ارسال کنید.", False)
2501
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود عکس...", False)
2502
+ try:
2503
+ file_bytes = await helper_download_file(client, msg_obj)
2504
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2505
+ user_states[str_chat_id]["mode"] = "image_edit_waiting_for_prompt"
2506
+ await send_with_keyboard(client, chat_id, "✅ عکس با موفقیت دریافت شد.\n\nحالا دستور خود را به صورت متنی تایپ کنید.\nمثال: یک کلاه قرمز روی سر این گربه بگذار.", False)
2507
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت عکس!\n{str(dl_err)}", False)
2508
+ return
2509
+
2510
+ elif current_mode == "image_edit_waiting_for_prompt":
2511
+ if user_text_str:
2512
+ saved_bytes = user_states[str_chat_id].get("file_bytes")
2513
+ user_states[str_chat_id]["mode"] = None
2514
+ user_states[str_chat_id]["file_bytes"] = None
2515
+ asyncio.create_task(process_image_edit(client, chat_id, saved_bytes, user_text_str))
2516
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
2517
+ return
2518
+
2519
+ elif current_mode == "tts_waiting_for_text":
2520
+ if user_text_str:
2521
+ if len(user_text_str) > 2500: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً متنی کوتاه‌تر از 2500 کاراکتر بفرستید.", False)
2522
+ user_states[str_chat_id]["text"] = user_text_str
2523
+ user_states[str_chat_id]["mode"] = "tts_waiting_for_speaker"
2524
+
2525
+ speakers_menu = """✅ متن شما ذخیره شد.
2526
+ لطفاً **شماره** گوینده مورد نظر خود را بفرستید:
2527
+ 1. شهاب | 2. آوا | 3. نوید
2528
+ 4. آرمان | 5. مهسا | 6. دانا
2529
+ 7. سامان | 8. آرش | 9. شبنم
2530
+ 10. سحر | 11. مریم | 12. بهرام
2531
+ 13. نیکان| 14. فرناز | 15. سارا
2532
+ 16. مانی | 17. آرتین | 18. دلنواز
2533
+ 19. روژان | 20. امید | 21. بردیا
2534
+ 22. ترانه | 23. نیکو | 24. هستی
2535
+ 25. کامیار| 26. کیانوش| 27. پویا
2536
+ 28. مهتاب | 29. سام | 30. لیدا"""
2537
+ await send_with_keyboard(client, chat_id, speakers_menu, False)
2538
+ return
2539
+
2540
+ elif current_mode == "tts_waiting_for_speaker":
2541
+ normalized_text = to_english_digits(user_text_str)
2542
+
2543
+ if normalized_text.isdigit() and normalized_text in SPEAKERS:
2544
+ spk_name, spk_id = SPEAKERS[normalized_text]
2545
+ txt = user_states[str_chat_id]["text"]
2546
+ user_states[str_chat_id]["mode"] = "tts_waiting_for_text"
2547
+ asyncio.create_task(process_tts(client, chat_id, txt, spk_id, spk_name))
2548
+ else:
2549
+ await send_with_keyboard(client, chat_id, "❌ شماره نامعتبر است! لطفاً فقط یک عدد بین 1 تا 30 بفرستید.", False)
2550
+ return
2551
+
2552
+ elif current_mode == "podcast_waiting_for_topic":
2553
+ if user_text_str: asyncio.create_task(process_podcast(client, chat_id, user_text_str))
2554
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع پادکست خود را به صورت متنی بفرستید.", False)
2555
+ return
2556
+
2557
+ elif current_mode == "stt_waiting_for_audio":
2558
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل ارسال کنید.", False)
2559
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
2560
+ try:
2561
+ audio_bytes = await helper_download_file(client, msg_obj)
2562
+ asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name))
2563
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دانلود فایل!\n{str(dl_err)}", False)
2564
+ return
2565
+
2566
+ elif current_mode == "file_waiting_for_file":
2567
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک فایل ارسال کنید.", False)
2568
+ await send_with_keyboard(client, chat_id, "📥 در حال دریافت فایل...", False)
2569
+ try:
2570
+ file_bytes = await helper_download_file(client, msg_obj)
2571
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2572
+ user_states[str_chat_id]["file_name"] = file_name
2573
+ user_states[str_chat_id]["mode"] = "file_waiting_for_prompt"
2574
+ await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بگویید **چگونه تحلیل شود؟**\n(می‌توانید سوال خاصی بپرسید یا فقط بخواهید فایل را توضیح دهد)", False)
2575
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2576
+ return
2577
+
2578
+ elif current_mode == "file_waiting_for_prompt":
2579
+ if user_text_str:
2580
+ saved_bytes = user_states[str_chat_id].get("file_bytes")
2581
+ saved_name = user_states[str_chat_id].get("file_name", "file.jpeg")
2582
+ user_states[str_chat_id]["mode"] = "file_waiting_for_file"
2583
+ asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str))
2584
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
2585
+ return
2586
+
2587
+ elif current_mode == "create_file_waiting_for_topic":
2588
+ if user_text_str:
2589
+ asyncio.create_task(process_create_file(client, chat_id, user_text_str))
2590
+ else:
2591
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع مقاله خود را به صورت متنی بفرستید.", False)
2592
+ return
2593
+
2594
+ # ===============================================
2595
+ # بخش‌های تغییر صدا و کلون صدا
2596
+ # ===============================================
2597
+ elif current_mode == "vc_waiting_for_voice":
2598
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل صوتی یا ویس ارسال کنید.", False)
2599
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل صوتی شما...", False)
2600
+ try:
2601
+ file_bytes = await helper_download_file(client, msg_obj)
2602
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2603
+ user_states[str_chat_id]["mode"] = "vc_waiting_for_model"
2604
+
2605
+ model_menu = "✅ صدای شما دریافت شد.\n\nلطفا **شماره** مدلی که می‌خواهید صدایتان به آن تبدیل شود را ارسال کنید:\n\n"
2606
+ for k, v in LEGACY_MODELS.items():
2607
+ if k == "8":
2608
+ model_menu += f"{k}. {v['name']} (نامحدود رایگان)\n"
2609
+ else:
2610
+ model_menu += f"{k}. {v['name']}\n"
2611
+ model_menu += "➖➖➖➖➖➖➖➖\n"
2612
+ for k, v in STANDARD_MODELS.items(): model_menu += f"{k}. {v['name']}\n"
2613
+
2614
+ await send_with_keyboard(client, chat_id, model_menu, False)
2615
+ except Exception as dl_err:
2616
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2617
+ return
2618
+
2619
+ elif current_mode == "vc_waiting_for_model":
2620
+ choice = to_english_digits(user_text_str).strip()
2621
+
2622
+ if choice in STANDARD_MODELS:
2623
+ if creds["voice_conv"] <= 0: return await send_with_keyboard(client, chat_id, "❌ سهمیه تغییر صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
2624
+ user_states[str_chat_id]["mode"] = None
2625
+ model = STANDARD_MODELS[choice]
2626
+ src_bytes = user_states[str_chat_id]["file_bytes"]
2627
+ await send_with_keyboard(client, chat_id, "📥 در حال آماده‌سازی فایل‌ها...", False)
2628
+ ref_bytes = await helper_download_url_to_bytes(model["ref"])
2629
+ if not ref_bytes: return await send_with_keyboard(client, chat_id, "❌ خطا در دسترسی به فایل صدای مدل.", False)
2630
+
2631
+ asyncio.create_task(process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, model["name"], "voice_conv"))
2632
+
2633
+ elif choice in LEGACY_MODELS:
2634
+ # اگر شماره مدل ۸ نبود و اعتبار هم صفر بود، ارور بده
2635
+ if choice != "8" and creds["voice_conv"] <= 0:
2636
+ return await send_with_keyboard(client, chat_id, "❌ سهمیه تغییر صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
2637
+
2638
+ user_states[str_chat_id]["selected_model"] = choice
2639
+ user_states[str_chat_id]["mode"] = "vc_waiting_for_gender"
2640
+
2641
+ gender_msg = f"✅ مدل {LEGACY_MODELS[choice]['name']} انتخاب شد.\n\nبرای تنظیم دقیق فرکانس‌ها به ما بگویید صدایی که خودتان فرستادید صدای یک **مرد** است یا **زن**؟\n\n1. مرد 👨\n2. زن 👩"
2642
+ await send_with_keyboard(client, chat_id, gender_msg, False)
2643
+ else:
2644
+ await send_with_keyboard(client, chat_id, "❌ شماره وارد شده صحیح نیست. لطفاً فقط عدد مدل را بفرستید.", False)
2645
+ return
2646
+
2647
+ elif current_mode == "vc_waiting_for_gender":
2648
+ choice = to_english_digits(user_text_str).strip()
2649
+ if choice not in["1", "2"]:
2650
+ return await send_with_keyboard(client, chat_id, "❌ لطفاً عدد 1 (مرد) یا 2 (زن) را ارسال کنید.", False)
2651
+
2652
+ user_gender = "male" if choice == "1" else "female"
2653
+ model_key = user_states[str_chat_id]["selected_model"]
2654
+ model = LEGACY_MODELS[model_key]
2655
+ target_gender = model["gender"]
2656
+
2657
+ pitch = 0
2658
+ if target_gender == "female" and user_gender == "male": pitch = 12
2659
+ elif target_gender == "male" and user_gender == "female": pitch = -12
2660
+
2661
+ user_states[str_chat_id]["mode"] = None
2662
+ src_bytes = user_states[str_chat_id]["file_bytes"]
2663
+ asyncio.create_task(process_legacy_vc_job(client, chat_id, src_bytes, model["url"], pitch, model["name"]))
2664
+ return
2665
+
2666
+ elif current_mode == "clone_waiting_for_src":
2667
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل صوتی صدای خودتان را بفرستید.", False)
2668
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود صدای شما...", False)
2669
+ try:
2670
+ file_bytes = await helper_download_file(client, msg_obj)
2671
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2672
+ user_states[str_chat_id]["mode"] = "clone_waiting_for_ref"
2673
+ await send_with_keyboard(client, chat_id, "✅ صدای شما دریافت شد.\n\nحالا **فایل الگو (صدای شخصی که می‌خواهید شبیه‌سازی کنید)** را ارسال کنید.\n(پیشنهاد: فایل بدون نویز و موسیقی، بین ۳ تا ۱۰ ثانیه)", False)
2674
+ except Exception as dl_err:
2675
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2676
+ return
2677
+
2678
+ elif current_mode == "clone_waiting_for_ref":
2679
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً فایل صوتی الگوی هدف را بفرستید.", False)
2680
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود صدای الگو...", False)
2681
+ try:
2682
+ ref_bytes = await helper_download_file(client, msg_obj)
2683
+
2684
+ if creds["voice_clone"] <= 0:
2685
+ return await send_with_keyboard(client, chat_id, "❌ سهمیه کلون کردن صدای شما تمام شده است. نیازمند تهیه اشتراک هستید.", False)
2686
+
2687
+ user_states[str_chat_id]["mode"] = None
2688
+ src_bytes = user_states[str_chat_id]["file_bytes"]
2689
+
2690
+ asyncio.create_task(process_standard_vc_job(client, chat_id, src_bytes, ref_bytes, "صدای اختصاصی (کلون شده)", "voice_clone"))
2691
+ except Exception as dl_err:
2692
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2693
+ return
2694
+
2695
+ except Exception: traceback.print_exc()
2696
 
2697
  if __name__ == "__main__":
2698
  threading.Thread(target=run_flask, daemon=True).start()
2699
+
2700
  if bot_token:
2701
+ loop = asyncio.get_event_loop()
2702
+
2703
+ # 🟢 تونل‌های پرسرعت: ارتقا از 32 کارگر به 256 کارگر برای پاسخگویی همزمان به هزاران نفر
2704
+ loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=256))
2705
+
2706
+ # 🟢 روشن کردن سیستم پاکسازی هوشمند رم در پس‌زمینه
2707
+ loop.create_task(ram_garbage_collector())
2708
+
2709
+ print("🚀 ربات آلفا پرو (نسخه صنعتی + دیتابیس هوشمند + پاکسازی رم + 256 تونل همزمان) روشن شد...")
2710
  bot.run()