Implement user subscription deletion and banned users management

- Replaced the trial reset functionality to delete all user subscriptions instead of deactivating them, ensuring a complete reset of trial eligibility.
- Added a new method to retrieve banned users from the database, enhancing the admin's ability to manage user bans.
- Updated user management handlers to utilize the new subscription deletion and banned users retrieval features.
- Enhanced localization files to support new messages related to banned users.
This commit is contained in:
machka-pasla
2025-08-03 17:51:37 +03:00
parent 39ba57951c
commit 0f26c9bdba
5 changed files with 128 additions and 8 deletions
+55 -6
View File
@@ -291,8 +291,8 @@ async def handle_reset_trial(callback: types.CallbackQuery, user: User,
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs) _ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
try: try:
# Deactivate all user subscriptions # Delete all user subscriptions to reset trial eligibility
await subscription_dal.deactivate_all_user_subscriptions(session, user.user_id) await subscription_dal.delete_all_user_subscriptions(session, user.user_id)
await session.commit() await session.commit()
await callback.answer(_( await callback.answer(_(
@@ -362,7 +362,10 @@ async def handle_toggle_ban(callback: types.CallbackQuery, user: User,
# Refresh user card with updated ban status # Refresh user card with updated ban status
user.is_banned = new_ban_status # Update local object user.is_banned = new_ban_status # Update local object
await handle_refresh_user_card(callback, user, None, session, i18n_instance, lang) from config.settings import Settings
settings = Settings()
subscription_service = SubscriptionService(settings)
await handle_refresh_user_card(callback, user, subscription_service, session, i18n_instance, lang)
except Exception as e: except Exception as e:
logging.error(f"Error toggling ban for user {user.user_id}: {e}") logging.error(f"Error toggling ban for user {user.user_id}: {e}")
@@ -624,8 +627,9 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
user_id=target_user_id user_id=target_user_id
)) ))
# Show user card again # Show user card again
user_card_text = await format_user_card(target_user, session, None, i18n, current_lang) subscription_service = SubscriptionService(settings)
user_card_text = await format_user_card(target_user, session, subscription_service, i18n, current_lang)
keyboard = get_user_card_keyboard(target_user.user_id, i18n, current_lang) keyboard = get_user_card_keyboard(target_user.user_id, i18n, current_lang)
await message.answer( await message.answer(
@@ -641,4 +645,49 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
default="❌ Ошибка отправки сообщения" default="❌ Ошибка отправки сообщения"
)) ))
await state.clear() await state.clear()
async def view_banned_users_handler(callback: types.CallbackQuery,
state: FSMContext, i18n_data: dict,
settings: Settings, session: AsyncSession):
"""Display list of banned users"""
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
if not i18n or not callback.message:
await callback.answer("Error preparing banned users list.", show_alert=True)
return
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
try:
# Get banned users
banned_users = await user_dal.get_banned_users(session)
if not banned_users:
message_text = _(
"admin_banned_users_empty",
default="📋 Заблокированные пользователи\n\nСписок пуст"
)
else:
user_list = []
for user in banned_users:
display_name = user.telegram_first_name or "Unknown"
if user.telegram_username:
display_name = f"@{user.telegram_username}"
user_list.append(f"{display_name} (ID: {user.user_id})")
message_text = _(
"admin_banned_users_list",
default="📋 Заблокированные пользователи ({count}):\n\n{users}",
count=len(banned_users),
users="\n".join(user_list)
)
await callback.message.edit_text(
message_text,
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n)
)
except Exception as e:
logging.error(f"Error displaying banned users: {e}")
await callback.answer("Error loading banned users", show_alert=True)
+12
View File
@@ -144,6 +144,18 @@ async def deactivate_all_user_subscriptions(
return result.rowcount return result.rowcount
async def delete_all_user_subscriptions(
session: AsyncSession, user_id: int) -> int:
"""Completely delete all user subscriptions (for trial reset)"""
stmt = delete(Subscription).where(Subscription.user_id == user_id)
result = await session.execute(stmt)
if result.rowcount > 0:
logging.info(
f"Deleted {result.rowcount} subscription records for user {user_id} for trial reset."
)
return result.rowcount
async def update_subscription_end_date( async def update_subscription_end_date(
session: AsyncSession, subscription_id: int, session: AsyncSession, subscription_id: int,
new_end_date: datetime) -> Optional[Subscription]: new_end_date: datetime) -> Optional[Subscription]:
+11
View File
@@ -93,6 +93,17 @@ async def set_user_ban_status(
return False return False
async def get_banned_users(session: AsyncSession) -> List[User]:
"""Get all banned users"""
stmt = (
select(User)
.where(User.is_banned == True)
.order_by(User.registration_date.desc())
)
result = await session.execute(stmt)
return result.scalars().all()
async def get_banned_users_paginated( async def get_banned_users_paginated(
session: AsyncSession, limit: int, offset: int session: AsyncSession, limit: int, offset: int
) -> Tuple[List[User], int]: ) -> Tuple[List[User], int]:
+25 -1
View File
@@ -299,5 +299,29 @@
"log_new_user_registration": "👤 <b>New User</b>\n\n🆔 ID: <code>{user_id}</code>\n👤 Name: {user_display}{referral_text}\n📅 Time: {timestamp}", "log_new_user_registration": "👤 <b>New User</b>\n\n🆔 ID: <code>{user_id}</code>\n👤 Name: {user_display}{referral_text}\n📅 Time: {timestamp}",
"log_payment_received": "{provider_emoji} <b>Payment Received</b>\n\n👤 User: {user_display}\n💰 Amount: <b>{amount} {currency}</b>\n📅 Period: <b>{months} mo.</b>\n🏦 Provider: {payment_provider}\n🕐 Time: {timestamp}", "log_payment_received": "{provider_emoji} <b>Payment Received</b>\n\n👤 User: {user_display}\n💰 Amount: <b>{amount} {currency}</b>\n📅 Period: <b>{months} mo.</b>\n🏦 Provider: {payment_provider}\n🕐 Time: {timestamp}",
"log_promo_activation": "🎁 <b>Promo Code Activated</b>\n\n👤 User: {user_display}\n🏷 Code: <code>{promo_code}</code>\n🎯 Bonus: <b>+{bonus_days}d</b>\n🕐 Time: {timestamp}", "log_promo_activation": "🎁 <b>Promo Code Activated</b>\n\n👤 User: {user_display}\n🏷 Code: <code>{promo_code}</code>\n🎯 Bonus: <b>+{bonus_days}d</b>\n🕐 Time: {timestamp}",
"log_trial_activation": "🆓 <b>Trial Activated</b>\n\n👤 User: {user_display}\n⏰ Valid until: <b>{end_date}</b>\n🕐 Time: {timestamp}" "log_trial_activation": "🆓 <b>Trial Activated</b>\n\n👤 User: {user_display}\n⏰ Valid until: <b>{end_date}</b>\n🕐 Time: {timestamp}",
"admin_general_cancel_operation": "Operation cancelled ❌",
"admin_promo_codes_menu_bulk_create": "📦 Bulk Creation",
"admin_bulk_promo_create_prompt": "📦 Bulk Promo Code Creation\n\nEnter data in format:\n<code>quantity|days|description</code>\n\nExample:\n<code>10|7|Weekly promo</code>",
"admin_bulk_promo_invalid_format": "❌ Invalid format. Use: quantity|days|description",
"admin_bulk_promo_created_success": "✅ Created {count} promo codes for {days} days!\n\nDescription: {description}\nCodes: {codes}",
"admin_logs_export_csv": "📄 Export to CSV",
"admin_logs_csv_export_started": "📄 Starting log export to CSV...",
"admin_logs_csv_export_success": "✅ Logs exported! File attached above.",
"admin_csv_header_log_id": "Log ID",
"admin_csv_header_timestamp": "Timestamp",
"admin_csv_header_user_id": "User ID",
"admin_csv_header_telegram_username": "Username",
"admin_csv_header_telegram_first_name": "First Name",
"admin_csv_header_event_type": "Event Type",
"admin_csv_header_content": "Content",
"admin_csv_header_is_admin_event": "Admin Event",
"admin_csv_header_target_user_id": "Target User ID",
"admin_csv_header_raw_update_preview": "Update Preview",
"admin_banned_users_empty": "📋 Banned Users\n\nList is empty",
"admin_banned_users_list": "📋 Banned Users ({count}):\n\n{users}"
} }
+25 -1
View File
@@ -299,5 +299,29 @@
"log_new_user_registration": "👤 <b>Новый пользователь</b>\n\n🆔 ID: <code>{user_id}</code>\n👤 Имя: {user_display}{referral_text}\n📅 Время: {timestamp}", "log_new_user_registration": "👤 <b>Новый пользователь</b>\n\n🆔 ID: <code>{user_id}</code>\n👤 Имя: {user_display}{referral_text}\n📅 Время: {timestamp}",
"log_payment_received": "{provider_emoji} <b>Получен платеж</b>\n\n👤 Пользователь: {user_display}\n💰 Сумма: <b>{amount} {currency}</b>\n📅 Период: <b>{months} мес.</b>\n🏦 Провайдер: {payment_provider}\n🕐 Время: {timestamp}", "log_payment_received": "{provider_emoji} <b>Получен платеж</b>\n\n👤 Пользователь: {user_display}\n💰 Сумма: <b>{amount} {currency}</b>\n📅 Период: <b>{months} мес.</b>\n🏦 Провайдер: {payment_provider}\n🕐 Время: {timestamp}",
"log_promo_activation": "🎁 <b>Активирован промокод</b>\n\n👤 Пользователь: {user_display}\n🏷 Код: <code>{promo_code}</code>\n🎯 Бонус: <b>+{bonus_days} дн.</b>\n🕐 Время: {timestamp}", "log_promo_activation": "🎁 <b>Активирован промокод</b>\n\n👤 Пользователь: {user_display}\n🏷 Код: <code>{promo_code}</code>\n🎯 Бонус: <b>+{bonus_days} дн.</b>\n🕐 Время: {timestamp}",
"log_trial_activation": "🆓 <b>Активирован триал</b>\n\n👤 Пользователь: {user_display}\n⏰ Действует до: <b>{end_date}</b>\n🕐 Время: {timestamp}" "log_trial_activation": "🆓 <b>Активирован триал</b>\n\n👤 Пользователь: {user_display}\n⏰ Действует до: <b>{end_date}</b>\n🕐 Время: {timestamp}",
"admin_general_cancel_operation": "Операция отменена ❌",
"admin_promo_codes_menu_bulk_create": "📦 Массовое создание",
"admin_bulk_promo_create_prompt": "📦 Массовое создание промокодов\n\nВведите данные в формате:\n<code>количество|дни|описание</code>\n\nПример:\n<code>10|7|Промо на неделю</code>",
"admin_bulk_promo_invalid_format": "❌ Неверный формат. Используйте: количество|дни|описание",
"admin_bulk_promo_created_success": "✅ Создано {count} промокодов на {days} дней!\n\nОписание: {description}\nПромокоды: {codes}",
"admin_logs_export_csv": "📄 Экспорт в CSV",
"admin_logs_csv_export_started": "📄 Начинаю экспорт логов в CSV...",
"admin_logs_csv_export_success": "✅ Логи экспортированы! Файл прикреплен выше.",
"admin_csv_header_log_id": "ID Лога",
"admin_csv_header_timestamp": "Время",
"admin_csv_header_user_id": "ID Пользователя",
"admin_csv_header_telegram_username": "Username",
"admin_csv_header_telegram_first_name": "Имя",
"admin_csv_header_event_type": "Тип события",
"admin_csv_header_content": "Содержание",
"admin_csv_header_is_admin_event": "Админ событие",
"admin_csv_header_target_user_id": "ID Целевого юзера",
"admin_csv_header_raw_update_preview": "Превью обновления",
"admin_banned_users_empty": "📋 Заблокированные пользователи\n\nСписок пуст",
"admin_banned_users_list": "📋 Заблокированные пользователи ({count}):\n\n{users}"
} }