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()
# Send success message
codes_text = " ".join(created_codes)
success_text = _(
"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,
days=bonus_days,
validity=valid_until_str_display,
max_act=max_activations,
codes=codes_text[:3500] + "..." if len(codes_text) > 3500 else codes_text
max_act=max_activations
)
if hasattr(callback_or_message, 'message'):
@@ -1162,22 +1160,21 @@ async def create_bulk_promo_codes_final(callback_or_message,
parse_mode="HTML"
)
# Send codes as file if too many
if len(codes_text) > 3500:
codes_file_content = "\n".join(created_codes)
codes_file = types.BufferedInputFile(
codes_file_content.encode('utf-8'),
filename=f"bulk_promo_codes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
)
await target_message.answer_document(
codes_file,
caption=_(
"admin_bulk_promo_codes_file",
default="📄 Все созданные промокоды в файле",
count=quantity
)
# Always send codes as file
codes_file_content = "\n".join(created_codes)
codes_file = types.BufferedInputFile(
codes_file_content.encode('utf-8'),
filename=f"bulk_promo_codes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
)
await target_message.answer_document(
codes_file,
caption=_(
"admin_bulk_promo_codes_file",
default="📄 Все созданные промокоды в файле ({count} шт.)",
count=quantity
)
)
except Exception as 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 = _(
"inline_user_stats_message",
default="👥 <b>Статистика пользователей</b>\n\n"
default="📊 <b>Статистика Бота</b>\n👥 Пользователи\n\n"
"📊 Всего: <b>{total}</b>\n"
"📈 Активных сегодня: <b>{active_today}</b>\n"
"💳 С платной подпиской: <b>{paid}</b>\n"
"🆓 На пробном периоде: <b>{trial}</b>\n"
"😴 Неактивных: <b>{inactive}</b>\n"
"🚫 Заблокированных: <b>{banned}</b>\n"
"🎁 По реферальной программе: <b>{referral}</b>",
"🎁 Привлечено по реферальной программе: <b>{referral}</b>",
total=user_stats['total_users'],
active_today=user_stats['active_today'],
paid=user_stats['paid_subscriptions'],
trial=user_stats['trial_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",
title=_(
"inline_admin_user_stats_title",
default="👥 Статистика пользователей"
default="📊 Статистика пользователей"
),
description=_(
"inline_stats_description",
default="Всего: {total}, Активных: {active}",
"inline_user_stats_description",
default="Всего: {total}, Платных: {active}",
total=user_stats['total_users'],
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]:
"""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)
try:
from datetime import datetime, timezone
from sqlalchemy import select, func, and_
from db.models import User, Subscription
from bot.services.panel_api_service import PanelApiService
now = datetime.now(timezone.utc)
# Count active subscriptions (online)
active_subs_stmt = select(func.count(Subscription.subscription_id)).where(
and_(
Subscription.is_active == True,
Subscription.end_date > now
)
)
active_subs = (await session.execute(active_subs_stmt)).scalar() or 0
# Count expired subscriptions
expired_subs_stmt = select(func.count(Subscription.subscription_id)).where(
and_(
Subscription.is_active == True,
Subscription.end_date <= now
)
)
expired_subs = (await session.execute(expired_subs_stmt)).scalar() or 0
# Count total users (approximation for "total")
total_users_stmt = select(func.count(User.user_id))
total_users = (await session.execute(total_users_stmt)).scalar() or 0
# Offline = users without active subscriptions
offline_users = total_users - active_subs
stats_text = _(
"inline_system_stats_message",
default="🖥 <b>Системная статистика</b>\n\n"
"🟢 Онлайн: <b>{online}</b>\n"
"🔴 Офлайн: <b>{offline}</b>\n"
"⏰ Истекшие: <b>{expired}</b>\n"
"👥 Всего пользователей: <b>{total}</b>",
online=active_subs,
offline=max(0, offline_users),
expired=expired_subs,
total=total_users
)
# Get panel stats similar to main statistics
async with PanelApiService(settings) as panel_service:
system_stats = await panel_service.get_system_stats()
bandwidth_stats = await panel_service.get_bandwidth_stats()
nodes_stats = await panel_service.get_nodes_statistics()
if system_stats:
users = system_stats.get('users', {})
status_counts = users.get('statusCounts', {})
online_stats = system_stats.get('onlineStats', {})
active_users = status_counts.get('ACTIVE', 0)
disabled_users = status_counts.get('DISABLED', 0)
expired_users = status_counts.get('EXPIRED', 0)
limited_users = status_counts.get('LIMITED', 0)
total_users = users.get('totalUsers', 0)
online_now = online_stats.get('onlineNow', 0)
# Memory usage
memory = system_stats.get('memory', {})
memory_usage = 0
if memory:
memory_total = memory.get('total', 1)
memory_used = memory.get('used', 0)
memory_usage = (memory_used / memory_total) * 100 if memory_total > 0 else 0
# Bandwidth
week_traffic = "N/A"
month_traffic = "N/A"
if bandwidth_stats:
week_data = bandwidth_stats.get('bandwidthLastSevenDays', {})
month_data = bandwidth_stats.get('bandwidthLast30Days', {}) or bandwidth_stats.get('bandwidthLastThirtyDays', {})
week_traffic = week_data.get('current', 'N/A') if week_data else 'N/A'
month_traffic = month_data.get('current', 'N/A') if month_data else 'N/A'
# Nodes
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(
id="admin_system_stats",
@@ -296,9 +329,7 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
),
description=_(
"inline_system_description",
default="Онлайн: {online}, Офлайн: {offline}",
online=active_subs,
offline=max(0, offline_users)
default="Панель: онлайн, ноды, трафик"
),
input_message_content=InputTextMessageContent(
message_text=stats_text,
@@ -309,6 +340,22 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
except Exception as 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
+3 -1
View File
@@ -305,7 +305,7 @@
"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_bulk_promo_created_success": "✅ Created {count} promo codes for {days} days!",
"admin_logs_export_csv": "📄 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_online_label": "Online",
"admin_panel_active_label": "Active",
"admin_panel_disabled_label": "Disabled",
"admin_panel_offline_label": "Offline",
"admin_panel_expired_label": "Expired",
"admin_panel_limited_label": "Limited",
+3 -1
View File
@@ -305,7 +305,7 @@
"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_bulk_promo_created_success": "✅ Создано {count} промокодов на {days} дней!",
"admin_logs_export_csv": "📄 Экспорт в CSV",
"admin_logs_csv_export_started": "📄 Начинаю экспорт логов в CSV...",
@@ -425,6 +425,8 @@
"admin_panel_stats_header": "Статистика панели",
"admin_panel_online_label": "Онлайн",
"admin_panel_active_label": "Активных",
"admin_panel_disabled_label": "Отключенных",
"admin_panel_offline_label": "Офлайн",
"admin_panel_expired_label": "Истекшие",
"admin_panel_limited_label": "Ограниченные",