Enhance inline and admin statistics features

- Updated inline user statistics message to provide clearer information about bot usage and user status.
- Refactored system statistics retrieval to utilize the PanelApiService for improved data accuracy and detail, including memory usage and bandwidth statistics.
- Simplified the success message for bulk promo code creation by removing the codes list from the message, ensuring clarity.
- Enhanced localization files to reflect changes in success messages and added new labels for better user experience.
This commit is contained in:
machka-pasla
2025-08-03 21:50:44 +03:00
parent 23aa3934e4
commit 52c4f83dd1
4 changed files with 122 additions and 74 deletions
+16 -19
View File
@@ -1140,15 +1140,13 @@ async def create_bulk_promo_codes_final(callback_or_message,
await session.commit() await session.commit()
# Send success message # Send success message
codes_text = " ".join(created_codes)
success_text = _( success_text = _(
"admin_bulk_promo_created_success", "admin_bulk_promo_created_success",
default="✅ Создано {count} промокодов на {days} дней!\n\nДействуют до: {validity}\nМакс. активации: {max_act}\n\nПромокоды:\n{codes}", default="✅ Создано {count} промокодов на {days} дней!\n\nДействуют до: {validity}\nМакс. активации: {max_act}",
count=quantity, count=quantity,
days=bonus_days, days=bonus_days,
validity=valid_until_str_display, validity=valid_until_str_display,
max_act=max_activations, max_act=max_activations
codes=codes_text[:3500] + "..." if len(codes_text) > 3500 else codes_text
) )
if hasattr(callback_or_message, 'message'): if hasattr(callback_or_message, 'message'):
@@ -1162,22 +1160,21 @@ async def create_bulk_promo_codes_final(callback_or_message,
parse_mode="HTML" parse_mode="HTML"
) )
# Send codes as file if too many # Always send codes as file
if len(codes_text) > 3500: codes_file_content = "\n".join(created_codes)
codes_file_content = "\n".join(created_codes) codes_file = types.BufferedInputFile(
codes_file = types.BufferedInputFile( codes_file_content.encode('utf-8'),
codes_file_content.encode('utf-8'), filename=f"bulk_promo_codes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
filename=f"bulk_promo_codes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt" )
)
await target_message.answer_document(
await target_message.answer_document( codes_file,
codes_file, caption=_(
caption=_( "admin_bulk_promo_codes_file",
"admin_bulk_promo_codes_file", default="📄 Все созданные промокоды в файле ({count} шт.)",
default="📄 Все созданные промокоды в файле", count=quantity
count=quantity
)
) )
)
except Exception as e: except Exception as e:
logging.error(f"Error creating bulk promo codes: {e}") logging.error(f"Error creating bulk promo codes: {e}")
+100 -53
View File
@@ -152,16 +152,14 @@ async def create_user_stats_result(session: AsyncSession, i18n_instance, lang: s
stats_text = _( stats_text = _(
"inline_user_stats_message", "inline_user_stats_message",
default="👥 <b>Статистика пользователей</b>\n\n" default="📊 <b>Статистика Бота</b>\n👥 Пользователи\n\n"
"📊 Всего: <b>{total}</b>\n" "📊 Всего: <b>{total}</b>\n"
"📈 Активных сегодня: <b>{active_today}</b>\n"
"💳 С платной подпиской: <b>{paid}</b>\n" "💳 С платной подпиской: <b>{paid}</b>\n"
"🆓 На пробном периоде: <b>{trial}</b>\n" "🆓 На пробном периоде: <b>{trial}</b>\n"
"😴 Неактивных: <b>{inactive}</b>\n" "😴 Неактивных: <b>{inactive}</b>\n"
"🚫 Заблокированных: <b>{banned}</b>\n" "🚫 Заблокированных: <b>{banned}</b>\n"
"🎁 По реферальной программе: <b>{referral}</b>", "🎁 Привлечено по реферальной программе: <b>{referral}</b>",
total=user_stats['total_users'], total=user_stats['total_users'],
active_today=user_stats['active_today'],
paid=user_stats['paid_subscriptions'], paid=user_stats['paid_subscriptions'],
trial=user_stats['trial_users'], trial=user_stats['trial_users'],
inactive=user_stats['inactive_users'], inactive=user_stats['inactive_users'],
@@ -173,11 +171,11 @@ async def create_user_stats_result(session: AsyncSession, i18n_instance, lang: s
id="admin_user_stats", id="admin_user_stats",
title=_( title=_(
"inline_admin_user_stats_title", "inline_admin_user_stats_title",
default="👥 Статистика пользователей" default="📊 Статистика пользователей"
), ),
description=_( description=_(
"inline_stats_description", "inline_user_stats_description",
default="Всего: {total}, Активных: {active}", default="Всего: {total}, Платных: {active}",
total=user_stats['total_users'], total=user_stats['total_users'],
active=user_stats['paid_subscriptions'] active=user_stats['paid_subscriptions']
), ),
@@ -240,53 +238,88 @@ async def create_financial_stats_result(session: AsyncSession, i18n_instance, la
async def create_system_stats_result(session: AsyncSession, i18n_instance, lang: str, settings: Settings) -> Optional[InlineQueryResultArticle]: async def create_system_stats_result(session: AsyncSession, i18n_instance, lang: str, settings: Settings) -> Optional[InlineQueryResultArticle]:
"""Create system statistics result with online/offline/expired/limited info""" """Create panel statistics result with system/nodes/bandwidth info"""
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs) _ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
try: try:
from datetime import datetime, timezone from bot.services.panel_api_service import PanelApiService
from sqlalchemy import select, func, and_
from db.models import User, Subscription
now = datetime.now(timezone.utc) # Get panel stats similar to main statistics
async with PanelApiService(settings) as panel_service:
# Count active subscriptions (online) system_stats = await panel_service.get_system_stats()
active_subs_stmt = select(func.count(Subscription.subscription_id)).where( bandwidth_stats = await panel_service.get_bandwidth_stats()
and_( nodes_stats = await panel_service.get_nodes_statistics()
Subscription.is_active == True,
Subscription.end_date > now if system_stats:
) users = system_stats.get('users', {})
) status_counts = users.get('statusCounts', {})
active_subs = (await session.execute(active_subs_stmt)).scalar() or 0 online_stats = system_stats.get('onlineStats', {})
# Count expired subscriptions active_users = status_counts.get('ACTIVE', 0)
expired_subs_stmt = select(func.count(Subscription.subscription_id)).where( disabled_users = status_counts.get('DISABLED', 0)
and_( expired_users = status_counts.get('EXPIRED', 0)
Subscription.is_active == True, limited_users = status_counts.get('LIMITED', 0)
Subscription.end_date <= now total_users = users.get('totalUsers', 0)
) online_now = online_stats.get('onlineNow', 0)
)
expired_subs = (await session.execute(expired_subs_stmt)).scalar() or 0 # Memory usage
memory = system_stats.get('memory', {})
# Count total users (approximation for "total") memory_usage = 0
total_users_stmt = select(func.count(User.user_id)) if memory:
total_users = (await session.execute(total_users_stmt)).scalar() or 0 memory_total = memory.get('total', 1)
memory_used = memory.get('used', 0)
# Offline = users without active subscriptions memory_usage = (memory_used / memory_total) * 100 if memory_total > 0 else 0
offline_users = total_users - active_subs
# Bandwidth
stats_text = _( week_traffic = "N/A"
"inline_system_stats_message", month_traffic = "N/A"
default="🖥 <b>Системная статистика</b>\n\n" if bandwidth_stats:
"🟢 Онлайн: <b>{online}</b>\n" week_data = bandwidth_stats.get('bandwidthLastSevenDays', {})
"🔴 Офлайн: <b>{offline}</b>\n" month_data = bandwidth_stats.get('bandwidthLast30Days', {}) or bandwidth_stats.get('bandwidthLastThirtyDays', {})
"⏰ Истекшие: <b>{expired}</b>\n"
"👥 Всего пользователей: <b>{total}</b>", week_traffic = week_data.get('current', 'N/A') if week_data else 'N/A'
online=active_subs, month_traffic = month_data.get('current', 'N/A') if month_data else 'N/A'
offline=max(0, offline_users),
expired=expired_subs, # Nodes
total=total_users active_nodes = 0
) total_nodes = 0
if nodes_stats and 'lastSevenDays' in nodes_stats:
unique_nodes = set()
for node_data in nodes_stats.get('lastSevenDays', []):
unique_nodes.add(node_data.get('nodeName', ''))
total_nodes = len(unique_nodes)
active_nodes = total_nodes # Assume all are active
elif system_stats and 'nodes' in system_stats:
active_nodes = system_stats.get('nodes', {}).get('totalOnline', 0)
total_nodes = active_nodes
stats_text = _(
"inline_system_stats_message",
default="🖥 <b>Статистика панели</b>\n\n"
"🟢 Онлайн: <b>{online}</b>\n"
"📊 Активных: <b>{active}</b>\n"
"🔴 Отключенных: <b>{disabled}</b>\n"
"⏰ Истекшие: <b>{expired}</b>\n"
"⚠️ Ограниченные: <b>{limited}</b>\n"
"👥 Всего пользователей: <b>{total}</b>\n"
"💾 Использование RAM: <b>{memory:.1f}%</b>\n"
"📊 Трафик за неделю: <b>{week_traffic}</b>\n"
"📊 Трафик за месяц: <b>{month_traffic}</b>\n"
"🔗 Активных нод: <b>{active_nodes}/{total_nodes}</b>",
online=online_now,
active=active_users,
disabled=disabled_users,
expired=expired_users,
limited=limited_users,
total=total_users,
memory=memory_usage,
week_traffic=week_traffic,
month_traffic=month_traffic,
active_nodes=active_nodes,
total_nodes=total_nodes
)
else:
stats_text = _("inline_panel_stats_error", default="❌ Ошибка получения данных с панели")
return InlineQueryResultArticle( return InlineQueryResultArticle(
id="admin_system_stats", id="admin_system_stats",
@@ -296,9 +329,7 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
), ),
description=_( description=_(
"inline_system_description", "inline_system_description",
default="Онлайн: {online}, Офлайн: {offline}", default="Панель: онлайн, ноды, трафик"
online=active_subs,
offline=max(0, offline_users)
), ),
input_message_content=InputTextMessageContent( input_message_content=InputTextMessageContent(
message_text=stats_text, message_text=stats_text,
@@ -309,6 +340,22 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
except Exception as e: except Exception as e:
logging.error(f"Error creating system stats result: {e}") logging.error(f"Error creating system stats result: {e}")
# Fallback error message
error_text = _("inline_panel_stats_error", default="❌ Ошибка получения данных с панели")
return InlineQueryResultArticle(
id="admin_system_stats",
title=_(
"inline_admin_system_stats_title",
default="🖥 Системная статистика"
),
description=_("inline_system_error", default="Ошибка получения данных"),
input_message_content=InputTextMessageContent(
message_text=error_text,
parse_mode="HTML"
),
thumbnail_url=settings.INLINE_SYSTEM_STATS_THUMBNAIL_URL
)
return None return None
+3 -1
View File
@@ -305,7 +305,7 @@
"admin_promo_codes_menu_bulk_create": "📦 Bulk Creation", "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_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_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_bulk_promo_created_success": "✅ Created {count} promo codes for {days} days!",
"admin_logs_export_csv": "📄 Export to CSV", "admin_logs_export_csv": "📄 Export to CSV",
"admin_logs_csv_export_started": "📄 Starting log export to CSV...", "admin_logs_csv_export_started": "📄 Starting log export to CSV...",
@@ -425,6 +425,8 @@
"admin_panel_stats_header": "Panel Statistics", "admin_panel_stats_header": "Panel Statistics",
"admin_panel_online_label": "Online", "admin_panel_online_label": "Online",
"admin_panel_active_label": "Active",
"admin_panel_disabled_label": "Disabled",
"admin_panel_offline_label": "Offline", "admin_panel_offline_label": "Offline",
"admin_panel_expired_label": "Expired", "admin_panel_expired_label": "Expired",
"admin_panel_limited_label": "Limited", "admin_panel_limited_label": "Limited",
+3 -1
View File
@@ -305,7 +305,7 @@
"admin_promo_codes_menu_bulk_create": "📦 Массовое создание", "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_create_prompt": "📦 Массовое создание промокодов\n\nВведите данные в формате:\n<code>количество|дни|описание</code>\n\nПример:\n<code>10|7|Промо на неделю</code>",
"admin_bulk_promo_invalid_format": "❌ Неверный формат. Используйте: количество|дни|описание", "admin_bulk_promo_invalid_format": "❌ Неверный формат. Используйте: количество|дни|описание",
"admin_bulk_promo_created_success": "✅ Создано {count} промокодов на {days} дней!\n\nОписание: {description}\nПромокоды: {codes}", "admin_bulk_promo_created_success": "✅ Создано {count} промокодов на {days} дней!",
"admin_logs_export_csv": "📄 Экспорт в CSV", "admin_logs_export_csv": "📄 Экспорт в CSV",
"admin_logs_csv_export_started": "📄 Начинаю экспорт логов в CSV...", "admin_logs_csv_export_started": "📄 Начинаю экспорт логов в CSV...",
@@ -425,6 +425,8 @@
"admin_panel_stats_header": "Статистика панели", "admin_panel_stats_header": "Статистика панели",
"admin_panel_online_label": "Онлайн", "admin_panel_online_label": "Онлайн",
"admin_panel_active_label": "Активных",
"admin_panel_disabled_label": "Отключенных",
"admin_panel_offline_label": "Офлайн", "admin_panel_offline_label": "Офлайн",
"admin_panel_expired_label": "Истекшие", "admin_panel_expired_label": "Истекшие",
"admin_panel_limited_label": "Ограниченные", "admin_panel_limited_label": "Ограниченные",