AI Update: بله

This commit is contained in:
Antigravity Bot
2026-08-30 21:18:25 +03:30
parent fb5e5ff404
commit ab4764f521
3 changed files with 197 additions and 20 deletions
+177 -4
View File
@@ -109,6 +109,9 @@ def get_lang_display(lang_code_or_name: str) -> str:
norm = LANG_ALIASES.get(norm, norm)
return AVAILABLE_LANGUAGES.get(norm, lang_code_or_name)
# Global active user input states for interactive prompts (e.g. FTP configuration wizards)
USER_INPUT_STATES: dict[int, dict] = {}
# Logging configuration
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
@@ -389,9 +392,12 @@ async def build_ftp_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]:
user=curr_proj.ftp_user,
path=curr_proj.ftp_path,
tls=curr_proj.ftp_tls,
password=curr_proj.ftp_password,
lang=session.language,
)
tls_toggle_label = f"🔒 پروتکل امن: {'روشن ✅' if curr_proj.ftp_tls else 'خاموش ❌'}" if is_fa else f"🔒 FTPS/TLS: {'ON ✅' if curr_proj.ftp_tls else 'OFF ❌'}"
if is_fa:
keyboard = [
[
@@ -399,10 +405,19 @@ async def build_ftp_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]:
InlineKeyboardButton("🔍 تست اتصال FTP", callback_data="btn_ftp_test"),
],
[
InlineKeyboardButton("🐙 منوی گیت و برنچ‌ها", callback_data="btn_git_menu"),
InlineKeyboardButton("📁 مدیریت پروژه‌ها", callback_data="proj_menu"),
InlineKeyboardButton("🌐 ویرایش هاست/پورت", callback_data="ftp_set:host"),
InlineKeyboardButton("👤 ویرایش نام کاربری", callback_data="ftp_set:user"),
],
[
InlineKeyboardButton("🔑 ویرایش رمز عبور", callback_data="ftp_set:pass"),
InlineKeyboardButton("📂 ویرایش مسیر مقصد", callback_data="ftp_set:path"),
],
[
InlineKeyboardButton(tls_toggle_label, callback_data="ftp_toggle_tls"),
InlineKeyboardButton("🗑️ پاک‌سازی اطلاعات", callback_data="ftp_clear_conf"),
],
[
InlineKeyboardButton("🐙 منوی گیت و برنچ‌ها", callback_data="btn_git_menu"),
InlineKeyboardButton("🏠 منوی اصلی", callback_data="btn_dashboard"),
],
]
@@ -413,10 +428,19 @@ async def build_ftp_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]:
InlineKeyboardButton("🔍 Test FTP Connection", callback_data="btn_ftp_test"),
],
[
InlineKeyboardButton("🐙 Git & Branches Menu", callback_data="btn_git_menu"),
InlineKeyboardButton("📁 Projects Menu", callback_data="proj_menu"),
InlineKeyboardButton("🌐 Edit Host/Port", callback_data="ftp_set:host"),
InlineKeyboardButton("👤 Edit Username", callback_data="ftp_set:user"),
],
[
InlineKeyboardButton("🔑 Edit Password", callback_data="ftp_set:pass"),
InlineKeyboardButton("📂 Edit Remote Path", callback_data="ftp_set:path"),
],
[
InlineKeyboardButton(tls_toggle_label, callback_data="ftp_toggle_tls"),
InlineKeyboardButton("🗑️ Clear Config", callback_data="ftp_clear_conf"),
],
[
InlineKeyboardButton("🐙 Git & Branches Menu", callback_data="btn_git_menu"),
InlineKeyboardButton("🏠 Main Dashboard", callback_data="btn_dashboard"),
],
]
@@ -4707,6 +4731,96 @@ async def callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
except Exception:
await query.message.reply_html(text, reply_markup=markup, disable_web_page_preview=True)
elif data.startswith("ftp_set:"):
field_name = data.split(":")[1]
if not curr_proj:
await query.answer("⚠️ پروژه‌ای یافت نشد." if is_fa else "⚠️ No project found.", show_alert=True)
return
USER_INPUT_STATES[chat_id] = {
"type": "ftp_set",
"field": field_name,
"project": curr_proj.name,
}
field_prompts = {
"host": (
"🌐 <b>لطفاً آدرس هاست یا سرور FTP را ارسال کنید:</b>\n\n"
"<i>مثال:</i> <code>ftp.mysite.com</code> یا <code>185.120.30.40:21</code>"
) if is_fa else (
"🌐 <b>Please enter the FTP Host address:</b>\n\n"
"<i>Example:</i> <code>ftp.mysite.com</code> or <code>185.120.30.40:21</code>"
),
"user": (
"👤 <b>لطفاً نام کاربری (Username) اتصال FTP را ارسال کنید:</b>\n\n"
"<i>مثال:</i> <code>deployer@mysite.com</code> یا <code>ftpuser</code>"
) if is_fa else (
"👤 <b>Please enter the FTP Username:</b>\n\n"
"<i>Example:</i> <code>deployer@mysite.com</code> or <code>ftpuser</code>"
),
"pass": (
"🔑 <b>لطفاً رمز عبور (Password) اتصال FTP را ارسال کنید:</b>\n\n"
"<i>(پیام پس از ثبت، جهت امنیت حذف یا پوشانده می‌شود)</i>"
) if is_fa else (
"🔑 <b>Please enter the FTP Password:</b>\n\n"
"<i>(Message will be stored securely)</i>"
),
"path": (
"📂 <b>لطفاً مسیر پوشه ریموت مقصد روی هاست را ارسال کنید:</b>\n\n"
"<i>مثال:</i> <code>/public_html</code> یا <code>/domains/site.com/public_html</code>"
) if is_fa else (
"📂 <b>Please enter the remote target path:</b>\n\n"
"<i>Example:</i> <code>/public_html</code>"
),
}
prompt_text = field_prompts.get(field_name, "لطفاً مقدار جدید را ارسال کنید:")
cancel_kb = InlineKeyboardMarkup([[InlineKeyboardButton("❌ انصراف و بازگشت" if is_fa else "❌ Cancel", callback_data="btn_ftp_menu")]])
await query.edit_message_text(prompt_text, parse_mode=constants.ParseMode.HTML, reply_markup=cancel_kb)
elif data == "ftp_toggle_tls":
if curr_proj:
curr_proj.ftp_tls = not curr_proj.ftp_tls
session_manager.save()
status_str = "روشن (FTPS/TLS) 🔒" if curr_proj.ftp_tls else "خاموش (FTP معمولی) 🔓"
await query.answer(f"✅ پروتکل امن: {status_str}")
text, markup = await build_ftp_menu(chat_id)
try:
await query.edit_message_text(text, parse_mode=constants.ParseMode.HTML, reply_markup=markup, disable_web_page_preview=True)
except Exception:
await query.message.reply_html(text, reply_markup=markup, disable_web_page_preview=True)
elif data == "ftp_clear_conf":
confirm_text = (
"🗑️ <b>آیا از پاک‌سازی کامل اطلاعات FTP این پروژه اطمینان دارید؟</b>\n\n"
"تمام مشخصات سرور، کاربر و رمز عبور حذف خواهند شد."
) if is_fa else (
"🗑️ <b>Are you sure you want to clear FTP settings for this project?</b>"
)
keyboard = [
[
InlineKeyboardButton("💥 بله، پاک شود" if is_fa else "💥 Yes, Clear", callback_data="ftp_clear_do"),
InlineKeyboardButton("❌ انصراف" if is_fa else "❌ Cancel", callback_data="btn_ftp_menu"),
]
]
await query.edit_message_text(confirm_text, parse_mode=constants.ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
elif data == "ftp_clear_do":
if curr_proj:
curr_proj.ftp_host = None
curr_proj.ftp_port = 21
curr_proj.ftp_user = None
curr_proj.ftp_password = None
curr_proj.ftp_path = "/"
curr_proj.ftp_tls = False
session_manager.save()
await query.answer("🗑️ اطلاعات FTP با موفقیت پاک شد." if is_fa else "🗑️ FTP settings cleared.")
text, markup = await build_ftp_menu(chat_id)
try:
await query.edit_message_text(text, parse_mode=constants.ParseMode.HTML, reply_markup=markup, disable_web_page_preview=True)
except Exception:
await query.message.reply_html(text, reply_markup=markup, disable_web_page_preview=True)
elif data == "btn_ftp_test":
await query.answer("🔍 در حال بررسی اتصال FTP..." if is_fa else "🔍 Testing FTP connection...")
if curr_proj and curr_proj.ftp_host:
@@ -6501,6 +6615,65 @@ async def message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = session_manager.get_or_create(chat_id)
is_fa = (session.language or "").lower() in ("fa", "farsi", "persian", "🇮🇷 persian / farsi (فارسی)")
# 1. Check if user is in an interactive input state (e.g. configuring FTP fields)
if chat_id in USER_INPUT_STATES:
state = USER_INPUT_STATES.pop(chat_id)
if state.get("type") == "ftp_set" and curr_proj:
field = state.get("field")
val = update.message.text.strip()
if field == "host":
# Check for host:port format
if ":" in val:
h, p = val.rsplit(":", 1)
curr_proj.ftp_host = h.strip()
try:
curr_proj.ftp_port = int(p.strip())
except Exception:
pass
else:
curr_proj.ftp_host = val
elif field == "user":
curr_proj.ftp_user = val
elif field == "pass":
curr_proj.ftp_password = val
# Delete password message from chat if possible for security
try:
await update.message.delete()
except Exception:
pass
elif field == "path":
curr_proj.ftp_path = val if val.startswith("/") else f"/{val}"
session_manager.save()
field_labels = {
"host": "هاست و پورت",
"user": "نام کاربری",
"pass": "رمز عبور",
"path": "مسیر مقصد",
}
label_fa = field_labels.get(field, field)
ack_msg = f"✅ <b>{label_fa} با موفقیت ذخیره شد.</b>" if is_fa else f"✅ <b>{field} updated successfully.</b>"
await update.message.reply_html(ack_msg)
# Auto-test connection if host, user and password are all present
if curr_proj.ftp_host and curr_proj.ftp_user and curr_proj.ftp_password:
test_ok, test_msg = await ftp_manager.test_connection(
host=curr_proj.ftp_host,
port=curr_proj.ftp_port,
user=curr_proj.ftp_user or "",
password=curr_proj.ftp_password or "",
path=curr_proj.ftp_path or "/",
tls=curr_proj.ftp_tls,
)
test_status = "✅ اتصال FTP با موفقیت برقرار و تایید شد." if test_ok else f"⚠️ هشدار در تست اتصال: {test_msg}"
await update.message.reply_html(f"<i>{test_status}</i>")
text, markup = await build_ftp_menu(chat_id)
await update.message.reply_html(text, reply_markup=markup, disable_web_page_preview=True)
return
if not curr_proj:
if is_fa:
msg = (
+9 -5
View File
@@ -622,6 +622,7 @@ def format_ftp_info(
user: Optional[str] = None,
path: str = "/",
tls: bool = False,
password: Optional[str] = None,
lang: str = "fa",
) -> str:
"""Formats FTP connection and deployment status for Telegram HTML."""
@@ -630,18 +631,20 @@ def format_ftp_info(
host_str = f"<code>{escape_html(host)}:{port}</code>" if host else ("<i>(تنظیم نشده)</i>" if is_fa else "<i>(Not configured)</i>")
user_str = f"<code>{escape_html(user)}</code>" if user else ("<i>(ندارد)</i>" if is_fa else "<i>(None)</i>")
path_str = f"<code>{escape_html(path)}</code>"
pass_str = f"<code>{'' * min(len(password), 8)}</code> <i>(تنظیم شده)</i>" if password else ("<i>(ندارد)</i>" if is_fa else "<i>(None)</i>")
tls_str = "بله (FTPS/TLS امن) 🔒" if tls else "خیر (FTP معمولی)"
tls_str_en = "Yes (Secure FTPS/TLS) 🔒" if tls else "No (Standard FTP)"
if is_fa:
return (
f"🚀 <b>تنظیمات استقرار و دیپلوی FTP پروژه</b>\n\n"
f"🚀 <b>تنظیمات استقرار و مشخصات FTP پروژه</b>\n\n"
f"• 📁 <b>پروژه:</b> <code>{clean_p}</code>\n"
f"• 🌐 <b>سرور / هاست FTP:</b> {host_str}\n"
f"• 🌐 <b>سرور / هاست:</b> {host_str}\n"
f"• 👤 <b>نام کاربری:</b> {user_str}\n"
f"• 🔑 <b>رمز عبور:</b> {pass_str}\n"
f"• 📂 <b>مسیر مقصد روی هاست:</b> {path_str}\n"
f"• 🔒 <b>پروتکل امن (FTPS):</b> {tls_str}\n\n"
f"💡 <i>در زمان دیپلوی، هوش مصنوعی فایل‌های زائد، کش، سشن‌ها و مخزن گیت (.git) را به صورت خودکار فیلتر کرده و تنها سورس اصلی و تمیز به سرور منتقل می‌شود.</i>"
f"💡 <i>می‌توانید با دکمه‌های زیر هر فیلد را به دلخواه ویرایش، تست یا ذخیره کنید. در زمان دیپلوی، فایل‌های زائد، کش و .git به صورت خودکار فیلتر می‌شوند.</i>"
)
else:
return (
@@ -649,9 +652,10 @@ def format_ftp_info(
f"• 📁 <b>Project:</b> <code>{clean_p}</code>\n"
f"• 🌐 <b>FTP Host:</b> {host_str}\n"
f"• 👤 <b>Username:</b> {user_str}\n"
f"📂 <b>Remote Target Path:</b> {path_str}\n"
f"🔑 <b>Password:</b> {pass_str}\n"
f"• 📂 <b>Remote Path:</b> {path_str}\n"
f"• 🔒 <b>Secure FTPS/TLS:</b> {tls_str_en}\n\n"
f"💡 <i>During deployment, unnecessary cache, temp, sessions, and .git files are automatically excluded for a clean production upload.</i>"
f"💡 <i>Use the buttons below to edit fields, test connection, or deploy. Junk and cache files are excluded automatically.</i>"
)
File diff suppressed because one or more lines are too long