Update main.py
Browse files
main.py
CHANGED
|
@@ -8,6 +8,8 @@ import asyncio
|
|
| 8 |
import base64
|
| 9 |
import mimetypes
|
| 10 |
import io
|
|
|
|
|
|
|
| 11 |
from flask import Flask
|
| 12 |
from rubpy.bot import BotClient, filters
|
| 13 |
|
|
@@ -19,6 +21,52 @@ from pydub import AudioSegment
|
|
| 19 |
# --- ثبت زمان روشن شدن ربات برای جلوگیری از پرتاب پیامهای قدیمی ---
|
| 20 |
BOT_STARTUP_TIME = time.time()
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
# --- تابع تبدیل اعداد فارسی/عربی به انگلیسی ---
|
| 23 |
def to_english_digits(text):
|
| 24 |
if not text:
|
|
@@ -34,43 +82,38 @@ app = Flask(__name__)
|
|
| 34 |
|
| 35 |
@app.route('/')
|
| 36 |
def home():
|
| 37 |
-
return "ربات یکپارچه آلفا (نسخه پرو +
|
| 38 |
|
| 39 |
def run_flask():
|
| 40 |
app.run(host="0.0.0.0", port=7860)
|
| 41 |
|
| 42 |
|
| 43 |
-
# --- ساختار کیبورد آپدیت شده (
|
| 44 |
MAIN_KEYPAD_DICT = {
|
| 45 |
"rows": [
|
| 46 |
-
# ردیف اول
|
| 47 |
{
|
| 48 |
"buttons": [
|
| 49 |
{"id": "chat_btn", "type": "Simple", "button_text": "چت با هوش مصنوعی 🤖"}
|
| 50 |
]
|
| 51 |
},
|
| 52 |
-
# ردیف دوم
|
| 53 |
{
|
| 54 |
"buttons": [
|
| 55 |
{"id": "img_btn", "type": "Simple", "button_text": "ساخت تصاویر🎨"},
|
| 56 |
{"id": "edit_img_btn", "type": "Simple", "button_text": "ویرایش تصاویر 🪄"}
|
| 57 |
]
|
| 58 |
},
|
| 59 |
-
# ردیف سوم
|
| 60 |
{
|
| 61 |
"buttons": [
|
| 62 |
{"id": "podcast_btn", "type": "Simple", "button_text": "ساخت پادکست 🎙️"},
|
| 63 |
{"id": "tts_btn", "type": "Simple", "button_text": "تبدیل متن به صدا🗣️"}
|
| 64 |
]
|
| 65 |
},
|
| 66 |
-
# ردیف چهارم
|
| 67 |
{
|
| 68 |
"buttons": [
|
| 69 |
{"id": "stt_btn", "type": "Simple", "button_text": "فایل صوتی به متن 📝"},
|
| 70 |
{"id": "file_btn", "type": "Simple", "button_text": "تحلیل فایل 📁"}
|
| 71 |
]
|
| 72 |
},
|
| 73 |
-
# ردیف پنجم
|
| 74 |
{
|
| 75 |
"buttons": [
|
| 76 |
{"id": "account_btn", "type": "Simple", "button_text": "حساب کاربری 👤"},
|
|
@@ -81,7 +124,6 @@ MAIN_KEYPAD_DICT = {
|
|
| 81 |
"resize_keyboard": True
|
| 82 |
}
|
| 83 |
|
| 84 |
-
|
| 85 |
# --- تابع ارسال منحصراً برای کیبورد پایین صفحه ---
|
| 86 |
async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
|
| 87 |
try:
|
|
@@ -105,7 +147,6 @@ async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
|
|
| 105 |
# --- 🚨 تابع هوشمند دانلود فایل 🚨 ---
|
| 106 |
async def helper_download_file(client, msg_obj):
|
| 107 |
errors = []
|
| 108 |
-
|
| 109 |
file_id = None
|
| 110 |
file_obj = None
|
| 111 |
for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
|
|
@@ -262,7 +303,13 @@ user_last_request_time = {}
|
|
| 262 |
|
| 263 |
# --- ۱. پردازش چت متنی و چندرسانهای ---
|
| 264 |
async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشدهاند.", False)
|
|
|
|
| 266 |
proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
|
| 267 |
history = user_states[chat_id].get("history", [])
|
| 268 |
new_parts = []
|
|
@@ -321,6 +368,10 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
|
|
| 321 |
await send_with_keyboard(client, chat_id, "❌ متأسفانه پاسخی دریافت نشد. (شاید سایز فایل بیش از حد مجاز بوده است)", False)
|
| 322 |
return
|
| 323 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
history.append({"role": "model", "parts": [{"text": final_answer}]})
|
| 325 |
user_states[chat_id]["history"] = history
|
| 326 |
|
|
@@ -346,9 +397,16 @@ async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=Non
|
|
| 346 |
except Exception:
|
| 347 |
await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
|
| 348 |
|
|
|
|
| 349 |
# --- ۲. پردازش ساخت عکس ---
|
| 350 |
async def process_image(client, chat_id, prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
if not HF_TOKENS: return await send_with_keyboard(client, chat_id, "❌ توکنهای هاگینگ فیس تنظیم نشدهاند.", False)
|
|
|
|
| 352 |
proc_msg = await send_with_keyboard(client, chat_id, "✨ در حال ترجمه و بهینهسازی پرامپت شما توسط جیمینای...\n(تبدیل به پرامپت حرفهای)", False)
|
| 353 |
enhanced_prompt = prompt
|
| 354 |
if GEMINI_KEYS:
|
|
@@ -400,6 +458,10 @@ async def process_image(client, chat_id, prompt):
|
|
| 400 |
|
| 401 |
if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
|
| 402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
try:
|
| 404 |
file_name = f"image_{random.randint(1000, 999999)}.jpg"
|
| 405 |
rgb_im = generated_image.convert('RGB')
|
|
@@ -411,11 +473,16 @@ async def process_image(client, chat_id, prompt):
|
|
| 411 |
if os.path.exists(file_name): os.remove(file_name)
|
| 412 |
except Exception as e: await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس:\n{str(e)[:150]}", True)
|
| 413 |
|
|
|
|
| 414 |
# --- ۲.۵. پردازش ویرایش عکس با FLUX.2-dev ---
|
| 415 |
async def process_image_edit(client, chat_id, image_bytes, prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
if not HF_TOKENS:
|
| 417 |
-
await send_with_keyboard(client, chat_id, "❌ توکنهای هاگینگ فیس تنظیم نشدهاند.", False)
|
| 418 |
-
return
|
| 419 |
|
| 420 |
proc_msg = await send_with_keyboard(client, chat_id, "🪄 در حال پردازش و اعمال جادوی FLUX.2 روی عکس شما...\n(این فرآیند ممکن است کمی طول بکشد)", False)
|
| 421 |
|
|
@@ -427,7 +494,6 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
|
|
| 427 |
for token in keys_to_try:
|
| 428 |
try:
|
| 429 |
hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
|
| 430 |
-
# استفاده از متد image_to_image با ارسال دیتای بایت عکس و پرامپت متنی کاربر
|
| 431 |
generated_image = await hf_client.image_to_image(
|
| 432 |
image_bytes,
|
| 433 |
prompt=prompt,
|
|
@@ -446,8 +512,11 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
|
|
| 446 |
except Exception: pass
|
| 447 |
|
| 448 |
if not generated_image:
|
| 449 |
-
await send_with_keyboard(client, chat_id, f"❌ متأسفانه ویرایش عکس انجام نشد.\n\n⚠️ علت:\n{last_error_log[:200]}", True)
|
| 450 |
-
|
|
|
|
|
|
|
|
|
|
| 451 |
|
| 452 |
try:
|
| 453 |
file_name = f"edited_flux_{random.randint(1000, 999999)}.jpg"
|
|
@@ -465,6 +534,11 @@ async def process_image_edit(client, chat_id, image_bytes, prompt):
|
|
| 465 |
|
| 466 |
# --- ۳. پردازش ساخت صدا ---
|
| 467 |
async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
try:
|
| 469 |
proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
|
| 470 |
payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
|
|
@@ -495,6 +569,10 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
|
| 495 |
except Exception: pass
|
| 496 |
|
| 497 |
if audio_bytes:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
file_name = f"audio_{random.randint(1000, 999999)}.mp3"
|
| 499 |
with open(file_name, "wb") as f: f.write(audio_bytes)
|
| 500 |
await asyncio.sleep(1)
|
|
@@ -504,8 +582,14 @@ async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
|
| 504 |
else: await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
|
| 505 |
except Exception: traceback.print_exc()
|
| 506 |
|
|
|
|
| 507 |
# --- ۳.۵. پردازش پادکست ---
|
| 508 |
async def process_podcast(client, chat_id, prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 509 |
proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست توسط هوش مصنوعی...\n(لطفاً صبور باشید)", False)
|
| 510 |
available_speakers = []
|
| 511 |
for num_key, (spk_name, spk_id) in SPEAKERS.items():
|
|
@@ -569,6 +653,10 @@ async def process_podcast(client, chat_id, prompt):
|
|
| 569 |
combined_audio += audio_segment
|
| 570 |
except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
|
| 571 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
file_name = f"final_podcast_{random.randint(1000, 999999)}.mp3"
|
| 573 |
combined_audio.export(file_name, format="mp3")
|
| 574 |
|
|
@@ -581,12 +669,19 @@ async def process_podcast(client, chat_id, prompt):
|
|
| 581 |
|
| 582 |
caption = f"🎧 پادکست شما با موفقیت میکس و ساخته شد!\n\n💡 موضوع شما: {prompt}"
|
| 583 |
upload_result = await helper_upload_file(client, chat_id, file_name, "Voice", caption)
|
| 584 |
-
if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ پادکست
|
| 585 |
if os.path.exists(file_name): os.remove(file_name)
|
| 586 |
|
|
|
|
| 587 |
# --- ۴. پردازش تبدیل فایل صوتی/تصویری به متن ---
|
| 588 |
async def process_stt(client, chat_id, audio_bytes, file_name):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای جیمینای تنظیم نشدهاند.", False)
|
|
|
|
| 590 |
proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال گوش دادن و پیادهسازی متن...", False)
|
| 591 |
base64_data = base64.b64encode(audio_bytes).decode('utf-8')
|
| 592 |
mime_type, _ = mimetypes.guess_type(file_name)
|
|
@@ -616,12 +711,24 @@ async def process_stt(client, chat_id, audio_bytes, file_name):
|
|
| 616 |
if msg_id: await client.delete_messages(chat_id, [msg_id])
|
| 617 |
except Exception: pass
|
| 618 |
|
| 619 |
-
if transcribed_text:
|
| 620 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 621 |
|
| 622 |
# --- ۵. پردازش تحلیل فایل مستقل ---
|
| 623 |
async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید جیمینای تنظیم نیست.", False)
|
|
|
|
| 625 |
proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
|
| 626 |
base64_data = base64.b64encode(file_bytes).decode('utf-8')
|
| 627 |
mime_type, _ = mimetypes.guess_type(file_name)
|
|
@@ -650,8 +757,13 @@ async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
|
|
| 650 |
if msg_id: await client.delete_messages(chat_id, [msg_id])
|
| 651 |
except Exception: pass
|
| 652 |
|
| 653 |
-
if final_answer:
|
| 654 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 655 |
|
| 656 |
|
| 657 |
# --- تنظیمات ربات روبیکا ---
|
|
@@ -666,7 +778,6 @@ else:
|
|
| 666 |
current_time = time.time()
|
| 667 |
|
| 668 |
# 🛡 ۱. فیلتر پیامهای قدیمی (Backlog) در زمان ریاستارت اسپیس 🛡
|
| 669 |
-
# اگر از زمان روشن شدن ربات کمتر از 8 ثانیه گذشته، پیام را نادیده بگیر
|
| 670 |
if current_time - BOT_STARTUP_TIME < 8.0:
|
| 671 |
return
|
| 672 |
|
|
@@ -695,9 +806,9 @@ else:
|
|
| 695 |
processed_message_ids.add(unique_msg_key)
|
| 696 |
if len(processed_message_ids) > 5000: processed_message_ids.clear()
|
| 697 |
|
| 698 |
-
# 🛡 سیستم محدودکننده سرعت کاربر (Rate Limit)
|
| 699 |
last_req_time = user_last_request_time.get(chat_id, 0)
|
| 700 |
-
if current_time - last_req_time < 1.5:
|
| 701 |
return
|
| 702 |
user_last_request_time[chat_id] = current_time
|
| 703 |
|
|
@@ -734,21 +845,24 @@ else:
|
|
| 734 |
await send_with_keyboard(client, chat_id, "سلام! به ربات هوشمند آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخشها را انتخاب کنید:", True)
|
| 735 |
return
|
| 736 |
|
| 737 |
-
# --- حساب کاربری ---
|
| 738 |
if user_text_str in ["/account", "حساب کاربری 👤"]:
|
|
|
|
| 739 |
account_profile = f"""👤 **اطلاعات حساب کاربری شما**
|
| 740 |
|
| 741 |
🔹 **شناسه یکتا:** `{chat_id}`
|
| 742 |
🔹 **وضعیت اشتراک:** 🥉 نسخه رایگان (آزمایشی)
|
| 743 |
|
| 744 |
-
📊 **سهمیه
|
| 745 |
-
- 💬 چت هوشمند:
|
| 746 |
-
- 🎨 ساخت عکس:
|
| 747 |
-
- 🪄 ویرایش عکس پیشرفته:
|
| 748 |
-
- 🎙 ساخت پادکست:
|
| 749 |
-
- 🗣 تبدیل متن به صدا:
|
|
|
|
|
|
|
| 750 |
|
| 751 |
-
*نکته: در
|
| 752 |
await send_with_keyboard(client, chat_id, account_profile, True)
|
| 753 |
return
|
| 754 |
|
|
@@ -834,7 +948,6 @@ else:
|
|
| 834 |
user_states[chat_id]["text"] = user_text_str
|
| 835 |
user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
|
| 836 |
|
| 837 |
-
# بازگرداندن متن کامل لیست گویندگان
|
| 838 |
speakers_menu = """✅ متن شما ذخیره شد.
|
| 839 |
لطفاً **شماره** گوینده مورد نظر خود را بفرستید:
|
| 840 |
1. شهاب | 2. آوا | 3. نوید
|
|
@@ -851,7 +964,6 @@ else:
|
|
| 851 |
return
|
| 852 |
|
| 853 |
elif current_mode == "tts_waiting_for_speaker":
|
| 854 |
-
# تبدیل اعداد فارسی/عربی احتمالی به انگلیسی
|
| 855 |
normalized_text = to_english_digits(user_text_str)
|
| 856 |
|
| 857 |
if normalized_text.isdigit() and normalized_text in SPEAKERS:
|
|
@@ -903,5 +1015,5 @@ else:
|
|
| 903 |
if __name__ == "__main__":
|
| 904 |
threading.Thread(target=run_flask, daemon=True).start()
|
| 905 |
if bot_token:
|
| 906 |
-
print("ربات آلفا روشن شد...")
|
| 907 |
bot.run()
|
|
|
|
| 8 |
import base64
|
| 9 |
import mimetypes
|
| 10 |
import io
|
| 11 |
+
import json
|
| 12 |
+
import datetime
|
| 13 |
from flask import Flask
|
| 14 |
from rubpy.bot import BotClient, filters
|
| 15 |
|
|
|
|
| 21 |
# --- ثبت زمان روشن شدن ربات برای جلوگیری از پرتاب پیامهای قدیمی ---
|
| 22 |
BOT_STARTUP_TIME = time.time()
|
| 23 |
|
| 24 |
+
# --- سیستم دیتابیس حساب کاربری و مدیریت اعتبار ---
|
| 25 |
+
DB_FILE = "users_db.json"
|
| 26 |
+
db_lock = threading.Lock()
|
| 27 |
+
|
| 28 |
+
def load_db():
|
| 29 |
+
if os.path.exists(DB_FILE):
|
| 30 |
+
try:
|
| 31 |
+
with open(DB_FILE, "r", encoding="utf-8") as f:
|
| 32 |
+
return json.load(f)
|
| 33 |
+
except Exception:
|
| 34 |
+
return {}
|
| 35 |
+
return {}
|
| 36 |
+
|
| 37 |
+
def save_db(db_data):
|
| 38 |
+
with db_lock:
|
| 39 |
+
try:
|
| 40 |
+
with open(DB_FILE, "w", encoding="utf-8") as f:
|
| 41 |
+
json.dump(db_data, f, ensure_ascii=False, indent=4)
|
| 42 |
+
except Exception as e:
|
| 43 |
+
print("خطا در ذخیره دیتابیس:", e)
|
| 44 |
+
|
| 45 |
+
user_credits_db = load_db()
|
| 46 |
+
|
| 47 |
+
def get_user_credits(chat_id):
|
| 48 |
+
str_chat_id = str(chat_id)
|
| 49 |
+
today_str = datetime.date.today().isoformat()
|
| 50 |
+
|
| 51 |
+
if str_chat_id not in user_credits_db:
|
| 52 |
+
user_credits_db[str_chat_id] = {}
|
| 53 |
+
|
| 54 |
+
user_data = user_credits_db[str_chat_id]
|
| 55 |
+
|
| 56 |
+
# بازنشانی خودکار سهمیه روزانه
|
| 57 |
+
if user_data.get("last_reset") != today_str:
|
| 58 |
+
user_data["last_reset"] = today_str
|
| 59 |
+
user_data["chat"] = 30
|
| 60 |
+
user_data["image"] = 5
|
| 61 |
+
user_data["edit_image"] = 3
|
| 62 |
+
user_data["podcast"] = 1
|
| 63 |
+
user_data["tts"] = 5
|
| 64 |
+
user_data["file"] = 5
|
| 65 |
+
user_data["stt"] = 5
|
| 66 |
+
save_db(user_credits_db)
|
| 67 |
+
|
| 68 |
+
return user_data
|
| 69 |
+
|
| 70 |
# --- تابع تبدیل اعداد فارسی/عربی به انگلیسی ---
|
| 71 |
def to_english_digits(text):
|
| 72 |
if not text:
|
|
|
|
| 82 |
|
| 83 |
@app.route('/')
|
| 84 |
def home():
|
| 85 |
+
return "ربات یکپارچه آلفا (نسخه پرو + دیتابیس اعتبار + پادکست + ویرایش عکس) روشن است! 🚀"
|
| 86 |
|
| 87 |
def run_flask():
|
| 88 |
app.run(host="0.0.0.0", port=7860)
|
| 89 |
|
| 90 |
|
| 91 |
+
# --- ساختار کیبورد آپدیت شده (۵ ردیف) ---
|
| 92 |
MAIN_KEYPAD_DICT = {
|
| 93 |
"rows": [
|
|
|
|
| 94 |
{
|
| 95 |
"buttons": [
|
| 96 |
{"id": "chat_btn", "type": "Simple", "button_text": "چت با هوش مصنوعی 🤖"}
|
| 97 |
]
|
| 98 |
},
|
|
|
|
| 99 |
{
|
| 100 |
"buttons": [
|
| 101 |
{"id": "img_btn", "type": "Simple", "button_text": "ساخت تصاویر🎨"},
|
| 102 |
{"id": "edit_img_btn", "type": "Simple", "button_text": "ویرایش تصاویر 🪄"}
|
| 103 |
]
|
| 104 |
},
|
|
|
|
| 105 |
{
|
| 106 |
"buttons": [
|
| 107 |
{"id": "podcast_btn", "type": "Simple", "button_text": "ساخت پادکست 🎙️"},
|
| 108 |
{"id": "tts_btn", "type": "Simple", "button_text": "تبدیل متن به صدا🗣️"}
|
| 109 |
]
|
| 110 |
},
|
|
|
|
| 111 |
{
|
| 112 |
"buttons": [
|
| 113 |
{"id": "stt_btn", "type": "Simple", "button_text": "فایل صوتی به متن 📝"},
|
| 114 |
{"id": "file_btn", "type": "Simple", "button_text": "تحلیل فایل 📁"}
|
| 115 |
]
|
| 116 |
},
|
|
|
|
| 117 |
{
|
| 118 |
"buttons": [
|
| 119 |
{"id": "account_btn", "type": "Simple", "button_text": "حساب کاربری 👤"},
|
|
|
|
| 124 |
"resize_keyboard": True
|
| 125 |
}
|
| 126 |
|
|
|
|
| 127 |
# --- تابع ارسال منحصراً برای کیبورد پایین صفحه ---
|
| 128 |
async def send_with_keyboard(client, chat_id, text, use_keyboard=True):
|
| 129 |
try:
|
|
|
|
| 147 |
# --- 🚨 تابع هوشمند دانلود فایل 🚨 ---
|
| 148 |
async def helper_download_file(client, msg_obj):
|
| 149 |
errors = []
|
|
|
|
| 150 |
file_id = None
|
| 151 |
file_obj = None
|
| 152 |
for attr in ['file', 'file_inline', 'photo', 'voice', 'audio', 'document', 'video']:
|
|
|
|
| 303 |
|
| 304 |
# --- ۱. پردازش چت متنی و چندرسانهای ---
|
| 305 |
async def process_gemini(client, chat_id, prompt, file_bytes=None, file_name=None):
|
| 306 |
+
str_chat_id = str(chat_id)
|
| 307 |
+
creds = get_user_credits(str_chat_id)
|
| 308 |
+
if creds["chat"] <= 0:
|
| 309 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار پیامهای چت شما برای امروز تمام شده است.", False)
|
| 310 |
+
|
| 311 |
if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای API جیمینای تنظیم نشدهاند.", False)
|
| 312 |
+
|
| 313 |
proc_msg = await send_with_keyboard(client, chat_id, "🧠 در حال پردازش...", False)
|
| 314 |
history = user_states[chat_id].get("history", [])
|
| 315 |
new_parts = []
|
|
|
|
| 368 |
await send_with_keyboard(client, chat_id, "❌ متأسفانه پاسخی دریافت نشد. (شاید سایز فایل بیش از حد مجاز بوده است)", False)
|
| 369 |
return
|
| 370 |
|
| 371 |
+
# کسر اعتبار بعد از موفقیت
|
| 372 |
+
user_credits_db[str_chat_id]["chat"] -= 1
|
| 373 |
+
save_db(user_credits_db)
|
| 374 |
+
|
| 375 |
history.append({"role": "model", "parts": [{"text": final_answer}]})
|
| 376 |
user_states[chat_id]["history"] = history
|
| 377 |
|
|
|
|
| 397 |
except Exception:
|
| 398 |
await send_with_keyboard(client, chat_id, "❌ خطایی در ارسال پیام رخ داد.", False)
|
| 399 |
|
| 400 |
+
|
| 401 |
# --- ۲. پردازش ساخت عکس ---
|
| 402 |
async def process_image(client, chat_id, prompt):
|
| 403 |
+
str_chat_id = str(chat_id)
|
| 404 |
+
creds = get_user_credits(str_chat_id)
|
| 405 |
+
if creds["image"] <= 0:
|
| 406 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت عکس روزانه شما تمام شده است.", False)
|
| 407 |
+
|
| 408 |
if not HF_TOKENS: return await send_with_keyboard(client, chat_id, "❌ توکنهای هاگینگ فیس تنظیم نشدهاند.", False)
|
| 409 |
+
|
| 410 |
proc_msg = await send_with_keyboard(client, chat_id, "✨ در حال ترجمه و بهینهسازی پرامپت شما توسط جیمینای...\n(تبدیل به پرامپت حرفهای)", False)
|
| 411 |
enhanced_prompt = prompt
|
| 412 |
if GEMINI_KEYS:
|
|
|
|
| 458 |
|
| 459 |
if not generated_image: return await send_with_keyboard(client, chat_id, f"❌ عکس ساخته نشد.\n\n⚠️ خطا:\n{last_error_log[:200]}", True)
|
| 460 |
|
| 461 |
+
# کسر اعتبار
|
| 462 |
+
user_credits_db[str_chat_id]["image"] -= 1
|
| 463 |
+
save_db(user_credits_db)
|
| 464 |
+
|
| 465 |
try:
|
| 466 |
file_name = f"image_{random.randint(1000, 999999)}.jpg"
|
| 467 |
rgb_im = generated_image.convert('RGB')
|
|
|
|
| 473 |
if os.path.exists(file_name): os.remove(file_name)
|
| 474 |
except Exception as e: await send_with_keyboard(client, chat_id, f"❌ خطا در ذخیره عکس:\n{str(e)[:150]}", True)
|
| 475 |
|
| 476 |
+
|
| 477 |
# --- ۲.۵. پردازش ویرایش عکس با FLUX.2-dev ---
|
| 478 |
async def process_image_edit(client, chat_id, image_bytes, prompt):
|
| 479 |
+
str_chat_id = str(chat_id)
|
| 480 |
+
creds = get_user_credits(str_chat_id)
|
| 481 |
+
if creds["edit_image"] <= 0:
|
| 482 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار ویرایش عکس روزانه شما تمام شده است.", False)
|
| 483 |
+
|
| 484 |
if not HF_TOKENS:
|
| 485 |
+
return await send_with_keyboard(client, chat_id, "❌ توکنهای هاگینگ فیس تنظیم نشدهاند.", False)
|
|
|
|
| 486 |
|
| 487 |
proc_msg = await send_with_keyboard(client, chat_id, "🪄 در حال پردازش و اعمال جادوی FLUX.2 روی عکس شما...\n(این فرآیند ممکن است کمی طول بکشد)", False)
|
| 488 |
|
|
|
|
| 494 |
for token in keys_to_try:
|
| 495 |
try:
|
| 496 |
hf_client = AsyncInferenceClient(provider="fal-ai", api_key=token)
|
|
|
|
| 497 |
generated_image = await hf_client.image_to_image(
|
| 498 |
image_bytes,
|
| 499 |
prompt=prompt,
|
|
|
|
| 512 |
except Exception: pass
|
| 513 |
|
| 514 |
if not generated_image:
|
| 515 |
+
return await send_with_keyboard(client, chat_id, f"❌ متأسفانه ویرایش عکس انجام نشد.\n\n⚠️ علت:\n{last_error_log[:200]}", True)
|
| 516 |
+
|
| 517 |
+
# کسر اعتبار
|
| 518 |
+
user_credits_db[str_chat_id]["edit_image"] -= 1
|
| 519 |
+
save_db(user_credits_db)
|
| 520 |
|
| 521 |
try:
|
| 522 |
file_name = f"edited_flux_{random.randint(1000, 999999)}.jpg"
|
|
|
|
| 534 |
|
| 535 |
# --- ۳. پردازش ساخت صدا ---
|
| 536 |
async def process_tts(client, chat_id, user_text, speaker_id, speaker_name):
|
| 537 |
+
str_chat_id = str(chat_id)
|
| 538 |
+
creds = get_user_credits(str_chat_id)
|
| 539 |
+
if creds["tts"] <= 0:
|
| 540 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار تبدیل متن به صدای شما تمام شده است.", False)
|
| 541 |
+
|
| 542 |
try:
|
| 543 |
proc_msg = await send_with_keyboard(client, chat_id, f"⏳ در حال ساخت صدا با «{speaker_name}»...\n(لطفاً صبور باشید)", False)
|
| 544 |
payload = {"text": user_text, "speaker": speaker_id, "temperature": 1.5, "prompt": "", "use_live_model": True}
|
|
|
|
| 569 |
except Exception: pass
|
| 570 |
|
| 571 |
if audio_bytes:
|
| 572 |
+
# کسر اعتبار
|
| 573 |
+
user_credits_db[str_chat_id]["tts"] -= 1
|
| 574 |
+
save_db(user_credits_db)
|
| 575 |
+
|
| 576 |
file_name = f"audio_{random.randint(1000, 999999)}.mp3"
|
| 577 |
with open(file_name, "wb") as f: f.write(audio_bytes)
|
| 578 |
await asyncio.sleep(1)
|
|
|
|
| 582 |
else: await send_with_keyboard(client, chat_id, f"❌ سرورها درگیر هستند.\nدلیل: {last_error}", True)
|
| 583 |
except Exception: traceback.print_exc()
|
| 584 |
|
| 585 |
+
|
| 586 |
# --- ۳.۵. پردازش پادکست ---
|
| 587 |
async def process_podcast(client, chat_id, prompt):
|
| 588 |
+
str_chat_id = str(chat_id)
|
| 589 |
+
creds = get_user_credits(str_chat_id)
|
| 590 |
+
if creds["podcast"] <= 0:
|
| 591 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار ساخت پادکست روزانه شما تمام شده است.", False)
|
| 592 |
+
|
| 593 |
proc_msg = await send_with_keyboard(client, chat_id, "📻 در حال بررسی موضوع و نگارش سناریوی پادکست توسط هوش مصنوعی...\n(لطفاً صبور باشید)", False)
|
| 594 |
available_speakers = []
|
| 595 |
for num_key, (spk_name, spk_id) in SPEAKERS.items():
|
|
|
|
| 653 |
combined_audio += audio_segment
|
| 654 |
except Exception as e: return await send_with_keyboard(client, chat_id, f"❌ خطا در پردازش صدا (آیا pydub و ffmpeg نصب است؟):\n{str(e)}", True)
|
| 655 |
|
| 656 |
+
# کسر اعتبار در صورت اتمام موفق میکس
|
| 657 |
+
user_credits_db[str_chat_id]["podcast"] -= 1
|
| 658 |
+
save_db(user_credits_db)
|
| 659 |
+
|
| 660 |
file_name = f"final_podcast_{random.randint(1000, 999999)}.mp3"
|
| 661 |
combined_audio.export(file_name, format="mp3")
|
| 662 |
|
|
|
|
| 669 |
|
| 670 |
caption = f"🎧 پادکست شما با موفقیت میکس و ساخته شد!\n\n💡 موضوع شما: {prompt}"
|
| 671 |
upload_result = await helper_upload_file(client, chat_id, file_name, "Voice", caption)
|
| 672 |
+
if upload_result is not True: await send_with_keyboard(client, chat_id, f"❌ پادکست ساخته شد اما روبیکا خطای آپلود داد.\n\n`{str(upload_result)[:800]}`", True)
|
| 673 |
if os.path.exists(file_name): os.remove(file_name)
|
| 674 |
|
| 675 |
+
|
| 676 |
# --- ۴. پردازش تبدیل فایل صوتی/تصویری به متن ---
|
| 677 |
async def process_stt(client, chat_id, audio_bytes, file_name):
|
| 678 |
+
str_chat_id = str(chat_id)
|
| 679 |
+
creds = get_user_credits(str_chat_id)
|
| 680 |
+
if creds["stt"] <= 0:
|
| 681 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار تبدیل صدا به متن شما تمام شده است.", False)
|
| 682 |
+
|
| 683 |
if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلیدهای جیمینای تنظیم نشدهاند.", False)
|
| 684 |
+
|
| 685 |
proc_msg = await send_with_keyboard(client, chat_id, "📝 در حال گوش دادن و پیادهسازی متن...", False)
|
| 686 |
base64_data = base64.b64encode(audio_bytes).decode('utf-8')
|
| 687 |
mime_type, _ = mimetypes.guess_type(file_name)
|
|
|
|
| 711 |
if msg_id: await client.delete_messages(chat_id, [msg_id])
|
| 712 |
except Exception: pass
|
| 713 |
|
| 714 |
+
if transcribed_text:
|
| 715 |
+
# کسر اعتبار
|
| 716 |
+
user_credits_db[str_chat_id]["stt"] -= 1
|
| 717 |
+
save_db(user_credits_db)
|
| 718 |
+
await send_with_keyboard(client, chat_id, f"📝 **متن استخراج شده:**\n\n{transcribed_text}", True)
|
| 719 |
+
else:
|
| 720 |
+
await send_with_keyboard(client, chat_id, f"❌ تبدیل فایل به متن ناموفق بود.", True)
|
| 721 |
+
|
| 722 |
|
| 723 |
# --- ۵. پردازش تحلیل فایل مستقل ---
|
| 724 |
async def process_file_analysis(client, chat_id, file_bytes, file_name, prompt):
|
| 725 |
+
str_chat_id = str(chat_id)
|
| 726 |
+
creds = get_user_credits(str_chat_id)
|
| 727 |
+
if creds["file"] <= 0:
|
| 728 |
+
return await send_with_keyboard(client, chat_id, "❌ اعتبار تحلیل فایل روزانه شما تمام شده است.", False)
|
| 729 |
+
|
| 730 |
if not GEMINI_KEYS: return await send_with_keyboard(client, chat_id, "❌ کلید جیمینای تنظیم نیست.", False)
|
| 731 |
+
|
| 732 |
proc_msg = await send_with_keyboard(client, chat_id, "👁️ در حال تحلیل فایل...", False)
|
| 733 |
base64_data = base64.b64encode(file_bytes).decode('utf-8')
|
| 734 |
mime_type, _ = mimetypes.guess_type(file_name)
|
|
|
|
| 757 |
if msg_id: await client.delete_messages(chat_id, [msg_id])
|
| 758 |
except Exception: pass
|
| 759 |
|
| 760 |
+
if final_answer:
|
| 761 |
+
# کسر اعتبار
|
| 762 |
+
user_credits_db[str_chat_id]["file"] -= 1
|
| 763 |
+
save_db(user_credits_db)
|
| 764 |
+
await send_with_keyboard(client, chat_id, f"💡 **نتیجه تحلیل:**\n\n{final_answer}", True)
|
| 765 |
+
else:
|
| 766 |
+
await send_with_keyboard(client, chat_id, "❌ پاسخی دریافت نشد.", True)
|
| 767 |
|
| 768 |
|
| 769 |
# --- تنظیمات ربات روبیکا ---
|
|
|
|
| 778 |
current_time = time.time()
|
| 779 |
|
| 780 |
# 🛡 ۱. فیلتر پیامهای قدیمی (Backlog) در زمان ریاستارت اسپیس 🛡
|
|
|
|
| 781 |
if current_time - BOT_STARTUP_TIME < 8.0:
|
| 782 |
return
|
| 783 |
|
|
|
|
| 806 |
processed_message_ids.add(unique_msg_key)
|
| 807 |
if len(processed_message_ids) > 5000: processed_message_ids.clear()
|
| 808 |
|
| 809 |
+
# 🛡 سیستم محدودکننده سرعت کاربر (Rate Limit) 🛡
|
| 810 |
last_req_time = user_last_request_time.get(chat_id, 0)
|
| 811 |
+
if current_time - last_req_time < 1.5:
|
| 812 |
return
|
| 813 |
user_last_request_time[chat_id] = current_time
|
| 814 |
|
|
|
|
| 845 |
await send_with_keyboard(client, chat_id, "سلام! به ربات هوشمند آلفا خوش آمدید 🤖\n\nلطفاً برای شروع، از کیبورد پایین یکی از بخشها را انتخاب کنید:", True)
|
| 846 |
return
|
| 847 |
|
| 848 |
+
# --- حساب کاربری پویا و دقیق ---
|
| 849 |
if user_text_str in ["/account", "حساب کاربری 👤"]:
|
| 850 |
+
creds = get_user_credits(chat_id)
|
| 851 |
account_profile = f"""👤 **اطلاعات حساب کاربری شما**
|
| 852 |
|
| 853 |
🔹 **شناسه یکتا:** `{chat_id}`
|
| 854 |
🔹 **وضعیت اشتراک:** 🥉 نسخه رایگان (آزمایشی)
|
| 855 |
|
| 856 |
+
📊 **سهمیه باقیمانده شما در امروز:**
|
| 857 |
+
- 💬 چت هوشمند: {creds['chat']} پیام
|
| 858 |
+
- 🎨 ساخت عکس: {creds['image']} تصویر
|
| 859 |
+
- 🪄 ویرایش عکس پیشرفته: {creds['edit_image']} تصویر
|
| 860 |
+
- 🎙 ساخت پادکست: {creds['podcast']} برنامه
|
| 861 |
+
- 🗣 تبدیل متن به صدا: {creds['tts']} فایل
|
| 862 |
+
- 📁 تحلیل فایل: {creds['file']} فایل
|
| 863 |
+
- 📝 تبدیل صدا به متن: {creds['stt']} فایل
|
| 864 |
|
| 865 |
+
*نکته: اعتبار شما هر روز ساعت ۰۰:۰۰ به صورت خودکار شارژ میشود. با ارتقا به نسخه آلفا پرو میتوانید تمام این محدودیتها را بردارید.* 🚀"""
|
| 866 |
await send_with_keyboard(client, chat_id, account_profile, True)
|
| 867 |
return
|
| 868 |
|
|
|
|
| 948 |
user_states[chat_id]["text"] = user_text_str
|
| 949 |
user_states[chat_id]["mode"] = "tts_waiting_for_speaker"
|
| 950 |
|
|
|
|
| 951 |
speakers_menu = """✅ متن شما ذخیره شد.
|
| 952 |
لطفاً **شماره** گوینده مورد نظر خود را بفرستید:
|
| 953 |
1. شهاب | 2. آوا | 3. نوید
|
|
|
|
| 964 |
return
|
| 965 |
|
| 966 |
elif current_mode == "tts_waiting_for_speaker":
|
|
|
|
| 967 |
normalized_text = to_english_digits(user_text_str)
|
| 968 |
|
| 969 |
if normalized_text.isdigit() and normalized_text in SPEAKERS:
|
|
|
|
| 1015 |
if __name__ == "__main__":
|
| 1016 |
threading.Thread(target=run_flask, daemon=True).start()
|
| 1017 |
if bot_token:
|
| 1018 |
+
print("ربات آلفا با موفقیت به همراه دیتابیس روشن شد...")
|
| 1019 |
bot.run()
|