AI Update: بله
This commit is contained in:
+177
-4
@@ -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 = (
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user