Добавлены функции для сохранения и обновления данных пользователей в классе OracleDatabase и обновлены обработчики в main.py:
- Реализован метод save_user для сохранения или обновления информации о пользователе. - Добавлены вызовы save_user в обработчики команд и событий для отслеживания взаимодействий пользователей. - Добавлен обработчик admin_stats для получения статистики пользователей по запросу администратора.
This commit is contained in:
parent
b1ba974b44
commit
3e175ff913
200
README_USER_TRACKING.md
Normal file
200
README_USER_TRACKING.md
Normal file
@ -0,0 +1,200 @@
|
||||
# 📊 Система учета пользователей SalvageDB Bot
|
||||
|
||||
## 🎯 Обзор
|
||||
|
||||
Данная система реализует полный учет пользователей Telegram бота в соответствии с требованиями KYC (Know Your Customer) для сервисов, принимающих платежи.
|
||||
|
||||
## 📝 Что отслеживается
|
||||
|
||||
### Данные пользователя Telegram:
|
||||
- **ID пользователя** (уникальный 64-bit идентификатор)
|
||||
- **Имя и фамилия** пользователя
|
||||
- **Username** (если есть)
|
||||
- **Код языка** (IETF language tag)
|
||||
- **Статус Premium** в Telegram
|
||||
- **Флаг бота** (для различения ботов и пользователей)
|
||||
|
||||
### Данные взаимодействий:
|
||||
- **Дата первого взаимодействия**
|
||||
- **Дата последнего взаимодействия**
|
||||
- **Количество взаимодействий**
|
||||
- **Источник регистрации** (какая команда/кнопка)
|
||||
|
||||
### Финансовые данные:
|
||||
- **Общая сумма платежей** в Telegram Stars
|
||||
- **Количество успешных платежей**
|
||||
- **История транзакций**
|
||||
|
||||
## 🗄️ Структура базы данных
|
||||
|
||||
### Таблица `bot_users`
|
||||
|
||||
```sql
|
||||
CREATE TABLE bot_users (
|
||||
id NUMBER(19) PRIMARY KEY, -- Telegram user ID
|
||||
first_name VARCHAR2(256) NOT NULL, -- Имя пользователя
|
||||
last_name VARCHAR2(256), -- Фамилия (опционально)
|
||||
username VARCHAR2(128), -- Username (опционально)
|
||||
language_code VARCHAR2(10), -- Код языка
|
||||
is_bot NUMBER(1) DEFAULT 0 NOT NULL, -- Флаг бота
|
||||
is_premium NUMBER(1) DEFAULT 0, -- Telegram Premium
|
||||
added_to_attachment_menu NUMBER(1) DEFAULT 0, -- Добавлен в меню вложений
|
||||
first_interaction_date DATE DEFAULT SYSDATE, -- Первое взаимодействие
|
||||
last_interaction_date DATE DEFAULT SYSDATE, -- Последнее взаимодействие
|
||||
interaction_count NUMBER(10) DEFAULT 1, -- Количество взаимодействий
|
||||
is_active NUMBER(1) DEFAULT 1, -- Активен ли пользователь
|
||||
is_blocked NUMBER(1) DEFAULT 0, -- Заблокирован ли
|
||||
registration_source VARCHAR2(50) DEFAULT 'bot', -- Источник регистрации
|
||||
total_payments NUMBER(10,2) DEFAULT 0, -- Сумма платежей
|
||||
successful_payments_count NUMBER(8) DEFAULT 0, -- Количество платежей
|
||||
created_at DATE DEFAULT SYSDATE, -- Дата создания записи
|
||||
updated_at DATE DEFAULT SYSDATE -- Дата обновления
|
||||
);
|
||||
```
|
||||
|
||||
## 🚀 Развертывание
|
||||
|
||||
### 1. Создание таблицы в Oracle
|
||||
|
||||
```bash
|
||||
# Выполните SQL скрипт для создания таблицы
|
||||
sqlplus username/password@database @create_users_table.sql
|
||||
```
|
||||
|
||||
### 2. Настройка переменных окружения
|
||||
|
||||
Добавьте в ваш `.env` файл:
|
||||
|
||||
```env
|
||||
# Настройки базы данных (уже должны быть)
|
||||
db_user=your_oracle_user
|
||||
db_password=your_oracle_password
|
||||
db_dsn=your_oracle_dsn
|
||||
|
||||
# ID администратора для команды /admin_stats
|
||||
ADMIN_USER_ID=123456789
|
||||
```
|
||||
|
||||
### 3. Обновление кода
|
||||
|
||||
Код уже интегрирован в основные файлы:
|
||||
- `main.py` - обработчики с сохранением данных пользователей
|
||||
- `db.py` - методы для работы с пользователями
|
||||
- `create_users_table.sql` - SQL скрипт для создания таблицы
|
||||
|
||||
## 📈 Использование
|
||||
|
||||
### Автоматическое отслеживание
|
||||
|
||||
Система автоматически сохраняет данные пользователя при:
|
||||
- Использовании команды `/start`
|
||||
- Нажатии любых кнопок
|
||||
- Обработке VIN
|
||||
- Совершении платежей
|
||||
|
||||
### Команды администратора
|
||||
|
||||
```
|
||||
/admin_stats - Просмотр статистики пользователей
|
||||
```
|
||||
|
||||
Пример вывода:
|
||||
```
|
||||
📊 Bot Users Statistics
|
||||
|
||||
👥 Users Overview:
|
||||
• Total users: 1,234
|
||||
• Premium users: 156
|
||||
|
||||
💰 Revenue:
|
||||
• Total revenue: 567 ⭐️
|
||||
• Total transactions: 445
|
||||
|
||||
📈 Activity:
|
||||
• Active last 24h: 89
|
||||
• Active last week: 234
|
||||
```
|
||||
|
||||
## 🔧 API методы
|
||||
|
||||
### Сохранение пользователя
|
||||
```python
|
||||
await db.save_user(user: User, interaction_source: str = "bot") -> bool
|
||||
```
|
||||
|
||||
### Обновление платежных данных
|
||||
```python
|
||||
await db.update_user_payment(user_id: int, payment_amount: float) -> bool
|
||||
```
|
||||
|
||||
### Получение статистики пользователя
|
||||
```python
|
||||
await db.get_user_stats(user_id: int) -> Optional[dict]
|
||||
```
|
||||
|
||||
### Общая статистика
|
||||
```python
|
||||
await db.get_users_summary() -> dict
|
||||
```
|
||||
|
||||
## 🛡️ Безопасность и соответствие
|
||||
|
||||
### GDPR/Персональные данные:
|
||||
- Сохраняются только данные, предоставляемые Telegram API
|
||||
- Данные используются только для предоставления сервиса
|
||||
- Пользователи могут быть помечены как неактивные
|
||||
|
||||
### Финансовая отчетность:
|
||||
- Все платежи логируются с timestamp
|
||||
- Связь платежей с пользователями для аудита
|
||||
- Возможность генерации отчетов
|
||||
|
||||
### Мониторинг:
|
||||
- Отслеживание активности пользователей
|
||||
- Выявление подозрительной активности
|
||||
- Статистика для бизнес-аналитики
|
||||
|
||||
## 📊 Полезные запросы
|
||||
|
||||
### Топ пользователей по платежам:
|
||||
```sql
|
||||
SELECT first_name, last_name, username, total_payments, successful_payments_count
|
||||
FROM bot_users
|
||||
WHERE total_payments > 0
|
||||
ORDER BY total_payments DESC
|
||||
FETCH FIRST 10 ROWS ONLY;
|
||||
```
|
||||
|
||||
### Активные пользователи за месяц:
|
||||
```sql
|
||||
SELECT COUNT(*) as active_users
|
||||
FROM bot_users
|
||||
WHERE last_interaction_date >= SYSDATE - 30
|
||||
AND is_active = 1;
|
||||
```
|
||||
|
||||
### Конверсия в платящих пользователей:
|
||||
```sql
|
||||
SELECT
|
||||
COUNT(*) as total_users,
|
||||
COUNT(CASE WHEN successful_payments_count > 0 THEN 1 END) as paying_users,
|
||||
ROUND(COUNT(CASE WHEN successful_payments_count > 0 THEN 1 END) * 100.0 / COUNT(*), 2) as conversion_rate
|
||||
FROM bot_users
|
||||
WHERE is_active = 1;
|
||||
```
|
||||
|
||||
## 🔍 Мониторинг и алерты
|
||||
|
||||
Рекомендуется настроить мониторинг для:
|
||||
- Необычно высокой активности от одного пользователя
|
||||
- Большого количества возвратов платежей
|
||||
- Резкого роста/падения количества новых пользователей
|
||||
|
||||
## 📞 Поддержка
|
||||
|
||||
При возникновении проблем проверьте:
|
||||
1. Права доступа к таблице `bot_users`
|
||||
2. Корректность переменных окружения
|
||||
3. Логи приложения для ошибок базы данных
|
||||
|
||||
Все операции с базой данных логируются для отладки.
|
||||
72
create_users_table.sql
Normal file
72
create_users_table.sql
Normal file
@ -0,0 +1,72 @@
|
||||
-- SQL скрипт для создания таблицы пользователей
|
||||
-- Сохраняет все данные о пользователе, передаваемые Telegram через aiogram
|
||||
|
||||
CREATE TABLE bot_users (
|
||||
-- Основные данные пользователя Telegram
|
||||
id NUMBER(19) PRIMARY KEY, -- Telegram user ID (64-bit integer)
|
||||
first_name VARCHAR2(256) NOT NULL, -- Имя пользователя
|
||||
last_name VARCHAR2(256), -- Фамилия пользователя (опционально)
|
||||
username VARCHAR2(128), -- Username (опционально)
|
||||
language_code VARCHAR2(10), -- Код языка (IETF language tag)
|
||||
|
||||
-- Флаги статуса пользователя
|
||||
is_bot NUMBER(1) DEFAULT 0 NOT NULL, -- Является ли пользователь ботом
|
||||
is_premium NUMBER(1) DEFAULT 0, -- Telegram Premium пользователь
|
||||
added_to_attachment_menu NUMBER(1) DEFAULT 0, -- Добавил ли бота в меню вложений
|
||||
|
||||
-- Системные поля для аудита
|
||||
first_interaction_date DATE DEFAULT SYSDATE NOT NULL, -- Дата первого взаимодействия
|
||||
last_interaction_date DATE DEFAULT SYSDATE NOT NULL, -- Дата последнего взаимодействия
|
||||
interaction_count NUMBER(10) DEFAULT 1 NOT NULL, -- Количество взаимодействий
|
||||
|
||||
-- Дополнительные бизнес-поля
|
||||
is_active NUMBER(1) DEFAULT 1 NOT NULL, -- Активен ли пользователь
|
||||
is_blocked NUMBER(1) DEFAULT 0 NOT NULL, -- Заблокирован ли пользователь
|
||||
registration_source VARCHAR2(50) DEFAULT 'bot', -- Источник регистрации
|
||||
|
||||
-- Финансовые данные
|
||||
total_payments NUMBER(10,2) DEFAULT 0, -- Общая сумма платежей в звездах
|
||||
successful_payments_count NUMBER(8) DEFAULT 0, -- Количество успешных платежей
|
||||
|
||||
-- Системные поля
|
||||
created_at DATE DEFAULT SYSDATE NOT NULL,
|
||||
updated_at DATE DEFAULT SYSDATE NOT NULL
|
||||
);
|
||||
|
||||
-- Создание индексов для оптимизации поиска
|
||||
CREATE INDEX idx_bot_users_username ON bot_users(username);
|
||||
CREATE INDEX idx_bot_users_language ON bot_users(language_code);
|
||||
CREATE INDEX idx_bot_users_last_interaction ON bot_users(last_interaction_date);
|
||||
CREATE INDEX idx_bot_users_is_premium ON bot_users(is_premium);
|
||||
CREATE INDEX idx_bot_users_created_at ON bot_users(created_at);
|
||||
|
||||
-- Создание комментариев к таблице и полям
|
||||
COMMENT ON TABLE bot_users IS 'Таблица пользователей Telegram бота с полными данными KYC';
|
||||
COMMENT ON COLUMN bot_users.id IS 'Уникальный идентификатор пользователя Telegram (64-bit)';
|
||||
COMMENT ON COLUMN bot_users.first_name IS 'Имя пользователя в Telegram';
|
||||
COMMENT ON COLUMN bot_users.last_name IS 'Фамилия пользователя в Telegram (опционально)';
|
||||
COMMENT ON COLUMN bot_users.username IS 'Username пользователя в Telegram (опционально)';
|
||||
COMMENT ON COLUMN bot_users.language_code IS 'Код языка пользователя (IETF language tag)';
|
||||
COMMENT ON COLUMN bot_users.is_bot IS 'Флаг: является ли пользователь ботом (0/1)';
|
||||
COMMENT ON COLUMN bot_users.is_premium IS 'Флаг: Telegram Premium пользователь (0/1)';
|
||||
COMMENT ON COLUMN bot_users.added_to_attachment_menu IS 'Флаг: добавил ли бота в меню вложений (0/1)';
|
||||
COMMENT ON COLUMN bot_users.first_interaction_date IS 'Дата первого взаимодействия с ботом';
|
||||
COMMENT ON COLUMN bot_users.last_interaction_date IS 'Дата последнего взаимодействия с ботом';
|
||||
COMMENT ON COLUMN bot_users.interaction_count IS 'Общее количество взаимодействий с ботом';
|
||||
COMMENT ON COLUMN bot_users.is_active IS 'Флаг: активен ли пользователь (0/1)';
|
||||
COMMENT ON COLUMN bot_users.is_blocked IS 'Флаг: заблокирован ли пользователь (0/1)';
|
||||
COMMENT ON COLUMN bot_users.registration_source IS 'Источник регистрации пользователя';
|
||||
COMMENT ON COLUMN bot_users.total_payments IS 'Общая сумма платежей пользователя в Telegram Stars';
|
||||
COMMENT ON COLUMN bot_users.successful_payments_count IS 'Количество успешных платежей';
|
||||
|
||||
-- Создание триггера для автоматического обновления updated_at
|
||||
CREATE OR REPLACE TRIGGER trg_bot_users_updated_at
|
||||
BEFORE UPDATE ON bot_users
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
:NEW.updated_at := SYSDATE;
|
||||
END;
|
||||
/
|
||||
|
||||
-- Создание последовательности для системных нужд (если потребуется)
|
||||
CREATE SEQUENCE seq_bot_users_internal START WITH 1 INCREMENT BY 1 NOCACHE;
|
||||
193
db.py
193
db.py
@ -1,7 +1,8 @@
|
||||
# db.py
|
||||
import oracledb
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
# import logging
|
||||
from aiogram.types import User
|
||||
|
||||
|
||||
class OracleDatabase:
|
||||
@ -122,3 +123,193 @@ class OracleDatabase:
|
||||
return detailed_info
|
||||
import asyncio
|
||||
return await asyncio.to_thread(_query)
|
||||
|
||||
async def save_user(self, user: User, interaction_source: str = "bot") -> bool:
|
||||
"""
|
||||
Сохраняет или обновляет данные пользователя в базе данных
|
||||
При первом взаимодействии создает запись, при последующих - обновляет
|
||||
"""
|
||||
def _save_user():
|
||||
with self._pool.acquire() as conn:
|
||||
with conn.cursor() as cur:
|
||||
# Проверяем, существует ли пользователь
|
||||
cur.execute("SELECT id FROM bot_users WHERE id = :user_id", {"user_id": user.id})
|
||||
existing_user = cur.fetchone()
|
||||
|
||||
if existing_user:
|
||||
# Обновляем существующего пользователя
|
||||
update_query = """
|
||||
UPDATE bot_users SET
|
||||
first_name = :first_name,
|
||||
last_name = :last_name,
|
||||
username = :username,
|
||||
language_code = :language_code,
|
||||
is_premium = :is_premium,
|
||||
added_to_attachment_menu = :added_to_attachment_menu,
|
||||
last_interaction_date = SYSDATE,
|
||||
interaction_count = interaction_count + 1
|
||||
WHERE id = :user_id
|
||||
"""
|
||||
|
||||
params = {
|
||||
"user_id": user.id,
|
||||
"first_name": user.first_name,
|
||||
"last_name": user.last_name,
|
||||
"username": user.username,
|
||||
"language_code": user.language_code,
|
||||
"is_premium": 1 if user.is_premium else 0,
|
||||
"added_to_attachment_menu": 1 if user.added_to_attachment_menu else 0
|
||||
}
|
||||
else:
|
||||
# Создаем нового пользователя
|
||||
insert_query = """
|
||||
INSERT INTO bot_users (
|
||||
id, first_name, last_name, username, language_code,
|
||||
is_bot, is_premium, added_to_attachment_menu,
|
||||
registration_source, first_interaction_date,
|
||||
last_interaction_date, interaction_count
|
||||
) VALUES (
|
||||
:user_id, :first_name, :last_name, :username, :language_code,
|
||||
:is_bot, :is_premium, :added_to_attachment_menu,
|
||||
:registration_source, SYSDATE, SYSDATE, 1
|
||||
)
|
||||
"""
|
||||
|
||||
params = {
|
||||
"user_id": user.id,
|
||||
"first_name": user.first_name,
|
||||
"last_name": user.last_name,
|
||||
"username": user.username,
|
||||
"language_code": user.language_code,
|
||||
"is_bot": 1 if user.is_bot else 0,
|
||||
"is_premium": 1 if user.is_premium else 0,
|
||||
"added_to_attachment_menu": 1 if user.added_to_attachment_menu else 0,
|
||||
"registration_source": interaction_source
|
||||
}
|
||||
cur.execute(insert_query, params)
|
||||
conn.commit()
|
||||
return True
|
||||
|
||||
cur.execute(update_query, params)
|
||||
conn.commit()
|
||||
return True
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(None, _save_user)
|
||||
except Exception as e:
|
||||
print(f"Error saving user {user.id}: {e}")
|
||||
return False
|
||||
|
||||
async def update_user_payment(self, user_id: int, payment_amount: float) -> bool:
|
||||
"""
|
||||
Обновляет данные о платежах пользователя
|
||||
"""
|
||||
def _update_payment():
|
||||
with self._pool.acquire() as conn:
|
||||
with conn.cursor() as cur:
|
||||
update_query = """
|
||||
UPDATE bot_users SET
|
||||
total_payments = total_payments + :amount,
|
||||
successful_payments_count = successful_payments_count + 1,
|
||||
last_interaction_date = SYSDATE
|
||||
WHERE id = :user_id
|
||||
"""
|
||||
|
||||
cur.execute(update_query, {
|
||||
"user_id": user_id,
|
||||
"amount": payment_amount
|
||||
})
|
||||
conn.commit()
|
||||
return cur.rowcount > 0
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(None, _update_payment)
|
||||
except Exception as e:
|
||||
print(f"Error updating payment for user {user_id}: {e}")
|
||||
return False
|
||||
|
||||
async def get_user_stats(self, user_id: int) -> Optional[dict]:
|
||||
"""
|
||||
Получает статистику пользователя из базы данных
|
||||
"""
|
||||
def _get_stats():
|
||||
with self._pool.acquire() as conn:
|
||||
with conn.cursor() as cur:
|
||||
query = """
|
||||
SELECT
|
||||
first_name, last_name, username, language_code,
|
||||
is_premium, interaction_count, total_payments,
|
||||
successful_payments_count, first_interaction_date,
|
||||
last_interaction_date
|
||||
FROM bot_users
|
||||
WHERE id = :user_id
|
||||
"""
|
||||
|
||||
cur.execute(query, {"user_id": user_id})
|
||||
result = cur.fetchone()
|
||||
|
||||
if result:
|
||||
return {
|
||||
"first_name": result[0],
|
||||
"last_name": result[1],
|
||||
"username": result[2],
|
||||
"language_code": result[3],
|
||||
"is_premium": bool(result[4]),
|
||||
"interaction_count": result[5],
|
||||
"total_payments": float(result[6]) if result[6] else 0.0,
|
||||
"successful_payments_count": result[7],
|
||||
"first_interaction_date": result[8],
|
||||
"last_interaction_date": result[9]
|
||||
}
|
||||
return None
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(None, _get_stats)
|
||||
except Exception as e:
|
||||
print(f"Error getting stats for user {user_id}: {e}")
|
||||
return None
|
||||
|
||||
async def get_users_summary(self) -> dict:
|
||||
"""
|
||||
Получает общую статистику по пользователям
|
||||
"""
|
||||
def _get_summary():
|
||||
with self._pool.acquire() as conn:
|
||||
with conn.cursor() as cur:
|
||||
summary_query = """
|
||||
SELECT
|
||||
COUNT(*) as total_users,
|
||||
COUNT(CASE WHEN is_premium = 1 THEN 1 END) as premium_users,
|
||||
SUM(total_payments) as total_revenue,
|
||||
SUM(successful_payments_count) as total_transactions,
|
||||
COUNT(CASE WHEN last_interaction_date >= SYSDATE - 1 THEN 1 END) as active_last_24h,
|
||||
COUNT(CASE WHEN last_interaction_date >= SYSDATE - 7 THEN 1 END) as active_last_week
|
||||
FROM bot_users
|
||||
WHERE is_active = 1
|
||||
"""
|
||||
|
||||
cur.execute(summary_query)
|
||||
result = cur.fetchone()
|
||||
|
||||
return {
|
||||
"total_users": result[0] or 0,
|
||||
"premium_users": result[1] or 0,
|
||||
"total_revenue": float(result[2]) if result[2] else 0.0,
|
||||
"total_transactions": result[3] or 0,
|
||||
"active_last_24h": result[4] or 0,
|
||||
"active_last_week": result[5] or 0
|
||||
}
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(None, _get_summary)
|
||||
except Exception as e:
|
||||
print(f"Error getting users summary: {e}")
|
||||
return {}
|
||||
|
||||
76
main.py
76
main.py
@ -1,7 +1,8 @@
|
||||
import asyncio
|
||||
from os import getenv
|
||||
import logging
|
||||
# import json
|
||||
from datetime import datetime
|
||||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, LabeledPrice, PreCheckoutQuery
|
||||
@ -37,7 +38,10 @@ class VinStates(StatesGroup):
|
||||
|
||||
# Command handler
|
||||
@dp.message(Command("start"))
|
||||
async def command_start_handler(message: Message) -> None:
|
||||
async def command_start_handler(message: Message, db: OracleDatabase) -> None:
|
||||
# Сохраняем данные пользователя при каждом взаимодействии
|
||||
await db.save_user(message.from_user, "start_command")
|
||||
|
||||
welcome_text = (
|
||||
"Welcome to SalvagedbBot — your trusted assistant for vehicle history checks via VIN!\n\n"
|
||||
"🔍 What You Can Discover:\n\n"
|
||||
@ -60,26 +64,34 @@ async def command_start_handler(message: Message) -> None:
|
||||
|
||||
|
||||
@dp.callback_query(lambda c: c.data == "decode_vin")
|
||||
async def decode_vin_callback(callback: CallbackQuery, state: FSMContext):
|
||||
async def decode_vin_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase):
|
||||
# Сохраняем данные пользователя при нажатии кнопки
|
||||
await db.save_user(callback.from_user, "decode_vin_button")
|
||||
|
||||
await callback.message.answer("Please enter the vehicle VIN.")
|
||||
await state.set_state(VinStates.waiting_for_vin)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@dp.callback_query(lambda c: c.data == "main_menu")
|
||||
async def main_menu_callback(callback: CallbackQuery, state: FSMContext):
|
||||
async def main_menu_callback(callback: CallbackQuery, state: FSMContext, db: OracleDatabase):
|
||||
# Сохраняем данные пользователя при возврате в главное меню
|
||||
await db.save_user(callback.from_user, "main_menu_button")
|
||||
|
||||
await state.clear()
|
||||
await command_start_handler(callback.message)
|
||||
await command_start_handler(callback.message, db)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@dp.callback_query(lambda c: c.data and c.data.startswith("pay_detailed_info:"))
|
||||
async def pay_detailed_info_callback(callback: CallbackQuery):
|
||||
async def pay_detailed_info_callback(callback: CallbackQuery, db: OracleDatabase):
|
||||
# Сохраняем данные пользователя при инициации платежа
|
||||
await db.save_user(callback.from_user, "payment_initiation")
|
||||
|
||||
# Extract VIN from callback data
|
||||
vin = callback.data.split(":")[1]
|
||||
|
||||
prices = [LabeledPrice(label="Detailed VIN Report", amount=1)]
|
||||
|
||||
logging.info(f"Sending invoice for VIN: {vin}")
|
||||
await callback.bot.send_invoice(
|
||||
chat_id=callback.message.chat.id,
|
||||
title="Detailed VIN Information",
|
||||
@ -94,6 +106,9 @@ async def pay_detailed_info_callback(callback: CallbackQuery):
|
||||
|
||||
@dp.message(VinStates.waiting_for_vin)
|
||||
async def process_vin(message: Message, state: FSMContext, db: OracleDatabase):
|
||||
# Сохраняем данные пользователя при обработке VIN
|
||||
await db.save_user(message.from_user, "vin_processing")
|
||||
|
||||
vin = message.text.strip().upper()
|
||||
if len(vin) == 17 and vin.isalnum() and all(c not in vin for c in ["I", "O", "Q"]):
|
||||
try:
|
||||
@ -129,8 +144,51 @@ async def pre_checkout_handler(pre_checkout_query: PreCheckoutQuery):
|
||||
await pre_checkout_query.answer(ok=True)
|
||||
|
||||
|
||||
ADMIN_USER_ID = int(getenv("ADMIN_USER_ID", "0")) # ID администратора из переменных окружения
|
||||
|
||||
@dp.message(Command("admin_stats"))
|
||||
async def admin_stats_handler(message: Message, db: OracleDatabase):
|
||||
# Проверяем, является ли пользователь администратором
|
||||
if message.from_user.id != ADMIN_USER_ID:
|
||||
await message.answer("❌ Access denied. This command is for administrators only.")
|
||||
return
|
||||
|
||||
try:
|
||||
# Получаем общую статистику
|
||||
stats = await db.get_users_summary()
|
||||
|
||||
# Формируем отчет
|
||||
report = f"""
|
||||
📊 **Bot Users Statistics**
|
||||
|
||||
👥 **Users Overview:**
|
||||
• Total users: {stats.get('total_users', 0)}
|
||||
• Premium users: {stats.get('premium_users', 0)}
|
||||
|
||||
💰 **Revenue:**
|
||||
• Total revenue: {stats.get('total_revenue', 0)} ⭐️
|
||||
• Total transactions: {stats.get('total_transactions', 0)}
|
||||
|
||||
📈 **Activity:**
|
||||
• Active last 24h: {stats.get('active_last_24h', 0)}
|
||||
• Active last week: {stats.get('active_last_week', 0)}
|
||||
|
||||
📅 **Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
"""
|
||||
|
||||
await message.answer(report, parse_mode="Markdown")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error generating admin stats: {e}")
|
||||
await message.answer("❌ Error generating statistics. Please try again later.")
|
||||
|
||||
|
||||
@dp.message(lambda message: message.successful_payment)
|
||||
async def successful_payment_handler(message: Message, db: OracleDatabase):
|
||||
# Сохраняем данные о платеже пользователя
|
||||
await db.save_user(message.from_user, "successful_payment")
|
||||
await db.update_user_payment(message.from_user.id, 1.0) # 1 Telegram Star
|
||||
|
||||
payload = message.successful_payment.invoice_payload
|
||||
|
||||
if payload.startswith("detailed_vin_info:"):
|
||||
@ -231,7 +289,7 @@ async def successful_payment_handler(message: Message, db: OracleDatabase):
|
||||
report += "⚠️ **TECHNICAL INFORMATION AND ERRORS**\n"
|
||||
for key, data in detailed_info['technical_information_and_errors'].items():
|
||||
if data['value'] == "0":
|
||||
report += f"✅ **No errors found**\n"
|
||||
report += "✅ **No errors found**\n"
|
||||
else:
|
||||
report += f"• **{data['param_name']}:** {data['value']}\n"
|
||||
report += "\n"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user