diff --git a/telegram-agy-bot/agy_engine.py b/telegram-agy-bot/agy_engine.py
index 21531d7..22e5160 100644
--- a/telegram-agy-bot/agy_engine.py
+++ b/telegram-agy-bot/agy_engine.py
@@ -360,6 +360,13 @@ class SessionManager:
"created_at": p_obj.created_at,
"description": p_obj.description,
"conversation_titles": p_obj.conversation_titles,
+ "active_branch": p_obj.active_branch,
+ "ftp_host": p_obj.ftp_host,
+ "ftp_port": p_obj.ftp_port,
+ "ftp_user": p_obj.ftp_user,
+ "ftp_password": p_obj.ftp_password,
+ "ftp_path": p_obj.ftp_path,
+ "ftp_tls": p_obj.ftp_tls,
}
curr = self.get_current_project(sess.chat_id)
data[str(k)] = {
diff --git a/telegram-agy-bot/bot.py b/telegram-agy-bot/bot.py
index 42c7fe4..801c57c 100644
--- a/telegram-agy-bot/bot.py
+++ b/telegram-agy-bot/bot.py
@@ -4832,9 +4832,24 @@ async def callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
path=curr_proj.ftp_path or "/",
tls=curr_proj.ftp_tls,
)
+ icon = "✅" if test_ok else "❌"
+ res_card = (
+ f"{icon} نتیجه تست اتصال FTP:\n\n"
+ f"• 🌐 هاست: {escape_html(curr_proj.ftp_host)}:{curr_proj.ftp_port}\n"
+ f"• 👤 نام کاربری: {escape_html(curr_proj.ftp_user or '(بدون نام کاربری)')}\n"
+ f"• 📂 مسیر: {escape_html(curr_proj.ftp_path or '/')}\n"
+ f"• 🔒 پروتکل: {'FTPS/TLS امن 🔒' if curr_proj.ftp_tls else 'FTP معمولی'}\n\n"
+ f"📋 گزارش ارتباط:\n{test_msg}"
+ ) if is_fa else (
+ f"{icon} FTP Connection Test Result:\n\n"
+ f"• 🌐 Host: {escape_html(curr_proj.ftp_host)}:{curr_proj.ftp_port}\n"
+ f"• 👤 User: {escape_html(curr_proj.ftp_user or '(none)')}\n"
+ f"• 📂 Path: {escape_html(curr_proj.ftp_path or '/')}\n"
+ f"• 🔒 Protocol: {'Secure FTPS/TLS 🔒' if curr_proj.ftp_tls else 'Standard FTP'}\n\n"
+ f"📋 Status:\n{test_msg}"
+ )
try:
- alert_text = ("✅ اتصال FTP برقرار است" if test_ok else "❌ خطا در اتصال FTP") + f"\n{test_msg}"
- await query.answer(alert_text[:200], show_alert=True)
+ await query.message.reply_html(res_card, disable_web_page_preview=True)
except Exception:
pass
else:
diff --git a/telegram-agy-bot/ftp_manager.py b/telegram-agy-bot/ftp_manager.py
index 616db7e..4434975 100644
--- a/telegram-agy-bot/ftp_manager.py
+++ b/telegram-agy-bot/ftp_manager.py
@@ -116,17 +116,27 @@ class FTPManager:
timeout: int = 15,
) -> ftplib.FTP:
"""Helper to create and authenticate an FTP or FTPS client."""
+ clean_host = host.strip()
+ clean_user = user.strip() if user else ""
+ clean_pass = password.strip() if password else ""
+
if tls:
ftp = ftplib.FTP_TLS(timeout=timeout)
else:
ftp = ftplib.FTP(timeout=timeout)
- ftp.connect(host=host, port=port, timeout=timeout)
- ftp.login(user=user or "anonymous", passwd=password or "")
-
+ ftp.encoding = "utf-8"
+ ftp.connect(host=clean_host, port=int(port), timeout=timeout)
+
if tls and isinstance(ftp, ftplib.FTP_TLS):
+ ftp.auth()
+ ftp.login(user=clean_user or "anonymous", passwd=clean_pass)
ftp.prot_p() # Secure data connection
-
+ else:
+ ftp.login(user=clean_user or "anonymous", passwd=clean_pass)
+
+ # Force passive mode (standard for firewalls / NAT)
+ ftp.set_pasv(True)
return ftp
async def test_connection(
@@ -137,39 +147,72 @@ class FTPManager:
password: str = "",
path: str = "/",
tls: bool = False,
- timeout: int = 10,
+ timeout: int = 12,
) -> Tuple[bool, str]:
"""Tests FTP credentials and verifies access to the target remote directory."""
if not host or not host.strip():
return False, "آدرس هاست (Host) FTP مشخص نشده است."
def _test():
+ ftp = None
try:
ftp = self._create_ftp_client(host.strip(), int(port), user.strip(), password, tls, timeout)
+
+ # Check root PWD
+ init_pwd = ftp.pwd()
+
# Test CWD to target path if specified
target_path = path.strip() if path else "/"
if target_path and target_path != "/":
try:
ftp.cwd(target_path)
except Exception as e:
- pwd = ftp.pwd()
- ftp.quit()
- return False, f"اتصال به FTP برقرار شد، اما مسیر ریموت «{target_path}» یافت نشد (مسیر فعلی: {pwd}): {e}"
+ curr_pwd = ftp.pwd() if ftp else "/"
+ try:
+ ftp.quit()
+ except Exception:
+ pass
+ return False, f"اتصال به سرور برقرار شد، اما مسیر ریموت «{target_path}» یافت نشد (مسیر فعلی: {curr_pwd}): {e}"
- pwd = ftp.pwd()
+ final_pwd = ftp.pwd()
+
+ # Test directory listing
+ item_count = 0
try:
listing = ftp.nlst()
- count = len(listing)
+ item_count = len(listing)
except Exception:
- count = 0
- ftp.quit()
- return True, f"اتصال با موفقیت برقرار شد. مسیر فعلی: {pwd} (تعداد آیتمها: {count})"
- except (socket.gaierror, socket.timeout) as e:
- return False, f"خطای شبکه / نامعتبر بودن آدرس سرور ({host}:{port}): {e}"
+ try:
+ lines = []
+ ftp.dir(lines.append)
+ item_count = len(lines)
+ except Exception:
+ item_count = 0
+
+ try:
+ ftp.quit()
+ except Exception:
+ pass
+
+ tls_label = " (FTPS/TLS امن)" if tls else " (FTP معمولی)"
+ return True, f"اتصال با موفقیت برقرار شد{tls_label} | مسیر: {final_pwd} | تعداد فایلها و پوشهها: {item_count}"
+
+ except socket.gaierror as e:
+ return False, f"نام دامنه/هاست سرور یافت نشد ({host}): {e}"
+ except (socket.timeout, TimeoutError) as e:
+ return False, f"مهلت زمانی اتصال به سرور به پایان رسید (Timeout روی پورت {port}): {e}"
+ except ConnectionRefusedError as e:
+ return False, f"اتصال توسط سرور رد شد (پورت {port} بسته است یا FTP روی آن فعال نیست): {e}"
except ftplib.error_perm as e:
- return False, f"خطای احراز هویت / دسترسی FTP: {e}"
+ return False, f"خطای نام کاربری یا رمز عبور (دسترسی نامعتبر): {e}"
except Exception as e:
return False, f"خطا در برقراری ارتباط با FTP: {e}"
+ finally:
+ if ftp:
+ try:
+ ftp.close()
+ except Exception:
+ pass
return await asyncio.to_thread(_test)
diff --git a/telegram-agy-bot/sessions.json b/telegram-agy-bot/sessions.json
index c6c32e3..5a50d87 100644
--- a/telegram-agy-bot/sessions.json
+++ b/telegram-agy-bot/sessions.json
@@ -20,9 +20,9 @@
"model": "gemini-3.7-flash-auto",
"effort": "high",
"language": "fa",
- "last_context_length": 41556,
- "last_total_tokens": 332309,
- "last_response": "\u0642\u0627\u0628\u0644\u06cc\u062a \u0645\u062f\u06cc\u0631\u06cc\u062a \u06a9\u0627\u0645\u0644 \u0648 \u062a\u0639\u0627\u0645\u0644\u06cc \u062a\u0646\u0638\u06cc\u0645\u0627\u062a FTP \u0645\u0633\u062a\u0642\u06cc\u0645\u0627\u064b \u0628\u0627 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0631\u0628\u0627\u062a \u062a\u0644\u06af\u0631\u0627\u0645 \u067e\u06cc\u0627\u062f\u0647\u200c\u0633\u0627\u0632\u06cc \u0648 \u0641\u0639\u0627\u0644 \u0634\u062f.\n\n---\n\n### \u2728 \u0627\u0645\u06a9\u0627\u0646\u0627\u062a \u0648 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u062c\u062f\u06cc\u062f \u0627\u0636\u0627\u0641\u0647 \u0634\u062f\u0647:\n\n1. **\ud83c\udf10 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0647\u0627\u0633\u062a / \u067e\u0648\u0631\u062a (`ftp_set:host`):**\n - \u0628\u0627 \u06a9\u0644\u06cc\u06a9 \u0631\u0648\u06cc \u062f\u06a9\u0645\u0647\u060c \u0631\u0628\u0627\u062a \u0645\u0646\u062a\u0638\u0631 \u062f\u0631\u06cc\u0627\u0641\u062a \u0622\u062f\u0631\u0633 \u0647\u0627\u0633\u062a \u0645\u06cc\u200c\u0645\u0627\u0646\u062f (\u0645\u062b\u0627\u0644: `ftp.mysite.com` \u06cc\u0627 `1.2.3.4:2121`).\n2. **\ud83d\udc64 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc (`ftp_set:user`):**\n - \u0628\u0631\u0627\u06cc \u0648\u0627\u0631\u062f \u06a9\u0631\u062f\u0646 \u06cc\u0648\u0632\u0631\u0646\u06cc\u0645 \u0627\u06a9\u0627\u0646\u062a FTP.\n3. **\ud83d\udd11 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0631\u0645\u0632 \u0639\u0628\u0648\u0631 (`ftp_set:pass`):**\n - \u0628\u0631\u0627\u06cc \u0648\u0627\u0631\u062f \u06a9\u0631\u062f\u0646 \u0631\u0645\u0632 \u0639\u0628\u0648\u0631 (\u067e\u0633 \u0627\u0632 \u062f\u0631\u06cc\u0627\u0641\u062a\u060c \u067e\u06cc\u0627\u0645 \u0628\u0631\u0627\u06cc \u0627\u0645\u0646\u06cc\u062a \u0628\u06cc\u0634\u062a\u0631 \u062d\u0630\u0641 \u0645\u06cc\u200c\u0634\u0648\u062f \u0648 \u062f\u0631 \u0645\u0646\u0648 \u0628\u0647 \u0635\u0648\u0631\u062a \u0645\u0627\u0633\u06a9\u200c\u0634\u062f\u0647 `\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022` \u0646\u0645\u0627\u06cc\u0634 \u062f\u0627\u062f\u0647 \u0645\u06cc\u200c\u0634\u0648\u062f).\n4. **\ud83d\udcc2 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0645\u0633\u06cc\u0631 \u0631\u06cc\u0645\u0648\u062a \u0631\u0648\u06cc \u0647\u0627\u0633\u062a (`ftp_set:path`):**\n - \u0628\u0631\u0627\u06cc \u0645\u0634\u062e\u0635 \u06a9\u0631\u062f\u0646 \u0645\u0633\u06cc\u0631 \u067e\u0648\u0634\u0647 \u062f\u0631 \u0647\u0627\u0633\u062a (\u0645\u0627\u0646\u0646\u062f `/public_html` \u06cc\u0627 `/domains/mysite.com/public_html`).\n5. **\ud83d\udd12 \u0633\u0648\u06cc\u06cc\u0686 \u067e\u0631\u0648\u062a\u06a9\u0644 \u0627\u0645\u0646 FTPS / TLS (`ftp_toggle_tls`):**\n - \u0628\u0627 \u06cc\u06a9 \u06a9\u0644\u06cc\u06a9 \u0628\u06cc\u0646 FTP \u0645\u0639\u0645\u0648\u0644\u06cc \u0648 TLS \u0627\u0645\u0646 \u062a\u063a\u06cc\u06cc\u0631 \u0648\u0636\u0639\u06cc\u062a \u0645\u06cc\u200c\u062f\u0647\u062f.\n6. **\ud83d\udd0d \u062a\u0633\u062a \u062e\u0648\u062f\u06a9\u0627\u0631 \u0648 \u0622\u0646\u0644\u0627\u06cc\u0646 \u0627\u062a\u0635\u0627\u0644 FTP (`btn_ftp_test`):**\n - \u0628\u0631\u0631\u0633\u06cc \u0632\u0646\u062f\u0647 \u0635\u062d\u062a \u0645\u0634\u062e\u0635\u0627\u062a \u0627\u062a\u0635\u0627\u0644 \u0628\u0627 \u0647\u0627\u0633\u062a \u0648 \u0646\u0645\u0627\u06cc\u0634 \u0648\u0636\u0639\u06cc\u062a \u0627\u0631\u062a\u0628\u0627\u0637.\n7. **\ud83d\uddd1\ufe0f \u067e\u0627\u06a9\u200c\u0633\u0627\u0632\u06cc \u0645\u0634\u062e\u0635\u0627\u062a FTP (`ftp_clear_conf`):**\n - \u0631\u06cc\u0633\u062a \u0648 \u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0645\u0644 \u0627\u0637\u0644\u0627\u0639\u0627\u062a \u0627\u062a\u0635\u0627\u0644 \u062f\u0631 \u0635\u0648\u0631\u062a \u0646\u06cc\u0627\u0632.\n\n---\n\n[[RESTART_BOT]]\n\n[[BUTTONS: layout=[\n [{\"text\": \"\ud83d\ude80 \u0628\u0627\u0632 \u06a9\u0631\u062f\u0646 \u0645\u0646\u0648\u06cc FTP\", \"cmd\": \"/ftp\"}, {\"text\": \"\ud83c\udfe0 \u0645\u0646\u0648\u06cc \u0627\u0635\u0644\u06cc\", \"cmd\": \"/menu\"}]\n]]]\n\n\u2022 \u0646\u0627\u0645 \u067e\u0631\u0648\u0698\u0647: default \n\u2022 \u0622\u062f\u0631\u0633 \u0645\u062e\u0632\u0646: https://git.msa.artacloud.ir/root/default\n",
+ "last_context_length": 78747,
+ "last_total_tokens": 774548,
+ "last_response": "\u0628\u0631\u0631\u0633\u06cc \u0639\u0645\u06cc\u0642 \u0648 \u0631\u06cc\u0634\u0647\u200c\u06cc\u0627\u0628\u06cc \u0639\u0645\u0644\u06a9\u0631\u062f \u062a\u0633\u062a \u0627\u062a\u0635\u0627\u0644 FTP \u0627\u0646\u062c\u0627\u0645 \u0634\u062f \u0648 \u062f\u0648 \u0645\u0634\u06a9\u0644 \u0627\u0635\u0644\u06cc \u0634\u0646\u0627\u0633\u0627\u06cc\u06cc \u0648 \u0628\u0631\u0637\u0631\u0641 \u06af\u0631\u062f\u06cc\u062f:\n\n---\n\n### \ud83d\udd0d \u0631\u06cc\u0634\u0647\u200c\u06cc\u0627\u0628\u06cc \u0648 \u0627\u0635\u0644\u0627\u062d\u0627\u062a \u0627\u0646\u062c\u0627\u0645\u200c\u0634\u062f\u0647:\n\n1. **\u0630\u062e\u06cc\u0631\u0647\u200c\u0633\u0627\u0632\u06cc \u067e\u0627\u06cc\u062f\u0627\u0631 \u0645\u0634\u062e\u0635\u0627\u062a FTP \u062f\u0631 \u0641\u0627\u06cc\u0644 \u0646\u0634\u0633\u062a\u200c\u0647\u0627 (`sessions.json`):**\n - **\u0639\u0644\u062a:** \u0641\u06cc\u0644\u062f\u0647\u0627\u06cc FTP \u062f\u0631 \u0632\u0645\u0627\u0646 \u0633\u0631\u06cc\u0627\u0644\u200c\u0633\u0627\u0632\u06cc \u0648 \u0630\u062e\u06cc\u0631\u0647 \u0646\u0634\u0633\u062a \u062f\u0631 `SessionManager.save()` \u0644\u062d\u0627\u0638 \u0646\u0634\u062f\u0647 \u0628\u0648\u062f\u0646\u062f\u061b \u062f\u0631 \u0646\u062a\u06cc\u062c\u0647 \u067e\u0633 \u0627\u0632 \u0630\u062e\u06cc\u0631\u0647 \u06cc\u0627 \u0631\u06cc\u200c\u0627\u0633\u062a\u0627\u0631\u062a \u0631\u0628\u0627\u062a\u060c \u0645\u0642\u0627\u062f\u06cc\u0631 \u0641\u06cc\u0644\u062f\u0647\u0627 \u0631\u06cc\u0633\u062a \u0645\u06cc\u200c\u0634\u062f\u0646\u062f \u0648 \u062a\u0633\u062a \u0628\u0627 \u0645\u0642\u0627\u062f\u06cc\u0631 \u062e\u0627\u0644\u06cc \u0627\u0646\u062c\u0627\u0645 \u0645\u06cc\u200c\u06af\u0631\u0641\u062a.\n - **\u0627\u0635\u0644\u0627\u062d:** \u062a\u0645\u0627\u0645 \u0641\u06cc\u0644\u062f\u0647\u0627\u06cc `ftp_host`\u060c `ftp_port`\u060c `ftp_user`\u060c `ftp_password`\u060c `ftp_path` \u0648 `ftp_tls` \u0628\u0647 \u0633\u0631\u06cc\u0627\u0644\u200c\u0633\u0627\u0632\u06cc \u0646\u0634\u0633\u062a\u200c\u0647\u0627 \u0627\u0636\u0627\u0641\u0647 \u0634\u062f\u0646\u062f.\n\n2. **\u0645\u062f\u06cc\u0631\u06cc\u062a \u062d\u0627\u0644\u062a Passive \u0648 \u067e\u0631\u0648\u062a\u06a9\u0644 TLS \u062f\u0631 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 `ftplib`:**\n - **\u0639\u0644\u062a:** \u062f\u0631 \u0627\u0631\u062a\u0628\u0627\u0637 \u0628\u0627 \u0628\u0631\u062e\u06cc \u0647\u0627\u0633\u062a\u200c\u0647\u0627 (\u0628\u0647 \u062f\u0644\u06cc\u0644 \u0641\u0627\u06cc\u0631\u0648\u0627\u0644 \u06cc\u0627 NAT \u0633\u0631\u0648\u0631\u0647\u0627)\u060c \u0639\u062f\u0645 \u0641\u0639\u0627\u0644\u200c\u0633\u0627\u0632\u06cc \u0635\u0631\u06cc\u062d \u062d\u0627\u0644\u062a Passive \u0648 \u06a9\u062f\u06af\u0630\u0627\u0631\u06cc UTF-8 \u062f\u0631 \u062f\u0631\u06cc\u0627\u0641\u062a \u0644\u06cc\u0633\u062a \u062f\u0627\u06cc\u0631\u06a9\u062a\u0648\u0631\u06cc\u060c \u0628\u0627\u0639\u062b \u062e\u0637\u0627\u06cc \u06a9\u0627\u0630\u0628 \u062f\u0631 \u0645\u0631\u062d\u0644\u0647 \u0646\u0647\u0627\u06cc\u06cc \u062a\u0633\u062a \u0645\u06cc\u200c\u0634\u062f. \u0647\u0645\u0686\u0646\u06cc\u0646 \u062f\u0631 FTPS \u0647\u0646\u062f\u0634\u06cc\u06a9 `auth()` \u0628\u0647 \u0635\u0648\u0631\u062a \u0627\u0633\u062a\u0627\u0646\u062f\u0627\u0631\u062f \u0627\u0636\u0627\u0641\u0647 \u0634\u062f.\n - **\u0627\u0635\u0644\u0627\u062d:** \u062a\u0646\u0638\u06cc\u0645 \u0635\u0631\u06cc\u062d Passive Mode (`ftp.set_pasv(True)`)\u060c \u0647\u0646\u062f\u0634\u06cc\u06a9 \u0627\u0645\u0646 FTPS\u060c \u0627\u0646\u06a9\u0648\u062f\u06cc\u0646\u06af UTF-8 \u0648 \u0641\u0627\u0644\u200c\u0628\u06a9 \u062f\u0631\u06cc\u0627\u0641\u062a \u0644\u06cc\u0633\u062a \u062f\u0627\u06cc\u0631\u06a9\u062a\u0648\u0631\u06cc \u0627\u0639\u0645\u0627\u0644 \u0634\u062f.\n\n3. **\u0646\u0645\u0627\u06cc\u0634 \u06a9\u0627\u0645\u0644 \u0648 \u062f\u0642\u06cc\u0642 \u06af\u0632\u0627\u0631\u0634 \u062a\u0633\u062a \u062f\u0631 \u0686\u062a:**\n - \u0628\u0647 \u062c\u0627\u06cc \u0647\u0634\u062f\u0627\u0631 \u06a9\u0648\u062a\u0627\u0647 \u062a\u0644\u06af\u0631\u0627\u0645 (Popup Alert) \u06a9\u0647 \u062e\u0637\u0627\u0647\u0627\u06cc \u0637\u0648\u0644\u0627\u0646\u06cc \u0631\u0627 \u06a9\u0648\u062a\u0627\u0647 \u0645\u06cc\u200c\u06a9\u0631\u062f\u060c \u0627\u06a9\u0646\u0648\u0646 \u06cc\u06a9 \u06a9\u0627\u0631\u062a \u06af\u0632\u0627\u0631\u0634 \u062a\u0641\u0635\u06cc\u0644\u06cc \u0628\u0627 \u062a\u0645\u0627\u0645 \u062c\u0632\u0626\u06cc\u0627\u062a \u062e\u0637\u0627 (\u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc\u060c \u0631\u0645\u0632 \u0639\u0628\u0648\u0631\u060c \u067e\u0648\u0631\u062a\u060c \u0641\u0627\u06cc\u0631\u0648\u0627\u0644 \u06cc\u0627 \u0645\u0633\u06cc\u0631 \u0646\u0627\u0645\u0639\u062a\u0628\u0631) \u0645\u0633\u062a\u0642\u06cc\u0645\u0627\u064b \u062f\u0631 \u0686\u062a \u0627\u0631\u0633\u0627\u0644 \u0645\u06cc\u200c\u0634\u0648\u062f.\n\n---\n\n[[RESTART_BOT]]\n\n[[BUTTONS: layout=[\n [{\"text\": \"\ud83d\ude80 \u0628\u0627\u0632 \u06a9\u0631\u062f\u0646 \u0645\u0646\u0648\u06cc FTP\", \"cmd\": \"/ftp\"}, {\"text\": \"\ud83d\udd0d \u062a\u0633\u062a \u0627\u062a\u0635\u0627\u0644 FTP\", \"prompt\": \"\u0648\u0636\u0639\u06cc\u062a \u0627\u062a\u0635\u0627\u0644 FTP \u067e\u0631\u0648\u0698\u0647 \u0631\u0648 \u062a\u0633\u062a \u06a9\u0646\"}]\n]]]\n\n\u2022 \u0646\u0627\u0645 \u067e\u0631\u0648\u0698\u0647: default \n\u2022 \u0622\u062f\u0631\u0633 \u0645\u062e\u0632\u0646: https://git.msa.artacloud.ir/root/default\n",
"created_at": 1787995349.975879,
"description": "",
"conversation_titles": {
@@ -97,19 +97,19 @@
}
},
"turn_in_progress": false,
- "last_prompt": "\u0628\u0644\u0647",
- "last_response": "\u0642\u0627\u0628\u0644\u06cc\u062a \u0645\u062f\u06cc\u0631\u06cc\u062a \u06a9\u0627\u0645\u0644 \u0648 \u062a\u0639\u0627\u0645\u0644\u06cc \u062a\u0646\u0638\u06cc\u0645\u0627\u062a FTP \u0645\u0633\u062a\u0642\u06cc\u0645\u0627\u064b \u0628\u0627 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u0631\u0628\u0627\u062a \u062a\u0644\u06af\u0631\u0627\u0645 \u067e\u06cc\u0627\u062f\u0647\u200c\u0633\u0627\u0632\u06cc \u0648 \u0641\u0639\u0627\u0644 \u0634\u062f.\n\n---\n\n### \u2728 \u0627\u0645\u06a9\u0627\u0646\u0627\u062a \u0648 \u062f\u06a9\u0645\u0647\u200c\u0647\u0627\u06cc \u062c\u062f\u06cc\u062f \u0627\u0636\u0627\u0641\u0647 \u0634\u062f\u0647:\n\n1. **\ud83c\udf10 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0647\u0627\u0633\u062a / \u067e\u0648\u0631\u062a (`ftp_set:host`):**\n - \u0628\u0627 \u06a9\u0644\u06cc\u06a9 \u0631\u0648\u06cc \u062f\u06a9\u0645\u0647\u060c \u0631\u0628\u0627\u062a \u0645\u0646\u062a\u0638\u0631 \u062f\u0631\u06cc\u0627\u0641\u062a \u0622\u062f\u0631\u0633 \u0647\u0627\u0633\u062a \u0645\u06cc\u200c\u0645\u0627\u0646\u062f (\u0645\u062b\u0627\u0644: `ftp.mysite.com` \u06cc\u0627 `1.2.3.4:2121`).\n2. **\ud83d\udc64 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc (`ftp_set:user`):**\n - \u0628\u0631\u0627\u06cc \u0648\u0627\u0631\u062f \u06a9\u0631\u062f\u0646 \u06cc\u0648\u0632\u0631\u0646\u06cc\u0645 \u0627\u06a9\u0627\u0646\u062a FTP.\n3. **\ud83d\udd11 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0631\u0645\u0632 \u0639\u0628\u0648\u0631 (`ftp_set:pass`):**\n - \u0628\u0631\u0627\u06cc \u0648\u0627\u0631\u062f \u06a9\u0631\u062f\u0646 \u0631\u0645\u0632 \u0639\u0628\u0648\u0631 (\u067e\u0633 \u0627\u0632 \u062f\u0631\u06cc\u0627\u0641\u062a\u060c \u067e\u06cc\u0627\u0645 \u0628\u0631\u0627\u06cc \u0627\u0645\u0646\u06cc\u062a \u0628\u06cc\u0634\u062a\u0631 \u062d\u0630\u0641 \u0645\u06cc\u200c\u0634\u0648\u062f \u0648 \u062f\u0631 \u0645\u0646\u0648 \u0628\u0647 \u0635\u0648\u0631\u062a \u0645\u0627\u0633\u06a9\u200c\u0634\u062f\u0647 `\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022` \u0646\u0645\u0627\u06cc\u0634 \u062f\u0627\u062f\u0647 \u0645\u06cc\u200c\u0634\u0648\u062f).\n4. **\ud83d\udcc2 \u0648\u06cc\u0631\u0627\u06cc\u0634 \u0645\u0633\u06cc\u0631 \u0631\u06cc\u0645\u0648\u062a \u0631\u0648\u06cc \u0647\u0627\u0633\u062a (`ftp_set:path`):**\n - \u0628\u0631\u0627\u06cc \u0645\u0634\u062e\u0635 \u06a9\u0631\u062f\u0646 \u0645\u0633\u06cc\u0631 \u067e\u0648\u0634\u0647 \u062f\u0631 \u0647\u0627\u0633\u062a (\u0645\u0627\u0646\u0646\u062f `/public_html` \u06cc\u0627 `/domains/mysite.com/public_html`).\n5. **\ud83d\udd12 \u0633\u0648\u06cc\u06cc\u0686 \u067e\u0631\u0648\u062a\u06a9\u0644 \u0627\u0645\u0646 FTPS / TLS (`ftp_toggle_tls`):**\n - \u0628\u0627 \u06cc\u06a9 \u06a9\u0644\u06cc\u06a9 \u0628\u06cc\u0646 FTP \u0645\u0639\u0645\u0648\u0644\u06cc \u0648 TLS \u0627\u0645\u0646 \u062a\u063a\u06cc\u06cc\u0631 \u0648\u0636\u0639\u06cc\u062a \u0645\u06cc\u200c\u062f\u0647\u062f.\n6. **\ud83d\udd0d \u062a\u0633\u062a \u062e\u0648\u062f\u06a9\u0627\u0631 \u0648 \u0622\u0646\u0644\u0627\u06cc\u0646 \u0627\u062a\u0635\u0627\u0644 FTP (`btn_ftp_test`):**\n - \u0628\u0631\u0631\u0633\u06cc \u0632\u0646\u062f\u0647 \u0635\u062d\u062a \u0645\u0634\u062e\u0635\u0627\u062a \u0627\u062a\u0635\u0627\u0644 \u0628\u0627 \u0647\u0627\u0633\u062a \u0648 \u0646\u0645\u0627\u06cc\u0634 \u0648\u0636\u0639\u06cc\u062a \u0627\u0631\u062a\u0628\u0627\u0637.\n7. **\ud83d\uddd1\ufe0f \u067e\u0627\u06a9\u200c\u0633\u0627\u0632\u06cc \u0645\u0634\u062e\u0635\u0627\u062a FTP (`ftp_clear_conf`):**\n - \u0631\u06cc\u0633\u062a \u0648 \u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u06a9\u0627\u0645\u0644 \u0627\u0637\u0644\u0627\u0639\u0627\u062a \u0627\u062a\u0635\u0627\u0644 \u062f\u0631 \u0635\u0648\u0631\u062a \u0646\u06cc\u0627\u0632.\n\n---\n\n[[RESTART_BOT]]\n\n[[BUTTONS: layout=[\n [{\"text\": \"\ud83d\ude80 \u0628\u0627\u0632 \u06a9\u0631\u062f\u0646 \u0645\u0646\u0648\u06cc FTP\", \"cmd\": \"/ftp\"}, {\"text\": \"\ud83c\udfe0 \u0645\u0646\u0648\u06cc \u0627\u0635\u0644\u06cc\", \"cmd\": \"/menu\"}]\n]]]\n\n\u2022 \u0646\u0627\u0645 \u067e\u0631\u0648\u0698\u0647: default \n\u2022 \u0622\u062f\u0631\u0633 \u0645\u062e\u0632\u0646: https://git.msa.artacloud.ir/root/default\n",
+ "last_prompt": "\u062a\u0633\u062a \u0627\u0641 \u062a\u06cc \u067e\u06cc \u0646\u062a\u06cc\u062c\u0647 \u0631\u0648 \u062f\u0631\u0633\u062a \u0646\u0645\u06cc\u06af\u0647",
+ "last_response": "\u0628\u0631\u0631\u0633\u06cc \u0639\u0645\u06cc\u0642 \u0648 \u0631\u06cc\u0634\u0647\u200c\u06cc\u0627\u0628\u06cc \u0639\u0645\u0644\u06a9\u0631\u062f \u062a\u0633\u062a \u0627\u062a\u0635\u0627\u0644 FTP \u0627\u0646\u062c\u0627\u0645 \u0634\u062f \u0648 \u062f\u0648 \u0645\u0634\u06a9\u0644 \u0627\u0635\u0644\u06cc \u0634\u0646\u0627\u0633\u0627\u06cc\u06cc \u0648 \u0628\u0631\u0637\u0631\u0641 \u06af\u0631\u062f\u06cc\u062f:\n\n---\n\n### \ud83d\udd0d \u0631\u06cc\u0634\u0647\u200c\u06cc\u0627\u0628\u06cc \u0648 \u0627\u0635\u0644\u0627\u062d\u0627\u062a \u0627\u0646\u062c\u0627\u0645\u200c\u0634\u062f\u0647:\n\n1. **\u0630\u062e\u06cc\u0631\u0647\u200c\u0633\u0627\u0632\u06cc \u067e\u0627\u06cc\u062f\u0627\u0631 \u0645\u0634\u062e\u0635\u0627\u062a FTP \u062f\u0631 \u0641\u0627\u06cc\u0644 \u0646\u0634\u0633\u062a\u200c\u0647\u0627 (`sessions.json`):**\n - **\u0639\u0644\u062a:** \u0641\u06cc\u0644\u062f\u0647\u0627\u06cc FTP \u062f\u0631 \u0632\u0645\u0627\u0646 \u0633\u0631\u06cc\u0627\u0644\u200c\u0633\u0627\u0632\u06cc \u0648 \u0630\u062e\u06cc\u0631\u0647 \u0646\u0634\u0633\u062a \u062f\u0631 `SessionManager.save()` \u0644\u062d\u0627\u0638 \u0646\u0634\u062f\u0647 \u0628\u0648\u062f\u0646\u062f\u061b \u062f\u0631 \u0646\u062a\u06cc\u062c\u0647 \u067e\u0633 \u0627\u0632 \u0630\u062e\u06cc\u0631\u0647 \u06cc\u0627 \u0631\u06cc\u200c\u0627\u0633\u062a\u0627\u0631\u062a \u0631\u0628\u0627\u062a\u060c \u0645\u0642\u0627\u062f\u06cc\u0631 \u0641\u06cc\u0644\u062f\u0647\u0627 \u0631\u06cc\u0633\u062a \u0645\u06cc\u200c\u0634\u062f\u0646\u062f \u0648 \u062a\u0633\u062a \u0628\u0627 \u0645\u0642\u0627\u062f\u06cc\u0631 \u062e\u0627\u0644\u06cc \u0627\u0646\u062c\u0627\u0645 \u0645\u06cc\u200c\u06af\u0631\u0641\u062a.\n - **\u0627\u0635\u0644\u0627\u062d:** \u062a\u0645\u0627\u0645 \u0641\u06cc\u0644\u062f\u0647\u0627\u06cc `ftp_host`\u060c `ftp_port`\u060c `ftp_user`\u060c `ftp_password`\u060c `ftp_path` \u0648 `ftp_tls` \u0628\u0647 \u0633\u0631\u06cc\u0627\u0644\u200c\u0633\u0627\u0632\u06cc \u0646\u0634\u0633\u062a\u200c\u0647\u0627 \u0627\u0636\u0627\u0641\u0647 \u0634\u062f\u0646\u062f.\n\n2. **\u0645\u062f\u06cc\u0631\u06cc\u062a \u062d\u0627\u0644\u062a Passive \u0648 \u067e\u0631\u0648\u062a\u06a9\u0644 TLS \u062f\u0631 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 `ftplib`:**\n - **\u0639\u0644\u062a:** \u062f\u0631 \u0627\u0631\u062a\u0628\u0627\u0637 \u0628\u0627 \u0628\u0631\u062e\u06cc \u0647\u0627\u0633\u062a\u200c\u0647\u0627 (\u0628\u0647 \u062f\u0644\u06cc\u0644 \u0641\u0627\u06cc\u0631\u0648\u0627\u0644 \u06cc\u0627 NAT \u0633\u0631\u0648\u0631\u0647\u0627)\u060c \u0639\u062f\u0645 \u0641\u0639\u0627\u0644\u200c\u0633\u0627\u0632\u06cc \u0635\u0631\u06cc\u062d \u062d\u0627\u0644\u062a Passive \u0648 \u06a9\u062f\u06af\u0630\u0627\u0631\u06cc UTF-8 \u062f\u0631 \u062f\u0631\u06cc\u0627\u0641\u062a \u0644\u06cc\u0633\u062a \u062f\u0627\u06cc\u0631\u06a9\u062a\u0648\u0631\u06cc\u060c \u0628\u0627\u0639\u062b \u062e\u0637\u0627\u06cc \u06a9\u0627\u0630\u0628 \u062f\u0631 \u0645\u0631\u062d\u0644\u0647 \u0646\u0647\u0627\u06cc\u06cc \u062a\u0633\u062a \u0645\u06cc\u200c\u0634\u062f. \u0647\u0645\u0686\u0646\u06cc\u0646 \u062f\u0631 FTPS \u0647\u0646\u062f\u0634\u06cc\u06a9 `auth()` \u0628\u0647 \u0635\u0648\u0631\u062a \u0627\u0633\u062a\u0627\u0646\u062f\u0627\u0631\u062f \u0627\u0636\u0627\u0641\u0647 \u0634\u062f.\n - **\u0627\u0635\u0644\u0627\u062d:** \u062a\u0646\u0638\u06cc\u0645 \u0635\u0631\u06cc\u062d Passive Mode (`ftp.set_pasv(True)`)\u060c \u0647\u0646\u062f\u0634\u06cc\u06a9 \u0627\u0645\u0646 FTPS\u060c \u0627\u0646\u06a9\u0648\u062f\u06cc\u0646\u06af UTF-8 \u0648 \u0641\u0627\u0644\u200c\u0628\u06a9 \u062f\u0631\u06cc\u0627\u0641\u062a \u0644\u06cc\u0633\u062a \u062f\u0627\u06cc\u0631\u06a9\u062a\u0648\u0631\u06cc \u0627\u0639\u0645\u0627\u0644 \u0634\u062f.\n\n3. **\u0646\u0645\u0627\u06cc\u0634 \u06a9\u0627\u0645\u0644 \u0648 \u062f\u0642\u06cc\u0642 \u06af\u0632\u0627\u0631\u0634 \u062a\u0633\u062a \u062f\u0631 \u0686\u062a:**\n - \u0628\u0647 \u062c\u0627\u06cc \u0647\u0634\u062f\u0627\u0631 \u06a9\u0648\u062a\u0627\u0647 \u062a\u0644\u06af\u0631\u0627\u0645 (Popup Alert) \u06a9\u0647 \u062e\u0637\u0627\u0647\u0627\u06cc \u0637\u0648\u0644\u0627\u0646\u06cc \u0631\u0627 \u06a9\u0648\u062a\u0627\u0647 \u0645\u06cc\u200c\u06a9\u0631\u062f\u060c \u0627\u06a9\u0646\u0648\u0646 \u06cc\u06a9 \u06a9\u0627\u0631\u062a \u06af\u0632\u0627\u0631\u0634 \u062a\u0641\u0635\u06cc\u0644\u06cc \u0628\u0627 \u062a\u0645\u0627\u0645 \u062c\u0632\u0626\u06cc\u0627\u062a \u062e\u0637\u0627 (\u0646\u0627\u0645 \u06a9\u0627\u0631\u0628\u0631\u06cc\u060c \u0631\u0645\u0632 \u0639\u0628\u0648\u0631\u060c \u067e\u0648\u0631\u062a\u060c \u0641\u0627\u06cc\u0631\u0648\u0627\u0644 \u06cc\u0627 \u0645\u0633\u06cc\u0631 \u0646\u0627\u0645\u0639\u062a\u0628\u0631) \u0645\u0633\u062a\u0642\u06cc\u0645\u0627\u064b \u062f\u0631 \u0686\u062a \u0627\u0631\u0633\u0627\u0644 \u0645\u06cc\u200c\u0634\u0648\u062f.\n\n---\n\n[[RESTART_BOT]]\n\n[[BUTTONS: layout=[\n [{\"text\": \"\ud83d\ude80 \u0628\u0627\u0632 \u06a9\u0631\u062f\u0646 \u0645\u0646\u0648\u06cc FTP\", \"cmd\": \"/ftp\"}, {\"text\": \"\ud83d\udd0d \u062a\u0633\u062a \u0627\u062a\u0635\u0627\u0644 FTP\", \"prompt\": \"\u0648\u0636\u0639\u06cc\u062a \u0627\u062a\u0635\u0627\u0644 FTP \u067e\u0631\u0648\u0698\u0647 \u0631\u0648 \u062a\u0633\u062a \u06a9\u0646\"}]\n]]]\n\n\u2022 \u0646\u0627\u0645 \u067e\u0631\u0648\u0698\u0647: default \n\u2022 \u0622\u062f\u0631\u0633 \u0645\u062e\u0632\u0646: https://git.msa.artacloud.ir/root/default\n",
"last_status_msg_id": null,
"restart_pending": false,
"last_delivered": true,
- "last_update_time": 1788112105.8170931,
+ "last_update_time": 1788112414.7699497,
"workspace": "/root",
"conversation_id": "ce39875a-dec2-41c5-b2fe-54593a48b00c",
"model": "gemini-3.7-flash-auto",
"effort": "high",
"language": "fa",
- "last_context_length": 41556,
- "last_total_tokens": 332309
+ "last_context_length": 78747,
+ "last_total_tokens": 774548
},
"232801087": {
"chat_id": 232801087,