Split System menu into submenus
This commit is contained in:
@@ -17,7 +17,7 @@ async def help_cmd(msg: Message):
|
|||||||
"🐳 Docker — управление контейнерами\n"
|
"🐳 Docker — управление контейнерами\n"
|
||||||
"📦 Backup — restic бэкапы\n"
|
"📦 Backup — restic бэкапы\n"
|
||||||
"🧉 Artifacts — критичные образы (Clonezilla, NAND)\n"
|
"🧉 Artifacts — критичные образы (Clonezilla, NAND)\n"
|
||||||
"⚙️ System — диски, безопасность, URL, metrics, reboot\n\n"
|
"⚙️ System — подменю: Info / Ops / Logs\n\n"
|
||||||
"Inline-кнопки используются для выбора контейнеров.",
|
"Inline-кнопки используются для выбора контейнеров.",
|
||||||
reply_markup=menu_kb,
|
reply_markup=menu_kb,
|
||||||
parse_mode="Markdown",
|
parse_mode="Markdown",
|
||||||
|
|||||||
@@ -2,7 +2,16 @@ from aiogram import F
|
|||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from app import dp
|
from app import dp
|
||||||
from auth import is_admin_msg
|
from auth import is_admin_msg
|
||||||
from keyboards import menu_kb, docker_kb, backup_kb, artifacts_kb, system_kb
|
from keyboards import (
|
||||||
|
menu_kb,
|
||||||
|
docker_kb,
|
||||||
|
backup_kb,
|
||||||
|
artifacts_kb,
|
||||||
|
system_menu_kb,
|
||||||
|
system_info_kb,
|
||||||
|
system_ops_kb,
|
||||||
|
system_logs_kb,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "/start")
|
@dp.message(F.text == "/start")
|
||||||
@@ -38,4 +47,28 @@ async def am(msg: Message):
|
|||||||
@dp.message(F.text == "⚙️ System")
|
@dp.message(F.text == "⚙️ System")
|
||||||
async def sm(msg: Message):
|
async def sm(msg: Message):
|
||||||
if is_admin_msg(msg):
|
if is_admin_msg(msg):
|
||||||
await msg.answer("⚙️ System", reply_markup=system_kb)
|
await msg.answer("⚙️ System", reply_markup=system_menu_kb)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text == "⬅️ System")
|
||||||
|
async def back_system(msg: Message):
|
||||||
|
if is_admin_msg(msg):
|
||||||
|
await msg.answer("⚙️ System", reply_markup=system_menu_kb)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text == "ℹ️ Info")
|
||||||
|
async def sys_info(msg: Message):
|
||||||
|
if is_admin_msg(msg):
|
||||||
|
await msg.answer("ℹ️ System info", reply_markup=system_info_kb)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text == "🛠 Ops")
|
||||||
|
async def sys_ops(msg: Message):
|
||||||
|
if is_admin_msg(msg):
|
||||||
|
await msg.answer("🛠 System ops", reply_markup=system_ops_kb)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text == "📄 Logs")
|
||||||
|
async def sys_logs(msg: Message):
|
||||||
|
if is_admin_msg(msg):
|
||||||
|
await msg.answer("📄 System logs", reply_markup=system_logs_kb)
|
||||||
|
|||||||
141
handlers/processes.py
Normal file
141
handlers/processes.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import asyncio
|
||||||
|
from aiogram import F
|
||||||
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
from app import dp, ADMIN_ID
|
||||||
|
from auth import is_admin_msg
|
||||||
|
from keyboards import system_logs_kb
|
||||||
|
from services.processes import get_top_processes, search_processes, terminate_process
|
||||||
|
from state import PROC_SEARCH_PENDING, PROC_KILL_PENDING
|
||||||
|
|
||||||
|
|
||||||
|
def _proc_kb() -> InlineKeyboardMarkup:
|
||||||
|
return InlineKeyboardMarkup(
|
||||||
|
inline_keyboard=[[
|
||||||
|
InlineKeyboardButton(text="🔄 Refresh", callback_data="proc:refresh"),
|
||||||
|
InlineKeyboardButton(text="🔍 Search", callback_data="proc:search"),
|
||||||
|
InlineKeyboardButton(text="🛑 Kill", callback_data="proc:kill"),
|
||||||
|
]]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_top(title: str, rows: list[dict]) -> str:
|
||||||
|
if not rows:
|
||||||
|
return f"{title}\n(no data)"
|
||||||
|
lines = ["PID CPU% MEM% NAME"]
|
||||||
|
for row in rows:
|
||||||
|
lines.append(
|
||||||
|
f"{row['pid']:<5} {row['cpu']:<5.1f} {row['mem']:<5.1f} {row['name']}"
|
||||||
|
)
|
||||||
|
return f"{title}\n" + "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
async def send_processes(msg: Message, edit: bool = False):
|
||||||
|
top_cpu, top_mem = await asyncio.to_thread(get_top_processes)
|
||||||
|
body = (
|
||||||
|
"🧰 **Processes**\n\n"
|
||||||
|
"```\n"
|
||||||
|
f"{_format_top('Top CPU', top_cpu)}\n\n"
|
||||||
|
f"{_format_top('Top RAM', top_mem)}\n"
|
||||||
|
"```"
|
||||||
|
)
|
||||||
|
if edit:
|
||||||
|
await msg.edit_text(body, reply_markup=_proc_kb(), parse_mode="Markdown")
|
||||||
|
else:
|
||||||
|
await msg.answer(body, reply_markup=_proc_kb(), parse_mode="Markdown")
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text == "🧰 Processes")
|
||||||
|
async def proc_menu(msg: Message):
|
||||||
|
if is_admin_msg(msg):
|
||||||
|
await send_processes(msg, edit=False)
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("proc:"))
|
||||||
|
async def proc_actions(cb: CallbackQuery):
|
||||||
|
if cb.from_user.id != ADMIN_ID:
|
||||||
|
return
|
||||||
|
await cb.answer()
|
||||||
|
action = cb.data.split(":", 1)[1]
|
||||||
|
if action == "refresh":
|
||||||
|
await send_processes(cb.message, edit=True)
|
||||||
|
return
|
||||||
|
if action == "search":
|
||||||
|
PROC_SEARCH_PENDING[cb.from_user.id] = {}
|
||||||
|
await cb.message.answer("🔍 Send search text", reply_markup=system_logs_kb)
|
||||||
|
return
|
||||||
|
if action == "kill":
|
||||||
|
PROC_KILL_PENDING[cb.from_user.id] = {}
|
||||||
|
await cb.message.answer("🛑 Send PID to terminate", reply_markup=system_logs_kb)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text, F.func(lambda msg: msg.from_user and msg.from_user.id in PROC_SEARCH_PENDING))
|
||||||
|
async def proc_search(msg: Message):
|
||||||
|
if not is_admin_msg(msg):
|
||||||
|
return
|
||||||
|
PROC_SEARCH_PENDING.pop(msg.from_user.id, None)
|
||||||
|
query = (msg.text or "").strip()
|
||||||
|
if not query:
|
||||||
|
await msg.answer("⚠️ Empty search", reply_markup=system_logs_kb)
|
||||||
|
return
|
||||||
|
|
||||||
|
rows = await asyncio.to_thread(search_processes, query)
|
||||||
|
if not rows:
|
||||||
|
await msg.answer("🔍 No matches", reply_markup=system_logs_kb)
|
||||||
|
return
|
||||||
|
|
||||||
|
lines = ["PID NAME CMD"]
|
||||||
|
for row in rows:
|
||||||
|
cmd = row["cmdline"] or "-"
|
||||||
|
if len(cmd) > 80:
|
||||||
|
cmd = cmd[:80] + "…"
|
||||||
|
lines.append(f"{row['pid']:<5} {row['name']:<6} {cmd}")
|
||||||
|
|
||||||
|
text = "🔍 **Search results**\n```\n" + "\n".join(lines) + "\n```"
|
||||||
|
await msg.answer(text, reply_markup=system_logs_kb, parse_mode="Markdown")
|
||||||
|
|
||||||
|
|
||||||
|
@dp.message(F.text, F.func(lambda msg: msg.from_user and msg.from_user.id in PROC_KILL_PENDING))
|
||||||
|
async def proc_kill_pid(msg: Message):
|
||||||
|
if not is_admin_msg(msg):
|
||||||
|
return
|
||||||
|
PROC_KILL_PENDING.pop(msg.from_user.id, None)
|
||||||
|
raw = (msg.text or "").strip()
|
||||||
|
try:
|
||||||
|
pid = int(raw)
|
||||||
|
except ValueError:
|
||||||
|
await msg.answer("⚠️ Invalid PID", reply_markup=system_logs_kb)
|
||||||
|
return
|
||||||
|
|
||||||
|
kb = InlineKeyboardMarkup(
|
||||||
|
inline_keyboard=[[
|
||||||
|
InlineKeyboardButton(text="✅ Confirm", callback_data=f"prockill:{pid}:confirm"),
|
||||||
|
InlineKeyboardButton(text="✖ Cancel", callback_data="prockill:cancel"),
|
||||||
|
]]
|
||||||
|
)
|
||||||
|
await msg.answer(f"⚠️ Terminate PID `{pid}`?", reply_markup=kb, parse_mode="Markdown")
|
||||||
|
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("prockill:"))
|
||||||
|
async def proc_kill_confirm(cb: CallbackQuery):
|
||||||
|
if cb.from_user.id != ADMIN_ID:
|
||||||
|
return
|
||||||
|
parts = cb.data.split(":")
|
||||||
|
if len(parts) < 2:
|
||||||
|
await cb.answer("Bad request")
|
||||||
|
return
|
||||||
|
if parts[1] == "cancel":
|
||||||
|
await cb.answer("Cancelled")
|
||||||
|
await cb.message.delete()
|
||||||
|
return
|
||||||
|
if len(parts) != 3 or parts[2] != "confirm":
|
||||||
|
await cb.answer("Bad request")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
pid = int(parts[1])
|
||||||
|
except ValueError:
|
||||||
|
await cb.answer("Bad PID")
|
||||||
|
return
|
||||||
|
await cb.answer()
|
||||||
|
result = await asyncio.to_thread(terminate_process, pid)
|
||||||
|
await cb.message.answer(result, reply_markup=system_logs_kb)
|
||||||
@@ -4,7 +4,7 @@ from aiogram import F
|
|||||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
from app import dp, cfg
|
from app import dp, cfg
|
||||||
from auth import is_admin_msg
|
from auth import is_admin_msg
|
||||||
from keyboards import system_kb
|
from keyboards import system_info_kb, system_ops_kb, system_logs_kb
|
||||||
from system_checks import security, disks, hardware
|
from system_checks import security, disks, hardware
|
||||||
from services.http_checks import get_url_checks, check_url
|
from services.http_checks import get_url_checks, check_url
|
||||||
from services.queue import enqueue
|
from services.queue import enqueue
|
||||||
@@ -21,13 +21,13 @@ from services.incidents import read_recent, incidents_path
|
|||||||
@dp.message(F.text == "💽 Disks")
|
@dp.message(F.text == "💽 Disks")
|
||||||
async def sd(msg: Message):
|
async def sd(msg: Message):
|
||||||
if is_admin_msg(msg):
|
if is_admin_msg(msg):
|
||||||
await msg.answer(disks(), reply_markup=system_kb)
|
await msg.answer(disks(), reply_markup=system_info_kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "🔐 Security")
|
@dp.message(F.text == "🔐 Security")
|
||||||
async def sec(msg: Message):
|
async def sec(msg: Message):
|
||||||
if is_admin_msg(msg):
|
if is_admin_msg(msg):
|
||||||
await msg.answer(security(), reply_markup=system_kb)
|
await msg.answer(security(), reply_markup=system_info_kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "🌐 URLs")
|
@dp.message(F.text == "🌐 URLs")
|
||||||
@@ -37,10 +37,10 @@ async def urls(msg: Message):
|
|||||||
|
|
||||||
checks = list(get_url_checks(cfg))
|
checks = list(get_url_checks(cfg))
|
||||||
if not checks:
|
if not checks:
|
||||||
await msg.answer("⚠️ Нет URL для проверки", reply_markup=system_kb)
|
await msg.answer("⚠️ Нет URL для проверки", reply_markup=system_logs_kb)
|
||||||
return
|
return
|
||||||
|
|
||||||
await msg.answer("⏳ Проверяю URL…", reply_markup=system_kb)
|
await msg.answer("⏳ Проверяю URL…", reply_markup=system_logs_kb)
|
||||||
|
|
||||||
async def worker():
|
async def worker():
|
||||||
tasks = [asyncio.to_thread(check_url, url) for _, url in checks]
|
tasks = [asyncio.to_thread(check_url, url) for _, url in checks]
|
||||||
@@ -56,7 +56,7 @@ async def urls(msg: Message):
|
|||||||
reason = err or "error"
|
reason = err or "error"
|
||||||
lines.append(f"🔴 {alias}: {reason} ({ms}ms)")
|
lines.append(f"🔴 {alias}: {reason} ({ms}ms)")
|
||||||
|
|
||||||
await msg.answer("\n".join(lines), reply_markup=system_kb)
|
await msg.answer("\n".join(lines), reply_markup=system_logs_kb)
|
||||||
|
|
||||||
asyncio.create_task(worker())
|
asyncio.create_task(worker())
|
||||||
|
|
||||||
@@ -66,9 +66,9 @@ async def metrics(msg: Message):
|
|||||||
if not is_admin_msg(msg):
|
if not is_admin_msg(msg):
|
||||||
return
|
return
|
||||||
if state.METRICS_STORE is None:
|
if state.METRICS_STORE is None:
|
||||||
await msg.answer("⚠️ Metrics not initialized", reply_markup=system_kb)
|
await msg.answer("⚠️ Metrics not initialized", reply_markup=system_info_kb)
|
||||||
return
|
return
|
||||||
await msg.answer(summarize(state.METRICS_STORE, minutes=15), reply_markup=system_kb)
|
await msg.answer(summarize(state.METRICS_STORE, minutes=15), reply_markup=system_info_kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "🧾 Audit")
|
@dp.message(F.text == "🧾 Audit")
|
||||||
@@ -77,9 +77,9 @@ async def audit_log(msg: Message):
|
|||||||
return
|
return
|
||||||
text = read_audit_tail(cfg, limit=200)
|
text = read_audit_tail(cfg, limit=200)
|
||||||
if text.startswith("⚠️") or text.startswith("ℹ️"):
|
if text.startswith("⚠️") or text.startswith("ℹ️"):
|
||||||
await msg.answer(text, reply_markup=system_kb)
|
await msg.answer(text, reply_markup=system_logs_kb)
|
||||||
else:
|
else:
|
||||||
await msg.answer(text, reply_markup=system_kb, parse_mode="Markdown")
|
await msg.answer(text, reply_markup=system_logs_kb, parse_mode="Markdown")
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "📣 Incidents")
|
@dp.message(F.text == "📣 Incidents")
|
||||||
@@ -88,7 +88,7 @@ async def incidents(msg: Message):
|
|||||||
return
|
return
|
||||||
path = incidents_path(cfg)
|
path = incidents_path(cfg)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
await msg.answer("⚠️ Incidents log not found", reply_markup=system_kb)
|
await msg.answer("⚠️ Incidents log not found", reply_markup=system_logs_kb)
|
||||||
return
|
return
|
||||||
last_24h = read_recent(cfg, hours=24, limit=500)
|
last_24h = read_recent(cfg, hours=24, limit=500)
|
||||||
last_7d = read_recent(cfg, hours=24 * 7, limit=1000)
|
last_7d = read_recent(cfg, hours=24 * 7, limit=1000)
|
||||||
@@ -102,7 +102,7 @@ async def incidents(msg: Message):
|
|||||||
"Recent (24h):\n"
|
"Recent (24h):\n"
|
||||||
f"```\n{body}\n```"
|
f"```\n{body}\n```"
|
||||||
)
|
)
|
||||||
await msg.answer(text, reply_markup=system_kb, parse_mode="Markdown")
|
await msg.answer(text, reply_markup=system_logs_kb, parse_mode="Markdown")
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "🔒 SSL")
|
@dp.message(F.text == "🔒 SSL")
|
||||||
@@ -110,7 +110,7 @@ async def ssl_certs(msg: Message):
|
|||||||
if not is_admin_msg(msg):
|
if not is_admin_msg(msg):
|
||||||
return
|
return
|
||||||
|
|
||||||
await msg.answer("⏳ Checking SSL certificates…", reply_markup=system_kb)
|
await msg.answer("⏳ Checking SSL certificates…", reply_markup=system_logs_kb)
|
||||||
|
|
||||||
async def worker():
|
async def worker():
|
||||||
try:
|
try:
|
||||||
@@ -118,7 +118,7 @@ async def ssl_certs(msg: Message):
|
|||||||
text = format_certificates(certs)
|
text = format_certificates(certs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
text = f"⚠️ NPMplus error: {e}"
|
text = f"⚠️ NPMplus error: {e}"
|
||||||
await msg.answer(text, reply_markup=system_kb)
|
await msg.answer(text, reply_markup=system_logs_kb)
|
||||||
|
|
||||||
asyncio.create_task(worker())
|
asyncio.create_task(worker())
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ async def updates_list(msg: Message):
|
|||||||
await send_updates_page(msg, msg.from_user.id, 0, edit=False)
|
await send_updates_page(msg, msg.from_user.id, 0, edit=False)
|
||||||
|
|
||||||
pos = await enqueue("pkg-updates", job)
|
pos = await enqueue("pkg-updates", job)
|
||||||
await msg.answer(f"🕓 Updates queued (#{pos})", reply_markup=system_kb)
|
await msg.answer(f"🕓 Updates queued (#{pos})", reply_markup=system_ops_kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.message(F.text == "⬆️ Upgrade")
|
@dp.message(F.text == "⬆️ Upgrade")
|
||||||
@@ -172,7 +172,7 @@ async def reboot_request(msg: Message):
|
|||||||
@dp.message(F.text == "🧱 Hardware")
|
@dp.message(F.text == "🧱 Hardware")
|
||||||
async def hw(msg: Message):
|
async def hw(msg: Message):
|
||||||
if is_admin_msg(msg):
|
if is_admin_msg(msg):
|
||||||
await msg.answer(hardware(), reply_markup=system_kb)
|
await msg.answer(hardware(), reply_markup=system_info_kb)
|
||||||
|
|
||||||
|
|
||||||
def _updates_kb(page: int, total_pages: int) -> InlineKeyboardMarkup:
|
def _updates_kb(page: int, total_pages: int) -> InlineKeyboardMarkup:
|
||||||
@@ -191,7 +191,7 @@ def _updates_kb(page: int, total_pages: int) -> InlineKeyboardMarkup:
|
|||||||
async def send_updates_page(msg: Message, user_id: int, page: int, edit: bool):
|
async def send_updates_page(msg: Message, user_id: int, page: int, edit: bool):
|
||||||
data = UPDATES_CACHE.get(user_id)
|
data = UPDATES_CACHE.get(user_id)
|
||||||
if not data:
|
if not data:
|
||||||
await msg.answer("⚠️ Updates cache empty", reply_markup=system_kb)
|
await msg.answer("⚠️ Updates cache empty", reply_markup=system_ops_kb)
|
||||||
return
|
return
|
||||||
|
|
||||||
lines = data["lines"]
|
lines = data["lines"]
|
||||||
@@ -231,10 +231,10 @@ async def upgrade_confirm(cb: CallbackQuery):
|
|||||||
|
|
||||||
async def job():
|
async def job():
|
||||||
text = await apply_updates()
|
text = await apply_updates()
|
||||||
await cb.message.answer(text, reply_markup=system_kb, parse_mode="Markdown")
|
await cb.message.answer(text, reply_markup=system_ops_kb, parse_mode="Markdown")
|
||||||
|
|
||||||
pos = await enqueue("pkg-upgrade", job)
|
pos = await enqueue("pkg-upgrade", job)
|
||||||
await cb.message.answer(f"🕓 Upgrade queued (#{pos})", reply_markup=system_kb)
|
await cb.message.answer(f"🕓 Upgrade queued (#{pos})", reply_markup=system_ops_kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query(F.data == "upgrade:cancel")
|
@dp.callback_query(F.data == "upgrade:cancel")
|
||||||
@@ -249,7 +249,7 @@ async def reboot_confirm(cb: CallbackQuery):
|
|||||||
return
|
return
|
||||||
await cb.answer()
|
await cb.answer()
|
||||||
REBOOT_PENDING[cb.from_user.id] = {}
|
REBOOT_PENDING[cb.from_user.id] = {}
|
||||||
await cb.message.answer("🔐 Send reboot password", reply_markup=system_kb)
|
await cb.message.answer("🔐 Send reboot password", reply_markup=system_ops_kb)
|
||||||
|
|
||||||
|
|
||||||
@dp.callback_query(F.data == "reboot:cancel")
|
@dp.callback_query(F.data == "reboot:cancel")
|
||||||
@@ -266,16 +266,16 @@ async def reboot_password(msg: Message):
|
|||||||
|
|
||||||
expected = cfg.get("security", {}).get("reboot_password")
|
expected = cfg.get("security", {}).get("reboot_password")
|
||||||
if not expected:
|
if not expected:
|
||||||
await msg.answer("⚠️ Reboot password not configured", reply_markup=system_kb)
|
await msg.answer("⚠️ Reboot password not configured", reply_markup=system_ops_kb)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (msg.text or "").strip() != expected:
|
if (msg.text or "").strip() != expected:
|
||||||
await msg.answer("❌ Wrong password", reply_markup=system_kb)
|
await msg.answer("❌ Wrong password", reply_markup=system_ops_kb)
|
||||||
return
|
return
|
||||||
|
|
||||||
async def job():
|
async def job():
|
||||||
await msg.answer("🔄 Rebooting…", reply_markup=system_kb)
|
await msg.answer("🔄 Rebooting…", reply_markup=system_ops_kb)
|
||||||
await run_cmd(["sudo", "reboot"], timeout=10)
|
await run_cmd(["sudo", "reboot"], timeout=10)
|
||||||
|
|
||||||
pos = await enqueue("reboot", job)
|
pos = await enqueue("reboot", job)
|
||||||
await msg.answer(f"🕓 Reboot queued (#{pos})", reply_markup=system_kb)
|
await msg.answer(f"🕓 Reboot queued (#{pos})", reply_markup=system_ops_kb)
|
||||||
|
|||||||
36
keyboards.py
36
keyboards.py
@@ -52,12 +52,38 @@ artifacts_kb = ReplyKeyboardMarkup(
|
|||||||
resize_keyboard=True,
|
resize_keyboard=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
system_kb = ReplyKeyboardMarkup(
|
system_menu_kb = ReplyKeyboardMarkup(
|
||||||
keyboard=[
|
keyboard=[
|
||||||
[KeyboardButton(text="💽 Disks"), KeyboardButton(text="🔐 Security"), KeyboardButton(text="🧾 Audit")],
|
[KeyboardButton(text="ℹ️ Info"), KeyboardButton(text="🛠 Ops")],
|
||||||
[KeyboardButton(text="🌐 URLs"), KeyboardButton(text="📈 Metrics"), KeyboardButton(text="🔒 SSL")],
|
[KeyboardButton(text="📄 Logs"), KeyboardButton(text="⬅️ Назад")],
|
||||||
[KeyboardButton(text="📦 Updates"), KeyboardButton(text="⬆️ Upgrade"), KeyboardButton(text="📣 Incidents")],
|
],
|
||||||
[KeyboardButton(text="🧱 Hardware"), KeyboardButton(text="🔄 Reboot"), KeyboardButton(text="⬅️ Назад")],
|
resize_keyboard=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
system_info_kb = ReplyKeyboardMarkup(
|
||||||
|
keyboard=[
|
||||||
|
[KeyboardButton(text="💽 Disks"), KeyboardButton(text="🔐 Security")],
|
||||||
|
[KeyboardButton(text="📈 Metrics"), KeyboardButton(text="🧱 Hardware")],
|
||||||
|
[KeyboardButton(text="⬅️ System")],
|
||||||
|
],
|
||||||
|
resize_keyboard=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
system_ops_kb = ReplyKeyboardMarkup(
|
||||||
|
keyboard=[
|
||||||
|
[KeyboardButton(text="📦 Updates"), KeyboardButton(text="⬆️ Upgrade")],
|
||||||
|
[KeyboardButton(text="🔄 Reboot")],
|
||||||
|
[KeyboardButton(text="⬅️ System")],
|
||||||
|
],
|
||||||
|
resize_keyboard=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
system_logs_kb = ReplyKeyboardMarkup(
|
||||||
|
keyboard=[
|
||||||
|
[KeyboardButton(text="🧾 Audit"), KeyboardButton(text="📣 Incidents")],
|
||||||
|
[KeyboardButton(text="🧰 Processes"), KeyboardButton(text="🔒 SSL")],
|
||||||
|
[KeyboardButton(text="🌐 URLs")],
|
||||||
|
[KeyboardButton(text="⬅️ System")],
|
||||||
],
|
],
|
||||||
resize_keyboard=True,
|
resize_keyboard=True,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user