Fix campaign deletion confirmation and add editing workflow

This commit is contained in:
Egor
2025-09-19 13:15:17 +03:00
parent adf94802b0
commit cf9fbc5ccc
3 changed files with 965 additions and 19 deletions

View File

@@ -2,7 +2,7 @@ import logging
import re
from typing import List
from aiogram import Dispatcher, types, F
from aiogram import Bot, Dispatcher, types, F
from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession
@@ -24,6 +24,7 @@ from app.keyboards.admin import (
get_admin_campaigns_keyboard,
get_admin_pagination_keyboard,
get_campaign_bonus_type_keyboard,
get_campaign_edit_keyboard,
get_campaign_management_keyboard,
get_confirmation_keyboard,
)
@@ -78,7 +79,12 @@ async def _get_bot_deep_link_from_message(
def _build_campaign_servers_keyboard(
servers, selected_uuids: List[str]
servers,
selected_uuids: List[str],
*,
toggle_prefix: str = "campaign_toggle_server_",
save_callback: str = "campaign_servers_save",
back_callback: str = "admin_campaigns",
) -> types.InlineKeyboardMarkup:
keyboard: List[List[types.InlineKeyboardButton]] = []
@@ -89,7 +95,7 @@ def _build_campaign_servers_keyboard(
keyboard.append(
[
types.InlineKeyboardButton(
text=text, callback_data=f"campaign_toggle_server_{server.id}"
text=text, callback_data=f"{toggle_prefix}{server.id}"
)
]
)
@@ -97,15 +103,44 @@ def _build_campaign_servers_keyboard(
keyboard.append(
[
types.InlineKeyboardButton(
text="✅ Сохранить", callback_data="campaign_servers_save"
text="✅ Сохранить", callback_data=save_callback
),
types.InlineKeyboardButton(
text="⬅️ Назад", callback_data=back_callback
),
types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_campaigns"),
]
)
return types.InlineKeyboardMarkup(inline_keyboard=keyboard)
async def _render_campaign_edit_menu(
bot: Bot,
chat_id: int,
message_id: int,
campaign,
language: str,
):
texts = get_texts(language)
text = (
"✏️ <b>Редактирование кампании</b>\n\n"
f"{_format_campaign_summary(campaign, texts)}\n"
"Выберите, что изменить:"
)
await bot.edit_message_text(
text=text,
chat_id=chat_id,
message_id=message_id,
reply_markup=get_campaign_edit_keyboard(
campaign.id,
is_balance_bonus=campaign.is_balance_bonus,
language=language,
),
parse_mode="HTML",
)
@admin_required
@error_handler
async def show_campaigns_menu(
@@ -300,6 +335,761 @@ async def show_campaign_detail(
await callback.answer()
@admin_required
@error_handler
async def show_campaign_edit_menu(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await state.clear()
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
await state.clear()
await _render_campaign_edit_menu(
callback.bot,
callback.message.chat.id,
callback.message.message_id,
campaign,
db_user.language,
)
await callback.answer()
@admin_required
@error_handler
async def start_edit_campaign_name(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
await state.clear()
await state.set_state(AdminStates.editing_campaign_name)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
)
await callback.message.edit_text(
(
"✏️ <b>Изменение названия кампании</b>\n\n"
f"Текущее название: <b>{campaign.name}</b>\n"
"Введите новое название (3-100 символов):"
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_campaign_name(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await message.answer("❌ Сессия редактирования устарела. Попробуйте снова.")
await state.clear()
return
new_name = message.text.strip()
if len(new_name) < 3 or len(new_name) > 100:
await message.answer(
"❌ Название должно содержать от 3 до 100 символов. Попробуйте снова."
)
return
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await message.answer("❌ Кампания не найдена")
await state.clear()
return
await update_campaign(db, campaign, name=new_name)
await state.clear()
await message.answer("✅ Название обновлено.")
edit_message_id = data.get("campaign_edit_message_id")
if edit_message_id:
await _render_campaign_edit_menu(
message.bot,
message.chat.id,
edit_message_id,
campaign,
db_user.language,
)
@admin_required
@error_handler
async def start_edit_campaign_start_parameter(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
await state.clear()
await state.set_state(AdminStates.editing_campaign_start)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
)
await callback.message.edit_text(
(
"🔗 <b>Изменение стартового параметра</b>\n\n"
f"Текущий параметр: <code>{campaign.start_parameter}</code>\n"
"Введите новый параметр (латинские буквы, цифры, - или _, 3-32 символа):"
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_campaign_start_parameter(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await message.answer("❌ Сессия редактирования устарела. Попробуйте снова.")
await state.clear()
return
new_param = message.text.strip()
if not _CAMPAIGN_PARAM_REGEX.match(new_param):
await message.answer(
"❌ Разрешены только латинские буквы, цифры, символы - и _. Длина 3-32 символа."
)
return
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await message.answer("❌ Кампания не найдена")
await state.clear()
return
existing = await get_campaign_by_start_parameter(db, new_param)
if existing and existing.id != campaign_id:
await message.answer("❌ Такой параметр уже используется. Введите другой вариант.")
return
await update_campaign(db, campaign, start_parameter=new_param)
await state.clear()
await message.answer("✅ Стартовый параметр обновлен.")
edit_message_id = data.get("campaign_edit_message_id")
if edit_message_id:
await _render_campaign_edit_menu(
message.bot,
message.chat.id,
edit_message_id,
campaign,
db_user.language,
)
@admin_required
@error_handler
async def start_edit_campaign_balance_bonus(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
if not campaign.is_balance_bonus:
await callback.answer("У кампании другой тип бонуса", show_alert=True)
return
await state.clear()
await state.set_state(AdminStates.editing_campaign_balance)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
)
await callback.message.edit_text(
(
"💰 <b>Изменение бонуса на баланс</b>\n\n"
f"Текущий бонус: <b>{get_texts(db_user.language).format_price(campaign.balance_bonus_kopeks)}</b>\n"
"Введите новую сумму в рублях (например, 100 или 99.5):"
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_campaign_balance_bonus(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await message.answer("❌ Сессия редактирования устарела. Попробуйте снова.")
await state.clear()
return
try:
amount_rubles = float(message.text.replace(",", "."))
except ValueError:
await message.answer("❌ Введите корректную сумму (например, 100 или 99.5)")
return
if amount_rubles <= 0:
await message.answer("❌ Сумма должна быть больше нуля")
return
amount_kopeks = int(round(amount_rubles * 100))
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await message.answer("❌ Кампания не найдена")
await state.clear()
return
if not campaign.is_balance_bonus:
await message.answer("У кампании другой тип бонуса")
await state.clear()
return
await update_campaign(db, campaign, balance_bonus_kopeks=amount_kopeks)
await state.clear()
await message.answer("✅ Бонус обновлен.")
edit_message_id = data.get("campaign_edit_message_id")
if edit_message_id:
await _render_campaign_edit_menu(
message.bot,
message.chat.id,
edit_message_id,
campaign,
db_user.language,
)
async def _ensure_subscription_campaign(message_or_callback, campaign) -> bool:
if campaign.is_balance_bonus:
if isinstance(message_or_callback, types.CallbackQuery):
await message_or_callback.answer(
"❌ Для этой кампании доступен только бонус на баланс",
show_alert=True,
)
else:
await message_or_callback.answer(
"❌ Для этой кампании нельзя изменить параметры подписки"
)
return False
return True
@admin_required
@error_handler
async def start_edit_campaign_subscription_days(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
if not await _ensure_subscription_campaign(callback, campaign):
return
await state.clear()
await state.set_state(AdminStates.editing_campaign_subscription_days)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
)
await callback.message.edit_text(
(
"📅 <b>Изменение длительности подписки</b>\n\n"
f"Текущее значение: <b>{campaign.subscription_duration_days or 0} д.</b>\n"
"Введите новое количество дней (1-730):"
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_campaign_subscription_days(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await message.answer("❌ Сессия редактирования устарела. Попробуйте снова.")
await state.clear()
return
try:
days = int(message.text.strip())
except ValueError:
await message.answer("❌ Введите число дней (1-730)")
return
if days <= 0 or days > 730:
await message.answer("❌ Длительность должна быть от 1 до 730 дней")
return
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await message.answer("❌ Кампания не найдена")
await state.clear()
return
if not await _ensure_subscription_campaign(message, campaign):
await state.clear()
return
await update_campaign(db, campaign, subscription_duration_days=days)
await state.clear()
await message.answer("✅ Длительность подписки обновлена.")
edit_message_id = data.get("campaign_edit_message_id")
if edit_message_id:
await _render_campaign_edit_menu(
message.bot,
message.chat.id,
edit_message_id,
campaign,
db_user.language,
)
@admin_required
@error_handler
async def start_edit_campaign_subscription_traffic(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
if not await _ensure_subscription_campaign(callback, campaign):
return
await state.clear()
await state.set_state(AdminStates.editing_campaign_subscription_traffic)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
)
current_traffic = campaign.subscription_traffic_gb or 0
traffic_text = "безлимит" if current_traffic == 0 else f"{current_traffic} ГБ"
await callback.message.edit_text(
(
"🌐 <b>Изменение лимита трафика</b>\n\n"
f"Текущее значение: <b>{traffic_text}</b>\n"
"Введите новый лимит в ГБ (0 = безлимит, максимум 10000):"
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_campaign_subscription_traffic(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await message.answer("❌ Сессия редактирования устарела. Попробуйте снова.")
await state.clear()
return
try:
traffic = int(message.text.strip())
except ValueError:
await message.answer("❌ Введите целое число (0 или больше)")
return
if traffic < 0 or traffic > 10000:
await message.answer("❌ Лимит трафика должен быть от 0 до 10000 ГБ")
return
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await message.answer("❌ Кампания не найдена")
await state.clear()
return
if not await _ensure_subscription_campaign(message, campaign):
await state.clear()
return
await update_campaign(db, campaign, subscription_traffic_gb=traffic)
await state.clear()
await message.answer("✅ Лимит трафика обновлен.")
edit_message_id = data.get("campaign_edit_message_id")
if edit_message_id:
await _render_campaign_edit_menu(
message.bot,
message.chat.id,
edit_message_id,
campaign,
db_user.language,
)
@admin_required
@error_handler
async def start_edit_campaign_subscription_devices(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
if not await _ensure_subscription_campaign(callback, campaign):
return
await state.clear()
await state.set_state(AdminStates.editing_campaign_subscription_devices)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
)
current_devices = campaign.subscription_device_limit or settings.DEFAULT_DEVICE_LIMIT
await callback.message.edit_text(
(
"📱 <b>Изменение лимита устройств</b>\n\n"
f"Текущее значение: <b>{current_devices}</b>\n"
f"Введите новое количество (1-{settings.MAX_DEVICES_LIMIT}):"
),
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[
[
types.InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
]
]
),
)
await callback.answer()
@admin_required
@error_handler
async def process_edit_campaign_subscription_devices(
message: types.Message,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await message.answer("❌ Сессия редактирования устарела. Попробуйте снова.")
await state.clear()
return
try:
devices = int(message.text.strip())
except ValueError:
await message.answer("❌ Введите целое число устройств")
return
if devices < 1 or devices > settings.MAX_DEVICES_LIMIT:
await message.answer(
f"❌ Количество устройств должно быть от 1 до {settings.MAX_DEVICES_LIMIT}"
)
return
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await message.answer("❌ Кампания не найдена")
await state.clear()
return
if not await _ensure_subscription_campaign(message, campaign):
await state.clear()
return
await update_campaign(db, campaign, subscription_device_limit=devices)
await state.clear()
await message.answer("✅ Лимит устройств обновлен.")
edit_message_id = data.get("campaign_edit_message_id")
if edit_message_id:
await _render_campaign_edit_menu(
message.bot,
message.chat.id,
edit_message_id,
campaign,
db_user.language,
)
@admin_required
@error_handler
async def start_edit_campaign_subscription_servers(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
campaign_id = int(callback.data.split("_")[-1])
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
if not await _ensure_subscription_campaign(callback, campaign):
return
servers, _ = await get_all_server_squads(db, available_only=False)
if not servers:
await callback.answer(
"Не найдены доступные серверы. Добавьте серверы перед изменением.",
show_alert=True,
)
return
selected = list(campaign.subscription_squads or [])
await state.clear()
await state.set_state(AdminStates.editing_campaign_subscription_servers)
await state.update_data(
editing_campaign_id=campaign_id,
campaign_edit_message_id=callback.message.message_id,
campaign_subscription_squads=selected,
)
keyboard = _build_campaign_servers_keyboard(
servers,
selected,
toggle_prefix=f"campaign_edit_toggle_{campaign_id}_",
save_callback=f"campaign_edit_servers_save_{campaign_id}",
back_callback=f"admin_campaign_edit_{campaign_id}",
)
await callback.message.edit_text(
(
"🌍 <b>Редактирование доступных серверов</b>\n\n"
"Нажмите на сервер, чтобы добавить или убрать его из кампании.\n"
"После выбора нажмите \"✅ Сохранить\"."
),
reply_markup=keyboard,
)
await callback.answer()
@admin_required
@error_handler
async def toggle_edit_campaign_server(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
parts = callback.data.split("_")
try:
server_id = int(parts[-1])
except (ValueError, IndexError):
await callback.answer("Не удалось определить сервер", show_alert=True)
return
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await callback.answer("❌ Сессия редактирования устарела", show_alert=True)
await state.clear()
return
server = await get_server_squad_by_id(db, server_id)
if not server:
await callback.answer("❌ Сервер не найден", show_alert=True)
return
selected = list(data.get("campaign_subscription_squads", []))
if server.squad_uuid in selected:
selected.remove(server.squad_uuid)
else:
selected.append(server.squad_uuid)
await state.update_data(campaign_subscription_squads=selected)
servers, _ = await get_all_server_squads(db, available_only=False)
keyboard = _build_campaign_servers_keyboard(
servers,
selected,
toggle_prefix=f"campaign_edit_toggle_{campaign_id}_",
save_callback=f"campaign_edit_servers_save_{campaign_id}",
back_callback=f"admin_campaign_edit_{campaign_id}",
)
await callback.message.edit_reply_markup(reply_markup=keyboard)
await callback.answer()
@admin_required
@error_handler
async def save_edit_campaign_subscription_servers(
callback: types.CallbackQuery,
db_user: User,
state: FSMContext,
db: AsyncSession,
):
data = await state.get_data()
campaign_id = data.get("editing_campaign_id")
if not campaign_id:
await callback.answer("❌ Сессия редактирования устарела", show_alert=True)
await state.clear()
return
selected = list(data.get("campaign_subscription_squads", []))
if not selected:
await callback.answer("❗ Выберите хотя бы один сервер", show_alert=True)
return
campaign = await get_campaign_by_id(db, campaign_id)
if not campaign:
await state.clear()
await callback.answer("❌ Кампания не найдена", show_alert=True)
return
if not await _ensure_subscription_campaign(callback, campaign):
await state.clear()
return
await update_campaign(db, campaign, subscription_squads=selected)
await state.clear()
await _render_campaign_edit_menu(
callback.bot,
callback.message.chat.id,
callback.message.message_id,
campaign,
db_user.language,
)
await callback.answer("✅ Сохранено")
@admin_required
@error_handler
async def toggle_campaign_status(
@@ -386,8 +1176,8 @@ async def confirm_delete_campaign(
await callback.message.edit_text(
text,
reply_markup=get_confirmation_keyboard(
confirm_callback=f"admin_campaign_delete_confirm_{campaign_id}",
cancel_callback=f"admin_campaign_manage_{campaign_id}",
confirm_action=f"admin_campaign_delete_confirm_{campaign_id}",
cancel_action=f"admin_campaign_manage_{campaign_id}",
),
)
await callback.answer()
@@ -765,6 +1555,43 @@ def register_handlers(dp: Dispatcher):
dp.callback_query.register(
show_campaign_detail, F.data.startswith("admin_campaign_manage_")
)
dp.callback_query.register(
start_edit_campaign_name, F.data.startswith("admin_campaign_edit_name_")
)
dp.callback_query.register(
start_edit_campaign_start_parameter,
F.data.startswith("admin_campaign_edit_start_"),
)
dp.callback_query.register(
start_edit_campaign_balance_bonus,
F.data.startswith("admin_campaign_edit_balance_"),
)
dp.callback_query.register(
start_edit_campaign_subscription_days,
F.data.startswith("admin_campaign_edit_sub_days_"),
)
dp.callback_query.register(
start_edit_campaign_subscription_traffic,
F.data.startswith("admin_campaign_edit_sub_traffic_"),
)
dp.callback_query.register(
start_edit_campaign_subscription_devices,
F.data.startswith("admin_campaign_edit_sub_devices_"),
)
dp.callback_query.register(
start_edit_campaign_subscription_servers,
F.data.startswith("admin_campaign_edit_sub_servers_"),
)
dp.callback_query.register(
save_edit_campaign_subscription_servers,
F.data.startswith("campaign_edit_servers_save_"),
)
dp.callback_query.register(
toggle_edit_campaign_server, F.data.startswith("campaign_edit_toggle_")
)
dp.callback_query.register(
show_campaign_edit_menu, F.data.startswith("admin_campaign_edit_")
)
dp.callback_query.register(
delete_campaign_confirmed, F.data.startswith("admin_campaign_delete_confirm_")
)
@@ -803,3 +1630,26 @@ def register_handlers(dp: Dispatcher):
process_campaign_subscription_devices,
AdminStates.creating_campaign_subscription_devices,
)
dp.message.register(
process_edit_campaign_name, AdminStates.editing_campaign_name
)
dp.message.register(
process_edit_campaign_start_parameter,
AdminStates.editing_campaign_start,
)
dp.message.register(
process_edit_campaign_balance_bonus,
AdminStates.editing_campaign_balance,
)
dp.message.register(
process_edit_campaign_subscription_days,
AdminStates.editing_campaign_subscription_days,
)
dp.message.register(
process_edit_campaign_subscription_traffic,
AdminStates.editing_campaign_subscription_traffic,
)
dp.message.register(
process_edit_campaign_subscription_devices,
AdminStates.editing_campaign_subscription_devices,
)

View File

@@ -167,21 +167,109 @@ def get_admin_campaigns_keyboard(language: str = "ru") -> InlineKeyboardMarkup:
])
def get_campaign_management_keyboard(campaign_id: int, is_active: bool, language: str = "ru") -> InlineKeyboardMarkup:
def get_campaign_management_keyboard(
campaign_id: int, is_active: bool, language: str = "ru"
) -> InlineKeyboardMarkup:
status_text = "🔴 Выключить" if is_active else "🟢 Включить"
return InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(text="📊 Статистика", callback_data=f"admin_campaign_stats_{campaign_id}"),
InlineKeyboardButton(text=status_text, callback_data=f"admin_campaign_toggle_{campaign_id}")
],
[
InlineKeyboardButton(text="🗑️ Удалить", callback_data=f"admin_campaign_delete_{campaign_id}")
],
[
InlineKeyboardButton(text="⬅️ К списку", callback_data="admin_campaigns_list")
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="📊 Статистика",
callback_data=f"admin_campaign_stats_{campaign_id}",
),
InlineKeyboardButton(
text=status_text,
callback_data=f"admin_campaign_toggle_{campaign_id}",
),
],
[
InlineKeyboardButton(
text="✏️ Редактировать",
callback_data=f"admin_campaign_edit_{campaign_id}",
)
],
[
InlineKeyboardButton(
text="🗑️ Удалить",
callback_data=f"admin_campaign_delete_{campaign_id}",
)
],
[
InlineKeyboardButton(
text="⬅️ К списку", callback_data="admin_campaigns_list"
)
],
]
])
)
def get_campaign_edit_keyboard(
campaign_id: int,
*,
is_balance_bonus: bool,
language: str = "ru",
) -> InlineKeyboardMarkup:
texts = get_texts(language)
keyboard: List[List[InlineKeyboardButton]] = [
[
InlineKeyboardButton(
text="✏️ Название",
callback_data=f"admin_campaign_edit_name_{campaign_id}",
),
InlineKeyboardButton(
text="🔗 Параметр",
callback_data=f"admin_campaign_edit_start_{campaign_id}",
),
]
]
if is_balance_bonus:
keyboard.append(
[
InlineKeyboardButton(
text="💰 Бонус на баланс",
callback_data=f"admin_campaign_edit_balance_{campaign_id}",
)
]
)
else:
keyboard.extend(
[
[
InlineKeyboardButton(
text="📅 Длительность",
callback_data=f"admin_campaign_edit_sub_days_{campaign_id}",
),
InlineKeyboardButton(
text="🌐 Трафик",
callback_data=f"admin_campaign_edit_sub_traffic_{campaign_id}",
),
],
[
InlineKeyboardButton(
text="📱 Устройства",
callback_data=f"admin_campaign_edit_sub_devices_{campaign_id}",
),
InlineKeyboardButton(
text="🌍 Серверы",
callback_data=f"admin_campaign_edit_sub_servers_{campaign_id}",
),
],
]
)
keyboard.append(
[
InlineKeyboardButton(
text=texts.BACK, callback_data=f"admin_campaign_manage_{campaign_id}"
)
]
)
return InlineKeyboardMarkup(inline_keyboard=keyboard)
def get_campaign_bonus_type_keyboard(language: str = "ru") -> InlineKeyboardMarkup:

View File

@@ -50,6 +50,14 @@ class AdminStates(StatesGroup):
creating_campaign_subscription_traffic = State()
creating_campaign_subscription_devices = State()
creating_campaign_subscription_servers = State()
editing_campaign_name = State()
editing_campaign_start = State()
editing_campaign_balance = State()
editing_campaign_subscription_days = State()
editing_campaign_subscription_traffic = State()
editing_campaign_subscription_devices = State()
editing_campaign_subscription_servers = State()
waiting_for_broadcast_message = State()
waiting_for_broadcast_media = State()