diff --git a/cli_launcher.py b/cli_launcher.py
index 2e0026e6..34fef7ae 100755
--- a/cli_launcher.py
+++ b/cli_launcher.py
@@ -96,17 +96,17 @@ def install_rsync_if_needed():
os.system("sudo apt update && sudo apt install -y rsync")
-def clean_project_dir_safe(update_buttons=False):
+def clean_project_dir_safe(update_buttons=False, update_img=False):
console.print("[yellow]🧹 Очистка проекта перед обновлением...[/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"),
- os.path.join(PROJECT_DIR, "img"),
}
if not update_buttons:
preserved_paths.add(os.path.join(PROJECT_DIR, "handlers", "buttons.py"))
+ if not update_img:
+ preserved_paths.add(os.path.join(PROJECT_DIR, "img"))
for root, dirs, files in os.walk(PROJECT_DIR, topdown=False):
for file in files:
@@ -124,6 +124,8 @@ def clean_project_dir_safe(update_buttons=False):
dir_path = os.path.join(root, dir)
if os.path.abspath(dir_path) == os.path.join(PROJECT_DIR, "handlers"):
continue
+ if not update_img and os.path.abspath(dir_path) == os.path.join(PROJECT_DIR, "img"):
+ continue
try:
os.rmdir(dir_path)
except Exception:
@@ -207,18 +209,15 @@ def update_from_beta():
else:
console.print("[red]⚠️ Не удалось определить версии.[/red]")
- if not Confirm.ask(
- "[yellow]🔁 Подтвердите обновление Solobot с ветки DEV[/yellow]"
- ):
+ if not Confirm.ask("[yellow]🔁 Подтвердите обновление Solobot с ветки DEV[/yellow]"):
return
console.print("[red]⚠️ ВНИМАНИЕ! Папка бота будет перезаписана![/red]")
if not Confirm.ask("[red]❓ Продолжить обновление?[/red]"):
return
- update_buttons = Confirm.ask(
- "[yellow]🔄 Обновлять файл buttons.py?[/yellow]", default=False
- )
+ update_buttons = Confirm.ask("[yellow]🔄 Обновлять файл buttons.py?[/yellow]", default=False)
+ update_img = Confirm.ask("[yellow]🖼 Обновлять папку img?[/yellow]", default=False)
backup_project()
install_git_if_needed()
@@ -232,11 +231,13 @@ def update_from_beta():
return
subprocess.run(["sudo", "rm", "-rf", os.path.join(PROJECT_DIR, "venv")])
- clean_project_dir_safe(update_buttons=update_buttons)
+ clean_project_dir_safe(update_buttons=update_buttons, update_img=update_img)
- exclude_options = "--exclude=img"
+ exclude_options = ""
+ if not update_img:
+ exclude_options += "--exclude=img "
if not update_buttons:
- exclude_options += " --exclude=handlers/buttons.py"
+ exclude_options += "--exclude=handlers/buttons.py "
subprocess.run(f"rsync -a {exclude_options} {TEMP_DIR}/ {PROJECT_DIR}/", shell=True)
subprocess.run(["rm", "-rf", TEMP_DIR])
@@ -259,9 +260,8 @@ def update_from_release():
if not Confirm.ask("[red]❓ Вы точно хотите продолжить?[/red]"):
return
- update_buttons = Confirm.ask(
- "[yellow]🔄 Обновлять файл buttons.py?[/yellow]", default=False
- )
+ update_buttons = Confirm.ask("[yellow]🔄 Обновлять файл buttons.py?[/yellow]", default=False)
+ update_img = Confirm.ask("[yellow]🖼 Обновлять папку img?[/yellow]", default=False)
backup_project()
install_git_if_needed()
@@ -292,9 +292,7 @@ def update_from_release():
):
return
- console.print(
- f"[cyan]📥 Клонируем релиз {tag_name} во временную папку...[/cyan]"
- )
+ console.print(f"[cyan]📥 Клонируем релиз {tag_name} во временную папку...[/cyan]")
subprocess.run(["rm", "-rf", TEMP_DIR])
subprocess.run(
f"git clone --depth 1 --branch {tag_name} {GITHUB_REPO} {TEMP_DIR}",
@@ -304,15 +302,15 @@ def update_from_release():
console.print("[red]⚠️ Начинается перезапись файлов бота![/red]")
subprocess.run(["sudo", "rm", "-rf", os.path.join(PROJECT_DIR, "venv")])
- clean_project_dir_safe(update_buttons=update_buttons)
+ clean_project_dir_safe(update_buttons=update_buttons, update_img=update_img)
- exclude_options = "--exclude=img"
+ exclude_options = ""
+ if not update_img:
+ exclude_options += "--exclude=img "
if not update_buttons:
- exclude_options += " --exclude=handlers/buttons.py"
+ exclude_options += "--exclude=handlers/buttons.py "
- subprocess.run(
- f"rsync -a {exclude_options} {TEMP_DIR}/ {PROJECT_DIR}/", shell=True
- )
+ subprocess.run(f"rsync -a {exclude_options} {TEMP_DIR}/ {PROJECT_DIR}/", shell=True)
subprocess.run(["rm", "-rf", TEMP_DIR])
fix_permissions()
@@ -350,7 +348,7 @@ def show_update_menu():
def show_menu():
table = Table(
- title="Solobot CLI v0.1.7", title_style="bold magenta", header_style="bold blue"
+ title="Solobot CLI v0.1.8", title_style="bold magenta", header_style="bold blue"
)
table.add_column("№", justify="center", style="cyan", no_wrap=True)
table.add_column("Операция", style="white")
diff --git a/handlers/admin/tariffs/tariffs_handler.py b/handlers/admin/tariffs/tariffs_handler.py
index 70af9ff5..6d7d807e 100644
--- a/handlers/admin/tariffs/tariffs_handler.py
+++ b/handlers/admin/tariffs/tariffs_handler.py
@@ -314,26 +314,8 @@ async def view_tariff(
await callback.message.edit_text("❌ Тариф не найден.")
return
- traffic_text = (
- f"{tariff.traffic_limit} ГБ" if tariff.traffic_limit else "Безлимит"
- )
- device_text = (
- f"{tariff.device_limit}" if tariff.device_limit is not None else "Безлимит"
- )
-
- text = (
- f"📄 Тариф: {tariff.name}\n\n"
- f"📁 Группа: {tariff.group_code}\n"
- f"📅 Длительность: {tariff.duration_days} дней\n"
- f"💰 Стоимость: {tariff.price_rub}₽\n"
- f"📦 Трафик: {traffic_text}\n"
- f"📱 Устройств: {device_text}\n"
- f"{'✅ Активен' if tariff.is_active else '⛔ Отключен'}"
- )
-
- await callback.message.edit_text(
- text, reply_markup=build_single_tariff_kb(tariff_id)
- )
+ text, markup = render_tariff_card(tariff)
+ await callback.message.edit_text(text=text, reply_markup=markup)
@router.callback_query(
@@ -452,9 +434,7 @@ async def apply_edit(message: Message, state: FSMContext, session: AsyncSession)
num = int(value)
if num < 0:
raise ValueError
- if field == "traffic_limit":
- value = num if num > 0 else None
- elif field == "device_limit":
+ if field in ["traffic_limit", "device_limit"]:
value = num if num > 0 else None
else:
value = num
@@ -467,29 +447,27 @@ async def apply_edit(message: Message, state: FSMContext, session: AsyncSession)
await session.commit()
await state.clear()
- await message.answer("✅ Тариф успешно обновлён.")
+
+ text, markup = render_tariff_card(tariff)
+ await message.answer(text=text, reply_markup=markup)
@router.callback_query(F.data.startswith("toggle_active|"), IsAdminFilter())
-async def toggle_tariff_status(callback: CallbackQuery, session):
+async def toggle_tariff_status(callback: CallbackQuery, session: AsyncSession):
tariff_id = int(callback.data.split("|")[1])
- row = await session.fetchrow(
- "SELECT is_active FROM tariffs WHERE id = $1", tariff_id
- )
- if not row:
+ result = await session.execute(select(Tariff).where(Tariff.id == tariff_id))
+ tariff = result.scalar_one_or_none()
+
+ if not tariff:
await callback.message.edit_text("❌ Тариф не найден.")
return
- new_status = not row["is_active"]
- await session.execute(
- "UPDATE tariffs SET is_active = $1, updated_at = NOW() WHERE id = $2",
- new_status,
- tariff_id,
- )
+ tariff.is_active = not tariff.is_active
+ await session.commit()
- status_text = "✅ Тариф активирован." if new_status else "⛔ Тариф отключён."
- await callback.message.edit_text(status_text)
+ text, markup = render_tariff_card(tariff)
+ await callback.message.edit_text(text=text, reply_markup=markup)
@router.callback_query(
@@ -505,3 +483,24 @@ async def start_tariff_creation_existing_group(
f"📦 Добавление нового тарифа в группу {group_code}\n\n📝 Введите название тарифа:",
reply_markup=build_cancel_kb(),
)
+
+
+def render_tariff_card(tariff: Tariff) -> tuple[str, InlineKeyboardMarkup]:
+ traffic_text = (
+ f"{tariff.traffic_limit} ГБ" if tariff.traffic_limit else "Безлимит"
+ )
+ device_text = (
+ f"{tariff.device_limit}" if tariff.device_limit is not None else "Безлимит"
+ )
+
+ text = (
+ f"📄 Тариф: {tariff.name}\n\n"
+ f"📁 Группа: {tariff.group_code}\n"
+ f"📅 Длительность: {tariff.duration_days} дней\n"
+ f"💰 Стоимость: {tariff.price_rub}₽\n"
+ f"📦 Трафик: {traffic_text}\n"
+ f"📱 Устройств: {device_text}\n"
+ f"{'✅ Активен' if tariff.is_active else '⛔ Отключен'}"
+ )
+
+ return text, build_single_tariff_kb(tariff.id)
\ No newline at end of file