Opera8 commited on
Commit
011aaa9
·
verified ·
1 Parent(s): a19ee96

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +397 -179
main.py CHANGED
@@ -18,13 +18,13 @@ app = Flask(__name__)
18
 
19
  @app.route('/')
20
  def home():
21
- return "ربات یکپارچه آلفا (نسخه پرو نهایی) روشن است! 🚀"
22
 
23
  def run_flask():
24
  app.run(host="0.0.0.0", port=7860)
25
 
26
 
27
- # --- ساختار کاملاً خالص کیبورد پایین صفحه (Chat Keypad) ---
28
  MAIN_KEYPAD_DICT = {
29
  "rows": [
30
  {
@@ -62,7 +62,7 @@ MAIN_KEYPAD_DICT = {
62
  }
63
 
64
 
65
- # --- تابع ارسال منحصراً برای کیبورد پایین صفحه (هک مستقیم API) ---
66
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
67
  if not use_keyboard:
68
  msg = await client.send_message(chat_id, text)
@@ -88,9 +88,10 @@ async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
88
  return msg
89
 
90
 
91
- # --- تابع هوشمند دانلود فایل ---
92
  async def helper_download_file(client, msg_obj):
93
  errors = []
 
94
  file_id = None
95
  file_obj = None
96
  for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
@@ -101,17 +102,22 @@ async def helper_download_file(client, msg_obj):
101
  elif isinstance(val, dict) and 'file_id' in val: file_id = val['file_id']
102
  if file_id: break
103
 
 
104
  temp_name = f"temp_dl_{random.randint(1000, 999999)}.tmp"
105
 
106
  if hasattr(client, "download_file"):
 
 
107
  try:
108
  await client.download_file(msg_obj, temp_name)
109
  if os.path.exists(temp_name):
110
  with open(temp_name, 'rb') as f: data = f.read()
111
  os.remove(temp_name)
112
  return data
113
- except Exception as e: errors.append(f"تلاش ۱: {e}")
 
114
 
 
115
  if file_obj:
116
  try:
117
  await client.download_file(file_obj, temp_name)
@@ -119,8 +125,10 @@ async def helper_download_file(client, msg_obj):
119
  with open(temp_name, 'rb') as f: data = f.read()
120
  os.remove(temp_name)
121
  return data
122
- except Exception as e: errors.append(f"تلاش ۲: {e}")
 
123
 
 
124
  if file_id:
125
  try:
126
  await client.download_file(file_id, file_name=temp_name)
@@ -128,12 +136,25 @@ async def helper_download_file(client, msg_obj):
128
  with open(temp_name, 'rb') as f: data = f.read()
129
  os.remove(temp_name)
130
  return data
131
- except Exception as e: errors.append(f"تلاش ۳: {e}")
 
132
 
133
- raise Exception("دانلود ناموفق:\n" + "\n".join(errors))
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
 
136
- # --- لیست سرورهای تولید صدا ---
137
  WORKER_URLS =[
138
  "https://hamed744-ttspro.hf.space/generate",
139
  "https://hamed744-ttspro2.hf.space/generate",
@@ -147,16 +168,16 @@ WORKER_URLS =[
147
  ]
148
 
149
  SPEAKERS = {
150
- "1": ("شهاب", "Charon"), "2": ("آوا", "Zephyr"), "3": ("نوید", "Achird"),
151
- "4": ("آرمان", "Zubenelgenubi"), "5": ("مهسا", "Vindemiatrix"), "6": ("دانا", "Rasalgethi"),
152
- "7": ("سامان", "Sadachbia"), "8": ("آرش", "Sadaltager"), "9": ("شبنم", "Sulafat"),
153
- "10": ("سحر", "Laomedeia"), "11": ("مریم", "Achernar"), "12": ("بهرام", "Alnilam"),
154
- "13": ("نیکان", "Schedar"), "14": ("فرناز", "Gacrux"), "15": ("سارا", "Pulcherrima"),
155
- "16": ("مانی", "Umbriel"), "17": ("آرتین", "Algieba"), "18": ("دلنواز", "Despina"),
156
- "19": ("روژان", "Erinome"), "20": ("امید", "Algenib"), "21": ("بردیا", "Orus"),
157
- "22": ("ترانه", "Aoede"), "23": ("نیکو", "Callirrhoe"), "24": ("هستی", "Autonoe"),
158
- "25": ("کامیار", "Enceladus"), "26": ("کیانوش", "Iapetus"), "27": ("پویا", "Puck"),
159
- "28": ("مهتاب", "Kore"), "29": ("سام", "Fenrir"), "30": ("لیدا", "Leda")
160
  }
161
 
162
  # --- تنظیمات کلیدها ---
@@ -172,198 +193,312 @@ user_states = {}
172
  # --- ۱. پردازش چت متنی ---
173
  async def process_gemini(client, chat_id, prompt):
174
  if not GEMINI_KEYS:
175
- await send_with_keyboard(client, chat_id, "❌ کلیدهای جیمینای تنظیم نشده‌اند.", False)
176
  return
177
 
178
  proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
 
179
  history = user_states[chat_id].get("history", [])
180
- history.append({"role": "user", "parts": [{"text": prompt}]})
181
 
182
- if len(history) > 10: history = history[-10:]
183
- if history[0]["role"] == "model": history = history[1:]
 
 
 
 
 
 
 
184
 
 
 
185
  final_answer = None
 
186
  async with aiohttp.ClientSession() as session:
187
- for key in random.sample(GEMINI_KEYS, len(GEMINI_KEYS)):
188
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
 
189
  try:
190
- async with session.post(url, json={"contents": history, "generationConfig": {"temperature": 0.7}}, timeout=45) as response:
191
  if response.status == 200:
192
  data = await response.json()
193
- final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
194
- break
195
- except Exception: continue
 
 
 
 
196
 
197
- if proc_msg:
198
- try:
199
- msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
200
- await client.delete_messages(chat_id, [msg_id])
201
- except Exception: pass
 
 
 
202
 
203
  if not final_answer:
204
- await send_with_keyboard(client, chat_id, " خطایی رخ داد. دوباره تلاش کنید.", False)
 
 
205
  return
206
 
207
- user_states[chat_id]["history"].append({"role": "model", "parts": [{"text": final_answer}]})
208
- await send_with_keyboard(client, chat_id, final_answer, False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
 
211
  # --- ۲. پردازش ساخت عکس ---
212
  async def process_image(client, chat_id, prompt):
213
  if not HF_TOKENS:
214
- await send_with_keyboard(client, chat_id, "❌ توکن هاگینگ فیس تنظیم نشده.", False)
215
  return
216
 
217
- proc_msg = await send_with_keyboard(client, chat_id, "🎨 در حال طراحی عکس...", False)
 
 
 
218
  generated_image = None
219
- for token in random.sample(HF_TOKENS, len(HF_TOKENS)):
 
 
220
  try:
221
  hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
222
  generated_image = await hf_client.text_to_image(prompt, model="Tongyi-MAI/Z-Image-Turbo")
223
  break
224
- except Exception: continue
 
 
225
 
226
- if proc_msg:
227
- try:
228
- msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
229
- await client.delete_messages(chat_id, [msg_id])
230
- except Exception: pass
 
 
 
231
 
232
  if not generated_image:
233
- await send_with_keyboard(client, chat_id, "❌ عکس ساخته نشد.", True)
234
  return
235
 
236
- file_name = f"img_{random.randint(100,999)}.jpg"
237
- generated_image.convert('RGB').save(file_name, format="JPEG", quality=100)
238
  try:
239
- await client.send_photo(chat_id, file_name)
240
- await send_with_keyboard(client, chat_id, "🎨 تصویر شما آماده شد!", True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  except Exception:
242
- await send_with_keyboard(client, chat_id, "❌ خطای آپلود در روبیکا.", True)
243
- if os.path.exists(file_name): os.remove(file_name)
244
 
245
 
246
  # --- ۳. پرداز�� ساخت صدا ---
247
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
248
- proc_msg = await send_with_keyboard(client, chat_id, f"⏳ ساخت صدا با «{speaker_name}»...", False)
249
- audio_bytes = None
250
- async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=600)) as session:
251
- for worker_url in random.sample(WORKER_URLS, 3):
252
- try:
253
- async with session.post(worker_url, json={"text": user_text, "speaker": speaker_id, "use_live_model": True}) as response:
254
- if response.status == 200:
255
- audio_bytes = await response.read()
256
- break
257
- except Exception: continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
- if proc_msg:
260
  try:
261
- msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
262
- await client.delete_messages(chat_id, [msg_id])
 
 
 
 
263
  except Exception: pass
264
 
265
- if audio_bytes:
266
- file_name = f"voice_{random.randint(100,999)}.wav"
267
- with open(file_name, "wb") as f: f.write(audio_bytes)
268
- try:
269
- await client.send_voice(chat_id, file_name)
270
- await send_with_keyboard(client, chat_id, "✅ صدا آماده شد.", True)
271
- except Exception:
272
- await send_with_keyboard(client, chat_id, "❌ خطای آپلود صدا.", True)
273
- if os.path.exists(file_name): os.remove(file_name)
274
- else:
275
- await send_with_keyboard(client, chat_id, "❌ سرورهای صدا در دسترس نیستند.", True)
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
 
278
- # --- ۴. پردازش تبدیل صدا به متن (STT) 🚨 اصلاح شده برای مشکل Content-Type 🚨 ---
279
- async def process_stt(client, chat_id, audio_data, original_name):
280
  if not HF_TOKENS:
281
- await send_with_keyboard(client, chat_id, "❌ توکن هاگینگ فیس نیست.", False)
282
  return
283
 
284
- proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال استخراج متن از فایل...", False)
285
-
286
- # تشخیص پسوند
287
- ext = ".ogg"
288
- if original_name and "." in original_name:
289
- ext = "." + original_name.split(".")[-1].lower()
290
-
291
- temp_file = f"stt_{random.randint(100,999)}{ext}"
292
- with open(temp_file, "wb") as f: f.write(audio_data)
293
 
 
 
294
  transcribed_text = None
295
- for token in random.sample(HF_TOKENS, len(HF_TOKENS)):
 
 
296
  try:
297
- # 💡 تغییر اصلی: ارسال فایل با تعیین Content-Type دقیق برای جلوگیری از ارور None
298
- mime, _ = mimetypes.guess_type(temp_file)
299
- if not mime: mime = "audio/ogg"
300
-
301
- async with aiohttp.ClientSession() as session:
302
- headers = {"Authorization": f"Bearer {token}", "Content-Type": mime}
303
- # استفاده از API مستقیم به جای کلاینت برای کنترل کامل هدرها
304
- api_url = "https://api-inference.huggingface.co/models/openai/whisper-large-v3-turbo"
305
- with open(temp_file, "rb") as f:
306
- data = f.read()
307
- async with session.post(api_url, headers=headers, data=data) as resp:
308
- if resp.status == 200:
309
- res_json = await resp.json()
310
- transcribed_text = res_json.get("text")
311
- break
312
- except Exception: continue
313
 
314
- if os.path.exists(temp_file): os.remove(temp_file)
315
- if proc_msg:
316
- try:
317
- msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
318
- await client.delete_messages(chat_id, [msg_id])
319
- except Exception: pass
320
 
321
  if transcribed_text:
322
  await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
323
  else:
324
- await send_with_keyboard(client, chat_id, "❌ تبدیل به متن انجام نشد (فرمت فایل یا سرور مشکل داشت).", True)
325
 
326
 
327
- # --- ۵. پردازش تحلیل فایل با جیمینای ---
328
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
329
  if not GEMINI_KEYS:
330
- await send_with_keyboard(client, chat_id, "❌ کلید جیمینای نیست.", False)
331
  return
332
 
333
- proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
 
 
334
  mime_type, _ = mimetypes.guess_type(file_name)
335
- if not mime_type: mime_type = "image/jpeg"
 
 
 
 
 
 
 
 
336
 
337
- base64_data = base64.b64encode(file_bytes).decode('utf-8')
 
338
  final_answer = None
 
339
  async with aiohttp.ClientSession() as session:
340
- for key in random.sample(GEMINI_KEYS, len(GEMINI_KEYS)):
341
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
342
- payload = {"contents": [{"parts": [{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}]}
 
 
 
343
  try:
344
- async with session.post(url, json=payload, timeout=45) as resp:
345
- if resp.status == 200:
346
- data = await resp.json()
347
- final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
348
- break
 
 
349
  except Exception: continue
350
 
351
- if proc_msg:
352
- try:
353
- msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
354
- await client.delete_messages(chat_id, [msg_id])
355
- except Exception: pass
 
356
 
357
  if final_answer:
358
  await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
359
  else:
360
- await send_with_keyboard(client, chat_id, "❌ تحلیل فایل با شکست مواجه شد.", True)
361
 
362
 
363
- # --- تنظیمات اصلی ربات ---
364
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
 
365
  if not bot_token:
366
- print("توکن وارد نشده!")
367
  else:
368
  bot = BotClient(bot_token)
369
 
@@ -373,16 +508,27 @@ else:
373
  user_text = getattr(update, "text", "") or getattr(getattr(update, "message", None), "text", "") or getattr(getattr(update, "new_message", None), "text", "")
374
  user_text_str = str(user_text).strip() if user_text else ""
375
 
 
376
  is_file = False
377
- file_name = "file.ogg"
378
  msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
 
379
  if msg_obj:
380
  for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
381
  file_attr = getattr(msg_obj, attr, None)
382
  if file_attr:
383
  is_file = True
384
- file_name = getattr(file_attr, 'file_name', "file.ogg")
385
  break
 
 
 
 
 
 
 
 
 
386
 
387
  chat_id = getattr(update, "chat_id", None) or getattr(update, "author_guid", None) or getattr(update, "object_guid", None)
388
  if not chat_id: return
@@ -392,65 +538,137 @@ else:
392
 
393
  if user_text_str in ["/start", "سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
394
  user_states[chat_id] = {"mode": None, "text": "", "history": [], "file_bytes": None, "file_name": None}
395
- await send_with_keyboard(client, chat_id, "سلام! به ربات هوشمند آلفا خوش آمدید 🤖\nیکی از بخش‌ها را انتخاب کنید:", True)
396
  return
397
 
398
- if user_text_str == "چت با هوش مصنوعی 🤖":
399
- user_states[chat_id] = {"mode": "chat", "history": []}
400
- await send_with_keyboard(client, chat_id, "💬 وارد بخش چت شدید. بپرسید:", True)
401
  return
402
 
403
- if user_text_str == "ساخت تصاویر با هوش مصنوعی🎨":
404
- user_states[chat_id]["mode"] = "image_waiting"
405
- await send_with_keyboard(client, chat_id, "🎨 توصیف عکس را بفرستید:", True)
406
  return
407
 
408
- if user_text_str == "تبدیل متن به صدا با هوش مصنوعی🎙️":
409
- user_states[chat_id]["mode"] = "tts_waiting"
410
- await send_with_keyboard(client, chat_id, "🎙️ متن را بفرستید (حداکثر ۲۵۰۰ کاراکتر):", True)
411
  return
412
 
413
- if user_text_str == "تحلیل فایل با هوش مصنوعی 📁":
414
- user_states[chat_id] = {"mode": "file_waiting", "file_bytes": None}
415
- await send_with_keyboard(client, chat_id, "📁 فایل را بفرستید:", True)
416
  return
417
 
418
- if user_text_str == "تبدیل فایل صوتی به متن 📝":
419
- user_states[chat_id]["mode"] = "stt_waiting"
420
- await send_with_keyboard(client, chat_id, "📝 فایل صوتی یا ویدیو را بفرستید:", True)
421
  return
422
 
423
  current_mode = user_states[chat_id].get("mode")
424
 
425
- if current_mode == "chat" and user_text_str:
426
- asyncio.create_task(process_gemini(client, chat_id, user_text_str))
427
- elif current_mode == "image_waiting" and user_text_str:
428
- asyncio.create_task(process_image(client, chat_id, user_text_str))
429
- elif current_mode == "tts_waiting" and user_text_str:
430
- user_states[chat_id]["text"] = user_text_str
431
- user_states[chat_id]["mode"] = "tts_speaker"
432
- await send_with_keyboard(client, chat_id, "شماره گوینده (۱ تا ۳۰) را بفرستید:", False)
433
- elif current_mode == "tts_speaker" and user_text_str.isdigit():
434
- s_name, s_id = SPEAKERS.get(user_text_str, ("شهاب", "Charon"))
435
- saved_txt = user_states[chat_id]["text"]
436
- user_states[chat_id]["mode"] = "tts_waiting"
437
- asyncio.create_task(process_tts(client, chat_id, saved_txt, s_id, s_name))
438
- elif current_mode == "stt_waiting" and is_file:
439
- audio_bytes = await helper_download_file(client, msg_obj)
440
- asyncio.create_task(process_stt(client, chat_id, audio_bytes, file_name))
441
- elif current_mode == "file_waiting" and is_file:
442
- user_states[chat_id]["file_bytes"] = await helper_download_file(client, msg_obj)
443
- user_states[chat_id]["file_name"] = file_name
444
- user_states[chat_id]["mode"] = "file_prompt"
445
- await send_with_keyboard(client, chat_id, "حالا بگویید چگونه تحلیل شود؟", False)
446
- elif current_mode == "file_prompt" and user_text_str:
447
- f_bytes = user_states[chat_id]["file_bytes"]
448
- f_name = user_states[chat_id]["file_name"]
449
- user_states[chat_id]["mode"] = "file_waiting"
450
- asyncio.create_task(process_file_analysis(client, chat_id, f_bytes, f_name, user_text_str))
451
-
452
- except Exception: traceback.print_exc()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
 
454
  if __name__ == "__main__":
455
  threading.Thread(target=run_flask, daemon=True).start()
456
- if bot_token: bot.run()
 
 
 
 
18
 
19
  @app.route('/')
20
  def home():
21
+ return "ربات یکپارچه آلفا (نسخه پرو + دانلود فایل ۱۰۰٪ تضمینی) روشن است! 🚀"
22
 
23
  def run_flask():
24
  app.run(host="0.0.0.0", port=7860)
25
 
26
 
27
+ # --- ساختار کیبورد پایین صفحه (Chat Keypad) ---
28
  MAIN_KEYPAD_DICT = {
29
  "rows": [
30
  {
 
62
  }
63
 
64
 
65
+ # --- تابع ارسال منحصراً برای کیبورد پایین صفحه ---
66
  async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
67
  if not use_keyboard:
68
  msg = await client.send_message(chat_id, text)
 
88
  return msg
89
 
90
 
91
+ # --- 🚨 تابع هوشمند دانلود فایل (آپدیت و رفع باگ مسیر ذخیره) 🚨 ---
92
  async def helper_download_file(client, msg_obj):
93
  errors = []
94
+
95
  file_id = None
96
  file_obj = None
97
  for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
 
102
  elif isinstance(val, dict) and 'file_id' in val: file_id = val['file_id']
103
  if file_id: break
104
 
105
+ # ساخت یک نام رندوم تا کتابخانه rubpy ارور NoneType برای نام فایل ندهد
106
  temp_name = f"temp_dl_{random.randint(1000, 999999)}.tmp"
107
 
108
  if hasattr(client, "download_file"):
109
+
110
+ # تلاش ۱: ارسال مستقیم آبجکت پیام و نام فایل
111
  try:
112
  await client.download_file(msg_obj, temp_name)
113
  if os.path.exists(temp_name):
114
  with open(temp_name, 'rb') as f: data = f.read()
115
  os.remove(temp_name)
116
  return data
117
+ except Exception as e:
118
+ errors.append(f"تلاش ۱ (msg_obj): {e}")
119
 
120
+ # تلاش ۲: ارسال آبجکت فایل اختصاصی
121
  if file_obj:
122
  try:
123
  await client.download_file(file_obj, temp_name)
 
125
  with open(temp_name, 'rb') as f: data = f.read()
126
  os.remove(temp_name)
127
  return data
128
+ except Exception as e:
129
+ errors.append(f"تلاش ۲ (file_obj): {e}")
130
 
131
+ # تلاش ۳: ارسال آیدی با آرگومان file_name
132
  if file_id:
133
  try:
134
  await client.download_file(file_id, file_name=temp_name)
 
136
  with open(temp_name, 'rb') as f: data = f.read()
137
  os.remove(temp_name)
138
  return data
139
+ except Exception as e:
140
+ errors.append(f"تلاش ۳ (file_id, file_name): {e}")
141
 
142
+ # تلاش ۴: ارسال آیدی با آرگومان save_as
143
+ if file_id:
144
+ try:
145
+ await client.download_file(file_id, save_as=temp_name)
146
+ if os.path.exists(temp_name):
147
+ with open(temp_name, 'rb') as f: data = f.read()
148
+ os.remove(temp_name)
149
+ return data
150
+ except Exception as e:
151
+ errors.append(f"تلاش ۴ (file_id, save_as): {e}")
152
+
153
+ # در صورت شکست تمام حالت‌ها
154
+ raise Exception("دانلود ناموفق. لاگ:\n" + "\n".join(errors))
155
 
156
 
157
+ # --- لیست تمام سرورهای تولید صدای شما ---
158
  WORKER_URLS =[
159
  "https://hamed744-ttspro.hf.space/generate",
160
  "https://hamed744-ttspro2.hf.space/generate",
 
168
  ]
169
 
170
  SPEAKERS = {
171
+ "1": ("شهاب (مرد)", "Charon"), "2": ("آوا (زن)", "Zephyr"), "3": ("نوید (مرد)", "Achird"),
172
+ "4": ("آرمان (مرد)", "Zubenelgenubi"), "5": ("مهسا (زن)", "Vindemiatrix"), "6": ("دانا (مرد)", "Rasalgethi"),
173
+ "7": ("سامان (مرد)", "Sadachbia"), "8": ("آرش (مرد)", "Sadaltager"), "9": ("شبنم (زن)", "Sulafat"),
174
+ "10": ("سحر (زن)", "Laomedeia"), "11": ("مریم (زن)", "Achernar"), "12": ("بهرام (مرد)", "Alnilam"),
175
+ "13": ("نیکان (مرد)", "Schedar"), "14": ("فرناز (زن)", "Gacrux"), "15": ("سارا (زن)", "Pulcherrima"),
176
+ "16": ("مانی (مرد)", "Umbriel"), "17": ("آرتین (مرد)", "Algieba"), "18": ("دلنواز (زن)", "Despina"),
177
+ "19": ("روژان (زن)", "Erinome"), "20": ("امید (مرد)", "Algenib"), "21": ("بردیا (مرد)", "Orus"),
178
+ "22": ("ترانه (زن)", "Aoede"), "23": ("نیکو (زن)", "Callirrhoe"), "24": ("هستی (زن)", "Autonoe"),
179
+ "25": ("کامیار (مرد)", "Enceladus"), "26": ("کیانوش (مرد)", "Iapetus"), "27": ("پویا (مرد)", "Puck"),
180
+ "28": ("مهتاب (زن)", "Kore"), "29": ("سام (مرد)", "Fenrir"), "30": ("لیدا (زن)", "Leda")
181
  }
182
 
183
  # --- تنظیمات کلیدها ---
 
193
  # --- ۱. پردازش چت متنی ---
194
  async def process_gemini(client, chat_id, prompt):
195
  if not GEMINI_KEYS:
196
+ await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشده‌اند.", False)
197
  return
198
 
199
  proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
200
+
201
  history = user_states[chat_id].get("history", [])
 
202
 
203
+ if history and history[-1]["role"] == "user":
204
+ history[-1]["parts"][0]["text"] += "\n" + prompt
205
+ else:
206
+ history.append({"role": "user", "parts": [{"text": prompt}]})
207
+
208
+ if len(history) > 10:
209
+ history = history[-10:]
210
+ if history[0]["role"] == "model":
211
+ history = history[1:]
212
 
213
+ keys_to_try = GEMINI_KEYS.copy()
214
+ random.shuffle(keys_to_try)
215
  final_answer = None
216
+
217
  async with aiohttp.ClientSession() as session:
218
+ for key in keys_to_try:
219
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
220
+ payload = {"contents": history, "generationConfig": {"temperature": 0.7, "maxOutputTokens": 8192}}
221
  try:
222
+ async with session.post(url, json=payload, timeout=45) as response:
223
  if response.status == 200:
224
  data = await response.json()
225
+ try:
226
+ final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
227
+ break
228
+ except (KeyError, IndexError):
229
+ continue
230
+ except Exception:
231
+ continue
232
 
233
+ try:
234
+ if proc_msg:
235
+ msg_id = getattr(proc_msg, 'message_id', None)
236
+ if isinstance(proc_msg, dict):
237
+ msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
238
+ if msg_id:
239
+ await client.delete_messages(chat_id, [msg_id])
240
+ except Exception: pass
241
 
242
  if not final_answer:
243
+ if history and history[-1]["role"] == "user":
244
+ history.pop()
245
+ await send_with_keyboard(client, chat_id, "❌ متأسفانه پاسخی دریافت نشد. لطفاً دوباره تلاش کنید.", False)
246
  return
247
 
248
+ history.append({"role": "model", "parts": [{"text": final_answer}]})
249
+ user_states[chat_id]["history"] = history
250
+
251
+ try:
252
+ max_len = 1000
253
+ chunks = []
254
+ temp_text = final_answer
255
+
256
+ while len(temp_text) > max_len:
257
+ split_idx = temp_text.rfind('\n', 0, max_len)
258
+ if split_idx == -1:
259
+ split_idx = temp_text.rfind(' ', 0, max_len)
260
+ if split_idx == -1: split_idx = max_len
261
+ chunks.append(temp_text[:split_idx])
262
+ temp_text = temp_text[split_idx:].strip()
263
+ if temp_text: chunks.append(temp_text)
264
+
265
+ for idx, chunk in enumerate(chunks):
266
+ if idx != len(chunks) - 1:
267
+ chunk += "\n\n⏳ *(ادامه در پیام بعدی)...* 👇"
268
+ try:
269
+ await send_with_keyboard(client, chat_id, chunk, False)
270
+ await asyncio.sleep(2.5)
271
+ except Exception:
272
+ await asyncio.sleep(2.5)
273
+
274
+ except Exception:
275
+ await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
276
 
277
 
278
  # --- ۲. پردازش ساخت عکس ---
279
  async def process_image(client, chat_id, prompt):
280
  if not HF_TOKENS:
281
+ await send_with_keyboard(client, chat_id, "❌ توکن‌های هاگینگ فیس تنظیم نشده‌اند.", False)
282
  return
283
 
284
+ proc_msg = await send_with_keyboard(client, chat_id, "🎨 در حال طراحی عکس...\n(ممکن است چند ثانیه زمان ببرد)", False)
285
+
286
+ keys_to_try = HF_TOKENS.copy()
287
+ random.shuffle(keys_to_try)
288
  generated_image = None
289
+ last_error_log = "هیچ اتصالی برقرار نشد."
290
+
291
+ for token in keys_to_try:
292
  try:
293
  hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
294
  generated_image = await hf_client.text_to_image(prompt, model="Tongyi-MAI/Z-Image-Turbo")
295
  break
296
+ except Exception as e:
297
+ last_error_log = str(e)
298
+ continue
299
 
300
+ try:
301
+ if proc_msg:
302
+ msg_id = getattr(proc_msg, 'message_id', None)
303
+ if isinstance(proc_msg, dict):
304
+ msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
305
+ if msg_id:
306
+ await client.delete_messages(chat_id, [msg_id])
307
+ except Exception: pass
308
 
309
  if not generated_image:
310
+ await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
311
  return
312
 
 
 
313
  try:
314
+ file_name = f"image_{random.randint(1000, 999999)}.jpg"
315
+ rgb_im = generated_image.convert('RGB')
316
+ rgb_im.save(file_name, format="JPEG", quality=100)
317
+
318
+ await asyncio.sleep(1)
319
+ caption_text = "🎨 تصویر شما آماده شد!\n(مدل: Z-Image-Turbo)"
320
+
321
+ uploaded_directly = False
322
+ try:
323
+ await client.send_photo(chat_id, file_name)
324
+ uploaded_directly = True
325
+ except Exception:
326
+ try:
327
+ await client.send_document(chat_id, file_name)
328
+ uploaded_directly = True
329
+ except Exception: pass
330
+
331
+ if uploaded_directly:
332
+ await send_with_keyboard(client, chat_id, caption_text, True)
333
+ else:
334
+ await send_with_keyboard(client, chat_id, "❌ تصویر ساخته شد اما روبیکا اجازه آپلود نداد!", True)
335
+
336
+ if os.path.exists(file_name): os.remove(file_name)
337
+
338
  except Exception:
339
+ await send_with_keyboard(client, chat_id, "❌ خطایی در فرآیند ذخیره عکس رخ داد.", True)
 
340
 
341
 
342
  # --- ۳. پرداز�� ساخت صدا ---
343
  async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
344
+ try:
345
+ proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
346
+
347
+ payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
348
+ headers = {"User-Agent": "Mozilla/5.0", "Content-Type": "application/json"}
349
+
350
+ audio_bytes = None
351
+ last_error = "پاسخی دریافت نشد"
352
+ workers = WORKER_URLS.copy()
353
+ random.shuffle(workers)
354
+
355
+ async with aiohttp.ClientSession(headers=headers, timeout=aiohttp.ClientTimeout(total=600)) as session:
356
+ for worker_url in workers[:3]:
357
+ try:
358
+ async with session.post(worker_url, json=payload) as response:
359
+ if response.status == 200:
360
+ content_type = response.headers.get('Content-Type', '')
361
+ if 'audio' in content_type or response.content_length > 1000:
362
+ audio_bytes = await response.read()
363
+ break
364
+ else: last_error = "فایل نامعتبر از سرور"
365
+ else: last_error = f"ارور ({response.status})"
366
+ except asyncio.TimeoutError:
367
+ last_error = "پایان زمان انتظار سرور."
368
+ continue
369
+ except Exception as e:
370
+ last_error = f"خطا در ارتباط: {str(e)}"
371
+ continue
372
 
 
373
  try:
374
+ if proc_msg:
375
+ msg_id = getattr(proc_msg, 'message_id', None)
376
+ if isinstance(proc_msg, dict):
377
+ msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
378
+ if msg_id:
379
+ await client.delete_messages(chat_id, [msg_id])
380
  except Exception: pass
381
 
382
+ if audio_bytes:
383
+ file_name = f"audio_{random.randint(1000, 999999)}.wav"
384
+ with open(file_name, "wb") as f: f.write(audio_bytes)
385
+
386
+ uploaded_directly = False
387
+ try:
388
+ await client.send_voice(chat_id, file_name)
389
+ uploaded_directly = True
390
+ except Exception:
391
+ try:
392
+ await client.send_audio(chat_id, file_name)
393
+ uploaded_directly = True
394
+ except Exception: pass
395
+
396
+ if uploaded_directly:
397
+ await send_with_keyboard(client, chat_id, "✅ پردازش صدا انجام شد.", True)
398
+ else:
399
+ await send_with_keyboard(client, chat_id, f"❌ صدا ساخته شد اما روبیکا اجازه آپلود نداد!", True)
400
+ if os.path.exists(file_name): os.remove(file_name)
401
+ else:
402
+ await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
403
+ except Exception:
404
+ traceback.print_exc()
405
 
406
 
407
+ # --- ۴. پردازش تبدیل فایل صوتی/تصویری به متن (STT) ---
408
+ async def process_stt(client, chat_id, audio_bytes):
409
  if not HF_TOKENS:
410
+ await send_with_keyboard(client, chat_id, "❌ توکن‌های هاگینگ فیس تنظیم نشده‌اند.", False)
411
  return
412
 
413
+ proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال گوش دادن و پیاده‌سازی متن... (لطفاً صبور باشید)", False)
 
 
 
 
 
 
 
 
414
 
415
+ keys_to_try = HF_TOKENS.copy()
416
+ random.shuffle(keys_to_try)
417
  transcribed_text = None
418
+ last_error_log = "ناموفق"
419
+
420
+ for token in keys_to_try:
421
  try:
422
+ hf_client = AsyncInferenceClient(provider="hf-inference", api_key=token)
423
+ response = await hf_client.automatic_speech_recognition(audio_bytes, model="openai/whisper-large-v3-turbo")
424
+ transcribed_text = response.text
425
+ break
426
+ except Exception as e:
427
+ last_error_log = str(e)
428
+ continue
 
 
 
 
 
 
 
 
 
429
 
430
+ try:
431
+ if proc_msg:
432
+ msg_id = getattr(proc_msg, 'message_id', None)
433
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
434
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
435
+ except Exception: pass
436
 
437
  if transcribed_text:
438
  await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
439
  else:
440
+ await send_with_keyboard(client, chat_id, f"❌ تبدیل فایل به متن ناموفق بود.\n\n⚠️ دلیل: {last_error_log[:150]}", True)
441
 
442
 
443
+ # --- ۵. پردازش تحلیل هر نوع فایل با جیمینای ---
444
  async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
445
  if not GEMINI_KEYS:
446
+ await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشده‌اند.", False)
447
  return
448
 
449
+ proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل و بررسی فایل با هوش مصنوعی...", False)
450
+ base64_data = base64.b64encode(file_bytes).decode('utf-8')
451
+
452
  mime_type, _ = mimetypes.guess_type(file_name)
453
+ if not mime_type:
454
+ if file_name.endswith(('.jpg', '.jpeg')): mime_type = "image/jpeg"
455
+ elif file_name.endswith('.png'): mime_type = "image/png"
456
+ elif file_name.endswith('.pdf'): mime_type = "application/pdf"
457
+ elif file_name.endswith('.mp4'): mime_type = "video/mp4"
458
+ elif file_name.endswith('.mp3'): mime_type = "audio/mp3"
459
+ elif file_name.endswith('.ogg'): mime_type = "audio/ogg"
460
+ elif file_name.endswith('.wav'): mime_type = "audio/wav"
461
+ else: mime_type = "image/jpeg"
462
 
463
+ keys_to_try = GEMINI_KEYS.copy()
464
+ random.shuffle(keys_to_try)
465
  final_answer = None
466
+
467
  async with aiohttp.ClientSession() as session:
468
+ for key in keys_to_try:
469
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={key}"
470
+ payload = {
471
+ "contents": [{"parts": [{"text": prompt}, {"inlineData": {"mimeType": mime_type, "data": base64_data}}]}],
472
+ "generationConfig": {"temperature": 0.6, "maxOutputTokens": 8192}
473
+ }
474
  try:
475
+ async with session.post(url, json=payload, timeout=45) as response:
476
+ if response.status == 200:
477
+ data = await response.json()
478
+ try:
479
+ final_answer = data["candidates"][0]["content"]["parts"][0]["text"]
480
+ break
481
+ except (KeyError, IndexError): continue
482
  except Exception: continue
483
 
484
+ try:
485
+ if proc_msg:
486
+ msg_id = getattr(proc_msg, 'message_id', None)
487
+ if isinstance(proc_msg, dict): msg_id = proc_msg.get('message_update', {}).get('message_id') or proc_msg.get('message_id')
488
+ if msg_id: await client.delete_messages(chat_id, [msg_id])
489
+ except Exception: pass
490
 
491
  if final_answer:
492
  await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
493
  else:
494
+ await send_with_keyboard(client, chat_id, "❌ در حال حاضر پاسخی برای این فایل دریافت نشد (شاید فرمت فایل پشتیبانی نمی‌شود).", True)
495
 
496
 
497
+ # --- تنظیمات ربات روبیکا ---
498
  bot_token = os.environ.get("RUBIKA_AUTH", "").strip()
499
+
500
  if not bot_token:
501
+ print("خطا: توکن ربات روبیکا وارد نشده است!")
502
  else:
503
  bot = BotClient(bot_token)
504
 
 
508
  user_text = getattr(update, "text", "") or getattr(getattr(update, "message", None), "text", "") or getattr(getattr(update, "new_message", None), "text", "")
509
  user_text_str = str(user_text).strip() if user_text else ""
510
 
511
+ # تشخیص فایل
512
  is_file = False
513
+ file_name = "unknown_file.jpg"
514
  msg_obj = getattr(update, "message", None) or getattr(update, "new_message", None)
515
+
516
  if msg_obj:
517
  for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
518
  file_attr = getattr(msg_obj, attr, None)
519
  if file_attr:
520
  is_file = True
521
+ if hasattr(file_attr, 'file_name'): file_name = file_attr.file_name
522
  break
523
+
524
+ if not is_file and hasattr(msg_obj, 'to_dict'):
525
+ msg_dict = msg_obj.to_dict()
526
+ for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
527
+ if attr in msg_dict:
528
+ is_file = True
529
+ file_attr = msg_dict.get(attr, {})
530
+ if isinstance(file_attr, dict) and 'file_name' in file_attr: file_name = file_attr['file_name']
531
+ break
532
 
533
  chat_id = getattr(update, "chat_id", None) or getattr(update, "author_guid", None) or getattr(update, "object_guid", None)
534
  if not chat_id: return
 
538
 
539
  if user_text_str in ["/start", "سلام", "لغو", "/cancel", "❌ لغو", "برگشت♻️"]:
540
  user_states[chat_id] = {"mode": None, "text": "", "history": [], "file_bytes": None, "file_name": None}
541
+ await send_with_keyboard(client, chat_id, "سلام! به ربات هوشمند آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخش‌ها را انتخاب کنید:", True)
542
  return
543
 
544
+ if user_text_str in ["/chat", "💬 چت", "چت با هوش مصنوعی 🤖"]:
545
+ user_states[chat_id] = {"mode": "chat", "text": "", "history": []}
546
+ await send_with_keyboard(client, chat_id, "💬 شما وارد بخش **چت با هوش مصنوعی** شدید.\n(حالا ربات سابقه گفتگو را به یاد می‌آورد)\n\nهر سوالی دارید بفرستید تا جواب بدم:\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
547
  return
548
 
549
+ if user_text_str in ["/image", "🎨 عکس", "ساخت تصاویر با هوش مصنوعی🎨"]:
550
+ user_states[chat_id] = {"mode": "image_waiting_for_text", "text": "", "history": []}
551
+ await send_with_keyboard(client, chat_id, "🎨 شما وارد بخش **ساخت عکس** شدید.\n\nلطفاً متنی که می‌خواهید به عکس تبدیل شود را توصیف کنید:\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
552
  return
553
 
554
+ if user_text_str in ["/tts", "🎙️ صدا", "تبدیل متن به صدا با هوش مصنوعی🎙️"]:
555
+ user_states[chat_id] = {"mode": "tts_waiting_for_text", "text": "", "history": []}
556
+ await send_with_keyboard(client, chat_id, "🎙️ شما وارد بخش **تبدیل متن به صدا** شدید.\n\nلطفاً متنی که می‌خواهید به صدا تبدیل شود را ارسال کنید (حداکثر 2500 کاراکتر):\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
557
  return
558
 
559
+ if user_text_str in ["/file", "تحلیل فایل با هوش مصنوعی 📁"]:
560
+ user_states[chat_id] = {"mode": "file_waiting_for_file", "text": "", "history": [], "file_bytes": None, "file_name": None}
561
+ await send_with_keyboard(client, chat_id, "📁 شما وارد بخش **تحلیل فایل** شدید.\n\nلطفاً هر نوع فایلی (عکس، ویدیو، PDF، داکیومنت و...) را که می‌خواهید بررسی شود ارسال کنید:\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
562
  return
563
 
564
+ if user_text_str in ["/stt", "تبدیل فایل صوتی به متن 📝"]:
565
+ user_states[chat_id] = {"mode": "stt_waiting_for_audio", "text": "", "history": []}
566
+ await send_with_keyboard(client, chat_id, "📝 شما وارد بخش **تبدیل صدا به متن** شدید.\n\nلطفاً فایل خود (ویس، آهنگ، ویدیو و...) را فوروارد یا ارسال کنید:\n\n(برای خروج دکمه «برگشت♻️» را بزنید)", True)
567
  return
568
 
569
  current_mode = user_states[chat_id].get("mode")
570
 
571
+ if current_mode is None:
572
+ if is_file: pass
573
+ elif user_text_str: await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا از کیبورد پایین، بخش مورد نظرتان را انتخاب کنید:", True)
574
+ return
575
+
576
+ # --- مسیریابی ---
577
+ elif current_mode == "chat":
578
+ if user_text_str: asyncio.create_task(process_gemini(client, chat_id, user_text_str))
579
+ return
580
+
581
+ elif current_mode == "image_waiting_for_text":
582
+ if user_text_str: asyncio.create_task(process_image(client, chat_id, user_text_str))
583
+ return
584
+
585
+ elif current_mode == "tts_waiting_for_text":
586
+ if user_text_str:
587
+ if len(user_text_str) > 2500:
588
+ await send_with_keyboard(client, chat_id, "⚠️ متن طولانی است. لطفاً متنی کوتاه‌تر از 2500 کاراکتر بفرستید.", False)
589
+ return
590
+
591
+ user_states[chat_id]["text"] = user_text_str
592
+ user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
593
+
594
+ speakers_menu = """✅ متن شما ذخیره شد.
595
+ لطفاً **شماره** گوینده مورد نظر خود را بفرستید:
596
+ 1. شهاب | 2. آوا | 3. نوید
597
+ 4. آرمان | 5. مهسا | 6. دانا
598
+ 7. سامان | 8. آرش | 9. شبنم
599
+ 10. سحر | 11. مریم | 12. بهرام
600
+ 13. نیکان| 14. فرناز | 15. سارا
601
+ 16. مانی | 17. آرتین | 18. دلنواز
602
+ 19. روژان | 20. امید | 21. بردیا
603
+ 22. ترانه | 23. نیکو | 24. هستی
604
+ 25. کامیار| 26. کیانوش| 27. پویا
605
+ 28. مهتاب | 29. سام | 30. لیدا"""
606
+ await send_with_keyboard(client, chat_id, speakers_menu, False)
607
+ return
608
+
609
+ elif current_mode == "tts_waiting_for_speaker":
610
+ if user_text_str.isdigit() and user_text_str in SPEAKERS:
611
+ speaker_name, speaker_id = SPEAKERS[user_text_str]
612
+ saved_text = user_states[chat_id]["text"]
613
+ user_states[chat_id]["mode"] = "tts_waiting_for_text"
614
+ user_states[chat_id]["text"] = ""
615
+ asyncio.create_task(process_tts(client, chat_id, saved_text, speaker_id, speaker_name))
616
+ else:
617
+ await send_with_keyboard(client, chat_id, "❌ شماره نامعتبر است! لطفاً فقط یک عدد بین 1 تا 30 بفرستید.", False)
618
+ return
619
+
620
+ # --- بخش STT (صدا به متن) ---
621
+ elif current_mode == "stt_waiting_for_audio":
622
+ if not is_file:
623
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً یک فایل (ویس، آهنگ، ویدیو و...) ارسال کنید.", False)
624
+ return
625
+
626
+ await send_with_keyboard(client, chat_id, "📥 در حال دانلود فایل...", False)
627
+ try:
628
+ audio_bytes = await helper_download_file(client, msg_obj)
629
+ user_states[chat_id]["mode"] = "stt_waiting_for_audio"
630
+ asyncio.create_task(process_stt(client, chat_id, audio_bytes))
631
+ except Exception as dl_err:
632
+ err_trace = traceback.format_exc()
633
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دانلود فایل!\n\n🔴 لاگ برنامه‌نویس:\n`{err_trace[-300:]}`\n{str(dl_err)}", False)
634
+ return
635
+
636
+ # --- بخش تحلیل فایل ---
637
+ elif current_mode == "file_waiting_for_file":
638
+ if not is_file:
639
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً ابتدا یک فایل (تصویر، ویدیو، PDF و...) ارسال کنید.", False)
640
+ return
641
+
642
+ await send_with_keyboard(client, chat_id, "📥 در حال دریافت فایل...", False)
643
+ try:
644
+ file_bytes = await helper_download_file(client, msg_obj)
645
+ user_states[chat_id]["file_bytes"] = file_bytes
646
+ user_states[chat_id]["file_name"] = file_name
647
+ user_states[chat_id]["mode"] = "file_waiting_for_prompt"
648
+ await send_with_keyboard(client, chat_id, "✅ فایل با موفقیت دریافت شد.\n\nحالا لطفاً به صورت متنی بگویید **چگونه تحلیل شود؟**", False)
649
+ except Exception as dl_err:
650
+ err_trace = traceback.format_exc()
651
+ await send_with_keyboard(client, chat_id, f"❌ خطا در دریافت فایل!\n\n🔴 لاگ برنامه‌نویس:\n`{err_trace[-300:]}`\n{str(dl_err)}", False)
652
+ return
653
+
654
+ elif current_mode == "file_waiting_for_prompt":
655
+ if user_text_str:
656
+ saved_bytes = user_states[chat_id].get("file_bytes")
657
+ saved_name = user_states[chat_id].get("file_name", "file.jpeg")
658
+ user_states[chat_id]["mode"] = "file_waiting_for_file"
659
+ user_states[chat_id]["file_bytes"] = None
660
+ user_states[chat_id]["file_name"] = None
661
+ asyncio.create_task(process_file_analysis(client, chat_id, saved_bytes, saved_name, user_text_str))
662
+ else:
663
+ await send_with_keyboard(client, chat_id, "⚠️ لطفاً درخواست خود را به صورت متنی بنویسید.", False)
664
+ return
665
+
666
+ except Exception:
667
+ traceback.print_exc()
668
 
669
  if __name__ == "__main__":
670
  threading.Thread(target=run_flask, daemon=True).start()
671
+
672
+ if bot_token:
673
+ print("ربات یکپارچه آلفا روشن شد...")
674
+ bot.run()