CLI version 0.1.4

This commit is contained in:
Vladless
2025-04-21 03:34:44 +03:00
parent fa85fb70f1
commit cc628a08fa
12 changed files with 74 additions and 40 deletions
+74 -40
View File
@@ -2,17 +2,13 @@ import os
import re import re
import subprocess import subprocess
import sys import sys
import requests import requests
from rich.console import Console from rich.console import Console
from rich.prompt import Confirm, Prompt from rich.prompt import Confirm, Prompt
from rich.table import Table from rich.table import Table
from rich.text import Text from rich.text import Text
from config import BOT_SERVICE from config import BOT_SERVICE
try: try:
sys.stdin.reconfigure(encoding="utf-8") sys.stdin.reconfigure(encoding="utf-8")
sys.stdout.reconfigure(encoding="utf-8") sys.stdout.reconfigure(encoding="utf-8")
@@ -31,6 +27,9 @@ SERVICE_NAME = BOT_SERVICE
console = Console() console = Console()
def is_service_exists(service_name):
result = subprocess.run(["systemctl", "list-unit-files", service_name], capture_output=True, text=True)
return service_name in result.stdout
def print_logo(): def print_logo():
logo = Text( logo = Text(
@@ -46,7 +45,6 @@ def print_logo():
) )
console.print(logo) console.print(logo)
def backup_project(): def backup_project():
console.print("[yellow]📦 Создаётся резервная копия проекта...[/yellow]") console.print("[yellow]📦 Создаётся резервная копия проекта...[/yellow]")
with console.status("[bold cyan]Копирование файлов...[/bold cyan]"): with console.status("[bold cyan]Копирование файлов...[/bold cyan]"):
@@ -54,19 +52,46 @@ def backup_project():
subprocess.run(["cp", "-r", PROJECT_DIR, BACK_DIR]) subprocess.run(["cp", "-r", PROJECT_DIR, BACK_DIR])
console.print(f"[green]✅ Бэкап сохранён в: {BACK_DIR}[/green]") console.print(f"[green]✅ Бэкап сохранён в: {BACK_DIR}[/green]")
def install_rsync_if_needed(): def install_rsync_if_needed():
if subprocess.run(["which", "rsync"], capture_output=True).returncode != 0: if subprocess.run(["which", "rsync"], capture_output=True).returncode != 0:
console.print("[blue]📦 Установка rsync...[/blue]") console.print("[blue]📦 Установка rsync...[/blue]")
os.system("sudo apt update && sudo apt install -y rsync") os.system("sudo apt update && sudo apt install -y rsync")
def clean_project_dir_safe():
console.print("[yellow]🧹 Очистка проекта перед обновлением (кроме config и кнопок)...[/yellow]")
preserved_paths = {
os.path.join(PROJECT_DIR, "config.py"),
os.path.join(PROJECT_DIR, "handlers", "buttons.py"),
os.path.join(PROJECT_DIR, "handlers", "texts.py"),
}
for root, dirs, files in os.walk(PROJECT_DIR, topdown=False):
for file in files:
path = os.path.join(root, file)
if path in preserved_paths:
continue
try:
os.remove(path)
except PermissionError:
subprocess.run(["sudo", "rm", "-f", path])
except Exception as e:
console.print(f"[red]Не удалось удалить файл: {path}: {e}[/red]")
for dir in dirs:
dir_path = os.path.join(root, dir)
if os.path.abspath(dir_path) == os.path.join(PROJECT_DIR, "handlers"):
continue
try:
os.rmdir(dir_path)
except Exception:
subprocess.run(["sudo", "rm", "-rf", dir_path])
def install_git_if_needed(): def install_git_if_needed():
if subprocess.run(["which", "git"], capture_output=True).returncode != 0: if subprocess.run(["which", "git"], capture_output=True).returncode != 0:
console.print("[blue]Установка Git...[/blue]") console.print("[blue]Установка Git...[/blue]")
os.system("sudo apt update && sudo apt install -y git") os.system("sudo apt update && sudo apt install -y git")
def install_dependencies(): def install_dependencies():
console.print("[blue]🔧 Установка зависимостей...[/blue]") console.print("[blue]🔧 Установка зависимостей...[/blue]")
with console.status("[bold green]Устанавливаются зависимости...[/bold green]"): with console.status("[bold green]Устанавливаются зависимости...[/bold green]"):
@@ -81,12 +106,13 @@ def install_dependencies():
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
console.print("[red]❌ Ошибка при установке зависимостей.[/red]") console.print("[red]❌ Ошибка при установке зависимостей.[/red]")
def restart_service(): def restart_service():
console.print("[blue]🚀 Перезапуск службы...[/blue]") if is_service_exists(SERVICE_NAME):
with console.status("[bold yellow]Перезапуск...[/bold yellow]"): console.print("[blue]🚀 Перезапуск службы...[/blue]")
subprocess.run(f"sudo systemctl restart {SERVICE_NAME}", shell=True) with console.status("[bold yellow]Перезапуск...[/bold yellow]"):
subprocess.run(f"sudo systemctl restart {SERVICE_NAME}", shell=True)
else:
console.print(f"[red]❌ Служба {SERVICE_NAME} не найдена.[/red]")
def get_local_version(): def get_local_version():
path = os.path.join(PROJECT_DIR, "bot.py") path = os.path.join(PROJECT_DIR, "bot.py")
@@ -99,7 +125,6 @@ def get_local_version():
return match.group(1) return match.group(1)
return None return None
def get_remote_version(branch="main"): def get_remote_version(branch="main"):
try: try:
url = f"https://raw.githubusercontent.com/Vladless/Solo_bot/{branch}/bot.py" url = f"https://raw.githubusercontent.com/Vladless/Solo_bot/{branch}/bot.py"
@@ -113,7 +138,6 @@ def get_remote_version(branch="main"):
return None return None
return None return None
def update_from_beta(): def update_from_beta():
local_version = get_local_version() local_version = get_local_version()
remote_version = get_remote_version(branch="dev") remote_version = get_remote_version(branch="dev")
@@ -134,13 +158,14 @@ def update_from_beta():
install_rsync_if_needed() install_rsync_if_needed()
os.chdir(PROJECT_DIR) os.chdir(PROJECT_DIR)
console.print("[cyan]📅 Клонируем временный репозиторий...[/cyan]")
console.print("[cyan]📥 Клонируем репозиторий dev во временную папку...[/cyan]")
subprocess.run(["rm", "-rf", TEMP_DIR]) subprocess.run(["rm", "-rf", TEMP_DIR])
if os.system(f"git clone -b dev {GITHUB_REPO} {TEMP_DIR}") != 0: if os.system(f"git clone -b dev {GITHUB_REPO} {TEMP_DIR}") != 0:
console.print("[red]❌ Ошибка при клонировании. Обновление отменено.[/red]") console.print("[red]❌ Ошибка при клонировании. Обновление отменено.[/red]")
return return
subprocess.run(["sudo", "rm", "-rf", os.path.join(PROJECT_DIR, "venv")])
clean_project_dir_safe()
subprocess.run(f"rsync -a --exclude=img --exclude=handlers/buttons.py {TEMP_DIR}/ {PROJECT_DIR}/", shell=True) subprocess.run(f"rsync -a --exclude=img --exclude=handlers/buttons.py {TEMP_DIR}/ {PROJECT_DIR}/", shell=True)
subprocess.run(["rm", "-rf", TEMP_DIR]) subprocess.run(["rm", "-rf", TEMP_DIR])
@@ -148,7 +173,6 @@ def update_from_beta():
restart_service() restart_service()
console.print("[green]✅ Обновление с ветки dev завершено.[/green]") console.print("[green]✅ Обновление с ветки dev завершено.[/green]")
def update_from_release(): def update_from_release():
if not Confirm.ask("[yellow]🔁 Подтвердите обновление Solobot до одного из последних релизов[/yellow]"): if not Confirm.ask("[yellow]🔁 Подтвердите обновление Solobot до одного из последних релизов[/yellow]"):
return return
@@ -170,14 +194,21 @@ def update_from_release():
console.print(f"[cyan]{idx}.[/cyan] {tag}") console.print(f"[cyan]{idx}.[/cyan] {tag}")
selected = Prompt.ask( selected = Prompt.ask(
"[bold blue]Выберите номер релиза[/bold blue]", choices=[str(i) for i in range(1, len(tag_choices) + 1)] "[bold blue]Выберите номер релиза[/bold blue]",
choices=[str(i) for i in range(1, len(tag_choices) + 1)]
) )
tag_name = tag_choices[int(selected) - 1] tag_name = tag_choices[int(selected) - 1]
if not Confirm.ask(f"[yellow]🔁 Подтвердите установку релиза {tag_name}[/yellow]"):
return
console.print(f"[cyan]📥 Клонируем релиз {tag_name} во временную папку...[/cyan]") console.print(f"[cyan]📥 Клонируем релиз {tag_name} во временную папку...[/cyan]")
subprocess.run(["rm", "-rf", TEMP_DIR]) subprocess.run(["rm", "-rf", TEMP_DIR])
subprocess.run(f"git clone --depth 1 --branch {tag_name} {GITHUB_REPO} {TEMP_DIR}", shell=True, check=True) subprocess.run(f"git clone --depth 1 --branch {tag_name} {GITHUB_REPO} {TEMP_DIR}", shell=True, check=True)
subprocess.run(["sudo", "rm", "-rf", os.path.join(PROJECT_DIR, "venv")])
clean_project_dir_safe()
subprocess.run(f"rsync -a --exclude=img --exclude=handlers/buttons.py {TEMP_DIR}/ {PROJECT_DIR}/", shell=True) subprocess.run(f"rsync -a --exclude=img --exclude=handlers/buttons.py {TEMP_DIR}/ {PROJECT_DIR}/", shell=True)
subprocess.run(["rm", "-rf", TEMP_DIR]) subprocess.run(["rm", "-rf", TEMP_DIR])
@@ -205,13 +236,10 @@ def show_update_menu():
elif choice == "2": elif choice == "2":
update_from_release() update_from_release()
def show_menu(): def show_menu():
table = Table(title="Solobot CLI", title_style="bold magenta", header_style="bold blue") table = Table(title=f"Solobot CLI v0.1.4", title_style="bold magenta", header_style="bold blue")
table.add_column("", justify="center", style="cyan", no_wrap=True) table.add_column("", justify="center", style="cyan", no_wrap=True)
table.add_column("Операция", style="white") table.add_column("Операция", style="white")
table.add_row("1", "Запустить бота (systemd)") table.add_row("1", "Запустить бота (systemd)")
table.add_row("2", "Запустить напрямую: venv/bin/python main.py") table.add_row("2", "Запустить напрямую: venv/bin/python main.py")
table.add_row("3", "Перезапустить бота (systemd)") table.add_row("3", "Перезапустить бота (systemd)")
@@ -220,46 +248,52 @@ def show_menu():
table.add_row("6", "Показать статус") table.add_row("6", "Показать статус")
table.add_row("7", "Обновить Solobot") table.add_row("7", "Обновить Solobot")
table.add_row("8", "Выход") table.add_row("8", "Выход")
console.print(table) console.print(table)
def main(): def main():
if os.geteuid() != 0:
console.print("[bold red]⛔ Требуется запуск от имени root или через sudo.[/bold red]")
sys.exit(1)
os.chdir(PROJECT_DIR) os.chdir(PROJECT_DIR)
print_logo() print_logo()
try: try:
while True: while True:
show_menu() show_menu()
choice = Prompt.ask("[bold blue]Введите номер действия[/bold blue]", choices=[str(i) for i in range(1, 9)]) choice = Prompt.ask("[bold blue]Введите номер действия[/bold blue]", choices=[str(i) for i in range(1, 9)])
if choice == "1": if choice == "1":
os.system(f"sudo systemctl start {SERVICE_NAME}") if is_service_exists(SERVICE_NAME):
subprocess.run(["sudo", "systemctl", "start", SERVICE_NAME])
else:
console.print(f"[red]❌ Служба {SERVICE_NAME} не найдена.[/red]")
elif choice == "2": elif choice == "2":
if Confirm.ask("[green]Вы действительно хотите запустить main.py вручную?[/green]"): if Confirm.ask("[green]Вы действительно хотите запустить main.py вручную?[/green]"):
os.system("sudo venv/bin/python main.py") subprocess.run(["venv/bin/python", "main.py"])
elif choice == "3": elif choice == "3":
if Confirm.ask("[yellow]Вы действительно хотите перезапустить бота?[/yellow]"): if is_service_exists(SERVICE_NAME):
os.system(f"sudo systemctl restart {SERVICE_NAME}") if Confirm.ask("[yellow]Вы действительно хотите перезапустить бота?[/yellow]"):
subprocess.run(["sudo", "systemctl", "restart", SERVICE_NAME])
else:
console.print(f"[red]❌ Служба {SERVICE_NAME} не найдена.[/red]")
elif choice == "4": elif choice == "4":
if Confirm.ask("[red]Вы уверены, что хотите остановить бота?[/red]"): if is_service_exists(SERVICE_NAME):
os.system(f"sudo systemctl stop {SERVICE_NAME}") if Confirm.ask("[red]Вы уверены, что хотите остановить бота?[/red]"):
subprocess.run(["sudo", "systemctl", "stop", SERVICE_NAME])
else:
console.print(f"[red]❌ Служба {SERVICE_NAME} не найдена.[/red]")
elif choice == "5": elif choice == "5":
os.system(f"sudo journalctl -u {SERVICE_NAME} -n 80 --no-pager") if is_service_exists(SERVICE_NAME):
subprocess.run(["sudo", "journalctl", "-u", SERVICE_NAME, "-n", "80", "--no-pager"])
else:
console.print(f"[red]❌ Служба {SERVICE_NAME} не найдена.[/red]")
elif choice == "6": elif choice == "6":
os.system(f"sudo systemctl status {SERVICE_NAME}") if is_service_exists(SERVICE_NAME):
subprocess.run(["sudo", "systemctl", "status", SERVICE_NAME])
else:
console.print(f"[red]❌ Служба {SERVICE_NAME} не найдена.[/red]")
elif choice == "7": elif choice == "7":
show_update_menu() show_update_menu()
elif choice == "8": elif choice == "8":
console.print("[bold cyan] Выход из CLI. Удачного дня![/bold cyan]") console.print("[bold cyan]Выход из CLI. Удачного дня![/bold cyan]")
break break
except KeyboardInterrupt: except KeyboardInterrupt:
console.print("\n[bold red]⏹ Прерывание. Выход из CLI.[/bold red]") console.print("\n[bold red]⏹ Прерывание. Выход из CLI.[/bold red]")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB