Opera8 commited on
Commit
7eeb5f9
·
verified ·
1 Parent(s): 51ef11a

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +521 -220
main.py CHANGED
@@ -23,18 +23,19 @@ from pydub import AudioSegment
23
  # --- کد مدیریت برای ارتقای کاربران بدون نیاز به لاگین ---
24
  ADMIN_CODE = "3011"
25
 
26
- # متغیر سراسری برای ذخیره آیدی خود ربات
27
  BOT_GUID = None
28
 
29
  # =======================================================
30
  # 🔥 سیستم کنترل سرعت و ضد رگبار (Burst Controller) 🔥
 
31
  # =======================================================
32
  global_burst_count = 0
33
  global_burst_time = time.time()
34
  burst_lock = threading.Lock()
35
 
36
  def is_backlog_burst():
37
- """اگر بیشتر از 5 پیام در 1 ثانیه بیاید، یعنی رگبار است و رد می‌شود"""
38
  global global_burst_count, global_burst_time
39
  with burst_lock:
40
  now = time.time()
@@ -51,10 +52,10 @@ def is_backlog_burst():
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
- # --- الگوریتم تبدیل تاریخ میلادی به شمسی ---
58
  def gregorian_to_jalali(gy, gm, gd):
59
  g_d_m =[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
60
  gy2 = (gy + 1) if (gm > 2) else gy
@@ -78,6 +79,7 @@ def load_db():
78
  print("در حال تلاش برای خواندن دیتابیس کاربران از دیتاست هاگینگ فیس...")
79
  if HF_TOKEN_DB:
80
  try:
 
81
  file_path = hf_hub_download(
82
  repo_id=DATASET_REPO,
83
  filename=DB_FILE,
@@ -88,8 +90,9 @@ def load_db():
88
  print("✅ دیتابیس با موفقیت از دیتاست هاگینگ فیس لود شد.")
89
  return json.load(f)
90
  except Exception as e:
91
- print("⚠️ فایل دیتابیس در هاگینگ فیس یافت نشد یا خطایی رخ داد (ساخت دیتابیس جدید).")
92
 
 
93
  if os.path.exists(DB_FILE):
94
  try:
95
  with open(DB_FILE, "r", encoding="utf-8") as f:
@@ -99,6 +102,7 @@ def load_db():
99
  return {}
100
 
101
  def _upload_db_background():
 
102
  if HF_TOKEN_DB:
103
  api = HfApi(token=HF_TOKEN_DB)
104
  try:
@@ -108,13 +112,17 @@ def _upload_db_background():
108
  repo_id=DATASET_REPO,
109
  repo_type="dataset"
110
  )
111
- except Exception: pass
 
112
 
113
  def save_db(db_data):
114
  with db_lock:
115
  try:
 
116
  with open(DB_FILE, "w", encoding="utf-8") as f:
117
  json.dump(db_data, f, ensure_ascii=False, indent=4)
 
 
118
  threading.Thread(target=_upload_db_background, daemon=True).start()
119
  except Exception as e:
120
  print("خطا در ذخیره دیتابیس:", e)
@@ -122,10 +130,12 @@ def save_db(db_data):
122
  user_credits_db = load_db()
123
 
124
  def get_user_credits(chat_id):
 
125
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
126
  today_str = datetime.date.today().isoformat()
127
 
128
  if str_chat_id not in user_credits_db:
 
129
  user_credits_db[str_chat_id] = {
130
  "is_premium": False,
131
  "expire_date": None,
@@ -143,19 +153,23 @@ def get_user_credits(chat_id):
143
  user_data = user_credits_db[str_chat_id]
144
  is_premium = user_data.get("is_premium", False)
145
 
 
146
  if is_premium and user_data.get("expire_date"):
147
  try:
148
  expire_date = datetime.datetime.fromisoformat(user_data["expire_date"])
149
  if datetime.datetime.now() > expire_date:
 
150
  user_data["is_premium"] = False
151
  user_data["expire_date"] = None
152
  is_premium = False
153
  except Exception:
154
  pass
155
 
 
156
  if not is_premium:
157
  if user_data.get("last_reset") != today_str:
158
  user_data["last_reset"] = today_str
 
159
  user_data["chat"] = 10
160
  user_data["image"] = 5
161
  user_data["edit_image"] = 1
@@ -167,8 +181,10 @@ def get_user_credits(chat_id):
167
 
168
  return user_data
169
 
 
170
  def to_english_digits(text):
171
- if not text: return text
 
172
  persian_digits = '۰۱۲۳۴۵۶۷۸۹'
173
  arabic_digits = '٠١٢٣٤٥٦٧٨٩'
174
  english_digits = '0123456789'
@@ -180,13 +196,13 @@ app = Flask(__name__)
180
 
181
  @app.route('/')
182
  def home():
183
- return "ربات آلفا پرو فعال است 🚀"
184
 
185
  def run_flask():
186
  app.run(host="0.0.0.0", port=7860)
187
 
188
 
189
- # --- کیبورد ---
190
  MAIN_KEYPAD_DICT = {
191
  "rows": [
192
  {
@@ -232,6 +248,7 @@ MAIN_KEYPAD_DICT = {
232
  "resize_keyboard": True
233
  }
234
 
 
235
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
236
  try:
237
  if not use_keyboard:
@@ -250,6 +267,8 @@ async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
250
  except Exception:
251
  return None
252
 
 
 
253
  async def helper_download_file(client, msg_obj):
254
  errors =[]
255
  file_id = None
@@ -262,6 +281,7 @@ async def helper_download_file(client, msg_obj):
262
  elif isinstance(val, dict) and 'file_id' in val: file_id = val['file_id']
263
  if file_id: break
264
 
 
265
  temp_name = f"temp_dl_{uuid.uuid4().hex}.tmp"
266
 
267
  if hasattr(client, "download_file"):
@@ -271,7 +291,7 @@ async def helper_download_file(client, msg_obj):
271
  with open(temp_name, 'rb') as f: data = f.read()
272
  os.remove(temp_name)
273
  return data
274
- except Exception as e: errors.append(f"Err1: {e}")
275
 
276
  if file_obj:
277
  try:
@@ -280,7 +300,7 @@ async def helper_download_file(client, msg_obj):
280
  with open(temp_name, 'rb') as f: data = f.read()
281
  os.remove(temp_name)
282
  return data
283
- except Exception as e: errors.append(f"Err2: {e}")
284
 
285
  if file_id:
286
  try:
@@ -289,7 +309,7 @@ async def helper_download_file(client, msg_obj):
289
  with open(temp_name, 'rb') as f: data = f.read()
290
  os.remove(temp_name)
291
  return data
292
- except Exception as e: errors.append(f"Err3: {e}")
293
 
294
  try:
295
  await client.download_file(file_id, save_as=temp_name)
@@ -297,41 +317,63 @@ async def helper_download_file(client, msg_obj):
297
  with open(temp_name, 'rb') as f: data = f.read()
298
  os.remove(temp_name)
299
  return data
300
- except Exception as e: errors.append(f"Err4: {e}")
301
 
302
  raise Exception("دانلود ناموفق. لاگ:\n" + "\n".join(errors))
303
 
 
 
304
  GEMINI_KEYS_STR1 = os.environ.get("GEMINI_API_KEYS1", "")
305
  GEMINI_KEYS_STR2 = os.environ.get("GEMINI_API_KEYS2", "")
306
 
307
  _raw_keys =[]
308
- if GEMINI_KEYS_STR1: _raw_keys.extend(GEMINI_KEYS_STR1.split(","))
309
- if GEMINI_KEYS_STR2: _raw_keys.extend(GEMINI_KEYS_STR2.split(","))
 
 
 
 
310
  GEMINI_KEYS = list(set([k.strip() for k in _raw_keys if k.strip()]))
 
311
 
 
312
  current_gemini_key_index = 0
313
  gemini_key_lock = threading.Lock()
314
 
315
  def get_next_gemini_keys(count=100):
 
 
 
 
 
 
316
  global current_gemini_key_index
317
  with gemini_key_lock:
318
  total_keys = len(GEMINI_KEYS)
319
- if total_keys == 0: return[]
 
 
320
  actual_count = min(count, total_keys)
321
  selected_keys =[]
 
322
  for _ in range(actual_count):
323
  selected_keys.append(GEMINI_KEYS[current_gemini_key_index])
324
  current_gemini_key_index = (current_gemini_key_index + 1) % total_keys
 
325
  return selected_keys
326
 
327
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
328
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
 
329
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
330
 
 
 
331
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
332
  abs_path = os.path.abspath(file_name)
333
  error_logs =[]
334
- api_file_type = "Image" if file_type in["photo", "Image", "image"] else "Voice" if file_type in["voice", "Voice", "audio"] else "File"
 
335
 
336
  try:
337
  url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
@@ -340,17 +382,21 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
340
  async with aiohttp.ClientSession() as session:
341
  async with session.post(url_request, json={"type": api_file_type}, timeout=20) as resp:
342
  req_data = await resp.json()
 
343
  if req_data.get("status") == "OK":
344
  data_dict = req_data.get("data", {})
345
  upload_url = data_dict.get("upload_url")
346
  file_id = data_dict.get("id") or data_dict.get("file_id")
 
347
  if upload_url and file_id:
348
  with open(abs_path, "rb") as f:
349
  form = aiohttp.FormData()
350
  form.add_field('file', f, filename=os.path.basename(abs_path))
351
  headers = {"file-id": str(file_id)}
 
352
  async with session.post(upload_url, data=form, headers=headers, timeout=60) as up_resp:
353
  up_data = await up_resp.json()
 
354
  if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
355
  send_payload = {
356
  "chat_id": chat_id,
@@ -361,22 +407,31 @@ async def helper_upload_file(client, chat_id, file_name, file_type="Image", capt
361
  }
362
  async with session.post(url_send, json=send_payload, timeout=20) as send_resp:
363
  s_data = await send_resp.json()
364
- if s_data.get("status") == "OK": return True
365
- else: error_logs.append(f"SendFile API Error: {s_data}")
366
- else: error_logs.append(f"Upload API Error: {up_data}")
367
- else: error_logs.append(f"Missing Key in Response: {data_dict}")
368
- else: error_logs.append(f"Request API Error: {req_data}")
369
- except Exception as e: error_logs.append(f"Raw HTTP Error: {e}")
 
 
 
 
 
 
370
 
371
  try:
372
  if hasattr(client, "send_file"):
373
  await client.send_file(chat_id, abs_path)
374
- if caption: await send_with_keyboard(client, chat_id, caption, True)
 
375
  return True
376
- except Exception as e: error_logs.append(f"Rubpy Send_file Error: {e}")
 
377
 
378
  return "\n".join(error_logs)
379
 
 
380
  WORKER_URLS =[
381
  "https://hamed744-ttspro.hf.space/generate",
382
  "https://hamed744-ttspro2.hf.space/generate",
@@ -406,30 +461,45 @@ user_states = {}
406
  processed_message_ids = set()
407
  user_last_request_time = {}
408
 
409
- # --- پردازش‌ها ---
 
410
  async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
411
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
412
  creds = get_user_credits(str_chat_id)
413
- if creds["chat"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده است.", False)
414
- if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید تنظیم نیست.", False)
 
 
415
 
416
  proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
417
- history = user_states[chat_id].get("history",[])
418
  new_parts =[]
419
 
420
  if prompt: new_parts.append({"text": prompt})
421
- elif file_bytes: new_parts.append({"text": "لطفاً این فایل را بررسی کن."})
422
 
423
  if file_bytes and file_name:
424
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
425
  mime_type, _ = mimetypes.guess_type(file_name)
426
- if not mime_type: mime_type = "image/jpeg"
 
 
 
 
 
 
 
 
427
  new_parts.append({"inlineData": {"mimeType": mime_type, "data": base64_data}})
428
 
429
  if history and history[-1]["role"] == "user": history[-1]["parts"].extend(new_parts)
430
  else: history.append({"role": "user", "parts": new_parts})
431
 
432
- if len(history) > 40: history = history[-40:]
 
 
 
 
433
  keys_to_try = get_next_gemini_keys(100)
434
  final_answer = None
435
 
@@ -444,24 +514,27 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
444
  try:
445
  final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
446
  break
447
- except Exception: continue
448
  except Exception: continue
449
 
450
  try:
451
  if proc_msg:
452
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
 
453
  if msg_id: await client.delete_messages(chat_id, [msg_id])
454
  except Exception: pass
455
 
456
  if not final_answer:
457
  if history and history[-1]["role"] == "user": history.pop()
458
- return await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است.", False)
 
459
 
 
460
  if not creds.get("is_premium"):
461
  user_credits_db[str_chat_id]["chat"] -= 1
462
  save_db(user_credits_db)
463
 
464
- history.append({"role": "model", "parts":[{"text": final_answer}]})
465
  user_states[chat_id]["history"] = history
466
 
467
  try:
@@ -482,25 +555,32 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
482
  await send_with_keyboard(client, chat_id, chunk, False)
483
  await asyncio.sleep(2.5)
484
  except Exception: await asyncio.sleep(2.5)
 
485
  except Exception:
486
- await send_with_keyboard(client, chat_id, "❌ خطایی رخ داد.", False)
487
 
 
 
488
  async def process_image(client, chat_id, prompt):
489
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
490
  creds = get_user_credits(str_chat_id)
491
- if creds["image"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده.", False)
492
- if not HF_TOKENS: return await send_with_keyboard(client, chat_id, "❌ توکن تنظیم نیست.", False)
 
 
493
 
494
- proc_msg = await send_with_keyboard(client, chat_id, "✨ پرامپت در حال بهینه‌سازی...", False)
495
  enhanced_prompt = prompt
496
  if GEMINI_KEYS:
 
497
  keys_to_try_gemini = get_next_gemini_keys(100)
498
- sys_p = f"You are an expert AI image prompt engineer. Translate to English and enhance with details. Return ONLY the English prompt.\nUser: {prompt}"
499
  async with aiohttp.ClientSession() as session:
500
  for key in keys_to_try_gemini:
501
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
 
502
  try:
503
- async with session.post(url, json={"contents":[{"parts":[{"text": sys_p}]}]}, timeout=20) as response:
504
  if response.status == 200:
505
  data = await response.json()
506
  enhanced_prompt = data["candidates"][0]["content"]["parts"][0]["text"].strip()
@@ -509,110 +589,164 @@ async def process_image(client, chat_id, prompt):
509
 
510
  try:
511
  if proc_msg:
512
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
513
- if msg_id: await client.delete_messages(chat_id, [msg_id])
 
514
  except Exception: pass
515
 
516
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
517
- proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی...\n`{short_preview}`", False)
518
 
519
  keys_to_try = HF_TOKENS.copy()
520
  random.shuffle(keys_to_try)
521
  generated_image = None
 
522
 
523
  for token in keys_to_try:
524
  try:
525
  hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
526
  generated_image = await hf_client.text_to_image(enhanced_prompt, model="Tongyi-MAI/Z-Image-Turbo")
527
  break
528
- except Exception: continue
 
 
529
 
530
  try:
531
  if proc_msg:
532
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
533
- if msg_id: await client.delete_messages(chat_id,[msg_id])
 
534
  except Exception: pass
535
 
536
- if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.", True)
537
 
 
538
  user_credits_db[str_chat_id]["image"] -= 1
539
  save_db(user_credits_db)
540
 
541
  try:
542
  file_name = f"image_{uuid.uuid4().hex}.jpg"
543
- generated_image.convert('RGB').save(file_name, format="JPEG", quality=100)
 
544
  await asyncio.sleep(1)
545
- upload_result = await helper_upload_file(client, chat_id, file_name, "Image", f"🎨 عکس آماده شد!\n✨ ایده: {prompt}")
 
 
546
  if os.path.exists(file_name): os.remove(file_name)
547
- except Exception as e: await send_with_keyboard(client, chat_id, f"❌ خطا: {str(e)[:100]}", True)
548
 
 
549
  async def translate_text_aloha(prompt_text):
550
  session_hash = ''.join(random.choices(string.ascii_lowercase + string.digits, k=11))
551
- payload = {"data":[prompt_text, "انگلیسی (آمریکا) - جنی (زن)", 0, 0, 0], "fn_index": 1, "session_hash": session_hash}
 
 
 
 
 
 
552
  try:
553
  async with aiohttp.ClientSession() as session:
554
- await session.post("https://hamed744-translate-tts-aloha.hf.space/gradio_api/queue/join", json=payload, timeout=20)
555
- async with session.get(f"https://hamed744-translate-tts-aloha.hf.space/gradio_api/queue/data?session_hash={session_hash}", timeout=60) as resp:
 
 
 
 
 
 
556
  async for line_bytes in resp.content:
557
  line = line_bytes.decode('utf-8').strip()
558
  if line.startswith("data: "):
559
  try:
560
  json_data = json.loads(line[6:])
561
- if json_data.get("msg") == "process_completed" and json_data.get("success"):
562
- return json_data["output"]["data"][0]
563
- except Exception: pass
564
- except Exception: pass
 
 
 
 
 
565
  return prompt_text
566
 
 
567
  async def process_image_edit(client, chat_id, image_bytes, prompt):
568
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
569
  creds = get_user_credits(str_chat_id)
570
- if creds["edit_image"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده.", False)
571
- if not HF_TOKENS: return await send_with_keyboard(client, chat_id, "❌ توکن تنظیم نیست.", False)
 
 
 
572
 
573
- proc_msg = await send_with_keyboard(client, chat_id, "🪄 در حال ویرایش...", False)
574
- translated_prompt = await translate_text_aloha(prompt) or prompt
 
 
 
 
575
 
576
  keys_to_try = HF_TOKENS.copy()
577
  random.shuffle(keys_to_try)
578
  generated_image = None
 
579
 
580
  for token in keys_to_try:
581
  try:
582
  hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
583
- generated_image = await hf_client.image_to_image(image_bytes, prompt=translated_prompt, model="black-forest-labs/FLUX.2-dev")
 
 
 
 
584
  break
585
- except Exception: continue
 
 
586
 
587
  try:
588
  if proc_msg:
589
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
590
- if msg_id: await client.delete_messages(chat_id,[msg_id])
 
591
  except Exception: pass
592
 
593
- if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ ویرایش انجام نشد.", True)
 
594
 
 
595
  user_credits_db[str_chat_id]["edit_image"] -= 1
596
  save_db(user_credits_db)
597
 
598
  try:
599
  file_name = f"edited_flux_{uuid.uuid4().hex}.jpg"
600
- generated_image.convert('RGB').save(file_name, format="JPEG", quality=100)
 
601
  await asyncio.sleep(1)
602
- upload_result = await helper_upload_file(client, chat_id, file_name, "Image", f"🪄 ویرایش انجام شد!\n🔤 دستور: {prompt}")
 
 
 
603
  if os.path.exists(file_name): os.remove(file_name)
604
- except Exception: pass
 
 
605
 
 
606
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
607
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
608
  creds = get_user_credits(str_chat_id)
609
- if creds["tts"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده.", False)
 
610
 
611
  try:
612
- proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا...", False)
613
  payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
614
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
615
  audio_bytes = None
 
616
  workers = WORKER_URLS.copy()
617
  random.shuffle(workers)
618
 
@@ -625,78 +759,104 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
625
  if 'audio' in content_type or response.content_length > 1000:
626
  audio_bytes = await response.read()
627
  break
628
- except Exception: continue
 
 
629
 
630
  try:
631
  if proc_msg:
632
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
633
- if msg_id: await client.delete_messages(chat_id,[msg_id])
 
634
  except Exception: pass
635
 
636
  if audio_bytes:
 
637
  if not creds.get("is_premium"):
638
  user_credits_db[str_chat_id]["tts"] -= 1
639
  save_db(user_credits_db)
 
640
  file_name = f"audio_{uuid.uuid4().hex}.mp3"
641
  with open(file_name, "wb") as f: f.write(audio_bytes)
642
  await asyncio.sleep(1)
643
- await helper_upload_file(client, chat_id, file_name, "Voice", "✅ صدای شما آماده شد.")
 
644
  if os.path.exists(file_name): os.remove(file_name)
645
- else: await send_with_keyboard(client, chat_id, "❌ سرورها درگیر هستند.", True)
646
- except Exception: pass
647
 
 
 
648
  async def process_podcast(client, chat_id, prompt):
649
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
650
  creds = get_user_credits(str_chat_id)
651
- if creds["podcast"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده.", False)
 
 
 
 
 
 
 
652
 
653
- proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال نگارش پادکست...", False)
654
- available_speakers = [{"id": v[1], "name": v[0].split(' (')[0], "gender": "male" if "مرد" in v[0] else "female"} for k, v in SPEAKERS.items()]
655
 
656
  async with aiohttp.ClientSession() as session:
657
  try:
658
- async with session.post("https://ezmarynoori-podgen.hf.space/api/create-full-podcast", json={"prompt": prompt, "available_speakers": available_speakers}, timeout=60) as resp:
659
- if resp.status != 202: return await send_with_keyboard(client, chat_id, "❌ خطا در سرور.", True)
660
  task_id = (await resp.json()).get("task_id")
661
- except Exception: return await send_with_keyboard(client, chat_id, "❌ خطا در اتصال.", True)
662
 
 
663
  script_data = None
664
  for _ in range(40):
665
  await asyncio.sleep(3)
666
  try:
667
- async with session.get(f"https://ezmarynoori-podgen.hf.space/api/podcast-status/{task_id}") as resp:
668
  if resp.status == 200:
669
  status_data = await resp.json()
670
  if status_data.get("status") == "completed":
671
  script_data = status_data.get("data", {}).get("script",[])
672
  break
673
- elif status_data.get("status") == "failed": return await send_with_keyboard(client, chat_id, "❌ شکست در سناریو.", True)
 
674
  except Exception: pass
675
 
676
- if not script_data: return await send_with_keyboard(client, chat_id, "❌ زمان انتظار پایان یافت.", True)
677
 
678
  try:
679
  if proc_msg:
680
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
681
- if msg_id: await client.delete_messages(chat_id,[msg_id])
 
682
  except: pass
683
 
684
- proc_msg = await send_with_keyboard(client, chat_id, "🎙 در حال میکس دیالوگ‌ها...", False)
 
685
  combined_audio = AudioSegment.empty()
 
686
 
687
- for turn in script_data:
 
688
  chunk_audio_bytes = None
689
- for _ in range(3):
690
  try:
691
- async with session.post("https://ezmarynoori-podgen.hf.space/api/generate", json={"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}, timeout=120) as resp:
692
  if resp.status == 200:
693
  chunk_audio_bytes = await resp.read()
694
  break
695
  except Exception: await asyncio.sleep(2)
696
- if not chunk_audio_bytes: return await send_with_keyboard(client, chat_id, "❌ خطا در تولید صدا.", True)
697
- try: combined_audio += AudioSegment.from_file(io.BytesIO(chunk_audio_bytes))
698
- except Exception: return await send_with_keyboard(client, chat_id, "❌ خطا در پردازش صدا.", True)
 
 
 
 
699
 
 
700
  if not creds.get("is_premium"):
701
  user_credits_db[str_chat_id]["podcast"] -= 1
702
  save_db(user_credits_db)
@@ -706,32 +866,40 @@ async def process_podcast(client, chat_id, prompt):
706
 
707
  try:
708
  if proc_msg:
709
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
710
- if msg_id: await client.delete_messages(chat_id,[msg_id])
 
711
  except: pass
712
 
713
- await helper_upload_file(client, chat_id, file_name, "Voice", f"🎧 پادکست ساخته شد!\n💡 موضوع: {prompt}")
 
 
714
  if os.path.exists(file_name): os.remove(file_name)
715
 
 
 
716
  async def process_stt(client, chat_id, audio_bytes, file_name):
717
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
718
  creds = get_user_credits(str_chat_id)
719
- if creds["stt"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده.", False)
720
- if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید تنظیم نشده.", False)
 
 
721
 
722
- proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال پیاده‌سازی متن...", False)
723
  base64_data = base64.b64encode(audio_bytes).decode('utf-8')
724
  mime_type, _ = mimetypes.guess_type(file_name)
725
  if not mime_type: mime_type = "audio/ogg"
726
 
 
727
  keys_to_try = get_next_gemini_keys(100)
728
  transcribed_text = None
729
- prompt = "کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
730
 
731
  async with aiohttp.ClientSession() as session:
732
  for key in keys_to_try:
733
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
734
- payload = {"contents":[{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.2}}
735
  try:
736
  async with session.post(url, json=payload, timeout=60) as response:
737
  if response.status == 200:
@@ -742,28 +910,36 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
742
 
743
  try:
744
  if proc_msg:
745
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
 
746
  if msg_id: await client.delete_messages(chat_id, [msg_id])
747
  except Exception: pass
748
 
749
  if transcribed_text:
 
750
  if not creds.get("is_premium"):
751
  user_credits_db[str_chat_id]["stt"] -= 1
752
  save_db(user_credits_db)
753
  await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
754
- else: await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است.", True)
 
755
 
 
 
756
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
757
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
758
  creds = get_user_credits(str_chat_id)
759
- if creds["file"] <= 0: return await send_with_keyboard(client, chat_id, "❌ اعتبار تمام شده.", False)
760
- if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید تنظیم نیست.", False)
 
 
761
 
762
  proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
763
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
764
  mime_type, _ = mimetypes.guess_type(file_name)
765
  if not mime_type: mime_type = "image/jpeg"
766
 
 
767
  keys_to_try = get_next_gemini_keys(100)
768
  final_answer = None
769
 
@@ -781,16 +957,19 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
781
 
782
  try:
783
  if proc_msg:
784
- msg_id = getattr(proc_msg, 'message_id', None) or (proc_msg.get('message_update', {}).get('message_id') if isinstance(proc_msg, dict) else None)
785
- if msg_id: await client.delete_messages(chat_id,[msg_id])
 
786
  except Exception: pass
787
 
788
  if final_answer:
 
789
  if not creds.get("is_premium"):
790
  user_credits_db[str_chat_id]["file"] -= 1
791
  save_db(user_credits_db)
792
  await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
793
- else: await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است.", True)
 
794
 
795
 
796
  # --- تنظیمات ربات روبیکا ---
@@ -803,85 +982,69 @@ else:
803
  async def main_handler(client, update):
804
  global BOT_GUID
805
 
806
- # 🔥 دیوار دفاعی اول: فیلتر ضد رگبار 🔥
 
807
  if is_backlog_burst():
808
  return
809
 
810
- current_time = time.time()
811
-
812
  try:
813
- # 1. استخراج آبجکت پیام
814
- msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
815
-
816
- # 2. فیلتر خود ربات
817
- author_id = getattr(update, 'author_guid', None)
818
- if not author_id and msg_obj:
819
- author_id = getattr(msg_obj, 'author_object_guid', None)
820
- if not author_id and isinstance(msg_obj, dict):
821
- author_id = msg_obj.get('author_object_guid') or msg_obj.get('author_guid')
822
-
823
  if not BOT_GUID:
824
  try:
825
  me_info = await client.get_me()
826
  if me_info and hasattr(me_info, 'user'):
827
  BOT_GUID = getattr(me_info.user, 'user_guid', None)
828
- except Exception: pass
 
829
 
 
 
 
 
 
 
 
 
830
  if BOT_GUID and author_id == BOT_GUID:
831
  return
832
-
833
- # 3. استخراج امن زمان پیام برای فیلتر تکمیلی
834
- msg_time = getattr(update, 'timestamp', None)
835
- if not msg_time and msg_obj:
836
- msg_time = getattr(msg_obj, 'timestamp', None)
837
- if not msg_time and isinstance(update, dict):
838
- msg_time = update.get('timestamp')
839
- if not msg_time and 'message' in update and isinstance(update['message'], dict):
840
- msg_time = update['message'].get('timestamp')
841
-
842
- if msg_time:
843
- try:
844
- ts = float(msg_time)
845
- while ts > 3e9: ts /= 10.0 # نرمال‌سازی زمان به ثانیه
846
- if current_time - ts > 60.0: # اگر پیام مال بیشتر از ۶۰ ثانیه پیش بود، دور ریخته می‌شود
847
- return
848
- except Exception: pass
849
-
850
- # 4. استخراج چت آیدی
851
- chat_id = getattr(update, 'object_guid', None) or getattr(update, 'chat_id', None)
852
- if not chat_id and msg_obj:
853
- chat_id = getattr(msg_obj, 'object_guid', None) or getattr(msg_obj, 'chat_id', None)
854
- if not chat_id: chat_id = author_id
855
  if not chat_id: return
856
 
857
- # 5. سیستم ضد تداخل آیدی یکتا
858
  msg_id = getattr(update, "message_id", None)
859
  if not msg_id and msg_obj:
860
- msg_id = getattr(msg_obj, "message_id", None)
861
- if msg_id:
862
- unique_msg_key = f"{chat_id}_{msg_id}"
 
 
863
  if unique_msg_key in processed_message_ids: return
864
  processed_message_ids.add(unique_msg_key)
865
  if len(processed_message_ids) > 5000: processed_message_ids.clear()
866
 
867
- # 6. محدودکننده سرعت هر کاربر (1 ثانیه)
 
 
868
  last_req_time = user_last_request_time.get(chat_id, 0)
869
  if current_time - last_req_time < 1.0:
870
  return
871
  user_last_request_time[chat_id] = current_time
872
 
873
- # 7. استخراج متن
874
- user_text = getattr(update, "text", "") or (getattr(msg_obj, "text", "") if msg_obj else "")
875
  user_text_str = str(user_text).strip() if user_text else ""
876
  user_text_lower = user_text_str.lower()
877
 
 
878
  if user_text_str and user_text_str.startswith(("⏳", "❌", "✅", "🧠", "🎨", "🪄", "🎙", "📻", "📝", "👁️", "💡", "📥")):
879
  return
880
 
881
  if chat_id not in user_states:
882
  user_states[chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None}
883
 
884
- # 🛠 --- سیستم پنل مدیریت --- 🛠
885
  if user_text_lower.startswith(f"{ADMIN_CODE} pro=") or user_text_lower.startswith(f"{ADMIN_CODE}pro="):
886
  parts = user_text_str.split("=", 1)
887
  if len(parts) >= 2:
@@ -889,12 +1052,22 @@ else:
889
  if target_id:
890
  if target_id not in user_credits_db:
891
  user_credits_db[target_id] = {
892
- "is_premium": False, "expire_date": None, "last_reset": "",
893
- "chat": 10, "image": 5, "edit_image": 1, "podcast": 2, "tts": 5, "file": 1, "stt": 5
 
 
 
 
 
 
 
 
894
  }
 
895
  user_credits_db[target_id]["is_premium"] = True
896
  expire_time = datetime.datetime.now() + datetime.timedelta(days=30)
897
  user_credits_db[target_id]["expire_date"] = expire_time.isoformat()
 
898
  user_credits_db[target_id]["chat"] = 999999
899
  user_credits_db[target_id]["image"] = 20
900
  user_credits_db[target_id]["edit_image"] = 10
@@ -902,44 +1075,83 @@ else:
902
  user_credits_db[target_id]["tts"] = 999999
903
  user_credits_db[target_id]["file"] = 999999
904
  user_credits_db[target_id]["stt"] = 999999
 
905
  save_db(user_credits_db)
906
- await send_with_keyboard(client, chat_id, f"✅ کاربر `{target_id}` پرو شد.", False)
907
- try: await send_with_keyboard(client, target_id, "🎉 حساب شما پرو شد.", True)
908
- except: pass
 
909
  return
910
 
911
  if user_text_lower.startswith(f"{ADMIN_CODE} free=") or user_text_lower.startswith(f"{ADMIN_CODE}free="):
912
  parts = user_text_str.split("=", 1)
913
  if len(parts) >= 2:
914
  target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
915
- if target_id and target_id in user_credits_db:
916
- user_credits_db[target_id]["is_premium"] = False
917
- user_credits_db[target_id]["expire_date"] = None
918
- save_db(user_credits_db)
919
- await send_with_keyboard(client, chat_id, f" کاربر `{target_id}` رایگان شد.", False)
 
 
 
 
 
920
  return
921
 
922
  if user_text_lower.startswith(f"{ADMIN_CODE} =") or user_text_lower.startswith(f"{ADMIN_CODE}="):
923
  parts = user_text_str.split("=", 1)
924
  if len(parts) >= 2:
925
  target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
926
- if target_id and target_id in user_credits_db:
927
- t_creds = user_credits_db[target_id]
928
- is_prem = t_creds.get("is_premium", False)
929
- if is_prem:
930
- days_left = 0
931
- if t_creds.get("expire_date"):
932
- diff = datetime.datetime.fromisoformat(t_creds["expire_date"]) - datetime.datetime.now()
933
- days_left = max(0, diff.days)
934
- status_text = f"🌟 پرو ({days_left} روز)"
935
- else: status_text = "🥉 رایگان"
936
- info_msg = f"شناسه: `{target_id}`\nوضعیت: {status_text}\nعکس: {t_creds.get('image',0)} | چت: {t_creds.get('chat',0)}"
937
- await send_with_keyboard(client, chat_id, info_msg, False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
938
  return
939
 
940
  # تشخیص فایل
941
  is_file = False
942
  file_name = f"unknown_file_{uuid.uuid4().hex[:6]}.jpg"
 
943
  if msg_obj:
944
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
945
  file_attr = getattr(msg_obj, attr, None)
@@ -947,6 +1159,7 @@ else:
947
  is_file = True
948
  file_name = getattr(file_attr, 'file_name', file_name)
949
  break
 
950
  if not is_file and hasattr(msg_obj, 'to_dict'):
951
  msg_dict = msg_obj.to_dict()
952
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
@@ -959,77 +1172,150 @@ else:
959
  if user_text_str in["/start", "سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
960
  user_states[chat_id]["mode"] = None
961
  user_states[chat_id]["file_bytes"] = None
962
- await send_with_keyboard(client, chat_id, "سلام! به ربات هوش مصنوعی آلفا خوش آمدید 🤖\n\nلطفاً از کیبورد پایین انتخاب کنید:", True)
963
  return
964
 
965
- if user_text_str in["/account", "حساب کاربری 👤"]:
 
966
  creds = get_user_credits(chat_id)
967
  is_prem = creds.get("is_premium", False)
 
968
  if is_prem:
 
969
  days_left = 0
970
  if creds.get("expire_date"):
971
- diff = datetime.datetime.fromisoformat(creds["expire_date"]) - datetime.datetime.now()
972
- days_left = max(0, diff.days)
973
- status_text = f"🌟 نسخه پرو (ویژه)\n⏳ **باقیمانده:** {days_left} روز"
974
- else: status_text = "🥉 نسخه رایگان (آزمایشی)"
975
-
976
- account_profile = f"""👤 **حساب کاربری**\n🔹 `{chat_id}`\n🔹 {status_text}\n\n📊 **سهمیه شما:**\n- چت: {"∞" if is_prem else creds['chat']}\n- عکس: {creds['image']}\n- ویرایش عکس: {creds['edit_image']}\n- پادکست: {"∞" if is_prem else creds['podcast']}\n- صدا: {"∞" if is_prem else creds['tts']}\n- تحلیل: {"∞" if is_prem else creds['file']}\n- صدا به متن: {"∞" if is_prem else creds['stt']}"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
977
  await send_with_keyboard(client, chat_id, account_profile, True)
978
  return
979
 
980
- if user_text_str in["/buy", "خرید اشتراک 💎"]:
981
- buy_text = f"💎 **خرید اشتراک یک ماهه**\n💳 مبلغ 250 هزار تومان\n💳 کارت: `6219861411958035` کوهی\n\nپس از واریز، رسید و شناسه زیر را به پشتیبانی ارسال کنید:\nشناسه: `{chat_id}`\nپشتیبانی: @H_a_m_e_d100"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982
  await send_with_keyboard(client, chat_id, buy_text, True)
983
  return
984
 
 
985
  if user_text_str in["/transfer", "انتقال اکانت از برنامه به ربات"]:
986
- transfer_text = f"🔄 **انتقال اکانت**\nاگر در برنامه اشتراک دارید، نیازی به خرید مجدد نیست. شناسه ربات خود را به پشتیبانی بفرستید:\nشناسه: `{chat_id}`\nپشتیبانی: @H_a_m_e_d100"
 
 
 
 
 
 
 
 
 
987
  await send_with_keyboard(client, chat_id, transfer_text, True)
988
  return
989
 
990
  if user_text_str in["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]:
991
  user_states[chat_id]["mode"] = "chat"
992
  user_states[chat_id]["history"] =[]
993
- await send_with_keyboard(client, chat_id, "💬 شما وارد بخش چت شدید. سوال خود را بپرسید:", True)
994
  return
995
 
996
  if user_text_str in["/image", "🎨 عکس", "ساخت تصاویر🎨"]:
997
  user_states[chat_id]["mode"] = "image_waiting_for_text"
998
- await send_with_keyboard(client, chat_id, "🎨 شما وارد ساخت عکس شدید. متن خود را بفرستید:", True)
999
  return
1000
 
1001
- if user_text_str in["/edit_image", "ویرایش تصاویر 🪄"]:
1002
  user_states[chat_id]["mode"] = "image_edit_waiting_for_image"
1003
  user_states[chat_id]["file_bytes"] = None
1004
- await send_with_keyboard(client, chat_id, "🪄 لطفاً عکسی که می‌خواهید ویرایش کنید را بفرستید:", True)
1005
  return
1006
 
1007
- if user_text_str in["/tts", "🎙️ صدا", "تبدیل متن به صدا🗣️"]:
1008
  user_states[chat_id]["mode"] = "tts_waiting_for_text"
1009
- await send_with_keyboard(client, chat_id, "🎙️ لطفاً متن خود را برای تبدیل به صدا بفرستید:", True)
1010
  return
1011
 
1012
  if user_text_str in["/podcast", "📻 پادکست", "ساخت پادکست 🎙️"]:
1013
  user_states[chat_id]["mode"] = "podcast_waiting_for_topic"
1014
- await send_with_keyboard(client, chat_id, "📻 لطفاً موضوع پادکست را بفرستید:", True)
1015
  return
1016
 
1017
- if user_text_str in ["/file", "تحلیل فایل 📁"]:
1018
  user_states[chat_id]["mode"] = "file_waiting_for_file"
1019
  user_states[chat_id]["file_bytes"] = None
1020
- await send_with_keyboard(client, chat_id, "📁 لطفاً فایل خود را بفرستید:", True)
1021
  return
1022
 
1023
  if user_text_str in["/stt", "فایل صوتی به متن 📝"]:
1024
  user_states[chat_id]["mode"] = "stt_waiting_for_audio"
1025
- await send_with_keyboard(client, chat_id, "📝 لطفاً فایل صوتی را بفرستید:", True)
1026
  return
1027
 
1028
  current_mode = user_states[chat_id].get("mode")
1029
 
1030
  if current_mode is None:
1031
  if is_file: pass
1032
- elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً از کیبورد پایین انتخاب کنید:", True)
1033
  return
1034
 
1035
  elif current_mode == "chat":
@@ -1038,7 +1324,7 @@ else:
1038
  try:
1039
  file_bytes = await helper_download_file(client, msg_obj)
1040
  asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=file_bytes, file_name=file_name))
1041
- except Exception as dl_err: await send_with_keyboard(client, chat_id, "❌ خطا در دریافت.", False)
1042
  elif user_text_str: asyncio.create_task(process_gemini(client, chat_id, user_text_str))
1043
  return
1044
 
@@ -1047,14 +1333,14 @@ else:
1047
  return
1048
 
1049
  elif current_mode == "image_edit_waiting_for_image":
1050
- if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً عکس ارسال کنید.", False)
1051
- await send_with_keyboard(client, chat_id, "📥 در حال دانلود...", False)
1052
  try:
1053
  file_bytes = await helper_download_file(client, msg_obj)
1054
  user_states[chat_id]["file_bytes"] = file_bytes
1055
  user_states[chat_id]["mode"] = "image_edit_waiting_for_prompt"
1056
- await send_with_keyboard(client, chat_id, "✅ عکس دریافت شد. حالا دستور خود را تایپ کنید.", False)
1057
- except Exception: await send_with_keyboard(client, chat_id, "❌ خطا در دانلود.", False)
1058
  return
1059
 
1060
  elif current_mode == "image_edit_waiting_for_prompt":
@@ -1063,51 +1349,66 @@ else:
1063
  user_states[chat_id]["mode"] = None
1064
  user_states[chat_id]["file_bytes"] = None
1065
  asyncio.create_task(process_image_edit(client, chat_id, saved_bytes, user_text_str))
1066
- else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست متنی بنویسید.", False)
1067
  return
1068
 
1069
  elif current_mode == "tts_waiting_for_text":
1070
  if user_text_str:
1071
- if len(user_text_str) > 2500: return await send_with_keyboard(client, chat_id, "⚠️ متن خیلی طولانی است.", False)
1072
  user_states[chat_id]["text"] = user_text_str
1073
  user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
1074
- await send_with_keyboard(client, chat_id, "✅ ذخیره شد.\nلطفاً شماره گوینده (1 تا 30) را بفرستید.", False)
 
 
 
 
 
 
 
 
 
 
 
 
 
1075
  return
1076
 
1077
  elif current_mode == "tts_waiting_for_speaker":
1078
  normalized_text = to_english_digits(user_text_str)
 
1079
  if normalized_text.isdigit() and normalized_text in SPEAKERS:
1080
  spk_name, spk_id = SPEAKERS[normalized_text]
1081
  txt = user_states[chat_id]["text"]
1082
  user_states[chat_id]["mode"] = "tts_waiting_for_text"
1083
  asyncio.create_task(process_tts(client, chat_id, txt, spk_id, spk_name))
1084
- else: await send_with_keyboard(client, chat_id, "❌ شماره بین 1 تا 30 بفرستید.", False)
 
1085
  return
1086
 
1087
  elif current_mode == "podcast_waiting_for_topic":
1088
  if user_text_str: asyncio.create_task(process_podcast(client, chat_id, user_text_str))
1089
- else: await send_with_keyboard(client, chat_id, "⚠️ موضوع را متنی بفرستید.", False)
1090
  return
1091
 
1092
  elif current_mode == "stt_waiting_for_audio":
1093
- if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل صوتی/تصویری بفرستید.", False)
1094
- await send_with_keyboard(client, chat_id, "📥 در حال دانلود...", False)
1095
  try:
1096
  audio_bytes = await helper_download_file(client, msg_obj)
1097
  asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name))
1098
- except Exception: await send_with_keyboard(client, chat_id, "❌ خطا در دانلود.", False)
1099
  return
1100
 
1101
  elif current_mode == "file_waiting_for_file":
1102
- if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل ارسال کنید.", False)
1103
- await send_with_keyboard(client, chat_id, "📥 در حال دانلود...", False)
1104
  try:
1105
  file_bytes = await helper_download_file(client, msg_obj)
1106
  user_states[chat_id]["file_bytes"] = file_bytes
1107
  user_states[chat_id]["file_name"] = file_name
1108
  user_states[chat_id]["mode"] = "file_waiting_for_prompt"
1109
- await send_with_keyboard(client, chat_id, "✅ فایل دریافت شد. حالا بپرسید چگونه تحلیل شود؟", False)
1110
- except Exception: await send_with_keyboard(client, chat_id, "❌ خطا در دانلود.", False)
1111
  return
1112
 
1113
  elif current_mode == "file_waiting_for_prompt":
@@ -1116,7 +1417,7 @@ else:
1116
  saved_name = user_states[chat_id].get("file_name", "file.jpeg")
1117
  user_states[chat_id]["mode"] = "file_waiting_for_file"
1118
  asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str))
1119
- else: await send_with_keyboard(client, chat_id, "⚠️ درخواست متنی بنویسید.", False)
1120
  return
1121
 
1122
  except Exception: traceback.print_exc()
@@ -1124,5 +1425,5 @@ else:
1124
  if __name__ == "__main__":
1125
  threading.Thread(target=run_flask, daemon=True).start()
1126
  if bot_token:
1127
- print("🚀 ربات آلفا پرو به همراه دیواره ضد-رگبار (Burst Controller) روشن شد...")
1128
  bot.run()
 
23
  # --- کد مدیریت برای ارتقای کاربران بدون نیاز به لاگین ---
24
  ADMIN_CODE = "3011"
25
 
26
+ # متغیر سراسری برای ذخیره آیدی خود ربات (جلوگیری از جواب دادن ربات به خودش)
27
  BOT_GUID = None
28
 
29
  # =======================================================
30
  # 🔥 سیستم کنترل سرعت و ضد رگبار (Burst Controller) 🔥
31
+ # جلوگیری از هنگ کردن ربات هنگام دریافت پیام‌های تلنبار شده
32
  # =======================================================
33
  global_burst_count = 0
34
  global_burst_time = time.time()
35
  burst_lock = threading.Lock()
36
 
37
  def is_backlog_burst():
38
+ """اگر بیشتر از 5 پیام در 1 ثانیه بیاید، یعنی رگبار از سمت سرور است و رد می‌شود"""
39
  global global_burst_count, global_burst_time
40
  with burst_lock:
41
  now = time.time()
 
52
  # --- سیستم دیتابیس حساب کاربری متصل به دیتاست هاگینگ فیس ---
53
  DB_FILE = "users_db.json"
54
  DATASET_REPO = "Opera8/Karbaran-rayegan-tedad"
55
+ HF_TOKEN_DB = os.environ.get("HF_TOKEN") # توکنی که برای دیتاست تنظیم کرده‌اید
56
  db_lock = threading.Lock()
57
 
58
+ # --- الگوریتم تبدیل تاریخ میلادی به شمسی (بدون نیاز به نصب کتابخانه) ---
59
  def gregorian_to_jalali(gy, gm, gd):
60
  g_d_m =[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
61
  gy2 = (gy + 1) if (gm > 2) else gy
 
79
  print("در حال تلاش برای خواندن دیتابیس کاربران از دیتاست هاگینگ فیس...")
80
  if HF_TOKEN_DB:
81
  try:
82
+ # دانلود دیتابیس از دیتاست خصوصی
83
  file_path = hf_hub_download(
84
  repo_id=DATASET_REPO,
85
  filename=DB_FILE,
 
90
  print("✅ دیتابیس با موفقیت از دیتاست هاگینگ فیس لود شد.")
91
  return json.load(f)
92
  except Exception as e:
93
+ print("⚠️ فایل دیتابیس در هاگینگ فیس یافت نشد یا خطایی رخ داد (ساخت دیتابیس جدید). جزئیات:", str(e)[:100])
94
 
95
+ # اگر از دیتاست لود نشد، از لوکال بخوان
96
  if os.path.exists(DB_FILE):
97
  try:
98
  with open(DB_FILE, "r", encoding="utf-8") as f:
 
102
  return {}
103
 
104
  def _upload_db_background():
105
+ """تابع کمکی برای آپلود دیتابیس بدون درگیر کردن سرعت ربات"""
106
  if HF_TOKEN_DB:
107
  api = HfApi(token=HF_TOKEN_DB)
108
  try:
 
112
  repo_id=DATASET_REPO,
113
  repo_type="dataset"
114
  )
115
+ except Exception as e:
116
+ print("❌ خطا در آپلود دیتابیس به هاگینگ فیس:", str(e)[:100])
117
 
118
  def save_db(db_data):
119
  with db_lock:
120
  try:
121
+ # ذخیره فایل به صورت لوکال
122
  with open(DB_FILE, "w", encoding="utf-8") as f:
123
  json.dump(db_data, f, ensure_ascii=False, indent=4)
124
+
125
+ # استارت کردن یک Thread جدید برای آپلود به دیتاست تا ربات منتظر نماند
126
  threading.Thread(target=_upload_db_background, daemon=True).start()
127
  except Exception as e:
128
  print("خطا در ذخیره دیتابیس:", e)
 
130
  user_credits_db = load_db()
131
 
132
  def get_user_credits(chat_id):
133
+ # اطمینان از پاک بودن استرینگ از علائم مزاحم
134
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
135
  today_str = datetime.date.today().isoformat()
136
 
137
  if str_chat_id not in user_credits_db:
138
+ # ساخت کاربر جدید با مقادیر اولیه
139
  user_credits_db[str_chat_id] = {
140
  "is_premium": False,
141
  "expire_date": None,
 
153
  user_data = user_credits_db[str_chat_id]
154
  is_premium = user_data.get("is_premium", False)
155
 
156
+ # 🛡 بررسی انقضای اشتراک ماهانه کاربران ویژه 🛡
157
  if is_premium and user_data.get("expire_date"):
158
  try:
159
  expire_date = datetime.datetime.fromisoformat(user_data["expire_date"])
160
  if datetime.datetime.now() > expire_date:
161
+ # اگر زمان اشتراک تمام شده باشد، دوباره رایگان می‌شود
162
  user_data["is_premium"] = False
163
  user_data["expire_date"] = None
164
  is_premium = False
165
  except Exception:
166
  pass
167
 
168
+ # 🔄 سیستم شارژ روزانه (فقط و فقط برای نسخه رایگان) 🔄
169
  if not is_premium:
170
  if user_data.get("last_reset") != today_str:
171
  user_data["last_reset"] = today_str
172
+ # شارژ روزانه نسخه رایگان
173
  user_data["chat"] = 10
174
  user_data["image"] = 5
175
  user_data["edit_image"] = 1
 
181
 
182
  return user_data
183
 
184
+ # --- تابع تبدیل اعداد فارسی/عربی به انگلیسی ---
185
  def to_english_digits(text):
186
+ if not text:
187
+ return text
188
  persian_digits = '۰۱۲۳۴۵۶۷۸۹'
189
  arabic_digits = '٠١٢٣٤٥٦٧٨٩'
190
  english_digits = '0123456789'
 
196
 
197
  @app.route('/')
198
  def home():
199
+ return "ربات یکپارچه آلفا (نسخه پرو + مدیریت اشتراک نامحدود + دیتاست + سیستم ضد باگ) روشن است! 🚀"
200
 
201
  def run_flask():
202
  app.run(host="0.0.0.0", port=7860)
203
 
204
 
205
+ # --- ساختار کیبورد آپدیت شده ---
206
  MAIN_KEYPAD_DICT = {
207
  "rows": [
208
  {
 
248
  "resize_keyboard": True
249
  }
250
 
251
+ # --- تابع ارسال منحصراً برای کیبورد پایین صفحه ---
252
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
253
  try:
254
  if not use_keyboard:
 
267
  except Exception:
268
  return None
269
 
270
+
271
+ # --- 🚨 تابع هوشمند دانلود فایل (با استفاده از UUID جهت جلوگیری از تداخل حریم خصوصی) 🚨 ---
272
  async def helper_download_file(client, msg_obj):
273
  errors =[]
274
  file_id = None
 
281
  elif isinstance(val, dict) and 'file_id' in val: file_id = val['file_id']
282
  if file_id: break
283
 
284
+ # 🛡 استفاده از UUID به جای اعداد تصادفی برای تضمین ۱۰۰ درصدی عدم تداخل نام فایل‌ها 🛡
285
  temp_name = f"temp_dl_{uuid.uuid4().hex}.tmp"
286
 
287
  if hasattr(client, "download_file"):
 
291
  with open(temp_name, 'rb') as f: data = f.read()
292
  os.remove(temp_name)
293
  return data
294
+ except Exception as e: errors.append(f"تلاش ۱: {e}")
295
 
296
  if file_obj:
297
  try:
 
300
  with open(temp_name, 'rb') as f: data = f.read()
301
  os.remove(temp_name)
302
  return data
303
+ except Exception as e: errors.append(f"تلاش ۲: {e}")
304
 
305
  if file_id:
306
  try:
 
309
  with open(temp_name, 'rb') as f: data = f.read()
310
  os.remove(temp_name)
311
  return data
312
+ except Exception as e: errors.append(f"تلاش ۳: {e}")
313
 
314
  try:
315
  await client.download_file(file_id, save_as=temp_name)
 
317
  with open(temp_name, 'rb') as f: data = f.read()
318
  os.remove(temp_name)
319
  return data
320
+ except Exception as e: errors.append(f"تلاش ۴: {e}")
321
 
322
  raise Exception("دانلود ناموفق. لاگ:\n" + "\n".join(errors))
323
 
324
+
325
+ # --- تنظیمات کلیدها ---
326
  GEMINI_KEYS_STR1 = os.environ.get("GEMINI_API_KEYS1", "")
327
  GEMINI_KEYS_STR2 = os.environ.get("GEMINI_API_KEYS2", "")
328
 
329
  _raw_keys =[]
330
+ if GEMINI_KEYS_STR1:
331
+ _raw_keys.extend(GEMINI_KEYS_STR1.split(","))
332
+ if GEMINI_KEYS_STR2:
333
+ _raw_keys.extend(GEMINI_KEYS_STR2.split(","))
334
+
335
+ # حذف کلیدهای تکراری و خالی
336
  GEMINI_KEYS = list(set([k.strip() for k in _raw_keys if k.strip()]))
337
+ print(f"✅ تعداد {len(GEMINI_KEYS)} کلید جیمینای با م��فقیت شناسایی شد.")
338
 
339
+ # --- 🚀 سیستم چرخشی پیشرفته (Round-Robin) برای کلیدهای جیمینای ---
340
  current_gemini_key_index = 0
341
  gemini_key_lock = threading.Lock()
342
 
343
  def get_next_gemini_keys(count=100):
344
+ """
345
+ این تابع تضمین می‌کند که به ترتیب از کلیدها استفاده شود.
346
+ مثلاً درخواست اول کلیدهای ۱ تا ۱۰۰ را می‌گیرد.
347
+ درخواست دوم کلیدهای ۱۰۱ تا ۲۰۰ را می‌گیرد.
348
+ و وقتی به آخر لیست برسد، دوباره از اول شروع می‌کند.
349
+ """
350
  global current_gemini_key_index
351
  with gemini_key_lock:
352
  total_keys = len(GEMINI_KEYS)
353
+ if total_keys == 0:
354
+ return[]
355
+
356
  actual_count = min(count, total_keys)
357
  selected_keys =[]
358
+
359
  for _ in range(actual_count):
360
  selected_keys.append(GEMINI_KEYS[current_gemini_key_index])
361
  current_gemini_key_index = (current_gemini_key_index + 1) % total_keys
362
+
363
  return selected_keys
364
 
365
  HF_TOKENS_STR = os.environ.get("HF_TOKENS", "")
366
  HF_TOKENS =[k.strip() for k in HF_TOKENS_STR.split(",") if k.strip()]
367
+
368
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
369
 
370
+
371
+ # --- 🚨 تابع اختصاصی آپلود فایل به روبیکا 🚨 ---
372
  async def helper_upload_file(client, chat_id, file_name, file_type="Image", caption=""):
373
  abs_path = os.path.abspath(file_name)
374
  error_logs =[]
375
+
376
+ api_file_type = "Image" if file_type in ["photo", "Image", "image"] else "Voice" if file_type in["voice", "Voice", "audio"] else "File"
377
 
378
  try:
379
  url_request = f"https://botapi.rubika.ir/v3/{bot_token}/requestSendFile"
 
382
  async with aiohttp.ClientSession() as session:
383
  async with session.post(url_request, json={"type": api_file_type}, timeout=20) as resp:
384
  req_data = await resp.json()
385
+
386
  if req_data.get("status") == "OK":
387
  data_dict = req_data.get("data", {})
388
  upload_url = data_dict.get("upload_url")
389
  file_id = data_dict.get("id") or data_dict.get("file_id")
390
+
391
  if upload_url and file_id:
392
  with open(abs_path, "rb") as f:
393
  form = aiohttp.FormData()
394
  form.add_field('file', f, filename=os.path.basename(abs_path))
395
  headers = {"file-id": str(file_id)}
396
+
397
  async with session.post(upload_url, data=form, headers=headers, timeout=60) as up_resp:
398
  up_data = await up_resp.json()
399
+
400
  if up_data.get("status") == "OK" or up_data.get("status_det") == "OK":
401
  send_payload = {
402
  "chat_id": chat_id,
 
407
  }
408
  async with session.post(url_send, json=send_payload, timeout=20) as send_resp:
409
  s_data = await send_resp.json()
410
+ if s_data.get("status") == "OK":
411
+ return True
412
+ else:
413
+ error_logs.append(f"SendFile API Error: {s_data}")
414
+ else:
415
+ error_logs.append(f"Upload API Error: {up_data}")
416
+ else:
417
+ error_logs.append(f"Missing Key in Response: {data_dict}")
418
+ else:
419
+ error_logs.append(f"Request API Error: {req_data}")
420
+ except Exception as e:
421
+ error_logs.append(f"Raw HTTP Error: {e}")
422
 
423
  try:
424
  if hasattr(client, "send_file"):
425
  await client.send_file(chat_id, abs_path)
426
+ if caption:
427
+ await send_with_keyboard(client, chat_id, caption, True)
428
  return True
429
+ except Exception as e:
430
+ error_logs.append(f"Rubpy Send_file Error: {e}")
431
 
432
  return "\n".join(error_logs)
433
 
434
+
435
  WORKER_URLS =[
436
  "https://hamed744-ttspro.hf.space/generate",
437
  "https://hamed744-ttspro2.hf.space/generate",
 
461
  processed_message_ids = set()
462
  user_last_request_time = {}
463
 
464
+
465
+ # --- ۱. پردازش چت متنی و چندرسانه‌ای ---
466
  async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
467
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
468
  creds = get_user_credits(str_chat_id)
469
+ if creds["chat"] <= 0:
470
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار پیام‌های چت شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
471
+
472
+ if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشده‌اند.", False)
473
 
474
  proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
475
+ history = user_states[chat_id].get("history", [])
476
  new_parts =[]
477
 
478
  if prompt: new_parts.append({"text": prompt})
479
+ elif file_bytes: new_parts.append({"text": "لطفاً این فایل را به دقت بررسی کن."})
480
 
481
  if file_bytes and file_name:
482
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
483
  mime_type, _ = mimetypes.guess_type(file_name)
484
+ if not mime_type:
485
+ if file_name.endswith(('.jpg', '.jpeg')): mime_type = "image/jpeg"
486
+ elif file_name.endswith('.png'): mime_type = "image/png"
487
+ elif file_name.endswith('.pdf'): mime_type = "application/pdf"
488
+ elif file_name.endswith('.mp4'): mime_type = "video/mp4"
489
+ elif file_name.endswith('.mp3'): mime_type = "audio/mp3"
490
+ elif file_name.endswith(('.ogg', '.oga')): mime_type = "audio/ogg"
491
+ elif file_name.endswith('.wav'): mime_type = "audio/wav"
492
+ else: mime_type = "image/jpeg"
493
  new_parts.append({"inlineData": {"mimeType": mime_type, "data": base64_data}})
494
 
495
  if history and history[-1]["role"] == "user": history[-1]["parts"].extend(new_parts)
496
  else: history.append({"role": "user", "parts": new_parts})
497
 
498
+ if len(history) > 40:
499
+ history = history[-40:]
500
+ if history[0]["role"] == "model": history = history[1:]
501
+
502
+ # دریافت ۱۰۰ کلید به صورت چرخشی
503
  keys_to_try = get_next_gemini_keys(100)
504
  final_answer = None
505
 
 
514
  try:
515
  final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
516
  break
517
+ except (KeyError, IndexError): continue
518
  except Exception: continue
519
 
520
  try:
521
  if proc_msg:
522
+ msg_id = getattr(proc_msg, 'message_id', None)
523
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
524
  if msg_id: await client.delete_messages(chat_id, [msg_id])
525
  except Exception: pass
526
 
527
  if not final_answer:
528
  if history and history[-1]["role"] == "user": history.pop()
529
+ await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", False)
530
+ return
531
 
532
+ # کسر اعتبار در دیتاست (اگر نامحدود نیست)
533
  if not creds.get("is_premium"):
534
  user_credits_db[str_chat_id]["chat"] -= 1
535
  save_db(user_credits_db)
536
 
537
+ history.append({"role": "model", "parts": [{"text": final_answer}]})
538
  user_states[chat_id]["history"] = history
539
 
540
  try:
 
555
  await send_with_keyboard(client, chat_id, chunk, False)
556
  await asyncio.sleep(2.5)
557
  except Exception: await asyncio.sleep(2.5)
558
+
559
  except Exception:
560
+ await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
561
 
562
+
563
+ # --- ۲. پردازش ساخت عکس ---
564
  async def process_image(client, chat_id, prompt):
565
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
566
  creds = get_user_credits(str_chat_id)
567
+ if creds["image"] <= 0:
568
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت عکس شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
569
+
570
+ if not HF_TOKENS: return await send_with_keyboard(client, chat_id, "❌ توکن‌های هاگینگ فیس تنظیم نشده‌اند.", False)
571
 
572
+ proc_msg = await send_with_keyboard(client, chat_id, "✨ در حال ترجمه و بهینه‌سازی پرامپت شما توسط جیمینای...\n(تبدیل به پرامپت حرفه‌ای)", False)
573
  enhanced_prompt = prompt
574
  if GEMINI_KEYS:
575
+ # دریافت ۱۰۰ کلید به صورت چرخشی
576
  keys_to_try_gemini = get_next_gemini_keys(100)
577
+ gemini_sys_prompt = f"You are an expert AI image generation prompt engineer. Translate the following user input to English, and enhance it with high-quality, highly detailed, 4k resolution, cinematic lighting, and visually striking descriptive keywords. Return ONLY the final English prompt string.\nUser input: {prompt}"
578
  async with aiohttp.ClientSession() as session:
579
  for key in keys_to_try_gemini:
580
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
581
+ payload = {"contents": [{"parts":[{"text": gemini_sys_prompt}]}], "generationConfig": {"temperature": 0.7}}
582
  try:
583
+ async with session.post(url, json=payload, timeout=20) as response:
584
  if response.status == 200:
585
  data = await response.json()
586
  enhanced_prompt = data["candidates"][0]["content"]["parts"][0]["text"].strip()
 
589
 
590
  try:
591
  if proc_msg:
592
+ msg_id = getattr(proc_msg, 'message_id', None)
593
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
594
+ if msg_id: await client.delete_messages(chat_id,[msg_id])
595
  except Exception: pass
596
 
597
  short_preview = enhanced_prompt[:150] + "..." if len(enhanced_prompt) > 150 else enhanced_prompt
598
+ proc_msg = await send_with_keyboard(client, chat_id, f"🎨 در حال طراحی عکس...\n\n📝 پرامپت ساخته شده:\n`{short_preview}`\n\n(ممکن است چند ثانیه زمان ببرد)", False)
599
 
600
  keys_to_try = HF_TOKENS.copy()
601
  random.shuffle(keys_to_try)
602
  generated_image = None
603
+ last_error_log = "هیچ اتصالی برقرار نشد."
604
 
605
  for token in keys_to_try:
606
  try:
607
  hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
608
  generated_image = await hf_client.text_to_image(enhanced_prompt, model="Tongyi-MAI/Z-Image-Turbo")
609
  break
610
+ except Exception as e:
611
+ last_error_log = str(e)
612
+ continue
613
 
614
  try:
615
  if proc_msg:
616
+ msg_id = getattr(proc_msg, 'message_id', None)
617
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
618
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
619
  except Exception: pass
620
 
621
+ if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
622
 
623
+ # کسر اعتبار (ساخت عکس محدود است حتی برای پرو)
624
  user_credits_db[str_chat_id]["image"] -= 1
625
  save_db(user_credits_db)
626
 
627
  try:
628
  file_name = f"image_{uuid.uuid4().hex}.jpg"
629
+ rgb_im = generated_image.convert('RGB')
630
+ rgb_im.save(file_name, format="JPEG", quality=100)
631
  await asyncio.sleep(1)
632
+ caption_text = f"🎨 تصویر شما با پرامپت هوشمند آماده شد!\n\n✨ ایده اولیه: {prompt}"
633
+ upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
634
+ if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ خطا در آپلود روبیکا:\n`{str(upload_result)[:800]}`", True)
635
  if os.path.exists(file_name): os.remove(file_name)
636
+ except Exception as e: await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس:\n{str(e)[:150]}", True)
637
 
638
+ # --- تابع جدید برای ارسال متن به اسپیس مترجم ---
639
  async def translate_text_aloha(prompt_text):
640
  session_hash = ''.join(random.choices(string.ascii_lowercase + string.digits, k=11))
641
+ join_url = "https://hamed744-translate-tts-aloha.hf.space/gradio_api/queue/join"
642
+ payload = {
643
+ "data":[prompt_text, "انگلیسی (آمریکا) - جنی (زن)", 0, 0, 0],
644
+ "fn_index": 1,
645
+ "session_hash": session_hash
646
+ }
647
+
648
  try:
649
  async with aiohttp.ClientSession() as session:
650
+ # پیوستن به صف ترجمه
651
+ async with session.post(join_url, json=payload, timeout=20) as resp:
652
+ if resp.status != 200:
653
+ return prompt_text
654
+
655
+ # دریافت داده‌ها به صورت جریانی (Stream)
656
+ data_url = f"https://hamed744-translate-tts-aloha.hf.space/gradio_api/queue/data?session_hash={session_hash}"
657
+ async with session.get(data_url, timeout=60) as resp:
658
  async for line_bytes in resp.content:
659
  line = line_bytes.decode('utf-8').strip()
660
  if line.startswith("data: "):
661
  try:
662
  json_data = json.loads(line[6:])
663
+ if json_data.get("msg") == "process_completed":
664
+ if json_data.get("success"):
665
+ return json_data["output"]["data"][0]
666
+ break
667
+ except Exception:
668
+ pass
669
+ except Exception:
670
+ pass
671
+
672
  return prompt_text
673
 
674
+ # --- ۲.۵. پردازش ویرایش عکس با FLUX.2-dev ---
675
  async def process_image_edit(client, chat_id, image_bytes, prompt):
676
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
677
  creds = get_user_credits(str_chat_id)
678
+ if creds["edit_image"] <= 0:
679
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار ویرایش عکس شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
680
+
681
+ if not HF_TOKENS:
682
+ return await send_with_keyboard(client, chat_id, "❌ توکن‌های هاگینگ فیس تنظیم نشده‌اند.", False)
683
 
684
+ proc_msg = await send_with_keyboard(client, chat_id, "🪄 در حال ترجمه دستور شما توسط اسپیس و اعمال جادوی FLUX.2...\n(این فرآیند ممکن است کمی طول بکشد)", False)
685
+
686
+ # گرفتن ترجمه از اسپیس درخواستی
687
+ translated_prompt = await translate_text_aloha(prompt)
688
+ if not translated_prompt or translated_prompt.strip() == "":
689
+ translated_prompt = prompt
690
 
691
  keys_to_try = HF_TOKENS.copy()
692
  random.shuffle(keys_to_try)
693
  generated_image = None
694
+ last_error_log = "سرور پاسخ نداد."
695
 
696
  for token in keys_to_try:
697
  try:
698
  hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
699
+ generated_image = await hf_client.image_to_image(
700
+ image_bytes,
701
+ prompt=translated_prompt,
702
+ model="black-forest-labs/FLUX.2-dev"
703
+ )
704
  break
705
+ except Exception as e:
706
+ last_error_log = str(e)
707
+ continue
708
 
709
  try:
710
  if proc_msg:
711
+ msg_id = getattr(proc_msg, 'message_id', None)
712
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
713
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
714
  except Exception: pass
715
 
716
+ if not generated_image:
717
+ return await send_with_keyboard(client, chat_id, f"❌ متأسفانه ویرایش عکس انجام نشد.\n\n⚠️ علت:\n{last_error_log[:200]}", True)
718
 
719
+ # کسر اعتبار (ویرایش عکس محدود است حتی برای پرو)
720
  user_credits_db[str_chat_id]["edit_image"] -= 1
721
  save_db(user_credits_db)
722
 
723
  try:
724
  file_name = f"edited_flux_{uuid.uuid4().hex}.jpg"
725
+ rgb_im = generated_image.convert('RGB')
726
+ rgb_im.save(file_name, format="JPEG", quality=100)
727
  await asyncio.sleep(1)
728
+ caption_text = f"🪄 ویرایش عکس با هوش مصنوعی Flux.2 انجام شد!\n\n✨ تغییرات خواسته شده: {prompt}\n🔤 متن ارسال شده به هوش: {translated_prompt}"
729
+ upload_result = await helper_upload_file(client, chat_id, file_name, "Image", caption_text)
730
+ if upload_result is not True:
731
+ await send_with_keyboard(client, chat_id, f"❌ عکس ساخته شد اما خطا در ارسال به روبیکا رخ داد:\n`{str(upload_result)[:800]}`", True)
732
  if os.path.exists(file_name): os.remove(file_name)
733
+ except Exception as e:
734
+ await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس ویرایش شده:\n{str(e)[:150]}", True)
735
+
736
 
737
+ # --- ۳. پردازش ساخت صدا ---
738
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
739
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
740
  creds = get_user_credits(str_chat_id)
741
+ if creds["tts"] <= 0:
742
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار تبدیل متن به صدای شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
743
 
744
  try:
745
+ proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
746
  payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
747
  headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
748
  audio_bytes = None
749
+ last_error = "پاسخی دریافت نشد"
750
  workers = WORKER_URLS.copy()
751
  random.shuffle(workers)
752
 
 
759
  if 'audio' in content_type or response.content_length > 1000:
760
  audio_bytes = await response.read()
761
  break
762
+ else: last_error = "فایل نامعتبر"
763
+ else: last_error = f"ارور ({response.status})"
764
+ except Exception as e: last_error = f"خطا: {str(e)}"; continue
765
 
766
  try:
767
  if proc_msg:
768
+ msg_id = getattr(proc_msg, 'message_id', None)
769
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
770
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
771
  except Exception: pass
772
 
773
  if audio_bytes:
774
+ # کسر اعتبار در دیتاست (اگر نامحدود نیست)
775
  if not creds.get("is_premium"):
776
  user_credits_db[str_chat_id]["tts"] -= 1
777
  save_db(user_credits_db)
778
+
779
  file_name = f"audio_{uuid.uuid4().hex}.mp3"
780
  with open(file_name, "wb") as f: f.write(audio_bytes)
781
  await asyncio.sleep(1)
782
+ upload_result = await helper_upload_file(client, chat_id, file_name, "Voice", "✅ صدای شما آماده شد.")
783
+ if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ ارور آپلود:\n`{str(upload_result)[:800]}`", True)
784
  if os.path.exists(file_name): os.remove(file_name)
785
+ else: await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
786
+ except Exception: traceback.print_exc()
787
 
788
+
789
+ # --- ۳.۵. پردازش پادکست ---
790
  async def process_podcast(client, chat_id, prompt):
791
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
792
  creds = get_user_credits(str_chat_id)
793
+ if creds["podcast"] <= 0:
794
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت پادکست شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
795
+
796
+ proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست توسط هوش مصنوعی...\n(لطفاً صبور باشید)", False)
797
+ available_speakers =[]
798
+ for num_key, (spk_name, spk_id) in SPEAKERS.items():
799
+ gender = "male" if "مرد" in spk_name else "female"
800
+ available_speakers.append({"id": spk_id, "name": spk_name.split(' (')[0], "gender": gender})
801
 
802
+ url_create = "https://ezmarynoori-podgen.hf.space/api/create-full-podcast"
803
+ payload_create = {"prompt": prompt, "available_speakers": available_speakers}
804
 
805
  async with aiohttp.ClientSession() as session:
806
  try:
807
+ async with session.post(url_create, json=payload_create, timeout=60) as resp:
808
+ if resp.status != 202: return await send_with_keyboard(client, chat_id, f"❌ خطا در سرور پادکست: وضعیت {resp.status}", True)
809
  task_id = (await resp.json()).get("task_id")
810
+ except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در اتصال به سرور پادکست:\n{str(e)}", True)
811
 
812
+ url_status = f"https://ezmarynoori-podgen.hf.space/api/podcast-status/{task_id}"
813
  script_data = None
814
  for _ in range(40):
815
  await asyncio.sleep(3)
816
  try:
817
+ async with session.get(url_status) as resp:
818
  if resp.status == 200:
819
  status_data = await resp.json()
820
  if status_data.get("status") == "completed":
821
  script_data = status_data.get("data", {}).get("script",[])
822
  break
823
+ elif status_data.get("status") == "failed":
824
+ return await send_with_keyboard(client, chat_id, "❌ متأسفانه هوش مصنوعی نتوانست برای این موضوع سناریو بنویسد.", True)
825
  except Exception: pass
826
 
827
+ if not script_data: return await send_with_keyboard(client, chat_id, "❌ زمان انتظار برای نوشتن سناریو به پایان رسید.", True)
828
 
829
  try:
830
  if proc_msg:
831
+ msg_id = getattr(proc_msg, 'message_id', None)
832
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
833
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
834
  except: pass
835
 
836
+ proc_msg = await send_with_keyboard(client, chat_id, f"🎙 سناریو نوشته شد! در حال ریکورد و میکس دیالوگ‌های گویندگان (شامل {len(script_data)} نوبت صحبت)...\nاین مرحله زمان‌بر است...", False)
837
+
838
  combined_audio = AudioSegment.empty()
839
+ url_generate = "https://ezmarynoori-podgen.hf.space/api/generate"
840
 
841
+ for index, turn in enumerate(script_data):
842
+ payload_gen = {"text": turn["dialogue"], "speaker": turn["speaker_id"], "temperature": 0.9}
843
  chunk_audio_bytes = None
844
+ for attempt in range(3):
845
  try:
846
+ async with session.post(url_generate, json=payload_gen, timeout=120) as resp:
847
  if resp.status == 200:
848
  chunk_audio_bytes = await resp.read()
849
  break
850
  except Exception: await asyncio.sleep(2)
851
+
852
+ if not chunk_audio_bytes: return await send_with_keyboard(client, chat_id, f"❌ خطا در تولید صدای بخش {index+1} از سرور. عملیات متوقف شد.", True)
853
+
854
+ try:
855
+ audio_segment = AudioSegment.from_file(io.BytesIO(chunk_audio_bytes))
856
+ combined_audio += audio_segment
857
+ except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
858
 
859
+ # کسر اعتبار در دیتاست (اگر نامحدود نیست)
860
  if not creds.get("is_premium"):
861
  user_credits_db[str_chat_id]["podcast"] -= 1
862
  save_db(user_credits_db)
 
866
 
867
  try:
868
  if proc_msg:
869
+ msg_id = getattr(proc_msg, 'message_id', None)
870
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
871
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
872
  except: pass
873
 
874
+ caption = f"🎧 پادکست شما با موفقیت میکس و ساخته شد!\n\n💡 موضوع شما: {prompt}"
875
+ upload_result = await helper_upload_file(client, chat_id, file_name, "Voice", caption)
876
+ if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ پادکست ساخته شد اما روبیکا خطای آپلود داد.\n\n`{str(upload_result)[:800]}`", True)
877
  if os.path.exists(file_name): os.remove(file_name)
878
 
879
+
880
+ # --- ۴. پردازش تبدیل فایل صوتی/تصویری به متن ---
881
  async def process_stt(client, chat_id, audio_bytes, file_name):
882
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
883
  creds = get_user_credits(str_chat_id)
884
+ if creds["stt"] <= 0:
885
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار تبدیل صدا به متن شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
886
+
887
+ if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای جیمینای تنظیم نشده‌اند.", False)
888
 
889
+ proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال گوش دادن و پیاده‌سازی متن...", False)
890
  base64_data = base64.b64encode(audio_bytes).decode('utf-8')
891
  mime_type, _ = mimetypes.guess_type(file_name)
892
  if not mime_type: mime_type = "audio/ogg"
893
 
894
+ # دریافت ۱۰۰ کلید به صورت چرخشی
895
  keys_to_try = get_next_gemini_keys(100)
896
  transcribed_text = None
897
+ prompt = "لطفاً این فایل صوتی/تصویری را با دقت کامل گوش بده و صحبت‌های داخل آن را کلمه به کلمه به متن تبدیل کن. هیچ توضیح اضافه‌ای نده."
898
 
899
  async with aiohttp.ClientSession() as session:
900
  for key in keys_to_try:
901
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
902
+ payload = {"contents": [{"parts":[{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}], "generationConfig": {"temperature": 0.2}}
903
  try:
904
  async with session.post(url, json=payload, timeout=60) as response:
905
  if response.status == 200:
 
910
 
911
  try:
912
  if proc_msg:
913
+ msg_id = getattr(proc_msg, 'message_id', None)
914
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
915
  if msg_id: await client.delete_messages(chat_id, [msg_id])
916
  except Exception: pass
917
 
918
  if transcribed_text:
919
+ # کسر اعتبار در دیتاست (اگر نامحدود نیست)
920
  if not creds.get("is_premium"):
921
  user_credits_db[str_chat_id]["stt"] -= 1
922
  save_db(user_credits_db)
923
  await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
924
+ else:
925
+ await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", True)
926
 
927
+
928
+ # --- ۵. پردازش تحلیل فایل مستقل ---
929
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
930
  str_chat_id = str(chat_id).replace("`", "").replace("'", "").replace('"', "").strip()
931
  creds = get_user_credits(str_chat_id)
932
+ if creds["file"] <= 0:
933
+ return await send_with_keyboard(client, chat_id, "❌ اعتبار تحلیل فایل شما تمام شده است. لطفاً از منوی اصلی وارد بخش «خرید اشتراک 💎» شوید.", False)
934
+
935
+ if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید جیمینای تنظیم نیست.", False)
936
 
937
  proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
938
  base64_data = base64.b64encode(file_bytes).decode('utf-8')
939
  mime_type, _ = mimetypes.guess_type(file_name)
940
  if not mime_type: mime_type = "image/jpeg"
941
 
942
+ # دریافت ۱۰۰ کلید به صورت چرخشی
943
  keys_to_try = get_next_gemini_keys(100)
944
  final_answer = None
945
 
 
957
 
958
  try:
959
  if proc_msg:
960
+ msg_id = getattr(proc_msg, 'message_id', None)
961
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
962
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
963
  except Exception: pass
964
 
965
  if final_answer:
966
+ # کسر اعتبار در دیتاست (اگر نامحدود نیست)
967
  if not creds.get("is_premium"):
968
  user_credits_db[str_chat_id]["file"] -= 1
969
  save_db(user_credits_db)
970
  await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
971
+ else:
972
+ await send_with_keyboard(client, chat_id, "❌ سرور شلوغ است فعلا بعدا امتحان کنید.", True)
973
 
974
 
975
  # --- تنظیمات ربات روبیکا ---
 
982
  async def main_handler(client, update):
983
  global BOT_GUID
984
 
985
+ # 🔥 سیستم ضد رگبار و تخلیه صف (Backlog) سرور 🔥
986
+ # در صورت هجوم پیام‌های تلنبار شده سرور روبیکا، پیام‌ها رد می‌شوند
987
  if is_backlog_burst():
988
  return
989
 
 
 
990
  try:
991
+ # 🛡 دریافت آیدی یکتای ربات برای جلوگیری از لوپ 🛡
 
 
 
 
 
 
 
 
 
992
  if not BOT_GUID:
993
  try:
994
  me_info = await client.get_me()
995
  if me_info and hasattr(me_info, 'user'):
996
  BOT_GUID = getattr(me_info.user, 'user_guid', None)
997
+ except Exception:
998
+ pass
999
 
1000
+ msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
1001
+
1002
+ # --- استخراج دقیق شناسه فرستنده (برای جلوگیری از حرف زدن ربات با خودش) ---
1003
+ author_id = getattr(update, 'author_guid', None)
1004
+ if not author_id and msg_obj:
1005
+ author_id = msg_obj.get('author_object_guid') if isinstance(msg_obj, dict) else getattr(msg_obj, 'author_object_guid', None)
1006
+
1007
+ # ⛔️ حیاتی‌ترین بخش: اگر نویسنده پیام خودِ ربات است، هرگز پردازش نشود!
1008
  if BOT_GUID and author_id == BOT_GUID:
1009
  return
1010
+
1011
+ # --- استخراج دقیق شناسه چت ---
1012
+ chat_id = getattr(update, 'object_guid', None) or getattr(update, 'author_guid', None) or getattr(update, "chat_id", None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1013
  if not chat_id: return
1014
 
1015
+ # استخراج آیدی پیام
1016
  msg_id = getattr(update, "message_id", None)
1017
  if not msg_id and msg_obj:
1018
+ msg_id = msg_obj.get("message_id") if isinstance(msg_obj, dict) else getattr(msg_obj, "message_id", None)
1019
+
1020
+ # 🛡 کلید ترکیبی ضد تداخل: آیدی چت + آیدی پیام 🛡
1021
+ unique_msg_key = f"{chat_id}_{msg_id}" if msg_id else None
1022
+ if unique_msg_key:
1023
  if unique_msg_key in processed_message_ids: return
1024
  processed_message_ids.add(unique_msg_key)
1025
  if len(processed_message_ids) > 5000: processed_message_ids.clear()
1026
 
1027
+ current_time = time.time()
1028
+
1029
+ # 🛡 سیستم محدودکننده سرعت کاربر (Rate Limit) 🛡
1030
  last_req_time = user_last_request_time.get(chat_id, 0)
1031
  if current_time - last_req_time < 1.0:
1032
  return
1033
  user_last_request_time[chat_id] = current_time
1034
 
1035
+ # تشخیص محتوای متنی
1036
+ user_text = getattr(update, "text", "") or getattr(msg_obj, "text", "")
1037
  user_text_str = str(user_text).strip() if user_text else ""
1038
  user_text_lower = user_text_str.lower()
1039
 
1040
+ # ⛔️ لایه امنیتی دوم برای جلوگیری از پاسخ دادن ربات به پیام‌های سیستم خودش
1041
  if user_text_str and user_text_str.startswith(("⏳", "❌", "✅", "🧠", "🎨", "🪄", "🎙", "📻", "📝", "👁️", "💡", "📥")):
1042
  return
1043
 
1044
  if chat_id not in user_states:
1045
  user_states[chat_id] = {"mode": None, "text": "", "history":[], "file_bytes": None, "file_name": None}
1046
 
1047
+ # 🛠 --- سیستم پنل مدیریت (بدون نیاز به لاگین) --- 🛠
1048
  if user_text_lower.startswith(f"{ADMIN_CODE} pro=") or user_text_lower.startswith(f"{ADMIN_CODE}pro="):
1049
  parts = user_text_str.split("=", 1)
1050
  if len(parts) >= 2:
 
1052
  if target_id:
1053
  if target_id not in user_credits_db:
1054
  user_credits_db[target_id] = {
1055
+ "is_premium": False,
1056
+ "expire_date": None,
1057
+ "last_reset": "",
1058
+ "chat": 10,
1059
+ "image": 5,
1060
+ "edit_image": 1,
1061
+ "podcast": 2,
1062
+ "tts": 5,
1063
+ "file": 1,
1064
+ "stt": 5
1065
  }
1066
+
1067
  user_credits_db[target_id]["is_premium"] = True
1068
  expire_time = datetime.datetime.now() + datetime.timedelta(days=30)
1069
  user_credits_db[target_id]["expire_date"] = expire_time.isoformat()
1070
+
1071
  user_credits_db[target_id]["chat"] = 999999
1072
  user_credits_db[target_id]["image"] = 20
1073
  user_credits_db[target_id]["edit_image"] = 10
 
1075
  user_credits_db[target_id]["tts"] = 999999
1076
  user_credits_db[target_id]["file"] = 999999
1077
  user_credits_db[target_id]["stt"] = 999999
1078
+
1079
  save_db(user_credits_db)
1080
+ await send_with_keyboard(client, chat_id, f"✅ حساب کاربر `{target_id}` به مدت ۳۰ روز شارژ شد و به پرو ارت��ا یافت.", False)
1081
+ try:
1082
+ await send_with_keyboard(client, target_id, "🎉 **کاربر گرامی، تبریک!**\n\nحساب شما با موفقیت توسط پشتیبانی به **🌟 نسخه پرو (ویژه)** ارتقا یافت.\nهم‌اکنون بسته‌های نامحدود و طلایی یک‌ماهه شما فعال گردید.\n\nجهت مشاهده جزئیات روی دکمه «حساب کاربری 👤» کلیک کنید.", True)
1083
+ except Exception: pass
1084
  return
1085
 
1086
  if user_text_lower.startswith(f"{ADMIN_CODE} free=") or user_text_lower.startswith(f"{ADMIN_CODE}free="):
1087
  parts = user_text_str.split("=", 1)
1088
  if len(parts) >= 2:
1089
  target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
1090
+ if target_id:
1091
+ if target_id in user_credits_db:
1092
+ user_credits_db[target_id]["is_premium"] = False
1093
+ user_credits_db[target_id]["expire_date"] = None
1094
+ user_credits_db[target_id]["last_reset"] = ""
1095
+ save_db(user_credits_db)
1096
+ await send_with_keyboard(client, chat_id, f"✅ اشتراک کاربر `{target_id}` لغو شد و به رایگان تبدیل گشت.", False)
1097
+ try:
1098
+ await send_with_keyboard(client, target_id, "⚠️ کاربر گرامی، اشتراک ویژه شما به پایان رسید و حساب شما به نسخه رایگان (آزمایشی) تغییر یافت.", True)
1099
+ except Exception: pass
1100
  return
1101
 
1102
  if user_text_lower.startswith(f"{ADMIN_CODE} =") or user_text_lower.startswith(f"{ADMIN_CODE}="):
1103
  parts = user_text_str.split("=", 1)
1104
  if len(parts) >= 2:
1105
  target_id = parts[1].replace("`", "").replace("'", "").replace('"', "").strip()
1106
+ if target_id:
1107
+ if target_id in user_credits_db:
1108
+ t_creds = user_credits_db[target_id]
1109
+ is_prem = t_creds.get("is_premium", False)
1110
+
1111
+ if is_prem:
1112
+ expire_txt = "نامشخص"
1113
+ days_left = 0
1114
+ if t_creds.get("expire_date"):
1115
+ exp_d = datetime.datetime.fromisoformat(t_creds["expire_date"])
1116
+ now = datetime.datetime.now()
1117
+ diff = exp_d - now
1118
+ days_left = diff.days if diff.days >= 0 else 0
1119
+
1120
+ jy, jm, jd = gregorian_to_jalali(exp_d.year, exp_d.month, exp_d.day)
1121
+ expire_txt = f"{jy:04d}/{jm:02d}/{jd:02d}"
1122
+ status_text = f"🌟 نسخه پرو (ویژه)\n📅 انقضا: {expire_txt}\n⏳ باقیمانده: {days_left} روز"
1123
+ else:
1124
+ status_text = "🥉 نسخه رایگان (آزمایشی)\n⏳ سهمیه روزانه"
1125
+
1126
+ chat_rem = "نامحدود ∞" if is_prem else t_creds.get('chat', 0)
1127
+ podcast_rem = "نامحدود ∞" if is_prem else t_creds.get('podcast', 0)
1128
+ tts_rem = "نامحدود ∞" if is_prem else t_creds.get('tts', 0)
1129
+ file_rem = "نامحدود ∞" if is_prem else t_creds.get('file', 0)
1130
+ stt_rem = "نامحدود ∞" if is_prem else t_creds.get('stt', 0)
1131
+ image_rem = t_creds.get('image', 0)
1132
+ edit_image_rem = t_creds.get('edit_image', 0)
1133
+
1134
+ info_msg = f"""🔍 **اطلاعات کاربر `{target_id}`:**
1135
+
1136
+ 🔹 **وضعیت:** {status_text}
1137
+
1138
+ 📊 **سهمیه:**
1139
+ - چت: {chat_rem}
1140
+ - تولید عکس: {image_rem}
1141
+ - ویرایش عکس: {edit_image_rem}
1142
+ - پادکست: {podcast_rem}
1143
+ - متن به صدا: {tts_rem}
1144
+ - تحلیل فایل: {file_rem}
1145
+ - صدا به متن: {stt_rem}"""
1146
+ await send_with_keyboard(client, chat_id, info_msg, False)
1147
+ else:
1148
+ await send_with_keyboard(client, chat_id, f"❌ کاربری با شناسه `{target_id}` در دیتابیس یافت نشد.", False)
1149
  return
1150
 
1151
  # تشخیص فایل
1152
  is_file = False
1153
  file_name = f"unknown_file_{uuid.uuid4().hex[:6]}.jpg"
1154
+
1155
  if msg_obj:
1156
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
1157
  file_attr = getattr(msg_obj, attr, None)
 
1159
  is_file = True
1160
  file_name = getattr(file_attr, 'file_name', file_name)
1161
  break
1162
+
1163
  if not is_file and hasattr(msg_obj, 'to_dict'):
1164
  msg_dict = msg_obj.to_dict()
1165
  for attr in['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
 
1172
  if user_text_str in["/start", "سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
1173
  user_states[chat_id]["mode"] = None
1174
  user_states[chat_id]["file_bytes"] = None
1175
+ await send_with_keyboard(client, chat_id, "سلام! به ربات هوش مصنوعی آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True)
1176
  return
1177
 
1178
+ # --- حساب کاربری ---
1179
+ if user_text_str in ["/account", "حساب کاربری 👤"]:
1180
  creds = get_user_credits(chat_id)
1181
  is_prem = creds.get("is_premium", False)
1182
+
1183
  if is_prem:
1184
+ expire_txt = "نامشخص"
1185
  days_left = 0
1186
  if creds.get("expire_date"):
1187
+ exp_d = datetime.datetime.fromisoformat(creds["expire_date"])
1188
+ now = datetime.datetime.now()
1189
+ diff = exp_d - now
1190
+ days_left = diff.days if diff.days >= 0 else 0
1191
+
1192
+ jy, jm, jd = gregorian_to_jalali(exp_d.year, exp_d.month, exp_d.day)
1193
+ expire_txt = f"{jy:04d}/{jm:02d}/{jd:02d} (ساعت {exp_d.hour:02d}:{exp_d.minute:02d})"
1194
+
1195
+ status_text = "🌟 نسخه پرو (ویژه)"
1196
+ expire_info = f"\n📅 **تاریخ انقضا:** {expire_txt}\n⏳ **زمان باقیمانده:** {days_left} روز"
1197
+ daily_note = "*نکته: سهمیه پردازشی شما مختص همین دوره یک‌ماهه می‌باشد.*"
1198
+ else:
1199
+ status_text = "🥉 نسخه رایگان (آزمایشی)"
1200
+ expire_info = ""
1201
+ daily_note = "*نکته: سهمیه شما هر روز ساعت ۰۰:۰۰ بامداد به صورت خودکار مجدداً شارژ می‌گردد.*"
1202
+
1203
+ chat_rem = "نامحدود ∞" if is_prem else creds['chat']
1204
+ podcast_rem = "نامح��ود ∞" if is_prem else creds['podcast']
1205
+ tts_rem = "نامحدود ∞" if is_prem else creds['tts']
1206
+ file_rem = "نامحدود ∞" if is_prem else creds['file']
1207
+ stt_rem = "نامحدود ∞" if is_prem else creds['stt']
1208
+ image_rem = creds['image']
1209
+ edit_image_rem = creds['edit_image']
1210
+
1211
+ account_profile = f"""👤 **اطلاعات حساب کاربری شما**
1212
+
1213
+ 🔹 **شناسه یکتا:** `{chat_id}`
1214
+ 🔹 **وضعیت اشتراک:** {status_text}{expire_info}
1215
+
1216
+ 📊 **سهمیه باقی‌مانده شما:**
1217
+ - 💬 چت هوشمند: {chat_rem}
1218
+ - 🎨 تولید تصویر: {image_rem} عدد
1219
+ - 🪄 ویرایش تصویر: {edit_image_rem} عدد
1220
+ - 🎙 ساخت پادکست: {podcast_rem}
1221
+ - 🗣 تبدیل متن به صدا: {tts_rem}
1222
+ - 📁 تحلیل فایل و سند: {file_rem}
1223
+ - 📝 تبدیل صدا به متن: {stt_rem}
1224
+
1225
+ {daily_note}"""
1226
  await send_with_keyboard(client, chat_id, account_profile, True)
1227
  return
1228
 
1229
+ # --- دکمه خرید اشتراک ---
1230
+ if user_text_str in ["/buy", "خرید اشتراک 💎"]:
1231
+ buy_text = f"""💎 **خرید اشتراک ویژه آلفا پرو (یک ماهه)**
1232
+
1233
+ با تهیه اشتراک ویژه، محدودیت‌ها را کنار بزنید و از نهایت قدرت هوش مصنوعی لذت ببرید! 🚀
1234
+
1235
+ 🎁 **بسته طلایی یک‌ماهه شامل:**
1236
+ 🤖 چت با هوش مصنوعی بصورت نامحدود
1237
+ 🗣 تبدیل متن به صدا بصورت نامحدود با ۳۰ گوینده
1238
+ 🎙 ساخت پادکست بصورت نامحدود
1239
+
1240
+ 🪄 ۱۰ ویرایش تصویر
1241
+ 🎨 ۲۰ تولید تصویر
1242
+ 📁 تحلیل نامحدود فایل و سند
1243
+ 📝 تبدیل فایل صوتی به متن نامحدود
1244
+
1245
+ 💳 **هزینه اشتراک یک ماهه:** 250 هزار تومان
1246
+
1247
+ 💳 **شماره کارت جهت واریز:**
1248
+ `6219861411958035`
1249
+ 👤 **به نام:** کوهی
1250
+
1251
+ ✅ **نحوه فعال‌سازی:**
1252
+ پس از واریز مبلغ، لطفاً رسید پرداختی را به همراه **شناسه یکتای خود** (که در پایین آمده) به آیدی پشتیبانی زیر ارسال کنید تا اشتراک شما فعال گردد:
1253
+
1254
+ 🔑 **شناسه یکتای شما:** `{chat_id}`
1255
+
1256
+ 👨‍💻 **ارتباط با پشتیبانی:**
1257
+ 🆔 @H_a_m_e_d100"""
1258
  await send_with_keyboard(client, chat_id, buy_text, True)
1259
  return
1260
 
1261
+ # --- دکمه انتقال اکانت ---
1262
  if user_text_str in["/transfer", "انتقال اکانت از برنامه به ربات"]:
1263
+ transfer_text = f"""🔄 **انتقال اکانت از برنامه به ربات**
1264
+
1265
+ کاربر گرامی، در صورتی که داخل برنامه «هوش مصنوعی آلفا» پیش‌تر اشتراک تهیه کرده‌اید، نیازی به خرید مجدد اشتراک داخل ربات نیست! 🎉
1266
+
1267
+ کافیست **شناسه یکتای** ربات روبیکای خود را کپی کرده و برای پشتیبانی ما در برنامه هوش مصنوعی آلفا ارسال کنید تا اکانت اشتراکی شما به سرعت از برنامه به ربات روبیکا انتقال داده شود.
1268
+
1269
+ 🔑 **شناسه یکتای ربات شما:** `{chat_id}`
1270
+
1271
+ 👨‍💻 **دقت کنید شناسه ربات رو به پشتیبانی داخل خود برنامه هوش مصنوعی آلفا ارسال کنید:**
1272
+ 🆔 @H_a_m_e_d100"""
1273
  await send_with_keyboard(client, chat_id, transfer_text, True)
1274
  return
1275
 
1276
  if user_text_str in["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]:
1277
  user_states[chat_id]["mode"] = "chat"
1278
  user_states[chat_id]["history"] =[]
1279
+ await send_with_keyboard(client, chat_id, "💬 شما وارد بخش **چت با هوش مصنوعی** شدید.\n\nهر سوالی دارید بفرستید تا جواب بدم:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1280
  return
1281
 
1282
  if user_text_str in["/image", "🎨 عکس", "ساخت تصاویر🎨"]:
1283
  user_states[chat_id]["mode"] = "image_waiting_for_text"
1284
+ await send_with_keyboard(client, chat_id, "🎨 شما وارد بخش **ساخت عکس پیشرفته** شدید.\n\nمتن خود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1285
  return
1286
 
1287
+ if user_text_str in ["/edit_image", "ویرایش تصاویر 🪄"]:
1288
  user_states[chat_id]["mode"] = "image_edit_waiting_for_image"
1289
  user_states[chat_id]["file_bytes"] = None
1290
+ await send_with_keyboard(client, chat_id, "🪄 به بخش **ویرایش عکس (Flux.2)** خوش آمدید.\n\nلطفاً ابتدا عکسی که می‌خواهید ویرایش کنید را بفرستید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1291
  return
1292
 
1293
+ if user_text_str in ["/tts", "🎙️ صدا", "تبدیل متن به صدا🗣️"]:
1294
  user_states[chat_id]["mode"] = "tts_waiting_for_text"
1295
+ await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تبدیل متن به صدا** شدید.\n\nلطفاً متنی که می‌خواهید به صدا تبدیل شود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1296
  return
1297
 
1298
  if user_text_str in["/podcast", "📻 پادکست", "ساخت پادکست 🎙️"]:
1299
  user_states[chat_id]["mode"] = "podcast_waiting_for_topic"
1300
+ await send_with_keyboard(client, chat_id, "📻 شما وارد بخش **ساخت پادکست** شدید.\n\nلطفاً موضوع پادکست خود را بفرستید.\nمثال: درباره تاریخچه پیدایش قهوه با ۳ گوینده یک پادکست جذاب بساز\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1301
  return
1302
 
1303
+ if user_text_str in["/file", "تحلیل فایل 📁"]:
1304
  user_states[chat_id]["mode"] = "file_waiting_for_file"
1305
  user_states[chat_id]["file_bytes"] = None
1306
+ await send_with_keyboard(client, chat_id, "📁 شما وارد بخش **تحلیل فایل اختصاصی** شدید.\n\nلطفاً فایل خود را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1307
  return
1308
 
1309
  if user_text_str in["/stt", "فایل صوتی به متن 📝"]:
1310
  user_states[chat_id]["mode"] = "stt_waiting_for_audio"
1311
+ await send_with_keyboard(client, chat_id, "📝 شما وارد بخش **تبدیل صدا به متن** شدید.\n\nلطفاً فایل خود (ویس، آهنگ، ویدیو و...) را ارسال کنید:\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
1312
  return
1313
 
1314
  current_mode = user_states[chat_id].get("mode")
1315
 
1316
  if current_mode is None:
1317
  if is_file: pass
1318
+ elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True)
1319
  return
1320
 
1321
  elif current_mode == "chat":
 
1324
  try:
1325
  file_bytes = await helper_download_file(client, msg_obj)
1326
  asyncio.create_task(process_gemini(client, chat_id, user_text_str, file_bytes=file_bytes, file_name=file_name))
1327
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
1328
  elif user_text_str: asyncio.create_task(process_gemini(client, chat_id, user_text_str))
1329
  return
1330
 
 
1333
  return
1334
 
1335
  elif current_mode == "image_edit_waiting_for_image":
1336
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک عکس ارسال کنید.", False)
1337
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود عکس...", False)
1338
  try:
1339
  file_bytes = await helper_download_file(client, msg_obj)
1340
  user_states[chat_id]["file_bytes"] = file_bytes
1341
  user_states[chat_id]["mode"] = "image_edit_waiting_for_prompt"
1342
+ await send_with_keyboard(client, chat_id, "✅ عکس با موفقیت دریافت شد.\n\nحالا دستور خود را به صورت متنی تایپ کنید.\nمثال: یک کلاه قرمز روی سر این گربه بگذار.", False)
1343
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت عکس!\n{str(dl_err)}", False)
1344
  return
1345
 
1346
  elif current_mode == "image_edit_waiting_for_prompt":
 
1349
  user_states[chat_id]["mode"] = None
1350
  user_states[chat_id]["file_bytes"] = None
1351
  asyncio.create_task(process_image_edit(client, chat_id, saved_bytes, user_text_str))
1352
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
1353
  return
1354
 
1355
  elif current_mode == "tts_waiting_for_text":
1356
  if user_text_str:
1357
+ if len(user_text_str) > 2500: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً متنی کوتاه‌تر از 2500 کاراکتر بفرستید.", False)
1358
  user_states[chat_id]["text"] = user_text_str
1359
  user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
1360
+
1361
+ speakers_menu = """✅ متن شما ذخیره شد.
1362
+ لطفاً **شماره** گوینده مورد نظر خود را بفرستید:
1363
+ 1. شهاب | 2. آوا | 3. نوید
1364
+ 4. آرمان | 5. مهسا | 6. دانا
1365
+ 7. سامان | 8. آرش | 9. شبنم
1366
+ 10. سحر | 11. مریم | 12. بهرام
1367
+ 13. نیکان| 14. فرناز | 15. سارا
1368
+ 16. مانی | 17. آرتین | 18. دلنواز
1369
+ 19. روژان | 20. امید | 21. بردیا
1370
+ 22. ترانه | 23. نیکو | 24. هستی
1371
+ 25. کامیار| 26. کیانوش| 27. پویا
1372
+ 28. مهتاب | 29. سام | 30. لیدا"""
1373
+ await send_with_keyboard(client, chat_id, speakers_menu, False)
1374
  return
1375
 
1376
  elif current_mode == "tts_waiting_for_speaker":
1377
  normalized_text = to_english_digits(user_text_str)
1378
+
1379
  if normalized_text.isdigit() and normalized_text in SPEAKERS:
1380
  spk_name, spk_id = SPEAKERS[normalized_text]
1381
  txt = user_states[chat_id]["text"]
1382
  user_states[chat_id]["mode"] = "tts_waiting_for_text"
1383
  asyncio.create_task(process_tts(client, chat_id, txt, spk_id, spk_name))
1384
+ else:
1385
+ await send_with_keyboard(client, chat_id, "❌ شماره نامعتبر است! لطفاً فقط یک عدد بین 1 تا 30 بفرستید.", False)
1386
  return
1387
 
1388
  elif current_mode == "podcast_waiting_for_topic":
1389
  if user_text_str: asyncio.create_task(process_podcast(client, chat_id, user_text_str))
1390
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً موضوع پادکست خود را به صورت متنی بفرستید.", False)
1391
  return
1392
 
1393
  elif current_mode == "stt_waiting_for_audio":
1394
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل ارسال کنید.", False)
1395
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
1396
  try:
1397
  audio_bytes = await helper_download_file(client, msg_obj)
1398
  asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name))
1399
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دانلود فایل!\n{str(dl_err)}", False)
1400
  return
1401
 
1402
  elif current_mode == "file_waiting_for_file":
1403
+ if not is_file: return await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک فایل ارسال کنید.", False)
1404
+ await send_with_keyboard(client, chat_id, "📥 در حال دریافت فایل...", False)
1405
  try:
1406
  file_bytes = await helper_download_file(client, msg_obj)
1407
  user_states[chat_id]["file_bytes"] = file_bytes
1408
  user_states[chat_id]["file_name"] = file_name
1409
  user_states[chat_id]["mode"] = "file_waiting_for_prompt"
1410
+ await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً متنی بگویید **چگونه تحلیل شود؟**", False)
1411
+ except Exception as dl_err: await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n{str(dl_err)}", False)
1412
  return
1413
 
1414
  elif current_mode == "file_waiting_for_prompt":
 
1417
  saved_name = user_states[chat_id].get("file_name", "file.jpeg")
1418
  user_states[chat_id]["mode"] = "file_waiting_for_file"
1419
  asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str))
1420
+ else: await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را متنی بنویسید.", False)
1421
  return
1422
 
1423
  except Exception: traceback.print_exc()
 
1425
  if __name__ == "__main__":
1426
  threading.Thread(target=run_flask, daemon=True).start()
1427
  if bot_token:
1428
+ print("ربات آلفا پرو با سیستم اشتراک نامحدود + سپر امنیتی Burst Controller روشن شد...")
1429
  bot.run()