AI Update: 📎 چرا این پاسخ اینجوری اوخد؟ چطوری اصلاحش کنیم؟
This commit is contained in:
@@ -1801,7 +1801,7 @@ class AGYEngine:
|
||||
text_out = rec["text"]
|
||||
elif rec.get("tools"):
|
||||
is_fa = (session.language or "").lower() in ("fa", "farsi", "persian", "🇮🇷 persian / farsi (فارسی)")
|
||||
tool_bullets = "\n".join(f"• <code>{t}</code>" for t in rec["tools"][-10:])
|
||||
tool_bullets = "\n".join(f"• `{t}`" for t in rec["tools"][-10:])
|
||||
if is_fa:
|
||||
text_out = f"✅ **دستورات و ابزارهای زیر با موفقیت اجرا شدند:**\n\n{tool_bullets}"
|
||||
else:
|
||||
|
||||
@@ -5040,6 +5040,336 @@ 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 == "btn_ssh_menu":
|
||||
text, markup = await build_ssh_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.startswith("ssh_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": "ssh_set",
|
||||
"field": field_name,
|
||||
"project": curr_proj.name,
|
||||
}
|
||||
|
||||
field_prompts = {
|
||||
"host": (
|
||||
"🌐 <b>لطفاً آدرس هاست یا IP سرور SSH را ارسال کنید:</b>\n\n"
|
||||
"<i>مثال:</i> <code>server.mysite.com</code> یا <code>185.120.30.40:22</code>"
|
||||
) if is_fa else (
|
||||
"🌐 <b>Please enter the SSH Host/IP address:</b>\n\n"
|
||||
"<i>Example:</i> <code>server.mysite.com</code> or <code>185.120.30.40:22</code>"
|
||||
),
|
||||
"user": (
|
||||
"👤 <b>لطفاً نام کاربری اتصال SSH را ارسال کنید:</b>\n\n"
|
||||
"<i>مثال:</i> <code>root</code> یا <code>ubuntu</code> یا <code>deployer</code>"
|
||||
) if is_fa else (
|
||||
"👤 <b>Please enter the SSH Username:</b>\n\n"
|
||||
"<i>Example:</i> <code>root</code> or <code>ubuntu</code>"
|
||||
),
|
||||
"pass": (
|
||||
"🔑 <b>لطفاً رمز عبور (Password) اتصال SSH را ارسال کنید:</b>\n\n"
|
||||
"<i>(پیام ارسالی بلافاصله پس از ثبت جهت امنیت حذف خواهد شد)</i>"
|
||||
) if is_fa else (
|
||||
"🔑 <b>Please enter the SSH Password:</b>\n\n"
|
||||
"<i>(Message will be deleted automatically for security)</i>"
|
||||
),
|
||||
"key": (
|
||||
"🗝️ <b>لطفاً محتوای کلید خصوصی SSH Key (مانند OpenSSH/RSA/Ed25519) یا مسیر فایل آن را ارسال کنید:</b>\n\n"
|
||||
"<i>مثال:</i> <code>-----BEGIN OPENSSH PRIVATE KEY----- ...</code>\n"
|
||||
"<i>(پیام ارسالی بلافاصله جهت امنیت حذف خواهد شد)</i>"
|
||||
) if is_fa else (
|
||||
"🗝️ <b>Please send your SSH Private Key content or file path:</b>\n\n"
|
||||
"<i>(Message will be deleted automatically for security)</i>"
|
||||
),
|
||||
"path": (
|
||||
"📂 <b>لطفاً مسیر پوشه ریموت مقصد روی سرور را ارسال کنید:</b>\n\n"
|
||||
"<i>مثال:</i> <code>/var/www/myproject</code> یا <code>/home/ubuntu/app</code>"
|
||||
) if is_fa else (
|
||||
"📂 <b>Please enter the remote directory path on the server:</b>\n\n"
|
||||
"<i>Example:</i> <code>/var/www/myproject</code>"
|
||||
),
|
||||
}
|
||||
|
||||
prompt_text = field_prompts.get(field_name, "لطفاً مقدار جدید را ارسال کنید:")
|
||||
cancel_kb = InlineKeyboardMarkup([[InlineKeyboardButton("❌ انصراف و بازگشت" if is_fa else "❌ Cancel", callback_data="btn_ssh_menu")]])
|
||||
await query.edit_message_text(prompt_text, parse_mode=constants.ParseMode.HTML, reply_markup=cancel_kb)
|
||||
|
||||
elif data == "btn_ssh_exec_prompt":
|
||||
if not curr_proj:
|
||||
await query.answer("⚠️ پروژهای یافت نشد." if is_fa else "⚠️ No project found.", show_alert=True)
|
||||
return
|
||||
|
||||
if not curr_proj.ssh_host:
|
||||
await query.answer("⚠️ ابتدا باید مشخصات هاست و سرور SSH را تنظیم نمایید.", show_alert=True)
|
||||
return
|
||||
|
||||
USER_INPUT_STATES[chat_id] = {
|
||||
"type": "ssh_exec",
|
||||
"project": curr_proj.name,
|
||||
}
|
||||
msg = (
|
||||
"💻 <b>اجرای فرمان در سرور ریموت SSH:</b>\n\n"
|
||||
f"• 🌐 <b>سرور:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 📂 <b>مسیر:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n\n"
|
||||
"لطفاً دستور مورد نظر خود را در یک پیام ارسال کنید:\n"
|
||||
"<i>مثال:</i> <code>php -v</code> یا <code>git status</code> یا <code>systemctl status nginx</code>"
|
||||
) if is_fa else (
|
||||
"💻 <b>Run Remote SSH Command:</b>\n\n"
|
||||
f"• 🌐 <b>Server:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 📂 <b>Path:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n\n"
|
||||
"Please send the shell command to execute:"
|
||||
)
|
||||
cancel_kb = InlineKeyboardMarkup([[InlineKeyboardButton("❌ انصراف و بازگشت" if is_fa else "❌ Cancel", callback_data="btn_ssh_menu")]])
|
||||
await query.edit_message_text(msg, parse_mode=constants.ParseMode.HTML, reply_markup=cancel_kb)
|
||||
|
||||
elif data == "ssh_clear_conf":
|
||||
confirm_text = (
|
||||
"🗑️ <b>آیا از پاکسازی کامل اطلاعات سرور SSH این پروژه اطمینان دارید؟</b>\n\n"
|
||||
"تمام مشخصات هاست، نام کاربری، رمز عبور و کلید SSH حذف خواهند شد."
|
||||
) if is_fa else (
|
||||
"🗑️ <b>Are you sure you want to clear SSH settings for this project?</b>"
|
||||
)
|
||||
keyboard = [
|
||||
[
|
||||
InlineKeyboardButton("💥 بله، پاک شود" if is_fa else "💥 Yes, Clear", callback_data="ssh_clear_do"),
|
||||
InlineKeyboardButton("❌ انصراف" if is_fa else "❌ Cancel", callback_data="btn_ssh_menu"),
|
||||
]
|
||||
]
|
||||
await query.edit_message_text(confirm_text, parse_mode=constants.ParseMode.HTML, reply_markup=InlineKeyboardMarkup(keyboard))
|
||||
|
||||
elif data == "ssh_clear_do":
|
||||
if curr_proj:
|
||||
curr_proj.ssh_host = None
|
||||
curr_proj.ssh_port = 22
|
||||
curr_proj.ssh_user = None
|
||||
curr_proj.ssh_password = None
|
||||
curr_proj.ssh_key = None
|
||||
curr_proj.ssh_path = "/"
|
||||
session_manager.save()
|
||||
remote_audit.log_operation(
|
||||
user_id=chat_id,
|
||||
project_name=curr_proj.name,
|
||||
service_type="ssh",
|
||||
action_type="update_config",
|
||||
requester="user_button",
|
||||
status="success",
|
||||
details="Cleared SSH config",
|
||||
result_summary="SSH configuration cleared",
|
||||
)
|
||||
await query.answer("🗑️ اطلاعات SSH با موفقیت پاک شد." if is_fa else "🗑️ SSH settings cleared.")
|
||||
text, markup = await build_ssh_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_ssh_test":
|
||||
await query.answer("🔍 در حال بررسی اتصال SSH..." if is_fa else "🔍 Testing SSH connection...")
|
||||
if curr_proj and curr_proj.ssh_host:
|
||||
test_ok, test_msg = await ssh_manager.test_connection(
|
||||
host=curr_proj.ssh_host,
|
||||
port=curr_proj.ssh_port,
|
||||
user=curr_proj.ssh_user or "",
|
||||
password=curr_proj.ssh_password,
|
||||
key=curr_proj.ssh_key,
|
||||
path=curr_proj.ssh_path or "/",
|
||||
user_id=chat_id,
|
||||
project_name=curr_proj.name,
|
||||
requester="user_button",
|
||||
)
|
||||
icon = "✅" if test_ok else "❌"
|
||||
res_card = (
|
||||
f"{icon} <b>نتیجه تست اتصال SSH:</b>\n\n"
|
||||
f"• 🌐 <b>هاست:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 👤 <b>نام کاربری:</b> <code>{escape_html(curr_proj.ssh_user or '(بدون نام کاربری)')}</code>\n"
|
||||
f"• 📂 <b>مسیر:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n\n"
|
||||
f"📋 <b>گزارش ارتباط:</b>\n{test_msg}"
|
||||
) if is_fa else (
|
||||
f"{icon} <b>SSH Connection Test Result:</b>\n\n"
|
||||
f"• 🌐 <b>Host:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 👤 <b>User:</b> <code>{escape_html(curr_proj.ssh_user or '(none)')}</code>\n"
|
||||
f"• 📂 <b>Path:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n\n"
|
||||
f"📋 <b>Status:</b>\n{test_msg}"
|
||||
)
|
||||
try:
|
||||
await query.message.reply_html(res_card, disable_web_page_preview=True)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
await query.answer("⚠️ اطلاعات SSH هنوز برای این پروژه تنظیم نشده است.", show_alert=True)
|
||||
except Exception:
|
||||
pass
|
||||
text, markup = await build_ssh_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_ssh_push":
|
||||
await query.answer("🚀 در حال ارسال شاخه پروداکشن به سرور SSH..." if is_fa else "🚀 Pushing production to SSH...")
|
||||
if curr_proj and curr_proj.ssh_host:
|
||||
status_msg = await query.message.reply_html("🚀 <i>در حال ارسال کدهای شاخه production به سرور SSH...</i>" if is_fa else "🚀 <i>Uploading production branch files via SFTP...</i>")
|
||||
|
||||
res = await ssh_manager.push_project(
|
||||
workspace_path=curr_proj.workspace,
|
||||
host=curr_proj.ssh_host,
|
||||
port=curr_proj.ssh_port,
|
||||
user=curr_proj.ssh_user or "",
|
||||
password=curr_proj.ssh_password,
|
||||
key=curr_proj.ssh_key,
|
||||
remote_path=curr_proj.ssh_path or "/",
|
||||
branch="production",
|
||||
user_id=chat_id,
|
||||
project_name=curr_proj.name,
|
||||
requester="user_button",
|
||||
)
|
||||
|
||||
if res.get("success"):
|
||||
files_up = res.get("files_uploaded", 0)
|
||||
files_skip = res.get("files_skipped", 0)
|
||||
dur = round(res.get("duration", 0), 2)
|
||||
b_trans = res.get("bytes_transferred", 0)
|
||||
size_mb = f"{b_trans / (1024 * 1024):.2f} MB" if b_trans > 1024 * 1024 else f"{b_trans / 1024:.1f} KB"
|
||||
|
||||
report = (
|
||||
f"✅ <b>پوش به سرور پروداکشن با موفقیت انجام شد!</b>\n\n"
|
||||
f"• 📁 <b>پروژه:</b> <code>{escape_html(curr_proj.name)}</code>\n"
|
||||
f"• 🌿 <b>شاخه مبدأ:</b> <code>production</code>\n"
|
||||
f"• 🌐 <b>سرور مقصد:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 📂 <b>مسیر سرور:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n"
|
||||
f"• 📦 <b>تعداد فایلهای آپلود شده:</b> {files_up}\n"
|
||||
f"• 🛡️ <b>فایلهای محلی/دیتابیس فیلتر شده:</b> {files_skip}\n"
|
||||
f"• 🔒 <b>دادههای سرور پروداکشن:</b> کاملاً دستنخورده و ایمن حفظ شدند\n"
|
||||
f"• 📊 <b>حجم کل منتقل شده:</b> {size_mb}\n"
|
||||
f"• ⏱️ <b>مدت زمان انتقال:</b> {dur} ثانیه"
|
||||
) if is_fa else (
|
||||
f"✅ <b>SSH Push Completed Successfully!</b>\n\n"
|
||||
f"• 📁 <b>Project:</b> <code>{escape_html(curr_proj.name)}</code>\n"
|
||||
f"• 🌿 <b>Source Branch:</b> <code>production</code>\n"
|
||||
f"• 🌐 <b>Target Server:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 📂 <b>Remote Path:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n"
|
||||
f"• 📦 <b>Files Uploaded:</b> {files_up}\n"
|
||||
f"• 🛡️ <b>Skipped Junk/Databases:</b> {files_skip}\n"
|
||||
f"• 🔒 <b>Remote Production Data:</b> Safe & Untouched\n"
|
||||
f"• 📊 <b>Transferred:</b> {size_mb}\n"
|
||||
f"• ⏱️ <b>Duration:</b> {dur}s"
|
||||
)
|
||||
try:
|
||||
await status_msg.edit_text(report, parse_mode=constants.ParseMode.HTML)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
err = res.get("error", "Error")
|
||||
err_report = (
|
||||
f"❌ <b>خطا در پوش به سرور SSH:</b>\n\n"
|
||||
f"• ⚠️ <b>پیام خطا:</b> {escape_html(str(err))}\n"
|
||||
f"• 💡 <i>پیشنهاد: ابتدا دکمه تست اتصال را بررسی کنید یا دسترسیهای دایرکتوری سرور را چک فرمایید.</i>"
|
||||
) if is_fa else f"❌ <b>SSH Push Error:</b>\n{escape_html(str(err))}"
|
||||
try:
|
||||
await status_msg.edit_text(err_report, parse_mode=constants.ParseMode.HTML)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
await query.answer("⚠️ اطلاعات SSH تنظیم نشده است.", show_alert=True)
|
||||
except Exception:
|
||||
pass
|
||||
text, markup = await build_ssh_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_ssh_pull":
|
||||
await query.answer("📥 در حال دریافت فایلها از سرور SSH..." if is_fa else "📥 Pulling files from SSH server...")
|
||||
if curr_proj and curr_proj.ssh_host:
|
||||
status_msg = await query.message.reply_html("📥 <i>در حال دانلود فایلهای پروژه از سرور SSH...</i>" if is_fa else "📥 <i>Downloading files via SFTP...</i>")
|
||||
|
||||
res = await ssh_manager.pull_project(
|
||||
workspace_path=curr_proj.workspace,
|
||||
host=curr_proj.ssh_host,
|
||||
port=curr_proj.ssh_port,
|
||||
user=curr_proj.ssh_user or "",
|
||||
password=curr_proj.ssh_password,
|
||||
key=curr_proj.ssh_key,
|
||||
remote_path=curr_proj.ssh_path or "/",
|
||||
branch="production",
|
||||
user_id=chat_id,
|
||||
project_name=curr_proj.name,
|
||||
requester="user_button",
|
||||
)
|
||||
|
||||
if res.get("success"):
|
||||
files_down = res.get("files_downloaded", 0)
|
||||
files_skip = res.get("files_skipped", 0)
|
||||
dur = round(res.get("duration", 0), 2)
|
||||
b_trans = res.get("bytes_transferred", 0)
|
||||
size_mb = f"{b_trans / (1024 * 1024):.2f} MB" if b_trans > 1024 * 1024 else f"{b_trans / 1024:.1f} KB"
|
||||
|
||||
report = (
|
||||
f"✅ <b>دریافت فایلها (Pull) با موفقیت انجام شد!</b>\n\n"
|
||||
f"• 📁 <b>پروژه:</b> <code>{escape_html(curr_proj.name)}</code>\n"
|
||||
f"• 🌐 <b>سرور مبدأ:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 📂 <b>مسیر سرور:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n"
|
||||
f"• 📦 <b>تعداد فایلهای دریافت شده:</b> {files_down}\n"
|
||||
f"• 🛡️ <b>فایلهای نادیده گرفتهشده (دیتابیس/لاگ):</b> {files_skip}\n"
|
||||
f"• 📊 <b>حجم کل دانلود شده:</b> {size_mb}\n"
|
||||
f"• ⏱️ <b>مدت زمان انتقال:</b> {dur} ثانیه"
|
||||
) if is_fa else (
|
||||
f"✅ <b>SSH Pull Completed Successfully!</b>\n\n"
|
||||
f"• 📁 <b>Project:</b> <code>{escape_html(curr_proj.name)}</code>\n"
|
||||
f"• 🌐 <b>Source Server:</b> <code>{escape_html(curr_proj.ssh_host)}:{curr_proj.ssh_port}</code>\n"
|
||||
f"• 📂 <b>Remote Path:</b> <code>{escape_html(curr_proj.ssh_path or '/')}</code>\n"
|
||||
f"• 📦 <b>Files Downloaded:</b> {files_down}\n"
|
||||
f"• 🛡️ <b>Skipped (Databases/Logs):</b> {files_skip}\n"
|
||||
f"• 📊 <b>Transferred:</b> {size_mb}\n"
|
||||
f"• ⏱️ <b>Duration:</b> {dur}s"
|
||||
)
|
||||
try:
|
||||
await status_msg.edit_text(report, parse_mode=constants.ParseMode.HTML)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
err = res.get("error", "Error")
|
||||
err_report = (
|
||||
f"❌ <b>خطا در دریافت از سرور SSH:</b>\n\n"
|
||||
f"• ⚠️ <b>پیام خطا:</b> {escape_html(str(err))}\n"
|
||||
) if is_fa else f"❌ <b>SSH Pull Error:</b>\n{escape_html(str(err))}"
|
||||
try:
|
||||
await status_msg.edit_text(err_report, parse_mode=constants.ParseMode.HTML)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
await query.answer("⚠️ اطلاعات SSH تنظیم نشده است.", show_alert=True)
|
||||
except Exception:
|
||||
pass
|
||||
text, markup = await build_ssh_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_ssh_logs":
|
||||
await query.answer("📜 در حال دریافت تاریخچه..." if is_fa else "📜 Loading logs...")
|
||||
proj_name = curr_proj.name if curr_proj else "default"
|
||||
logs = remote_audit.get_recent_logs(user_id=chat_id, project_name=proj_name, limit=12)
|
||||
log_text = remote_audit.format_logs_for_tg(logs, project_name=proj_name, is_fa=is_fa)
|
||||
back_kb = InlineKeyboardMarkup([[InlineKeyboardButton("🔙 بازگشت به منوی SSH" if is_fa else "🔙 Back to SSH", callback_data="btn_ssh_menu")]])
|
||||
try:
|
||||
await query.edit_message_text(log_text, parse_mode=constants.ParseMode.HTML, reply_markup=back_kb)
|
||||
except Exception:
|
||||
await query.message.reply_html(log_text, reply_markup=back_kb)
|
||||
|
||||
elif data == "btn_git_sync":
|
||||
await query.answer("🔄 در حال همگامسازی با Gitea..." if is_fa else "🔄 Syncing with Gitea...")
|
||||
if curr_proj:
|
||||
@@ -7663,6 +7993,7 @@ def main():
|
||||
application.add_handler(CommandHandler(["git", "repo", "repository", "gitea"], git_command))
|
||||
application.add_handler(CommandHandler(["publish", "gitpublish", "release"], publish_command))
|
||||
application.add_handler(CommandHandler(["ftp", "ftpdeploy", "deploy"], ftp_command))
|
||||
application.add_handler(CommandHandler(["ssh", "remotedev", "sshdev", "remote"], ssh_command))
|
||||
application.add_handler(CommandHandler(["sync", "gitsync", "pull"], sync_command))
|
||||
application.add_handler(CommandHandler(["commit", "gitcommit"], commit_command))
|
||||
application.add_handler(CommandHandler(["undo", "revert", "gitundo", "gitrevert", "laghv"], undo_command))
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user