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:
@@ -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)
|
||||||
@@ -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]:
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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}"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user