add statistics
This commit is contained in:
parent
23de80f94d
commit
ceb62b408a
6 changed files with 318 additions and 3 deletions
|
|
@ -11,6 +11,7 @@ from telegram.ext import Application, CommandHandler, ContextTypes
|
||||||
|
|
||||||
from config import ADMINPANEL_TELEGRAM_BOT_TOKEN, DATABASE_PATH
|
from config import ADMINPANEL_TELEGRAM_BOT_TOKEN, DATABASE_PATH
|
||||||
from database import Database
|
from database import Database
|
||||||
|
from message_counters import MessageCounters
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
|
|
@ -24,6 +25,7 @@ class AdminBot:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.db = Database()
|
self.db = Database()
|
||||||
self.application = None
|
self.application = None
|
||||||
|
self.counters = MessageCounters()
|
||||||
|
|
||||||
async def send_notification(self, message: str):
|
async def send_notification(self, message: str):
|
||||||
"""Send notification to admin chat"""
|
"""Send notification to admin chat"""
|
||||||
|
|
@ -115,10 +117,30 @@ class AdminBot:
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
# Get message counters statistics
|
||||||
|
stats = self.counters.get_stats_summary()
|
||||||
|
|
||||||
|
# Filter out commands that should not be displayed
|
||||||
|
excluded_commands = {'lang', 'resetlang', 'start'}
|
||||||
|
filtered_stats = {
|
||||||
|
cmd: data for cmd, data in stats['by_command'].items()
|
||||||
|
if cmd not in excluded_commands
|
||||||
|
}
|
||||||
|
|
||||||
|
# Format message counters
|
||||||
|
counters_text = "\n".join([
|
||||||
|
f" • {cmd}: {data['total']} (сегодня: {data['today']})"
|
||||||
|
for cmd, data in sorted(filtered_stats.items())
|
||||||
|
])
|
||||||
|
|
||||||
message = (
|
message = (
|
||||||
f"📊 <b>Статистика базы данных</b>\n\n"
|
f"📊 <b>Статистика базы данных</b>\n\n"
|
||||||
f"👥 Пользователей Telegram: {users_count}\n"
|
f"👥 Пользователей Telegram: {users_count}\n"
|
||||||
f"🎮 Отслеживаемых игроков: {gamers_count}"
|
f"🎮 Отслеживаемых игроков: {gamers_count}\n\n"
|
||||||
|
f"📨 <b>Счетчики сообщений</b>\n\n"
|
||||||
|
f"Всего отправлено: <b>{stats['total_all_time']}</b>\n"
|
||||||
|
f"Сегодня отправлено: <b>{stats['total_today']}</b>\n\n"
|
||||||
|
f"<b>По командам:</b>\n{counters_text}"
|
||||||
)
|
)
|
||||||
await update.message.reply_text(message, parse_mode='HTML')
|
await update.message.reply_text(message, parse_mode='HTML')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ from lichess_api import LichessAPI
|
||||||
from formatters import StatsFormatter
|
from formatters import StatsFormatter
|
||||||
from i18n import t
|
from i18n import t
|
||||||
from admin_bot import get_admin_bot, init_admin_bot
|
from admin_bot import get_admin_bot, init_admin_bot
|
||||||
|
from message_counters import MessageCounters
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
|
|
@ -38,6 +39,7 @@ class LichessBot:
|
||||||
self.periodic_tasks = {} # Store periodic tasks
|
self.periodic_tasks = {} # Store periodic tasks
|
||||||
self.period_start_times = {} # Store start times for each gamer
|
self.period_start_times = {} # Store start times for each gamer
|
||||||
self.application = None # Will be set when application is created
|
self.application = None # Will be set when application is created
|
||||||
|
self.counters = MessageCounters() # Message counters
|
||||||
|
|
||||||
def get_user_language_from_update(self, update: Update) -> str:
|
def get_user_language_from_update(self, update: Update) -> str:
|
||||||
"""Always return English language"""
|
"""Always return English language"""
|
||||||
|
|
@ -65,9 +67,38 @@ class LichessBot:
|
||||||
# Start periodic task with user_id and gamer
|
# Start periodic task with user_id and gamer
|
||||||
await self.start_periodic_task(gamer, user_id, gamer['period_minutes'])
|
await self.start_periodic_task(gamer, user_id, gamer['period_minutes'])
|
||||||
logger.info(f"Started periodic task for {gamer['username']} (user {user_id}) with period {gamer['period_minutes']} minutes")
|
logger.info(f"Started periodic task for {gamer['username']} (user {user_id}) with period {gamer['period_minutes']} minutes")
|
||||||
|
|
||||||
|
# Start daily counter reset task
|
||||||
|
asyncio.create_task(self.daily_counter_reset_task())
|
||||||
|
logger.info("Started daily counter reset task")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error starting existing periodic tasks: {e}")
|
logger.error(f"Error starting existing periodic tasks: {e}")
|
||||||
|
|
||||||
|
async def daily_counter_reset_task(self):
|
||||||
|
"""Background task to reset daily counters at midnight"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# Calculate seconds until next midnight
|
||||||
|
now = datetime.now()
|
||||||
|
next_midnight = (now + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
seconds_until_midnight = (next_midnight - now).total_seconds()
|
||||||
|
|
||||||
|
logger.info(f"Daily counter reset task: waiting {seconds_until_midnight} seconds until next midnight")
|
||||||
|
await asyncio.sleep(seconds_until_midnight)
|
||||||
|
|
||||||
|
# Reset daily counters
|
||||||
|
self.counters._reset_daily_counters_if_needed()
|
||||||
|
logger.info("Daily counters reset at midnight")
|
||||||
|
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in daily counter reset task: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
# Wait 1 hour before retrying
|
||||||
|
await asyncio.sleep(3600)
|
||||||
|
|
||||||
async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Start command handler"""
|
"""Start command handler"""
|
||||||
|
|
@ -96,6 +127,7 @@ class LichessBot:
|
||||||
lang = self.get_user_language_from_update(update)
|
lang = self.get_user_language_from_update(update)
|
||||||
start_msg = t('start_message', lang)
|
start_msg = t('start_message', lang)
|
||||||
await update.message.reply_text(start_msg)
|
await update.message.reply_text(start_msg)
|
||||||
|
self.counters.increment('start')
|
||||||
|
|
||||||
async def start_and_addgamer(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def start_and_addgamer(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Start command that shows welcome message and starts addgamer conversation"""
|
"""Start command that shows welcome message and starts addgamer conversation"""
|
||||||
|
|
@ -121,6 +153,7 @@ class LichessBot:
|
||||||
try:
|
try:
|
||||||
await update.message.reply_text(t('addgamer_prompt', lang))
|
await update.message.reply_text(t('addgamer_prompt', lang))
|
||||||
logger.info(f"Addgamer prompt sent to user {update.effective_user.id}")
|
logger.info(f"Addgamer prompt sent to user {update.effective_user.id}")
|
||||||
|
self.counters.increment('addgamer')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error sending addgamer prompt: {e}")
|
logger.error(f"Error sending addgamer prompt: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
|
|
@ -131,6 +164,7 @@ class LichessBot:
|
||||||
"""Start addtoken command - token required"""
|
"""Start addtoken command - token required"""
|
||||||
lang = self.get_user_language_from_update(update)
|
lang = self.get_user_language_from_update(update)
|
||||||
await update.message.reply_text(t('addtoken_prompt', lang))
|
await update.message.reply_text(t('addtoken_prompt', lang))
|
||||||
|
self.counters.increment('addtoken')
|
||||||
return WAITING_FOR_TOKEN
|
return WAITING_FOR_TOKEN
|
||||||
|
|
||||||
async def handle_token(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def handle_token(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
|
@ -283,8 +317,11 @@ class LichessBot:
|
||||||
lang = self.get_user_language_from_update(update)
|
lang = self.get_user_language_from_update(update)
|
||||||
if not gamers:
|
if not gamers:
|
||||||
await update.message.reply_text(t('no_gamers', lang))
|
await update.message.reply_text(t('no_gamers', lang))
|
||||||
|
self.counters.increment('getgamers')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.counters.increment('getgamers')
|
||||||
|
|
||||||
# Show loading message
|
# Show loading message
|
||||||
loading_msg = await update.message.reply_text(t('loading_ratings', lang))
|
loading_msg = await update.message.reply_text(t('loading_ratings', lang))
|
||||||
|
|
||||||
|
|
@ -431,8 +468,11 @@ class LichessBot:
|
||||||
lang = self.get_user_language_from_update(update)
|
lang = self.get_user_language_from_update(update)
|
||||||
if not gamers:
|
if not gamers:
|
||||||
await update.message.reply_text(t('no_gamers_to_delete', lang))
|
await update.message.reply_text(t('no_gamers_to_delete', lang))
|
||||||
|
self.counters.increment('delgamer')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.counters.increment('delgamer')
|
||||||
|
|
||||||
# Show loading message
|
# Show loading message
|
||||||
loading_msg = await update.message.reply_text(t('loading_gamers', lang))
|
loading_msg = await update.message.reply_text(t('loading_gamers', lang))
|
||||||
|
|
||||||
|
|
@ -563,6 +603,14 @@ class LichessBot:
|
||||||
# Format and send response
|
# Format and send response
|
||||||
formatted_response = StatsFormatter.format_stats_response(data, username, period, lang)
|
formatted_response = StatsFormatter.format_stats_response(data, username, period, lang)
|
||||||
await update.message.reply_text(formatted_response)
|
await update.message.reply_text(formatted_response)
|
||||||
|
|
||||||
|
# Increment counter for the period command
|
||||||
|
if period == "today":
|
||||||
|
self.counters.increment('today')
|
||||||
|
elif period == "yesterday":
|
||||||
|
self.counters.increment('yesterday')
|
||||||
|
elif period == "week":
|
||||||
|
self.counters.increment('week')
|
||||||
|
|
||||||
async def today(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def today(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Today command"""
|
"""Today command"""
|
||||||
|
|
@ -603,6 +651,7 @@ class LichessBot:
|
||||||
t('select_period', lang, username=active_gamer['username']),
|
t('select_period', lang, username=active_gamer['username']),
|
||||||
reply_markup=reply_markup
|
reply_markup=reply_markup
|
||||||
)
|
)
|
||||||
|
self.counters.increment('setperiod')
|
||||||
|
|
||||||
async def select_period(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def select_period(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Handle period selection"""
|
"""Handle period selection"""
|
||||||
|
|
@ -668,6 +717,7 @@ class LichessBot:
|
||||||
f"Bot uses English language only."
|
f"Bot uses English language only."
|
||||||
)
|
)
|
||||||
await update.message.reply_text(message)
|
await update.message.reply_text(message)
|
||||||
|
self.counters.increment('lang')
|
||||||
else:
|
else:
|
||||||
await update.message.reply_text("❌ Failed to get user information")
|
await update.message.reply_text("❌ Failed to get user information")
|
||||||
|
|
||||||
|
|
@ -689,6 +739,7 @@ class LichessBot:
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"✅ Language reset! Bot uses English language only."
|
"✅ Language reset! Bot uses English language only."
|
||||||
)
|
)
|
||||||
|
self.counters.increment('resetlang')
|
||||||
else:
|
else:
|
||||||
await update.message.reply_text("❌ Failed to get user information")
|
await update.message.reply_text("❌ Failed to get user information")
|
||||||
|
|
||||||
|
|
@ -799,6 +850,8 @@ class LichessBot:
|
||||||
logger.info(f"Sent periodic notification for {gamer['username']} to user {user_id}")
|
logger.info(f"Sent periodic notification for {gamer['username']} to user {user_id}")
|
||||||
# Обновляем время начала только после успешной отправки уведомления
|
# Обновляем время начала только после успешной отправки уведомления
|
||||||
self.period_start_times[task_key] = now
|
self.period_start_times[task_key] = now
|
||||||
|
# Increment periodic notification counter
|
||||||
|
self.counters.increment('periodic_notification')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send notification to user {user_id}: {e}")
|
logger.error(f"Failed to send notification to user {user_id}: {e}")
|
||||||
# Не обновляем время начала при ошибке отправки
|
# Не обновляем время начала при ошибке отправки
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,26 @@ class Database:
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
# Create message_counters table for tracking sent messages
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS message_counters (
|
||||||
|
command TEXT PRIMARY KEY,
|
||||||
|
total_count INTEGER DEFAULT 0,
|
||||||
|
today_count INTEGER DEFAULT 0,
|
||||||
|
last_reset_date DATE DEFAULT CURRENT_DATE
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Initialize counters for all commands
|
||||||
|
commands = ['start', 'addgamer', 'addtoken', 'getgamers', 'delgamer',
|
||||||
|
'today', 'yesterday', 'week', 'setperiod', 'lang', 'resetlang',
|
||||||
|
'periodic_notification']
|
||||||
|
for cmd in commands:
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO message_counters (command, total_count, today_count, last_reset_date)
|
||||||
|
VALUES (?, 0, 0, CURRENT_DATE)
|
||||||
|
''', (cmd,))
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
# Migrate tokens from gamers to user_gamers if needed
|
# Migrate tokens from gamers to user_gamers if needed
|
||||||
|
|
|
||||||
136
LichessClientTG_bot/message_counters.py
Normal file
136
LichessClientTG_bot/message_counters.py
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
"""
|
||||||
|
Module for tracking message counters in Telegram bot
|
||||||
|
"""
|
||||||
|
import sqlite3
|
||||||
|
import logging
|
||||||
|
from datetime import datetime, date
|
||||||
|
from typing import Dict, Any
|
||||||
|
from config import DATABASE_PATH
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageCounters:
|
||||||
|
"""Class for managing message counters"""
|
||||||
|
|
||||||
|
def __init__(self, db_path: str = DATABASE_PATH):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._ensure_counters_exist()
|
||||||
|
self._reset_daily_counters_if_needed()
|
||||||
|
|
||||||
|
def _ensure_counters_exist(self):
|
||||||
|
"""Ensure all command counters exist in database"""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
commands = ['start', 'addgamer', 'addtoken', 'getgamers', 'delgamer',
|
||||||
|
'today', 'yesterday', 'week', 'setperiod', 'lang', 'resetlang',
|
||||||
|
'periodic_notification']
|
||||||
|
for cmd in commands:
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO message_counters (command, total_count, today_count, last_reset_date)
|
||||||
|
VALUES (?, 0, 0, CURRENT_DATE)
|
||||||
|
''', (cmd,))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def _reset_daily_counters_if_needed(self):
|
||||||
|
"""Reset daily counters if it's a new day"""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
today = date.today()
|
||||||
|
|
||||||
|
# Check all counters and reset if needed
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT command, last_reset_date FROM message_counters
|
||||||
|
''')
|
||||||
|
|
||||||
|
for row in cursor.fetchall():
|
||||||
|
cmd, last_reset = row[0], row[1]
|
||||||
|
if last_reset:
|
||||||
|
try:
|
||||||
|
last_reset_date = datetime.strptime(last_reset, '%Y-%m-%d').date()
|
||||||
|
if last_reset_date < today:
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE message_counters
|
||||||
|
SET today_count = 0, last_reset_date = ?
|
||||||
|
WHERE command = ?
|
||||||
|
''', (today.isoformat(), cmd))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
# Invalid date format, reset it
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE message_counters
|
||||||
|
SET today_count = 0, last_reset_date = ?
|
||||||
|
WHERE command = ?
|
||||||
|
''', (today.isoformat(), cmd))
|
||||||
|
else:
|
||||||
|
# No reset date, set it to today
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE message_counters
|
||||||
|
SET last_reset_date = ?
|
||||||
|
WHERE command = ?
|
||||||
|
''', (today.isoformat(), cmd))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def increment(self, command: str):
|
||||||
|
"""Increment counter for a command"""
|
||||||
|
self._reset_daily_counters_if_needed()
|
||||||
|
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Ensure counter exists
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO message_counters (command, total_count, today_count, last_reset_date)
|
||||||
|
VALUES (?, 0, 0, CURRENT_DATE)
|
||||||
|
''', (command,))
|
||||||
|
|
||||||
|
# Increment both counters
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE message_counters
|
||||||
|
SET total_count = total_count + 1,
|
||||||
|
today_count = today_count + 1
|
||||||
|
WHERE command = ?
|
||||||
|
''', (command,))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_all_stats(self) -> Dict[str, Any]:
|
||||||
|
"""Get all statistics"""
|
||||||
|
self._reset_daily_counters_if_needed()
|
||||||
|
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT command, total_count, today_count
|
||||||
|
FROM message_counters
|
||||||
|
ORDER BY command
|
||||||
|
''')
|
||||||
|
|
||||||
|
stats = {
|
||||||
|
'by_command': {},
|
||||||
|
'total_all_time': 0,
|
||||||
|
'total_today': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in cursor.fetchall():
|
||||||
|
cmd, total, today = row[0], row[1], row[2]
|
||||||
|
stats['by_command'][cmd] = {
|
||||||
|
'total': total,
|
||||||
|
'today': today
|
||||||
|
}
|
||||||
|
stats['total_all_time'] += total
|
||||||
|
stats['total_today'] += today
|
||||||
|
|
||||||
|
return stats
|
||||||
|
|
||||||
|
def get_stats_summary(self) -> Dict[str, Any]:
|
||||||
|
"""Get summary statistics for display"""
|
||||||
|
stats = self.get_all_stats()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'total_all_time': stats['total_all_time'],
|
||||||
|
'total_today': stats['total_today'],
|
||||||
|
'by_command': stats['by_command']
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
from flask import Flask, jsonify, render_template
|
from flask import Flask, jsonify, render_template
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
import sys
|
||||||
|
import os
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
CORS(app)
|
CORS(app)
|
||||||
|
|
@ -9,6 +11,14 @@ CORS(app)
|
||||||
# Путь к базе данных бота
|
# Путь к базе данных бота
|
||||||
DB_PATH = "/app/data/lichess_bot.db"
|
DB_PATH = "/app/data/lichess_bot.db"
|
||||||
|
|
||||||
|
# Add parent directory to path to import message_counters
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'LichessClientTG_bot'))
|
||||||
|
try:
|
||||||
|
from message_counters import MessageCounters
|
||||||
|
except ImportError:
|
||||||
|
# Fallback if import fails
|
||||||
|
MessageCounters = None
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
"""Главная страница"""
|
"""Главная страница"""
|
||||||
|
|
@ -60,11 +70,32 @@ def get_users():
|
||||||
''')
|
''')
|
||||||
total_gamers = cursor.fetchone()[0]
|
total_gamers = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# Get message counters statistics
|
||||||
|
message_stats = {}
|
||||||
|
if MessageCounters:
|
||||||
|
try:
|
||||||
|
counters = MessageCounters(db_path=DB_PATH)
|
||||||
|
message_stats = counters.get_stats_summary()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting message counters: {e}")
|
||||||
|
message_stats = {
|
||||||
|
'total_all_time': 0,
|
||||||
|
'total_today': 0,
|
||||||
|
'by_command': {}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
message_stats = {
|
||||||
|
'total_all_time': 0,
|
||||||
|
'total_today': 0,
|
||||||
|
'by_command': {}
|
||||||
|
}
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'users': users,
|
'users': users,
|
||||||
'total_users': len(users),
|
'total_users': len(users),
|
||||||
'total_gamers': total_gamers
|
'total_gamers': total_gamers,
|
||||||
|
'message_stats': message_stats
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,20 @@
|
||||||
Кол-во игроков: <strong id="total-gamers">0</strong>
|
Кол-во игроков: <strong id="total-gamers">0</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="stats" style="margin-top: 15px; padding: 15px; background: #f5f5f5; border-radius: 8px;">
|
||||||
|
<h3 style="margin-top: 0; margin-bottom: 10px; font-size: 16px;">📨 Счетчики сообщений</h3>
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
Всего отправлено: <strong id="total-messages-all">0</strong>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
Сегодня отправлено: <strong id="total-messages-today">0</strong>
|
||||||
|
</div>
|
||||||
|
<div id="message-stats-by-command" style="margin-top: 10px; font-size: 12px; color: #666;">
|
||||||
|
<div style="margin-bottom: 5px;"><strong>По командам:</strong></div>
|
||||||
|
<div id="command-stats-list"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<input type="text" id="search-input" placeholder="Поиск по имени или никнейму...">
|
<input type="text" id="search-input" placeholder="Поиск по имени или никнейму...">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -292,6 +306,45 @@
|
||||||
users = data.users;
|
users = data.users;
|
||||||
document.getElementById('total-users').textContent = data.total_users;
|
document.getElementById('total-users').textContent = data.total_users;
|
||||||
document.getElementById('total-gamers').textContent = data.total_gamers;
|
document.getElementById('total-gamers').textContent = data.total_gamers;
|
||||||
|
|
||||||
|
// Update message counters
|
||||||
|
if (data.message_stats) {
|
||||||
|
document.getElementById('total-messages-all').textContent = data.message_stats.total_all_time || 0;
|
||||||
|
document.getElementById('total-messages-today').textContent = data.message_stats.total_today || 0;
|
||||||
|
|
||||||
|
// Render command stats
|
||||||
|
const commandStatsList = document.getElementById('command-stats-list');
|
||||||
|
if (data.message_stats.by_command && Object.keys(data.message_stats.by_command).length > 0) {
|
||||||
|
const commandNames = {
|
||||||
|
'addgamer': 'Add Gamer',
|
||||||
|
'addtoken': 'Add Token',
|
||||||
|
'getgamers': 'Get Gamers',
|
||||||
|
'delgamer': 'Del Gamer',
|
||||||
|
'today': 'Today',
|
||||||
|
'yesterday': 'Yesterday',
|
||||||
|
'week': 'Week',
|
||||||
|
'setperiod': 'Set Period',
|
||||||
|
'periodic_notification': 'Periodic Notifications'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Filter out excluded commands
|
||||||
|
const excludedCommands = ['start', 'lang', 'resetlang'];
|
||||||
|
const filteredCommands = Object.entries(data.message_stats.by_command)
|
||||||
|
.filter(([cmd]) => !excludedCommands.includes(cmd));
|
||||||
|
|
||||||
|
commandStatsList.innerHTML = filteredCommands
|
||||||
|
.sort((a, b) => b[1].total - a[1].total)
|
||||||
|
.map(([cmd, stats]) => {
|
||||||
|
const cmdName = commandNames[cmd] || cmd;
|
||||||
|
return `<div style="margin-bottom: 3px;">
|
||||||
|
${cmdName}: <strong>${stats.total}</strong> (сегодня: <strong>${stats.today}</strong>)
|
||||||
|
</div>`;
|
||||||
|
}).join('');
|
||||||
|
} else {
|
||||||
|
commandStatsList.innerHTML = '<div style="color: #999;">Нет данных</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
filteredUsers = users;
|
filteredUsers = users;
|
||||||
renderUsers();
|
renderUsers();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue