Opera8 commited on
Commit
c7fd5aa
·
verified ·
1 Parent(s): 6885eb2

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +572 -361
main.py CHANGED
@@ -29,14 +29,12 @@ BOT_GUID = None
29
 
30
  # =======================================================
31
  # 🔥 سیستم کنترل سرعت و ضد رگبار (Burst Controller) 🔥
32
- # جلوگیری از هنگ کردن ربات هنگام دریافت پیام‌های تلنبار شده
33
  # =======================================================
34
  global_burst_count = 0
35
  global_burst_time = time.time()
36
  burst_lock = threading.Lock()
37
 
38
  def is_backlog_burst():
39
- """اگر بیشتر از 5 پیام در 1 ثانیه بیاید، یعنی رگبار از سمت سرور است و رد می‌شود"""
40
  global global_burst_count, global_burst_time
41
  with burst_lock:
42
  now = time.time()
@@ -46,17 +44,16 @@ def is_backlog_burst():
46
  return False
47
  else:
48
  global_burst_count += 1
49
- if global_burst_count > 5:
50
  return True
51
  return False
52
 
53
  # --- سیستم دیتابیس حساب کاربری متصل به دیتاست هاگینگ فیس ---
54
  DB_FILE = "users_db.json"
55
  DATASET_REPO = "Opera8/Karbaran-rayegan-tedad"
56
- HF_TOKEN_DB = os.environ.get("HF_TOKEN") # توکنی که برای دیتاست تنظیم کرده‌اید
57
  db_lock = threading.Lock()
58
 
59
- # --- الگوریتم تبدیل تاریخ میلادی به شمسی (بدون نیاز به نصب کتابخانه) ---
60
  def gregorian_to_jalali(gy, gm, gd):
61
  g_d_m =[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
62
  gy2 = (gy + 1) if (gm > 2) else gy
@@ -124,12 +121,10 @@ def save_db(db_data):
124
 
125
  user_credits_db = load_db()
126
 
127
- # --- سیستم تولید و یافتن کد هدیه 8 رقمی یکتا ---
128
  def get_or_create_referral_code(chat_id):
129
  user_data = user_credits_db[chat_id]
130
  if not user_data.get("referral_code"):
131
  while True:
132
- # فقط از اعداد برای تولید کد 8 رقمی استفاده می‌کنیم
133
  new_code = ''.join(random.choices(string.digits, k=8))
134
  exists = any(u.get("referral_code") == new_code for u in user_credits_db.values())
135
  if not exists:
@@ -195,7 +190,6 @@ def get_user_credits(chat_id):
195
 
196
  return user_data
197
 
198
- # --- تابع تبدیل اعداد فارسی/عربی به انگلیسی ---
199
  def to_english_digits(text):
200
  if not text:
201
  return text
@@ -215,7 +209,6 @@ def home():
215
  def run_flask():
216
  app.run(host="0.0.0.0", port=7860, threaded=True)
217
 
218
-
219
  # --- توابع کمکی برای جلوگیری از بلاک شدن Event Loop ---
220
  def sync_save_image(image, file_name):
221
  rgb_im = image.convert('RGB')
@@ -236,8 +229,7 @@ def sync_combine_audio(current_audio, new_bytes):
236
  def sync_export_audio(audio_obj, file_name):
237
  audio_obj.export(file_name, format="mp3")
238
 
239
-
240
- # --- ساختار کیبورد آپدیت شده ---
241
  MAIN_KEYPAD_DICT = {
242
  "rows":[
243
  {
@@ -352,7 +344,6 @@ async def check_channel_membership(client, user_id):
352
  except Exception:
353
  return False
354
 
355
-
356
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
357
  try:
358
  if not use_keyboard:
@@ -372,56 +363,112 @@ async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
372
  return None
373
 
374
 
 
 
 
 
 
375
  async def helper_download_file(client, msg_obj):
376
  errors =[]
377
- file_id = None
378
  file_obj = None
 
 
 
379
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
380
  val = getattr(msg_obj, attr, None)
381
  if val:
382
  file_obj = val
383
- if hasattr(val, 'file_id'): file_id = val.file_id
384
- elif isinstance(val, dict) and 'file_id' in val: file_id = val['file_id']
385
- if file_id: break
 
 
 
 
 
 
 
 
 
386
 
387
  temp_name = f"temp_dl_{uuid.uuid4().hex}.tmp"
388
 
389
- if hasattr(client, "download_file"):
 
390
  try:
391
- await client.download_file(msg_obj, temp_name)
392
- if os.path.exists(temp_name):
393
- data = await asyncio.to_thread(sync_read_file, temp_name)
394
- os.remove(temp_name)
395
- return data
396
- except Exception as e: errors.append(f"تلاش ۱: {e}")
397
-
398
- if file_obj:
399
- try:
400
- await client.download_file(file_obj, temp_name)
401
- if os.path.exists(temp_name):
402
- data = await asyncio.to_thread(sync_read_file, temp_name)
403
- os.remove(temp_name)
404
- return data
405
- except Exception as e: errors.append(f"تلاش ۲: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
 
407
- if file_id:
 
 
408
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  await client.download_file(file_id, file_name=temp_name)
410
  if os.path.exists(temp_name):
411
  data = await asyncio.to_thread(sync_read_file, temp_name)
412
  os.remove(temp_name)
413
- return data
414
- except Exception as e: errors.append(f"تلاش ۳: {e}")
415
-
416
- try:
417
- await client.download_file(file_id, save_as=temp_name)
418
- if os.path.exists(temp_name):
419
- data = await asyncio.to_thread(sync_read_file, temp_name)
420
- os.remove(temp_name)
421
- return data
422
- except Exception as e: errors.append(f"تلاش ۴: {e}")
423
-
424
- raise Exception("دانلود ناموفق. لاگ:\n" + "\n".join(errors))
425
 
426
 
427
  GEMINI_KEYS_STR1 = os.environ.get("GEMINI_API_KEYS1", "")
@@ -458,28 +505,35 @@ def get_next_gemini_keys(count=100):
458
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
459
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
460
 
461
- bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
462
-
463
 
 
 
 
464
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
465
  abs_path = os.path.abspath(file_name)
466
  error_logs =[]
467
 
468
- api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in["voice", "Voice", "audio"] else "File"
 
469
 
 
470
  if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'):
471
  api_file_type = "Music"
 
 
472
 
473
- for attempt in range(3):
 
474
  try:
475
  url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
476
  url_send = f"https://botapi.rubika.ir/v3/{bot_token}/sendFile"
477
 
478
  async with aiohttp.ClientSession() as session:
479
- async with session.post(url_request, json={"type": api_file_type}, timeout=20) as resp:
 
480
  if resp.status != 200:
481
  error_logs.append(f"Request HTTP {resp.status}")
482
- await asyncio.sleep(1.5)
483
  continue
484
 
485
  req_data = await resp.json()
@@ -492,21 +546,24 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
492
  form = aiohttp.FormData()
493
  form.add_field('file', f, filename=os.path.basename(abs_path))
494
 
495
- async with session.post(upload_url, data=form, timeout=60) as up_resp:
 
496
  if up_resp.status != 200:
497
  error_logs.append(f"Upload HTTP {up_resp.status}")
498
- if up_resp.status == 502 and api_file_type == "Voice":
499
- api_file_type = "Music"
500
- await asyncio.sleep(1.5)
 
501
  continue
502
 
503
  try:
504
  up_data = await up_resp.json()
505
  except Exception as json_err:
506
  error_logs.append(f"JSON Decode Error: {json_err}")
507
- await asyncio.sleep(1.5)
508
  continue
509
 
 
510
  final_file_id = None
511
  if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
512
  if "data" in up_data and isinstance(up_data["data"], dict):
@@ -522,10 +579,10 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
522
  "chat_keypad_type": "New",
523
  "chat_keypad": MAIN_KEYPAD_DICT
524
  }
525
- async with session.post(url_send, json=send_payload, timeout=20) as send_resp:
526
  if send_resp.status != 200:
527
  error_logs.append(f"Send HTTP {send_resp.status}")
528
- await asyncio.sleep(1.5)
529
  continue
530
 
531
  s_data = await send_resp.json()
@@ -543,21 +600,29 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
543
  error_logs.append(f"Request API Error: {req_data}")
544
  except Exception as e:
545
  error_logs.append(f"Raw HTTP Error: {str(e)[:100]}")
546
- await asyncio.sleep(1.5)
547
 
548
- try:
549
- if hasattr(client, "send_file"):
550
- await client.send_file(chat_id, abs_path)
551
- if caption:
552
- await send_with_keyboard(client, chat_id, caption, True)
553
- return True
554
- elif hasattr(client, "send_document"):
555
- await client.send_document(chat_id, abs_path, caption=caption)
556
- return True
557
- except Exception as e:
558
- error_logs.append(f"Rubpy Send Error: {str(e)[:100]}")
 
 
 
 
 
 
 
 
559
 
560
- return "\n".join(error_logs[-4:])
561
 
562
 
563
  WORKER_URLS =[
@@ -626,26 +691,27 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
626
  history = history[-40:]
627
  if history[0]["role"] == "model": history = history[1:]
628
 
629
- keys_to_try = get_next_gemini_keys(100)
630
  final_answer = None
631
-
632
- async with aiohttp.ClientSession() as session:
633
- for key in keys_to_try:
634
- url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
635
- payload = {"contents": history, "generationConfig": {"temperature": 0.7, "maxOutputTokens": 8192}}
636
- try:
637
- async with session.post(url, json=payload, timeout=60) as response:
638
- if response.status == 200:
639
- data = await response.json()
640
- try:
641
- final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
642
- break
643
- except (KeyError, IndexError): continue
644
- except Exception: continue
 
 
 
645
 
646
- # اگر جیمینای جواب نداد
647
  if not final_answer:
648
- # اگر فایل ارسال شد�� عکس نبود، خطای اختصاصی می‌دهیم
649
  if has_non_image_file:
650
  if history and history[-1]["role"] == "user": history.pop()
651
  try:
@@ -657,7 +723,6 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
657
  await send_with_keyboard(client, chat_id, "❌ متأسفانه سرور اصلی در حال حاضر شلوغ است و در این شرایط **فعلاً تنها امکان تحلیل تصاویر** توسط سرور جایگزین در دسترس است.", False)
658
  return
659
 
660
- # اگر فایل عکس بود یا فقط متن بود، می‌رویم سراغ مدل جایگزین (هاگینگ فیس)
661
  if HF_TOKENS:
662
  hf_messages =[]
663
  for msg in history:
@@ -677,26 +742,28 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
677
  if content_parts:
678
  hf_messages.append({"role": role, "content": content_parts})
679
 
680
- keys_to_try_hf = HF_TOKENS.copy()
681
- random.shuffle(keys_to_try_hf)
682
-
683
- async with aiohttp.ClientSession() as session:
684
- for hf_key in keys_to_try_hf:
685
- url = "https://router.huggingface.co/v1/chat/completions"
686
- headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"}
687
- payload = {
688
- "model": "google/gemma-4-31B-it:novita",
689
- "messages": hf_messages,
690
- "max_tokens": 4096
691
- }
692
- try:
693
- async with session.post(url, headers=headers, json=payload, timeout=60) as response:
694
- if response.status == 200:
695
- data = await response.json()
696
- final_answer = data["choices"][0]["message"]["content"]
697
- break
698
- except Exception:
699
- continue
 
 
700
 
701
  try:
702
  if proc_msg:
@@ -710,10 +777,6 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
710
  await send_with_keyboard(client, chat_id, "❌ تمامی سرورها شلوغ هستند. لطفاً بعداً امتحان کنید.", False)
711
  return
712
 
713
- if not creds.get("is_premium"):
714
- user_credits_db[str_chat_id]["chat"] -= 1
715
- save_db(user_credits_db)
716
-
717
  history.append({"role": "model", "parts":[{"text": final_answer}]})
718
  user_states[chat_id]["history"] = history
719
 
@@ -729,12 +792,19 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
729
  temp_text = temp_text[split_idx:].strip()
730
  if temp_text: chunks.append(temp_text)
731
 
 
732
  for idx, chunk in enumerate(chunks):
733
  if idx != len(chunks) - 1: chunk += "\n\n⏳ *(ادامه در پیام بعدی)...* 👇"
734
  try:
735
- await send_with_keyboard(client, chat_id, chunk, False)
 
736
  await asyncio.sleep(2.5)
737
  except Exception: await asyncio.sleep(2.5)
 
 
 
 
 
738
 
739
  except Exception:
740
  await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
@@ -794,7 +864,6 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
794
  if msg_id: await client.delete_messages(chat_id,[msg_id])
795
  except Exception: pass
796
 
797
- # تنظیم ابعاد انتخابی کاربر
798
  w, h = 1024, 1024
799
  size_name = "مربع (1:1) ⬛"
800
  if size_choice == "2":
@@ -810,24 +879,28 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
810
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
811
  proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی عکس...\n\n📏 **ابعاد:** {size_name}\n📝 **پرامپت ساخته شده:**\n`{short_preview}`\n\n(ممکن است چند ثانیه زمان ببرد)", False)
812
 
813
- keys_to_try = HF_TOKENS.copy()
814
- random.shuffle(keys_to_try)
815
  generated_image = None
816
  last_error_log = "هیچ اتصالی برقرار نشد."
817
 
818
- for token in keys_to_try:
819
- try:
820
- hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
821
- generated_image = await hf_client.text_to_image(
822
- enhanced_prompt,
823
- model="Tongyi-MAI/Z-Image-Turbo",
824
- width=w,
825
- height=h
826
- )
827
- break
828
- except Exception as e:
829
- last_error_log = str(e)
830
- continue
 
 
 
 
 
831
 
832
  try:
833
  if proc_msg:
@@ -836,21 +909,38 @@ async def process_image(client, chat_id, prompt, size_choice="1"):
836
  if msg_id: await client.delete_messages(chat_id,[msg_id])
837
  except Exception: pass
838
 
839
- if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
840
-
841
- user_credits_db[str_chat_id]["image"] -= 1
842
- save_db(user_credits_db)
843
 
844
  try:
845
  file_name = f"image_{uuid.uuid4().hex}.jpg"
846
- # استفاده از ترد برای جلوگیری از مسدود شدن برنامه حین کار با PIL
847
  await asyncio.to_thread(sync_save_image, generated_image, file_name)
848
  await asyncio.sleep(1)
849
  caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n📏 ابعاد تصویر: {size_name}\n✨ ایده اولیه: {prompt}"
850
- upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
851
- if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ خطا در آپلود روبیکا:\n`{str(upload_result)[:800]}`", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852
  if os.path.exists(file_name): os.remove(file_name)
853
- except Exception as e: await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس:\n{str(e)[:150]}", True)
 
854
 
855
 
856
  async def translate_text_aloha(prompt_text):
@@ -902,46 +992,63 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
902
  if not translated_prompt or translated_prompt.strip() == "":
903
  translated_prompt = prompt
904
 
905
- keys_to_try = HF_TOKENS.copy()
906
- random.shuffle(keys_to_try)
907
  generated_image = None
908
  last_error_log = "سرور پاسخ نداد."
909
 
910
- for token in keys_to_try:
911
- try:
912
- hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
913
- generated_image = await hf_client.image_to_image(
914
- image_bytes,
915
- prompt=translated_prompt,
916
- model="black-forest-labs/FLUX.2-dev"
917
- )
918
- break
919
- except Exception as e:
920
- last_error_log = str(e)
921
- continue
 
 
 
 
 
922
 
923
  try:
924
  if proc_msg:
925
  msg_id = getattr(proc_msg, 'message_id', None)
926
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
927
- if msg_id: await client.delete_messages(chat_id,[msg_id])
928
  except Exception: pass
929
 
930
  if not generated_image:
931
  return await send_with_keyboard(client, chat_id, f"❌ متأسفانه ویرایش عکس انجام نشد.\n\n⚠️ علت:\n{last_error_log[:200]}", True)
932
 
933
- user_credits_db[str_chat_id]["edit_image"] -= 1
934
- save_db(user_credits_db)
935
-
936
  try:
937
  file_name = f"edited_flux_{uuid.uuid4().hex}.jpg"
938
- # استفاده از ترد برای ذخیره عکس
939
  await asyncio.to_thread(sync_save_image, generated_image, file_name)
940
  await asyncio.sleep(1)
941
  caption_text = f"🪄 ویرایش عکس با هوش مصنوعی Flux.2 انجام شد!\n\n✨ تغییرات خواسته شده: {prompt}\n🔤 متن ارسال شده به هوش: {translated_prompt}"
942
- upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
943
- if upload_result is not True:
944
- await send_with_keyboard(client, chat_id, f"❌ عکس ساخته شد اما خطا در ارسال به روبیکا رخ داد:\n`{str(upload_result)[:800]}`", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
945
  if os.path.exists(file_name): os.remove(file_name)
946
  except Exception as e:
947
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
@@ -957,23 +1064,30 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
957
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
958
  payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
959
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
 
 
960
  audio_bytes = None
961
  last_error = "پاسخی دریافت نشد"
962
- workers = WORKER_URLS.copy()
963
- random.shuffle(workers)
964
 
965
- async with aiohttp.ClientSession(headers=headers, timeout=aiohttp.ClientTimeout(total=600)) as session:
966
- for worker_url in workers[:3]:
967
- try:
968
- async with session.post(worker_url, json=payload) as response:
969
- if response.status == 200:
970
- content_type = response.headers.get('Content-Type', '')
971
- if 'audio' in content_type or response.content_length > 1000:
972
- audio_bytes = await response.read()
973
- break
974
- else: last_error = "فایل نامعتبر"
975
- else: last_error = f"ارور ({response.status})"
976
- except Exception as e: last_error = f"خطا: {str(e)}"; continue
 
 
 
 
 
 
 
977
 
978
  try:
979
  if proc_msg:
@@ -983,23 +1097,34 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
983
  except Exception: pass
984
 
985
  if audio_bytes:
986
- if not creds.get("is_premium"):
987
- user_credits_db[str_chat_id]["tts"] -= 1
988
- save_db(user_credits_db)
989
-
990
  file_name_mp3 = f"audio_{uuid.uuid4().hex}.mp3"
991
-
992
- # ذخیره فایل در ترد جداگانه
993
  await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes)
994
-
995
  await asyncio.sleep(1)
996
- upload_result_file = await helper_upload_file(client, chat_id, file_name_mp3, "File", "✅ صدای شما با موفقیت آماده شد (فایل MP3):")
997
 
998
- if upload_result_file is not True:
999
- await send_with_keyboard(client, chat_id, f"❌ ارور آپلود فایل:\n`{str(upload_result_file)[:800]}`", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1000
 
1001
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
1002
- else: await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
 
1003
  except Exception: traceback.print_exc()
1004
 
1005
 
@@ -1057,7 +1182,9 @@ async def process_podcast(client, chat_id, prompt):
1057
  for index, turn in enumerate(script_data):
1058
  payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1059
  chunk_audio_bytes = None
1060
- for attempt in range(3):
 
 
1061
  try:
1062
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
1063
  if resp.status == 200:
@@ -1065,19 +1192,15 @@ async def process_podcast(client, chat_id, prompt):
1065
  break
1066
  except Exception: await asyncio.sleep(2)
1067
 
1068
- if not chunk_audio_bytes: return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} از سرور. عملیات متوقف شد.", True)
 
1069
 
1070
  try:
1071
- # جلوگیری از گیر کردن لوپ در هنگام ترکیب فایل‌های صوتی سنگین
1072
  combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
1073
- except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
1074
-
1075
- if not creds.get("is_premium"):
1076
- user_credits_db[str_chat_id]["podcast"] -= 1
1077
- save_db(user_credits_db)
1078
 
1079
  file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
1080
- # اکسپورت فایل صوتی در ترد جدا
1081
  await asyncio.to_thread(sync_export_audio, combined_audio, file_name_mp3)
1082
 
1083
  try:
@@ -1088,10 +1211,28 @@ async def process_podcast(client, chat_id, prompt):
1088
  except: pass
1089
 
1090
  caption_file = f"🎧 فایل پادکست شما با فرمت MP3:\n\n💡 موضوع شما: {prompt}"
1091
- upload_result_file = await helper_upload_file(client, chat_id, file_name_mp3, "File", caption_file)
1092
 
1093
- if upload_result_file is not True:
1094
- await send_with_keyboard(client, chat_id, f"❌ پادکست ساخته شد اما روبیکا خطای آپلود داد.\n\n`{str(upload_result_file)[:800]}`", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1095
 
1096
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
1097
 
@@ -1109,21 +1250,25 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
1109
  mime_type, _ = mimetypes.guess_type(file_name)
1110
  if not mime_type: mime_type = "audio/ogg"
1111
 
1112
- keys_to_try = get_next_gemini_keys(100)
1113
- transcribed_text = None
1114
  prompt = "لطفاً این فایل صوتی/تصویری را با دقت کامل گوش بده و صحبت‌های داخل آن را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
1115
 
1116
- async with aiohttp.ClientSession() as session:
1117
- for key in keys_to_try:
1118
- url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
1119
- payload = {"contents":[{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.2}}
1120
- try:
1121
- async with session.post(url, json=payload, timeout=60) as response:
1122
- if response.status == 200:
1123
- data = await response.json()
1124
- transcribed_text = data["candidates"][0]["content"]["parts"][0]["text"]
1125
- break
1126
- except Exception: continue
 
 
 
 
 
 
1127
 
1128
  try:
1129
  if proc_msg:
@@ -1133,10 +1278,11 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
1133
  except Exception: pass
1134
 
1135
  if transcribed_text:
1136
- if not creds.get("is_premium"):
 
 
1137
  user_credits_db[str_chat_id]["stt"] -= 1
1138
  save_db(user_credits_db)
1139
- await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
1140
  else:
1141
  await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", True)
1142
 
@@ -1164,70 +1310,76 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
1164
 
1165
  is_image = mime_type.startswith('image/')
1166
 
1167
- keys_to_try = get_next_gemini_keys(100)
1168
  final_answer = None
1169
-
1170
- async with aiohttp.ClientSession() as session:
1171
- for key in keys_to_try:
1172
- url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
1173
- payload = {"contents":[{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.6}}
1174
- try:
1175
- async with session.post(url, json=payload, timeout=45) as response:
1176
- if response.status == 200:
1177
- data = await response.json()
1178
- final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
1179
- break
1180
- except Exception: continue
 
 
 
1181
 
1182
- # جایگزین تصویر اگر جیمینای جواب نداد
1183
  if not final_answer:
1184
  if is_image and HF_TOKENS:
1185
- keys_to_try_hf = HF_TOKENS.copy()
1186
- random.shuffle(keys_to_try_hf)
1187
-
1188
- hf_messages =[
1189
- {
1190
- "role": "user",
1191
- "content":[
1192
- {"type": "text", "text": prompt},
1193
- {
1194
- "type": "image_url",
1195
- "image_url": {"url": f"data:{mime_type};base64,{base64_data}"}
1196
- }
1197
- ]
1198
- }
1199
- ]
1200
-
1201
- async with aiohttp.ClientSession() as session:
1202
- for hf_key in keys_to_try_hf:
1203
- url = "https://router.huggingface.co/v1/chat/completions"
1204
- headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"}
1205
- payload = {
1206
- "model": "google/gemma-4-31B-it:novita",
1207
- "messages": hf_messages,
1208
- "max_tokens": 4096
1209
  }
1210
- try:
1211
- async with session.post(url, headers=headers, json=payload, timeout=60) as response:
1212
- if response.status == 200:
1213
- data = await response.json()
1214
- final_answer = data["choices"][0]["message"]["content"]
1215
- break
1216
- except Exception:
1217
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
1218
 
1219
  try:
1220
  if proc_msg:
1221
  msg_id = getattr(proc_msg, 'message_id', None)
1222
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1223
- if msg_id: await client.delete_messages(chat_id, [msg_id])
1224
  except Exception: pass
1225
 
1226
  if final_answer:
1227
- if not creds.get("is_premium"):
 
 
1228
  user_credits_db[str_chat_id]["file"] -= 1
1229
  save_db(user_credits_db)
1230
- await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
1231
  else:
1232
  if not is_image:
1233
  await send_with_keyboard(client, chat_id, "❌ متأسفانه سرور اصلی در حال حاضر شلوغ است و در این شرایط **فعلاً تنها امکان تحلیل تصاویر** توسط سرور جایگزین در دسترس است.", True)
@@ -1247,40 +1399,47 @@ async def process_create_file(client, chat_id, topic):
1247
  proc_msg = await send_with_keyboard(client, chat_id, "✍️ در حال تحقیق و نگارش مقاله جامع و حرفه‌ای...\n(این عملیات با توجه به طولانی بودن متن ممکن است کمی طول بکشد)", False)
1248
 
1249
  ai_prompt = f"یک مقاله بسیار جامع، کاملا حرفه‌ای، بسیار طولانی و با جزئیات کامل درباره موضوع زیر به زبان فارسی بنویس. فقط متن مقاله را بده و هیچ توضیح اضافه‌ای نده:\n\nموضوع: {topic}"
 
 
1250
  article_text = None
1251
-
1252
- keys_to_try = get_next_gemini_keys(100)
1253
- async with aiohttp.ClientSession() as session:
1254
- for key in keys_to_try:
1255
- url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
1256
- payload = {"contents":[{"parts":[{"text": ai_prompt}]}], "generationConfig": {"temperature": 0.7, "maxOutputTokens": 8192}}
1257
- try:
1258
- async with session.post(url, json=payload, timeout=60) as response:
1259
- if response.status == 200:
1260
- data = await response.json()
1261
- article_text = data["candidates"][0]["content"]["parts"][0]["text"]
1262
- break
1263
- except Exception: continue
1264
-
1265
- if not article_text and HF_TOKENS:
1266
- keys_to_try_hf = HF_TOKENS.copy()
1267
- random.shuffle(keys_to_try_hf)
1268
  async with aiohttp.ClientSession() as session:
1269
- for hf_key in keys_to_try_hf:
1270
- url = "https://router.huggingface.co/v1/chat/completions"
1271
- headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"}
1272
- payload = {
1273
- "model": "google/gemma-4-31B-it:novita",
1274
- "messages":[{"role": "user", "content":[{"type": "text", "text": ai_prompt}]}],
1275
- "max_tokens": 4096
1276
- }
1277
  try:
1278
- async with session.post(url, headers=headers, json=payload, timeout=60) as response:
1279
  if response.status == 200:
1280
  data = await response.json()
1281
- article_text = data["choices"][0]["message"]["content"]
1282
  break
1283
  except Exception: continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1284
 
1285
  if not article_text:
1286
  return await send_with_keyboard(client, chat_id, "❌ سرور تولید متن شلوغ است، لطفاً بعداً تلاش کنید.", True)
@@ -1292,78 +1451,116 @@ async def process_create_file(client, chat_id, topic):
1292
  if msg_id: await client.delete_messages(chat_id,[msg_id])
1293
  except Exception: pass
1294
 
 
 
1295
  converter_url = "https://opera8-texttopdf.hf.space/"
1296
  uid = uuid.uuid4().hex
1297
-
1298
- # پیام اطلاع رسانی قبل از ساخت فایل‌ها
1299
- proc_msg = await send_with_keyboard(client, chat_id, "📄 مقاله با موفقیت نوشته شد!\n\n⏳ در حال ارتباط همزمان با هوش مصنوعی برای دریافت فایل‌های PDF و Word...\n", False)
1300
 
1301
- # تابع کمکی برای درخواست همزمان به سرور
1302
- async def fetch_document(fmt):
1303
- for attempt in range(3):
1304
- try:
1305
- async with aiohttp.ClientSession() as session:
1306
- form_data = aiohttp.FormData()
1307
- form_data.add_field('content', article_text)
1308
- form_data.add_field('format', fmt)
1309
- # تایم‌اوت 180 ثانیه برای فرصت کافی به Weasyprint
1310
- async with session.post(converter_url, data=form_data, timeout=180) as resp:
1311
- if resp.status == 200:
1312
- data = await resp.read()
1313
- if data and len(data) > 0:
1314
- return data
1315
- except Exception:
1316
- pass
1317
- await asyncio.sleep(2)
1318
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1319
 
1320
- # اجرای همزمان هر دو درخواست برای سرعت بیشتر و جلوگیری از تایم‌اوت متوالی
1321
- pdf_bytes, docx_bytes = await asyncio.gather(
1322
- fetch_document('pdf'),
1323
- fetch_document('docx')
1324
- )
1325
-
1326
  try:
1327
  if proc_msg:
1328
  msg_id = getattr(proc_msg, 'message_id', None)
1329
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1330
- if msg_id: await client.delete_messages(chat_id, [msg_id])
1331
  except Exception: pass
1332
 
1333
- # --- آپلود فایل‌ها در روبیکا ---
1334
- uploaded_pdf = False
1335
- uploaded_docx = False
1336
-
1337
- if pdf_bytes:
1338
- pdf_filename = f"Article_{uid}.pdf"
1339
- await asyncio.to_thread(sync_write_file, pdf_filename, pdf_bytes)
1340
- res = await helper_upload_file(client, chat_id, pdf_filename, "File", f"📄 فایل PDF مقاله شما:\n\n💡 موضوع: {topic}")
1341
- if res is True:
1342
- uploaded_pdf = True
1343
- if os.path.exists(pdf_filename):
1344
- os.remove(pdf_filename)
1345
-
1346
- await asyncio.sleep(1.5)
1347
-
1348
- if docx_bytes:
1349
- docx_filename = f"Article_{uid}.docx"
1350
- await asyncio.to_thread(sync_write_file, docx_filename, docx_bytes)
1351
- res = await helper_upload_file(client, chat_id, docx_filename, "File", f"📝 فایل Word (DOCX) مقاله شما:\n\n💡 موضوع: {topic}")
1352
- if res is True:
1353
- uploaded_docx = True
1354
- if os.path.exists(docx_filename):
1355
- os.remove(docx_filename)
1356
-
1357
- if not creds.get("is_premium"):
1358
- user_credits_db[str_chat_id]["chat"] -= 1
1359
- save_db(user_credits_db)
1360
-
1361
- if uploaded_pdf and uploaded_docx:
1362
- await send_with_keyboard(client, chat_id, "✅ هر دو فایل (PDF و Word) مقاله شما با موفقیت تحویل داده شد!", True)
1363
- elif uploaded_pdf or uploaded_docx:
1364
- await send_with_keyboard(client, chat_id, "⚠️ یکی از فایل‌ها با موفقیت ارسال شد، اما در ساخت یا ارسال فایل دیگر مشکلی از سمت سرور پیش آمد.", True)
1365
  else:
1366
- await send_with_keyboard(client, chat_id, "❌ فایل‌ها ساخته شدند اما متأسفانه روبیکا در ارسال آن‌ها دچار مشکل شد.", True)
1367
 
1368
 
1369
  if not bot_token:
@@ -1723,7 +1920,7 @@ else:
1723
  🔑 **شناسه یکتای شما:** `{chat_id}`
1724
 
1725
  👨‍💻 **ارتباط با پشتیبانی:**
1726
- 🆔 @aialpha_admin"""
1727
  await send_with_keyboard(client, chat_id, buy_text, True)
1728
  return
1729
 
@@ -1737,7 +1934,7 @@ else:
1737
  🔑 **شناسه یکتای ربات شما:** `{chat_id}`
1738
 
1739
  👨‍💻 **دقت کنید شناسه ربات رو به پشتیبانی داخل خود برنامه هوش مصنوعی آلفا ارسال کنید:**
1740
- 🤖"""
1741
  await send_with_keyboard(client, chat_id, transfer_text, True)
1742
  return
1743
 
@@ -1796,7 +1993,6 @@ else:
1796
  # ===============================================
1797
  elif current_mode == "waiting_for_referral_code":
1798
  if user_text_str:
1799
- # تبدیل به اعداد انگلیسی در صورت استفاده از کیبورد فارسی
1800
  normalized_code = to_english_digits(user_text_str).strip()
1801
 
1802
  if not normalized_code.isdigit() or len(normalized_code) != 8:
@@ -1815,11 +2011,9 @@ else:
1815
 
1816
  user_states[str_chat_id]["mode"] = None
1817
 
1818
- # 1. پاداش کاربر دعوت شده (اضافه شدن 10 تبدیل صدا)
1819
  user_credits_db[str_chat_id]["used_referral"] = True
1820
  user_credits_db[str_chat_id]["tts"] = user_credits_db[str_chat_id].get("tts", 0) + 10
1821
 
1822
- # 2. پاداش شخص دعوت کننده
1823
  user_credits_db[inviter_id]["invited_count"] = user_credits_db[inviter_id].get("invited_count", 0) + 1
1824
  current_invites = user_credits_db[inviter_id]["invited_count"]
1825
 
@@ -1864,18 +2058,37 @@ else:
1864
  await send_with_keyboard(client, chat_id, "✅ کد هدیه با موفقیت ثبت شد!\n🎁 **10 سهمیه تبدیل رایگان متن به صدا** به حساب شما اضافه گردید.", True)
1865
  return
1866
 
 
 
 
1867
  elif current_mode == "chat":
1868
  if is_file:
1869
  await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
1870
  try:
1871
  file_bytes = await helper_download_file(client, msg_obj)
1872
- asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=file_bytes, file_name=file_name))
1873
- except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
1874
- elif user_text_str: asyncio.create_task(process_gemini(client, chat_id, user_text_str))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1875
  return
1876
 
1877
  # ===============================================
1878
- # آپدیت جدید: دریافت سایز قبل از ساخت عکس
1879
  # ===============================================
1880
  elif current_mode == "image_waiting_for_text":
1881
  if user_text_str:
@@ -2006,10 +2219,8 @@ if __name__ == "__main__":
2006
  threading.Thread(target=run_flask, daemon=True).start()
2007
 
2008
  if bot_token:
2009
- # افزایش ظرفیت کارگرهای (Workers) پس‌زمینه در Asyncio برای افزایش شدید سرعت پاسخگویی
2010
  loop = asyncio.get_event_loop()
2011
- # تنظیم ترد پول روی 16 کارگر به جای 1 پیش‌فرض که باعث کندی میشد
2012
  loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=16))
2013
 
2014
- print("ربات آلفا پرو با سیستم اشتراک نامحدود + سپر امنیتی + 16 Worker پس‌زمینه روشن شد...")
2015
  bot.run()
 
29
 
30
  # =======================================================
31
  # 🔥 سیستم کنترل سرعت و ضد رگبار (Burst Controller) 🔥
 
32
  # =======================================================
33
  global_burst_count = 0
34
  global_burst_time = time.time()
35
  burst_lock = threading.Lock()
36
 
37
  def is_backlog_burst():
 
38
  global global_burst_count, global_burst_time
39
  with burst_lock:
40
  now = time.time()
 
44
  return False
45
  else:
46
  global_burst_count += 1
47
+ if global_burst_count > 60:
48
  return True
49
  return False
50
 
51
  # --- سیستم دیتابیس حساب کاربری متصل به دیتاست هاگینگ فیس ---
52
  DB_FILE = "users_db.json"
53
  DATASET_REPO = "Opera8/Karbaran-rayegan-tedad"
54
+ HF_TOKEN_DB = os.environ.get("HF_TOKEN")
55
  db_lock = threading.Lock()
56
 
 
57
  def gregorian_to_jalali(gy, gm, gd):
58
  g_d_m =[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
59
  gy2 = (gy + 1) if (gm > 2) else gy
 
121
 
122
  user_credits_db = load_db()
123
 
 
124
  def get_or_create_referral_code(chat_id):
125
  user_data = user_credits_db[chat_id]
126
  if not user_data.get("referral_code"):
127
  while True:
 
128
  new_code = ''.join(random.choices(string.digits, k=8))
129
  exists = any(u.get("referral_code") == new_code for u in user_credits_db.values())
130
  if not exists:
 
190
 
191
  return user_data
192
 
 
193
  def to_english_digits(text):
194
  if not text:
195
  return text
 
209
  def run_flask():
210
  app.run(host="0.0.0.0", port=7860, threaded=True)
211
 
 
212
  # --- توابع کمکی برای جلوگیری از بلاک شدن Event Loop ---
213
  def sync_save_image(image, file_name):
214
  rgb_im = image.convert('RGB')
 
229
  def sync_export_audio(audio_obj, file_name):
230
  audio_obj.export(file_name, format="mp3")
231
 
232
+ # --- ساختار کیبورد ---
 
233
  MAIN_KEYPAD_DICT = {
234
  "rows":[
235
  {
 
344
  except Exception:
345
  return False
346
 
 
347
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
348
  try:
349
  if not use_keyboard:
 
363
  return None
364
 
365
 
366
+ bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
367
+
368
+ # ==================================================================
369
+ # 🔥 تابع دانلود کاملاً ضد بمب اتم (35 بار پافشاری و کنترل 502)
370
+ # ==================================================================
371
  async def helper_download_file(client, msg_obj):
372
  errors =[]
 
373
  file_obj = None
374
+ file_id = None
375
+
376
+ # 1. استخراج آبجکت و آیدی فایل با دقت بالا
377
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
378
  val = getattr(msg_obj, attr, None)
379
  if val:
380
  file_obj = val
381
+ if hasattr(val, 'file_id'):
382
+ file_id = val.file_id
383
+ elif isinstance(val, dict) and 'file_id' in val:
384
+ file_id = val['file_id']
385
+ break
386
+
387
+ if not file_obj and hasattr(msg_obj, "file_id"):
388
+ file_id = msg_obj.file_id
389
+ file_obj = msg_obj
390
+
391
+ if not file_id:
392
+ raise Exception("خطا: هیچ فایلی در پیام یافت نشد.")
393
 
394
  temp_name = f"temp_dl_{uuid.uuid4().hex}.tmp"
395
 
396
+ # روش 1: استفاده مستقیم از API قدرتمند روبیکا با پافشاری بسیار بالا (20 بار)
397
+ for attempt in range(20):
398
  try:
399
+ url_get_file = f"https://botapi.rubika.ir/v3/{bot_token}/getFile"
400
+ payload = {"file_id": str(file_id)}
401
+ headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
402
+
403
+ async with aiohttp.ClientSession() as session:
404
+ async with session.post(url_get_file, json=payload, headers=headers, timeout=30) as resp:
405
+ if resp.status == 200:
406
+ res_data = await resp.json()
407
+ if res_data.get("status") == "OK" or res_data.get("status_det") == "OK":
408
+ download_url = res_data.get("data", {}).get("download_url") or res_data.get("data", {}).get("file_url")
409
+ if download_url:
410
+ # اگر لینک داد، 3 بار هم برای خود لینک تلاش میکنیم تا اگر قطع شد جبران شود
411
+ for dl_attempt in range(3):
412
+ try:
413
+ async with session.get(download_url, headers=headers, timeout=60) as dl_resp:
414
+ if dl_resp.status == 200:
415
+ data = await dl_resp.read()
416
+ if data and len(data) > 0:
417
+ return data
418
+ else:
419
+ errors.append(f"DL HTTP {dl_resp.status}")
420
+ await asyncio.sleep(2)
421
+ except Exception as dl_err:
422
+ errors.append(f"DL Error: {str(dl_err)[:30]}")
423
+ await asyncio.sleep(2)
424
+ else:
425
+ errors.append("No download URL in response.")
426
+ else:
427
+ errors.append(f"API Not OK: {res_data.get('status')}")
428
+ elif resp.status in[502, 503, 500]:
429
+ errors.append(f"GetFile HTTP {resp.status}")
430
+ await asyncio.sleep(3.5) # صبر بیشتر برای خطای 502
431
+ continue
432
+ else:
433
+ errors.append(f"GetFile HTTP {resp.status}")
434
+ await asyncio.sleep(2)
435
+ except Exception as e:
436
+ errors.append(f"API Error: {str(e)[:50]}")
437
+ await asyncio.sleep(2)
438
 
439
+ # روش 2: کتابخانه rubpy (بک‌آپ با 10 بار پافشاری)
440
+ if file_obj:
441
+ for attempt in range(10):
442
  try:
443
+ if hasattr(client, "download"):
444
+ result = await client.download(file_obj, save_as=temp_name)
445
+ if isinstance(result, bytes) and len(result) > 0:
446
+ return result
447
+ if os.path.exists(temp_name):
448
+ data = await asyncio.to_thread(sync_read_file, temp_name)
449
+ os.remove(temp_name)
450
+ if data and len(data) > 0:
451
+ return data
452
+ except Exception as e:
453
+ err_str = str(e)
454
+ errors.append(f"Rubpy Obj Error: {err_str[:50]}")
455
+ await asyncio.sleep(3)
456
+
457
+ # روش 3: متد قدیمی rubpy (بک‌آپ نهایی با 5 بار پافشاری)
458
+ for attempt in range(5):
459
+ try:
460
+ if hasattr(client, "download_file"):
461
  await client.download_file(file_id, file_name=temp_name)
462
  if os.path.exists(temp_name):
463
  data = await asyncio.to_thread(sync_read_file, temp_name)
464
  os.remove(temp_name)
465
+ if data and len(data) > 0:
466
+ return data
467
+ except Exception as e:
468
+ errors.append(f"Rubpy FileId Error: {str(e)[:50]}")
469
+ await asyncio.sleep(3)
470
+
471
+ raise Exception(f"سرورهای دانلود روبیکا پس از ۳۵ بار تلاش پاسخ ندادند!\nلاگ خطاها: {str(errors[-5:])}")
 
 
 
 
 
472
 
473
 
474
  GEMINI_KEYS_STR1 = os.environ.get("GEMINI_API_KEYS1", "")
 
505
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
506
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
507
 
 
 
508
 
509
+ # ==================================================================
510
+ # 🔥 تابع آپلود فایل فوق قدرتمند و ضدضربه (75 بار مقاومت در برابر 502)
511
+ # ==================================================================
512
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
513
  abs_path = os.path.abspath(file_name)
514
  error_logs =[]
515
 
516
+ # تعیین خودکار و هوشمندانه نوع فرمت ارسالی به روبیکا
517
+ api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in ["voice", "Voice", "audio"] else file_type
518
 
519
+ # روبیکا با ارسال فایل‌های ogg غیر ویس و فایل‌های mp3 به عنوان File مشکل دارد و 502 میدهد.
520
  if api_file_type == "Voice" and not abs_path.lower().endswith('.ogg'):
521
  api_file_type = "Music"
522
+ if api_file_type == "File" and abs_path.lower().endswith('.mp3'):
523
+ api_file_type = "Music"
524
 
525
+ # روش اول: استفاده مستقیم از API روبیکا با 15 بار پافشاری قدرتمند
526
+ for attempt in range(15):
527
  try:
528
  url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
529
  url_send = f"https://botapi.rubika.ir/v3/{bot_token}/sendFile"
530
 
531
  async with aiohttp.ClientSession() as session:
532
+ # 1. درخواست لینک آپلود
533
+ async with session.post(url_request, json={"type": api_file_type}, timeout=30) as resp:
534
  if resp.status != 200:
535
  error_logs.append(f"Request HTTP {resp.status}")
536
+ await asyncio.sleep(2)
537
  continue
538
 
539
  req_data = await resp.json()
 
546
  form = aiohttp.FormData()
547
  form.add_field('file', f, filename=os.path.basename(abs_path))
548
 
549
+ # 2. آپلود فایل در سرور (با تایم‌اوت 300 ثانیه مخصوص فایل‌های حجیم مثل پادکست)
550
+ async with session.post(upload_url, data=form, timeout=300) as up_resp:
551
  if up_resp.status != 200:
552
  error_logs.append(f"Upload HTTP {up_resp.status}")
553
+ if up_resp.status in[502, 503, 500]:
554
+ await asyncio.sleep(3)
555
+ else:
556
+ await asyncio.sleep(1.5)
557
  continue
558
 
559
  try:
560
  up_data = await up_resp.json()
561
  except Exception as json_err:
562
  error_logs.append(f"JSON Decode Error: {json_err}")
563
+ await asyncio.sleep(2)
564
  continue
565
 
566
+ # 3. دریافت آیدی و ارسال نهایی پیام
567
  final_file_id = None
568
  if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
569
  if "data" in up_data and isinstance(up_data["data"], dict):
 
579
  "chat_keypad_type": "New",
580
  "chat_keypad": MAIN_KEYPAD_DICT
581
  }
582
+ async with session.post(url_send, json=send_payload, timeout=30) as send_resp:
583
  if send_resp.status != 200:
584
  error_logs.append(f"Send HTTP {send_resp.status}")
585
+ await asyncio.sleep(2)
586
  continue
587
 
588
  s_data = await send_resp.json()
 
600
  error_logs.append(f"Request API Error: {req_data}")
601
  except Exception as e:
602
  error_logs.append(f"Raw HTTP Error: {str(e)[:100]}")
603
+ await asyncio.sleep(2.5)
604
 
605
+ # روش دوم: بک‌آپ قدرتمند با کتابخانه rubpy با 10 بار پافشاری
606
+ for attempt in range(10):
607
+ try:
608
+ if hasattr(client, "send_file"):
609
+ await client.send_file(chat_id, abs_path)
610
+ if caption:
611
+ await send_with_keyboard(client, chat_id, caption, True)
612
+ return True
613
+ elif hasattr(client, "send_document"):
614
+ await client.send_document(chat_id, abs_path, caption=caption)
615
+ return True
616
+ except Exception as e:
617
+ err_msg = str(e)[:100]
618
+ error_logs.append(f"Rubpy Send Error: {err_msg}")
619
+ if "502" in err_msg or "timeout" in err_msg.lower() or "time" in err_msg.lower():
620
+ await asyncio.sleep(4)
621
+ continue
622
+ else:
623
+ await asyncio.sleep(2)
624
 
625
+ return "\n".join(error_logs[-6:])
626
 
627
 
628
  WORKER_URLS =[
 
691
  history = history[-40:]
692
  if history[0]["role"] == "model": history = history[1:]
693
 
694
+ # --- سیستم پافشاری (تلاش مجدد) برای دریافت جواب از هوش مصنوعی ---
695
  final_answer = None
696
+ for attempt in range(3):
697
+ keys_to_try = get_next_gemini_keys(100)
698
+ async with aiohttp.ClientSession() as session:
699
+ for key in keys_to_try:
700
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
701
+ payload = {"contents": history, "generationConfig": {"temperature": 0.7, "maxOutputTokens": 8192}}
702
+ try:
703
+ async with session.post(url, json=payload, timeout=60) as response:
704
+ if response.status == 200:
705
+ data = await response.json()
706
+ try:
707
+ final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
708
+ break
709
+ except (KeyError, IndexError): continue
710
+ except Exception: continue
711
+ if final_answer: break
712
+ await asyncio.sleep(2)
713
 
 
714
  if not final_answer:
 
715
  if has_non_image_file:
716
  if history and history[-1]["role"] == "user": history.pop()
717
  try:
 
723
  await send_with_keyboard(client, chat_id, "❌ متأسفانه سرور اصلی در حال حاضر شلوغ است و در این شرایط **فعلاً تنها امکان تحلیل تصاویر** توسط سرور جایگزین در دسترس است.", False)
724
  return
725
 
 
726
  if HF_TOKENS:
727
  hf_messages =[]
728
  for msg in history:
 
742
  if content_parts:
743
  hf_messages.append({"role": role, "content": content_parts})
744
 
745
+ for attempt in range(3):
746
+ keys_to_try_hf = HF_TOKENS.copy()
747
+ random.shuffle(keys_to_try_hf)
748
+ async with aiohttp.ClientSession() as session:
749
+ for hf_key in keys_to_try_hf:
750
+ url = "https://router.huggingface.co/v1/chat/completions"
751
+ headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"}
752
+ payload = {
753
+ "model": "google/gemma-4-31B-it:novita",
754
+ "messages": hf_messages,
755
+ "max_tokens": 4096
756
+ }
757
+ try:
758
+ async with session.post(url, headers=headers, json=payload, timeout=60) as response:
759
+ if response.status == 200:
760
+ data = await response.json()
761
+ final_answer = data["choices"][0]["message"]["content"]
762
+ break
763
+ except Exception:
764
+ continue
765
+ if final_answer: break
766
+ await asyncio.sleep(2)
767
 
768
  try:
769
  if proc_msg:
 
777
  await send_with_keyboard(client, chat_id, "❌ تمامی سرورها شلوغ هستند. لطفاً بعداً امتحان کنید.", False)
778
  return
779
 
 
 
 
 
780
  history.append({"role": "model", "parts":[{"text": final_answer}]})
781
  user_states[chat_id]["history"] = history
782
 
 
792
  temp_text = temp_text[split_idx:].strip()
793
  if temp_text: chunks.append(temp_text)
794
 
795
+ success_sent = False
796
  for idx, chunk in enumerate(chunks):
797
  if idx != len(chunks) - 1: chunk += "\n\n⏳ *(ادامه در پیام بعدی)...* 👇"
798
  try:
799
+ res = await send_with_keyboard(client, chat_id, chunk, False)
800
+ if res: success_sent = True
801
  await asyncio.sleep(2.5)
802
  except Exception: await asyncio.sleep(2.5)
803
+
804
+ # --- کسر اعتبار فقط در صورتی که حداقل یک بخش از پیام موفقیت‌آمیز ارسال شده باشد ---
805
+ if success_sent and not creds.get("is_premium"):
806
+ user_credits_db[str_chat_id]["chat"] -= 1
807
+ save_db(user_credits_db)
808
 
809
  except Exception:
810
  await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
 
864
  if msg_id: await client.delete_messages(chat_id,[msg_id])
865
  except Exception: pass
866
 
 
867
  w, h = 1024, 1024
868
  size_name = "مربع (1:1) ⬛"
869
  if size_choice == "2":
 
879
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
880
  proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی عکس...\n\n📏 **ابعاد:** {size_name}\n📝 **پرامپت ساخته شده:**\n`{short_preview}`\n\n(ممکن است چند ثانیه زمان ببرد)", False)
881
 
882
+ # --- سیستم پافشاری برای ساخت عکس ---
 
883
  generated_image = None
884
  last_error_log = "هیچ اتصالی برقرار نشد."
885
 
886
+ for attempt in range(5):
887
+ keys_to_try = HF_TOKENS.copy()
888
+ random.shuffle(keys_to_try)
889
+ for token in keys_to_try:
890
+ try:
891
+ hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
892
+ generated_image = await hf_client.text_to_image(
893
+ enhanced_prompt,
894
+ model="Tongyi-MAI/Z-Image-Turbo",
895
+ width=w,
896
+ height=h
897
+ )
898
+ break
899
+ except Exception as e:
900
+ last_error_log = str(e)
901
+ continue
902
+ if generated_image: break
903
+ await asyncio.sleep(2)
904
 
905
  try:
906
  if proc_msg:
 
909
  if msg_id: await client.delete_messages(chat_id,[msg_id])
910
  except Exception: pass
911
 
912
+ if not generated_image:
913
+ return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
 
 
914
 
915
  try:
916
  file_name = f"image_{uuid.uuid4().hex}.jpg"
 
917
  await asyncio.to_thread(sync_save_image, generated_image, file_name)
918
  await asyncio.sleep(1)
919
  caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n📏 ابعاد تصویر: {size_name}\n✨ ایده اولیه: {prompt}"
920
+
921
+ # آپلود قدرتمند با 3 بار تلاش مجدد بیرونی
922
+ upload_result = False
923
+ error_log_img = ""
924
+ for up_att in range(3):
925
+ res = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
926
+ if res is True:
927
+ upload_result = True
928
+ break
929
+ else:
930
+ error_log_img = res
931
+ await asyncio.sleep(4)
932
+
933
+ # --- کسر اعتبار فقط در صورت موفقیت کامل آپلود به کاربر ---
934
+ if upload_result is True:
935
+ if not creds.get("is_premium"):
936
+ user_credits_db[str_chat_id]["image"] -= 1
937
+ save_db(user_credits_db)
938
+ else:
939
+ await send_with_keyboard(client, chat_id, f"❌ عکس ساخته شد اما آپلود در روبیکا با خطا مواجه شد:\n`{str(error_log_img)[:800]}`", True)
940
+
941
  if os.path.exists(file_name): os.remove(file_name)
942
+ except Exception as e:
943
+ await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره و ارسال عکس:\n{str(e)[:150]}", True)
944
 
945
 
946
  async def translate_text_aloha(prompt_text):
 
992
  if not translated_prompt or translated_prompt.strip() == "":
993
  translated_prompt = prompt
994
 
995
+ # --- سیستم پافشاری برای ویرایش عکس ---
 
996
  generated_image = None
997
  last_error_log = "سرور پاسخ نداد."
998
 
999
+ for attempt in range(5):
1000
+ keys_to_try = HF_TOKENS.copy()
1001
+ random.shuffle(keys_to_try)
1002
+ for token in keys_to_try:
1003
+ try:
1004
+ hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
1005
+ generated_image = await hf_client.image_to_image(
1006
+ image_bytes,
1007
+ prompt=translated_prompt,
1008
+ model="black-forest-labs/FLUX.2-dev"
1009
+ )
1010
+ break
1011
+ except Exception as e:
1012
+ last_error_log = str(e)
1013
+ continue
1014
+ if generated_image: break
1015
+ await asyncio.sleep(2)
1016
 
1017
  try:
1018
  if proc_msg:
1019
  msg_id = getattr(proc_msg, 'message_id', None)
1020
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1021
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
1022
  except Exception: pass
1023
 
1024
  if not generated_image:
1025
  return await send_with_keyboard(client, chat_id, f"❌ متأسفانه ویرایش عکس انجام نشد.\n\n⚠️ علت:\n{last_error_log[:200]}", True)
1026
 
 
 
 
1027
  try:
1028
  file_name = f"edited_flux_{uuid.uuid4().hex}.jpg"
 
1029
  await asyncio.to_thread(sync_save_image, generated_image, file_name)
1030
  await asyncio.sleep(1)
1031
  caption_text = f"🪄 ویرایش عکس با هوش مصنوعی Flux.2 انجام شد!\n\n✨ تغییرات خواسته شده: {prompt}\n🔤 متن ارسال شده به هوش: {translated_prompt}"
1032
+
1033
+ upload_result = False
1034
+ error_log_edit = ""
1035
+ for up_att in range(3):
1036
+ res = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
1037
+ if res is True:
1038
+ upload_result = True
1039
+ break
1040
+ else:
1041
+ error_log_edit = res
1042
+ await asyncio.sleep(4)
1043
+
1044
+ # --- کسر اعتبار فقط پس از تحویل موفقیت آمیز ---
1045
+ if upload_result is True:
1046
+ if not creds.get("is_premium"):
1047
+ user_credits_db[str_chat_id]["edit_image"] -= 1
1048
+ save_db(user_credits_db)
1049
+ else:
1050
+ await send_with_keyboard(client, chat_id, f"❌ عکس ساخته شد اما خطا در ارسال به روبیکا رخ داد:\n`{str(error_log_edit)[:800]}`", True)
1051
+
1052
  if os.path.exists(file_name): os.remove(file_name)
1053
  except Exception as e:
1054
  await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
 
1064
  proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
1065
  payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
1066
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
1067
+
1068
+ # --- سیستم پافشاری برای ساخت صدا ---
1069
  audio_bytes = None
1070
  last_error = "پاسخی دریافت نشد"
 
 
1071
 
1072
+ for attempt in range(10):
1073
+ workers = WORKER_URLS.copy()
1074
+ random.shuffle(workers)
1075
+ async with aiohttp.ClientSession(headers=headers, timeout=aiohttp.ClientTimeout(total=600)) as session:
1076
+ for worker_url in workers[:3]:
1077
+ try:
1078
+ async with session.post(worker_url, json=payload) as response:
1079
+ if response.status == 200:
1080
+ content_type = response.headers.get('Content-Type', '')
1081
+ if 'audio' in content_type or response.content_length > 1000:
1082
+ audio_bytes = await response.read()
1083
+ break
1084
+ else: last_error = "فایل نامعتبر"
1085
+ else: last_error = f"ارور ({response.status})"
1086
+ except Exception as e:
1087
+ last_error = f"خطا: {str(e)}"
1088
+ continue
1089
+ if audio_bytes: break
1090
+ await asyncio.sleep(2)
1091
 
1092
  try:
1093
  if proc_msg:
 
1097
  except Exception: pass
1098
 
1099
  if audio_bytes:
 
 
 
 
1100
  file_name_mp3 = f"audio_{uuid.uuid4().hex}.mp3"
 
 
1101
  await asyncio.to_thread(sync_write_file, file_name_mp3, audio_bytes)
 
1102
  await asyncio.sleep(1)
 
1103
 
1104
+ # ارسال با فرمت Music و مقاومت ۳ برابری
1105
+ upload_result_file = False
1106
+ error_log_tts = ""
1107
+ for up_att in range(3):
1108
+ # 🔴 ارسال متن به صدا بصورت موزیک 🔴
1109
+ res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", "✅ صدای شما با موفقیت آماده شد (فایل MP3):")
1110
+ if res is True:
1111
+ upload_result_file = True
1112
+ break
1113
+ else:
1114
+ error_log_tts = res
1115
+ await asyncio.sleep(4)
1116
+
1117
+ # --- کسر اعتبار فقط پس از تحویل موفق فایل ---
1118
+ if upload_result_file is True:
1119
+ if not creds.get("is_premium"):
1120
+ user_credits_db[str_chat_id]["tts"] -= 1
1121
+ save_db(user_credits_db)
1122
+ else:
1123
+ await send_with_keyboard(client, chat_id, f"❌ فایل صدا ساخته شد اما سرور روبیکا اجازه آپلود نداد:\n`{str(error_log_tts)[:800]}`", True)
1124
 
1125
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
1126
+ else:
1127
+ await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
1128
  except Exception: traceback.print_exc()
1129
 
1130
 
 
1182
  for index, turn in enumerate(script_data):
1183
  payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
1184
  chunk_audio_bytes = None
1185
+
1186
+ # --- پافشاری قدرتمندتر برای دریافت هر بخش پادکست ---
1187
+ for attempt in range(10):
1188
  try:
1189
  async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
1190
  if resp.status == 200:
 
1192
  break
1193
  except Exception: await asyncio.sleep(2)
1194
 
1195
+ if not chunk_audio_bytes:
1196
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} از سرور. عملیات متوقف شد.", True)
1197
 
1198
  try:
 
1199
  combined_audio = await asyncio.to_thread(sync_combine_audio, combined_audio, chunk_audio_bytes)
1200
+ except Exception as e:
1201
+ return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
 
 
 
1202
 
1203
  file_name_mp3 = f"final_podcast_{uuid.uuid4().hex}.mp3"
 
1204
  await asyncio.to_thread(sync_export_audio, combined_audio, file_name_mp3)
1205
 
1206
  try:
 
1211
  except: pass
1212
 
1213
  caption_file = f"🎧 فایل پادکست شما با فرمت MP3:\n\n💡 موضوع شما: {prompt}"
 
1214
 
1215
+ # ⚠️ آپلود بسیار قدرتمند پادکست (ارسال با هویت Music برای جلوگیری از 502)
1216
+ # همراه با 3 مرحله تکرار خارجی، که خود شامل 15 + 10 بار تکرار داخلی است!
1217
+ upload_result_file = False
1218
+ error_log_pod = ""
1219
+ for up_att in range(3):
1220
+ # 🔴 ارسال پادکست بصورت موزیک 🔴
1221
+ res = await helper_upload_file(client, chat_id, file_name_mp3, "Music", caption_file)
1222
+ if res is True:
1223
+ upload_result_file = True
1224
+ break
1225
+ else:
1226
+ error_log_pod = res
1227
+ await asyncio.sleep(5)
1228
+
1229
+ # --- کسر اعتبار فقط در صورتی که فایل پادکست با موفقیت در روبیکا آپلود شده باشد ---
1230
+ if upload_result_file is True:
1231
+ if not creds.get("is_premium"):
1232
+ user_credits_db[str_chat_id]["podcast"] -= 1
1233
+ save_db(user_credits_db)
1234
+ else:
1235
+ await send_with_keyboard(client, chat_id, f"❌ پادکست ساخته شد اما روبیکا پس از ده‌ها تلاش خطای آپلود داد.\n\n`{str(error_log_pod)[:800]}`", True)
1236
 
1237
  if os.path.exists(file_name_mp3): os.remove(file_name_mp3)
1238
 
 
1250
  mime_type, _ = mimetypes.guess_type(file_name)
1251
  if not mime_type: mime_type = "audio/ogg"
1252
 
 
 
1253
  prompt = "لطفاً این فایل صوتی/تصویری را با دقت کامل گوش بده و صحبت‌های داخل آن را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
1254
 
1255
+ # --- سیستم پافشاری برای تبدیل صدا به متن ---
1256
+ transcribed_text = None
1257
+ for attempt in range(5):
1258
+ keys_to_try = get_next_gemini_keys(100)
1259
+ async with aiohttp.ClientSession() as session:
1260
+ for key in keys_to_try:
1261
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
1262
+ payload = {"contents":[{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.2}}
1263
+ try:
1264
+ async with session.post(url, json=payload, timeout=60) as response:
1265
+ if response.status == 200:
1266
+ data = await response.json()
1267
+ transcribed_text = data["candidates"][0]["content"]["parts"][0]["text"]
1268
+ break
1269
+ except Exception: continue
1270
+ if transcribed_text: break
1271
+ await asyncio.sleep(2)
1272
 
1273
  try:
1274
  if proc_msg:
 
1278
  except Exception: pass
1279
 
1280
  if transcribed_text:
1281
+ sent = await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
1282
+ # --- کسر اعتبار فقط اگر پیام با موفقیت در روبیکا ارسال شد ---
1283
+ if sent and not creds.get("is_premium"):
1284
  user_credits_db[str_chat_id]["stt"] -= 1
1285
  save_db(user_credits_db)
 
1286
  else:
1287
  await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", True)
1288
 
 
1310
 
1311
  is_image = mime_type.startswith('image/')
1312
 
1313
+ # --- سیستم پافشاری برای تحلیل فایل ---
1314
  final_answer = None
1315
+ for attempt in range(5):
1316
+ keys_to_try = get_next_gemini_keys(100)
1317
+ async with aiohttp.ClientSession() as session:
1318
+ for key in keys_to_try:
1319
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
1320
+ payload = {"contents": [{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.6}}
1321
+ try:
1322
+ async with session.post(url, json=payload, timeout=45) as response:
1323
+ if response.status == 200:
1324
+ data = await response.json()
1325
+ final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
1326
+ break
1327
+ except Exception: continue
1328
+ if final_answer: break
1329
+ await asyncio.sleep(2)
1330
 
 
1331
  if not final_answer:
1332
  if is_image and HF_TOKENS:
1333
+ for attempt in range(5):
1334
+ keys_to_try_hf = HF_TOKENS.copy()
1335
+ random.shuffle(keys_to_try_hf)
1336
+
1337
+ hf_messages =[
1338
+ {
1339
+ "role": "user",
1340
+ "content":[
1341
+ {"type": "text", "text": prompt},
1342
+ {
1343
+ "type": "image_url",
1344
+ "image_url": {"url": f"data:{mime_type};base64,{base64_data}"}
1345
+ }
1346
+ ]
 
 
 
 
 
 
 
 
 
 
1347
  }
1348
+ ]
1349
+
1350
+ async with aiohttp.ClientSession() as session:
1351
+ for hf_key in keys_to_try_hf:
1352
+ url = "https://router.huggingface.co/v1/chat/completions"
1353
+ headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"}
1354
+ payload = {
1355
+ "model": "google/gemma-4-31B-it:novita",
1356
+ "messages": hf_messages,
1357
+ "max_tokens": 4096
1358
+ }
1359
+ try:
1360
+ async with session.post(url, headers=headers, json=payload, timeout=60) as response:
1361
+ if response.status == 200:
1362
+ data = await response.json()
1363
+ final_answer = data["choices"][0]["message"]["content"]
1364
+ break
1365
+ except Exception:
1366
+ continue
1367
+ if final_answer: break
1368
+ await asyncio.sleep(2)
1369
 
1370
  try:
1371
  if proc_msg:
1372
  msg_id = getattr(proc_msg, 'message_id', None)
1373
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1374
+ if msg_id: await client.delete_messages(chat_id,[msg_id])
1375
  except Exception: pass
1376
 
1377
  if final_answer:
1378
+ sent = await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
1379
+ # --- کسر اعتبار فقط پس از ارسال موفقیت‌آمیز نتیجه ---
1380
+ if sent and not creds.get("is_premium"):
1381
  user_credits_db[str_chat_id]["file"] -= 1
1382
  save_db(user_credits_db)
 
1383
  else:
1384
  if not is_image:
1385
  await send_with_keyboard(client, chat_id, "❌ متأسفانه سرور اصلی در حال حاضر شلوغ است و در این شرایط **فعلاً تنها امکان تحلیل تصاویر** توسط سرور جایگزین در دسترس است.", True)
 
1399
  proc_msg = await send_with_keyboard(client, chat_id, "✍️ در حال تحقیق و نگارش مقاله جامع و حرفه‌ای...\n(این عملیات با توجه به طولانی بودن متن ممکن است کمی طول بکشد)", False)
1400
 
1401
  ai_prompt = f"یک مقاله بسیار جامع، کاملا حرفه‌ای، بسیار طولانی و با جزئیات کامل درباره موضوع زیر به زبان فارسی بنویس. فقط متن مقاله را بده و هیچ توضیح اضافه‌ای نده:\n\nموضوع: {topic}"
1402
+
1403
+ # --- سیستم پافشاری برای تولید متن مقاله ---
1404
  article_text = None
1405
+ for attempt in range(5):
1406
+ keys_to_try = get_next_gemini_keys(100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1407
  async with aiohttp.ClientSession() as session:
1408
+ for key in keys_to_try:
1409
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
1410
+ payload = {"contents":[{"parts":[{"text": ai_prompt}]}], "generationConfig": {"temperature": 0.7, "maxOutputTokens": 8192}}
 
 
 
 
 
1411
  try:
1412
+ async with session.post(url, json=payload, timeout=60) as response:
1413
  if response.status == 200:
1414
  data = await response.json()
1415
+ article_text = data["candidates"][0]["content"]["parts"][0]["text"]
1416
  break
1417
  except Exception: continue
1418
+ if article_text: break
1419
+ await asyncio.sleep(2)
1420
+
1421
+ if not article_text and HF_TOKENS:
1422
+ for attempt in range(5):
1423
+ keys_to_try_hf = HF_TOKENS.copy()
1424
+ random.shuffle(keys_to_try_hf)
1425
+ async with aiohttp.ClientSession() as session:
1426
+ for hf_key in keys_to_try_hf:
1427
+ url = "https://router.huggingface.co/v1/chat/completions"
1428
+ headers = {"Authorization": f"Bearer {hf_key}", "Content-Type": "application/json"}
1429
+ payload = {
1430
+ "model": "google/gemma-4-31B-it:novita",
1431
+ "messages":[{"role": "user", "content":[{"type": "text", "text": ai_prompt}]}],
1432
+ "max_tokens": 4096
1433
+ }
1434
+ try:
1435
+ async with session.post(url, headers=headers, json=payload, timeout=60) as response:
1436
+ if response.status == 200:
1437
+ data = await response.json()
1438
+ article_text = data["choices"][0]["message"]["content"]
1439
+ break
1440
+ except Exception: continue
1441
+ if article_text: break
1442
+ await asyncio.sleep(2)
1443
 
1444
  if not article_text:
1445
  return await send_with_keyboard(client, chat_id, "❌ سرور تولید متن شلوغ است، لطفاً بعداً تلاش کنید.", True)
 
1451
  if msg_id: await client.delete_messages(chat_id,[msg_id])
1452
  except Exception: pass
1453
 
1454
+ proc_msg = await send_with_keyboard(client, chat_id, "📄 مقاله با موفقیت نوشته شد! در حال ارتباط با سرور و تبدیل به فایل‌های PDF و Word...\n(لطفا صبور باشید)", False)
1455
+
1456
  converter_url = "https://opera8-texttopdf.hf.space/"
1457
  uid = uuid.uuid4().hex
 
 
 
1458
 
1459
+ # =======================================================================
1460
+ # ---- 1. پردازش و ارسال فایل PDF (دونه دونه و با پافشاری شدید) ----
1461
+ # =======================================================================
1462
+ pdf_success = False
1463
+ for attempt in range(30): # تا 30 بار تلاش (تقریبا ول کن نیست)
1464
+ try:
1465
+ pdf_bytes = None
1466
+ async with aiohttp.ClientSession() as session:
1467
+ form_data = aiohttp.FormData()
1468
+ form_data.add_field('content', article_text)
1469
+ form_data.add_field('format', 'pdf')
1470
+ async with session.post(converter_url, data=form_data, timeout=90) as resp:
1471
+ if resp.status == 200:
1472
+ data_bytes = await resp.read()
1473
+ if data_bytes and len(data_bytes) > 100:
1474
+ pdf_bytes = data_bytes
1475
+
1476
+ if pdf_bytes:
1477
+ filename = f"Article_{uid}.pdf"
1478
+ await asyncio.to_thread(sync_write_file, filename, pdf_bytes)
1479
+
1480
+ res_upload = False
1481
+ for _ in range(3):
1482
+ r = await helper_upload_file(client, chat_id, filename, "File", f"📄 فایل PDF مقاله شما:\n\n💡 موضوع: {topic}")
1483
+ if r is True:
1484
+ res_upload = True
1485
+ break
1486
+ await asyncio.sleep(4)
1487
+
1488
+ if os.path.exists(filename):
1489
+ os.remove(filename)
1490
+
1491
+ if res_upload is True:
1492
+ pdf_success = True
1493
+ break # با موفقیت ساخته و ارسال شد -> خروج از حلقه
1494
+ except Exception as e:
1495
+ print(f"PDF error (attempt {attempt+1}):", e)
1496
+
1497
+ await asyncio.sleep(4) # در صورت خطا 4 ثانیه مکث و تلاش مجدد
1498
+
1499
+ # =======================================================================
1500
+ # استراحت بین دو فایل برای هضم شدن فایل قبلی توسط سیستم روبیکا
1501
+ # =======================================================================
1502
+ await asyncio.sleep(3.5)
1503
+
1504
+ # =======================================================================
1505
+ # ---- 2. پردازش و ارسال فایل Word (دونه دونه و با پافشاری شدید) ----
1506
+ # =======================================================================
1507
+ docx_success = False
1508
+ for attempt in range(30): # تا 30 بار تلاش
1509
+ try:
1510
+ docx_bytes = None
1511
+ async with aiohttp.ClientSession() as session:
1512
+ form_data = aiohttp.FormData()
1513
+ form_data.add_field('content', article_text)
1514
+ form_data.add_field('format', 'docx')
1515
+ async with session.post(converter_url, data=form_data, timeout=90) as resp:
1516
+ if resp.status == 200:
1517
+ data_bytes = await resp.read()
1518
+ if data_bytes and len(data_bytes) > 100:
1519
+ docx_bytes = data_bytes
1520
+
1521
+ if docx_bytes:
1522
+ filename = f"Article_{uid}.docx"
1523
+ await asyncio.to_thread(sync_write_file, filename, docx_bytes)
1524
+
1525
+ res_upload = False
1526
+ for _ in range(3):
1527
+ r = await helper_upload_file(client, chat_id, filename, "File", f"📝 فایل Word (DOCX) مقاله شما:\n\n💡 موضوع: {topic}")
1528
+ if r is True:
1529
+ res_upload = True
1530
+ break
1531
+ await asyncio.sleep(4)
1532
+
1533
+ if os.path.exists(filename):
1534
+ os.remove(filename)
1535
+
1536
+ if res_upload is True:
1537
+ docx_success = True
1538
+ break # با موفقیت ساخته و ارسال شد -> خروج از حلقه
1539
+ except Exception as e:
1540
+ print(f"DOCX error (attempt {attempt+1}):", e)
1541
+
1542
+ await asyncio.sleep(4) # در صورت خطا 4 ثانیه مکث و تلاش مجدد
1543
 
 
 
 
 
 
 
1544
  try:
1545
  if proc_msg:
1546
  msg_id = getattr(proc_msg, 'message_id', None)
1547
  if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
1548
+ if msg_id: await client.delete_messages(chat_id,[msg_id])
1549
  except Exception: pass
1550
 
1551
+ # --- کسر اعتبار فقط در صورتی که حداقل یکی از فایل‌ها با موفقیت کامل آپلود شده باشد ---
1552
+ if pdf_success and docx_success:
1553
+ if not creds.get("is_premium"):
1554
+ user_credits_db[str_chat_id]["chat"] -= 1
1555
+ save_db(user_credits_db)
1556
+ await send_with_keyboard(client, chat_id, "✅ مقاله شما با موفقیت به صورت هر دو فایل (PDF و Word) تحویل داده شد!", True)
1557
+ elif pdf_success or docx_success:
1558
+ if not creds.get("is_premium"):
1559
+ user_credits_db[str_chat_id]["chat"] -= 1
1560
+ save_db(user_credits_db)
1561
+ await send_with_keyboard(client, chat_id, "⚠️ یکی از فایل‌ها با موفقیت ارسال شد اما دیگری پس از تلاش‌های مکرر سرور با خطا مواجه شد.", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1562
  else:
1563
+ await send_with_keyboard(client, chat_id, "❌ متأسفانه پس از تلاش‌های مکرر، سرور قادر به ساخت و ارسال هیچیک از فایل‌ها نبود و اعتباری از شما کسر نشد.", True)
1564
 
1565
 
1566
  if not bot_token:
 
1920
  🔑 **شناسه یکتای شما:** `{chat_id}`
1921
 
1922
  👨‍💻 **ارتباط با پشتیبانی:**
1923
+ 🆔 @H_a_m_e_d100"""
1924
  await send_with_keyboard(client, chat_id, buy_text, True)
1925
  return
1926
 
 
1934
  🔑 **شناسه یکتای ربات شما:** `{chat_id}`
1935
 
1936
  👨‍💻 **دقت کنید شناسه ربات رو به پشتیبانی داخل خود برنامه هوش مصنوعی آلفا ارسال کنید:**
1937
+ 🆔 @H_a_m_e_d100"""
1938
  await send_with_keyboard(client, chat_id, transfer_text, True)
1939
  return
1940
 
 
1993
  # ===============================================
1994
  elif current_mode == "waiting_for_referral_code":
1995
  if user_text_str:
 
1996
  normalized_code = to_english_digits(user_text_str).strip()
1997
 
1998
  if not normalized_code.isdigit() or len(normalized_code) != 8:
 
2011
 
2012
  user_states[str_chat_id]["mode"] = None
2013
 
 
2014
  user_credits_db[str_chat_id]["used_referral"] = True
2015
  user_credits_db[str_chat_id]["tts"] = user_credits_db[str_chat_id].get("tts", 0) + 10
2016
 
 
2017
  user_credits_db[inviter_id]["invited_count"] = user_credits_db[inviter_id].get("invited_count", 0) + 1
2018
  current_invites = user_credits_db[inviter_id]["invited_count"]
2019
 
 
2058
  await send_with_keyboard(client, chat_id, "✅ کد هدیه با موفقیت ثبت شد!\n🎁 **10 سهمیه تبدیل رایگان متن به صدا** به حساب شما اضافه گردید.", True)
2059
  return
2060
 
2061
+ # ===============================================
2062
+ # آپدیت جدید: در چت ابتدا فایل را می‌گیرد و می‌پرسد چه کار کند
2063
+ # ===============================================
2064
  elif current_mode == "chat":
2065
  if is_file:
2066
  await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
2067
  try:
2068
  file_bytes = await helper_download_file(client, msg_obj)
2069
+ user_states[str_chat_id]["file_bytes"] = file_bytes
2070
+ user_states[str_chat_id]["file_name"] = file_name
2071
+ user_states[str_chat_id]["mode"] = "chat_waiting_for_prompt"
2072
+ await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بفرستید و بگویید **چه کاری با این فایل انجام دهم؟**\n(مثلاً: این تصویر را توصیف کن، یا متن این سند را خلاصه کن)", False)
2073
+ except Exception as dl_err:
2074
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
2075
+ elif user_text_str:
2076
+ asyncio.create_task(process_gemini(client, chat_id, user_text_str))
2077
+ return
2078
+
2079
+ elif current_mode == "chat_waiting_for_prompt":
2080
+ if user_text_str:
2081
+ saved_bytes = user_states[str_chat_id].get("file_bytes")
2082
+ saved_name = user_states[str_chat_id].get("file_name", "file.jpeg")
2083
+ user_states[str_chat_id]["mode"] = "chat"
2084
+ user_states[str_chat_id]["file_bytes"] = None
2085
+ asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=saved_bytes, file_name=saved_name))
2086
+ else:
2087
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
2088
  return
2089
 
2090
  # ===============================================
2091
+ # دریافت سایز قبل از ساخت عکس
2092
  # ===============================================
2093
  elif current_mode == "image_waiting_for_text":
2094
  if user_text_str:
 
2219
  threading.Thread(target=run_flask, daemon=True).start()
2220
 
2221
  if bot_token:
 
2222
  loop = asyncio.get_event_loop()
 
2223
  loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=16))
2224
 
2225
+ print("ربات آلفا پرو با سیستم اشتراک نامحدود + سپر امنیتی + دانلود پیشرفته فایل + 16 Worker پس‌زمینه روشن شد...")
2226
  bot.run()