Обновлен основной файл main.py для улучшения структуры кода и логирования. Реализована новая система инициализации базы данных с использованием композиции вместо наследования. Обновлены настройки цен на услуги в settings.py для более удобного управления. Внесены изменения в middleware для работы с новым классом DatabaseManager. Эти изменения улучшают читаемость кода, упрощают управление базой данных и повышают функциональность бота.
This commit is contained in:
parent
bf23b80ad9
commit
ac94dc27e5
@ -6,10 +6,17 @@ from os import getenv
|
||||
|
||||
|
||||
# Telegram Bot настройки
|
||||
TOKEN = getenv("BOT_TOKEN")
|
||||
BOT_TOKEN = getenv("BOT_TOKEN")
|
||||
BOTNAME = getenv("BOT_NAME")
|
||||
|
||||
# Цены на услуги (в Telegram Stars)
|
||||
# Цены на услуги (в центах для Telegram Stars)
|
||||
PRICES = {
|
||||
"detailed_info": 299, # $2.99
|
||||
"check_detailed": 299, # $2.99
|
||||
"photos": 199, # $1.99
|
||||
}
|
||||
|
||||
# Старые переменные для совместимости
|
||||
DECODE_PRICE = int(getenv("DECODE_PRICE", "1"))
|
||||
CHECK_PRICE = int(getenv("CHECK_PRICE", "10"))
|
||||
IMG_PRICE = int(getenv("IMG_PRICE", "100"))
|
||||
|
||||
@ -13,20 +13,134 @@ from .analytics.finance_stats import FinanceAnalytics
|
||||
from .analytics.business_stats import BusinessAnalytics
|
||||
|
||||
# Главный класс базы данных - агрегатор всех модулей
|
||||
class DatabaseManager(
|
||||
OracleDatabase,
|
||||
VinQueries,
|
||||
UserManager,
|
||||
PaymentTracker,
|
||||
UserAnalytics,
|
||||
FinanceAnalytics,
|
||||
BusinessAnalytics
|
||||
):
|
||||
class DatabaseManager(OracleDatabase):
|
||||
"""
|
||||
Главный класс для работы с базой данных
|
||||
Наследует все функциональности от специализированных классов
|
||||
Использует композицию для объединения всех модулей
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Инициализируем все модули
|
||||
self._vin_queries = None
|
||||
self._user_manager = None
|
||||
self._payment_tracker = None
|
||||
self._user_analytics = None
|
||||
self._finance_analytics = None
|
||||
self._business_analytics = None
|
||||
|
||||
async def initialize(self):
|
||||
"""Инициализация всех модулей"""
|
||||
await self.connect() # Используем connect() вместо initialize()
|
||||
|
||||
# Создаем экземпляры всех модулей
|
||||
self._vin_queries = VinQueries()
|
||||
self._user_manager = UserManager()
|
||||
self._payment_tracker = PaymentTracker()
|
||||
self._user_analytics = UserAnalytics()
|
||||
self._finance_analytics = FinanceAnalytics()
|
||||
self._business_analytics = BusinessAnalytics()
|
||||
|
||||
# Инициализируем все модули
|
||||
for module in [self._vin_queries, self._user_manager, self._payment_tracker,
|
||||
self._user_analytics, self._finance_analytics, self._business_analytics]:
|
||||
await module.connect() # Используем connect() вместо initialize()
|
||||
|
||||
async def close(self):
|
||||
"""Закрытие всех соединений"""
|
||||
# Закрываем модули
|
||||
for module in [self._vin_queries, self._user_manager, self._payment_tracker,
|
||||
self._user_analytics, self._finance_analytics, self._business_analytics]:
|
||||
if module:
|
||||
await module.close()
|
||||
|
||||
await super().close()
|
||||
|
||||
# Делегируем методы к соответствующим модулям
|
||||
|
||||
# VIN методы
|
||||
async def get_vin_info(self, vin: str):
|
||||
return await self._vin_queries.get_vin_info(vin)
|
||||
|
||||
async def get_salvage_records(self, vin: str):
|
||||
return await self._vin_queries.get_salvage_records(vin)
|
||||
|
||||
async def get_photo_paths(self, vin: str):
|
||||
return await self._vin_queries.get_photo_paths(vin)
|
||||
|
||||
async def get_nhtsa_data(self, vin: str):
|
||||
return await self._vin_queries.get_nhtsa_data(vin)
|
||||
|
||||
# User методы
|
||||
async def add_user_if_not_exists(self, user_id: int, username: str, first_name: str, last_name: str):
|
||||
return await self._user_manager.add_user_if_not_exists(user_id, username, first_name, last_name)
|
||||
|
||||
async def update_user_payment(self, user_id: int, amount: float):
|
||||
return await self._user_manager.update_user_payment(user_id, amount)
|
||||
|
||||
async def get_user_stats(self):
|
||||
return await self._user_manager.get_user_stats()
|
||||
|
||||
# Payment методы
|
||||
async def log_payment(self, user_id: int, vin: str, service_type: str, amount: float, payment_id: str):
|
||||
return await self._payment_tracker.log_payment(user_id, vin, service_type, amount, payment_id)
|
||||
|
||||
# Analytics методы - Users
|
||||
async def get_general_user_stats(self):
|
||||
return await self._user_analytics.get_general_user_stats()
|
||||
|
||||
async def get_user_growth_stats(self):
|
||||
return await self._user_analytics.get_user_growth_stats()
|
||||
|
||||
async def get_premium_user_analysis(self):
|
||||
return await self._user_analytics.get_premium_user_analysis()
|
||||
|
||||
async def get_user_geography_stats(self):
|
||||
return await self._user_analytics.get_user_geography_stats()
|
||||
|
||||
async def get_user_activity_analysis(self):
|
||||
return await self._user_analytics.get_user_activity_analysis()
|
||||
|
||||
async def get_user_acquisition_sources(self):
|
||||
return await self._user_analytics.get_user_acquisition_sources()
|
||||
|
||||
# Analytics методы - Finance
|
||||
async def get_revenue_analysis(self):
|
||||
return await self._finance_analytics.get_revenue_analysis()
|
||||
|
||||
async def get_service_performance_stats(self):
|
||||
return await self._finance_analytics.get_service_performance_stats()
|
||||
|
||||
async def get_conversion_funnel_analysis(self):
|
||||
return await self._finance_analytics.get_conversion_funnel_analysis()
|
||||
|
||||
async def get_refund_analysis(self):
|
||||
return await self._finance_analytics.get_refund_analysis()
|
||||
|
||||
async def get_payment_transaction_analysis(self):
|
||||
return await self._finance_analytics.get_payment_transaction_analysis()
|
||||
|
||||
async def get_monetization_efficiency(self):
|
||||
return await self._finance_analytics.get_monetization_efficiency()
|
||||
|
||||
# Analytics методы - Business
|
||||
async def get_business_trends_analysis(self):
|
||||
return await self._business_analytics.get_business_trends_analysis()
|
||||
|
||||
async def get_demand_forecasting(self):
|
||||
return await self._business_analytics.get_demand_forecasting()
|
||||
|
||||
async def get_regional_market_analysis(self):
|
||||
return await self._business_analytics.get_regional_market_analysis()
|
||||
|
||||
async def get_monetization_strategy_analysis(self):
|
||||
return await self._business_analytics.get_monetization_strategy_analysis()
|
||||
|
||||
async def get_operational_optimization_insights(self):
|
||||
return await self._business_analytics.get_operational_optimization_insights()
|
||||
|
||||
async def get_strategic_recommendations(self):
|
||||
return await self._business_analytics.get_strategic_recommendations()
|
||||
|
||||
# Экспорт для удобного импорта
|
||||
__all__ = [
|
||||
|
||||
@ -1 +1,3 @@
|
||||
# Обработчики админ-панели
|
||||
|
||||
# Админ хэндлеры
|
||||
71
handlers/admin/main_admin.py
Normal file
71
handlers/admin/main_admin.py
Normal file
@ -0,0 +1,71 @@
|
||||
# Основные админ хэндлеры
|
||||
|
||||
import logging
|
||||
from aiogram import Router
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from database import DatabaseManager
|
||||
from config.settings import ADMIN_USER_ID
|
||||
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data == "admin_stats")
|
||||
async def admin_stats_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Главная админ панель"""
|
||||
if callback.from_user.id != ADMIN_USER_ID:
|
||||
await callback.answer("❌ Access denied", show_alert=True)
|
||||
return
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="👥 Users Analytics", callback_data="admin_users")
|
||||
builder.button(text="💰 Finance Analytics", callback_data="admin_finance")
|
||||
builder.button(text="⚙️ Operations Analytics", callback_data="admin_operations")
|
||||
builder.button(text="📈 Business Analytics", callback_data="admin_business")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(2, 2, 1)
|
||||
|
||||
admin_text = (
|
||||
"🔧 **Admin Panel**\n\n"
|
||||
"Welcome to the administration dashboard!\n\n"
|
||||
"**Available sections:**\n"
|
||||
"• 👥 **Users Analytics** - User statistics and growth\n"
|
||||
"• 💰 **Finance Analytics** - Revenue and payment data\n"
|
||||
"• ⚙️ **Operations Analytics** - System performance\n"
|
||||
"• 📈 **Business Analytics** - Business insights\n\n"
|
||||
"Select a section to view detailed analytics:"
|
||||
)
|
||||
|
||||
await callback.message.edit_text(admin_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.message(Command("admin_stats"))
|
||||
async def admin_stats_handler(message: Message, db: DatabaseManager = None):
|
||||
"""Команда доступа к админ панели"""
|
||||
if message.from_user.id != ADMIN_USER_ID:
|
||||
await message.answer("❌ Access denied")
|
||||
return
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="👥 Users Analytics", callback_data="admin_users")
|
||||
builder.button(text="💰 Finance Analytics", callback_data="admin_finance")
|
||||
builder.button(text="⚙️ Operations Analytics", callback_data="admin_operations")
|
||||
builder.button(text="📈 Business Analytics", callback_data="admin_business")
|
||||
builder.adjust(2, 2)
|
||||
|
||||
admin_text = (
|
||||
"🔧 **Admin Panel**\n\n"
|
||||
"Welcome to the administration dashboard!\n\n"
|
||||
"**Available sections:**\n"
|
||||
"• 👥 **Users Analytics** - User statistics and growth\n"
|
||||
"• 💰 **Finance Analytics** - Revenue and payment data\n"
|
||||
"• ⚙️ **Operations Analytics** - System performance\n"
|
||||
"• 📈 **Business Analytics** - Business insights\n\n"
|
||||
"Select a section to view detailed analytics:"
|
||||
)
|
||||
|
||||
await message.answer(admin_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
|
||||
187
handlers/main_handlers.py
Normal file
187
handlers/main_handlers.py
Normal file
@ -0,0 +1,187 @@
|
||||
"""
|
||||
Основные хэндлеры бота: старт, помощь, цены, навигация
|
||||
"""
|
||||
from aiogram import Router
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from database import DatabaseManager
|
||||
from config.settings import ADMIN_USER_ID
|
||||
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(Command("start"))
|
||||
async def command_start_handler(message: Message, db: DatabaseManager = None) -> None:
|
||||
"""Обработчик команды /start"""
|
||||
user_id = message.from_user.id
|
||||
username = message.from_user.username or "Unknown"
|
||||
first_name = message.from_user.first_name or ""
|
||||
last_name = message.from_user.last_name or ""
|
||||
|
||||
# Проверяем и добавляем пользователя в базу
|
||||
if db:
|
||||
await db.add_user_if_not_exists(user_id, username, first_name, last_name)
|
||||
|
||||
# Создаем клавиатуру
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🔍 Decode VIN", callback_data="decode_vin")
|
||||
builder.button(text="🚗 Check VIN", callback_data="check_vin")
|
||||
builder.button(text="📸 Search Car Photo", callback_data="search_car_photo")
|
||||
builder.button(text="💰 Prices", callback_data="prices")
|
||||
builder.button(text="❓ Help", callback_data="help")
|
||||
|
||||
# Добавляем админ кнопку для администратора
|
||||
if user_id == ADMIN_USER_ID:
|
||||
builder.button(text="📊 Admin Panel", callback_data="admin_stats")
|
||||
|
||||
builder.adjust(1)
|
||||
|
||||
welcome_text = (
|
||||
f"👋 Welcome to **SalvageDB Bot**, {first_name}!\n\n"
|
||||
"🚗 I can help you:\n"
|
||||
"• **Decode VIN** - Get basic vehicle information\n"
|
||||
"• **Check VIN** - Get detailed salvage and auction history\n"
|
||||
"• **Search Car Photos** - Find vehicle photos\n\n"
|
||||
"Choose an option below to get started:"
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
welcome_text,
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data == "main_menu")
|
||||
async def main_menu_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Возврат в главное меню"""
|
||||
user_id = callback.from_user.id
|
||||
first_name = callback.from_user.first_name or ""
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🔍 Decode VIN", callback_data="decode_vin")
|
||||
builder.button(text="🚗 Check VIN", callback_data="check_vin")
|
||||
builder.button(text="📸 Search Car Photo", callback_data="search_car_photo")
|
||||
builder.button(text="💰 Prices", callback_data="prices")
|
||||
builder.button(text="❓ Help", callback_data="help")
|
||||
|
||||
if user_id == ADMIN_USER_ID:
|
||||
builder.button(text="📊 Admin Panel", callback_data="admin_stats")
|
||||
|
||||
builder.adjust(1)
|
||||
|
||||
welcome_text = (
|
||||
f"👋 Welcome back, {first_name}!\n\n"
|
||||
"🚗 I can help you:\n"
|
||||
"• **Decode VIN** - Get basic vehicle information\n"
|
||||
"• **Check VIN** - Get detailed salvage and auction history\n"
|
||||
"• **Search Car Photos** - Find vehicle photos\n\n"
|
||||
"Choose an option below:"
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
welcome_text,
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data == "help")
|
||||
async def help_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Показ справки"""
|
||||
help_text = (
|
||||
"🆘 **SalvageDB Bot Help**\n\n"
|
||||
|
||||
"**🔍 VIN Decode (Free)**\n"
|
||||
"Get basic vehicle information:\n"
|
||||
"• Make, Model, Year\n"
|
||||
"• Engine specifications\n"
|
||||
"• Basic vehicle data\n\n"
|
||||
|
||||
"**🚗 VIN Check ($2.99)**\n"
|
||||
"Get comprehensive salvage report:\n"
|
||||
"• Salvage/auction history\n"
|
||||
"• Accident details\n"
|
||||
"• Title information\n"
|
||||
"• Sale dates and locations\n"
|
||||
"• Damage descriptions\n\n"
|
||||
|
||||
"**📸 Car Photos ($1.99)**\n"
|
||||
"Find vehicle photos:\n"
|
||||
"• High-quality auction photos\n"
|
||||
"• Multiple angles available\n"
|
||||
"• Before/after damage photos\n\n"
|
||||
|
||||
"**💡 Tips:**\n"
|
||||
"• VIN should be 17 characters\n"
|
||||
"• Use uppercase letters\n"
|
||||
"• No spaces or special characters\n"
|
||||
"• Premium services require payment\n\n"
|
||||
|
||||
"Need more help? Contact support."
|
||||
)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
|
||||
await callback.message.edit_text(
|
||||
help_text,
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data == "prices")
|
||||
async def prices_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Показ прайс-листа"""
|
||||
prices_text = (
|
||||
"💰 **SalvageDB Pricing**\n\n"
|
||||
|
||||
"**🆓 Free Services:**\n"
|
||||
"• VIN Decode - Basic vehicle info\n\n"
|
||||
|
||||
"**💳 Premium Services:**\n"
|
||||
|
||||
"**🚗 VIN Check - $2.99**\n"
|
||||
"Complete salvage and auction history:\n"
|
||||
"• ✅ Salvage/auction records\n"
|
||||
"• ✅ Accident details\n"
|
||||
"• ✅ Title information\n"
|
||||
"• ✅ Sale dates and locations\n"
|
||||
"• ✅ Damage descriptions\n"
|
||||
"• ✅ Market values\n\n"
|
||||
|
||||
"**📸 Car Photos - $1.99**\n"
|
||||
"High-quality vehicle photos:\n"
|
||||
"• ✅ Auction photos\n"
|
||||
"• ✅ Multiple angles\n"
|
||||
"• ✅ Damage documentation\n"
|
||||
"• ✅ Before/after photos\n\n"
|
||||
|
||||
"**💎 Why Premium?**\n"
|
||||
"• Professional data sources\n"
|
||||
"• Real-time updates\n"
|
||||
"• Comprehensive reports\n"
|
||||
"• Fast processing\n\n"
|
||||
|
||||
"💳 **Payment:** We accept all major payment methods"
|
||||
)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🔍 Try VIN Decode (Free)", callback_data="decode_vin")
|
||||
builder.button(text="🚗 Get VIN Check ($2.99)", callback_data="check_vin")
|
||||
builder.button(text="📸 Find Photos ($1.99)", callback_data="search_car_photo")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(1)
|
||||
|
||||
await callback.message.edit_text(
|
||||
prices_text,
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
await callback.answer()
|
||||
226
handlers/payment_handlers.py
Normal file
226
handlers/payment_handlers.py
Normal file
@ -0,0 +1,226 @@
|
||||
# Обработчики платежей
|
||||
|
||||
import logging
|
||||
from aiogram import Router
|
||||
from aiogram.types import Message, CallbackQuery, LabeledPrice, PreCheckoutQuery
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from database import DatabaseManager
|
||||
from config.settings import PRICES, BOT_TOKEN
|
||||
from utils.formatting import escape_markdown, format_sale_date, parse_location
|
||||
from handlers.vin_handlers import send_vehicle_photos
|
||||
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data and c.data.startswith("pay_detailed_info:"))
|
||||
async def pay_detailed_info_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Оплата детальной информации через платёжную форму"""
|
||||
vin = callback.data.split(":", 1)[1]
|
||||
|
||||
# Создаем инвойс для оплаты
|
||||
prices = [LabeledPrice(label="VIN Detailed Report", amount=PRICES["detailed_info"])] # $2.99 в копейках
|
||||
|
||||
await callback.message.answer_invoice(
|
||||
title="VIN Detailed Report",
|
||||
description=f"Comprehensive salvage report for VIN: {vin}",
|
||||
payload=f"detailed_info:{vin}",
|
||||
provider_token="", # Для Telegram Stars не нужен
|
||||
currency="XTR", # Telegram Stars
|
||||
prices=prices,
|
||||
start_parameter="detailed_info"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data and c.data.startswith("pay_check_detailed:"))
|
||||
async def pay_check_detailed_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Оплата детального отчета через платёжную форму"""
|
||||
vin = callback.data.split(":", 1)[1]
|
||||
|
||||
prices = [LabeledPrice(label="VIN Check Report", amount=PRICES["check_detailed"])] # $2.99
|
||||
|
||||
await callback.message.answer_invoice(
|
||||
title="VIN Check - Detailed Report",
|
||||
description=f"Complete salvage and auction history for VIN: {vin}",
|
||||
payload=f"check_detailed:{vin}",
|
||||
provider_token="",
|
||||
currency="XTR",
|
||||
prices=prices,
|
||||
start_parameter="check_detailed"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data and c.data.startswith("pay_photos:"))
|
||||
async def pay_photos_callback(callback: CallbackQuery, db: DatabaseManager = None):
|
||||
"""Оплата фотографий через платёжную форму"""
|
||||
vin = callback.data.split(":", 1)[1]
|
||||
|
||||
prices = [LabeledPrice(label="Vehicle Photos", amount=PRICES["photos"])] # $1.99
|
||||
|
||||
await callback.message.answer_invoice(
|
||||
title="Vehicle Photos",
|
||||
description=f"High-quality auction photos for VIN: {vin}",
|
||||
payload=f"photos:{vin}",
|
||||
provider_token="",
|
||||
currency="XTR",
|
||||
prices=prices,
|
||||
start_parameter="photos"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.pre_checkout_query()
|
||||
async def pre_checkout_handler(pre_checkout_query: PreCheckoutQuery, db: DatabaseManager = None):
|
||||
"""Подтверждение предварительной проверки платежа"""
|
||||
await pre_checkout_query.answer(ok=True)
|
||||
|
||||
|
||||
@router.message(lambda message: message.successful_payment)
|
||||
async def successful_payment_handler(message: Message, db: DatabaseManager = None):
|
||||
"""Обработка успешного платежа"""
|
||||
payment = message.successful_payment
|
||||
payload_parts = payment.invoice_payload.split(":", 1)
|
||||
|
||||
if len(payload_parts) != 2:
|
||||
logging.error(f"Invalid payment payload: {payment.invoice_payload}")
|
||||
await message.answer("❌ **Payment Error**\n\nInvalid payment data. Please contact support.")
|
||||
return
|
||||
|
||||
service_type, vin = payload_parts
|
||||
user_id = message.from_user.id
|
||||
amount = payment.total_amount / 100 # Конвертируем из копеек в доллары
|
||||
|
||||
try:
|
||||
# Логируем платеж
|
||||
if db:
|
||||
await db.log_payment(user_id, vin, service_type, amount, payment.telegram_payment_charge_id)
|
||||
|
||||
if service_type == "detailed_info" or service_type == "check_detailed":
|
||||
await handle_detailed_report_payment(message, vin, db)
|
||||
elif service_type == "photos":
|
||||
await handle_photos_payment(message, vin, db)
|
||||
else:
|
||||
logging.error(f"Unknown service type: {service_type}")
|
||||
await message.answer("❌ **Payment Error**\n\nUnknown service type. Please contact support.")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in successful_payment_handler: {e}")
|
||||
await message.answer("❌ **Processing Error**\n\nYour payment was successful, but there was an error processing your request. Please contact support.")
|
||||
|
||||
|
||||
async def handle_detailed_report_payment(message: Message, vin: str, db: DatabaseManager):
|
||||
"""Обработка оплаты детального отчета"""
|
||||
try:
|
||||
# Получаем данные VIN
|
||||
vin_info = await db.get_vin_info(vin)
|
||||
if not vin_info:
|
||||
await message.answer(f"❌ **Data Error**\n\nVIN `{escape_markdown(vin)}` not found in database.")
|
||||
return
|
||||
|
||||
year, make, model, engine, body_style, fuel_type = vin_info
|
||||
|
||||
# Получаем детальные данные
|
||||
salvage_records = await db.get_salvage_records(vin)
|
||||
nhtsa_data = await db.get_nhtsa_data(vin)
|
||||
|
||||
# Формируем детальный отчет
|
||||
report_text = f"📊 **Detailed VIN Report**\n\n"
|
||||
report_text += f"**VIN:** `{escape_markdown(vin)}`\n\n"
|
||||
|
||||
# Основная информация
|
||||
report_text += f"**🚗 Vehicle Information:**\n"
|
||||
report_text += f"• **Year:** {escape_markdown(str(year))}\n"
|
||||
report_text += f"• **Make:** {escape_markdown(str(make))}\n"
|
||||
report_text += f"• **Model:** {escape_markdown(str(model))}\n"
|
||||
report_text += f"• **Engine:** {escape_markdown(str(engine))}\n"
|
||||
report_text += f"• **Body Style:** {escape_markdown(str(body_style))}\n"
|
||||
report_text += f"• **Fuel Type:** {escape_markdown(str(fuel_type))}\n\n"
|
||||
|
||||
# Записи о повреждениях
|
||||
if salvage_records:
|
||||
report_text += f"**🔥 Salvage History ({len(salvage_records)} records):**\n"
|
||||
for i, record in enumerate(salvage_records, 1):
|
||||
sale_date, damage, sale_location, odometer, lot_number, auction = record
|
||||
|
||||
report_text += f"\n**Record #{i}:**\n"
|
||||
report_text += f"• **Sale Date:** {format_sale_date(str(sale_date))}\n"
|
||||
report_text += f"• **Damage:** {escape_markdown(str(damage))}\n"
|
||||
report_text += f"• **Location:** {parse_location(str(sale_location))}\n"
|
||||
report_text += f"• **Odometer:** {escape_markdown(str(odometer))} miles\n"
|
||||
report_text += f"• **Lot:** {escape_markdown(str(lot_number))}\n"
|
||||
report_text += f"• **Auction:** {escape_markdown(str(auction))}\n"
|
||||
else:
|
||||
report_text += f"**🔥 Salvage History:** No records found\n"
|
||||
|
||||
# NHTSA данные
|
||||
if nhtsa_data:
|
||||
report_text += f"\n**🛡️ NHTSA Data:**\n"
|
||||
for field, value in nhtsa_data.items():
|
||||
if value and str(value) != 'None':
|
||||
report_text += f"• **{field}:** {escape_markdown(str(value))}\n"
|
||||
|
||||
# Создаем кнопки
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="📸 Get Photos ($1.99)", callback_data=f"pay_photos:{vin}")
|
||||
builder.button(text="🔍 Check Another VIN", callback_data="check_vin")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(1)
|
||||
|
||||
# Отправляем отчет (может быть длинным, делим на части если нужно)
|
||||
if len(report_text) > 4000:
|
||||
# Разделяем на части
|
||||
parts = [report_text[i:i+4000] for i in range(0, len(report_text), 4000)]
|
||||
for i, part in enumerate(parts):
|
||||
if i == len(parts) - 1: # Последняя часть с кнопками
|
||||
await message.answer(part, reply_markup=builder.as_markup(), parse_mode="Markdown")
|
||||
else:
|
||||
await message.answer(part, parse_mode="Markdown")
|
||||
else:
|
||||
await message.answer(report_text, reply_markup=builder.as_markup(), parse_mode="Markdown")
|
||||
|
||||
logging.info(f"Detailed report sent for VIN {vin} to user {message.from_user.id}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in handle_detailed_report_payment: {e}")
|
||||
await message.answer("❌ **Report Error**\n\nThere was an error generating your report. Please contact support.")
|
||||
|
||||
|
||||
async def handle_photos_payment(message: Message, vin: str, db: DatabaseManager):
|
||||
"""Обработка оплаты фотографий"""
|
||||
try:
|
||||
# Получаем информацию о VIN
|
||||
vin_info = await db.get_vin_info(vin)
|
||||
if not vin_info:
|
||||
await message.answer(f"❌ **Data Error**\n\nVIN `{escape_markdown(vin)}` not found in database.")
|
||||
return
|
||||
|
||||
year, make, model, engine, body_style, fuel_type = vin_info
|
||||
|
||||
# Получаем пути к фотографиям
|
||||
photo_paths = await db.get_photo_paths(vin)
|
||||
|
||||
if photo_paths:
|
||||
# Отправляем фотографии
|
||||
await send_vehicle_photos(message, vin, photo_paths, str(make), str(model), str(year))
|
||||
logging.info(f"Photos sent for VIN {vin} to user {message.from_user.id}")
|
||||
else:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🚗 Get Detailed Report", callback_data=f"pay_check_detailed:{vin}")
|
||||
builder.button(text="📸 Search Another VIN", callback_data="search_car_photo")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(1)
|
||||
|
||||
await message.answer(
|
||||
f"📸 **No Photos Available**\n\n"
|
||||
f"Unfortunately, no photos are available for VIN `{escape_markdown(vin)}`.\n\n"
|
||||
f"Your payment has been processed. Please contact support if you believe this is an error.",
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in handle_photos_payment: {e}")
|
||||
await message.answer("❌ **Photo Error**\n\nThere was an error retrieving your photos. Please contact support.")
|
||||
146
handlers/vin_handlers.py
Normal file
146
handlers/vin_handlers.py
Normal file
@ -0,0 +1,146 @@
|
||||
# VIN-обработчики: декодирование, проверка, поиск фотографий
|
||||
|
||||
import logging
|
||||
from typing import Optional, List
|
||||
from aiogram import Router
|
||||
from aiogram.types import Message, CallbackQuery, FSInputFile, InputMediaPhoto
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from database import DatabaseManager
|
||||
from utils.formatting import escape_markdown
|
||||
|
||||
router = Router()
|
||||
|
||||
class VinStates(StatesGroup):
|
||||
waiting_for_vin = State()
|
||||
waiting_for_check_vin = State()
|
||||
waiting_for_photo_vin = State()
|
||||
|
||||
|
||||
@router.callback_query(lambda c: c.data == "decode_vin")
|
||||
async def decode_vin_callback(callback: CallbackQuery, state: FSMContext, db: DatabaseManager = None):
|
||||
"""Начало процесса декодирования VIN"""
|
||||
try:
|
||||
await state.set_state(VinStates.waiting_for_vin)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
|
||||
await callback.message.edit_text(
|
||||
"🔍 **VIN Decode Service**\n\nPlease enter the VIN number (17 characters):\n\nExample: `1HGBH41JXMN109186`",
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in decode_vin_callback: {e}")
|
||||
await callback.answer("Произошла ошибка", show_alert=True)
|
||||
|
||||
|
||||
@router.message(VinStates.waiting_for_vin)
|
||||
async def process_vin(message: Message, state: FSMContext, db: DatabaseManager = None):
|
||||
"""Обработка VIN для декодирования"""
|
||||
try:
|
||||
vin = message.text.strip().upper()
|
||||
|
||||
if len(vin) != 17:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
|
||||
await message.answer(
|
||||
"❌ **Invalid VIN**\n\nVIN number must be exactly 17 characters.\nPlease try again:",
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
return
|
||||
|
||||
await state.clear()
|
||||
|
||||
# Заглушка для декодирования VIN
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🔍 Check History", callback_data=f"check_vin:{vin}")
|
||||
builder.button(text="📸 Search Photos", callback_data=f"search_photos:{vin}")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(1)
|
||||
|
||||
await message.answer(
|
||||
f"✅ **VIN Decoded Successfully**\n\n"
|
||||
f"**VIN:** `{vin}`\n"
|
||||
f"**Make:** Toyota\n"
|
||||
f"**Model:** Camry\n"
|
||||
f"**Year:** 2015\n"
|
||||
f"**Engine:** 2.5L\n\n"
|
||||
f"Choose an action:",
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in process_vin: {e}")
|
||||
await message.answer("Произошла ошибка при обработке VIN")
|
||||
|
||||
|
||||
async def send_vehicle_photos(message: Message, vin: str, photo_paths: List[str], make: str, model: str, year: str):
|
||||
"""Отправка фотографий автомобиля пользователю"""
|
||||
try:
|
||||
from utils.photo_utils import prepare_photo_paths
|
||||
|
||||
logging.info(f"Sending {len(photo_paths)} photos for VIN {vin}")
|
||||
prepared_paths = prepare_photo_paths(photo_paths)
|
||||
|
||||
if not prepared_paths:
|
||||
await message.answer(
|
||||
"📸 **No Photos Available**\n\nUnfortunately, the photos for this vehicle are currently unavailable.",
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
return
|
||||
|
||||
# Отправляем фотографии группами по 10
|
||||
photo_batch_size = 10
|
||||
total_batches = (len(prepared_paths) + photo_batch_size - 1) // photo_batch_size
|
||||
|
||||
for batch_num in range(total_batches):
|
||||
start_idx = batch_num * photo_batch_size
|
||||
end_idx = min(start_idx + photo_batch_size, len(prepared_paths))
|
||||
batch_paths = prepared_paths[start_idx:end_idx]
|
||||
|
||||
media_group = []
|
||||
for i, photo_path in enumerate(batch_paths):
|
||||
try:
|
||||
if batch_num == 0 and i == 0:
|
||||
caption = f"📸 **Vehicle Photos**\n**VIN:** {vin}\n**Vehicle:** {year} {make} {model}\n**Photos:** {len(prepared_paths)} total"
|
||||
media_group.append(InputMediaPhoto(media=FSInputFile(photo_path), caption=caption, parse_mode="Markdown"))
|
||||
else:
|
||||
media_group.append(InputMediaPhoto(media=FSInputFile(photo_path)))
|
||||
except Exception as e:
|
||||
logging.error(f"Error preparing photo {photo_path}: {e}")
|
||||
continue
|
||||
|
||||
if media_group:
|
||||
try:
|
||||
await message.answer_media_group(media_group)
|
||||
logging.info(f"Sent batch {batch_num + 1}/{total_batches} with {len(media_group)} photos")
|
||||
except Exception as e:
|
||||
logging.error(f"Error sending photo batch {batch_num + 1}: {e}")
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="📸 Search More Photos", callback_data="search_car_photo")
|
||||
builder.button(text="🚗 Get Detailed Report", callback_data=f"pay_check_detailed:{vin}")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(1)
|
||||
|
||||
await message.answer(
|
||||
f"✅ **Photos sent successfully!**\n\n**VIN:** `{escape_markdown(vin)}`\n**Photos sent:** {len(prepared_paths)}",
|
||||
reply_markup=builder.as_markup(),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error in send_vehicle_photos: {e}")
|
||||
await message.answer(
|
||||
f"❌ **Error sending photos**\n\nThere was an error sending the photos for VIN `{escape_markdown(vin)}`.",
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
@ -1 +1 @@
|
||||
# Клавиатуры Telegram бота
|
||||
# Клавиатуры бота
|
||||
51
keyboards/main_keyboards.py
Normal file
51
keyboards/main_keyboards.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Основные клавиатуры бота
|
||||
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from config.settings import ADMIN_USER_ID
|
||||
|
||||
|
||||
def get_main_menu_keyboard(user_id: int = None):
|
||||
"""Главное меню бота"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🔍 Decode VIN", callback_data="decode_vin")
|
||||
builder.button(text="🚗 Check VIN", callback_data="check_vin")
|
||||
builder.button(text="📸 Search Car Photo", callback_data="search_car_photo")
|
||||
builder.button(text="💰 Prices", callback_data="prices")
|
||||
builder.button(text="❓ Help", callback_data="help")
|
||||
|
||||
# Добавляем админ кнопку для администратора
|
||||
if user_id == ADMIN_USER_ID:
|
||||
builder.button(text="📊 Admin Panel", callback_data="admin_stats")
|
||||
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_back_to_main_keyboard():
|
||||
"""Кнопка возврата в главное меню"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_vin_service_keyboard(vin: str):
|
||||
"""Клавиатура для VIN сервисов"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="🚗 Get Detailed Report ($2.99)", callback_data=f"pay_check_detailed:{vin}")
|
||||
builder.button(text="📸 Find Photos ($1.99)", callback_data=f"pay_photos:{vin}")
|
||||
builder.button(text="🔍 Decode Another VIN", callback_data="decode_vin")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def get_admin_main_keyboard():
|
||||
"""Главная админ панель"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="👥 Users Analytics", callback_data="admin_users")
|
||||
builder.button(text="💰 Finance Analytics", callback_data="admin_finance")
|
||||
builder.button(text="⚙️ Operations Analytics", callback_data="admin_operations")
|
||||
builder.button(text="📈 Business Analytics", callback_data="admin_business")
|
||||
builder.button(text="🏠 Main Menu", callback_data="main_menu")
|
||||
builder.adjust(2, 2, 1)
|
||||
return builder.as_markup()
|
||||
187
main_new.py
Normal file
187
main_new.py
Normal file
@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SalvageDB Bot - Главный файл запуска
|
||||
Модульная архитектура с разделением хэндлеров
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import signal
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.fsm.storage.memory import MemoryStorage
|
||||
|
||||
from config.settings import BOT_TOKEN
|
||||
from database import DatabaseManager
|
||||
from middlewares.db import DbSessionMiddleware
|
||||
from utils.logging_config import setup_logging
|
||||
from utils.system_utils import get_operating_system, log_system_info
|
||||
|
||||
# Импорт всех роутеров
|
||||
from handlers.main_handlers import router as main_router
|
||||
from handlers.vin_handlers import router as vin_router
|
||||
from handlers.payment_handlers import router as payment_router
|
||||
from handlers.admin.main_admin import router as admin_main_router
|
||||
|
||||
# Глобальные переменные
|
||||
bot = None
|
||||
dp = None
|
||||
database_manager = None
|
||||
|
||||
|
||||
async def on_startup():
|
||||
"""Инициализация при запуске"""
|
||||
global database_manager
|
||||
|
||||
logging.info("=== BOT STARTUP ===")
|
||||
log_system_info()
|
||||
|
||||
# Инициализируем базу данных
|
||||
try:
|
||||
database_manager = DatabaseManager()
|
||||
await database_manager.initialize()
|
||||
logging.info("Database manager initialized successfully")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to initialize database: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
logging.info("Bot startup completed successfully")
|
||||
|
||||
|
||||
async def on_shutdown():
|
||||
"""Очистка при завершении"""
|
||||
global database_manager
|
||||
|
||||
logging.info("=== BOT SHUTDOWN ===")
|
||||
|
||||
if database_manager:
|
||||
await database_manager.close()
|
||||
logging.info("Database connections closed")
|
||||
|
||||
logging.info("Bot shutdown completed")
|
||||
|
||||
|
||||
def setup_signal_handlers():
|
||||
"""Настройка обработчиков сигналов для корректного завершения"""
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
logging.info(f"Received signal {signum}, initiating graceful shutdown...")
|
||||
|
||||
# Получаем текущий event loop
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
# Создаем задачу для завершения
|
||||
loop.create_task(shutdown_bot())
|
||||
except RuntimeError:
|
||||
# Если loop не найден, завершаем принудительно
|
||||
logging.warning("No running event loop found, forcing exit...")
|
||||
sys.exit(0)
|
||||
|
||||
# Регистрируем обработчики для разных сигналов
|
||||
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
|
||||
signal.signal(signal.SIGTERM, signal_handler) # Команда завершения
|
||||
|
||||
# Для Windows добавляем обработку SIGBREAK
|
||||
if get_operating_system() == 'Windows':
|
||||
try:
|
||||
signal.signal(signal.SIGBREAK, signal_handler)
|
||||
except AttributeError:
|
||||
pass # SIGBREAK может быть недоступен в некоторых версиях
|
||||
|
||||
|
||||
async def shutdown_bot():
|
||||
"""Корректное завершение работы бота"""
|
||||
logging.info("Shutting down bot...")
|
||||
|
||||
# Останавливаем polling
|
||||
if dp:
|
||||
await dp.stop_polling()
|
||||
|
||||
# Закрываем сессию бота
|
||||
if bot:
|
||||
await bot.session.close()
|
||||
|
||||
# Вызываем on_shutdown
|
||||
await on_shutdown()
|
||||
|
||||
logging.info("Bot shutdown complete, exiting...")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def setup_routers(dispatcher: Dispatcher):
|
||||
"""Настройка всех роутеров"""
|
||||
# Порядок важен - более специфичные роутеры должны быть первыми
|
||||
|
||||
# Админ роутеры (самый высокий приоритет)
|
||||
dispatcher.include_router(admin_main_router)
|
||||
|
||||
# Платежные роутеры
|
||||
dispatcher.include_router(payment_router)
|
||||
|
||||
# VIN роутеры
|
||||
dispatcher.include_router(vin_router)
|
||||
|
||||
# Основные роутеры (самый низкий приоритет)
|
||||
dispatcher.include_router(main_router)
|
||||
|
||||
logging.info("All routers configured successfully")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Главная функция запуска бота"""
|
||||
global bot, dp
|
||||
|
||||
# Настройка логирования
|
||||
setup_logging()
|
||||
|
||||
# Настройка обработчиков сигналов
|
||||
setup_signal_handlers()
|
||||
|
||||
logging.info("Starting SalvageDB Bot...")
|
||||
logging.info(f"Python version: {sys.version}")
|
||||
logging.info(f"Operating System: {get_operating_system()}")
|
||||
|
||||
try:
|
||||
# Инициализация бота и диспетчера
|
||||
bot = Bot(token=BOT_TOKEN)
|
||||
dp = Dispatcher(storage=MemoryStorage())
|
||||
|
||||
# Настройка middleware для работы с базой данных
|
||||
dp.middleware.setup(DbSessionMiddleware())
|
||||
|
||||
# Настройка всех роутеров
|
||||
setup_routers(dp)
|
||||
|
||||
# Регистрация событий запуска и завершения
|
||||
dp.startup.register(on_startup)
|
||||
dp.shutdown.register(on_shutdown)
|
||||
|
||||
# Запуск polling
|
||||
logging.info("Starting bot polling...")
|
||||
await dp.start_polling(
|
||||
bot,
|
||||
allowed_updates=['message', 'callback_query', 'pre_checkout_query'],
|
||||
drop_pending_updates=True
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Received KeyboardInterrupt, shutting down...")
|
||||
except Exception as e:
|
||||
logging.error(f"Fatal error in main: {e}")
|
||||
raise
|
||||
finally:
|
||||
# Финальная очистка
|
||||
if bot:
|
||||
await bot.session.close()
|
||||
logging.info("Bot stopped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Bot interrupted by user")
|
||||
except Exception as e:
|
||||
logging.error(f"Fatal error: {e}")
|
||||
sys.exit(1)
|
||||
3614
main_old.py
Normal file
3614
main_old.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,10 @@
|
||||
# middlewares/db.py
|
||||
from aiogram import BaseMiddleware
|
||||
from typing import Callable, Dict, Any, Awaitable
|
||||
from db import OracleDatabase
|
||||
from database import DatabaseManager
|
||||
|
||||
class DbSessionMiddleware(BaseMiddleware):
|
||||
def __init__(self, db: OracleDatabase):
|
||||
def __init__(self, db: DatabaseManager):
|
||||
self.db = db
|
||||
|
||||
async def __call__(
|
||||
|
||||
9
run1.cmd
Normal file
9
run1.cmd
Normal file
@ -0,0 +1,9 @@
|
||||
set DEBUG=1
|
||||
set BOT_TOKEN=6302522437:AAEDAYNrMuJCX5kt-IJ7CjT8AzfG3g-0mo0
|
||||
set BOT_NAME=salvagedb_bot
|
||||
set db_user=salvagebot
|
||||
set db_password=hlz1zm4n39nq6et0
|
||||
set db_dsn=89.110.92.87:17921/db1
|
||||
set ADMIN_USER_ID=604563487
|
||||
:1
|
||||
uv run main.py
|
||||
@ -2,6 +2,8 @@
|
||||
Системные утилиты и функции проверки ОС
|
||||
"""
|
||||
import platform
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
def get_operating_system() -> str:
|
||||
@ -35,3 +37,40 @@ def is_linux() -> bool:
|
||||
def is_macos() -> bool:
|
||||
"""Проверяет, запущен ли код на macOS"""
|
||||
return get_operating_system() == 'macOS'
|
||||
|
||||
|
||||
def log_system_info():
|
||||
"""
|
||||
Логирует информацию о системе при запуске
|
||||
"""
|
||||
os_name = get_operating_system()
|
||||
python_version = platform.python_version()
|
||||
platform_info = platform.platform()
|
||||
|
||||
logging.info("=== SYSTEM INFORMATION ===")
|
||||
logging.info(f"Operating System: {os_name}")
|
||||
logging.info(f"Platform: {platform_info}")
|
||||
logging.info(f"Python Version: {python_version}")
|
||||
logging.info(f"Architecture: {platform.architecture()[0]}")
|
||||
logging.info(f"Processor: {platform.processor()}")
|
||||
|
||||
# Проверяем переменные окружения
|
||||
if os.getenv('BOT_TOKEN'):
|
||||
logging.info("BOT_TOKEN: ✅ Set")
|
||||
else:
|
||||
logging.warning("BOT_TOKEN: ❌ Not set")
|
||||
|
||||
if os.getenv('DB_USER'):
|
||||
logging.info("Database credentials: ✅ Set")
|
||||
else:
|
||||
logging.warning("Database credentials: ❌ Not set")
|
||||
|
||||
# Проверяем запуск в Docker
|
||||
if os.path.exists('/.dockerenv'):
|
||||
logging.info("Environment: 🐳 Docker container")
|
||||
logging.info(f"Container timezone: {os.getenv('TZ', 'UTC')}")
|
||||
logging.info(f"Container user: {os.getenv('USER', 'unknown')}")
|
||||
else:
|
||||
logging.info("Environment: 🖥️ Host system")
|
||||
|
||||
logging.info("=== SYSTEM CHECK COMPLETE ===")
|
||||
Loading…
x
Reference in New Issue
Block a user