feat(admin_bot): show link-senders count and top-5 active users in /stat
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 1m43s

Adds users.link_count, incremented in bot.py whenever a user submits
a recognized link (regardless of eventual download success), and
surfaces it in admin_bot's /stat as a count of users who ever sent a
link plus a top-5 leaderboard by link count.
This commit is contained in:
vrubelroman 2026-06-30 22:28:53 +00:00
parent 772e9fd5b4
commit d9ff2aec57
2 changed files with 67 additions and 5 deletions

View file

@ -60,6 +60,38 @@ def get_total_users() -> int:
return 0 return 0
def get_users_sent_link_count() -> int:
"""Возвращает количество пользователей, отправивших хотя бы одну ссылку"""
try:
conn = sqlite3.connect(str(DB_FILE))
cursor = conn.cursor()
cursor.execute('SELECT COUNT(*) FROM users WHERE link_count > 0')
result = cursor.fetchone()
conn.close()
return result[0] if result else 0
except Exception as e:
logger.error(f"Ошибка при получении количества активных пользователей: {e}")
return 0
def get_top_active_users(limit: int = 5) -> list[tuple[int, str, str, int]]:
"""Возвращает топ пользователей по количеству отправленных ссылок"""
try:
conn = sqlite3.connect(str(DB_FILE))
cursor = conn.cursor()
cursor.execute(
'SELECT chat_id, username, first_name, link_count FROM users '
'WHERE link_count > 0 ORDER BY link_count DESC LIMIT ?',
(limit,)
)
results = cursor.fetchall()
conn.close()
return results
except Exception as e:
logger.error(f"Ошибка при получении топа активных пользователей: {e}")
return []
def get_error_stats() -> dict[str, int]: def get_error_stats() -> dict[str, int]:
"""Возвращает статистику ошибок по сервисам""" """Возвращает статистику ошибок по сервисам"""
try: try:
@ -179,6 +211,8 @@ async def stat_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
total_downloads = get_total_downloads() total_downloads = get_total_downloads()
total_users = get_total_users() total_users = get_total_users()
users_sent_link = get_users_sent_link_count()
top_users = get_top_active_users(5)
error_stats = get_error_stats() error_stats = get_error_stats()
# Форматируем статистику ошибок # Форматируем статистику ошибок
@ -200,10 +234,21 @@ async def stat_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not error_stats_text: if not error_stats_text:
error_stats_text = " Нет ошибок" error_stats_text = " Нет ошибок"
# Форматируем топ активных пользователей
top_users_text = ""
for i, (uid, username, first_name, link_count) in enumerate(top_users, start=1):
display_name = f"@{username}" if username else (first_name or str(uid))
top_users_text += f" {i}. {display_name}{link_count}\n"
if not top_users_text:
top_users_text = " Пока никто не отправлял ссылки"
stat_message = ( stat_message = (
f"📊 Статистика бота:\n\n" f"📊 Статистика бота:\n\n"
f"👥 Всего пользователей: {total_users}\n" f"👥 Всего пользователей: {total_users}\n"
f"🔗 Отправляли ссылки: {users_sent_link}\n"
f"📹 Всего скачано видео: {total_downloads}\n\n" f"📹 Всего скачано видео: {total_downloads}\n\n"
f"🏆 Топ-5 активных пользователей:\n{top_users_text.strip()}\n\n"
f"❌ Ошибки по сервисам:\n{error_stats_text.strip()}" f"❌ Ошибки по сервисам:\n{error_stats_text.strip()}"
) )

17
bot.py
View file

@ -224,6 +224,9 @@ def init_database():
if 'locale' not in columns: if 'locale' not in columns:
cursor.execute("ALTER TABLE users ADD COLUMN locale TEXT DEFAULT 'en'") cursor.execute("ALTER TABLE users ADD COLUMN locale TEXT DEFAULT 'en'")
logger.info("Добавлена колонка locale в таблицу users") logger.info("Добавлена колонка locale в таблицу users")
if 'link_count' not in columns:
cursor.execute("ALTER TABLE users ADD COLUMN link_count INTEGER DEFAULT 0")
logger.info("Добавлена колонка link_count в таблицу users")
# Таблица статистики # Таблица статистики
cursor.execute(''' cursor.execute('''
@ -377,6 +380,18 @@ def add_user(chat_id: int, username: str = None, first_name: str = None, locale:
logger.error(f"Ошибка при добавлении пользователя: {e}") logger.error(f"Ошибка при добавлении пользователя: {e}")
def increment_user_link_count(chat_id: int):
"""Увеличивает счётчик отправленных пользователем ссылок"""
try:
conn = sqlite3.connect(str(DB_FILE))
cursor = conn.cursor()
cursor.execute('UPDATE users SET link_count = link_count + 1 WHERE chat_id = ?', (chat_id,))
conn.commit()
conn.close()
except Exception as e:
logger.error(f"Ошибка при увеличении счётчика ссылок: {e}")
# ============================================================================ # ============================================================================
# УТИЛИТЫ # УТИЛИТЫ
# ============================================================================ # ============================================================================
@ -1250,6 +1265,8 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(get_text(locale, 'error_vk_not_video')) await update.message.reply_text(get_text(locale, 'error_vk_not_video'))
return return
increment_user_link_count(chat_id)
# Отправляем сообщение о начале обработки # Отправляем сообщение о начале обработки
status_message = await update.message.reply_text(get_text(locale, 'processing')) status_message = await update.message.reply_text(get_text(locale, 'processing'))